AT24C64 I2C EEPROM驱动开发:跨页写、应答轮询与避坑指南
简介AT24C64驱动文件面向嵌入式开发人员尤其是需要在微控制器系统中读写I²C接口EEPROM的工程师与学习者。资源提供drv_at24c64.c与drv_at24c64.h两个核心文件前者实现初始化I²C总线、按地址读取与写入数据、通信错误检查及等待应答等操作后者给出I²C地址、页写入大小、地址位数等宏定义与函数原型便于在其他模块中直接调用。压缩包共3个文件包含1个c源文件、1个h头文件和1个txt说明文档整体约1KB结构精简适合快速集成到固件工程中。已有450人学习下载。借助这套驱动读者可掌握AT24C64的典型读写流程与I²C通信要点用于保存设备序列号、配置参数等小量数据提升代码可移植性与可维护性也可作为嵌入式存储驱动开发的参考模板。1. 从一颗 EEPROM 说起AT24C64 驱动文件到底解决什么问题如果你手头有一块带 I2C 接口的存储芯片丝印写着 AT24C64而你的 MCU 工程里恰好缺一个能直接编译、能读写、能跨页写、能处理应答轮询的驱动文件那这份资源就是冲着你来的。AT24C64 是 64Kbit 容量的 I2C EEPROM换算成字节是 8192 字节页大小 32 字节7 位从机地址由 A2/A1/A0 三根引脚决定。它常出现在需要掉电保存参数、校准系数、设备序列号的板子上。驱动文件的价值不在于“能读写”而在于把跨页写、写周期等待、地址回卷、连续读这些容易翻车的细节一次性封装好。适合正在调试 I2C 存储的嵌入式工程师也适合刚接触 EEPROM 时序、想找一个可复现参考实现的新手。下面按“先讲清原理再落到代码最后说坑”的顺序拆开。2. AT24C64 的 I2C 时序与地址规则驱动前必须对齐的四个参数2.1 从机地址、页边界与容量换算AT24C64 的 7 位从机地址高四位固定为1010低三位由 A2/A1/A0 引脚电平决定。写操作地址是0xA0 | (A2A1A0)读操作地址是写地址加 1。很多驱动翻车就翻在这里有人直接把0xA0写死结果换了板子 A0 拉高就通信失败。容量 8192 字节意味着内部地址是 13 位需要两个字节传输高字节在前。页大小 32 字节写操作不能跨页这是硬件层面的限制不是驱动能绕过的。参数取值说明容量8192 字节64Kbit / 8页大小32 字节单次写不能跨页地址宽度13 位需两字节地址从机地址1010 A2A1A0读地址为写地址 1最大写周期5ms典型值轮询应答更稳2.2 写周期与应答轮询的必要性AT24C64 每次写操作后芯片进入内部写周期典型 5ms期间不响应任何 I2C 命令。如果你写完立刻读大概率读到旧数据或者 NACK。常见做法是延时 5ms 再操作但更稳的是应答轮询反复发起写地址直到芯片返回 ACK说明内部写完成。这个细节决定了驱动在批量写参数时会不会丢数据。我一般会在驱动里提供两个版本一个带固定延时一个带轮询默认用轮询。/* 应答轮询反复发起写地址直到 ACK 或超时 */ static int at24c64_wait_ready(uint8_t dev_addr, uint32_t timeout_ms) { uint32_t start get_tick_ms(); while ((get_tick_ms() - start) timeout_ms) { if (i2c_probe(dev_addr) 0) { /* 收到 ACK */ return 0; } } return -1; /* 超时芯片可能未焊接或总线异常 */ }逻辑说明i2c_probe只发送从机地址不发送数据收到 ACK 即表示芯片空闲。参数timeout_ms建议给 10 到 20留出余量。如果一直超时先查总线上拉电阻和焊接而不是改超时时间。2.3 连续读与地址回卷连续读是 AT24C64 的强项给一个起始地址然后一直读地址会自动递增到 8192 边界回卷到 0。这意味着你可以一次读出一整块参数区不需要每读一字节就重新发地址。但要注意回卷是硬件行为如果你读的长度超过剩余空间数据会从头开始覆盖驱动层最好做长度校验避免调用者误用。/* 连续读从 addr 开始读 len 字节自动处理地址回卷 */ int at24c64_read(uint8_t dev_addr, uint16_t addr, uint8_t *buf, uint16_t len) { if (addr len AT24C64_SIZE) { return -1; /* 驱动层拦截越界不依赖硬件回卷 */ } i2c_start(dev_addr | 0x01); /* 读地址 */ for (uint16_t i 0; i len; i) { buf[i] i2c_read_byte(i (len - 1)); } i2c_stop(); return 0; }参数说明addr是 13 位内部地址len由调用者保证不越界。最后一个字节发送 NACK 再 STOP这是 I2C 连续读的标准收尾。3. 驱动文件落地从写一字节到跨页写的完整实现3.1 单字节写与页写封装单字节写是最小单元发写地址、发地址高字节、发地址低字节、发数据、STOP、等写周期。页写则是在同一页内连续写多个字节地址低 5 位决定页内偏移高 8 位决定页号。如果写入长度超过页尾硬件会回卷到本页开头覆盖而不是进入下一页。这是 AT24C64 最经典的坑驱动必须在页写函数里做拆分。/* 页写自动按 32 字节页边界拆分避免跨页回卷 */ int at24c64_write_page(uint8_t dev_addr, uint16_t addr, const uint8_t *buf, uint16_t len) { while (len 0) { uint16_t page_remain AT24C64_PAGE_SIZE - (addr % AT24C64_PAGE_SIZE); uint16_t chunk (len page_remain) ? len : page_remain; i2c_start(dev_addr 0xFE); /* 写地址 */ i2c_write_byte(addr 8); /* 地址高字节 */ i2c_write_byte(addr 0xFF); /* 地址低字节 */ for (uint16_t i 0; i chunk; i) { i2c_write_byte(buf[i]); } i2c_stop(); if (at24c64_wait_ready(dev_addr, 20) ! 0) { return -1; /* 写周期未完成 */ } addr chunk; buf chunk; len - chunk; } return 0; }逻辑说明page_remain算出当前地址到页尾还剩多少字节chunk取剩余长度和页剩余的最小值。每写完一页就轮询等待再继续下一页。参数dev_addr是写地址addr是 13 位内部地址。这个函数是驱动文件的核心跨页写、写周期等待、地址递增都在里面。3.2 参数区读写与校验实际项目里不会只写一字节通常是保存一个结构体参数区。常见做法是在驱动之上再封一层把结构体序列化成字节数组写入指定地址读回时做 CRC 校验。这样即使写入过程中掉电也能通过 CRC 判断参数是否有效回退到默认值。/* 参数区写入先写数据再写 CRC最后回读校验 */ int at24c64_save_params(uint8_t dev_addr, uint16_t base, const void *params, uint16_t size) { uint8_t crc crc8(params, size); if (at24c64_write_page(dev_addr, base, (const uint8_t *)params, size) ! 0) { return -1; } if (at24c64_write_page(dev_addr, base size, crc, 1) ! 0) { return -1; } return 0; }参数说明base是参数区起始地址建议避开 0 地址因为 0 地址有时被用作上电标志。size是结构体字节数CRC 存在参数区末尾。读回时先读数据再读 CRC比对失败就加载默认参数。3.3 上电初始化与设备探测驱动文件通常提供一个初始化函数做两件事探测芯片是否存在、检查参数区是否有效。探测就是发写地址看 ACK参数区检查就是读 CRC。如果芯片不存在初始化返回错误上层决定是否继续运行。这个设计比“默认成功”要稳避免在芯片虚焊时写出莫名其妙的行为。/* 初始化探测芯片 校验参数区 */ int at24c64_init(uint8_t dev_addr, uint16_t param_base, uint16_t param_size) { if (i2c_probe(dev_addr) ! 0) { return -1; /* 芯片未响应 */ } uint8_t crc_stored, crc_calc; uint8_t *buf malloc(param_size); if (at24c64_read(dev_addr, param_base, buf, param_size) ! 0) { free(buf); return -2; } at24c64_read(dev_addr, param_base param_size, crc_stored, 1); crc_calc crc8(buf, param_size); free(buf); return (crc_calc crc_stored) ? 0 : 1; /* 1 表示参数无效 */ }返回值设计0 表示芯片正常且参数有效1 表示芯片正常但参数无效需要加载默认值负值表示通信失败。调用者根据返回值决定后续流程比单一布尔值信息量大。4. 避坑与排查AT24C64 调试中最容易翻车的五个点4.1 现象写入后立刻读数据没变原因芯片处于内部写周期不响应命令读回的是旧数据或随机值。解决每次写操作后调用at24c64_wait_ready轮询或者至少延时 5ms。不要用“读一次不对再延时读第二次”这种补丁批量写时会累积出错。4.2 现象跨页写时后半段数据覆盖了前半段原因页写超过 32 字节边界硬件回卷到本页开头。解决用at24c64_write_page自动拆分不要自己拼一个大数组直接发。如果已经写了裸驱动检查页写函数里有没有addr % 32的判断。4.3 现象换一块板子就通信失败原因A2/A1/A0 引脚电平不同从机地址变了而驱动里写死了0xA0。解决把从机地址作为参数传入或者用宏定义在板级头文件里配置。探测阶段可以先扫描0xA0到0xAE的偶地址确认实际地址。4.4 现象连续读长度超过剩余空间数据从头开始原因AT24C64 地址到 8192 后自动回卷到 0这是硬件行为。解决在at24c64_read里加addr len 8192的判断直接返回错误。不要依赖硬件回卷那会让调用者困惑。4.5 现象参数区偶尔校验失败原因写入过程中掉电数据写了一半CRC 自然对不上。解决这是 CRC 机制正常工作的表现不是 bug。上层检测到校验失败后加载默认参数并重新保存即可。如果想更稳可以用双备份区加标志位但会增加代码量按项目需求取舍。5. 进阶技巧把驱动做成可移植层与批量写入优化5.1 用函数指针隔离 I2C 硬件层驱动文件如果直接调用某个 MCU 的 I2C 寄存器换平台就要重写。常见做法是定义一个 I2C 操作结构体把 start、stop、write_byte、read_byte、probe 做成函数指针初始化时传入。这样 AT24C64 的时序逻辑完全复用只替换底层实现。typedef struct { int (*probe)(uint8_t addr); void (*start)(uint8_t addr); void (*stop)(void); int (*write_byte)(uint8_t data); uint8_t (*read_byte)(int ack); } i2c_bus_t; static const i2c_bus_t *bus; void at24c64_bind_bus(const i2c_bus_t *b) { bus b; /* 绑定后所有操作走函数指针 */ }参数说明probe返回 0 表示 ACK非 0 表示 NACK。read_byte的ack参数为 1 时发送 ACK为 0 时发送 NACK。绑定后驱动不再依赖具体硬件移植时只写一个适配层。5.2 批量写入时合并轮询等待如果你要写多个不连续的参数块每块都等 5ms 会拖慢启动时间。一个技巧是连续发起多个页写中间不等待最后统一轮询一次。但要注意芯片在写周期内不响应任何命令所以第二个页写的起始信号会 NACK。正确做法是每页写完后轮询但轮询本身很快实际开销主要是写周期。如果参数区是连续的直接用at24c64_write_page一次调用内部自动拆分和等待比手动分块更省心。5.3 验证方法用已知图案做全片读写测试驱动写完后我习惯做一次全片测试从 0 到 8191 写入递增图案再读回比对。这个测试能暴露页边界、地址回卷、写周期等待的所有问题。测试代码不复杂但跑一遍心里有底。/* 全片测试写入递增图案并回读比对 */ int at24c64_self_test(uint8_t dev_addr) { uint8_t wbuf[32], rbuf[32]; for (uint16_t addr 0; addr AT24C64_SIZE; addr 32) { for (int i 0; i 32; i) { wbuf[i] (uint8_t)((addr i) 0xFF); } if (at24c64_write_page(dev_addr, addr, wbuf, 32) ! 0) { return -1; } if (at24c64_read(dev_addr, addr, rbuf, 32) ! 0) { return -2; } if (memcmp(wbuf, rbuf, 32) ! 0) { return -3; /* 比对失败定位到具体地址 */ } } return 0; }逻辑说明每次写 32 字节正好一页写完立即读回比对。如果失败返回值能区分是写失败、读失败还是数据不一致。这个测试会擦写全片量产固件里不要保留只在调试阶段用。从那以后我每次拿到新的 EEPROM 驱动都强制先跑一遍全片读写测试再跑一遍掉电模拟确认 CRC 机制真的能兜住。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

