/*******************************************************************************
  * @file    KungFu8_I2C.c
  * @author  ChipON Application Team
  * @version V0.0.1
  * @datef
  * @brief
  * *
  ******************************************************************************
  */
#include "includes.h"
#include "KungFu8_I2C.h"


//********************************************************************************************

s_I2C_RAMBuffer   SSCII2C_DateStruct;
//********************************************************************************************

/********************************************************************************************
*						I2C
*
*SCL对应引脚								SDA对应引脚
*  P1.0									P1.1
*  P0.2									P0.1
*
**********************************************************************************************/

/***********************************************************************************
 * 函数名     	：I2C_Delayus
 * 函数功能	：延时时间
 * 入口参数	：延时时间dealy_us
 * 返回         ：无
 *
 **********************************************************************************/
#define I2C_Delayus(delay_us)  Delay_Wait_CLK_Sample(delay_us)

/***********************************************************************************
 * 函数名     	：I2C_Idle
 * 函数功能	：等待I2C模块空闲(主控模式下)
 * 入口参数	：无
 * 返回         ：无
 * 等待I2C模块空闲(主控模式下)
 **********************************************************************************/
void	I2C_Idle(void)
{
	// 空闲的条件：应答序列未开或以完成、接收空闲、停止条件重启条件启动条件空闲,发送接收
	while(  SSCI1CTL2&0x1F || SSCI1RW );
	SSCI1IF=0;
	BCL1IF=0;      //总线冲突中断清零
}
/***********************************************************************************
 * 函数名     	：I2C_Start
 * 函数功能	：启动I2C(主控模式下)
 * 入口参数	：无
 * 返回         ：无
 * 产生I2C启动条件
 **********************************************************************************/
void I2C_Start(void)
{
	I2C_Idle();				// 等待I2C空闲
	STARTEN1=1; 				// 发送起始，I2C主控模式
	while(STARTEN1); 		// 等待起始完成
	SSCI1IF=0;  				// 清除起始完成的标记
	I2C_Delayus(10);  		// 给予使用启动中断的响应时间，根据从机响应速度
}
/***********************************************************************************
 * 函数名     	：I2C_ReStart
 * 函数功能	：重启动I2C(主控模式下)
 * 入口参数	：无
 * 返回         ：无
 * 产生I2C重新启动条件
 **********************************************************************************/
void I2C_ReStart(void)
{
	I2C_Idle();     //等待空闲
	RESTARTEN1=1;
	while(RESTARTEN1);
	SSCI1IF=0;
}
/***********************************************************************************
 * 函数名     	：I2C_Stop
 * 函数功能	：停止I2C(主控模式下)
 * 入口参数	：无
 * 返回         ：无
 * 产生I2C停止条件
 **********************************************************************************/
void I2C_Stop(void)
{
	I2C_Idle();				// 等待I2C空闲
	STOPEN1=1;
	while(STOPEN1);
	SSCI1IF=0;
	I2C_Delayus(10);  		// 给予使用停止中断的响应时间，根据从机响应速度
}
/***********************************************************************************
 * 函数名     	：I2C_SendAck
 * 函数功能	：I2C发送应答数据(主控模式下)
 * 入口参数	：
 * 返回         ：无
 * I2C发送应答数据  0应答  1不应答
 **********************************************************************************/


void I2C_RSendXAck()
{
//	// 应答与否配置,提前配置
//	SSCI1ACKDAT=0;
//	SSCI1ACKDAT=1;
	// 启动应答序列
	SSCI1ACKEN=1;
	// 等待应答接收
	while(SSCI1ACKEN);
	// 应答接收的中断标志清零
	SSCI1IF=0;
}

void I2C_SendAck()
{
	// 应答与否配置
	SSCI1ACKDAT=0;
	// 使能应答发送
	SSCI1ACKEN=1;
	// 等待应答接收
	while(SSCI1ACKEN);
	// 应答接收的中断标志清零
	SSCI1IF=0;
}

void I2C_SendNAck()
{
	// 应答与否配置
	SSCI1ACKDAT=1;
	// 使能应答发送
	SSCI1ACKEN=1;
	// 等待应答接收
	while(SSCI1ACKEN);
	// 应答接收的中断标志清零
	SSCI1IF=0;
}
/***********************************************************************************
 * 函数名     	：I2C_SendData
 * 函数功能	：I2C发送单字节数据(主控模式下)
 * 入口参数	：待发送的单字节数据Data
 * 返回         ：
 * I2C发送数据
 **********************************************************************************/
