引子
E2PROM作为掉电存储的最佳方式,在第九届的省赛的电子时钟中出现了。刚开始准备时发现i2c时序已经难度了,后面发现不同数据类型进行读写也有难度。花时间研究了下发现理解以后还是可以接受的。
CT117E这块板子上用的是AT24C02这块芯片
E2PROM基本读写
赛点的资源包提供了i2c的驱动文件,所以其实驱动这块直接调用它的函数就可以啦。驱动写起来也很简单,不过没这个必要,自己写函数即可。
复制到自己工程文件夹并在keil里添加即可
提供的函数
这里的基本读写是无符号短整型即一字节数据(u8/uint_8)读写。
芯片的读写地址是0XA0(写指令)和0XA1(读指令)
写函数:传入参数 address,data 开始/发送0xa0/等待应答 发送地址/等待应答/发送数据(写数据)/等待应答/停止
读函数:传入参数 address,开始/发送0xa0(写操作)/等待应答 发送地址/等待应答
开始(重新开始)/发送0xa1(读操作)/等待应答/设置变量接收数据/等待应答/停止 返回值
注意:读函数不能单纯理解成一个读数据过程,其实它是把地址通过写的方式传入再读取
即
从 EEPROM 读取数据是一个 复合的 I2C 时序 ,它实际上包含一个写过程和一个读过程,即 开始写地址 再开始读信息
void i2c_write(u8 add,u8 data)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}
u8 i2c_read(u8 add)
{
u8 data;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
data=I2CReceiveByte();
I2CWaitAck();
I2CStop();
return data;
}
32为整型读写
在前面的8位读写基础上操作起来就简单多了,其实就是把32位数据每八位存储一个地址然后再逆写操作逻辑读取出来。一种是高八位存首地址,第八位存首地址+3即数据从高位到低位存储。另一种是第八位存首地址,高八位存首地址+3。哪种方式都可以,只要读写对应确保读出来正确即可。
高位到低位存储
void wirte_32b(u8 add,u32 data)
{
i2c_write(add,data>>24&0xff);
delay_ms(2);
i2c_write(add+1,data>>16&0xff);
delay_ms(2);
i2c_write(add+2,data>>8&0xff);
delay_ms(2);
i2c_write(add+3,data&0xff);
delay_ms(2);
}
u32 read_32b(u8 add)
{
u32 temp;
temp=i2c_read(add)<<24;
delay_ms(2);
temp+=i2c_read(add+1)<<16;
delay_ms(2);
temp+=i2c_read(add+2)<<8;
delay_ms(2);
temp+=i2c_read(add+3);
delay_ms(2);
return temp;
}
低位到高位存储
void wirte_32b(u8 add,u32 data)
{
i2c_write(add,data&0xff);
delay_ms(2);
i2c_write(add+1,(data>>8)&0xff);
delay_ms(2);
i2c_write(add+2,(data>>16)&0xff);
delay_ms(2);
i2c_write(add+3,(data>>24)&0xff);
delay_ms(2);
}
u32 read_32b(u8 add)
{
u32 temp;
temp=i2c_read(add);
delay_ms(2);
temp+=i2c_read(add+1)<<8;
delay_ms(2);
temp+=i2c_read(add+2)<<16;
delay_ms(2);
temp+=i2c_read(add+3)<<24;
delay_ms(2);
return temp;
}
浮点型数据存储
之前了解了一下浮点型数据机器读写的逻辑感觉挺复杂的,后来朋友提醒单精度浮点数也是32位,考虑试了下强制转换成整数类型存储再读取强制转换float,即float->int->写 和 读->int->float 。尝试了一下可行。
常用的浮点数一般精确度要求小数点后两位,所以进行100的乘除变化即可。也可根据情况自己调整,个人感觉这样操作还是比较简单的。
void write_flo(u8 add,float data)
{
u8 str[20];
u32 temp;
temp = (u32)(data*100);
sprintf((char*)str," flo: %d ",temp);
LCD_DisplayStringLine(Line6 ,str);
wirte_32b(add,temp);
}
float read_flo(u8 add)
{
u32 temp;
temp=read_32b(add);
return (float)temp/100;
}
常见问题
1.读写数据异常,一般显示变量最大值
比如我u8数据读写函数结果显示出来一直是255,大概率是因为读写操作后缺一个延时,即i2c操作速度跟不上单片机。加延时即可,不用很长2ms、5ms基本就可以。
2.上面的浮点函数存储以后掉电重新读取只有整数
问题描述:通过按键控制浮点数每次加0.2,后来发现每次不是整数的情况下,比如我加到11.4存储后上电显示11。会自动把小数点后数值删去。后来发现是强制转换的问题
出错情况:
修改后:
return (float)temp/100;
原因也很简单,比如扩大一百倍读出来2345的整型,加括号是除100得到23再强制转换浮点得到23.00.
3.掉电存储失效
可能是因为在main函数调用写函数,例如:
原因分析:一般调用写函数是为了初始化一个值,但是得执行一次后把这句话注释了编译。不然就会出现上面情况,比如每次掉电之前存储了一个值,但是你每次上电会执行一遍你调用的主函数的写函数导致你掉电存储的值被覆盖。