STM32之EEPROM驱动
本⽂介绍如何使⽤STM32标准外设库驱动EEPROM,本例程驱动的EEPROM为AT24C02,通讯协议为IIC,使⽤IO⼝模拟⽅式。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. EEPROM简介
EEPROM全称为EEPROM(Electrically Erasable Programmable Read Only Memory)是电可擦除可编程只读存储器。虽然名称为只读存储器,但是擦除和写⼊都是直接使⽤电路控制,不需要再使⽤外部设备来擦写,即设备在运⾏过程中即可随时擦除和写⼊。可以按字节为单位修改数据,⽆需整个芯⽚擦除,且掉电后数据不丢失,⼀般⽤来存储⼀些配置信息,以便系统重新上电的时候加载。
2. 常⽤EEPROM
⼀般常⽤的EEPROM为ATMEL公司(已被Microchip收购)的AT24Cxx系列,常⽤容量从1K到64Kbit不等,换算成字节为128到8K Bytes,可以根据项⽬需求和价格综合考虑选型。
3. EEPROM操作说明
AT24C02容量为2Kbit,即256Byte,地址范围为0~255,即0~0xFF,使⽤1个字节即可表⽰,因此地址长度为1字节。
3.1. 通讯⽅式I
AT24C02使⽤IIC协议跟MCU通讯。
3.2. 设备地址
如果仅接⼊⼀个AT24C02,可以将设备的A0、A1、A2引脚全部接⼊低电平,那么此时该设备的地位为0x50,再增加⼀位读写标志位,最终读取操作时地址为0xA1,写⼊操作时地址为0xA0。
guihua3.3. 读取数据
读取当前字节:MCU直接发起读操作,设备返回当前字节,当前字节⾃动加1,该操作较少使⽤。
读取指定地址⼀个字节:MCU先向AT24C02写⼊⼀个地址,然后再发起⼀个读操作,AT24C02返回该地址存储的字节。
连续读取:MCU发起读当前字节,或者读指定地址字节,设备返回数据,MCU发送ACK,设备继续返回后续地址数据,直到MCU发送NACK,设备不再返回数据。
1 uint8_t EE_Read(uint8_t addr, uint8_t *pBuffer, uint8_t numToRead)
2 {
3 uint8_t i;
4
5if(EE_WaitReady()){
6goto iic_fail;
7 }
8
9 IIC_Start();
10 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR);
11if(IIC_WaitAck()){
12goto iic_fail;
13 }
14 IIC_WriteByte(addr);
15if(IIC_WaitAck()){
16goto iic_fail;
17 }
18 IIC_Start();
19 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_RD);
20if(IIC_WaitAck()){
100个简单的谜语21goto iic_fail;
22 }
23
24for(i = numToRead; i > 0; i--)
25 {
26 *pBuffer++ = ((i == 1) ? IIC_ReadByte(0) : IIC_ReadByte(1));
27 }
28
29 IIC_Stop();
30return0;
31
32 iic_fail:
33 IIC_Stop();
34return1;
35 }
3.4. 写⼊数据
写⼊⼀个字节:MCU先向AT24C02写⼊⼀个地址,然后再写⼊数据。
写⼊⼀页:MCU先向AT24C02写⼊⼀个地址,然后再依次写⼊数据,注意AT24C02⼀页有8个字节,每页开始地址均是8的整数倍,⼀次页写⼊操作地址不能超过当前页的尾地址。
连续写⼊:AT24C02本⾝没有提供连续写⼊的操作,因此必须先将数据按页地址分为若⼲页,然后再依次调⽤页写⼊操作进⾏写⼊。
数据分页函数为EE_GetWritePages(),详细介绍见后⾯。
1//AT24C02页写⼊函数,不得超过当前页的最后地址,每页可写⼊8个字节
2//addr: 写⼊数据的⽬的地址
3//pBuffer: 要写⼊的数据
4//numToWrite: 要写⼊数据的长度
5//返回值: 1,写⼊失败
6// 0,写⼊成功
7 uint8_t EE_PageWrite(uint8_t addr, uint8_t *pBuffer, uint8_t numToWrite)
小学六年级数学教学计划8 {
9 uint8_t i;
10
11if(EE_WaitReady()){
12goto iic_fail;
13 }
14
15 IIC_Start();
16 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR);
17if(IIC_WaitAck()){
18goto iic_fail;
19 }
20 IIC_WriteByte(addr);
21if(IIC_WaitAck()){
22goto iic_fail;
23 }
我真的好想你歌词24for(i = 0; i < numToWrite; i++){
25 IIC_WriteByte(*pBuffer++);
26if(IIC_WaitAck()){
27goto iic_fail;
28 }
29 }
30
31 IIC_Stop();
32return0;
33
34 iic_fail:
35 IIC_Stop();
36return1;
37 }
38
39//在AT24C02⾥⾯的指定地址开始写⼊指定个数的数据
40//addr: 写⼊数据的⽬的地址
41//pBuffer: 要写⼊的数据
42//numToWrite: 要写⼊数据的长度
43//返回值: 1,读取失败
44// 0,读取成功
45 uint8_t EE_Write(uint8_t addr, uint8_t *pBuffer, uint8_t numToWrite)
46 {
47 uint8_t i;
48 uint8_t firstPageLen, lastPageLen, pageNum;
49
50 EE_GetWritePages(addr, numToWrite, EEPROM_PAGE_SIZE,
51 &firstPageLen, &lastPageLen, &pageNum);
52
53for(i = 0; i < pageNum; i++)
54 {
55if(i == 0){ //⾸页写⼊长度为firstPageLen
56if(EE_PageWrite(addr, pBuffer, firstPageLen)){
57goto iic_fail;
58 }
59 addr += firstPageLen;
60 pBuffer += firstPageLen;
61 }else if(i == pageNum - 1){ //尾页写⼊长度为lastPageLen
62if(EE_PageWrite(addr, pBuffer, lastPageLen)){
63goto iic_fail;
64 }
65 addr += lastPageLen;
66 pBuffer += lastPageLen;
67 }else{ //除⾸页和尾页外写⼊长度为EEPROM_PAGE_SIZE
68if(EE_PageWrite(addr, pBuffer, EEPROM_PAGE_SIZE)){
69goto iic_fail;
70 }
71 addr += EEPROM_PAGE_SIZE;
72 pBuffer += EEPROM_PAGE_SIZE;
73 }
74 }
75
76return0;
77
78 iic_fail:
79 IIC_Stop();
80return1;
81 }
3.5. 状态等待
EEPROM调⽤写⼊操作后,需要⼀段时间才能将数据更新,通过查看AT24C02的datasheet,可以看到写⼊循环最⼤为5ms,数据更新期间对设备⽆法进⾏读写操作,程序具体实现为向AT24C02发送START+设备地址(写命令),如果AT24C02返回ACK信号,表⽰AT24C02⽬前已经可以正常读写,否则需要继续等待,等待过程中可以继续发送START+设备地址(写命令),直到返回ACK信号。
为防⽌设备故障导致⼀直⽆法返回ACK信号,需要设定⼀个最⼤检测次数,因为AT24C02最⼤写⼊循环为5ms,因此可以将每次检测时间间隔设定为100us,循环次数为50次,如果检测50次均没有返回ACK信号,此时可以放弃读写操作,返回错误。
1//查看AT24C02是否空闲
2//返回值: 1,EEPROM忙,⽆法读写
3// 0,EEPROM空闲,可以读写
4 uint8_t EE_WaitReady(void)
5 {
6 uint8_t i;
7 uint8_t ret = 1;
8
9for(i = 0; i < 50; i++){入梅出梅时间2022
10 IIC_Start();
11 IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR); //发送写命令
12if(!IIC_WaitAck()){
13 ret = 0;
14break;
15 }
16 delay_us(100);
17 }
18 IIC_Stop();
19return ret;
20 }
3.6. 数据存储如何按Page对齐
AT24C02存储数据时把整个存储区会分成若⼲页,每页8个字节,⼀次写操作不能跨页写⼊,因此如果连续写⼊时必须要使⽤⼀定的算法将数据按页将数据分割成若⼲块,然后按块进⾏写⼊。
如果需要连续写⼊,那么需要分成若⼲次页写⼊,除⾸页和尾页可能不是整页写⼊之外,中间页均可以整页写⼊,那么只要能够计算出⾸页需要写⼊的字节数、尾页需要写⼊的字节数以及需要写⼊的总页数n,然后调⽤n次页写⼊函数,即可实现数据的全部写⼊,第⼀次调⽤时写⼊长度为⾸页字节数,最后⼀次调⽤时写⼊长度为尾页字节数,中间的写⼊长度为整页长度,每次调⽤完毕后地址增加实际写⼊的长度。
网上可以办理银行卡吗具体计算过程:
⾸块偏移 = 起始地址 % 页⼤⼩,
⾸块写⼊长度 = 页⼤⼩ - ⾸块偏移,
如果写⼊长度 < ⾸块写⼊长度,⾸块写⼊长度 = 写⼊长度,
剩余长度 = 写⼊长度 - ⾸块写⼊长度,
剩余整数块数 = 剩余长度 / 页⼤⼩,
尾块长度 = 剩余长度 % 页⼤⼩,
总块数 = 1 + 剩余整数块数,
如果尾块长度不为0,总块数加1。
举例:写⼊起始地址为2,写⼊长度为18,那么通过计算,可以得出⾸页写⼊长度为6,尾页写⼊长度为4,写⼊页数为3,需要调⽤3次页写⼊函数EE_PageWrite()。该函数第⼀个参数为写⼊的地址,第⼆个参数为写⼊数据指针,第三个参数为写⼊的字节数。每次调⽤完毕后数据指针(pBuffer)也需要
增加写⼊的长度。
1 EE_PageWrite(2, pBuffer, 6);
2 pBuffer += 6;
3 EE_PageWrite(8, pBuffer, 8);
4 pBuffer += 8;
5 EE_PageWrite(16, pBuffer, 4);
6 pBuffer += 4;
1/*
2根据要写⼊的地址、长度、页⼤⼩计算如何分页
3输⼊参数:addr:写⼊起始地址
4 len:写⼊数据长度
5 pageSize:每页存储的数据,对于AT24C02来说,该值为8
6要写⼊参数:pFirstPageLen:⾸页要写⼊的字节
7 pLastPageLen:尾页要写⼊的字节
8 pPageNum:总共要写⼊的页数
9*/
10void EE_GetWritePages(uint8_t addr, uint8_t len, uint8_t pageSize,
11 uint8_t * pFirstPageLen, uint8_t * pLastPageLen, uint8_t * pPageNum)
12 {
13 uint8_t firstPageOffset; //⾸页偏移
14 uint8_t otherLen; //去除⾸页之后剩余长度
15 uint8_t otherPageNum; //去除⾸页之后剩余整数页数量
16
17 firstPageOffset = addr % pageSize;
18 *pFirstPageLen = pageSize - firstPageOffset;
19
20if(len < *pFirstPageLen){
21 *pFirstPageLen = len;
22 }
23
24 otherLen = len - *pFirstPageLen;
25 otherPageNum = otherLen / pageSize;
26 *pLastPageLen = otherLen % pageSize;
27
28 *pPageNum = otherPageNum + 1;
29
30if(*pLastPageLen){
31 (*pPageNum)++;
32 }
33 }
源码下载:(不包括⼯程⽂件和库⽂件)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论