void I2C_SendData(uint8 sData)
{
//	// 判断也是代码消耗，直接写 写错误的清零，正常逻辑不不会产生写冲突
//	SSCIWCFL=0;
//	// 确保无中断标志
//	SSCIIF=0;
//	// 发送数据
//	SSCIBUFR=sData;
//	while (!SSCIIF);//等待发送完成,因为要识别从机的应答状态，等待bit9结束才结束发送函数，而不是发送状态空的SSCIBUF
//	SSCIIF=0;


//::	注：以上逻辑看似无问题，因为存在优化处理，参数R0可以直接使用
//::	    但会将发送数据   SSCIBUFR=Data;优先执行，而SSCIWCFL=0;和SSCIIF=0;后执行
//::	 如果需要这样的逻辑，建立volatile uint8 的局部变量，并中转赋值
//::	即volatile uint8 sdatabuf=sData,随后   SSCIBUFR=sdatabuf  ，否则存在后续发送失效问题

//		volatile uint8 sdatabuf=sData;
//		// 判断也是代码消耗，直接写 写错误的清零，正常逻辑不不会产生写冲突
//		SSCIWCFL=0;
//		// 确保无中断标志
//		SSCIIF=0;
//		// 发送数据
//		SSCIBUFR=sdatabuf;
//		while (!SSCIIF);//等待发送完成，因为要识别从机的应答状态，等待bit9结束才结束发送函数，而不是发送状态空的SSCIBUF
//		SSCIIF=0;

	SSCI1BUFR=sData;   // 建立在主机避免写冲突情况下,状态机下SSCIIF=0
	while (!SSCI1IF);
	SSCI1WCFL=0;	  // 主机避免写冲突，软件直接清零
	SSCI1IF=0;		  // 主机一般不开启中断
}
/***********************************************************************************
 * 函数名     	：I2C_ReceiveData
 * 函数功能	：I2C接收数据(主控模式下)
 * 入口参数	：无
 * 返回         ：接收的单字节数据Recdata
 * I2C接收数据
 **********************************************************************************/
uint8 I2C_ReceiveData(void)
{
	// 判断也是代码消耗，直接清零接收溢出标志
	SSCI1OV=0;
	// 接收数据 使能
	SSCI1RCEN=1;
	// 先中断完成的接收
	while(!SSCI1IF);
	SSCI1IF=0;
	// 返回数据，上层应用控制是否数据的应答
	return SSCI1BUFR;
}

/***********************************************************************************
 * 函数名     	：I2C_Write_Data
 * 函数功能	：给从机下发写内容(主控模式下)
 * 入口参数	：SSCII2C_DateStruct
 * 返回         ：无
 *
 **********************************************************************************/
uint8  I2C_Write_s_Data()
{
	uint8   i;
	// 判断空闲模式
	I2C_Idle();				// 等待I2C空闲
	// 启动I2C,并考虑给予适当延迟
	I2C_Start();
	//============================================
	// 发送写期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrH);  if(SSCI1ACKSTA) goto write_s_end_I2C_Write_s_Data;
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrL);  if(SSCI1ACKSTA) goto write_s_end_I2C_Write_s_Data;
	}
	else
	{// 7bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrL);  if(SSCI1ACKSTA)  goto write_s_end_I2C_Write_s_Data;
	}
	//============================================
	// 地址命令 或资源地址
	I2C_SendData(SSCII2C_DateStruct.Command );  	   if(SSCI1ACKSTA)  goto write_s_end_I2C_Write_s_Data;
	//============================================
	// 写数据，buffer有10个字节的数据
	for(i=0;i<SSCII2C_DateStruct.length;i++)
	{
		I2C_SendData(SSCII2C_DateStruct.buffer[i]);	   if(SSCI1ACKSTA)  goto write_s_end_I2C_Write_s_Data;
	}
	// 发送结束
	I2C_Stop();
	return (0);
write_s_end_I2C_Write_s_Data:
	I2C_Stop();
	return (1);  //从机未应答，返回1
}
/***********************************************************************************
 * 函数名     	：I2C_Write_Data
 * 函数功能	：给从机下发写内容(主控模式下)
 * 入口参数	：从机目标地址，要写入长度，数据指针
 * 返回         ：无
 *
 **********************************************************************************/
