// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// File name    : i2c_drv.c
// Version      : V0.1
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "delay.h"
#include "debug.h"
#include "common.h"
#include "i2c_drv.h"
#include "tsi_drv.h"

static volatile UINT8 i2c_status;
static volatile UINT8  i2c_hs_status=0xff;
static volatile UINT32 g_len_receive = 0;
static volatile UINT32 g_len_send = 0;

static UINT8 *g_data_send ;
static UINT8 *g_data_receive;

#define I2C_RECV_MAX_LEN  0x200

typedef struct i2c_recv_buf
{
	UINT16 rp;//read pointer
	UINT16 wp;//write pointer
	UINT8  data[I2C_RECV_MAX_LEN];//buffer
}I2CRecvBufStruct;

static I2CRecvBufStruct g_I2C1RecvBufStruct = {0, 0};//uart1 buffer
static I2CRecvBufStruct g_I2C2RecvBufStruct = {0, 0};//uart2 buffer
static I2CRecvBufStruct g_I2C3RecvBufStruct = {0, 0};//uart3 buffer

typedef struct i2c_tranmit_buf
{
	UINT16 wp;//write counter
	UINT16 len;//
	UINT8 data[I2C_RECV_MAX_LEN];//buffer
}I2CTransmitBufStruct;

static I2CTransmitBufStruct g_I2CTransmitBufStruct;

//-----------------------------------------------------------------------------
//void I2CMaster_Init(uint8_t I2CAddr)
//
// I2C could be implemented by 2 modes, 1: GPIO mode, 2: Standard I2C mode
// We use Standard I2C mode
//
//


void I2CMaster_Init(I2C_TypeDef *I2Cx, uint8_t preScaler)
{
	I2Cx->CR=0;
	I2Cx->CR|=CR_EN;

	delay(100); // To meet hardware requirement

	I2Cx->CPR=preScaler;

	I2Cx->CR |= (CR_ACKEN|CR_REPSTA);

	I2Cx->PCR =0;
	I2Cx->PCR &=~PCR_PUI2C;		// SDA and SCL with Pull up 0bxxxxxx11
	
	I2Cx->SSHTR=(I2CSHT_SCL_FILTER_EN|I2CSHT_SDA_FILTER_EN) | 0x09;
}


void I2CSlave_Init(I2C_TypeDef *I2Cx, uint16_t addrID)
{
	I2Cx->CR = 0x0;
	I2Cx->CR |= CR_EN ;

	delay(100);		// To meet hardware requirement 
	
	I2Cx->CR |= (CR_ACKEN | CR_IEN);
	
	I2Cx->SAHR = (UINT8)(((addrID>>8)<<1)|0XF0);
	I2Cx->SALR = (UINT8)addrID;
	
	I2Cx->SSHTR=(I2CSHT_SCL_FILTER_EN|I2CSHT_SDA_FILTER_EN) | 0x09;
	
	if (I2Cx==I2C1) 
		NVIC_Init(3, 3, I2C_1_IRQn, 2);
	else if (I2Cx==I2C2) NVIC_Init(3, 3, I2C_2_IRQn, 2);
	else NVIC_Init(3, 3, I2C_3_IRQn, 2);
}

