码迷,mamicode.com
首页 > 其他好文 > 详细

STM32之EEPROM驱动

时间:2020-08-31 13:21:48      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:add   png   连续   实现   else   读取数据   mcu   stm32   不能   

本文介绍如何使用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。

3.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 
 5     if(EE_WaitReady()){
 6         goto iic_fail;
 7     }
 8 
 9     IIC_Start();  
10     IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR);
11     if(IIC_WaitAck()){
12         goto iic_fail;
13     }
14     IIC_WriteByte(addr);
15     if(IIC_WaitAck()){
16         goto iic_fail;
17     }
18     IIC_Start();              
19     IIC_WriteByte(EEPROM_IIC_ADDR | IIC_RD);
20     if(IIC_WaitAck()){
21         goto iic_fail;
22     }
23 
24     for(i = numToRead; i > 0; i--)
25     {
26         *pBuffer++ = ((i == 1) ? IIC_ReadByte(0) : IIC_ReadByte(1));
27     }
28 
29     IIC_Stop();
30     return 0;
31 
32 iic_fail:    
33     IIC_Stop();
34     return 1;
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     
11     if(EE_WaitReady()){
12         goto iic_fail;
13     }
14 
15     IIC_Start();  
16     IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR);
17     if(IIC_WaitAck()){
18         goto iic_fail;
19     }
20     IIC_WriteByte(addr);
21     if(IIC_WaitAck()){
22         goto iic_fail;
23     }
24     for(i = 0; i < numToWrite; i++){
25         IIC_WriteByte(*pBuffer++);
26         if(IIC_WaitAck()){
27             goto iic_fail;
28         }
29     }
30     
31     IIC_Stop();
32     return 0;
33 
34 iic_fail:    
35     IIC_Stop();
36     return 1;
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     
53     for(i = 0; i < pageNum; i++)
54     {
55         if(i == 0){            //首页写入长度为firstPageLen
56             if(EE_PageWrite(addr, pBuffer, firstPageLen)){
57                 goto iic_fail;
58             }
59             addr += firstPageLen;
60             pBuffer += firstPageLen;
61         }else if(i == pageNum - 1){        //尾页写入长度为lastPageLen
62             if(EE_PageWrite(addr, pBuffer, lastPageLen)){
63                 goto iic_fail;
64             }
65             addr += lastPageLen;
66             pBuffer += lastPageLen;
67         }else{                            //除首页和尾页外写入长度为EEPROM_PAGE_SIZE
68             if(EE_PageWrite(addr, pBuffer, EEPROM_PAGE_SIZE)){
69                 goto iic_fail;
70             }
71             addr += EEPROM_PAGE_SIZE;
72             pBuffer += EEPROM_PAGE_SIZE;
73         }
74     }
75     
76     return 0;
77 
78 iic_fail:
79     IIC_Stop();
80     return 1;
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 
 9     for(i = 0; i < 50; i++){
10         IIC_Start();  
11         IIC_WriteByte(EEPROM_IIC_ADDR | IIC_WR);               //发送写命令
12         if(!IIC_WaitAck()){
13             ret = 0;
14             break;
15         }
16         delay_us(100);
17     }
18     IIC_Stop();
19     return 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 */
10 void 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     
20     if(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     
30     if(*pLastPageLen){
31         (*pPageNum)++;
32     }
33 }

 

源码下载:(不包括工程文件和库文件)

 https://files.cnblogs.com/files/greatpumpkin/IIC_soft.rar

STM32之EEPROM驱动

标签:add   png   连续   实现   else   读取数据   mcu   stm32   不能   

原文地址:https://www.cnblogs.com/greatpumpkin/p/13546446.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!