uint8  I2C_Write_Data(uint8 cAddr,uint8 Length,uint8 * DataAddressPoint)
{
	uint8   i;
	// 判断空闲模式
	I2C_Idle();				// 等待I2C空闲
	//============================================
	// 启动I2C,并考虑给予适当延迟
	I2C_Start();
	//============================================
	// 发送写期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(I2C_Add_H | 0x00);  	if(SSCI1ACKSTA) goto write_p_end_I2C_Write_Data;
		I2C_SendData(I2C_Add_L);		  	if(SSCI1ACKSTA) goto write_p_end_I2C_Write_Data;
	}
	else
	{// 7bit
		I2C_SendData(I2C_Add_L | 0x00);		 if(SSCI1ACKSTA) goto write_p_end_I2C_Write_Data;
	}
	//============================================
	// 地址命令 或资源地址
	I2C_SendData(cAddr );					  if(SSCI1ACKSTA) goto write_p_end_I2C_Write_Data;
	//============================================
	// 写数据
	for(i=0;i<Length;i++)
	{
		I2C_SendData(*(DataAddressPoint+i));	  if(SSCI1ACKSTA) goto write_p_end_I2C_Write_Data;
	}
	// 发送结束
	I2C_Stop();
	return (0);
write_p_end_I2C_Write_Data:
	I2C_Stop();
	return (1);
}
/***********************************************************************************
 * 函数名     	：I2C_Read_s_Data
 * 函数功能	：从从机下读取内容(主控模式下)
 * 入口参数	：SSCII2C_DateStruct
 * 返回         ：无
 *
 **********************************************************************************/
uint8  I2C_Read_s_Data()
{
	uint8   i;
	// 判断空闲模式
	I2C_Idle();				// 等待I2C空闲
	// 启动I2C,并考虑给予适当延迟
	I2C_Start();
	//============================================ 写地址 定位命令或地址
	// 发送写期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrH);  if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrL);  if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
	}
	else
	{// 7bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrL);  if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
	}
	//============================================
	// 地址命令 或资源地址
	I2C_SendData(SSCII2C_DateStruct.Command );  		if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
	//============================================   重启命令准备读取
	// 重新启动进入读取模式
	I2C_ReStart();
	//============================================   从机寻址的读
	// 发送写期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrH|0x01);  if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
	 // 读模式不产生SSCIUA信号，故仅传递高地址并读取模式，状态机就转入数据了。不要发送低位地址
	}
	else
	{// 7bit
		I2C_SendData(SSCII2C_DateStruct.i2cDevAddrL|0x01);  if(SSCI1ACKSTA)  goto read_s_end_I2C_Read_s_Data;
	}
	//============================================  数量下的循环读取
	// 数量下内容的读取
	for(i=0;i<SSCII2C_DateStruct.length;i++)
	{
		SSCII2C_DateStruct.buffer[i]=I2C_ReceiveData();     //ReceiveData()返回SSCIBUFR内容

		// 读取的最后1个内容主机不给予应答，否则应答
		if(i==(SSCII2C_DateStruct.length-1))
		{
			SSCI1ACKDAT=1;  // 不应答  ，最后1次必须不应答，否则停止会产生错误，后续i2c不能正常工作
		}
		else
		{
			SSCI1ACKDAT=0;  // 应答
		}
		I2C_RSendXAck();
	}
	// 发送结束
	I2C_Stop();
	return (0);
read_s_end_I2C_Read_s_Data:
	I2C_Stop();
	return (1);
}
/***********************************************************************************
 * 函数名     	：I2C_Read_Data
 * 函数功能	：从从机下读取内容(主控模式下)
 * 入口参数	：从机目标地址，要写入长度，数据指针
 * 返回         ：无
 *
 **********************************************************************************/
uint8 I2C_Read_Data(uint8 cAddr,uint8 Length,uint8 * TempAddressPoint)
{
	uint8   i;

	// 判断空闲模式
	I2C_Idle();			// 等待I2C空闲
	// 启动I2C,并考虑给予适当延迟
	I2C_Start();
	//============================================ 写地址 定位命令或地址
	// 发送写期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(I2C_Add_H | 0x00);  	if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
		I2C_SendData(I2C_Add_L);  			if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
	}
	else
	{// 7bit
		I2C_SendData(I2C_Add_L | 0x00);  	if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
	}
	//============================================
	// 地址命令 或资源地址
	I2C_SendData(cAddr);  					if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
	//============================================  重启命令准备读取
	// 重新启动进入读取模式
	I2C_ReStart();
	//============================================   从机寻址的读
	// 发送读期望的 从机地址
	if(isSampleMode10bits)
	{// 10bit
		I2C_SendData(I2C_Add_H | 0x01);		  if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
		// 读模式不产生SSCIUA信号，故仅传递高地址并读取模式，状态机就转入数据了。不要发送低位地址
	}
	else
	{// 7bit
		I2C_SendData(I2C_Add_L | 0x01);		  if(SSCI1ACKSTA) goto read_p_end_I2C_Read_Data;
	}
	//============================================  数量下的循环读取
	// 数量下内容的读取
	for(i=0;i<Length;i++)
	{
		TempAddressPoint[i]=I2C_ReceiveData();
		// 读取的最后1个内容主机不给予应答，否则应答
		if(i==(Length-1))
		{
			SSCI1ACKDAT=1;  // 不应答  ，最后1次必须不应答，否则停止产生会错误，后续不能i2c正常工作
		}
		else
		{
			SSCI1ACKDAT=0;  // 应答
		}
		I2C_RSendXAck();
	}
	// 发送结束
	I2C_Stop();
	return (0);
