/**
  ******************************************************************************
  * @file    HAL_RF1A.c
  * @author  Johan Zwarteveld
  * @version V0.1
  * @date    04-08-2015
  * @brief   CC1101-Based Radio Module Driver
  *          This file provides firmware functions to manage the following
  *          functionalities of the CC1101-Based Radio Module (RF1A):
  *           + Setting Vcore voltage
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "HAL_RF1A.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t instruction;	// instruction + 1st address
uint8_t *buffer;		// value buffer
uint8_t bufferlength;	// buffer length in bytes
uint8_t buffercount;	// buffer count in bytes
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/

uint8_t HAL_RF1A_RegisterSingleRead(uint8_t addr)
{
	uint8_t value;

	// Check for valid configuration register address, 0x3E refers to PATABLE
	if ((addr <= 0x2E) || (addr == 0x3E))
	{
		// Send address + Instruction + 1 dummy byte (auto-read)
		instruction = (addr | RF_SNGLREGRD);
	}
	else
	{
		// Send address + Instruction + 1 dummy byte (auto-read)
		instruction = (addr | RF_STATREGRD);
	}

	buffer = &value;
	bufferlength = 1;
	buffercount = 0;

	// Enable instruction interrupt
	RF1AIFCTL1 |= RFINSTRIE;

	// Wait for the transfer to complete
	__bis_SR_register(LPM3_bits + GIE);

	// Return register value
	return value;
}

void HAL_RF1A_RegisterBurstRead(uint8_t addr, uint8_t *databuffer, uint8_t count)
{
	// Check for valid count value
	if (count == 0)
		return;

	instruction = (addr | RF_REGRD);
	buffer = databuffer;
	bufferlength = count;
	buffercount = 0;

	// Enable instruction interrupt
	RF1AIFCTL1 |= RFINSTRIE;

	// Wait for the transfer to complete
	__bis_SR_register(LPM3_bits + GIE);
}

void HAL_RF1A_RegisterSingleWrite(uint8_t addr, uint8_t value)
{
	instruction = (addr | RF_SNGLREGWR);

	buffer = &value;
	bufferlength = 1;
	buffercount = 0;

	// Enable instruction interrupt
	RF1AIFCTL1 |= RFINSTRIE;

	// Wait for the transfer to complete
	__bis_SR_register(LPM3_bits + GIE);
}

void HAL_RF1A_RegisterBurstWrite(uint8_t addr, uint8_t *databuffer, uint8_t count)
{
	// Check for valid count value
	if (count == 0)
		return;

	instruction = (addr | RF_REGWR);
	buffer = databuffer;
	bufferlength = count;
	buffercount = 0;

	// Enable instruction interrupt
	RF1AIFCTL1 |= RFINSTRIE;

	// Wait for the transfer to complete
	__bis_SR_register(LPM3_bits + GIE);
}

uint8_t HAL_RF1A_CommandStrobe(uint8_t cmd)
{
	uint8_t status;
	uint8_t gdo_state;

	// Check for valid command strobe
	if (((cmd >= (RF_SRES | RF_TXSTAT)) && (cmd <= (RF_SNOP | RF_TXSTAT))) || ((cmd >= (RF_SRES | RF_RXSTAT)) && (cmd <= (RF_SNOP | RF_RXSTAT))))
	{
		// Clear status flag
		RF1AIFCTL1 &= ~RFSTATIFG;

		// Write command strobe
		if (((cmd > (RF_SRES | RF_TXSTAT)) && (cmd < (RF_SNOP | RF_TXSTAT))) || ((cmd > (RF_SRES | RF_RXSTAT)) && (cmd < (RF_SNOP | RF_RXSTAT))))
		{
			// Change GDO2 signal so chip ready can be monitored
			gdo_state = HAL_RF1A_RegisterSingleRead(IOCFG2);					// Backup GDO2 configuration
			HAL_RF1A_RegisterSingleWrite(IOCFG2, 0x69);							// Change GDO2 to chip ready (active high)

			// Write command
			RF1AINSTRB = cmd;
			// TODO further check behavior
			if ((RF1AIN & 0x04) == 0x00)										// chip in sleep mode
			{
				if ((cmd == (RF_SXOFF | RF_TXSTAT)) || (cmd == (RF_SPWD | RF_TXSTAT)) || (cmd == (RF_SWOR | RF_TXSTAT)) ||
					(cmd == (RF_SXOFF | RF_RXSTAT)) || (cmd == (RF_SPWD | RF_RXSTAT)) || (cmd == (RF_SWOR | RF_RXSTAT)))
				{
					// do nothing command puts chip in sleep, which is already sleeping.
				}
				else
				{
					// Wait for rf chip to be ready
					/* Note: When the radio needs to transition from the SLEEP state to an active state
					 * (for example, from IDLE, to RX or TX) the CPU must not go into a low-power mode until the radio is active.
					 */
