/*
 * Copyright  2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*******************************************************************************
 * Header File
 ******************************************************************************/
#include "board.h"
#include "pin_mux.h"
#include <stdbool.h>
#include "fsl_spi.h"
#include "fsl_syscon.h"
#include "fsl_pint.h"
#include "fsl_debug_console.h"

/*******************************************************************************
 * Macro Definition
 ******************************************************************************/
/*---------------------------------------fixed macro***********************/
//SPI0 base address
#define EXAMPLE_SPI_MASTER             SPI0
//select main clock as system clock source
#define EXAMPLE_CLK_SRC                kCLOCK_MainClk
//get SPI clock frequency
#define EXAMPLE_SPI_MASTER_CLK_FREQ    CLOCK_GetFreq(EXAMPLE_CLK_SRC)
//chip select is ssel0
#define EXAMPLE_SPI_MASTER_SSEL        kSPI_Ssel0Assert
//SPI0 interrupt id
#define EXAMPLE_SPI_MASTER_IRQ         SPI0_IRQn
//SPI0 interrupt service routine
#define SPI_MASTER_IRQHandler          SPI0_IRQHandler

//command byte count for reading data:1 Byte
#define RECV_COMMAND_BYTE_COUNT        1
//information byte count for reading data:1 Byte
#define RECV_INFO_BYTE_COUNT           1
//size of transmitting data buffer and receiving data buffer
#define BUFFER_SIZE                    66
/*---------------------------------------fixed macro***********************/
/*---------------------------------------configurable macro****************/
//SPI shift clock rate
#define EXAMPLE_SPI_MASTER_BAUDRATE    400000U//400Kbps
/*---------------------------------------configurable macro****************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static void EXAMPLE_SPIMasterInit(void);
static void EXAMPLE_MasterStartTransfer(void);
static void EXAMPLE_TransferDataCheck(void);
//SPI to dual USARTs bridge register address type
typedef enum reg_addr
{
/************************general register address********************************************/
	THR_RHR_ADDR                  = 0x00,
	IER_ADDR                      = 0x01,
	FCR_IIR_ADDR                  = 0x02,
	LCR_ADDR                      = 0x03,
	MCR_ADDR                      = 0x04,
	LSR_ADDR                      = 0x05,
	TCR_MSR_ADDR                  = 0x06,
	TLR_SPR_ADDR                  = 0x07,
	TXLVL_ADDR                    = 0x08,
	RXLVL_ADDR                    = 0x09,
	IODir_ADDR                    = 0x0A,
	IOState_ADDR                  = 0x0B,
	IOIntEna_ADDR                 = 0x0C,
	reserved_ADDR                 = 0x0D,
	IOControl_ADDR                = 0x0E,
	EFCR_ADDR                     = 0x0F,
/************************general register address********************************************/

/************************special register and enhanced register address**********************/
	DLL_ADDR                      = 0x00,
	DLH_ADDR                      = 0x01,
	EFR_ADDR                      = 0x02,
	XON1_ADDR                     = 0x04,
	XON2_ADDR                     = 0x05,
	XOFF1_ADDR                    = 0x06,
	XOFF2_ADDR                    = 0x07,
/************************special register and enhanced register address**********************/
}Reg_Addr_Type;

typedef enum int_type
{
	THR_INT        = 0x02,//data count reaches at Tx buffer trigger level
	RHR_INT        = 0x04,//data count reaches at Rx buffer trigger level
	RX_TIMEOUT_INT = 0x0C//data count doesn't reach data Rx buffer trigger level and time out
}irqIntType;
/*******************************************************************************
 * Variables
 ******************************************************************************/
//transmitting data buffer 
static uint8_t txBuffer[BUFFER_SIZE];
//receiving data buffer
static uint8_t rxBuffer[BUFFER_SIZE];
//index for transmitting data buffer
static uint32_t txIndex            = BUFFER_SIZE;
//index for receiving data buffer
static uint32_t rxIndex            = BUFFER_SIZE;
//data transmission flag indicates whether SPI data transmission is finished or not
static bool slaveFinished = false;
//IRQ flag indicates whether SPI-USART bridge generates an IRQ interrupt or not
static bool IRQFlag = false;
//size for data transmission including SPI master to/from USART peripheral 
static uint32_t bufferSize = BUFFER_SIZE;
//Systick timer counter
static uint32_t g_systickCounter;

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * brief  Systick timer interrupt service routine
 * param  void
 * return void
 */
void SysTick_Handler(void)
{
	if(g_systickCounter != 0U)
	{
		g_systickCounter--;
	}
}

/*!
 * brief  time delay function based on systick timer
 * param  uint32_t ms->time delay
 * return void
 */
void SysTick_DelayTicks(uint32_t ms)
{
	g_systickCounter = ms;
	while(g_systickCounter != 0U)
	{
	}
}

