/****************************************************************************
 Module
   Cannon.c

 Revision
   1.0.1

 Description
   This module reads the potentiometer, positions the cannon, vibrates the
   knob and lights up the tip LED on the cannnon. 
   This module uses the Servo library for positioning the cannon. this module
   uses the AD library to read the potentiometer.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 11/8/19 11:08  ram     initial release
 11/11/19 21:07 ram		Added manual positioning function
 
****************************************************************************/

// the common headers for C99 types 
#include <stdint.h>
#include <stdbool.h>

// the headers to access the GPIO subsystem
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"

// the headers to access the TivaWare Library
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "termio.h"

#include "BITDEFS.H"

//Libraries
#include "Cannon.h"
#include "Servo.h"
#include "ServoDefs.h"
#include "ADMulti.h"
#include "PWM16Tiva.h"

//Definitions
#define ALL_BITS (0xff<<2) 
#define CANNON_IO_PORT HWREG(GPIO_PORTC_BASE+(GPIO_O_DATA+ALL_BITS))

#define LED_OFF BIT6LO
#define LED_ON BIT6HI
#define VIBE_OFF BIT7LO
#define VIBE_ON BIT7HI
#define READ_BUTTON(x) ((x &BIT5HI)>>5) // will this work?

#define ADC_COUNT_MASK 0x0000FFFF

/***Private Functions Prototypes***/
static uint16_t TrimCount(uint16_t ADCount);
static uint8_t CalcPosition(uint16_t ADCount);

/***Public Functions Prototypes***/
void Cannon_HWInit(void);

void Cannon_LED(UI_State_t state);
void Cannon_Vibrate(UI_State_t state);

uint8_t Cannon_ReadButton(void);
uint16_t Cannon_GetPotValue(void);

bool Cannon_UserPosition(uint16_t ADCount);
bool Cannon_MoveToBank(uint8_t Bank);
bool Cannon_ManualPosition(uint8_t position);

uint8_t QueryCannonPosition(void);
uint8_t QueryBankPositions(uint8_t Bank);



/***Module Level Variables***/
static uint8_t CannonPosition;
static const uint8_t BankPositions[3] = 
{
	74, //Left Bank
	50, //Middle Bank
	27 //Right Bank
};

/***
Cannon_HWInit Function Description
	Arguements: None
	Returns nothing
	Intializes the output pins for controling the LED and vibration motor
	Intializes the input pin to read the button
	Intializes the pin to read the pot using ADC methods
	
***/
void Cannon_HWInit(void)
{
	// set up port C by enabling the peripheral clock, waiting for the 
	// peripheral to be ready and setting the direction
	// of PC5 to input, PC6 & PC7 to output
	HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R2;
	while((HWREG(SYSCTL_PRGPIO)&SYSCTL_PRGPIO_R2) != SYSCTL_PRGPIO_R2)
	{
	}
	// Set PC6 and PC7 as digital outputs, PC5 as digital input
	HWREG(GPIO_PORTC_BASE+GPIO_O_DEN) |= (BIT5HI|BIT6HI|BIT7HI);
	HWREG(GPIO_PORTC_BASE+GPIO_O_DIR) |= (BIT6HI|BIT7HI);
	HWREG(GPIO_PORTC_BASE+GPIO_O_DIR) &= BIT5LO;
  
  //Enable Pull-up on PC5
  HWREG(GPIO_PORTC_BASE+GPIO_O_PUR) |= BIT5HI;
  
  //Cofigure open-drain on PC6 & PC7
  HWREG(GPIO_PORTF_BASE+GPIO_O_ODR) |= (BIT6HI|BIT7HI);
  
  //Turn everything off
  CANNON_IO_PORT &= LED_OFF;
  CANNON_IO_PORT &= VIBE_OFF;
	
	// setup the analog system to read one channel (PE4)
	ADC_MultiInit(1);
  
	return;
}

/***
Cannon_LED Function Description
	Arguements: state (on or off)
		NOTE: Use "On" or "Off". These are enums defined in the header
	Returns nothing
	If argument is on -> turn on LED
	If argument is off -> turn off LED
	
***/
void Cannon_LED(UI_State_t state)
{
	//If state = ON
	if(state == On)
	{
		// Set pin High
		CANNON_IO_PORT |= LED_ON;
	}
	//Else if state = OFF
	else if(state == Off)
	{
		// Set pin low
		CANNON_IO_PORT &= LED_OFF;
	}
	//end if
	return;
}

/***
Cannon_Vibrate Function Description
	Arguements: state (on or off)
		NOTE: Use "On" or "Off". These are enums defined in the header
	Returns nothing
	If argument is on -> turn on vibration
	If argument is off -> turn off vibration
	
***/
void Cannon_Vibrate(UI_State_t state)
{
	//If state = ON
	if(state == On)
	{
		// Set pin High
		CANNON_IO_PORT |= VIBE_ON;
	}
	//Else if state = OFF
	else if(state == Off)
	{
		// Set pin low
		CANNON_IO_PORT &= VIBE_OFF;
	}
	//end if
	return; 
}