/*******************************************************************************
* Function Name  : I2c_writedata
* Description    :I2C主设备发送多字节
* Input          : -slave_addr      :从设备地址
*                  -buf             :待发送数据
*                  -len             :数据长度
* Output         : None
* Return         : TRUE 执行成功  FALSE 执行失败
******************************************************************************/
bool I2CMaster_SendBytes(I2C_TypeDef *I2Cx,u16 addrID, u8 *buffer, u16 length)
{
    u16 i;
    u8 status = 0xff;
    bool ret = FALSE;

//#ifndef I2C_REPEAT_START
	
//	printf("Tx bytes...\r\n");
	
    if(addrID &0x300)
    {
		//printf("10 bit...\r\n");
    	I2Cx->CDR = (UINT8)(((addrID>>8)<<1)|0xF0);
    	while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
    	I2Cx->CR |= CR_MSMOD;
    	while((I2Cx->CSR & SR_TF) != SR_TF);
    	while((I2Cx->CSR & SR_DACK) != SR_DACK);
    	status = I2Cx->CSR;
    	I2Cx->CDR =(UINT8)(addrID &0xFF);
		while((I2Cx->CSR & SR_TF) != SR_TF);
//		 printf("10 bit...\r\n");
    }
    else
    {
		//send slave addr;
		I2Cx->CDR = addrID & 0xfe;
		while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
		I2C->CR |= CR_MSMOD;
		while((I2Cx->CSR & SR_TF) != SR_TF);
//		 printf("7 bit...\r\n");
    }
//#endif
	 
		
    //send Data
    while((I2Cx->CSR & SR_DACK) != SR_DACK);

	 //printf("10 bit...\r\n");
	 
    status = I2Cx->CSR;

    if((status & SR_ARBL) == SR_ARBL)
    {
        I2Cx->CR &= ~CR_MSMOD;
    }
    else if((status & SR_AACK) == SR_AACK)
    {
        I2Cx->CR &= ~CR_MSMOD;		//clear MSMOD to send STOP
    }
    else
    {
//		printf("send data...\r\n");  
		for(i = 0; i < length; i++)
		{
			I2Cx->CDR = *buffer++;
			while((I2Cx->CSR & SR_TF) != SR_TF);
//			printf("TF\r\n");
			while((I2Cx->CSR & SR_DACK) != SR_DACK);
//			printf("Dack\r\n");			
		}
		ret = TRUE;

//		printf("Okay...\r\n");
    }

    I2Cx->CR &= ~CR_MSMOD;
    while(I2Cx->CSR & SR_BBUSY);		//busy bit clear indicate STOP finish
//	printf("send data finished...\n");
	 
    return ret;
}

/*******************************************************************************
* Function Name  : I2c_readdata
* Description    :I2C主设备发送多字节
* Input          : -slave_addr      :从设备地址
* Output         : -buf             :待接收数据
*                  -len             :数据长度
* Return         : TRUE 执行成功  FALSE 执行失败
******************************************************************************/
bool I2CMaster_ReceiveByte(I2C_TypeDef *I2Cx,u16 addrID, u8 *buffer)
{
   u16 i;
   u8 status;
   bool ret = FALSE;

#ifndef I2C_REPEAT_START
   //slave addr
   if(addrID &0x300)
	{
		I2Cx->CDR = (UINT8)(((addrID>>8)<<1)|0xF0);
		while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
		I2Cx->CR |= CR_MSMOD;
		while((I2Cx->CSR & SR_TF) != SR_TF);

		I2Cx->CDR =(UINT8)(addrID &0xFF);
		while((I2Cx->CSR & SR_TF) != SR_TF);
		while((I2Cx->CSR & SR_DACK) != SR_DACK);

		I2Cx->CR |= CR_REPSTA;
		I2Cx->CDR = (UINT8)(((addrID>>8)<<1)|0xF1);
		while((I2Cx->CSR & SR_RC) != SR_RC);
		I2Cx->CR |= CR_ACKEN;
		while((I2Cx->CSR & SR_DACK) != SR_DACK);
//		printf("Read data...\r\n");
	}
   else
	{
		I2Cx->CDR = addrID | 0x01;
		while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
		I2Cx->CR |= CR_MSMOD;
		while((I2Cx->CSR & SR_RC) != SR_RC);

		I2Cx->CR &= ~CR_ACKEN;

		while((I2Cx->CSR & SR_DACK) != SR_DACK);
	}
#endif

	//receive data
	while((I2Cx->CSR & SR_RC) != SR_RC);

	status = I2Cx->CSR;

	if((status & SR_ARBL) == SR_ARBL)
	{
		I2Cx->CR &= ~CR_MSMOD;		//clear MSMOD to send STOP
	}
	else
	{		 
		if((status & SR_AACK) == SR_AACK)
		{
			I2Cx->CR &= ~CR_MSMOD;		//clear MSMOD to send STOP
//        printf("I2C Master aack error!\n");
		}
		else
		{
			while((I2Cx->CSR & SR_TF) != SR_TF);
			*buffer++ = I2Cx->CDR;
			
			I2Cx->CR &= ~CR_ACKEN;	//no write ACKEN to stop next byte from Slave 
				
			//if((i != (length - 2))) I2Cx->CR |= CR_ACKEN;		//write ACKEN to clear RC status and enable next byte from Slave
			//else I2Cx->CR &= ~CR_ACKEN;	//no write ACKEN to stop next byte from Slave 

			ret = TRUE;
		}
	}

	//delayUs(10000);
	I2C->CR &= ~CR_MSMOD;
	while(I2C->CSR & SR_BBUSY);		//busy bit clear indicate STOP finish

	return ret;
}