如何快速上手Fable-5 Premium V2:新手10分钟跑通第一次智能体微调

如何快速上手Fable-5 Premium V2:新手10分钟跑通第一次智能体微调

【免费下载链接】fable-5-premium-v2 项目地址: https://ai.gitcode.com/hf_mirrors/saidutta69/fable-5-premium-v2 点击查看 免费下载 Fable-5 Premium V2 是一个包含 10 万条高质量智能体轨迹的 SFT 微调数据集,专为训练"会调用工具"的 Ag…

2026/10/16 20:09:38 阅读更多 →
DPDK性能机制解析:中断、轮询与内存子系统的体系结构

DPDK性能机制解析:中断、轮询与内存子系统的体系结构

简介:《深入浅出DPDK》读书笔记PDF由作者Rong_Toa整理,面向准备系统学习高性能网络I/O、用户态驱动及NFV/虚拟化原理的开发者。笔记覆盖DPDK核心库、多队列与流分类、NAPI、Netmap、用户态驱动、无锁环与大页内存等关键技术,并梳理了rte_eal_…

2026/10/16 20:08:38 阅读更多 →
GD32VF103硬核实战:RISC-V MCU驱动VGA与PS2的全栈工程解析

GD32VF103硬核实战:RISC-V MCU驱动VGA与PS2的全栈工程解析