/***
Cannon_ReadButton Function Description
	Arguements: none
	Returns 1 if pin high, 0 if low
	Reads PC5

***/
uint8_t Cannon_ReadButton(void)
{
	uint8_t CannonButtonVal;
	//Read the button pin
	CannonButtonVal = CANNON_IO_PORT;
	CannonButtonVal = READ_BUTTON(CannonButtonVal);
	//return pin value
	return CannonButtonVal;
}

/***
Cannon_GetPotValue Function Description
	Arguements: None
	Returns ADC Count
	Reads the voltage at the pot sweep, trims it, and returns the trimmed
	value for further use
***/
uint16_t Cannon_GetPotValue(void)
{
	//Intialize array to get count
	uint32_t ADC_Results[1];
	//Get ADC count and store to array
	ADC_MultiRead(ADC_Results);
	//Isolate the count and store to int
	uint32_t count32 = ADC_Results[0];
	count32 &= ADC_COUNT_MASK;
	uint16_t CountVal = (uint16_t)count32;
	//Call TrimCount
	CountVal = TrimCount(CountVal);
	// return the count
	return (CountVal);
}


/***
Cannon_UserPosition Function Description
	Arguments: Cannon Position Value
	returns status
	Takes the count value, converts it to a servo tick value and then sends
	the tick count to the servo library
  Updates current cannon position
	NOTE: This function is to be used for any analog movement. 
***/

bool Cannon_UserPosition(uint16_t ADCcount)
{
	//Convert ADC count to position
	uint8_t position;
	position = CalcPosition(ADCcount);
	//Call Servo_MoveCannon and give it the position
	bool PWMStatus;
	PWMStatus = Servo_MoveCannon(position);
	//If movement successful
	if(PWMStatus)
	{
		//update CannonPosition var
		CannonPosition = position;
	}
	//end if
	//Return status of PWM function
	return (PWMStatus);
}

/***
Cannon_MoveToBank Function Description
	Arguments: Bank Index
	returns status
	Takes the Bank index, finds the servo position associated with said bank
  and moves the cannon to the bank.
  Updates current cannon position
	NOTE: This function is to be used for bank-to-bank movement. 
***/
bool Cannon_MoveToBank(uint8_t Bank)
{
	//Set status to false
	bool PWMStatus = false;
	//If bank < 3
	if(Bank <3)
	{
		//Get bank position value
    uint8_t position;
		position = BankPositions[Bank];
		//Call Servo_MoveCannon with bank position
		PWMStatus = Servo_MoveCannon(position);
	}
  	//If movement successful
	if(PWMStatus)
	{
		//update CannonPosition var
		CannonPosition = BankPositions[Bank];
	}
	//end if
	
	//return status
	return (PWMStatus);
}

/***
Cannon_ManualPosition Function Description
	Arguments: position (0-100)
	returns status
	Takes a servo count value and moves the cannon to that servo count
	Updates current cannon position
	NOTE: This function is to be used for sweeping movement. 
***/
bool Cannon_ManualPosition(uint8_t position)
{
	// Call servo function
	bool status;
	status = Servo_MoveCannon(position);
	
	if(status)// Update position count if successful
	{
		CannonPosition = position;
	}
	//return status
	return(status);
}

/***
QueryCannonPosition
  Arguments: none
  returns module level variable CannonPosition
***/
uint8_t QueryCannonPosition(void)
{
	return (CannonPosition);
}

uint8_t QueryBankPositions(uint8_t Bank)
{
  int8_t PosValue;
  if(Bank>2)
  {
    PosValue = 0xFF;
  }
  else
  {
    PosValue = BankPositions[Bank];
  }
  return(PosValue);
  
}
/******************************************************************************/

/***
Cannon_TrimCount Function Description
	Arguments: ADCcount
	Returns trimmed count
	Takes the count value and adjusts it to match thresholds if value exceeds
	NOTE: This function is to be used for any analog movement. 
***/
static uint16_t TrimCount(uint16_t ADCount)
{
	uint16_t TrimmedValue;
	//If value is above higher threshold, set value to upper threshold
	if(ADCount > COUNT_UPPER_LIM)
	{
		TrimmedValue = COUNT_UPPER_LIM;
	}
	//If value is below lower threshold, set value to lower threshold
	else if(ADCount < COUNT_LOWER_LIM)
	{
		TrimmedValue = COUNT_LOWER_LIM;
	}
  else
  {
    	TrimmedValue = ADCount;
  }
	//return value
	return(TrimmedValue);
}

static uint8_t CalcPosition(uint16_t ADCount)
{
	double countRange;
	countRange = COUNT_UPPER_LIM - COUNT_LOWER_LIM;
	double offset = COUNT_LOWER_LIM;
	//Figure out count ratio
	double ratio;
	ratio = ((float)ADCount-offset)/(countRange);

	//Determine tick count based on ratio
	uint8_t ServoCount;
	ServoCount = ratio*100; 
	
	//return tick count
	return(ServoCount);
}
	
//----------------------------------End File------------------------------------//