/*******************************************************************************
* Function Name  : I2c_readdata
* Description    :I2C主设备发送多字节
* Input          : -slave_addr      :从设备地址
* Output         : -buf             :待接收数据
*                  -len             :数据长度
* Return         : TRUE 执行成功  FALSE 执行失败
******************************************************************************/
bool I2CMaster_ReceiveBytes(I2C_TypeDef *I2Cx,u16 addrID, u8 *buffer, u16 length)
{
   u16 i;
   u8 status;
   bool ret = FALSE;

#ifndef I2C_REPEAT_START
   //slave addr
   if(addrID &0x300)
	{
		I2Cx->CDR = (UINT8)(((addrID>>8)<<1)|0xF0);
		while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
		I2Cx->CR |= CR_MSMOD;
		while((I2Cx->CSR & SR_TF) != SR_TF);

		I2Cx->CDR =(UINT8)(addrID &0xFF);
		while((I2Cx->CSR & SR_TF) != SR_TF);
		while((I2Cx->CSR & SR_DACK) != SR_DACK);

		I2Cx->CR |= CR_REPSTA;
		I2Cx->CDR = (UINT8)(((addrID>>8)<<1)|0xF1);
		while((I2Cx->CSR & SR_RC) != SR_RC);
		I2Cx->CR |= CR_ACKEN;
		while((I2Cx->CSR & SR_DACK) != SR_DACK);
		printf("Read data...\r\n");
	}
   else
	{
		I2Cx->CDR = addrID | 0x01;
		while((I2Cx->CSR & SR_BBUSY) == SR_BBUSY);
		I2Cx->CR |= CR_MSMOD;
		while((I2Cx->CSR & SR_RC) != SR_RC);
		if(length > 1)
		{
			I2Cx->CR |= CR_ACKEN;
		}
		else
		{
			I2Cx->CR &= ~CR_ACKEN;
		}
		while((I2Cx->CSR & SR_DACK) != SR_DACK);
	}
#endif

	//receive data
	while((I2Cx->CSR & SR_RC) != SR_RC);

	status = I2Cx->CSR;

	if((status & SR_ARBL) == SR_ARBL)
	{
		I2Cx->CR &= ~CR_MSMOD;		//clear MSMOD to send STOP
	}
	else
	{		 
		if((status & SR_AACK) == SR_AACK)
		{
			I2Cx->CR &= ~CR_MSMOD;		//clear MSMOD to send STOP
//        printf("I2C Master aack error!\n");
		}
		else
		{
			for(i = 0; i < length; i++)
			{
			//*buffer++ = I2CMaster_ReceiveData(i, length);
			  
				while((I2Cx->CSR & SR_TF) != SR_TF);
				*buffer++ = I2Cx->CDR;
				
				if((i != (length - 2))) I2Cx->CR |= CR_ACKEN;		//write ACKEN to clear RC status and enable next byte from Slave
				else I2Cx->CR &= ~CR_ACKEN;	//no write ACKEN to stop next byte from Slave 
			}
			
			printf("Read data okay...\r\n");
			for(i = 0; i < length; i++)
			  printf("%02x ",*(buffer-length+i));	// 
			
			//while(1);
			
			ret = TRUE;
		}
	}

	//delayUs(10000);
	I2C->CR &= ~CR_MSMOD;
	while(I2C->CSR & SR_BBUSY);		//busy bit clear indicate STOP finish

	return ret;
}

