STM32学习笔记(9)——(I2C续)读写EEPROM

  • 一、概述
    • 1. 背景介绍
    • 2. EEPROM简介
  • 二、AT24C02——常用的EEPROM
    • 1. 电路原理图
    • 2. 写操作
      • (1)按字节写操作(Byte Write)
      • (2)按页写操作(Page Write)
    • 3. 读操作
      • (1)随机读操作
      • (2)顺序读操作
  • 三、实战:读写EEPROM(单字节操作)
    • 1. 单字节写入
    • 2. 单字节读取
    • 3. 需要注意的问题
  • 四、实战:读写EEPROM(多字节操作)
    • 1. 页写入
    • 2. 连续读取

一、概述

1. 背景介绍

在微机发展的早期,出现了BIOS(Basic Input Output System),它是个人电脑启动时加载的第一个软件,用来完成对系统的加电自检、各功能模块的初始化、基本输入输出的驱动程序及引导操作系统。人们希望掉电之后 BIOS 数据不能丢失,于是将 BIOS 烧录到 ROM。事实上它是一组固化到计算机内主板上一个 ROM 芯片上的程序。

最初的最初,BIOS 都是通过一种特殊的烧录方法烧入 ROM 中的,但是一旦烧进去,你只能读 ROM,里面的内容是无法更改的。万一发现错误,只能丢弃,换另一块ROM,重做一遍,这就非常麻烦了。

后来的后来,一位以色列工程师发明了EPROM(Erasable Programmable Read-Only Memory),这种芯片比较奇葩,封装顶部开了一个透明小窗口,如果要往芯片内部录入程序,就需要使用强紫外线进行擦除(当然你放到太阳底下也是可以的)。因此,一般情况下为了不破坏内部的数据,就需要用纸来盖住窗口。好景不长,程序员和硬件工程师还是觉得这样太麻烦了。

最后的最后,EEPROM(Electrically Erasable Programmable Read Only Memory) 登场了,它是一种带电可擦可编程只读存储器 EEPROM,你可以在电脑上或专用设备上擦除已有信息,重新编程,这为许多开发人员提供了便利。

如今,40多年过去了,ROM 早已失去当年的完整意思,不再是曾经的只读存储器了,它不仅能读,还能写,然而它的名字却像人类的传统风俗一样被保留了下来,成为硬件发展史上的一颗璀璨的明珠。

2. EEPROM简介

EEPROM(带电可擦可编程只读存储器,Electrically Erasable Programmable Read Only Memory) 是用户可更改的只读存储器,其可通过高于普通电压的作用来擦除和重编程(重写)。不像EPROM芯片,EEPROM不需从计算机中取出即可修改。

在一个EEPROM中,当计算机在使用的时候可频繁地反复编程,因此EEPROM的寿命是一个很重要的设计考虑参数。EEPROM是一种特殊形式的闪存,其应用通常是个人电脑中的电压来擦写和重编程。一般情况下,EEPROM拥有30万到100万次的寿命,也就是它可以反复写入30-100万次,而读取次数是无限的。

二、AT24C02——常用的EEPROM

AT24C02 是一个常用的基于 I2C 通信协议的 EEPROM 元件,例如ATMEL公司的AT24C02、CATALYST公司的 CAT24C02 和ST公司的 ST24C02 等芯片。我们实验板使用的是ATMEL公司的AT24C02(Automotive Temperature Serial EEPROM)。

ATMEL公司曾经推出不同型号的EEPROM:AT24C01A、AT24C02、AT24C04、AT24C08A、AT24C16A,分别对应不同容量:1K (128 x 8)、2K (256 x 8)、4K (512 x 8)、8K (1024 x 8)、16K (2048 x 8)。

AT24C02的容量描述如下:

AT24C02, 2K SERIAL EEPROM: Internally organized with 32 pages of 8 bytes each,
the 2K requires an 8-bit data word address for random word addressing.

1. 电路原理图

首先需要注意一点:AT24C02采用了 I2C 协议的接口,但这不意味着 EEPROM 就一定要用 I2C 接口,EEPROM 也可以用其它接口。I2C 和 EEPROM 没有任何联系。

我们截取了正点原子精英版的电路原理图:

从图中可以知道,AT24C02 有8个接口,每个接口的功能如下表所示:

引脚名 功能
A0-A2 地址输入
SDA I2C数据总线
SCL I2C时钟总线
WP 写保护(Write Protect)
NC 无连接(No Connect)

这里需要特别说明一下 AT24C02 的设备地址的组成。设备地址一共有八位,高四位已经固化为1010,用户不能修改;低四位中,后三位为可配置的地址位,最低一位为读写位(还记得 I2C 的协议层吧,0为写,1为读)。如下表所示:

1 0 1 0 A2 A1 A0 R/W
MSB LSB

所以,一般情况下,我们要进行写操作,设备地址就为1010 0000(十六进制:0xA0);我们要进行读操作,设备地址就为1010 0001(十六进制:0xA1)。

我们的电路图显示,WP 接地,A2-A0 都接地,SDA 和 SCL 与 MCU 主机相连。

2. 写操作

(1)按字节写操作(Byte Write)

与我们之前讲的 I2C 协议类似,字节写入的通讯过程如下:

  • 主机产生起始信号和 EEPROM 地址,并且读写方向为写方向(0)。
  • 主机发送要写入数据的地址,EEPROM 收到后会将其存入缓存中,同时发送应答信号。
  • 主机发送8位数据至 EEPROM ,EEPROM 将数据存入缓存后,开始往非易失区写入数据。注意,这个过程需要一定时间,根据官方手册可知一次写入过程的最大时间为 5ms,此时 EEPROM 不会响应主机任何的请求,相当于 EEPROM 从 I2C 总线断开了。

AT24Cxx官方手册: At this time the EEPROM enters an internally timed write cycle, tWR (Write Cycle Time, Max = 5 ms) , to the nonvolatile memory. All inputs are disabled during this write cycle and the EEPROM will not respond until the
write is complete (see Figure 8 on page 10).

  • 由于 EEPROM 的写入时间比 STM32 的运行速度要慢得多,因此 STM32 会误以为没有收到应答信号。应答轮询(ACKNOWLEDGE POLLING) 前来解决这个问题:STM32 再次发送一个起始信号和 EEPROM 地址,当 EEPROM 完成一次写入周期后,它会发送应答信号至主机。

AT24Cxx官方手册: ACKNOWLEDGE POLLING: Once the internally timed write cycle has started and the
EEPROM inputs are disabled, acknowledge polling can be initiated. This involves sending a start condition followed by the device address word. The read/write bit is representative of the operation desired. Only if the internal write cycle has completed will the EEPROM respond with a “0”, allowing the read or write sequence to continue.

  • 最后,主机发送停止信号,一次通讯过程完成。

(2)按页写操作(Page Write)

由于按字节写入多个数据的时候,每次都要发起起始条件和设备地址,十分耗费时间,于是就有了按页写入的操作。和字节写入有区别的是,页写入是多个数据写入,在此期间不需要起始信号,如上图所示,每成功写入一次数据,地址加一。

这里需要说明一点,对于 AT24C02 的分页管理是每8个字节为一页,它一共有256个字节,所以总页数为 256 / 8 = 16页,因此每一次页写入操作的是8个字节。例如,下面的地址 0-7 为一页:

数据
地址 0 1 2 3 4 5 6 7 8 9

这里还有一个坑,如果你想从地址2到地址9写入数据(a-h),正好是8个地址,你想通过页写将数据写入,会出现这样的情况:

(你以为的结果)

数据 a b c d e f g h
地址 0 1 2 3 4 5 6 7 8 9

(实际的结果:由于地址8和9是下一页,且只能对一整块页进行操作,不能跨页写,所以回到0地址继续写,有可能会把原来数据覆盖掉,这种情况叫 回滚到页首 。每页的首地址是与8对齐的,即 addr mod 8 = 0)

数据 g h a b c d e f
地址 0 1 2 3 4 5 6 7 8 9

另外,当写到 EEPROM 的最后一个地址还要往下写时,会产生溢出,它回到0地址继续写。

3. 读操作

(1)随机读操作

注意,这里的随机指的是可任意读取一个地址的数据。

和之前写操作很类似,不过有一个地方不同:在读取数据之前,STM32 需要将准备读取的数据的地址写入到 EEPROM 的缓存区。接下来 STM32 还要发送一次起始信号和设备地址,选择读方向(1)。

另外,由于是读取不是写入,EEPROM的一次读取周期比写入周期快得多,不需要像写入操作那样应答轮询。

(2)顺序读操作

与页写不同,顺序读不受页的限制,你想读多少就多少。与页写类似,不再赘述。

请注意,以上这4种操作请务必熟练掌握!

三、实战:读写EEPROM(单字节操作)

1. 单字节写入

按照之前的讲解,我们可以写出编写的思路:

  • 产生起始信号
  • 发送从机地址
  • 发送要写入数据的地址
  • 发送要写入的数据
  • 产生停止信号
/*** @brief  软件模拟EEPROM字节写入* @param  addr:要写入数据的地址data:要写入的数据* @retval    无*/
void EEPROM_ByteWrite(uint8_t addr, uint8_t data)
{/* 产生起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS );/* 发送要写入数据的地址 */I2C_SendData(I2Cx, addr);/* 检测EV8事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS );/* 发送要写入的数据 */I2C_SendData(I2Cx, data);/* 检测EV8_2事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS );/* 产生停止信号 */I2C_GenerateSTOP(I2Cx, ENABLE); /* 重新使能ACK */I2C_AcknowledgeConfig(I2Cx, ENABLE);
}

2. 单字节读取

按照之前的讲解,我们可以写出编写的思路:

  • 产生起始信号
  • 发送从机地址
  • 发送要读取数据的地址
  • 再产生一次起始信号
  • 发送从机地址
  • 读取数据
  • 产生停止信号
/*** @brief  软件模拟EEPROM随机读取* @param  addr:要读取数据的地址*data:要接收数据的容器* @retval 无*/
void EEPROM_RandomRead(uint8_t addr, uint8_t* data)
{/* 产生第一次起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) != SUCCESS );/* 发送要读取数据的地址 */I2C_SendData(I2Cx, addr);/* 检测EV8事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS );/* 产生第二次起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Receiver);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS );/* 检测EV7事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS );/* 读取数据寄存器中的数据 */*data = I2C_ReceiveData(I2Cx);/* 产生停止信号 */I2C_GenerateSTOP(I2Cx, ENABLE);/* 重新使能ACK */I2C_AcknowledgeConfig(I2Cx, ENABLE);
}

3. 需要注意的问题

我们在 main 函数调用以上两个函数进行实验:

#include "stm32f10x.h"
#include "usart.h"
#include "i2c.h"
#include "led.h"uint8_t data = 45;
uint8_t data_rec = 0;
uint8_t addr = 11;int main(void)
{       LED_Init();USART_Config();I2C_Config();LED0_ON;LED1_OFF;printf("这是一个IIC通讯实验\n");EEPROM_ByteWrite(addr, data);EEPROM_RandomRead(addr, &data_rec);printf("发送和接收成功!数据为:%d\n", data_rec);LED0_OFF;LED1_ON;while(1){}
}

我们预期的结果是两条 printf 语句都被执行,然而实际结果是只输出了“这是一个IIC通讯实验”,后面的内容并没有输出,这是怎么一回事呢?通过 debug 进行程序调试,我们发现程序卡死在了这条语句上(EEPROM_RandomRead函数内第一次检测 EV6 事件时发生死循环):

while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) != SUCCESS );

这是为什么呢?原来是 EEPROM 写入周期较长,人家还没写完呢,STM32 就要开始读取数据了,发送过去的起始信号和从机地址人家收不到,不会产生应答,接着开始检测 EV5 和 EV6 事件了,就会一直产生 ERROR,就发生死循环了。

所以,大家可以试一下,如果在以下两个语句加上断点进行调试的话,输出结果是正确的。因为运行到断点时程序会暂停,给了 EEPROM 充足的时间写入数据,而且也将 STM32 的操作也暂停了。

 EEPROM_ByteWrite(addr, data);EEPROM_RandomRead(addr, &data_rec);

因此我们要做的是:必须确保 EEPROM 写完后,STM32 才能发起读取操作。这里有两个办法:一是直接在写操作和读操作之间加个延时函数,等待 5ms 再去读取数据;二是检测 EEPROM 是否可以产生应答,如果 STM32 收到应答,那么说明 EEPROM 完成了写入,可以进行读取操作了。这里我们直接给出第二种办法的代码:

 /*** @brief  等待EEPROM写入完成* @param  无* @retval    无*/
void EEPROM_WaitForWriteEnd(void)
{do{I2C_GenerateSTART(I2Cx, ENABLE);while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB) == RESET );/* EV5事件被检测到,发送设备地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);}while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR) == RESET );/* EEPROM内部时序完成传输完成 */I2C_GenerateSTOP(I2Cx, ENABLE);
}

这里说明一下,如果用I2C_CheckEvent函数进行检测,那么也会出现卡死的情况。这个涉及到 event 的清除原理,本人还不是很懂,就没去深究了。

在 main 函数加入等待函数,问题解决:

 EEPROM_ByteWrite(addr, data);EEPROM_WaitForWriteEnd();EEPROM_RandomRead(addr, &data_rec);

四、实战:读写EEPROM(多字节操作)

和上面的思路类似,这里不再赘述。程序均已通过验证,没有问题。

1. 页写入

这个函数我在调用时曾经出现一次卡死的情况,不太清楚什么原因,因为后来又没发生这种情况,可能这种简单的写法不太稳定吧。

/*** @brief  软件模拟EEPROM页写入* @param  addr:要写入数据的地址*data:数组的首地址num:数据个数* @retval    无*/
void EEPROM_PageWrite(uint8_t addr, uint8_t *data, uint8_t num)
{/* 产生起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS );/* 发送要写入数据的地址 */I2C_SendData(I2Cx, addr);/* 检测EV8事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS );while(num--){/* 发送要写入的数据 */I2C_SendData(I2Cx, *data);/* 检测EV8_2事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS );data++;}/* 产生停止信号 */I2C_GenerateSTOP(I2Cx, ENABLE); /* 重新使能ACK */I2C_AcknowledgeConfig(I2Cx, ENABLE);
}

2. 连续读取

 /*** @brief  软件模拟EEPROM连续读取* @param  addr:要读取数据的地址*data:要接收数据的容器num:数据个数* @retval    无*/
void EEPROM_SeqRead(uint8_t addr, uint8_t* data, uint8_t num)
{/* 产生第一次起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) != SUCCESS );/* 发送要读取数据的地址 */I2C_SendData(I2Cx, addr);/* 检测EV8事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS );/* 产生第二次起始信号 */I2C_GenerateSTART(I2Cx, ENABLE);/* 检测EV5事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );/* 发送7位从机地址 */I2C_Send7bitAddress(I2Cx, EEPROM_ADDR, I2C_Direction_Receiver);/* 检测EV6事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS );while(num--){/* 检测EV7事件 */while( I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS );/* 读取数据寄存器中的数据 */*data = I2C_ReceiveData(I2Cx);data++;}/* 产生停止信号 */I2C_GenerateSTOP(I2Cx, ENABLE);/* 重新使能ACK */I2C_AcknowledgeConfig(I2Cx, ENABLE);
}

暂时先写到这,鉴于程序还有可改进之处,以后可能会有补充。

今天,你也是玩过I2C的人了,祝贺学习进度已过半

初学STM32,请多多指教!

STM32学习笔记(9)——(I2C续)读写EEPROM相关推荐

  1. STM32学习笔记之硬件SPI读写与极性设置

    废话不多说讲重点!!! [软件中如何设置SPI的极性和相位]  SPI分主设备和从设备,两者通过SPI协议通讯. 而设置SPI的模式,是从设备的模式,决定了主设备的模式.  所以要先去搞懂从设备的SP ...

  2. STM32学习笔记:FLASH读写之一

    因为关于STM32的Flash相关的知识点比较多,所以该内容的学习我们分为以下4个部分 1.RAM和ROM的一些基本概念 -- STM32学习笔记:FLASH读写之一 2.STM32的Flash寄存器 ...

  3. STM32学习笔记:FLASH读写之二

    因为关于STM32的Flash相关的知识点比较多,所以该内容的学习我们分为以下4个部分 1.RAM和ROM的一些基本概念 -- STM32学习笔记:FLASH读写之一 2.STM32的Flash寄存器 ...

  4. STM32模拟I2C时序读写EEPROM精简版

    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线+外部EEPROM(不需要上拉电阻) 工程介绍:主要文件在USER组中,bsp_i2c_ee.c,bsp_i2c_ee.h ...

  5. 《STM32学习笔记》4——核心功能电路与编程(下)

    接上文,文中的图片,大多数来自视频的截图(来自洋桃电子). 欢迎大家批评指正! STM32学习笔记-专栏 文章目录 一.蜂鸣器驱动 1.蜂鸣器介绍 2.蜂鸣器电路 3.蜂鸣器程序 二. MIDI 音乐 ...

  6. 硬件学习、高速dsp开发板制作、STM32学习笔记

    1.硬件工程师成长之路(1)--元件基础_[云轩]的博客-CSDN博客_硬件工程师的成长之路 总目录:https://blog.csdn.net/weixin_44407238/category_10 ...

  7. STM32学习笔记(三)丨中断系统丨EXTI外部中断(对射式红外传感器计次、旋转编码器计次)

    本篇文章包含的内容 一.中断系统 1.1 中断的定义 1.2 中断优先级 1.3 中断的嵌套 1.4 STM32中的中断系统 1.4.1 STM32的中断资源 1.4.2 嵌套中断向量控制器 NVIC ...

  8. STM32学习笔记(15)——SPI协议

    STM32学习笔记(15)--SPI协议 一.SPI协议简介 1. 物理层 2. 协议层 (1) 通讯的开始与停止 (2)时钟极性CPOL.时钟相位CPHA 二.STM32的SPI外设 1. 通讯引脚 ...

  9. 《STM32学习笔记》3——核心功能电路与编程(上)

    接上文,文中的图片,大多数来自视频的截图(来自洋桃电子). 欢迎大家批评指正! STM32学习笔记-专栏 文章目录 一.核心板电路分析 二.点灯 LED 1.LED电路 2.LED功能相关初始化配置 ...

最新文章

  1. mysql常用命令汇总
  2. 第十六届智能车竞赛参赛队员提问与回答 |2021年7月12
  3. c语言源程序的下载,编程(C语言源程序代码)
  4. python在审计中的应用-基于python的自动化代码审计
  5. python推荐入门书籍-学python入门看什么书
  6. v-pre的指令|| v-cloak 的指令
  7. 笔记45 | 代码性能优化建议[转]
  8. Jenkins持续集成环境, 如何自定义 maven repositories
  9. PythonEggs
  10. springboot 集成mybatis_SpringBoot快速集成Mybatis并轻松上手调试教程,请查收!
  11. git remote prune,git prune,git fetch --prune等有什么区别
  12. 泛微oa部署linux步骤,泛微oa部署微搜功能手册
  13. 图像处理算法 之 Hough变换
  14. mysql中增删改查的详解 例题 sql 语句
  15. 音乐迷(无损音乐下载器)
  16. google Chrome 浏览器升级更新了来源策略,导致不同域名来源页面信息【Referer】只有域名信息
  17. 透过西安未来人工智能计算中心,看到AI不一样的未来
  18. aws lambda_AWS Lambda和Node JS的路由管理框架
  19. HarmonyOS(二)应用开发环境搭建准备
  20. vue项目中通过WebSocket实现实时消息提示及遇到的问题

热门文章

  1. 【北京.5月28日】第六届 Mobile Dev Day – WP7优秀应用经验分享日
  2. 康华医疗旗下康华医院道歉:生命不该用钱衡量,还曾是中植系
  3. dedecms织梦快照被挟持和篡改入侵漏洞修复
  4. jiny的博客开通啦~~
  5. 系统自带ftp工具,4步掌握windows系统自带ftp工具的使用方法
  6. el-select和el-tree树形结构下拉单选和多选
  7. 利用go制作微信机器人
  8. 红黑树的性质以及时间复杂度证明
  9. 构筑数字底座,同济医院用全闪存提速智慧医疗
  10. 让企业用上金融合规专有云,腾讯云专有云TCE矩阵来支招儿!