☰
基于STM32的AT24C系列EEPROM通用驱动与分页写实现
2026/10/5 22:39:20 网站建设 项目流程

简介:面向STM32开发者的AT24C系列EEPROM驱动代码包,覆盖AT24C32、AT24C64与AT24C128三种常规容量,适用于物联网终端、智能家居、工业控制等需要掉电保存配置信息或用户数据的项目。压缩包内共有两个文件:一个C源文件负责I²C读写时序与命令封装,另一个头文件提供函数声明和地址定义,整体不足四KB,移植时只需复制源码并调整引脚,即可快速接入现有工程。驱动编写过程中重点考虑了I²C总线初始化、STM32复用功能配置、七位设备地址与读写控制位组合、字节寻址和连续写操作等关键环节,同时给出超时与校验出错时的基础处理思路,便于理解硬件I²C与软件模拟I²C两种实现方式的差别。代码结构简洁,注释清晰,适合学习与二次开发。目前已有四千二百四十二人学习下载,对正在学习AT24C系列驱动编写或需要在嵌入式项目中增加外部存储功能的工程师具有直接参考价值。 做嵌入式这几年,EEPROM基本是绕不开的存储器件。ST芯片、各家电控板、传感器模块上都能看到AT24系列,我在STM32项目里前后用过AT24C02、AT24C32、AT24C64,后来做设备日志存储又换到AT24C128。坦白说,从AT24C02换到AT24C32时挺折腾——字地址从1字节变成2字节,读写出参完全变了。但AT24C32、AT24C64、AT24C128这三颗,协议基本一致,只是容量、页大小、从机地址引脚数有差异。用宏把这些差异参数化后,我从三份驱动瘦身成一份通用驱动,后续换型号只改一行宏。这篇文章就把这套驱动程序的设计思路、完整代码和实际调试中踩过的坑一次性讲清楚。

1. 三个型号到底差在哪:容量、页大小与I2C从机地址

1.1 容量与字地址

先说最基础的单位换算。AT24C32的"32"指的是32Kbit,换算成字节要除以8,也就是4KB;AT24C64是8KB;AT24C128是16KB。很多新手在这里直接被坑,按32KB去规划存储结构,写到一半发现地址越界。

这三颗芯片内部存储地址都用2字节表示,数据手册里叫Word Address。也就是说,一次I2C写操作要先发内存地址高字节,再发低字节,然后才发数据。这和AT24C01/02/04/08/16这系只用1字节地址的老芯片完全不同。在STM32 HAL库里,对应参数是I2C_MEMADD_SIZE_16BIT,写驱动时千万别选成8位。

型号容量(字节)地址范围页大小可配置从机地址
AT24C3240960x000~0xFFF32字节0x50~0x57
AT24C6481920x000~0x1FFF32字节0x50~0x57
AT24C128163840x000~0x3FFF64字节0x50~0x51

1.2 从机地址:AT24C128 只有 A2 有效

I2C设备地址是硬件引脚决定的,AT24C32/64有A2、A1、A0三个地址引脚,7位设备地址格式是1010 A2 A1 A0,基地址0x50,三根引脚排列组合后可以挂8片。AT24C128就不一样了,它只引出A2引脚,A1和A0在芯片内部被固定为0,所以同一总线上最多挂2片,地址只能是0x50或0x51。

这个差异在换型号时最容易翻车。我见过一个同事把板子上的AT24C64直接换成AT24C128,驱动地址没改,I2C扫描死活找不到设备。一查,原来AT24C64的A1、A0接了IO口拉高,设备地址是0x53;AT24C128根本没有A1、A0引脚,实际地址变成0x50,不查手册根本想不到。

1.3 页大小影响写入策略

EEPROM的页写入机制决定了驱动怎么写。AT24C32/64的页大小是32字节,AT24C128是64字节。在一个页写周期内,连续写入的数据不能超过页大小,一旦越过页末尾,地址会回卷到本页开头,把刚才写的数据覆盖掉。

所以驱动的写函数核心就一句话:按页拆分写入。先算当前地址距离本页末尾还剩多少字节,本次最多写这么多;写完了再推进地址,直到所有数据写完。这个逻辑我会在第三节详细展开,它是整个驱动最重要的部分。

2. 为什么值得抽一套通用驱动而不是三份驱动

2.1 三颗芯片的协议几乎相同

AT24C32/64/128的操作时序完全一致:START -> 从机地址+写位 -> 内存地址高字节 -> 内存地址低字节 -> 数据 -> STOP。读操作就是随机读前面加一个重复START再切到读模式。差异只有三个参数:容量上限、页大小、设备地址位宽。

如果给每颗芯片单独写一份驱动,读函数、写函数的主体结构几乎一样,只是宏定义不同。后果就是维护成本翻三倍——修一个跨页bug要改三处。我把差异点抽成宏,用条件编译选择型号,一份驱动走天下。产品线从AT24C64升到AT24C128时,只需要改AT24C_DEVICE这个宏,其他代码一概不动。