void I2CSlave_SendBytes(I2C_TypeDef *I2Cx,uint8_t *buffer,uint16_t length)
{
	g_I2CTransmitBufStruct.wp=0;
	*g_I2CTransmitBufStruct.data=*buffer;
	g_I2CTransmitBufStruct.len=length;
}

bool I2CSlave_ReceiveByte(I2C_TypeDef *I2Cx,uint8_t *buffer,uint16_t length)
{
	I2CRecvBufStruct *I2CRxBufStruct;
	if (I2Cx == I2C1)
		 I2CRxBufStruct = &g_I2C1RecvBufStruct;
	else if (I2Cx == I2C2)
		I2CRxBufStruct = &g_I2C2RecvBufStruct;
	else
		I2CRxBufStruct = &g_I2C3RecvBufStruct;

	if (I2CRxBufStruct->wp != I2CRxBufStruct->rp)
	{
		*buffer = I2CRxBufStruct->data[I2CRxBufStruct->rp];
		I2CRxBufStruct->rp += 1;
		if (I2CRxBufStruct->rp >= I2C_RECV_MAX_LEN)
			I2CRxBufStruct->rp = 0;
		return TRUE;
	}
	return FALSE;
}

bool I2CSlave_ReceiveBytes(I2C_TypeDef *I2Cx,uint8_t *buffer,uint16_t length)
{
	uint16_t dataLen,i;
	I2CRecvBufStruct *I2CRxBufStruct;
	if (I2Cx == I2C1)
		 I2CRxBufStruct = &g_I2C1RecvBufStruct;
	else if (I2Cx == I2C2)
		I2CRxBufStruct = &g_I2C2RecvBufStruct;
	else
		I2CRxBufStruct = &g_I2C3RecvBufStruct;

	if (I2CRxBufStruct->wp < I2CRxBufStruct->rp)
		dataLen=I2CRxBufStruct->wp+I2C_RECV_MAX_LEN-I2CRxBufStruct->rp;
	else dataLen=I2CRxBufStruct->wp-I2CRxBufStruct->rp;
	
	if (dataLen>=length)
	{
		for (i=0;i<dataLen;i++)
		{
			*buffer++ = I2CRxBufStruct->data[I2CRxBufStruct->rp];
			I2CRxBufStruct->rp += 1;
			if (I2CRxBufStruct->rp >= I2C_RECV_MAX_LEN)
				I2CRxBufStruct->rp = 0;
		}
		//printf("wp=%d rp=%d %d \r\n",UARTxRecvBufStruct->wp,UARTxRecvBufStruct->rp,dataLen);
		return TRUE;
	}
	return FALSE;
}

/*******************************************************************************
* Function Name  : I2C_ISR
* Description    :I2C从中断
* Input          : -None
* Output         : -None
*
* Return         : -None
******************************************************************************/
// For i2c_demo.c
static void I2C_ISR2(I2C_TypeDef *I2Cx)
{
	volatile UINT8 tmp =0;
	i2c_status = I2Cx->CSR;
	i2c_hs_status = I2Cx->SHSIR;

	if((i2c_hs_status & SHIR_SLV_HS) != SHIR_SLV_HS )
	{
		if((i2c_status & SR_AASLV) == SR_AASLV )		//The current slave device is addressed by master
		{
			if((i2c_status & SR_RC) == SR_RC) // RECEIVER interrupt
			{
				if((I2Cx->CSR & SR_TF) == SR_TF) // transfer complete
				{
					g_data_receive[g_len_receive] = I2Cx->CDR;
					g_len_receive++;
					I2Cx->CR |= CR_ACKEN;
				}
				else I2Cx->CR &= ~CR_AMIE;
			}
			else // RECEIVER interrupt
			{
				if((i2c_status & SR_TF) == SR_TF) // transfer complete
				{
					if((i2c_status & SR_DACK)==SR_DACK)
					{
						I2Cx->CDR =g_data_send[g_len_send];
						g_len_send++;
						I2Cx->CR &= ~CR_AMIE;
					}
					else
					{
						tmp = I2Cx->CDR;
						tmp ++;//add for warning
					}
				}
				else I2Cx->CR &= ~CR_AMIE;
			}
		}
		else
		{
			//asm("bkpt");
		}
	}
	else
	{
		I2Cx->SSHTR =(I2Cx->SSHTR&0xc0)|0x1;
		I2Cx->SSHTR =I2Cx->SSHTR|0x1;

		I2Cx->SHSIR |=SHIR_SLV_HS;
	}
}


