小凌派-RK2206开发板:EEPROM存储案例
在实际的应用中,保存在RAM中的数据掉电后就丢失了,保存在FLASH中的数据又不能随意改变,也就是不能用它来记录变化的数值。但是在某些特定场合,我们又确实需要记录下某些数据,而它们还时常需要改变或更新,掉电之后数据还不能丢失。比如,我们的家用电表度数,电视机里边的频道记忆,一般都是使用EEPROM来保存数据,特点就是掉电后存储的数据不丢失。
EEPROM(ElectricallyErasableProgrammablereadonlymemory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。EEPROM可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。一般情况下,EEPROM拥有30万到100万次的寿命,也就是它可以反复写入30~100万次,而读取次数是无限的。
本文基于瑞芯微RK2206芯片+鸿蒙LiteOS操作系统,通过i2c总线控制EEPROM读写。
二、硬件电路设计我使用的EEPROM型号是K24C02,它是一个常用的基于IIC通信协议的EEPROM元件,例如ATMEL公司的AT24C02、CATALYST公司的CAT24C02和ST公司的ST24C02等芯片。IIC是一个通信协议,它拥有严密的通信时序逻辑要求,而EEPROM是一个元件,只是这个元件采样了IIC协议的接口与单片机相连而已,二者并没有必然的联系,EEPROM可以用其它接口,I2C也可以用在其它很多器件上。根据K24C02芯片手册,可获知如下:
K24C02芯片的从设备地址。因本章节采用的2Kbit的EEPROM,所以该芯片I2C从设备地址为0x51。如下图所示:

图K24C02的从设备地址图
(2)K24C02芯片的读操作
K24C02芯片的读操作共分为3种,分别为当前地址读(CurrentAddressRead)、随机读(RandomRead)和连续读(SequentialRead)。
其中,当前地址读(CurrentAddressRead)操作是控制i2c与K24C02通信,通信内容为:从设备地址(1个字节,bit0为1,表示读)+数据(1个字节,K24C02发送给CPU的存储内容)。该读操作没有附带EEPROM的存储地址,存储地址是由上一次存储地址累加而来。如下图所示:

图K24C02的当前地址读操作
而随机读操作则控制i2c与K24C02进行2次通信:
第1次i2c通信:从设备地址(1个字节,bit0为0,表示写)+存储地址(1个字节,CPU发送给K24C02的存储地址)
第2次i2c通信:从设备地址(1个字节,bit0为1,表示读)+数据(1个字节,K24C02发送给CPU的存储内容)。
具体数据传输如下图所示:
图K24C02的随机地址读操作
连续读操作(SequentialRead)则控制控制i2c往K24C02发送N个字节,通信内容为:从设备地址(bit0为1,表示读)+N个数据(K24C02发送给CPU)。具体数据传输如下图所示:
图K24C02的连续读操作
(3)K24C02芯片的写操作
K24C02芯片写数据操作可分为2种,分别为字节写操作(ByteWrite)和页写操作(PageWrite)。
其中,字节写操作(ByteWrite)控制i2c与K24C02通信,通信内容为:从设备地址(1个字节,bit0为0,表示写)+存储地址(1个字节)+数据(1个字节,CPU发送给K24C0的存储内容)。具体数据传输如下图所示:
图K24C02的字节写操作
页写操作(PageWrite)则控制i2c与K24C02通信,通信内容为:从设备地址(1个字节,bit0为0,表示写)+存储地址(1个字节)+数据(N个字节,CPU发送给K24C0的存储内容)。其中,存储数据的N个字节,N不能超过Page大小(K24C02的页大小为8个字节)。
三、程序设计程序控制RK2206芯片的I2C与K24C02芯片通信,每5秒往某一块存储空间(该存储空间地址依次累加)写入不同数据,然后再读取出来。
1、主程序设计如图所示为EEPROM存储主程序流程图,开机LiteOS系统初始化后,进入主程序后。主程序首先初始化i2c总线。其次,程序进入主循环,每5秒将不同的数据写入到一块存储空间,然后再读取回去。其中,存储空间地址每次循环都累加32,数据也随着循环而累加1。
图主程序流程图
while(1){printf("************EepromProcess************\n");printf("BlockSize=0x%x\n",eeprom_get_blocksize());/*写EEPROM*/memset(buffer,0,sizeof(buffer));for(unsignedinti=0;iFOR_CHAR;i++){buffer[i]=data_offset+i;printf("WriteByte:%d=%c\n",addr_offset+i,buffer[i]);}ret=eeprom_write(addr_offset,buffer,FOR_CHAR);if(ret!=FOR_CHAR){printf("EepromWritefailed(%d)\n",ret);}/*读EEPROM*/memset(buffer,0,sizeof(buffer));ret=eeprom_read(addr_offset,buffer,FOR_CHAR);if(ret!=FOR_CHAR){printf("ReadBytes:failed!\n");}else{for(unsignedinti=0;iFOR_CHAR;i++){printf("ReadByte:%d=%c\n",addr_offset+i,buffer[i]);}}data_offset++;if(data_offset=CHAR_END){data_offset=CHAR_START;}addr_offset+=FOR_ADDRESS;if(addr_offset=200){addr_offset=0;}printf("\n");LOS_Msleep(5000);}2、EEPROM初始化程序设计主程序通过控制RK2206芯片的接口对i2c总线进行初始化。
defineEEPROM_I2C_ADDRESS0x51staticI2cBusIom_i2cBus={.scl={.gpio=GPIO0_PA1,.func=MUX_FUNC3,.type=PULL_NONE,.drv=DRIVE_KEEP,.dir=LZGPIO_DIR_KEEP,.val=LZGPIO_LEVEL_KEEP},.sda={.gpio=GPIO0_PA0,.func=MUX_FUNC3,.type=PULL_NONE,.drv=DRIVE_KEEP,.dir=LZGPIO_DIR_KEEP,.val=LZGPIO_LEVEL_KEEP},.id=FUNC_ID_I2C0,.mode=FUNC_MODE_M2,};staticunsignedintm_i2c_freq=100000;unsignedinteeprom_init(){if(I2cIoInit(m_i2cBus)!=LZ_HARDWARE_SUCCESS){printf("%s,%d:I2cIoInitfailed!\n",__FILE__,__LINE__);return__LINE__;}if(LzI2cInit(EEPROM_I2C_BUS,m_i2c_freq)!=LZ_HARDWARE_SUCCESS){printf("%s,%d:I2cInitfailed!\n",__FILE__,__LINE__);return__LINE__;}/*GPIO0_A0=I2C1_SDA_M1*/PinctrlSet(GPIO0_PA0,MUX_FUNC3,PULL_NONE,DRIVE_KEEP);/*GPIO0_A1=I2C1_SCL_M1*/PinctrlSet(GPIO0_PA1,MUX_FUNC3,PULL_NONE,DRIVE_KEEP);return0;}3、EEPROM读操作程序设计蜂鸣器控制程序当开启蜂鸣器时,打开蜂鸣器,并且设置PWM波的周期为100毫秒,其中占空比50%;当关闭蜂鸣器时,则停止蜂鸣器。
defineEEPROM_I2C_ADDRESS0x51/*EEPROM型号:K24C02,2Kbit(256Byte),32页,每页8个字节(Byte)*/defineEEPROM_PAGE8unsignedinteeprom_readbyte(unsignedintaddr,unsignedchar*data){unsignedintret=0;unsignedcharbuffer[1];LzI2cMsgmsgs[2];/*K24C02的存储地址是0~255*/if(addr=EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr(0x%x)=EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_ADDRESS_MAX);return0;}buffer[0]=(unsignedchar)addr;msgs[0].addr=EEPROM_I2C_ADDRESS;msgs[0].flags=0;msgs[0].buf=buffer[0];msgs[0].len=1;msgs[1].addr=EEPROM_I2C_ADDRESS;msgs[1].flags=I2C_M_RD;msgs[1].buf=data;msgs[1].len=1;ret=LzI2cTransfer(EEPROM_I2C_BUS,msgs,2);if(ret!=LZ_HARDWARE_SUCCESS){printf("%s,%s,%d:LzI2cTransferfailed(%d)!\n",__FILE__,__func__,__LINE__,ret);return0;}return1;}unsignedinteeprom_read(unsignedintaddr,unsignedchar*data,unsignedintdata_len){unsignedintret=0;if(addr=EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr(0x%x)=EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_ADDRESS_MAX);return0;}if((addr+data_len)EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr+len(0x%x)EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr+data_len,EEPROM_ADDRESS_MAX);return0;}ret=eeprom_readbyte(addr,data);if(ret!=1){printf("%s,%s,%d:EepromReadBytefailed(%d)\n",__FILE__,__func__,__LINE__,ret);return0;}if(data_len1){ret=LzI2cRead(EEPROM_I2C_BUS,EEPROM_I2C_ADDRESS,data[1],data_len-1);if(ret0){printf("%s,%s,%d:LzI2cReadfailed(%d)!\n",__FILE__,__func__,__LINE__,ret);return0;}}returndata_len;}4、EEPROM写操作程序设计主程序根据存储地址、存储数据和数据长度的不同,选用字节写操作或页面写操作。具体源代码如下所示:
defineEEPROM_I2C_ADDRESS0x51/*EEPROM型号:K24C02,2Kbit(256Byte),32页,每页8个字节(Byte)*/defineEEPROM_PAGE8unsignedinteeprom_writebyte(unsignedintaddr,unsignedchardata){unsignedintret=0;LzI2cMsgmsgs[1];unsignedcharbuffer[2];/*K24C02的存储地址是0~255*/if(addr=EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr(0x%x)=EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_ADDRESS_MAX);return0;}buffer[0]=(unsignedchar)(addr0xFF);buffer[1]=data;msgs[0].addr=EEPROM_I2C_ADDRESS;msgs[0].flags=0;msgs[0].buf=buffer[0];msgs[0].len=2;ret=LzI2cTransfer(EEPROM_I2C_BUS,msgs,1);if(ret!=LZ_HARDWARE_SUCCESS){printf("%s,%s,%d:LzI2cTransferfailed(%d)!\n",__FILE__,__func__,__LINE__,ret);return0;}/*K24C02芯片需要时间完成写操作,在此之前不响应其他操作*/eeprog_delay_usec(1000);return1;}unsignedinteeprom_writepage(unsignedintaddr,unsignedchar*data,unsignedintdata_len){unsignedintret=0;LzI2cMsgmsgs[1];unsignedcharbuffer[EEPROM_PAGE+1];/*K24C02的存储地址是0~255*/if(addr=EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr(0x%x)=EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_ADDRESS_MAX);return0;}if((addr%EEPROM_PAGE)!=0){printf("%s,%s,%d:addr(0x%x)isnotpageaddr(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_PAGE);return0;}if((addr+data_len)EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr+data_len(0x%x)EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr+data_len,EEPROM_ADDRESS_MAX);return0;}if(data_lenEEPROM_PAGE){printf("%s,%s,%d:data_len(%d)EEPROM_PAGE(%d)\n",__FILE__,__func__,__LINE__,data_len,EEPROM_PAGE);return0;}buffer[0]=addr;memcpy(buffer[1],data,data_len);msgs[0].addr=EEPROM_I2C_ADDRESS;msgs[0].flags=0;msgs[0].buf=buffer[0];msgs[0].len=1+data_len;ret=LzI2cTransfer(EEPROM_I2C_BUS,msgs,1);if(ret!=LZ_HARDWARE_SUCCESS){printf("%s,%s,%d:LzI2cTransferfailed(%d)!\n",__FILE__,__func__,__LINE__,ret);return0;}/*K24C02芯片需要时间完成写操作,在此之前不响应其他操作*/eeprog_delay_usec(1000);returndata_len;}unsignedinteeprom_write(unsignedintaddr,unsignedchar*data,unsignedintdata_len){unsignedintret=0;unsignedintoffset_current=0;unsignedintpage_start,page_;unsignedcharis_data_front=0;unsignedcharis_data_back=0;unsignedintlen;if(addr=EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr(0x%x)=EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr,EEPROM_ADDRESS_MAX);return0;}if((addr+data_len)EEPROM_ADDRESS_MAX){printf("%s,%s,%d:addr+len(0x%x)EEPROM_ADDRESS_MAX(0x%x)\n",__FILE__,__func__,__LINE__,addr+data_len,EEPROM_ADDRESS_MAX);return0;}/*判断addr是否是页地址*/page_start=addr/EEPROM_PAGE;if((addr%EEPROM_PAGE)!=0){page_start+=1;is_data_front=1;}/*判断addr+data_len是否是页地址*/page_=(addr+data_len)/EEPROM_PAGE;if((addr+data_len)%EEPROM_PAGE!=0){page_+=1;is_data_back=1;}offset_current=0;/*处理前面非页地址的数据,如果是页地址则不执行*/for(unsignedinti=addr;i(page_start*EEPROM_PAGE);i++){ret=eeprom_writebyte(i,data[offset_current]);if(ret!=1){printf("%s,%s,%d:EepromWriteBytefailed(%d)\n",__FILE__,__func__,__LINE__,ret);returnoffset_current;}offset_current++;}/*处理后续的数据,如果数据长度不足一个Page,则不执行*/for(unsignedintpage=page_start;pagepage_;page++){len=EEPROM_PAGE;if((page==(page_-1))(is_data_back)){len=(addr+data_len)%EEPROM_PAGE;}ret=eeprom_writepage(page*EEPROM_PAGE,data[offset_current],len);if(ret!=len){printf("%s,%s,%d:EepromWritePagefailed(%d)\n",__FILE__,__func__,__LINE__,ret);returnoffset_current;}offset_current+=EEPROM_PAGE;}returndata_len;}四、编译过程1、搭建和下载源代码
我已将OpenHarmony源代码上传到Gitee社区中,大家可以根据以下网址下载。
注意:编译环境可根据以下网址来操作:
2、打开sdk下面路径的文件
/vor/lockzhiner/rk2206/samples/b3_eeprom/eeprom_
注意:Gitee上的EEPROM案例为通用案例,请大家根据上述的需求修改相关源代码。
3、修改编译脚本
修改vor/lockzhiner/rk2206/sample路径下文件,指定eeprom_example参与编译。
"./b3_eeprom:eeprom_example",
修改device/lockzhiner/rk2206/sdk_liteos路径下Makefile文件,添加-leeprom_example参与编译。
hardware_LIBS=-lhal_iothardware-lhardware-leeprom_example
3、编译固件
4、烧写固件
请参考Gitee网址的说明手册(“烧录打印”章节):
五、实验结果程序编译烧写到开发板后,按下开发板的RESET按键,通过串口软件查看日志如下:
************EepromProcess************BlockSize=0x8WriteByte:3=!WriteByte:4="WriteByte:5=ReadByte:6=$ReadByte:7=%ReadByte:8=ReadByte:9='ReadByte:10=(ReadByte:11=)ReadByte:12=*ReadByte:13=+ReadByte:14=,ReadByte:15=-ReadByte:16=.ReadByte:17=/ReadByte:18=0ReadByte:19=1ReadByte:20=2ReadByte:21=3ReadByte:22=4ReadByte:23=5ReadByte:24=6ReadByte:25=7ReadByte:26=8ReadByte:27=9ReadByte:28=:ReadByte:29=;ReadByte:30=ReadByte:31==ReadByte:32=
好了,今天的课程就到这里。
推荐阅读
-
解构亿光电子VS日亚化(Nichia)之多国专利诉讼案
“历经10年的日亚化与亿光间专利诉讼案。”2022年5月10日,台湾亿光电子发表声明,该公司收到欧洲专利局上诉委员会(BoardofAppealforEuropePatentOffice)的判决,判决同意亿光主张日亚化专利无效之上诉请求,判定日亚化的欧洲专利EP2197053全部权利项无效[1]。惟...
-
预计五月底完工!四川两弹城博物馆和航天科技馆要来啦
近日,记者在绵阳梓潼两弹城采访了解到,四川两弹城博物馆和航天科技馆建设正在加紧建设中,预计五月底完工。◆四川两弹城博物馆建筑面积12400平方米,主要以我国“两弹一星”研制历程背景为脉络进行建设,通过历史文物、图片、多媒体等方式,展示两弹研制历史全貌和“两弹一星”精神;◆航天科技馆建筑面积13500...
-
科技赋能运动康复!全民生活方式促进专家论坛在穗举办
构建健康生活方式慢病需合理的运动干预2023全民生活方式促进专家论坛现场在“慢病管理中的运动干预”话题讨论上,全国防控重大慢病创新融合试点项目运营组负责人徐烨表示,“健康中国2030”提出在定位上从“以治病为中心”向“以人民健康为中心”转变,在策略上从注重“治已病”向“治未病”转变。因此,运动健康正...
-
元琛科技取得聚四氟乙烯复合膜专利,有效提高滤料的过滤精度、力学性能及使用寿命
金融界2024年3月14日消息,据国家知识产权局公告,安徽元琛环保科技股份有限公司取得一项名为“一种具有梯度结构的聚四氟乙烯复合膜“,授权公告号CN220573155U,申请日期为2023年4月。专利摘要显示,本发明涉及覆膜滤料技术领域,公开了一种具有梯度结构的聚四氟乙烯复合膜,包括粗滤层和复合在粗...