/***************************************************************
 * FeRAM铁电存储器IIC通讯驱动文件
 * @time	: 2025-12-25
 * @auther	: Ye YangWen
 * @model	: MB85RC04V-SOIC-8
 * @note	:
 * 		1、内存空间   : 512字节
 * 		2、设备地址   : 硬件A1、A2引脚，00
 * 		3、设备类型码 : 1010
 * 
 * 		4、引脚定义 :
 * 			IIC_SCL	: PA13
 * 			IIC_SDA : PB0
 * */
#include "includes.h"


FRAM_ENG_t g_engfram;

void Bsp_I2C_Fram_Init(void)
{
	U8_T rev = 0xFF;
	
	//软件IIC引脚
	GPIO_Init(GPIOB0,FRAM_IIC_SDA,Output);
	GPIO_Init(GPIOA0,FRAM_IIC_SCL,Output);	
	
	memset(&g_engfram,0,sizeof(FRAM_ENG_t));
	
	
	Dbg_Println(DBG_BIT_SYS_STATUS,"/************> Fram_Init Read TotalEng <*****************/");
	rev = FRAM_ReadTotalEng(&g_engfram);
	if(rev == 0x00)
	{	//读取成功
		Dbg_Println(DBG_BIT_SYS_STATUS,"Init Total_Eng Succ");
	}else{
		Dbg_Println(DBG_BIT_SYS_STATUS,"Init Total_Eng Fail");
	}

}




/*******************************************************************************
 * @name  	: Fram_SDA_GPIO
 * @brief   : 修改IIC通讯的SDA输入/输出模式
 * @param	: state : 0x01,输出模式; other : 输入模式(有硬件上拉)
 * @retval	: 无
 * @note	: 注意输入模式要上拉
*******************************************************************************/
void Fram_SDA_GPIO(U8_T state)
{
	if(state == 0x01)
	{
		//设置为输出模式
		GPIO_Init(GPIOB0,FRAM_IIC_SDA,Output);			
	}else {
		//设置为输入上拉模式
		GPIO_Init(GPIOB0,FRAM_IIC_SDA,Intput);
	}
}


/*******************************************************************************
 * @name  	: IIC_Wait
 * @brief   : IIC通讯等待时间
 * @param	: 
 * @retval	: 无
 * @note	: 
 * 				1、本项目主频48M, 时钟周期nop约为20.8ns
 * 				2、IIC快速模式延时需要0.6亅= 29个nop
*******************************************************************************/
void IIC_Wait(void)
{
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");
	asm volatile("nop");asm volatile("nop");asm volatile("nop");asm volatile("nop");	
}


/*******************************************************************************
 * @name  	: IIC_Start
 * @brief   : IIC通讯开始
 * @retval	: 无
 * @note	: 
*******************************************************************************/
void IIC_Start(void)
{
	Fram_SDA_GPIO(0x01);
	SDA_ON;
	SCL_ON;
	IIC_Wait();
	SDA_OFF;
	IIC_Wait();
	SCL_OFF;
	IIC_Wait();
}


/*******************************************************************************
 * @name  	: IIC_Stop
 * @brief   : IIC通讯结束
 * @retval	: 无
 * @note	: 
*******************************************************************************/
void IIC_Stop(void)
{
	Fram_SDA_GPIO(0x01);
	SCL_OFF;
	SDA_OFF;
	IIC_Wait();
	SCL_ON;
	IIC_Wait();
	SDA_ON;
}


/*******************************************************************************
 * @name  	: IIC_ACK
 * @brief   : IIC通讯应答
 * @retval	: 无
 * @note	: 
*******************************************************************************/
void IIC_ACK(void)	
{
	Fram_SDA_GPIO(0x01);
	SCL_OFF;
	SDA_OFF;
	IIC_Wait();
	SCL_ON;
	IIC_Wait();
	SCL_OFF;
}


/*******************************************************************************
 * @name  	: IIC_NoACK
 * @brief   : IIC通讯接收数据结束
 * @retval	: 无
 * @note	: 
*******************************************************************************/
void IIC_NoACK(void)
{
	Fram_SDA_GPIO(0x01);
	SCL_OFF;
	SDA_ON;
	IIC_Wait();
	SCL_ON;
	IIC_Wait();
	SCL_OFF;
}