/*******************************************************************************
* Function Name  : I2C_ISR
* Description    :I2C从中断
* Input          : -None
* Output         : -None
*
* Return         : -None
******************************************************************************/
static void I2C_ISR(I2C_TypeDef *I2Cx, I2CRecvBufStruct *ringBuffer)
{
	volatile UINT8 tmp =0;
	i2c_status = I2Cx->CSR;
	i2c_hs_status = I2Cx->SHSIR;

	if((i2c_hs_status & SHIR_SLV_HS) != SHIR_SLV_HS )
	{
		if((i2c_status & SR_AASLV) == SR_AASLV )		//The current slave device is addressed by master
		{
			if((i2c_status & SR_RC) == SR_RC) // Receive interrupt
			{
				if((I2Cx->CSR & SR_TF) == SR_TF) // transfer complete
				{
					
					//g_data_receive[g_len_receive] = I2Cx->CDR;
					//g_len_receive++;
					
					ringBuffer->data[ringBuffer->wp] = I2Cx->CDR;
					ringBuffer->wp += 1;
					if (ringBuffer->wp >= I2C_RECV_MAX_LEN)
						ringBuffer->wp = 0;
									
					I2Cx->CR |= CR_ACKEN;
				}
				else I2Cx->CR &= ~CR_AMIE;
			}
			else // Transmit interrupt
			{
				if((i2c_status & SR_TF) == SR_TF) // transfer complete
				{
					if((i2c_status & SR_DACK)==SR_DACK)
					{
						//I2Cx->CDR =g_data_send[g_len_send];
						//g_len_send++;
						
						if (g_I2CTransmitBufStruct.wp<g_I2CTransmitBufStruct.len)
						{
							I2Cx->CDR =g_I2CTransmitBufStruct.data[g_I2CTransmitBufStruct.wp];
							g_I2CTransmitBufStruct.wp++;
							
							I2Cx->CR &= ~CR_AMIE;
						}
								
						
					}
					else
					{
						tmp = I2Cx->CDR;
						tmp ++;//add for warning
					}
				}
				else I2Cx->CR &= ~CR_AMIE;
			}
		}
		else
		{
			//asm("bkpt");
		}
	}
	else
	{
		I2Cx->SSHTR =(I2Cx->SSHTR&0xc0)|0x1;
		I2Cx->SSHTR =I2Cx->SSHTR|0x1;

		I2Cx->SHSIR |=SHIR_SLV_HS;
	}
}

void I2C1_IRQHandler(void)
{
	//I2C_ISR2(I2C1);
	I2C_ISR(I2C1,&g_I2C1RecvBufStruct);
}

void I2C2_IRQHandler(void)
{
	//I2C_ISR(I2C2);
	I2C_ISR(I2C2,&g_I2C2RecvBufStruct);
}

void I2C3_IRQHandler(void)
{
	//I2C_ISR(I2C3);
	I2C_ISR(I2C3,&g_I2C3RecvBufStruct);
}