// TODO Hangpoint
					while((RF1AIN & 0x04) == 0x00);
					/* Note: The necessary delay of up to 810us for the radio's transition during which the radio oscillator turns on and stabilizes,
					 * is required in order for the radio core to obtain its high speed clock and re-synchronize with the '430 core.
					 * Since no stable clock is available to the radio core during this time, interactions with the radio core should be avoided or postponed.
					 */
					__delay_cycles(1000);
				}
			}
			// Restore GDO2 setting
			HAL_RF1A_RegisterSingleWrite(IOCFG2, gdo_state);

			// wait for status register to update
			RF1AIFCTL1 |= RFSTATIE;
			__bis_SR_register(LPM3_bits + GIE);
		}
		else
		{
			RF1AINSTRB = cmd;
		}
		status = RF1ASTATB;
	}
	return status;
}
/* Private functions  --------------------------------------------------------*/
/* Interrupt Service Routine -------------------------------------------------*/
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=CC1101_VECTOR
__interrupt void CC1101_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(CC1101_VECTOR))) CC1101_ISR (void)
#else
#error Compiler not supported!
#endif
{
	// radio interface interrupt register
	switch(__even_in_range(RF1AIFIV, 14))
	{
		case 0: break;																		// No interrupt
		case 2:																				// Interrupt source: Radio interface error
			// radio error interrupt register
			switch(__even_in_range(RF1AIFERRV, 8))
			{
				case 0: break;																// No interrupt
				case 2: break;																// Error source: Low voltage
				case 4: break;																// Error source: Operand
				case 6: break;																// Error source: Output data not available
				case 8: break;																// Error source: Operand overwrite
				default: break;
			}
			break;
		case 4: 																			// Interrupt source: Radio interface data out
			if (buffercount < (bufferlength-1))												// Read complete?
			{
				buffer[buffercount] = RF1ADOUT1B;											// Read data
				buffercount++;																// Increase buffer counter
			}
			else
			{
				buffer[buffercount] = RF1ADOUT0B;											// Read data
				RF1AIFCTL1 &= ~RFDOUTIE;													// Disable data out interrupt
				LPM4_EXIT;
			}
			break;
		case 6:																				// Interrupt source: Radio interface status out
			RF1AIFCTL1 &= ~RFSTATIE;
			LPM4_EXIT;
			break;
		case 8: 																			// Interrupt source: Radio interface data in
			if (buffercount < bufferlength)													// Write complete?
			{
				RF1ADINB = buffer[buffercount];												// Write data
				buffercount++;																// increase buffer counter
			}
			else
			{
				buffercount = RF1ADOUTB;													// Read data out to clear interrupt flag
				RF1AIFCTL1 &= ~RFDINIE;														// Disable data in interrupt;
				LPM4_EXIT;
			}
			break;
		case 10: 																			// Interrupt source: Radio interface instruction in
			switch(instruction & 0x80)
			{
				case RF_SNGLREGRD:															// Read register
					RF1AINSTR1B = instruction;												// Send address + Instruction + 1 dummy byte (auto-read)
					RF1AIFCTL1 |= RFDOUTIE;													// Enable data out interrupt
					break;
				case RF_SNGLREGWR:															// Write register
					RF1AINSTRW = (instruction <<8 ) | buffer[buffercount]; 					// Send address + instruction
					buffercount++;															// increase counter
					RF1AIFCTL1 |= RFDINIE;													// Enable data in interrupt
					break;
				default: break;
			}
			RF1AIFCTL1 &= ~RFINSTRIE;														// Disable instruction interrupt
			break;
		case 12: break;																		// Interrupt source: Reserved
		case 14: break;																		// Interrupt source: Reserved
		default: break;
	}
	// radio core interrupt register
	// see: Table 25-5. CC1101 Radio Core Interrupt Mapping of the CC430 Family User's guide
	switch(__even_in_range(RF1AIV, 32))
	{
		case 0: break;																		// No interrupt
		case 2:	break;																		// Interrupt source: Radio core signal 0: GDO0
		case 4: break;																		// Interrupt source: Radio core signal 1: GDO1
		case 6:																				// Interrupt source: Radio core signal 2: GDO2
// TODO Hang point
			__no_operation();
			LPM4_EXIT;
			break;
		case 8: break;																		// Interrupt source: Radio core signal 3:
		case 10: break;																		// Interrupt source: Radio core signal 4:
		case 12: break;																		// Interrupt source: Radio core signal 5:
		case 14: break;																		// Interrupt source: Radio core signal 6:
		case 16: break;																		// Interrupt source: Radio core signal 7:
		case 18: break;																		// Interrupt source: Radio core signal 8:
		case 20: 																			// Interrupt source: Radio core signal 9:
			if ((RF1AIN & BIT9) == BIT9)
			{
				RF1AIE &= ~BIT9;															// Disable TX end-of-packet interrupt
				LPM4_EXIT;
			}
			break;
		case 22: break;																		// Interrupt source: Radio core signal 10:
		case 24: break;																		// Interrupt source: Radio core signal 11:
		case 26: break;																		// Interrupt source: Radio core signal 12:
		case 28: break;																		// Interrupt source: Radio core signal 13:
		case 30: break;																		// Interrupt source: Radio core signal 14:
		case 32: break;																		// Interrupt source: Radio core signal 15:
		default: break;
	}
}
