#include "spi.h"

#define SPI_BASE	((SPI_TypeDef *)spiBase_Addr_List[spi_hw_ch])

void spiReset(spiCH_t spi_hw_ch)
{
	if(spiBus_No[spi_hw_ch] == 1) {
		RCC->APB1RSTR |= (1 << spiBus_Rst_bitPos[spi_hw_ch]);
		RCC->APB1RSTR &= ~(1 << spiBus_Rst_bitPos[spi_hw_ch]);
		return;
	}
		
	RCC->APB2RSTR |= (1 << spiBus_Rst_bitPos[spi_hw_ch]);
	RCC->APB2RSTR &= ~(1 << spiBus_Rst_bitPos[spi_hw_ch]);
}

void spiEnableBus(spiCH_t spi_hw_ch)
{
	if(spiBus_No[spi_hw_ch] == 1) {
		RCC->APB1ENR |= (1 << spiBus_En_bitPos[spi_hw_ch]);
		return;
	}

	RCC->APB2ENR |= (1 << spiBus_En_bitPos[spi_hw_ch]);
}

void spiEnable(spiCH_t spi_hw_ch)
{
	SPI_BASE->CR1 |= SPI_CR1_SPE;
}

void spiDissable(spiCH_t spi_hw_ch)
{
	// TODO: implement p.768 procedure for dissabling
	//while(SPI_BASE->SR	
	SPI_BASE->CR1 &= ~SPI_CR1_SPE;
}

void spiSetMode(spiCH_t spi_hw_ch, spi_mode_t mode)
{
	SPI_BASE->CR1 &= ~(mode << SPI_CR1_MSTR_Pos);
	SPI_BASE->CR1 |= mode << SPI_CR1_MSTR_Pos;

	// TODO: find out if this is the correct place to set the SSOE bit
	SPI_BASE->CR2 &= ~SPI_CR2_SSOE;
	if(mode == SPI_MASTER) {
		SPI_BASE->CR2 |= SPI_CR2_SSOE;
	}	
}

void spiSetPolarity(spiCH_t spi_hw_ch, spi_clkPol_t clkPol)
{
	// reset
	SPI_BASE->CR1 &= ~SPI_CR1_CPOL;
	// set
	SPI_BASE->CR1 |= clkPol << SPI_CR1_CPOL_Pos;
}

void spiSetPhase(spiCH_t spi_hw_ch, spi_phase_t phase)
{
	// reset
	SPI_BASE->CR1 &= ~(phase << SPI_CR1_CPHA_Pos);
	// set
	SPI_BASE->CR1 |= phase << SPI_CR1_CPHA_Pos;	
}

void spiSetBitFrameLength(spiCH_t spi_hw_ch, spi_framel_t framel)
{

	SPI_BASE->CR2 &= ~(SPI_CR2_FRXTH | SPI_CR2_DS);

	// using p.974 as example
	if(framel == SPI_FRAME_LENGTH_8BIT) {
		// set FIFO reception threshold to 8 bit 
		SPI_BASE->CR2 |= SPI_CR2_FRXTH;
		// set transfer lwnght to 8 bit
		SPI_BASE->CR2 |= SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2;
		return;
	}
	SPI_BASE->CR2 |= SPI_CR2_DS;
}

void spiSetFrameFormat(spiCH_t spi_hw_ch, spi_framef_t framef)
{
	// reset
	SPI_BASE->CR1 &= ~SPI_CR1_LSBFIRST;
	// set
	SPI_BASE->CR1 |= framef << SPI_CR1_LSBFIRST_Pos;
}

spi_framef_t spiGetFrameFormat(spiCH_t spi_hw_ch)
{
	return (spi_framef_t)(SPI_BASE->CR1 & SPI_CR1_LSBFIRST) >> SPI_CR1_LSBFIRST_Pos;
}

void spiSetClockPrescaler(spiCH_t spi_hw_ch, uint32_t clkDiv)
{
	// reset
	SPI_BASE->CR1 &= ~SPI_CR1_BR;
	// set
	SPI_BASE->CR1 |= (clkDiv << SPI_CR1_BR_Pos) & SPI_CR1_BR;
}

void spiSetComMode(spiCH_t spi_hw_ch, spi_comMode_t comMode)
{
	// reset
	SPI_BASE->CR1 &= ~SPI_CR1_RXONLY;
	// set
	SPI_BASE->CR1 |= comMode << SPI_CR1_RXONLY_Pos;
}

void spiSetSoftwareSlaveManagement(spiCH_t spi_hw_ch, uint8_t logic)
{
	SPI_BASE->CR1 &= ~SPI_CR1_SSM;

	if(logic){
		SPI_BASE->CR1 |= SPI_CR1_SSM;
	}
}

void spiSetInternalSlaveSelect(spiCH_t spi_hw_ch, uint8_t logic)
{
	SPI_BASE->CR1 &= ~SPI_CR1_SSI;
	
	if(logic) {
		SPI_BASE->CR1 |= SPI_CR1_SSI;
	}
}

uint8_t spiTrx8BitPolling(spiCH_t spi_hw_ch, uint8_t tx_data)
{
	// wait for BSY bit to Reset -> This will indicate that SPI is not busy in communication	
	while (SPI_BASE->SR & SPI_SR_BSY);
	
	// from example code p.975 f
	// this masking must be done. otherwise 16bits frame will be used
	*(uint8_t*)&(SPI_BASE->DR) = tx_data;

	// Wait for RXNE to set -> This will indicate that the Rx buffer is not empty
	while (!(SPI_BASE->SR &SPI_SR_RXNE));  
	

	// TODO: test read!	
	return *(uint8_t*)&(SPI_BASE->DR);
}