/*******************************************************************************
 * @name  	: IIC_Wait_ACK2
 * @brief   : IIC通讯等待应答
 * @retval	: 无
 * @note	: 
*******************************************************************************/
uint8_t IIC_Wait_ACK2(void)
{
	U16_T temp = 0;
	SCL_OFF;
	Fram_SDA_GPIO(0x00);
	SDA_ON;
	IIC_Wait();
	SCL_ON;
	while(SDA_READ == 1)
	{
		temp++;
		if(temp > 1500)	return 1;	//超时退出
	} 
	SCL_OFF;

	return 0;
}


/*******************************************************************************
 * @name  	: IIC_Wait_ACK
 * @brief   : IIC通讯等待应答
 * @retval	: 无
 * @note	: 
*******************************************************************************/
uint8_t IIC_Wait_ACK(void)
{
    U16_T temp = 0;
    const U16_T ACK_TIMEOUT = 1000;
    
    // 确保SCL初始状态为低电平
    SCL_OFF;
    Fram_SDA_GPIO(0x00);
    IIC_Wait();
    SCL_ON;		// 拉高SCL，开始ACK检测
    IIC_Wait(); 
    while(SDA_READ == 1)
    {
        temp++;
        if(temp > ACK_TIMEOUT)
        {
            SCL_OFF;	 	// 超时处理：恢复总线状态
            return 1; 		// 超时退出
        }
        asm volatile("nop");// 添加小延时，避免CPU过度占用
    }
    
    // 检测到ACK，拉低SCL结束周期
    SCL_OFF;
    return 0;
}


/*******************************************************************************
 * @name  	: IIC_Send_Byte
 * @brief   : IIC通讯 - 发送1个字节数据
 * @param	: txd : 写入字节
 * @retval	: 无
 * @note	: 
*******************************************************************************/
void IIC_Send_Byte(uint8_t txd)
{
	uint8_t t;
	Fram_SDA_GPIO(0x01);
	for(t=0;t<8;t++)
	{
		SCL_OFF;
		if((txd&0x80))
		{
			SDA_ON;
		}else{
			SDA_OFF;
		}
		txd<<=1;
		IIC_Wait();
		SCL_ON;
		IIC_Wait();
	}
	SCL_OFF;
}


/*******************************************************************************
 * @name  	: IIC_Read_Byte
 * @brief   : IIC通讯 - 检测接收1个字节数据
 * @param	:
 * @retval	: data ： 接收到的数据
 * @note	: 
*******************************************************************************/
uint8_t IIC_Read_Byte(void)
{
	uint8_t i = 0,data = 0;
	Fram_SDA_GPIO(0x00);
	
	for(i = 0;i < 8;i++)
	{
		data <<= 1;
		SCL_OFF;
		IIC_Wait();
		SCL_ON;
		IIC_Wait();
		if(SDA_READ == 1) data |= 0x01;
	}
	SCL_OFF;

	return data;
}


/*******************************************************************************
 * @name  	: Fram_I2C_WriteByte
 * @brief   : IIC通讯 - 给FeRAM指定地址写入一个字节
 * @param	: 	addr ：写入地址
				data ：写入数据
 * @retval	: 1:成功, 2~4:写入失败, 5:写入地址越界
 * @note	: 
 * 		发送流程 : 
 * 				1、发送设备地址码1010 (C1)(C0)(A8)(W/R = 0),
 * 				2、等待应答
 * 				2、发送要写入的内存地址低8位，A7~A0
 * 				3、等待应答
 * 				4、发送待写入的数据
 * 				5、等待应答
 * 				6、发送停止
*******************************************************************************/
U8_T Fram_I2C_WriteByte(U16_T addr, U8_T data)
{
    U8_T dev_addr;
 
    if(addr > 0x1FF)
	{
        return 5; // 地址越界
    }
   
    // 计算设备地址：1010 (C1)(C0)(A8)(W/R)
    // C1=C0=0, A8是addr的最高位, W/R=0(写)
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x00;
    
    IIC_Start();
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 2; // 写入失败
    }
    
    // 发送低8位地址
    IIC_Send_Byte((U8_T)(addr & 0xFF));
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 3; // 写入失败
    }
    
    // 发送数据
    IIC_Send_Byte(data);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 4; // 写入失败
    }
    
    IIC_Stop();
    return 1; // 写入成功
}