/*!
 * brief  Systick timer initialize
 * param  void
 * return void
 */
void SystickInitialize(void)
{
	//Set systick reload value to generate 1ms interrupt
  if(SysTick_Config(SystemCoreClock / 1000U))
  {
		while (1)
		{
		}
  }
}

/*!
 * brief  SPI master interrupt service routine
 * param  void
 * return void
 */
void SPI_MASTER_IRQHandler(void)
{
	//Check if data is ready in RX register
	if((SPI_GetStatusFlags(EXAMPLE_SPI_MASTER) & kSPI_RxReadyFlag))
	{
		//read data received and save it to receiving data buffer
		rxBuffer[bufferSize - rxIndex] = SPI_ReadData(EXAMPLE_SPI_MASTER);
		//decrement receiving data buffer index
		rxIndex--;
	}
	//Write data to TX regsiter if TX register is ready
	if((SPI_GetStatusFlags(EXAMPLE_SPI_MASTER) & kSPI_TxReadyFlag) && (txIndex != 0U))
	{
		//If this is the last byte to send
		if(1U == txIndex)
		{
			//add end of transfer configuration
			SPI_WriteConfigFlags(EXAMPLE_SPI_MASTER, kSPI_EndOfTransfer);
			//send the last byte
			SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - txIndex]);
		}
		else
		{
			//send other bytes
			SPI_WriteData(EXAMPLE_SPI_MASTER, (uint16_t)(txBuffer[bufferSize - txIndex])); 
		}
		//decrement transmiting data buffer index
		txIndex--;
	}
	//If no data to be transferred, enable transfer finish flag
	if((txIndex == 0U) && (rxIndex == 0U))
	{
		//reconfigure index of transiting data buffer
		txIndex            = bufferSize;
		//reconfigure index of receiving data buffer
		rxIndex            = bufferSize;
		//set slave data transimission finish flag to true
		slaveFinished      = true;
	}
}

/*!
 * brief  read SPI2USART bridge register except for RHR
 * param  uint8_t regAddr->register address
 * return uint8_t->register value
 */
uint8_t readRegExceptRHR(uint8_t regAddr)
{
	if(regAddr != THR_RHR_ADDR)
	{//target register is not RHR
		//bridge register reading need to send two data bytes, 
		//format of the first byte is R/~W(read/write bit) + register address + usart channel number + don't care bit
		//the second byte is dummy byte, that means any value is ok
		bufferSize = 2;
		//set index of transmitting buffer and receiving buffer 
		txIndex            = bufferSize;
		rxIndex            = bufferSize;
		//set frame header 
		txBuffer[0] = ((1 << 7) | ((regAddr & 0x0F) << 3) | (0 << 1));
		//send frame header, also the first byte to be sent
		SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - (txIndex--)]);
		//wait until SPI transfer finish
		while(slaveFinished == false)
		{
		}
		//reset SPI transfer flag to false
		slaveFinished = false;
		//this delay is very important,otherwise register reading will fail
		SysTick_DelayTicks(10);
		
		//return register value
		return (uint8_t)(rxBuffer[1]);
	}
	else
	{
		return 0;
	}
}

/*!
 * brief  interrupt service routine for PINT Pin interrupt 0-7.
 * param  pint_pin_int_t pintr->PINT Pin Interrupt type
 *        uint32_t pmatch_status->match status
 * return void
 */
void pint_intr_callback(pint_pin_int_t pintr, uint32_t pmatch_status)
{
	//set IRQ check flag to true when IRQ interrupt from SPI2USART bridge is checked
	IRQFlag = true;
}

/*!
 * brief  main entry  
 * param  void
 * return void
 */
