/**
**************************************************************************************************
* @file		spi.h
* @author	Kerem Yollu & Edwin Koch
* @date		12.03.2022
* @version	1.0
************************************************************************************************** 
* @brief	This is the genral interface for spi.
* 
* **Detailed Description :**
* This the spi interface and belongs to the interface layer.
*
**************************************************************************************************
*/

#ifndef _SPI_H_
#define  _SPI_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "hwd_spi.h"
#include "pin.h"

// TODO: when everything worksmove this into imp.spi.c
//#include "hardwareDescription.h"
//#define SPI_BASE	((SPI_TypeDef *)spiBase_Addr_List[spi_hw_ch])

/*! Enum of possible States*/
typedef enum{
	SPI_SLAVE,
	SPI_MASTER
} spi_mode_t;

/* Enum of clock polarities*/
typedef enum{
	SPI_NONINVERTED,
	SPI_INVERTED
}spi_clkPol_t;

/*! Enum of phases*/
typedef enum{
	SPI_CAPTURE_ON_FIRST_CLK_TRANSITION,
	SPI_CAPTURE_ON_SECCOND_CLK_TRANSITION
} spi_phase_t;

/*! Enum of frame formats*/
typedef enum{
	SPI_MSB_FIRST,
	SPI_LSB_FIRST
}spi_framef_t;

/*! Enum of frame lenghts*/
typedef enum{
	SPI_FRAME_LENGTH_8BIT,
	SPI_FRAME_LENGTH_16BIT
}spi_framel_t;

/*! Enum of communication mode*/
typedef enum{
	SPI_DOUPLEX,
	SPI_SIMPLEX
}spi_comMode_t;

/*! \brief SPI cannel class
 * This class cpntains the pin and spi channel number 
 * select.
 * */
typedef struct{
	pinNo_t pin;	/*!< pin number */	
	spiCH_t spi;	/*!< spi hardware channel number */
}spi_ch_t;

typedef uint8_t (*readReg_t) (uint8_t); 

// generic code

/** TODO: setup init with all attributes
 *  TODO: setup auto pin set alternate function
 * @brief This is the spi hardware channel class
 * @param spi_hw_ch SPI hardware channel
 */
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);


/**
 * \brief Set up SPI channel
 * Set up a SPI channel by passig a hardware SPI channel and a chipselect pin.
 * The chipselect pin will be set to high (chipselect is lowactive).
 * \param *ch pointer to SPI channel
 * \param spi_hw_ch SPI hardware channel
 * \param chipslectPin designated pin for chipslect
 */
void spiSetupCH(
		spi_ch_t *ch,
		spiCH_t spi_hw_ch,
		pinNo_t chipselectPin);

/**
 * \brief Read register
 * Read one byte from a one register with one byte address.
 * \param *spi_ch spi pointer to spi channel object
 * \param reg_address register address
 * \return register content
 */
uint8_t spiReadReg(
		spi_ch_t *spi_ch,
		uint8_t reg_address);

/**
 * \brief Read Block
 * Read a block of data starting at a given start address.
 * This function makes use of the auto register increment of the device to be read from. 
 * The address will be sent once and then data is read.
 * \param *spi_ch pointer to spi cannel object
 * \param start_address start address to the first register
 * \param *buffer pointer to the buffer in which the read content is written into
 * \param buf_len length of buffer
 */
void spiAutoReadBlock(
		spi_ch_t *spi_ch,
		uint8_t start_address,
		uint8_t* buffer,
		uint8_t buf_len);

/**
 * \brief Write register
 * Write one byte to one register with one byte address.
 * \param *spi_ch pointer to spi channel object
 * \param reg_address register address
 * \param data data byte to be written into register
 *
 */
void spiWriteReg(
		spi_ch_t *spi_ch,
		uint8_t reg_address,
		uint8_t data);	
/**
 * \brief  Write data  block
 * Write a block of data starting at a given start address.
 * This function makes use of the auto register increment of the device to be written to. The
 * address will be sent once an then data is written.
 * \param *spi_ch pointer to spi channel object
 * \param start_address start address of the first reister
 * \param *data pointer to data to be written
 * \param data_len length of data to be written
 */
void spiWriteBlock(
		spi_ch_t *spi_ch,
		uint8_t start_address,
		const uint8_t *data,
		uint8_t data_len);


/**
 * \brief write 8 bits
 * \param bits 8 bits
 */
void spiWrite8bit(
		spi_ch_t *spi_ch,
		uint8_t bits);

/**
 * \brief read and write simultainously 8 bits
 * \param bits 8 bits
 * \return 8 bits
 */
uint8_t spiReadWrite8bit(
		spi_ch_t *spi_ch,
		uint8_t bits);

/**
 * \brief write 16 bits
 * \param bits 16 bits
 */
void spiWrite16bit(
		spi_ch_t *spi_ch,
		uint16_t bits);

/**
 * \brief read and write simultainously 16 bits
 * \param bits 16 bits
 * \return 16 bits
 */
uint16_t spiReadWrite16bit(
		spi_ch_t *spi_ch,
		uint16_t bits);