/*******************************************************************************
 * @name  	: Fram_I2C_ReadByte
 * @brief   : IIC通讯 - 在FeRAM指定地址读取一个字节
 * @param	: 	addr ：读取地址
				data ：读取数据
 * @retval	: 1:成功, 2~4:读取失败, 5:读取地址越界
 * @note	: 
*******************************************************************************/
U8_T Fram_I2C_ReadByte(U16_T addr, U8_T *data)
{
    U8_T dev_addr;

    // 地址范围检查
    if(addr > 0x1FF)
    {
        return 5; // 地址越界
    }
    
    // 第一步：写入要读取的地址
    IIC_Start();
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x00;
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 2; // 读取失败
    }
    
    // 发送低8位地址
    IIC_Send_Byte((U8_T)(addr & 0xFF));
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 3; // 读取失败
    }
    
    // 第二步：读取数据
    IIC_Start();
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x01; // W/R=1
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 4; // 读取失败
    }
    
    // 读取数据，发送NAK
    *data = IIC_Read_Byte();
    IIC_NoACK();
    IIC_Stop();
    
    return 1; // 读取成功
}


/*******************************************************************************
 * @name  	: Fram_I2C_WriteBytes
 * @brief   : IIC通讯 - 在FeRAM指定地址写入多个字节
 * @param	: 	addr : 写入地址
				data : 写入数据缓存
 *				len  : 写入数据长度
 * @retval	: 1:成功, 2~4:写入失败, 5:写入地址越界、结束地址越界、长度越界、数据指针为空
 * @note	: 
*******************************************************************************/
U8_T Fram_I2C_WriteBytes(U16_T addr, U8_T *data, U16_T len)
{

    U8_T dev_addr;
    U16_T i;

    if((addr > 0x1FF) || ((addr + len) > 0x200) || (len == 0) || (data == NULL))
    {
        return 5; // 长度或地址错误
    }
    
    // 计算设备地址：1010 (C1)(C0)(A8)(W/R)
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x00;
    
    IIC_Start();
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 2; // 发送设备类型码失败
    }
    
    // 发送低8位地址
    IIC_Send_Byte((U8_T)(addr & 0xFF));
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 3; // 发送内存地址失败
    }
    
    // 连续写入多个字节
    for(i = 0; i < len; i++)
    {
        IIC_Send_Byte(data[i]);
        if(IIC_Wait_ACK())
        {
            IIC_Stop();
            return 4; // 写入数据失败
        }
    }
    
    IIC_Stop();
    return 1; // 写入成功
}