/*******************************************************************************
* Function Name  : i2c_slave_trans
* Description    :I2C从设备收发送多字节
* Input          : -s_buf           :接收数据
* 				   -s_len           :数据长度
* Output         : -r_buf           :待接收数据
*                  -r_len           :数据长度
* Return         : None
******************************************************************************/
void i2c_slave_trans(UINT8* r_buf,UINT16 r_len,UINT8* s_buf,UINT16 s_len)
{
	g_data_send = s_buf;
	g_data_receive = r_buf;

	while(1)
	{
		if(r_len==g_len_receive)
		{
			g_len_receive=0x0;

			break;
		}

		if(s_len==g_len_send)
		{
			g_len_send=0x0;

			break;
		}

	}
}

/*******************************************************************************
* Function Name  : i2c_gpio_init
* Description    :I2C GPIO Init
* Input          :- None
*
* Output         : None
* Return         : 无
******************************************************************************/
void i2c_gpio_init(void)
{
	I2C_ConfigGpio(I2C_SDA, GPIO_OUTPUT);
	I2C_ConfigGpio(I2C_SCL, GPIO_OUTPUT);
	I2C_WriteGpioData(I2C_SDA, Bit_SET);
	I2C_WriteGpioData(I2C_SCL, Bit_SET);
}

/*******************************************************************************
* Function Name  : I2C_ConfigGpio
* Description    : I2C配置成GPIO用途
* Input          : - I2C_PINx：SPI对应的PIN脚，取值I2C_SCL、I2C_SDA
*                  - GPIO_Dir：设置GPIO方向   GPIO_OUTPUT：输出  GPIO_INPUT：输入
*
* Output         : None
* Return         : None
******************************************************************************/
void I2C_ConfigGpio(I2C_PIN I2C_PINx, UINT8 GPIO_Dir)
{
	/* Check the parameters */
	assert_param(IS_I2C_PINx(I2C_PINx));
	assert_param(IS_GPIO_DIR_BIT(GPIO_Dir));
	
	TSI_CHEN &= ~0x00000060;		//SDA、SCL和TSI CH6、CH5复用，禁止TSI CH6、CH5

	I2C->PCR |= 0xc0;//config gpio
	if (GPIO_Dir == GPIO_OUTPUT)
	{
		I2C->DDR |= (1<<I2C_PINx);//output
	}
	else if (GPIO_Dir == GPIO_INPUT)
	{
		I2C->DDR &= (~(1<<I2C_PINx));//input
	}
}

/*******************************************************************************
* Function Name  : I2C_ReadGpioData
* Description    : 获取I2C_PINx对应引脚的电平
* Input          : - I2C_PINx：SPI对应的PIN脚，取值I2C_SCL、I2C_SDA
*
* Output         : None
* Return         : Bit_SET:高电平  Bit_RESET：低电平
******************************************************************************/
UINT8 I2C_ReadGpioData(I2C_PIN I2C_PINx)
{
	UINT8 bitstatus = 0x00;

	/* Check the parameters */
	assert_param(IS_I2C_PINx(I2C_PINx));

	bitstatus = I2C->PDR;
	if (bitstatus &(Bit_SET<<I2C_PINx))
		bitstatus = Bit_SET;
	else
		bitstatus = Bit_RESET;

	return bitstatus;
}

/*******************************************************************************
* Function Name  : I2C_WriteGpioData
* Description    : 设置I2C_PINx对应引脚的电平
* Input          : - I2C_PINx：SPI对应的PIN脚，取值I2C_SCL、I2C_SDA
*                  - bitVal：设置的电平，Bit_SET：设置为高电平  Bit_RESET：设置为低电平
*
* Output         : None
* Return         : None
******************************************************************************/
void I2C_WriteGpioData(I2C_PIN I2C_PINx, UINT8 bitVal)
{
	/* Check the parameters */
	assert_param(IS_I2C_PINx(I2C_PINx));

	if (bitVal == Bit_SET)
		I2C->PDR |= (Bit_SET<<I2C_PINx);
	else
		I2C->PDR &= (~(Bit_SET<<I2C_PINx));
}