/**
 * \brief write 32 bits
 * \param bits 32 bits
 */
void spiWrite32bit(
		spi_ch_t *spi_ch,
		uint32_t bits);

/**
 * \brief read and write simultainously 32 bits
 * \param bits 32 bits
 * \return 32 bits
 */
uint32_t spiReadWrite32bit(
		spi_ch_t *spi_ch,
		uint8_t bits);

// implementation

/**
 * @brief SPI hardware peripheral reset
 * @param spi_hw_ch SPI hardware channel
 */
void spiReset(
		spiCH_t spi_hw_ch);

/**
 * @brief Enable Bus for SPI
 * @param spi_hw_ch SPI hardware channel
 */
void spiEnableBus(
		spiCH_t spi_hw_ch);

/**
 * @brief Enable SPI hardware channel
 * @param spi_hw_ch SPI hardware channel
 */
void spiEnable(
		spiCH_t spi_hw_ch);

/**
 * @brief Dissable SPI hardware channel
 * @param spi_hw_ch SPI hardware channel
 */
void spiDissable(
		spiCH_t spi_hw_ch);

/**
 * @brief Set SPI operation mode (MASTER or SLAVE)
 * @param spi_hw_ch SPI hardware channel
 * @param mode
 */
void spiSetMode(
		spiCH_t spi_hw_ch,
		spi_mode_t mode);

/**
 * @brief Set SPI clock polarity
 * @param spi_hw_ch SPI hardware channel
 * @param clkPol Clock polarity
 */
void spiSetPolarity(
		spiCH_t spi_hw_ch,
		spi_clkPol_t clkPol);

/**
 * @breif Get SPI polarity
 * @param spi_hw_ch SPI hardware channel
 * @return  polarity
 */
spi_clkPol_t spiGetPolarity(
		spiCH_t spi_hw_ch);

/**
 * @brief Set SPI clock phase
 * @param spi_hw_ch SPI hardware channel
 * @param phase
 */
void spiSetPhase(
		spiCH_t spi_hw_ch,
		spi_phase_t phase);

/**
 * @brief Get SPI clock phase
 * @param spi_hw_ch SPI hardware channel
 * @return phase
 */
spi_phase_t spiGetPhase(
		spiCH_t spi_hw_ch);

/**
 * @brief Set frame length
 * one can choose between 4/8/16 bits. For devices that not support a given frame length an error
 * will be generated.
 * @param spi_hw_ch SPI hardware channel
 * @param framel Frame length
 */
void spiSetBitFrameLength(
		spiCH_t spi_hw_ch,
		spi_framel_t framel);

/**
 * @brief Set SPI frame format
 * @param spi_hw_ch SPI hardware channel
 * @param framef Frame format
 */
void spiSetFrameFormat(
		spiCH_t spi_hw_ch,
		spi_framef_t framef);
/**
 * @brief Get SPI frame format
 * @param spi_hw_ch SPI hardware channel
 * @return Frame format
 */
spi_framef_t spiGetFrameFormat(
		spiCH_t spi_hw_ch);

/**
 * @brief Set Clock Prescaler
 * This is dependent on your target device. Please enter in the correct value.
 * The entered Value will be masked with the maximal number of bits (truncated)
 * @param spi_hw_ch SPI hardware channel
 * @param clkDiv
 */
void spiSetClockPrescaler(
		spiCH_t spi_hw_ch,
		uint32_t clkDiv);

/**
 * @brief Set communication Mode
 * @param spi_hw_ch SPI hardware channel
 * @param comMode
 */
void spiSetComMode(
		spiCH_t spi_hw_ch,
		spi_comMode_t comMode);

/**
 * @brief Get Clock Prescaler
 * @param spi_hw_ch SPI hardware channel
 * @return prescaler
 */
uint32_t spiGetClockPrescaler(
		spiCH_t spi_hw_ch);

#if 0
/**
 * @brief Set software slave management
 * @param logic 1 = enabled, 0 = dissabled
 */
void spiSetSoftwareSlaveManagement(spiCH_t spi_hw_ch, uint8_t logic);
#endif

/**
 * @brief Enable software slave management
 * @param spi_hw_ch SPI hardware channel
 * @param logic  Slave management done by software... 1 = enables / 0 = dissabled
 */
void spiSetSoftwareSlaveManagement(
		spiCH_t spi_hw_ch,
		uint8_t logic);


/**
 * @brief Enable internal slave select
 * @param spi_hw_ch SPI hardware channel
 * @param logic 1 = enable / 0 = dissable
 */
void spiSetInternalSlaveSelect(
		spiCH_t spi_hw_ch,
		uint8_t logic); 

/*!
 * @brief Transmits and receives one byte of data in polling mode
 * @param spi_hw_ch SPI hardware channel
 * @param tx_data 'tx_data' The byte to be transmitted"
 * @return The received data
 */
uint8_t spiTrx8BitPolling(
		spiCH_t spi_hw_ch,
		uint8_t tx_data);


#ifdef __cplusplus
}
#endif

#endif // _SPI_H_