/*******************************************************************************
 * @name  	: Fram_I2C_ReadBytes
 * @brief   : IIC通讯 - 在FeRAM指定地址开始读取多个字节
 * @param	: 	addr : 读取地址
				data : 读取数据缓存
 *				len  : 读取数据长度
 * @retval	: 1:成功, 2~4:读取失败, 5:读取地址越界、结束地址越界、长度越界、接收数据指针为空
 * @note	: 
*******************************************************************************/
U8_T Fram_I2C_ReadBytes(U16_T addr, U8_T *buf, U16_T len)
{
    U8_T dev_addr;
    U16_T i;

    if((addr > 0x1FF) || ((addr + len) > 0x200) || (len == 0) || (buf == NULL))
    {
        return 5; // 长度或地址错误
    }
    
    // 第一步：写入要读取的地址
    IIC_Start();
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x00;
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 2; // 发送设备类型码失败
    }
    
    // 发送低8位地址
    IIC_Send_Byte((U8_T)(addr & 0xFF));
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 3; // 发送内存地址失败
    }
    
    // 第二步：读取多个字节
    IIC_Start();
    dev_addr = 0xA0 | ((addr & 0x100) >> 7) | 0x01; // W/R=1(读)
    IIC_Send_Byte(dev_addr);
    if(IIC_Wait_ACK())
    {
        IIC_Stop();
        return 4; // 发送读取
    }
    
    // 连续读取多个字节
    for(i = 0; i < len; i++)
    {
        buf[i] = IIC_Read_Byte();
        // 最后一个字节发送NAK，其他发送ACK
        if(i < len - 1)
        {
            IIC_ACK();
        }
        else
        {
            IIC_NoACK();
        }
    }
    
    IIC_Stop();
    return 1; // 读取成功
}





 /*******************************************************************************
 * @name  	: FRAM_WriteTotalEng
 * @brief   : 写入总电量
 * @param	: info : 电量结构体
 * @retval	: 0x00 : 写入成功, 
 * @note	: 
*******************************************************************************/
U8_T FRAM_WriteTotalEng(FRAM_ENG_t *info)
{
	U8_T rev = 0x00;
	U8_T save_data[FRAM_ENERGY_SIZE + 6];
	U16_T save_len = sizeof(FRAM_ENG_t);
	
	if(save_len >= FRAM_ENERGY_SIZE) save_len = FRAM_ENERGY_SIZE;
	
	save_data[0] = FRAM_ENERGY_SAVE_FLAG;
	save_data[1] = save_len & 0xFF;
	save_data[2] = (save_len >> 8) & 0xFF;
	save_data[3] = 0x00;	//和校验字
	
	memcpy(&save_data[4],(uint8_t *)info,save_len);
	
	save_data[3] = EEPROM_CheckSum(&save_data[4],save_len);
	
	save_len += 4;
	rev = Fram_I2C_WriteBytes(FRAM_ENERGY_SAVE_ADDR,save_data,save_len);
	
	if(rev != 0x01){
		Dbg_Println(DBG_BIT_SYS_STATUS, "writc total_eng err");
		return 0x01;
	}
	
	Dbg_Println(DBG_BIT_SYS_STATUS, "writc total_eng succ");
	return 0x00;
}

 /*******************************************************************************
 * @name  	: ReadTotalEng
 * @brief   : 读取总电量
 * @param	: info : 电量结构体
 * @retval	: 0x00 : 读取成功, 
 * @note	: 
*******************************************************************************/
U8_T FRAM_ReadTotalEng(FRAM_ENG_t *info)
{
	U8_T read_info[6];
	U8_T para_data[FRAM_ENERGY_SIZE];
	U16_T read_len = 0;
	
	memset(read_info,0,sizeof(read_info));
	memset(para_data,0,sizeof(para_data));
	
	Fram_I2C_ReadBytes(FRAM_ENERGY_SAVE_ADDR,read_info,4);
	
	if(read_info[0] == FRAM_ENERGY_SAVE_FLAG){
		read_len = read_info[2];
		read_len <<= 8;
		read_len |= read_info[1];
		
		if(read_len <= FRAM_ENERGY_SIZE){
			
			Fram_I2C_ReadBytes(FRAM_ENERGY_SAVE_ADDR + PRAM_Offset_Data,para_data,read_len);
			if(EEPROM_CheckSum(para_data,sizeof(FRAM_ENG_t)) == read_info[3]){
				//校验成功
				memcpy((uint8_t *)info,para_data,sizeof(FRAM_ENG_t));
				
			#if DBG_LOG_EN
				SYSCON_IWDCNT_Reload();
				Dbg_Println(DBG_BIT_SYS_STATUS,"total_A_eng	 : %d",info->total_A_eng);
				Dbg_Println(DBG_BIT_SYS_STATUS,"total_B_eng	 : %d",info->total_B_eng);
				Dbg_Println(DBG_BIT_SYS_STATUS,"Eng_A_RegData : %d",info->Eng_A_RegData);
				Dbg_Println(DBG_BIT_SYS_STATUS,"Eng_A_RegData : %d",info->Eng_B_RegData);
			#endif
				return 0x00;
			}
		}
	}
	
	return 0x01;
}