1. 为什么是GD32VF103?——从RISC-V生态落地看硬件选型的底层逻辑很多人看到“GD32VF103 VGA PS2”这个组合,第一反应是:这不就是个复古玩具项目?但实际动手做过的人才知道,它背后是一次对国产RISC-V微控制器工程化能…

2026/10/16 20:08:38 阅读更多 →

最新新闻

AirLLM:单张显卡也能跑70B大模型的分层推理显存优化方案

AirLLM:单张显卡也能跑70B大模型的分层推理显存优化方案

1. AirLLM 到底是什么:一个把“大模型塞进小显存”的暴力优雅方案QLoRA、GPTQ、GGUF,这些名字想必大家都不陌生。这几年为了在消费级显卡上跑大模型,各路方案层出不穷,核心思路无非两种:量化压缩模型权重,或…

2026/10/18 16:45:42 阅读更多 →
Playnite 主题定制 10 分钟上手:把游戏库界面改成自己的样子

Playnite 主题定制 10 分钟上手:把游戏库界面改成自己的样子

Playnite 主题定制 10 分钟上手:把游戏库界面改成自己的样子 【免费下载链接】Playnite Video game library manager with support for wide range of 3rd party libraries and game emulation support, providing one unified interface for your games. 项目地址…

2026/10/18 16:45:42 阅读更多 →
AI教材生成全流程:低查重高质量编写实战指南

