#include "spi.h"

// generic implementation of spi channel class

void spiInitMaster(
		spiCH_t spi_hw_ch,
		spi_clkPol_t clockPolarity,
		spi_phase_t phase,
		spi_framef_t frameFormat,
		spi_comMode_t comMode,
		uint32_t prescaler,
		pinNo_t clkPin,
		uint16_t altFuncClkPin,
		pinNo_t MOSIPin,
		uint16_t altFuncMOSIPin,
		pinNo_t MISOPin,
		uint16_t altFuncMISOPin)
{
	// GPIO setup
	pinInit(clkPin);
	pinInit(MOSIPin);
	pinInit(MISOPin);
	pinConfig(clkPin, alternate, pushPull, output, veryFast);
	pinConfig(MISOPin, alternate, floating, input , veryFast);
	pinConfig(MOSIPin, alternate, pushPull, output, veryFast);
	pinSetAlternate(clkPin, altFuncClkPin);
	pinSetAlternate(MOSIPin, altFuncMOSIPin);
	pinSetAlternate(MISOPin, altFuncMISOPin);

	// SPI setup
	spiEnableBus(spi_hw_ch);
	spiSetPolarity(spi_hw_ch,clockPolarity); 
	spiSetPhase(spi_hw_ch,phase);
	spiSetMode(spi_hw_ch, SPI_MASTER);
	spiSetClockPrescaler(spi_hw_ch, prescaler);
	spiSetFrameFormat(spi_hw_ch,frameFormat);
	spiSetSoftwareSlaveManagement(spi_hw_ch,1);
	spiSetInternalSlaveSelect(spi_hw_ch,0);
	spiSetComMode(spi_hw_ch, comMode);
	spiSetClockPrescaler(spi_hw_ch, prescaler);
	spiSetBitFrameLength(spi_hw_ch, SPI_FRAME_LENGTH_8BIT);
}

void spiSetupCH(spi_ch_t *ch, spiCH_t spi_hw_ch, pinNo_t chipselectPin)
{
	ch->pin  = chipselectPin;
	ch->spi = spi_hw_ch;
	pinWrite(chipselectPin, 0);
}

uint8_t spiReadReg(spi_ch_t *spi_ch, uint8_t reg_address) {
	uint8_t buf;
	
	// select target device
	pinWrite(spi_ch->pin,0);
	
	// send address of target register
	spiTrx8BitPolling(spi_ch->spi, reg_address);

	// read from target register
	buf = spiTrx8BitPolling(spi_ch->spi,0x00);

	// release target device
	pinWrite(spi_ch->pin,1);

	return buf;
}

void spiAutoReadBlock(spi_ch_t *spi_ch,
						 uint8_t start_address,
						 uint8_t* buffer,
						 uint8_t buf_len) {
	uint8_t i = 0;

	// select target device
	pinWrite(spi_ch->pin,0);
	
	// send address of starting register
	spiTrx8BitPolling(spi_ch->spi, start_address);

	// read block from device
	for(;i < buf_len;i++) {
		buffer[i] = spiTrx8BitPolling(spi_ch->spi, 0x00);
	}

	// release target device
	pinWrite(spi_ch->pin,1);
}

void spiWriteReg(spi_ch_t *spi_ch,
					uint8_t reg_address,
					uint8_t data) {
	// select target device
	pinWrite(spi_ch->pin,0);
	
	// send address of target register
	spiTrx8BitPolling(spi_ch->spi, reg_address);

	// write to target register
	spiTrx8BitPolling(spi_ch->spi, data);

	// release target device
	pinWrite(spi_ch->pin,1);
}

void spiWriteBlock(spi_ch_t *spi_ch,
					  uint8_t start_address,
					  const uint8_t *data,
					  uint8_t data_len) {
	uint8_t i = 0;

	// select target device
	pinWrite(spi_ch->pin,0);
	
	// send address of starting register
	spiTrx8BitPolling(spi_ch->spi, start_address);

	// read block from device
	for(;i < data_len;i++) {
		spiTrx8BitPolling(spi_ch->spi, data[i]);
	}

	// release target device
	pinWrite(spi_ch->pin,1);
}

void spiWrite8bit(spi_ch_t *spi_ch,
					uint8_t bits)
{
	pinWrite(spi_ch->pin,0);
	spiTrx8BitPolling(spi_ch->spi,bits);
	pinWrite(spi_ch->pin,1);
}

uint8_t spiReadWrite8bit(spi_ch_t *spi_ch,
							uint8_t bits)
{
	uint8_t buf;
	pinWrite(spi_ch->pin,0);
	buf = spiTrx8BitPolling(spi_ch->spi,bits);
	pinWrite(spi_ch->pin,1);
	return buf;
}

void spiWrite16bit(spi_ch_t *spi_ch,
					uint16_t bits)
{
	pinWrite(spi_ch->pin,0);
	if(spiGetFrameFormat(spi_ch->spi) == SPI_MSB_FIRST) {
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
	} else {	
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8));
	}
	pinWrite(spi_ch->pin,1);
}

uint16_t spiReadWrite16bit(spi_ch_t *spi_ch,
							uint16_t bits)
{
	uint16_t buf;

	pinWrite(spi_ch->pin,0);
	if(spiGetFrameFormat(spi_ch->spi) == SPI_LSB_FIRST) {
		buf = spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8)) << 8;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
	} else {	
		buf = spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8)) << 8;
	}
	pinWrite(spi_ch->pin,1);
	
	return buf;
}

void spiWrite32bit(spi_ch_t *spi_ch,
					uint32_t bits)
{
	pinWrite(spi_ch->pin,0);
		
	if(spiGetFrameFormat(spi_ch->spi) == SPI_LSB_FIRST) {
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 24));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 16));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
	} else {	
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 16));
		spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 24));
	}
	pinWrite(spi_ch->pin,1);
}

uint32_t spiReadWrite32bit(spi_ch_t *spi_ch,
							uint8_t bits)
{
	uint32_t buf;

	pinWrite(spi_ch->pin,0);
	if(spiGetFrameFormat(spi_ch->spi) == SPI_LSB_FIRST) {
		buf = spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 24)) << 24;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 16)) << 16;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8)) << 8;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
	} else {	
		buf = spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits));
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 8)) >> 8;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 16)) >> 16;
		buf |= spiTrx8BitPolling(spi_ch->spi,(uint8_t)(bits >> 24)) >> 24;
	}
	pinWrite(spi_ch->pin,1);

	return buf;
}