小凌派-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=
好了,今天的课程就到这里。
推荐阅读
-
邦基科技开盘涨停 拟收购生猪养殖资产延伸产业链
截至6月18日9时25分,邦基科技涨停,涨幅9.98%,报20.71元,总市值34.79亿元,封板资金0.43亿元。公司股票自6月17日复牌后连续两日涨停,主要受重大资产重组预案刺激。根据公告,公司拟通过发行股份及支付现金方式收购持有的北溪农牧等6家公司100%股权及派斯东80%股权。标的资产涉及生...
-
光器与花器紫砂壶在审美当中的差异
当代紫砂艺术创作正处于一个急剧变化的时代大背景中,转型过程中的显著特征是日新月异,变化太快,信息太多,足以令我们几乎“目不暇接”而“不知所措”。历史传统的传承和创新精神的交汇,所形成的全方位辐射不仅拓展了紫砂艺术家的视野,也使得创作取向多元化、风格多样化、表达方法综合化。我一直认为光器紫砂如中国画中...
-
临港海立方科技园三期开工,打造多种功能一体的现代化智慧创新社区
港城集团·海立方科技园落户企业集中签约暨三期项目开工仪式刚刚在临港新片区举行,海立方科技园三期项目总投资约10.7亿元,规划总建筑面积约13.7万平方米,计划建设13栋楼宇,包含2栋高层研发办公楼及配套裙房、8栋多层研发实验楼、2栋智能制造研发中心和1栋展示中心,预计2022年12月完工,将打造集总...
-
贵州又一特大桥全桥合龙
日前,贵州剑黎高速公路重难点工程——锦屏段的小瑶光特大桥左幅贯通,标志着该大桥实现全桥合龙,为剑黎高速公路如期通车奠定了坚实基础。小瑶光特大桥位于黔东南苗族侗族自治州锦屏县境内,大桥梁跨越乌下江,桥址区属构造剥蚀侵蚀低山地貌,地势较陡峭,桥梁最大高度达159米,具有高墩、大跨、大截面等特点。剑黎高速...