int main(void)
{
	//temporary register value
	uint8_t tempRegVal = 0;
	//available data count from bridge
	uint8_t availableDataCountFromBridge = 0;
	
  //Attach main clock to USART0(console used to observe received data)
	//this USART is connected to PC USART terminal by USB wire
	//open serial terminal and set baudrate 9600,8 data bit, no parity, no flow control
  CLOCK_Select(kUART0_Clk_From_MainClk);
	//Attach main clock to SPI0
  CLOCK_Select(kSPI0_Clk_From_MainClk);
	
	//Input/Output Pin Initialization
  BOARD_InitPins();
	//System Clock Initialization
  BOARD_InitBootClocks();
	//Debug Console Initialization, debug console is used to output data to PC terminal
	//user can observe data on PC end
  BOARD_InitDebugConsole();
	
	//Connect trigger source to PINT
	SYSCON_AttachSignal(SYSCON, kPINT_PinInt0, kSYSCON_GpioPort0Pin13ToPintsel);
	//PINT Initialization
	PINT_Init(PINT);
	//Setup Pin Interrupt 0 for falling edge 
	PINT_PinInterruptConfig(PINT, kPINT_PinInt0, kPINT_PinIntEnableFallEdge, pint_intr_callback);
	//Enable callbacks for PINT0 by Index
	PINT_EnableCallbackByIndex(PINT, kPINT_PinInt0);
  
	//Systick timer initialize
  SystickInitialize();
	//This is a hint that the system is running
  PRINTF("SPI master connected to SPI-USART bridge starts up!\r\n");
  //Initialize the SPI master with configuration
  EXAMPLE_SPIMasterInit();
  //Start SPI transfer with slave
  EXAMPLE_MasterStartTransfer();

  while(1)
  {
		if(IRQFlag == true)
		{//SPI master has checked IRQ signal from SPI2USART bridge
			//clear IRQ flag
			IRQFlag = false;
			//clear tx buffer
			memset(txBuffer,0,BUFFER_SIZE);
			//clear rx buffer
			memset(rxBuffer,0,BUFFER_SIZE);
			//read SPI2USART bridge IIR register to identify interrupt source
			tempRegVal = readRegExceptRHR(FCR_IIR_ADDR);
      if(tempRegVal == THR_INT)
			{//THR interrupt,data transmission from SPI master to USART peripheral
			 //THR interrupt means that Tx buffer in SPI2USART bridge has reached trigger level 
       //also means that SPI2USART bridge has space to support another Tx transfer
        /**********************************2nd Tx test****************************************/	
        //It is better to add a read TXLVL register command to insure data count to sent less than space count in
        //SPI2USART Tx buffer
				//readRegExceptRHR(TXLVL_ADDR);
				//initialize transmitting data buffer and receiving data buffer size to 20 
				bufferSize = 20;
				//initialize transmitting data buffer index
				txIndex            = bufferSize;
				//initialize receiving data buffer index
				rxIndex            = bufferSize;
				//             R/~W  reg address  usart channel number   don't care
				//command byte   0      0000           01                     0      =  0x02
				txBuffer[0] = 0x02;
			  //initialize transmitting data buffer and receiving data buffer 
				//this data transmission will send one command byte 0x02 and 18 data bytes(0x00 - 0x12)
			  for (int i = 1U; i < bufferSize; i++)
				{
					txBuffer[i] = i-1;
					rxBuffer[i] = 0U;
				}
				//initiate data transmission from SPI master to USART peripheral
				SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - (txIndex--)]);
				/**********************************2nd Tx test****************************************/			
			}
			if(tempRegVal == RHR_INT)
			{//RHR interrupt, data transmission from USART peripheral to SPI master
			 //RHR interrupt means that Rx buffer in SPI2USART bridge has reached trigger level(64bytes)
			 //also means that SPI2USART has received available data from USART peripheral, data count is equal to trigger level
				/********************************************1st Rx test*****************************************************************************/
				//read RXLVL register to obtain available data count in Rx buffer in SPI2USART bridge
				availableDataCountFromBridge = readRegExceptRHR(RXLVL_ADDR);
				//PRINTF("Rx trigger available data count is %d\r\n",availableDataCountFromBridge);
				
				//initialize transmitting data buffer and receiving data buffer size to 66, it means that this data transmission will receive one dummy data byte + one information byte + 64 data bytes
				//information byte format is R/~W bit + register address bits + usart channel number bits + don't care bit
        bufferSize = availableDataCountFromBridge + RECV_COMMAND_BYTE_COUNT + RECV_INFO_BYTE_COUNT;
				//initialize transmitting data buffer index
				txIndex            = bufferSize;
				//initialize receiving data buffer index
				rxIndex            = bufferSize;
				//                R/~W + reg addr + usart channel      + don't care
				//command byte      1      0000       00(don't care)         0      = 0x80
				txBuffer[0] = 0x80;
				//initiate data tranmission from SPI2USART bridge to SPI master to read data from USART peripheral
				SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - (txIndex--)]);
				//output received data to console
				EXAMPLE_TransferDataCheck();
				/********************************************1st Rx test*****************************************************************************/
			}
			if(tempRegVal == RX_TIMEOUT_INT)
			{//Rx Timeout interrupt, data transmission from USART peripheral to SPI master
			 //Rx Timeout interrupt means that Rx buffer in SPI2USART bridge has not reached trigger level(64bytes)
			 //also means that SPI2USART has received available data from USART peripheral,data count is less than trigger level
				/********************************************2nd Rx test*****************************************************************************/
				//read RXLVL register to obtain available data count in Rx buffer in SPI2USART bridge
				availableDataCountFromBridge = readRegExceptRHR(RXLVL_ADDR);
				//PRINTF("Rx timeout available data count is %d\r\n",availableDataCountFromBridge);
	
				//initialize transmitting data buffer and receiving data buffer size to 65, it means that this data transmission will receive one dummy data byte + one information byte + 63 data bytes
				//information byte format is R/~W bit + register address bits + usart channel nember bits+ don't care bit
				bufferSize = availableDataCountFromBridge + RECV_COMMAND_BYTE_COUNT + RECV_INFO_BYTE_COUNT;
				//initialize transmitting data buffer index
				txIndex            = bufferSize;
				//initialize receiving data buffer index
				rxIndex            = bufferSize;
				//                R/~W + reg addr + usart channel      + don't care
				//command byte      1      0000       00(don't care)         0      = 0x80
				txBuffer[0] = 0x80;
				//initiate data tranmission from SPI2USART bridge to SPI master to read data from USART peripheral
				SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - (txIndex--)]);
				//output received data to console
				EXAMPLE_TransferDataCheck();
				/********************************************2nd Rx test*****************************************************************************/
			}
		}
  }
}

