/**
  **************************************************************************************************
  * @file		i2c.c
  * @author		Kerem Yollu & Edwin Koch
  * @date		18.07.2022
  * @version	1.0
  ************************************************************************************************** 
  * @brief		I2C communitation based on the Standart I2C Protocol V7 Defined by NXP/Philips : 
  *				following third Party Protocols based on I2C Bus are not going to be implemented : C-BUS SMBUS PMBUS IPMI DDC ATCA		
  *				This will also not have a I3C support for the forseable futrue. This code is generated for the stm32f4xK6 series of mcu for stm
  *				
  * **Detailed Description :**
  * 
  *	I2C communitation based on the Standart I2C Protocol V7 Defined by NXP/Philips : 
  *	following third Party Protocols based on I2C Bus are not going to be implemented : C-BUS SMBUS PMBUS IPMI DDC ATCA		
  *	This will also not have a I3C support for the forseable futrue. 
  *
  * @todo 
  * - 18.07.2021 : Implement the i2c.c 
  **************************************************************************************************
*/

#include "i2c.h"
#define I2C_BASE ((I2C_TypeDef*)i2cBase_Addr_List[i2cHardware->channelNo])

void i2cInit(i2c_t *i2cHardware)
{
	i2cReset(i2cHardware);
	
	// Enables the i2c bus
	RCC->APB1ENR |= (1 << i2cBus_En_bitPos[i2cHardware->channelNo]);

	// Make sure that the periferal is disabled.	
	I2C_BASE->CR1 &= ~I2C_CR1_PE;

	//Configure analog filter. Anlalog filter is on
	I2C_BASE->CR1 |= I2C_CR1_ANFOFF; 
	
	//Configure NoStrech Streching mode is disabled (slave only) 
	//I2C_BASE->CR1 |= I2C_CR1_NOSTRETCH; 

	//Configure the clock
	I2C_BASE->TIMINGR = i2cHardware->timing; 

	//Automatic end mode (master mode) Enablede as default 
	//I2C_BASE->CR2 &= ~I2C_CR2_AUTOEND; 
	//I2C_BASE->CR2 |= I2C_CR2_AUTOEND; 

	if(i2cHardware->mode == i2cModeMaster)
	{
		if(i2cHardware->addressSize == i2cAddressSizeTenBits)
		{
			I2C_BASE->CR2 |= I2C_CR2_ADD10; // 10 Bit addressing
			I2C_BASE->CR2 &= ~I2C_CR2_HEAD10R; // 7 Bit header read turned on DEFAULT
		}
		else
		{
			I2C_BASE->CR2 &= ~I2C_CR2_ADD10; // 7 Bit addressing DEFAULT
			I2C_BASE->CR2 |= I2C_CR2_HEAD10R; // 7 Bit header read turned off DEFAULT
		}
	}

	//activating the Perriferal. 
	I2C_BASE->CR1 |= I2C_CR1_PE;

	i2cCR1= I2C_BASE->CR1;
	i2cCR2= I2C_BASE->CR2;
	i2cISR= I2C_BASE->ISR;

	i2cHardware->state = i2cInitialized;
}	

void i2cReset(i2c_t *i2cHardware) 
{
	RCC->APB1RSTR |= (1 << i2cBus_Rst_bitPos[i2cHardware->channelNo]);
	RCC->APB1RSTR &= ~(1 << i2cBus_Rst_bitPos[i2cHardware->channelNo]);
}

void i2cRead(	i2c_t *i2cHardware, 
				uint16_t *devAddress, 
				uint8_t *registerAddress, 
				uint8_t *data, 
				uint8_t dataLenght)
{
	i2cHardware->state = i2cBusy;
	switch(i2cHardware->mode)
	{
		case i2cModeMaster: // Implement for loops for more than one byte.  
			i2cMasterRecieve(i2cHardware, devAddress, registerAddress, data);
			break; 
		case i2cModeSlave: 
			i2cSlaveRecieve(i2cHardware, devAddress, registerAddress, data);
			break; 
		case i2cModeMultyMaster:
			// TO implement
			break;
		default: 
			i2cThrowError(1); 	
	}
}

// this function still doesn't implment 10 bit oopeartion TODO
uint8_t i2cMasterRecieve(i2c_t *i2cHardware, uint8_t devAddress, uint8_t registerAddress, uint8_t *data)
{
	// Wait until no communication is ongoign
	while((I2C_BASE->ISR & (I2C_ISR_BUSY))==I2C_ISR_BUSY);
	
	i2cHardware->state = i2cRecieving;
	
	//Slave address
	I2C_BASE->CR2 |= devAddress << 1; // The bit no 0 is not taken in concideration in 7bit mode	
	
	//Write Mode
	I2C_BASE->CR2 &= ~I2C_CR2_RD_WRN;

	//Set Buffer size
	I2C_BASE->CR2 |= 1 << I2C_CR2_NBYTES_Pos;

	//Generate start condition
	I2C_BASE->CR2 |= I2C_CR2_START; 
	
	//Wait until the start condition in generated. 
	while(!(I2C_BASE->ISR & (I2C_ISR_BUSY)));
	
	//Check if the TX buffer is empty
	while(!(I2C_BASE->ISR & (I2C_ISR_TXE)));

	//Register to be sent
	I2C_BASE->TXDR |= registerAddress; 

	//Wait untill all is Transfered
	while(!(I2C_BASE->ISR & (I2C_ISR_TXE)));

	// working well up to this point
	
	//Is the tranfes complete ?
	while(!(I2C_BASE->ISR & (I2C_ISR_TC)));
	
	//Read Mode	
	I2C_BASE->CR2 |= I2C_CR2_RD_WRN;
	
	//Set Buffer size
	I2C_BASE->CR2 |= 1 << I2C_CR2_NBYTES_Pos;

	//New start condition to read from the slave	
	I2C_BASE->CR2 |= I2C_CR2_START; 
	
	//Wait untill all is Transfered
	while(!(I2C_BASE->ISR & (I2C_ISR_TXE)));

	//Wait for the input buffer to be full
	while(!(I2C_BASE->ISR & (I2C_ISR_RXNE)));
	
	//NACK is generated automatically in master mode.
	i2cCR1= I2C_BASE->CR1;
	i2cCR2= I2C_BASE->CR2;
	i2cISR= I2C_BASE->ISR;
	
	// Sned stop command	
	I2C_BASE->CR2 |= I2C_CR2_STOP; 

	//read dtaa from the input register to clear it out	
	data = I2C_BASE->RXDR;	
	return data; 
}
