本人正式使用I2C的经历只有一次,使用EEPROM是为了实现DSP的RAM中的变量断电后仍不会丢失的目的。这可能不是一个恰当的比喻。下面我来详细描述EEPROM的过程。
项目中使用的EEPROM的型号为AT24C256C,擦写次数约为100万次,内存约32768 字节。
项目中实际DSP需要写入的EEPROM中的字节,约100个。
项目中的系统大概类似下图:
其他设备在DSP运行时通过CAN通讯,修改DSP中的特定变量,然后DSP检测到这部分变量被修改以后,则会立即将这部分变量写入EEPROM中,下次DSP断电重启后,会在初始化中,从EEPROM中读取数据,覆盖原有RAM中特定变量的值,这样便实现了CAN修改后的变量,掉电后仍然不会丢失的功能。
本人在使用EEPROM时,实际考虑两个点:
1、EEPROM的擦写次数;
2、从EEPROM中读取数据准确性;
针对第一点,虽然EEPROM的擦写次数,其芯片手册上写的是100万次,但还是得有节制地向其中写入数据,我目前用的方法比较简单,对DSP中的变量,进行CRC计算得出CRC校验值,
如果当前周期的CRC校验值与上个周期不同,则将变量写入EEPROM中,变量的CRC检验与上个周期不同,则通常情况下应该是CAN通讯修改了DSP中变量的值,当然这种做法,是否周全,还值得细细考虑,不过目前使用中还没有出现意外。还有一种做法是在修改DSP中变量时,加入一个选项,就是是否将当前的变量写入EEPROM中,这种做法更为细致,不过代码上就需要实现更多的功能,暂时没有使用这种功能。
针对第二点,我的做法是向EEPROM写入变量时,不单写入变量值,变量写完之后,还写入两个字节的CRC校验值,这CRC校验是DSP将所有变量进行CRC计算而生成的。从EEPROM中读取数据时,不单单读取变量,也将写入的CRC校验值读出,
当所有数据从EEPROM中读取完毕后,DSP会重新对去所有读取的数据(除了最后两个字节)进行CRC计算,再生成新的CRC校验,这新的CRC校验会与读取的CRC校验进行对比,如果两者相同,则说明写入与读取的EEPROM数据没有问题,否则数据损坏。如果数据出现损坏怎么办?我的做法是,DSP检测到读取的数据出现损坏时,则DSP会重新从EEPROM中读取数据,但最多只会读取10次,如果10次之后,读取的结果仍是数据损坏怎么办,那我的做法是在RAM中事先写好一个备份数据,当真的出现数据损坏时,DSP会使用备份数据覆盖数据覆盖特定变量的值,这个备份数据起码能够保证系统正常运行,不会出现设备损坏,及人身安全的问题,但此时整个系统应该出现警告状态,告诉用户从EEPROM中读取数据损坏。
代码方面:
初始化:
void InitI2c(void)
{
I2caRegs.I2CSAR = 0x0050; // Slave address - EEPROM control code
I2caRegs.I2CMDR.bit.IRS = 0;
I2caRegs.I2CPSC.all = 7; // Prescaler - need 7-12 Mhz on module clk
// module clock frequency = SYSCLKOUT/(IPSC + 1) 80/8 = 10Mhz
I2caRegs.I2CCLKL = 10; // NOTE: must be non zero
I2caRegs.I2CCLKH = 5; // Tmst = Tmod *[( ICCL + d ) + ( ICCH + d )] d = 5;
I2caRegs.I2CIER.all = 0; // Disable interrupts
I2caRegs.I2CMDR.all = 0x0020; // Take I2C out of reset
I2caRegs.I2CMDR.bit.FREE = 1; // runs free; that is, it continues to operate when a breakpoint occurs
// I2caRegs.I2CMDR.bit.XA = 0; // 7 bit addressing mode
// I2caRegs.I2CMDR.bit.IRS = 1; //The I2C module is enabled
// I2caRegs.I2CMDR.bit.BC = 0 //8 bit
I2caRegs.I2CFFTX.all = 0x6000; // Enable FIFO mode and TXFIFO
// I2caRegs.I2CFFTX.bit.I2CFFEN = 1; //Enable the I2C FIFO mode.
// I2caRegs.I2CFFTX.bit.TXFFRST = 1; //Enable the transmit FIFO operation
I2caRegs.I2CFFRX.all = 0x2040; // Enable RXFIFO, clear RXFFINT,
// I2caRegs.I2CFFRX.bit.RXFFST = 1 ; //Enable the receive FIFO operation.
// I2caRegs.I2CFFRX.bit.RXFFINTCLR = 1; // clears the RXFFINT flag
}
void InitI2cGpio(void)
{
EALLOW;
// First
// GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0; // Enable pull-up for GPIO32 (SDAA)
// GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; // Enable pull-up for GPIO33 (SCLA)
// GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3; // Asynch input GPIO32 (SDAA)
// GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3; // Asynch input GPIO33 (SCLA)
// GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 1; // Configure GPIO32 for SDAA operation
// GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 1; // Configure GPIO33 for SCLA operation
//Secnod
GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;
GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;
GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;
GpioCtrlRegs.GPAQSEL2.bit.GPIO29 = 3;
GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 2;
GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 2;
EDIS;
}
发送有停止位:
void SendI2c_WithStop(Uchar addr,Uchar *sndbuf,Uchar cnt)
{
Uint16 i = 0;
while(I2caRegs.I2CMDR.bit.STP == 1 || I2caRegs.I2CFFTX.bit.TXFFST != 0)
{
DELAY_US(3);
if(i >= 1000)
{
I2caRegs.I2CMDR.all = 0;
I2caRegs.I2CFFTX.bit.TXFFRST = 0;
I2caRegs.I2CFFRX.bit.RXFFRST = 0;
}
i++;
}
I2caRegs.I2CMDR.bit.IRS = 1;
I2caRegs.I2CFFTX.bit.TXFFRST = 1;
I2caRegs.I2CFFRX.bit.RXFFRST = 1;
I2caRegs.I2CSAR = addr;
I2caRegs.I2CCNT = cnt;
for(i = 0; i < cnt; i ++)
{
I2caRegs.I2CDXR = sndbuf[i];
}
I2caRegs.I2CMDR.all = 0x6E20; //STT STP MST TRX IRS=1 RM=0 8bit
}
发送无停止位:
void SendI2c_WithNoStop(Uchar addr,Uchar *sndbuf,Uchar cnt)
{
Uint16 i = 0;
while(I2caRegs.I2CMDR.bit.STP == 1 || I2caRegs.I2CFFTX.bit.TXFFST != 0)
{
DELAY_US(3);
if(i >= 1000)
{
I2caRegs.I2CMDR.all = 0;
I2caRegs.I2CFFTX.bit.TXFFRST = 0;
I2caRegs.I2CFFRX.bit.RXFFRST = 0;
}
i++;
}
I2caRegs.I2CMDR.bit.IRS = 1;
I2caRegs.I2CFFTX.bit.TXFFRST = 1;
I2caRegs.I2CFFRX.bit.RXFFRST = 1;
I2caRegs.I2CSAR = addr;
I2caRegs.I2CCNT = cnt;
for(i = 0;i < cnt;i ++)
{
I2caRegs.I2CDXR = sndbuf[i];
}
I2caRegs.I2CMDR.all = 0x6620; //STT MST TRX IRS RM=1 STP=0 8bit
}
读取有停止位:
void ReadI2c_WithStop(Uchar addr,Uchar *sndbuf,Uchar cnt)
{
Uint16 i = 0;
while(I2caRegs.I2CMDR.bit.STP == 1 || I2caRegs.I2CFFTX.bit.TXFFST != 0)
{
DELAY_US(3);
if(i >= 1000)
{
I2caRegs.I2CMDR.all = 0;
I2caRegs.I2CFFTX.bit.TXFFRST = 0;
I2caRegs.I2CFFRX.bit.RXFFRST = 0;
}
i++;
}
I2caRegs.I2CMDR.bit.IRS = 1;
I2caRegs.I2CFFTX.bit.TXFFRST = 1;
I2caRegs.I2CFFRX.bit.RXFFRST = 1;
I2caRegs.I2CSAR = addr;
I2caRegs.I2CCNT = cnt;
I2caRegs.I2CMDR.all = 0x2C20; //STT STP MST IRS=1 RM TRX=0 8bit
i = 0;
do
{
DELAY_US(3);
if(i >= 1000)
{
I2caRegs.I2CMDR.all = 0;
I2caRegs.I2CFFTX.bit.TXFFRST = 0;
I2caRegs.I2CFFRX.bit.RXFFRST = 0;
return ;
}
i++;
}while(I2caRegs.I2CSTR.bit.BB != 0);
i = 0;
while(I2caRegs.I2CFFRX.bit.RXFFST != 0)
{
sndbuf[i++] = I2caRegs.I2CDRR;
}
}
单个字节写函数:
void WriteByte_AT24CX_Demo(Uchar data,Uint32 addr)
{
Uchar Dev_Addr = 0;
Uchar buff[3];
Dev_Addr = AT24C256_Dev;
buff[0] = (addr >> 8) & 0x0FF;
buff[1] = addr & 0x0FF;
buff[2] = data;
SendI2c_WithStop(Dev_Addr,buff,3);
}
单个字节读函数:
void ReadByte_AT24CX_Demo(Uchar *data,Uint32 addr,Uint16 cnt)
{
Uchar Dev_Addr = 0;
Uchar buff[5];
Dev_Addr = AT24C256_Dev;
buff[0] = (addr >> 8) & 0x0FF;
buff[1] = addr & 0x0FF;
SendI2c_WithNoStop(Dev_Addr,buff,2);
DELAY_US(5);
ReadI2c_WithStop(Dev_Addr,data,1);
}
static char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 };
/* CRC低位字节值表*/
static char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40 };
Uint16 CRC16(Uchar *puchMsgg, Uint16 usDataLen)
{
Uchar i;
Uchar uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
Uchar uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
Uchar uIndex ; /* CRC循环中的索引 */
for(i=0;i
写入函数:
每次写一个字节,每隔10个循环周期写一个字节;
void EEPROM_DataSend(void)
{
EEPROM_MianLoopCnt ++;
if(EEPROM_MianLoopCnt >= 10)
{
EEPROM_SendDataHandle();
if(SendData_CRC != LastSendData_CRC)
{
SendDataNum += 99 ;
}
if(SendDataNum != 0)
{
if( DataSendCnt < 98 )
{
WriteByte_AT24CX_Demo( DataDic_SendBuf[DataSendCnt], SendDataAddr);
SendDataAddr ++;
DataSendCnt ++;
}
else
{
DataSendCnt = 0 ;
SendDataAddr = DataDic[EEPROM_SendAddress];
}
SendDataNum--;
}
LastSendData_CRC = SendData_CRC;
EEPROM_MianLoopCnt = 0;
}
}
读取函数:
void EEPROM_SystemStartingDataRead(void)
{
while(1)
{
EEPROM_DataRead();
if(DataReadErrCnt > 10)
{
EEPROM_BackupDataCoverage(); //使用备份数据此时系统应该处于警告状态,
//但并不影响实际运行
break;
}
if(ReadCla_CRC != ReadData_CRC)
{
DataReadErrCnt ++;
continue;
}
else
{
EEPROM_ReadDataCoverage(); //读取正常
break;
}
}
}