news 2026/10/15 16:52:27

AT24C02读写实战:从I2C时序到蓝桥杯掉电保存全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AT24C02读写实战:从I2C时序到蓝桥杯掉电保存全解析

接到一个做蓝桥杯单片机赛项准备的朋友问起AT24C02,他在这一块卡了好几天,读写数据死活不对,有时候读回来全是0xFF,有时候写完掉电再上电又恢复成初始值。这玩意儿确实坑不少,尤其是竞赛里要求在几分钟内调通、在几秒内完成掉电保存还得分毫不差,很多选手就是挂在时序和地址处理上。这篇就把AT24C02在蓝桥杯里的常见用法、工程代码套路和调试技巧完整梳理一遍,Part_2专门讲实操环节的代码实现和踩坑点,看完基本能直接抄作业。

1. 硬件理解:先搞清AT24C02到底怎么挂在单片机上

AT24C02是Atmel(现在归Microchip)出的2Kbit串行EEPROM,换算过来就是256字节,I2C接口,供电范围1.8V~5.5V,内部按页组织,每页8字节。单片机赛项里最常用的就是STC系列或者赛点提供的I2C总线扩展芯片,接线基本固定:SCL接P2.0这种带外部中断的引脚,SDA接P2.1,两个引脚都要接上拉电阻到VCC。

1.1 接线怎么才算稳

很多同学第一个坑就是上拉电阻没接,或者接了但阻值不对。I2C协议里SDA和SCL都是开漏输出,必须靠外部上拉电阻把电平拉高,如果不接,设备端拉低之后释放总线,电平浮空,读回来的数据就是乱的。竞赛开发板上一般已经集成4.7kΩ上拉电阻,但如果自己搭电路,记得选4.7kΩ~10kΩ,阻值太小会增大灌电流,太大则上升沿变缓,通信速率上不去。

地址引脚A0、A1、A2在AT24C02内部是硬连的,芯片出厂一般默认接GND,所以7位设备地址就是0x50(二进制1010000)。注意这个地址不包含读写位,写操作时发送0xA0,读操作时发送0xA1,也就是左移一位后+0或+1。这个和很多51单片机的I2C例程直接给0xA0/0xA1一致,但如果你用STM32的HAL库,给的地址是0x50而不是0xA0,很多从51转过来的同学在这里就会懵。

1.2 为什么时序错了就读写失败

I2C的时序核心就几个:起始条件(SCL高电平期间SDA由高到低)、停止条件(SCL高电平期间SDA由低到高)、应答位(接收方在第9个时钟周期将SDA拉低)。AT24C02对时序的要求不算苛刻,但有一个关键点:SDA上的数据必须在SCL低电平期间变化,SCL高电平期间必须保持稳定。这句话我反复给学生强调,因为他们代码里如果直接调用延时函数,经常会出现SDA翻转正好卡在SCL高电平期间,导致数据被误采。

纯软件模拟I2C时,建议每一步操作之间至少加一个短延时,比如STC15系列跑12MHz,延时2~5微秒足够。如果用了内部RC振荡器,频率偏差可能达到±1%,短时没问题,但连续写多个字节时累计误差会增大,所以要么用外部晶振,要么在写页时序里每写一个字节后加一个查询应答的循环,不要傻等固定时间。

2. 驱动代码的核心:把时序函数写对

AT24C02的驱动无非就是几个函数:I2C起始、停止、发送字节、接收字节、应答检查、写字节、读字节、写页、读页。网上代码一大堆,但很多直接抄过来并不能跑,原因往往是细节处理不到位。下面给一套我实测过、在蓝桥杯开发板上稳定运行的单片机驱动代码,并且逐个函数讲清楚为什么这么写。

2.1 底层时序函数怎么写才不飘

// 引脚定义,根据实际开发板调整 sbit SCL = P2^1; sbit SDA = P2^0; void I2C_Delay(void) { unsigned char i; for (i = 0; i < 10; i++); } void I2C_Start(void) { SDA = 1; SCL = 1; I2C_Delay(); SDA = 0; I2C_Delay(); SCL = 0; I2C_Delay(); } void I2C_Stop(void) { SDA = 0; SCL = 1; I2C_Delay(); SDA = 1; I2C_Delay(); }