AI教材生成全流程:低查重高质量编写实战指南

最近一年我接了好几本教材编写的活儿,从高校专业课讲义到企业内训手册都有。绕不开的两件事:一是写不完的量,二是躲不掉的查重率。身边不少同行开始把AI教材生成工具纳入工作流,确实能把初稿周期从一两个月压缩到一两周&#xff0…

2026/10/18 16:45:42 阅读更多 →
视觉目标跟踪丢失后重定位:丢失检测与搜索策略工程实践

视觉目标跟踪丢失后重定位:丢失检测与搜索策略工程实践

1. 从“跟丢”说起:为什么重定位是视觉跟踪的必修课做过视觉目标跟踪的人都有一个共识:跟踪算法在视频前几十帧跑得再漂亮,都不算数,真正的考验出现在目标被遮挡、快速运动、出画再入画、或者画面剧烈抖动的那一刻。我见过太多Dem…

2026/10/18 16:45:42 阅读更多 →
PHP轻量级代理分销系统:三级分佣+订单穿透+自动结算

PHP轻量级代理分销系统:三级分佣+订单穿透+自动结算

简介:这是一套基于PHP开发的代理分销型商城系统源码,面向中小型电商创业者、PHP初中级开发者及二次开发学习者,解决多级代理管理、佣金自动结算与分销渠道拓展等核心运营需求。资源包为RAR格式,大小11.5MB,虽未提供具体…

2026/10/18 16:45:42 阅读更多 →
深度学习实战:食物分类从数据准备到部署完整指南

深度学习实战:食物分类从数据准备到部署完整指南

先说实话,做了几年图像分类的活儿,我越来越觉得“食物分类”这个题目被低估了。它不像猫狗识别那样简单到几轮就收敛封顶,也不像人名识别那样需要极其苛刻的数据条件。食材种类上百、同一种食材换个切法和熟度就完全变了样、摆盘和背景还在添…

2026/10/18 16:44:42 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →