LV16-19-I2C-02-读写EEPROM
本文主要是STM32开发——I2C读写EE2PROM AT24C02的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
- 通用
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.st.com/content/st_com/zh.html | ST官方网站,在这里我们可以找到STM32的相关文档 | |
https://www.stmcu.com.cn/ | 意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档 | |
http://elm-chan.org/fsw/ff/00index_e.html | FatFs文件系统官网 | |
教程书籍 | 《ARM Cortex-M3权威指南》 | ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩 |
《ARM Cortex-M0权威指南》 | ||
《ARM Cortex-M3与Cortex-M4权威指南》 | ||
开发论坛 | http://47.111.11.73/forum.php | 开源电子网,正点原子的资料下载及问题讨论论坛 |
https://www.firebbs.cn/forum.php | 国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT | |
https://www.amobbs.com/index.php | 阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。 | |
http://download.100ask.net/index.html | 韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。 | |
博客参考 | http://www.openedv.com/ | 开源网-原子哥个人博客 |
http://blog.chinaaet.com/jihceng0622 | 博主是原Freescale现NXP的现场应用工程师 | |
cortex-m-resources | 这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接) |
- STM32
STM32 | STM32 HAL库开发实战指南——基于F103系列开发板 | 野火STM32开发教程在线文档 |
STM32库开发实战指南——基于野火霸道开发板 | 野火STM32开发教程在线文档 |
- SD卡
SD Association | 提供了SD存储卡和SDIO卡系统规范 |
点击查看相关文件下载
STM32F103xx英文数据手册 | STM32F103xC/D/E系列的英文数据手册 |
STM32F103xx中文数据手册 | STM32F103xC/D/E系列的中文数据手册 |
STM32F10xxx英文参考手册(RM0008) | STM32F10xxx系列的英文参考手册 |
STM32F10xxx中文参考手册(RM0008) | STM32F10xxx系列的中文参考手册 |
Arm Cortex-M3 处理器技术参考手册-英文版 | Cortex-M3技术参考手册-英文版 |
STM32F10xxx Cortex-M3编程手册-英文版(PM0056) | STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版 |
SD卡相关资料——最新版本 | 有关SD卡的一些资料可以从这里下载 |
SD卡相关资料——历史版本 | 有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议 |
SD 2.0 协议标准完整版 | 这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看 |
一、EEPROM简介
EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。
我们这一节的笔记主要是关于AT24C02的使用,它是一个串行电可擦除和可编程只读存储器(EEPROM),总容量是256(2K/8)个字节,通过IIC通信。它是Atmel公司的一款芯片,我们我使用的战舰V3的开发板上时AT24C02BN,我们可以在这里找到它的数据手册:AT24C01A/02/04/08/16 (microchip.com)。
二、AT24C02功能
1. 框图
我们可以看 AT24C01A/02/04/08/16 (microchip.com)
引脚功能描述如下:
引脚 | 说明 |
---|---|
A0~A2 | Address Input |
SDA | Serial Data |
SCL | Serial Clock Input |
WP | Write Protect |
VCC | Supply Voltage(VCC = 1.8V to 5.5V) |
GND | Ground |
2. 设备地址
我们知道I2C总线是通过设备地址来区分不同的设备,我们使用的AT24C02有三根地址线,它的地址是这样的:
A0A2可以接GND(代表0)也可以接VCC(代表1),这样根据A0A2的接线的不同,可以有不同的地址,比如我们把A0~A2全接地,那么设备地址就是1010000x。(我们可以看 AT24C01A/02/04/08/16 (microchip.com)的 6. Device Addressing)
那么当我们要读数据的时候,发送的地址就是10100001 = 0xA1,当我们要写数据的时候发送的地址就是10100000 = 0xA0。
3. 多少页?
我们需要知道的一些信息:
1 | AT24C02 一共是 2Kb = 2048/8B = 256B |
我们看芯片手册有这么一句: 8-byte Page (2K) Write Modes,也就是说,这个256B按照8字节一页的方式,被分为32页:
1 | AT24C02 一共是 256B/8 = 32 页 |
三、读写数据
1. 写操作
1.1 BYTE WRITE
1.2 PAGE WRITE
2. 读操作
2.1 CURRENT ADDRESS READ
2.2 RANDOM READ
2.3 SEQUENTIAL READ
3. 怎么找到要读写的位置?
上边是读写操作的过程,那么我们在实际读写过程中,怎么确定我们要往那个地址写数据呢?AT24C02一共是2Kbit,也就是256x8bit,也就是一共256页,每一页是1字节(8位),地址在实际读写的过程中是这样的:
地址 | 数据 |
---|---|
0x00 | xxxx xxxx |
0x01 | xxxx xxxx |
0x02 | xxxx xxxx |
…… | …… |
0xFF | xxxx xxxx |
四、I2C读写实例
1. 硬件设计
可以看到这里的AT24C02接在了PB6和PB7,也就是I2C1上边。
2. 物理I2C读写AT24C02
一般步骤如下:
(1)配置通讯使用的目标引脚为开漏模式;
(2)使能 I2C 外设的时钟;
(3)配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
(4)编写基本 I2C 按字节收发的函数;
(5)编写读写 EEPROM 存储内容的函数;
(6)编写测试程序,对读写数据进行校验。
2.1 STM32CubeMX配置
2.2 宏定义
我们先来定义一些要用的宏:
1 |
|
2.3 写入一个字节数据
1 | uint32_t I2C_EE_ByteWrite(uint8_t* pBuffer, uint8_t WriteAddr) |
这里只是简单调用库函数 HAL_I2C_Mem_Write 就可以实现,通过封装一次使用更方。在这个通讯过程中, STM32 实际上通过 I2C 向 EEPROM 发送了两个数据,但为何第一个数据被解释为 EEPROM 的内存地址?这是由 EEPROM 的自己定义的单字节写入时序,见下图:
EEPROM 的单字节时序规定,向它写入数据的时候,第一个字节为内存地址,第二个字节是要写入的数据内容。所以我们需要理解:命令、地址的本质都是数据,对数据的解释不同,它就有了不同的功能。
2.4 按页写入数据
1 | uint32_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint8_t NumByteToWrite) |
在以上的数据通讯中,每写入一个数据都需要向 EEPROM 发送写入的地址,我们希望向连续地址写入多个数据的时候,只要告诉 EEPROM 第一个内存地址 address1,后面的数据按次序写入到address2、 address3…这样可以节省通讯的内容,加快速度。为应对这种需求, EEPROM 定义了一种页写入时序 :
根据页写入时序,第一个数据被解释为要写入的内存地址 address1,后续可连续发送 n 个数据,这些数据会依次写入到内存中。这段页写入函数主体跟单字节写入函数是一样的,只是它在发送数据的时候,使用 while 循环控制发送多个数据,发送完多个数据后才产生 I2C 停止信号,只要每次传输的数据小于等于 EEPROM时序规定的页大小,就能正常传输。
2.5 多字节数据写入
1 | void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) |
多次写入数据时,利用 EEPROM 的页写入方式,避免单字节读写时候的等待。 其实它的主旨就是对输入的数据进行分页 (本型号芯片每页 8 个位)。通过“整除”计算要写入的数据 NumByteToWrite 能写满多少“完整的页”,计算得的值存储在 NumOfPage 中,但有时数据不是刚好能写满完整页的,会多一点出来,通过“求余”计算得出“不满一页的数据个数”就存储在 NumOfSingle 中。计算后通过按页传输 NumOfPage 次整页数据及最后的 NumOfSing 个数据,使用页传输,比之前的单个字节数据传输要快很多。
除了基本的分页传输,还要考虑首地址的问题。若首地址不是刚好对齐到页的首地址,会需要一个 count 值,用于存储从该首地址开始写满该地址所在的页,还能写多少个数据。实际传输时,先把这部分 count 个数据先写入,填满该页,然后把剩余的数据 (NumByteToWrite-count),再重复上述求出 NumOPage 及 NumOfSingle 的过程,按页传输到 EEPROM。
(1)若 writeAddress=16,计算得 Addr=16%8= 0 , count=8-0= 8;
(2)同时,若 NumOfPage=22,计算得 NumOfPage=22/8= 2, NumOfSingle=22%8= 6。
(3)数据传输情况如下表
不影响 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
不影响 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
第 1 页 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
第 2 页 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
NumOfSingle=6 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
(4)若 writeAddress=17,计算得 Addr=17%8= 1, count=8-1= 7;
(5)同时,若 NumOfPage=22,
(6)先把 count 去掉,特殊处理,计算得新的 NumOfPage=22-7= 15
(7)计算得 NumOfPage=15/8= 1, NumOfSingle=15%8= 7。
(8)数据传输情况如下表:
不影响 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
不影响 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
count=7 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
第 1 页 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
NumOfSingle=7 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
EEPROM 支持的页写入只是一种加速的 I2C 的传输时序,实际上并不要求每次都以页为单位进行读写, EEPROM 是支持随机访问的 (直接读写任意一个地址),如前面的单个字节写入。在某些存储器,如 NAND FLASH,它是必须按照 Block 写入的,例如每个 Block 为 512或 4096 字节,数据写入的最小单位是 Block,写入前都需要擦除整个 Block; NOR FLASH 则是写入前必须以 Sector/Block 为单位擦除,然后才可以按字节写入。而我们的 EEPROM 数据写入和擦除的最小单位是“字节”而不是“页”,数据写入前不需要擦除整页。
2.6 读取数据
1 | uint32_t I2C_EE_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead) |
我们只需要确定 I2C 的地址,数据格式,数据存储指针,数据大小,超时设置就可以把想要的数据读回来。
2.7 测试函数
1 |
|
3. 模拟I2C读写AT24C02
我们还可以通过模拟I2C的方式来读写,模拟方式可以看另一篇专门写通信的笔记,这里就不多写了。