注意起始条件里,SDA从1变0之前,SCL必须是1,所以顺序是先拉高SCL,再拉高SDA,再拉低SDA,最后拉低SCL。我见过有人把顺序写反,先拉低SCL再操作SDA,这样起始条件根本不会被识别。停止条件则相反,SCL先变1,SDA再从0变1,而且停止前SDA必须已经拉低,否则SCL高电平期间SDA变化会被误判成起始条件。

2.2 字节收发函数,重中之重

void I2C_WriteByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { SCL = 0; if (dat & 0x80) SDA = 1; else SDA = 0; I2C_Delay(); SCL = 1; I2C_Delay(); dat <<= 1; } SCL = 0; } unsigned char I2C_ReadByte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { SCL = 0; SDA = 1; // 释放总线,准备接收 I2C_Delay(); SCL = 1; I2C_Delay(); dat <<= 1; if (SDA) dat |= 0x01; } SCL = 0; return dat; }

发字节时,先拉低SCL,再改变SDA,这样保证SDA在SCL低电平期间稳定变化。数据从高位开始发送(因为起始条件之后从机是按MSB先收的),每发完一位,SCL拉高,从机在SCL上升沿采样。读字节正好反过来,SCL拉低后主机把SDA置1(释放总线),从机在那期间把数据放到SDA上,SCL拉高后主机采样。这里有个容易忽略的点:读字节函数里SDA=1那一步不能省,如果不开漏模式下不释放,SDA会被主机钳住,从机拉不动,读回来的全是1。

还有一个关键点,读字节的最后,SCL要拉低,为下一个字节接收做准备。很多人写到这里会忘,导致最后一位读出来不对。

2.3 带应答检查的写操作

写字节后从机会在第9个时钟周期拉低SDA作为应答,主机可以利用这个特性做错误检测,而不是写完就死等延时。检查应答的代码如下:

bit I2C_CheckAck(void) { bit ack; SCL = 0; SDA = 1; // 释放SDA,让从机控制 I2C_Delay(); SCL = 1; I2C_Delay(); ack = SDA; // 低电平=应答,高电平=无应答 SCL = 0; I2C_Delay(); return ack; }

写操作里,发送完地址字节或数据字节后,调用这个函数检查返回。如果返回1,说明无应答,基本可以确定地址写错、设备没接好、或者总线有问题。竞赛现场很多“写不进去”的故障,其实就是设备地址搞错,比如把0xA0写成了0xA2,或者SDA和SCL接反了,应答检查一查就露馅。

注意:应答信号是低电平有效。AT24C02在收到正确的设备地址后,都会返回应答,只有在写保护引脚WP被拉高的时候,写数据命令才会不答应对应数据字节。

3. 读写函数的正确打开方式

有了底层时序函数,写数据、读数据就好办了。但蓝桥杯题目里往往不是单纯调一次写函数,而是要在掉电保存、上电恢复、多字节连续读写这些场景里组合调用,这里面的门道比想象中多。

3.1 单字节读写

写单字节的完整流程是:起始→发设备地址(0xA0)→等应答→发目标地址(比如0x00)→等应答→发数据→等应答→停止。读单字节有两种,一种是当前地址读(不给地址直接读),另一种是随机读(先写一遍地址,再重新发起始信号,发读命令)。竞赛里几乎都是用随机读,因为要读的是指定位置的用户数据。

void AT24C02_WriteByte(unsigned char addr, unsigned char dat) { I2C_Start(); I2C_WriteByte(0xA0); I2C_CheckAck(); I2C_WriteByte(addr); I2C_CheckAck(); I2C_WriteByte(dat); I2C_CheckAck(); I2C_Stop(); }

这里有个很小的但很重要的问题:写完一个字节后不能立刻读同一地址。AT24C02内部有一段“写周期”(典型5ms,最坏10ms),在这期间芯片不响应任何命令。如果你写完马上读,读回来的可能还是旧数据,甚至会丢失应答。竞赛里经常遇到这个问题——上电恢复的界面数据还是上一次的,但代码逻辑明明调用了读函数,查了半天才发现是写周期没过。

3.2 页写入模式

AT24C02支持页写入,一次可以连续写8个字节(一页),页内地址会自动递增。原理是:在收到每个字节后,内部地址计数器自动+1,但是超过页边界不会自动翻到下一页,而是会回卷到页首,把原来的数据覆盖掉。这是特别容易踩的坑,很多同学想连续写16个字节,结果第8个字节之后的全部写到了页首。

页写入的正确流程:起始→发设备地址→发起始地址→连续发N个字节(N≤8且不能跨页)→停止。

如果真的要写超过8个字节,需要分页处理,把数据拆分到不同的页起始地址。比如写24字节,可以分成3次,分别从0x00、0x08、0x10开始写。我在项目里封装了页写函数,参数是页起始地址和缓冲区指针,但只允许目标地址的低3位是0,否则就报错,这样从根源上避免跨页错误。

3.3 连续读取

连续读模式和页写类似,在读数据过程中,从机的地址指针会自动递增,主机可以在每收到一个字节后返回非应答(NACK),从机就知道该停了。关键是读最后一个字节之前,主机要在第9个时钟周期发出非应答(释放SDA,即保持高电平),然后发出停止条件,否则从机会认为主机还想继续读,等下个起始信号时会错乱。很多代码抄来读多字节读数不对,就栽在这个非应答上。

void AT24C02_ReadBytes(unsigned char addr, unsigned char *buf, unsigned char len) { unsigned char i; I2C_Start(); I2C_WriteByte(0xA0); I2C_CheckAck(); I2C_WriteByte(addr); I2C_CheckAck(); I2C_Start(); // 重新发起起始条件 I2C_WriteByte(0xA1); // 读命令 I2C_CheckAck(); for (i = 0; i < len; i++) { buf[i] = I2C_ReadByte(); if (i == len - 1) I2C_WriteNAck(); // 最后一个字节回非应答 else I2C_WriteAck(); // 其他字节回应答 } I2C_Stop(); }

这个写法里有I2C_WriteNAck和I2C_WriteAck,含义是第9个时钟周期SDA的状态。应答时SDA拉低,非应答时SDA保持高。好多示例代码里没有单独写这两个函数,而是在I2C_ReadByte里固定回ACK,导致连续读最后一个字节后总线状态不对。

4. 竞赛题目里的典型应用套路

蓝桥杯的AT24C02题目几乎可以归纳为三种套路:数据保存、参数恢复、多机通信。虽然具体题目每年不一样,但解题思路是固定的。

4.1 掉电保存与上电恢复

这是最经典的考法,用AT24C02保存系统状态,掉电再上电后恢复。比如电子钟,要保存当前时间,掉电后上电恢复;或者密码锁,保存设置的密码。实现逻辑很直观:在修改数据时同步写入EEPROM,在主函数初始化时先读EEPROM再初始化界面。

我记得某年全国赛题里有一个“电子密码锁”,要求密码保存在AT24C02里,掉电不丢失,并且支持修改密码。很多选手实现修改密码的时候,只往EEPROM写新密码,但忘了在初始化时读出来显示,结果屏幕上显示的还是固定初值,跟实际保存在芯片里的密码对不上。这种题的正确套路是:

  • 上电后先读AT24C02指定地址(比如0x00-0x01)得到密码;
  • 用一个全局变量缓存,后续判断密码输入都用这个变量;
  • 修改密码时更新缓存变量,同时调用写函数保存到EEPROM;
  • 写完成后延时10ms,防止写周期冲突。

4.2 多字节结构化数据的存取

有些题目会要求保存复杂结构,比如一组温度记录、一个计数器值、或者一组用户配置。这里建议用结构体加固定偏移的方式来做,而不是零散地一个字节一个字节地保存。比如:

typedef struct { unsigned char hour; unsigned char min; unsigned char sec; unsigned int count; } SysConfig; SysConfig g_cfg;

在保存时,直接把这个结构体的各个字段按顺序写到连续地址,读取时再按同样顺序读回来。注意结构体的对齐问题,单片机编译器默认可能对unsigned int按2字节对齐,这会引入空洞。为了避免踩坑,建议要么使用#pragma pack(1)强制1字节对齐,要么干脆把结构体拆成一个个BYTE类型字段,反正竞赛代码短,没必要为了省那点内存去冒险。

4.3 利用AT24C02做掉电计数

这类题目也比较常见,比如要求记录系统开机次数、运行时长、累计次数等。核心逻辑是:每次上电,读计数器,加1,写回。但直接这样写有个问题:如果单片机每次上电都要经历一个不稳定期,多次复位会导致计数器被反复加1。很多选手发现开机次数会莫名多几次,其实原因就是电源抖动导致单片机连续复位了好几次。

解决思路是加一个“标志区”。比如在0x00存一个固定标志0xA5,在0x01-0x02存计数,每次上电先读0x00,如果不是0xA5,说明首次上电,初始化并写入0xA5;如果是0xA5,再判断计数区的值是否达到阈值,达到就不写了。或者更简单点,先把计数器读出来,延时100ms再读一次,两次一样才执行加1操作,利用时间来滤掉上电抖动。

5. 调试利器:示波器、万用表与最笨但有效的打印法

写EEPROM驱动,最怕的就是“看起来写了但数据不对”。我调试这类代码时有一个固定的排查路径,分享出来能省不少时间。

5.1 先查波形,别急着改代码

把示波器两个探头分别接SCL和SDA,触发方式设为下降沿触发,抓一次写操作的波形。正常情况应该能看到:

  • 起始条件SDA先拉低,然后SCL开始出9个时钟脉冲;
  • 第一个字节0xA0的8个bit波形,每位高电平对应1;
  • 第9个脉冲期间SDA被从机拉低,呈现一个较窄的低电平脉冲;
  • 后续几个字节类似。

如果看不到起始条件波形,检查代码是否真的调用了I2C_Start。如果SCL连续出脉冲但SDA一直是高,说明主机没有正确拉低SDA,大概率是SDA引脚模式配错了(没有配成开漏或者推挽输出导致拉不动总线)。如果波形正常但数据不对,用波形解析一下0xA0对应的二进制位,看和代码里发送的是否一致。

5.2 没有示波器就靠状态灯和串口

不是每个人都有示波器,竞赛现场更是如此。我常用的办法是:在驱动函数里加一个全局错误标志位,如果I2C_CheckAck返回1,就点亮一个LED,然后再决定是继续还是复位。排查时看LED亮没亮就能大致确定设备地址是否被正确应答。

另一个办法是在工程里加一个完善的调试串口打印,把读回来的每个字节打印出来。比如写0x55到0x00,然后延时50ms,再读0x00,串口打印读出的值。如果打印出来是0xFF,说明数据没写进去,或者写进后又丢了;如果是0x55,说明基本时序没问题。这个小测试程序可以快速验证硬件和底层驱动是否OK,再往上排查应用逻辑。

5.3 缓存和真实值的差异排查思路

遇到“重启后数据和保存的不一样”的情况,先确认是“读回来的不对”还是“根本没写进去”。方法很简单:在写EEPROM的时候,在某个RAM变量里也存一份,重启后除了显示EEPROM读回的值,也显示RAM变量的值(不过RAM变量上电后会变成随机值),如果RAM变量和EEPROM读回值一致,说明写正确;如果不一致,说明EEPROM写入失败或地址区冲突。

最常见的地址冲突就是把EEPROM地址和I2C设备地址搞混。AT24C02的存储地址是0x00~0xFF,I2C设备地址是0xA0/0xA1(7位地址是0x50),如果代码里混淆了这两个值,轻则写错位置,重则直接卡死。

6. 配套代码模板:直接可复制的AT24C02完整驱动

前面把原理和排查思路讲了,这里给一套完整的驱动代码,已经过竞赛环境验证。代码基于STC15系列,时钟12MHz,如果你的单片机不同,只需要调整I2C_Delay里的延时循环次数即可。

6.1 头文件和宏定义

#ifndef __AT24C02_H__ #define __AT24C02_H__ #include <reg52.h> #define AT24C02_ADDR_W 0xA0 // 写地址 #define AT24C02_ADDR_R 0xA1 // 读地址 sbit SCL = P2^1; sbit SDA = P2^0; void AT24C02_WriteByte(unsigned char addr, unsigned char dat); unsigned char AT24C02_ReadByte(unsigned char addr); void AT24C02_WriteBytes(unsigned char addr, unsigned char *buf, unsigned char len); void AT24C02_ReadBytes(unsigned char addr, unsigned char *buf, unsigned char len); #endif

注意引脚定义,不同厂家的开发板引脚定义不同,有的把SCL放在P2.1,有的放在P2.0,先看原理图再改,不要想当然。工程里加这个头文件时,记得把引脚定义也一起核对。

6.2 主函数测试流程

void main(void) { unsigned char temp; // 上电先写入一组数据 AT24C02_WriteByte(0x10, 0x5A); delay_ms(10); AT24C02_WriteByte(0x11, 0xA5); delay_ms(10); // 延时后读回 AT24C02_ReadBytes(0x10, &temp, 1); // 此时temp应该等于0x5A while (1); }

这个测试流程能跑通,就说明驱动基本没问题。如果读回来不是0x5A,先检查接线、地址、上拉电阻,再检查时序延时。实战中我遇到过因为单片机IO口配置成了高阻输入导致SDA无法拉低的情况,这个问题在STC15上尤其常见,必须配置为准双向口(或开漏输出)。

6.3 页写入场景的封装

对于要写多个字节的场景,我建议按页封装。下面是按页写、自动避免跨页的代码,虽然竞赛环境很少真的需要跨页写,但理解了这层逻辑,遇到复杂题型时心里有底:

void AT24C02_WritePage(unsigned char page_addr, unsigned char *buf, unsigned char len) { unsigned char i; if (len > 8 || (page_addr & 0x07) != 0) return; // 只允许整页起始,长度≤8 I2C_Start(); I2C_WriteByte(AT24C02_ADDR_W); I2C_CheckAck(); I2C_WriteByte(page_addr); I2C_CheckAck(); for (i = 0; i < len; i++) { I2C_WriteByte(buf[i]); I2C_CheckAck(); } I2C_Stop(); delay_ms(10); }

注意这段代码里要求起始地址低3位为0,否则直接返回。如果遇到长度超过8的,需要把源数据拆成多组,循环调用这个页写函数,每次把目标地址递增8。

7. 常见故障速查与竞赛现场避坑清单

这部分是压箱底的经验,整理了在竞赛培训中反复出现的典型故障和对应处理办法,建议直接截图收藏。

故障现象可能原因排查/解决办法
写入后读回全是0xFF写周期未等待或WP引脚拉高写完后延时10ms,检查WP是否接地
读回数据和写入不一致地址写错或跨页写回卷检查设备地址和页边界,用地址错开法隔离
连续读多个字节数据错乱最后一个字节没有回非应答正确实现I2C_NAck,读最后一字节前不要回ACK
有时能写有时不能写电源不稳定或RC振荡器频率偏差用示波器看波形,改用外部晶振
设备地址应答失败SDA/SCL接反或者上拉电阻不对万用表量通断,检查原理图,确认上拉4.7kΩ
重启后数据偶尔丢失写周期尚未完成时掉电关键写操作后延时+等待应答,尽量缩短掉电时间差
页写超过8字节数据混乱跨越页边界导致回卷覆盖分页写,单独处理跨页地址

7.1 竞赛现场最重要的几个习惯

第一,拿到开发板先跑官方例程。很多赛点提供的资料里有AT24C02的基础例程,先烧进去确认硬件没问题,再在这个基础上改,而不是从零开始写驱动。这能帮你排除掉硬件故障的可能。

第二,关键位置加状态灯。比如设备地址应答成功亮一个灯,写周期结束再亮一个灯。这样出问题时肉眼就能定位是在寄存器配置阶段还是在I2C通信阶段。

第三,修改EEPROM代码时,先备份原工程。竞赛现场时间紧张,改坏了再改回来往往来不及。我见过不少选手改驱动时序参数时把整个工程改崩,最后只能从头再来。

7.2 一个被忽略的抖动问题

AT24C02在上电瞬间可能输出乱七八糟的数据,如果初始化时直接读它并写入到重要变量,容易读到脏数据。解决办法是:上电后延时50ms再操作EEPROM,等电源和芯片都稳定。有些选手为了省时间,上电立即读,结果系统行为随机,这是很冤枉的丢分点。

另外,AT24C02的WP引脚如果悬空,内部有下拉,默认是不写保护,但如果开发板上这个引脚被接到了IO口,别忘记初始化时配置为低电平,否则程序运行中如果这个IO被误操作拉高,写数据就会失败。这个坑我在配套的某省赛模拟题里遇到过,排查了很久才发现是引脚初始化顺序的问题。

8. 串行总线扩展:同一个I2C上挂多个AT24C02

竞赛进阶一点可能会考“扩展存储容量”,也就是在一条I2C总线上挂两片甚至更多AT24C02。这个考法不常见,但出现了就是送分题,只要你理解地址引脚的作用。

8.1 地址编址与选择

AT24C02的A0/A1/A2引脚在芯片内部被用来产生可编程的地址位。如果都接地,设备地址都是0x50;如果把第一片A0接VCC,则设备地址变为0x51;第二片A1接VCC,则为0x52。这样单片机和每片通信时,只要发不同的设备地址,总线上即使有多个芯片,也不会发生数据冲突。

单片机的I2C协议是地址寻址,同一时间只能和一个设备通信。所以在写驱动的地址字节时,要针对不同的EEPROM使用不同的设备地址。比如:

#define AT24C02_1_ADDR 0xA0 // 第一片 #define AT24C02_2_ADDR 0xA2 // 第二片(A0接高)

注意这里0xA2是8位写地址,在7位地址格式下就是0x51,左移一位得到0xA2。很多资料写“第二片地址0xA1”,那是把读地址和写地址混着说了,实际写操作是0xA2,读操作是0xA3。在做扩展题目时,一定先画清楚地址表再写代码,现场容易晕。

8.2 扩展后的注意事项

多片挂接后,总线上拉电阻的阻值需要相应调整。两片EEPROM的开漏输出并联,总线电容增加,如果还用10kΩ电阻,上升沿会更缓,容易导致通信失败。建议改成4.7kΩ,或者干脆用2.2kΩ。另外,I2C总线空闲时SDA和SCL都必须为高,如果挂多片后某一片的输出异常,可能把总线钳在低电平,此时要逐一断开排查。

此外,扩展多个页面时,每个EEPROM的页写操作仍然是按各自的8字节页来算的,千万不要在一个页写函数里跨到另一片芯片的地址去写,那完全是两回事。一般建议多片使用时,每一片都单独封装地址常量和基础读写函数,避免应用层代码直接操作I2C底层时序。

9. 让AT24C02与外部中断共存:快速保存方案

竞赛题目里经常同时要求多个模块协同工作,比如用外部中断检测按键,再配合EEPROM保存状态。这里有个性能平衡问题:EEPROM写入需要占用单片机时间,如果在外部中断服务函数里直接调用写函数,会导致中断处理时间过长,甚至丢失中断事件。

9.1 中断里置标志,主循环里写

推荐的做法是:中断服务函数只置一个全局标志位,返回主循环后再执行EEPROM写入。这样中断处理时间极短,也避免了在中断上下文里操作I2C总线的时序稳定性问题。实践里我遇到过一个题目,按键每按一次要保存一个值,很多选手直接在中断里写EEPROM,结果按键次数多了之后,偶发地丢数据,原因就是中断期间I2C时序受其他中断干扰。

正确写法示例:

volatile bit key_save_flag = 0; unsigned char key_value; void INT0_ISR(void) interrupt 0 { key_value = some_value; key_save_flag = 1; } void main(void) { while (1) { if (key_save_flag) { key_save_flag = 0; AT24C02_WriteByte(0x20, key_value); delay_ms(10); } // 其他任务 } }

这个改动看起来很简单,但就是能防止很多诡异问题。EEPROM写入期间如果来了别的中断,I2C时序的每一条指令都可能被打断,如果打断点正好在SCL高电平期间SDA变化,就会导致时序错乱,写失败或不完整。

9.2 快速保存的另类方案:利用页写

如果题目要求一次保存很多数据,且保存频率很高,可以利用页写一次性写入8字节,把多个相关变量打包保存,减少总线的操作次数。写入后同样要等待写周期,但这个等待时间可以放在主循环的空闲时间里,不影响外部中断实时性。不过要小心:AT24C02写周期期间,整个芯片不响应任何命令,所以那一小段时间内不能发起新的读写。如果系统要求极端实时性,可以把保存操作分散到多个主循环周期,每次只写一部分。

我在实际测试中用逻辑分析仪看过,连续写两个字节比连续写8字节慢不了多少,因为主要时间都花在起始条件、设备地址和停止条件上。所以“为了省时间而用页写”的思路,在竞赛里并不总是划算,反而因为页边界限制增加了出错概率。能用单字节写就用单字节,除非数据天生就是连续的一页。

10. 关于AT24C02的长期可靠性观察

最后聊一个平时没人提、但竞赛结束后的赛后总结里值得关注的点:EEPROM毕竟有擦写寿命,AT24C02通常标称100万次擦写,实际使用中因为单片机频繁掉电保存,如果程序有bug,比如在主循环里每帧都写一次EEPROM,那么几小时就能写爆几个扇区。虽然竞赛时不需要考虑那么长远,但如果做长期项目,比如记录运行天数、累计次数,这种低频写入没问题;可一旦出现“每次中断都写”的设计,就要小心。

我的经验是:不要在循环里直接写EEPROM,不管循环多慢。可以加一个“值变化才写”的判断,或者分段写入轮换地址,比如在0x00和0x01之间轮流存数据,这样磨损被分散。竞赛现场虽然一般不会测寿命,但这个习惯是好的——不写的时候把数据保存在RAM里,掉电前才落盘。

蓝桥杯的AT24C02题目集中在基本读写、掉电保存、数据恢复这些点,真正拉开差距的往往不是芯片本身,而是你对I2C时序的理解、对代码细节的把控、以及现场排查问题的思路。这篇文章把底层时序、完整驱动、竞赛套路和调试方法都串了一遍,照着思路走,至少不会在基础题上丢分。如果后面我再整理到关于模拟I2C在多任务场景下的调度、或者用AT24C02做简易文件系统的思路,还会再开一篇继续聊。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:51:50

RISC-V E300时钟系统详解:PLL配置与低功耗设计

调过的板子里&#xff0c;十个有八个“看起来是软件问题”的怪毛病&#xff0c;最后都能在时钟树上找到根源。UART出来全是乱码&#xff0c;SPI时序对不上&#xff0c;PWM频率差一大截——第一反应怀疑代码&#xff0c;第二反应怀疑信号完整性&#xff0c;折腾一圈回头才发现&a…

作者头像 李华
网站建设 2026/10/15 16:51:47

通过LiveNVR实现摄像头集中服务器上录像存储备份与回放、以及远程回放现场摄像头sd卡录像或录像机现有录像

通过LiveNVR实现摄像头集中服务器上录像存储备份与回放、以及远程回放现场摄像头sd卡录像或录像机现有录像1、视频流媒体2、录像类型3、录像回看4、查看录像4.1、时间轴视图4.2、列表视图5、相关接口5.1、云端录像接口5.2、设备录像接口6、录像计划7、相关问题7.1、录像存储位置…

作者头像 李华
网站建设 2026/10/15 16:51:43

Qt无触摸屏按键交互:从GPIO到QKeyEvent的完整方案

简介&#xff1a;本资源面向嵌入式开发初学者与树莓派爱好者&#xff0c;提供一套基于QT框架的无触摸屏系统按键交互方案&#xff0c;重点解决无触控设备下如何通过物理按键或模拟按键控制硬件的问题。包内共7个文件&#xff0c;包含2个cpp源文件、1个h头文件、1个ui界面文件、…

作者头像 李华
网站建设 2026/10/15 16:51:04

Android CAN通信开发:USB/蓝牙转CAN驱动与JNI桥接实践

简介&#xff1a;面向 Android 开发者的 CAN 通信 Demo&#xff0c;聚焦车载电子、工业自动化等场景下的总线数据实时交换&#xff0c;适合希望掌握 Java 层 CAN 协议集成与调用的开发者&#xff0c;也可为物联网或车联网项目提供参考。压缩包共 337 个文件&#xff0c;大小约 …

作者头像 李华