/*!
 * brief  SPI master initialization
 * param  void
 * return void
 */
void EXAMPLE_SPIMasterInit(void)
{
	//SPI master user configure structure
	spi_master_config_t masterConfig = {0};
	//SPI master source clock frequency
	uint32_t srcFreq                 = 0U;

	/* configuration from using SPI_MasterGetDefaultConfig():
	 * userConfig->enableLoopback = false;
	 * userConfig->enableMaster = true;
	 * userConfig->polarity = kSPI_ClockPolarityActiveHigh;
	 * userConfig->phase = kSPI_ClockPhaseFirstEdge;
	 * userConfig->direction = kSPI_MsbFirst;
	 * userConfig->baudRate_Bps = 500000U;
	 * userConfig->dataWidth = kSPI_Data8Bits;
	 * userConfig->sselNum = kSPI_Ssel0Assert;
	 * userConfig->sselPol = kSPI_SpolActiveAllLow;
	 */
	//get default configuration of SPI
	SPI_MasterGetDefaultConfig(&masterConfig);
	//reconfigure SPI shift clock rate
	masterConfig.baudRate_Bps = EXAMPLE_SPI_MASTER_BAUDRATE;
	//select chip select 0, chip select in LPC804 has two:0 and 1
	masterConfig.sselNumber   = EXAMPLE_SPI_MASTER_SSEL;
	//get SPI peripheral source clock frequency
	srcFreq                   = EXAMPLE_SPI_MASTER_CLK_FREQ;
	//configure SPI using configuration above
	SPI_MasterInit(EXAMPLE_SPI_MASTER, &masterConfig, srcFreq);

	//Enable SPI interrupt in NVIC
	EnableIRQ(EXAMPLE_SPI_MASTER_IRQ);
	//Enable SPI receive ready interrupt in SPI
	SPI_EnableInterrupts(EXAMPLE_SPI_MASTER, kSPI_RxReadyInterruptEnable);
}

/*!
 * brief  start SPI Tx transfer
 * param  void
 * return void
 */
void EXAMPLE_MasterStartTransfer(void)
{
	/**********************************1st Tx test****************************************/	
	//set transmitting data buffer size including command byte(1Byte) and 64 data bytes
  bufferSize = 65;
  //initialize transmitting and receiving data buffer
  for(uint32_t i = 1U; i < bufferSize; i++)
  {
		txBuffer[i] = i-1;
    rxBuffer[i] = 0U;
  } 
	//                                                                                                                      R/~W  reg address  USART channel   don't care
	//this byte is command byte including R/~W(0) + register address(0000) + USART channel number(01) + don't care bit(0) =   0     0000            01              0          = 0x02
	txBuffer[0] = 0x02;
	//initialize transmitting buffer index
  txIndex = bufferSize;
	//initialize receiving buffer index
  rxIndex = bufferSize;
	//initiate data transmission from SPI master to USART peripheral
  SPI_WriteData(EXAMPLE_SPI_MASTER, txBuffer[bufferSize - (txIndex--)]);
	/**********************************1st Tx test****************************************/	
}

/*!
 * brief  output received data to console on PC end
 * param  void 
 * return void
 */
void EXAMPLE_TransferDataCheck(void)
{
	//loop index
	uint32_t index = 0U;
	
	//Wait for the transmission complete
	while(slaveFinished != true)
	{
	}
  
	//prompt
	PRINTF("\n\rReceived data from USART peripheral are:\r\n");
	//output data from bridge to console
	for(index = 0; index < bufferSize; index++)
	{
		//Print 8 numbers in a line
		if((index & 0x07U) == 0U)
		{
				PRINTF("\n\r");
		}
		//output data received to console on PC end
		PRINTF(" %02X", rxBuffer[index]);
		//clear rx buffer
		rxBuffer[index] = 0;
	}
}
