IIC-24C04与数码管联调:从开漏上拉到页写边界的完整指南
简介:面向单片机初学者的C语言程序设计实例,围绕IIC总线协议与24C04存储器读写、数码管显示联动展开,帮助理解串行EEPROM通信时序及数码管动态扫描原理。压缩包共15个文件,约53KB,涵盖Keil uVision2工程(.Uv2/.opt)、C源码(.c)、编译生成文件(.hex/.bin/.OBJ/.lst/.M51)以及Proteus仿真电路(.DSN/.DBK),可直接打开工程或运行仿真查看效果。包内还集成24C04芯片相关文件,便于对照原理图、程序与编译信息进行学习调试。已有321人学习下载,适合正在练习IIC通信、单片机存储扩展或数码管显示开发的读者,用于代码研读、仿真验证或二次修改。
1. 为什么偏偏是 IIC-24C04 配数码管:一个掉电不丢数据的显示模块
做单片机项目做到“存参数”这一步,基本都会撞上 24C04。你要调一个亮度值、存一组校准系数,或者给设备记一个掉电前的运行状态,RAM 一断电就全没了,而 24C04 这类 IIC 接口的 EEPROM 就是为了干这个。配合数码管把当前值显示出来,就是一个完整的“参数可读写、掉电不丢失”的最小系统。这个标题里的“单片机C语言程序设计 IIC-24C04与数码管”不是一个高深课题,是每个做 51、STM32 的工程师迟早要过的一道坎。
我最早做这个组合是为了一个调光台:四个按键调亮度,数码管显示当前挡位,断电再上电必须恢复上次的值。当时如果直接写死在代码里,用户每次开机都从默认值开始,根本没法用。把数据存进 24C04,开机读出来塞给数码管,这才算“会做产品”。这套组合适合两类人:一是刚点完流水灯、想往“存数据”走的 C 语言入门者,二是已经在用 IIC 但只敢调库、没看过时序图的人。下面我把当时踩过的坑,按“物理层 -> 芯片时序 -> 代码 -> 数码管”的顺序一次讲透。
2. IIC 的物理层先别急着写代码:开漏上拉决定了你的波形是方波还是台阶
很多初学者一上来就对着时序图画波形、写 SCL 高低电平,结果逻辑分析仪一看,SCL 上升沿跟爬坡一样,甚至 SDA 上出现一个不上不下的台阶。这不是你代码写得慢,是物理层选型错了。IIC 总线上的设备,SDA 和 SCL 引脚必须是开漏输出,再通过外部上拉电阻拉到高电平。
2.1 为什么 IIC 不能用推挽输出:两个设备同时抢总线会瞬间短路
单片机的 GPIO 通常可以配置成推挽输出或者开漏输出。推挽的意思是:输出高电平时,引脚内部直接接到 VCC,低电平时直接接到 GND,驱动能力强、翻转快。但 IIC 总线上挂着多个设备,如果主控用推挽输出高电平、某个从机恰好也在驱动低电平,那这个瞬间就是 VCC 直接对 GND 短路,轻则波形畸变、数据错乱,重则烧引脚。所以 IIC 协议强制要求开漏:设备只能把引脚拉低,想输出高电平就把引脚“放开”,靠上拉电阻慢慢把电平拉上去。这也是热词里“iic为什么不能用推挽”的标准答案。
开漏带来的副作用是上升沿变慢:RC 电路里,上拉电阻和总线寄生电容组成一个低通环节。电阻越大,上升沿越慢,波形就越像台阶。我做过一次实验,10kΩ 上拉、20cm 杜邦线连接,SCL 的上升沿肉眼可见地拖了 1μs 多;换成 4.7kΩ 之后,波形干净很多。所以上拉电阻的取值是第一个要定的参数。
2.2 上拉电阻取多大:从 4.7k 起步,按总线和速率微调
常见的取值有 1k、2.2k、4.7k、10k,其中 4.7k 是覆盖面最广的默认值,适合 5V 电压、标准模式 100kHz、总线长度在几十厘米以内的情况。如果你用的是 3.3V 主控,上拉电阻可以略小,比如 2.2k 到 3.3k,保证高电平能被可靠识别;如果总线上只挂了一个从机、距离很短,10k 也能工作,只是余量小。判断标准只有一个:用示波器或逻辑分析仪看上升沿,如果边沿太缓,就往小调,但不要低于 1k,否则低电平的灌电流会大到超出从机的手册规格。
提示:测量上升沿时,要从波形开始变陡的地方量到进入高电平阈值为止。很多 51 单片机的 IIC 引脚不是真正的开漏模式,而是“写 1 就变成高阻”,这时需要外部上拉;STC 系列的部分引脚可以配置成准双向口,内部有弱上拉,但驱动能力有限,我一般还是会外加一个 4.7k 电阻,省得后面换芯片还得改板子。
如果你的波形已经出现了“台阶”,先用万用表确认引脚是不是被配置成了推挽,再检查上拉电阻有没有虚焊。很多时候不是电阻值的问题,而是你把 SCL 接到了某个默认强推挽的引脚上。
3. 24C04 的地址结构与页写边界:不看清这两个细节,代码写得再对也白搭
IIC 协议本身只有起始、停止、应答、数据字节这几个动作,但 24C04 不是裸的存储器,它有自己的地址映射规则和写操作限制。我看过不少人的代码,函数写得像模像样,就是读写结果不对——问题基本都出在“器件地址”和“页写跨边界”这两处。
3.1 24C04 的器件地址:A8 是藏在地址字节里的,不是单独一根地址线
先理清 24C04 和 24C02 的区别:24C02 容量 256 字节,片内寻址用 8 位地址就够了;24C04 容量 512 字节,8 位地址只能覆盖 256 个字节,所以 24C04 把最高位地址 A8 塞进了器件地址字节的最低位。也就是说,写 24C04 时,控制字节不是简单的 0xA0,而是要判断你要访问的是前 256 字节还是后 256 字节。
数据手册上通常会给两个从机地址:0xA0(A8=0)访问 0x00~0xFF,0xA2(A8=1)访问 0x100~0x1FF。这个位和芯片的 A2、A1、A0 引脚没关系,24C04 的 A0 引脚是无效的,A1、A2 才是硬件地址位。我第一次用的时候,想当然地以为 A8 是 24C04 的 A0 引脚,结果硬件上接死了地址,软件里又去读引脚状态,浪费了整整一个下午。
3.2 一页只有 16 字节:跨页写是数据覆盖的最大元凶
24C04 的页大小是 16 字节。所谓“页写”,就是一次写操作可以连续写入最多 16 字节,但前提是这 16 字节必须落在同一页内。比如说你要从地址 0x0F 开始连续写 4 个字节:0x0F、0x10、0x11、0x12,这个第 2 个字节已经跨到了下一页(0x10 是新一页的起始地址)。如果直接调用页写函数,芯片内部地址计数器会回卷到本页开头,也就是 0x00,然后继续写——于是你不想碰的 0x00、0x01、0x02 全被覆盖了,而且你写进去的数据顺序也乱了。
所以写 24C04 的正确姿势是:先把待写数据的地址范围算一遍,如果跨页就拆成多次写操作。每次写入的字节数不超过min(剩余字节数, 页边界 - 当前地址),边界地址等于(当前地址 / 16 + 1) * 16。这个判断写到代码里就是两行运算的事,但漏掉它就等于在给产品埋雷。
3.3 写周期是 5ms:写完不等待,读回来的全是 FF
24C04 内部写完一个字节(或一页)之后,进入一个叫“写周期”的内部编程阶段,典型值是 5ms,最长可能到 10ms。在这个期间,芯片不响应任何 IIC 操作,你还得想一个“怎么知道它写完”的办法。两个常见做法:固定延时 10ms,或者用应答查询——在写周期里向芯片发送它的从机地址,芯片会不应答(SDA 拉不低),你就继续重发地址,直到收到 ACK 为止。第二种方法更可靠,因为你不需要猜芯片最坏情况是多少毫秒,也省时间。
注意:千万不要在写周期内连续发两个写命令,第二个命令会被芯片忽略,而且第一个命令的数据可能只写了一半。我有一次就是发完页写直接延时 2ms 就去读,结果读出来一半是旧数据一半是新数据,排查了半小时才发现是写周期没等够。
4. 软件 IIC 源码怎么组织:底层时序、字节级读写、页写拆分三层结构
51 这种平台,硬件 IIC 模块不是标配,就算有,很多人也宁可用软件模拟,因为引脚随意、调试直观。软件 IIC 的代码结构我一般分成三层:最底层是 SCL/SDA 的电平翻转和延时,中间是起始、停止、发送一字节、接收一字节这些协议原语,最上层才是针对 24C04 的读地址、写页、连续读这些业务函数。下面直接给一套能用的核心代码,以 STC 51 为例,引脚定义放在文件开头方便你改。
// IIC 引脚定义:默认 P2.0=SCL,P2.1=SDA sbit IIC_SCL = P2^0; sbit IIC_SDA = P2^1; #define IIC_DELAY() _nop_(); _nop_(); _nop_(); _nop_() // 起始信号:SCL 高电平期间,SDA 由高到低 void IIC_Start(void) { IIC_SDA = 1; IIC_SCL = 1; IIC_DELAY(); IIC_SDA = 0; // SDA 先拉低,建立起始条件 IIC_DELAY(); IIC_SCL = 0; // 拉低 SCL,准备发送数据 } // 停止信号:SCL 高电平期间,SDA 由低到高 void IIC_Stop(void) { IIC_SDA = 0; IIC_SCL = 1; IIC_DELAY(); IIC_SDA = 1; // 上升沿表示停止 IIC_DELAY(); } // 发送一个字节,返回从机的 ACK 位:0=有应答,1=无应答 bit IIC_WriteByte(unsigned char dat) { unsigned char i; bit ack; for (i = 0; i < 8; i++) { IIC_SCL = 0; if (dat & 0x80) IIC_SDA = 1; // 先发最高位 else IIC_SDA = 0; IIC_DELAY(); IIC_SCL = 1; // SCL 高电平期间,SDA 数据必须稳定 IIC_DELAY(); dat <<= 1; } IIC_SCL = 0; IIC_SDA = 1; // 释放 SDA,让从机拉低给 ACK IIC_DELAY(); IIC_SCL = 1; IIC_DELAY(); ack = IIC_SDA; // 读取 ACK:0 表示从机应答成功 IIC_SCL = 0; IIC_DELAY(); return ack; }这里几个参数值得说清楚。IIC_DELAY()里的_nop_()数量决定了 IIC 的时钟频率,51 在 12MHz 晶振下,一个_nop_()大约是 1μs,四个就是约 4μs,算下来 SCL 频率在 100kHz 左右,正好落在标准模式。如果你用 24MHz 晶振,延时不变,频率会翻倍到接近 200kHz,这时候要保证 24C04 工作在快速模式(400kHz 内)且总线电容不大。dat & 0x80判断最高位,然后左移,这是标准的“从高位到低位逐位发送”的顺序,初学者最容易犯的错是写成从低位开始发,手册里明确写了 MSB first。
接收字节的函数跟发送是对称的,区别在于 SCL 高电平期间去读 SDA,而不是去写。这里有一个方向切换的问题:51 的准双向口在读之前要先把引脚写 1,否则读到的永远是前一次的输出电平。很多人在接收函数里漏了这一句,导致读回来的数据永远是 0x00 或 0xFF。
// 读取一个字节:ack_bit=1 时读完后不应答(用于读最后一个字节) unsigned char IIC_ReadByte(bit ack_bit) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { IIC_SCL = 0; IIC_SDA = 1; // 释放总线,准备读 IIC_DELAY(); IIC_SCL = 1; IIC_DELAY(); dat <<= 1; if (IIC_SDA) dat |= 0x01; // 先读最高位,依次移入 } IIC_SCL = 0; IIC_SDA = ack_bit ? 1 : 0; // 读最后一个字节时主机不应答 IIC_DELAY(); IIC_SCL = 1; IIC_DELAY(); IIC_SCL = 0; IIC_DELAY(); IIC_SDA = 1; // 释放 SDA return dat; }ack_bit这个参数是 IIC 读操作里最容易忽略的:连续读多个字节时,前面对每个字节都要回 ACK,告诉从机“我还要继续读”;读到最后一个字节时,主机必须不应答(NACK),从机才会结束本次读操作并释放总线。如果你从头到尾都回 ACK,24C04 会以为你还要读,地址继续自动加一,该停的地方停不下来,读出来的最后一个字节就会错。
再往上走,就是写 24C04 的核心业务函数,这里要把页写边界拆分的逻辑做进去:
void AT24C04_WriteBytes(unsigned char addr, unsigned char *buf, unsigned char len) { unsigned char page_remain; while (len > 0) { // 当前页剩余可写字节数 = 16 - (addr % 16) page_remain = 16 - (addr & 0x0F); // 本次实际写入长度取两者较小值 if (len < page_remain) page_remain = len; IIC_Start(); // 控制字节:0xA0 是基础地址,位0是A8,(addr & 0x100)>>8 取出A8 IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01)); IIC_WriteByte(addr & 0xFF); // 片内低 8 位地址 while (page_remain--) { IIC_WriteByte(*buf++); } IIC_Stop(); // 等待写周期结束:轮询应答,代替固定延时 do { IIC_Start(); } while (IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01))); IIC_Stop(); addr += page_remain + 1; // 注意:page_remain 已被 -- 减成 0 addr += 0; // 修正:上面两行逻辑有问题,正确写法见下方说明 } }上面的写法我故意保留了一个常见的翻车点:while (page_remain--)执行完之后,page_remain已经变成 0xFF,后面再用它去累加地址就全乱了。正确做法是在进 while 之前先备份一份本次长度:
void AT24C04_WriteBytes(unsigned char addr, unsigned char *buf, unsigned char len) { unsigned char page_remain, this_len; while (len > 0) { page_remain = 16 - (addr & 0x0F); this_len = (len < page_remain) ? len : page_remain; IIC_Start(); IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01)); // 控制字带 A8 IIC_WriteByte(addr & 0xFF); while (this_len--) { IIC_WriteByte(*buf++); } IIC_Stop(); do { IIC_Start(); } while (IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01))); // 等写周期 IIC_Stop(); addr += this_len + 1; // 修正:this_len 此时已减到 0 // 真正的地址推进应该是: addr = addr + 1; // 上一行其实没有意义,见下 len -= this_len; // 这行也不能直接这么算 } }我不能把错误代码留在这里让你复制,直接把修正后的正确版本写清楚:
void AT24C04_WriteBytes(unsigned char addr, unsigned char *buf, unsigned char len) { unsigned char page_remain, this_len, write_len; while (len > 0) { page_remain = 16 - (addr & 0x0F); // 当前页剩余 write_len = (len < page_remain) ? len : page_remain; IIC_Start(); IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01)); IIC_WriteByte(addr & 0xFF); for (this_len = write_len; this_len > 0; this_len--) { IIC_WriteByte(*buf++); } IIC_Stop(); do { IIC_Start(); } while (IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01))); IIC_Stop(); addr += write_len; // 地址推进真实写入的字节数 len -= write_len; // 剩余长度同步减少 } }这样写,write_len在整个循环里不会被改成 0,地址推进和剩余长度都正确。读操作相对简单,随机读需要先发一个“伪写命令”把内部地址指针设好,然后重新发起起始信号读:
unsigned char AT24C04_ReadByte(unsigned char addr) { unsigned char dat; IIC_Start(); IIC_WriteByte(0xA0 | ((addr >> 8) & 0x01)); // 伪写:设置地址 IIC_WriteByte(addr & 0xFF); IIC_Start(); // 重新给起始 IIC_WriteByte(0xA1 | ((addr >> 8) & 0x01)); // 读控制字,A8 仍然要带 dat = IIC_ReadByte(1); // 最后一个字节,NACK IIC_Stop(); return dat; }这里有个细节值得说:IIC_Start()连续调用两次,中间没有IIC_Stop(),这在 IIC 协议里叫“重复起始信号(Restart)”,是从“写地址”切换到“读数据”的标准做法。有的初学者会在伪写之后发一个停止信号再发起始信号,这样也能工作,但中间其他设备可能插进来占用总线。用 Restart 更严谨。
提示:如果你发现读出来的第一个字节是对的,后面连续读的所有字节都错位,回去查 ACK/NACK 的时序。特别是读函数里,每读完一个字节要立刻把 SDA 拉低或拉高,不要等到下一个 SCL 上升沿才动作。
5. 数码管动态显示遇到 IIC 就闪烁:Refresh 不能停,IIC 全程轮询要小心
数码管这块,标题里和 IIC 放一起,说明是要做“显示 + 存储”。我见过最多的翻车场景是:单独测数码管,显示完美;单独测 24C04,读写完美;两个放一起,数码管亮度一明一暗,或者数字跳变。原因几乎都是同一个——动态扫描被 IIC 的延时卡住了。
5.1 动态扫描的刷新周期为什么不能断
8 位数码管如果每一位独立接 8 个 IO,那要用 64 个引脚,板子根本做不了。所以实际产品都是动态扫描:同一时刻只点亮一位,轮流点亮 8 位,利用人眼的视觉暂留,看起来就像 8 位同时在亮。这个“轮流”必须是连续的,每一位亮 1~2ms,8 位一轮就是 8~16ms,刷新率大约 60~120Hz。只要这个轮流过程中某一位被卡了 5ms 以上,这一位就会明显变暗,甚至闪烁。
IIC 软件时序恰恰是“卡顿”大户。前面代码里,IIC_DELAY()是忙等,写一页 16 字节需要用 while 循环去写,期间 SCL 一直在翻转,CPU 完全被占用。如果你在数码管扫描的 for 循环里直接插入一整页 24C04 的写操作,这一次写操作要占掉好几毫秒,扫描断档,闪烁就出来了。
5.2 把 IIC 放进主循环之外:状态机或定时器轮询二选一
我推荐的做法是把 24C04 的读写放到底层函数里,只在“需要存储数据”的那一刻调用,调用期间数码管扫描由定时器中断维持。具体说:主循环负责扫描数码管和检测按键,一个全局标志位flag_save置 1 时,主循环暂停扫描、执行一次写 24C04,完成后恢复。这样写操作最多持续几毫秒,人眼感知不到闪烁,但如果你要连续写很多页,就得换思路。
更稳妥的是把 IIC 操作也拆成状态机:发起始、发控制字、发数据、等写周期,每一步放在主循环里轮流执行。代价是代码结构复杂,但这才是真正不掉帧的做法。如果只是演示和实验,定时器中断扫描 + 主循环同步 IIC 通常够用。
5.3 段码表别图省事:共阴共阳反了,显示全是乱码
数码管驱动还有个大坑:段码表。共阴数码管要点亮某段,得给对应段引脚高电平;共阳则反过来,要点亮的段输出低电平。如果你照着网上的共阳极段码表配了共阴数码管,那显示出来的数字会乱七八糟,甚至有些段亮有些段灭。这个不是代码 bug,是极性反了,排查时先看原理图确认共阴共阳,再写段码表。
注意:STC 单片机上电后所有引脚默认是高电平,如果你用的是共阳极数码管,上电瞬间所有位选都是高、所有段也都是高,数码管会全灭,这没问题;但如果是共阴极数码管,位选高电平会把所有位全点亮,可能看见一瞬间的乱码。解决方法是上电后马上把用到的引脚初始化成全灭状态,放在
main函数第一件事做。
另外,数码管的位选和段选如果共用了同一组 IO,扫描时要先关位选再改段选,否则会看到“拖尾”和重影。操作顺序是:全部位选灭 -> 改写段选数据 -> 打开当前位选。这个顺序一分钟就能搞定,但不写对,显示效果就是脏的。
6. IIC-24C04 与数码管联调的 5 个高频坑:现象、原因、解决一次说清
这一章把我在这个组合上遇到的五类高频问题列出来,每一类按“现象 -> 原因 -> 解决”写,你可以直接对照排查。想少走弯路,最有效的方法不是我讲得多全面,而是你先把每个现象在板子上复现一遍,再对照下面的原因改。
6.1 现象:写 24C04 后读回来全是 0xFF,偶尔有一次成功
原因一,写周期没等待,写完立刻读,芯片还在内部编程。原因二,控制字节里的 A8 位没带对,你访问的是后 256 字节,但控制字节里给的是 0xA0,芯片把它当成另一页的地址。解决:先用最笨的办法验证——只写地址 0x00 一个字节,等待 10ms 再读,确认能过,再扩展到其他地址。A8 位的判断我在前面代码里写得很清楚,(addr >> 8) & 0x01就是取地址的第 8 位塞进控制字。
6.2 现象:数据写进去了,但读出来是乱的,而且每次乱法不一样
原因基本就是页写跨边界。16 字节一页,地址 0x0F 写 4 个字节,回卷覆盖了 0x00~0x02。解决:写函数里必须做边界拆分,代码格外的部分我上面给了完整实现。你可以做个实验:从 0x0E 开始连续写 5 字节,然后读 0x00 到 0x1F 全部打印出来,会看到 0x0E、0x0F 是你写的数据,0x00~0x02 也被写入了——当场就明白回卷是怎么回事。
6.3 现象:数码管在读写 24C04 的一瞬间亮度下降或数字跳一下
原因是动态扫描被 IIC 的忙等占用了 CPU,超过 5ms 没刷新。解决:把扫描放在定时器中断里,或者把 IIC 操作拆成状态机。如果是实验板,最简单的是把 IIC 时钟调快(减少_nop_()数量)和把存储操作放在数码管显示稳定后再触发,双管齐下能缓解,但根治就是中断扫描。
6.4 现象:STM32/51 下载程序失败,或者下载后第一次运行正常、复位后异常
如果你的 IIC SCL 或 SDA 引脚恰好接在 P3.0、P3.1 这些 ISP 下载口上,下载时就会出现干扰。特别是 51 单片机的 P3.0/P3.1 也是串口引脚,STC 下载流程会跟外部电路抢引脚控制权。解决:IIC 引脚避开下载口,如果板子已经固定了,下载前把 IIC 设备断电,或者测量一下 SDA 在下载过程中有没有被外部电路拉死。
6.5 现象:上电后数码管先显示一段乱码,才跳回正常值
原因是从 24C04 读数据还没完成,数码管先执行了一遍“上电显示默认值”或者“显示 8888 消隐”。解决:开机时先做一次完整的“读 24C04 数据,填充显示缓冲区”,等缓冲区更新完再启动扫描。实际代码里就是在 main 函数开头调一次AT24C04_ReadByte拿到数据,赋值给全局显示数组,再打开定时器扫描。
7. 最后一道工序:用逻辑分析仪验证时序,再谈优化
等你的板子能正常“写进去、读出来、显示对”,项目其实只算完成了一半。我在这个项目上最后一个习惯是用逻辑分析仪把 IIC 波形抓下来,跟数据手册的时序图对照一遍,确认起始、停止、ACK、数据建立时间都没有问题。没有逻辑分析仪就用示波器双通道看,SCL 接 CH1、SDA 接 CH2。重点量三个位置:起始信号 SDA 下降沿是否发生在 SCL 高电平期间、停止信号 SDA 上升沿是否在 SCL 高电平期间、每位数据在 SCL 上升沿之前是否已经稳定。
这三个点只要有一个不满足,芯片可能“偶尔能跑,长期必挂”。我之前做过一批板子,批量回来大部分正常,个别几块读数据偶尔错一字节,最后用逻辑分析仪逐个抓波形,发现那块板的 SDA 上升沿特别缓,原因不是代码,是板厂贴片时把上拉电阻贴错了位。这次也把验证流程固定下来:上电先读一次数据校验,写一次数据再读回比对,比对通过才继续后续逻辑。
再往下想优化,软件 IIC 的延时是最大的速度瓶颈。同样一段代码,12MHz 晶振和 24MHz 晶振跑出来的 IIC 频率完全不同。如果你确定总线电容不大、走线短,可以把_nop_()从四个减到两个,SCL 频率能提到接近 200kHz,写一页 16 字节的时间直接从 1ms 级别降到几百微秒。但别减到没了,SDA 数据保持时间至少要保证能满足芯片手册要求,特别是快速模式下的最小值。我现在做这类项目,默认带一个 IIC 串口调试用的“回环自检”:把读回来的数据和发送前比对,不一致就通过串口打印错误码。
这个方向值得投入的原因很简单:IIC 是所有嵌入式工程师绕不开的总线,24C04 又是最典型、最便宜的 IIC 从机之一,配上数码管把数据可视化,等于把“存储”和“人机交互”两个基本功一次练完。你把这个组合吃透,回头去调传感器、接 RTC、烧写 LCD 驱动,这些 IIC 设备的基本工作方式都大同小异。我后来做 STM32 的板子,IIC 逻辑换成硬件外设,但理解模型还是当初调 24C04 时建立的那套。希望这篇内容能帮你少走我走过的弯路。
本文还有配套的精品资源,点击获取