2.2 硬件接线与I2C初始化

通用驱动要跑稳,硬件基础必须对。SDA和SCL都是开漏引脚,必须接上拉电阻到VCC。STM32的I2C引脚在CubeMX里要配置为开漏复用模式,上拉电阻一般选4.7k,如果总线上设备多、走线长,或者频率用到400kHz以上,换成2.2k更稳。上拉电阻太大,信号上升沿变缓,通信容易丢数据;太小灌电流偏大,长期可靠性受影响。

EEPROM的VCC范围通常是1.8V~5.5V,和STM32同电源供电最省事。WP写保护引脚必须处理,最简单粗暴的做法是直接接地,让芯片永远可写。A2/A1/A0根据你规划的设备地址接VCC或GND,不要悬空。

CubeMX里选择I2C1或I2C2,时钟配置到400kHz快速模式即可。AT24C系列最高支持1MHz,但MCU端I2C外设在400kHz以上时对板上走线和上拉很敏感,不是必须的话没必要冒险。

3. 驱动实现:宏配置、分页写、ACK轮询

3.1 头文件:把差异全部收敛成宏

先来看头文件的关键部分。型号选择、页大小、最大地址、设备地址都在这里定义,驱动源文件只跟这些宏打交道。

#ifndef __AT24CXX_H #define __AT24CXX_H #include "main.h" /* 型号选择 */ #define AT24C32 0 #define AT24C64 1 #define AT24C128 2 #define AT24C_DEVICE AT24C128 /* 硬件地址引脚电平,根据实际接线修改 */ #define AT24C_A2_LEVEL 0 #define AT24C_A1_LEVEL 0 #define AT24C_A0_LEVEL 0 #if (AT24C_DEVICE == AT24C32) #define AT24C_PAGE_SIZE 32 #define AT24C_MAX_ADDR 0x0FFF /* 4096B */ #elif (AT24C_DEVICE == AT24C64) #define AT24C_PAGE_SIZE 32 #define AT24C_MAX_ADDR 0x1FFF /* 8192B */ #elif (AT24C_DEVICE == AT24C128) #define AT24C_PAGE_SIZE 64 #define AT24C_MAX_ADDR 0x3FFF /* 16384B */ #else #error "Unknown AT24C device" #endif /* HAL库要求7位地址左移1位,0x50 << 1 = 0xA0 */ #define AT24C_BASE_ADDR (0x50 << 1) #if (AT24C_DEVICE == AT24C128) #define AT24C_DEV_ADDR (AT24C_BASE_ADDR | (AT24C_A2_LEVEL << 2)) #else #define AT24C_DEV_ADDR (AT24C_BASE_ADDR | (AT24C_A2_LEVEL << 2) | \ (AT24C_A1_LEVEL << 1) | AT24C_A0_LEVEL) #endif uint8_t at24cxx_Init(void); uint8_t at24cxx_WriteBytes(uint16_t addr, uint8_t *pData, uint16_t len); uint8_t at24cxx_ReadBytes(uint16_t addr, uint8_t *pData, uint16_t len); #endif

AT24C_DEV_ADDR这个宏要注意,HAL库的DevAddress参数要求填7位地址左移1位的8位格式,也就是0x50要写成0xA0。bit0由HAL内部根据读写操作自动设置,用户不要自己拼读写位。

3.2 写函数:先算页剩余空间再下笔

写函数是整个驱动的核心,分页逻辑就在这里:

#include "at24cxx.h" extern I2C_HandleTypeDef hi2c1; #define AT24C_I2C_TIMEOUT 100 static void at24cxx_WaitReady(void) { while (HAL_I2C_IsDeviceReady(&hi2c1, AT24C_DEV_ADDR, 1, AT24C_I2C_TIMEOUT) != HAL_OK) { /* 继续轮询,直到EEPROM内部写周期结束 */ } } static uint8_t at24cxx_IsAddrValid(uint16_t addr, uint16_t len) { if ((addr + len) > (AT24C_MAX_ADDR + 1)) { return 0; } return 1; } uint8_t at24cxx_WriteBytes(uint16_t addr, uint8_t *pData, uint16_t len) { uint16_t offset = 0; uint16_t chunk; if (pData == NULL || len == 0) { return 2; } if (!at24cxx_IsAddrValid(addr, len)) { return 3; } while (offset < len) { /* 当前地址距离本页末尾还剩多少字节 */ chunk = AT24C_PAGE_SIZE - (addr % AT24C_PAGE_SIZE); /* 本次实际写入长度不能超过剩余数据长度 */ if (chunk > (len - offset)) { chunk = len - offset; } if (HAL_I2C_Mem_Write(&hi2c1, AT24C_DEV_ADDR, addr, I2C_MEMADD_SIZE_16BIT, pData + offset, <p> <a href="https://download.csdn.net/download/qq_35731693/20387665" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询