read_p_end_I2C_Read_Data:
	I2C_Stop();
	return (1);
}
/***********************************************************************************
 * 函数名     	：I2C_Configuration
 * 函数功能	：设置I2C 配置模式的初始化功能函数
 * 入口参数	：无
 * 返回         ：无
 *
 **********************************************************************************/
void I2C_Configuration_Slave(void)
{
	// 根据需要优先关闭SSCI1的I2C模块
	SSCI1EN=0;
	// 模块的引脚配置配置为数字输入，这里选择了P02和P01
	APFCTL10 &= 0x0F;
	APFCTL10 |= 0x50; 		 //管脚复用SCL:P02 SDA:P01
	ANS01=0;	ANS02=0;
	TR01=1;		TR02=1;

//#####################   从机模式   #######################################
	// 广播的使能
	SSCI1CALLEN=0;
	// 配置掩码
	SSCI1CTL0=I2C_SSCIMSK_RW_Enabled;  // 选择使能掩码寄存器,这里手册描述不清楚
	SSCI1MSK=0x00;  // bit0位不参与匹配
	// 根据需要配置掩码，定义bit位被掩   从机有效

	if(isSampleMode10bits)
	{// 10bit
		SSCI1CTL0=I2C_Slave_Mode_Address_10Bits;
		// SSCIADD  先高地址等待匹配
		SSCI1ADD=I2C_Add_H;
		Mode10bitsAddrUAState=0;
	}
	else
	{// 7bit
		SSCI1CTL0=I2C_Slave_Mode_Address_7Bits;
		// SSCIADD
		SSCI1ADD=I2C_Add_L;
	}
	// 清零标志和控制默认位
	SSCI1STA=0x00;

	// 配置中断的使能
		BCL1IF=0;
		SSCI1IF=0;
		BCL1IE=0;
		SSCI1IE=1;
//		PBCL=1;
//		PSSCI=1;

	// 使能模块的工作
		SSCI1CKP=1;  //  =1  SCK释放，使能时钟   否则响应后 时钟拉低 等待处理完毕的赋1使能主机时钟继续
		SSCI1EN=1;
}
void I2C_Configuration_Master(void)
{
	// 根据需要优先关闭I2C模块
	SSCI1EN=0;
	// 模块的引脚配置配置为数字输入，这里选择了P02和P01
	APFCTL10 &= 0x0F;
	APFCTL10 |= 0x50; 		 //管脚复用SCL:P02 SDA:P01
	ANS02=0;	ANS01=0;
	TR02=1;		TR01=1;
//#####################   主机模式   #######################################
	// 作为主机应该考虑这种异常情况，即第9bit的从机应答的完整性，可靠性设计建议主机假定发送CLK9的上升延后，如果设备复位，完整性破坏
	// 作为从机设备在CLK9的上升沿应答为拉低DAT，等待CLK9的下降沿释放DAT。故主机CLK9完整性异常下，从机锁定了DAT，设备锁死。
	// 主机配置起始应该模拟一个CLK9的下降沿，并模拟停止信号，使能从机因主机异常中恢复数据应答。

	TR02=0; P0LR2=0;
	I2C_Delayus(100); // 满足从机支持时钟的速度的延时

	TR01=0; P0LR1=0;
	I2C_Delayus(100);

	P0LR2=1;
	I2C_Delayus(100); // 满足从机支持时钟的速度的延时

	P0LR1=1;
	I2C_Delayus(100);

	TR02=1;TR01=1;

	//----------------------------------
	SSCI1ADD=79;//主控模式下，该寄存器 时钟=SCLK/(4*(SSCIxADD+1))决定了波特率 39-100K,79-50K;
	// SCL时钟使能控制        主控模式
	SSCI1CTL0=0x10 | I2C_Master_Mode_SCL_UserDefined;
	SSCI1CTL1=0;
	// 清零标志和控制默认位
	SSCI1STA=0x00;

	// 配置中断的使能
	BCL1IF=0;				//总线冲突中断标志位
	SSCI1IF=0;
	BCL1IE=0;
	SSCI1IE=0;
//	PBCL=1;
//	PSSCI=1;

	// 使能模块的工作
	SSCI1EN=1;

}

