ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

单片机C语言项目实战:IIC驱动24C04与数码管动态显示

单片机C语言项目实战:IIC驱动24C04与数码管动态显示

简介:这是一套面向单片机初学者的IIC-24C04与数码管C语言程序设计及仿真资源,以Keil C为开发平台,完整演示了IIC总线对EEPROM 24C04的读写操作,并将读取的数据通过数码管显示出来。压缩包共十五个文件,大小约五十三千字节,内容涵盖C源码、Keil工程配置、编译生成文件、Proteus仿真电路及备份文件等,从程序编写、编译链接到硬件仿真各环节均有对应,目录结构一目了然。已有三百二十一人学习下载,适合正在学习单片机IIC通信协议、外部存储器扩展或数码管驱动的读者。通过源码和仿真工程,可以直观掌握IIC总线的起始、停止、应答机制,熟悉24C04的页写与顺序读指令流程,同时参考数码管段码与位选控制方法,对完成课程设计或二次开发非常有帮助,无论是实验实训还是项目开发,都能获得完整的实践参考。

1. 单片机C语言程序设计 IIC-24C04与数码管:这套源码到底解决了什么

说到单片机C语言程序设计里最容易被劝退的组合,IIC-24C04与数码管绝对排得上号。IIC 协议看着就两条线,真调起来全是玄学;24C04 跟 24C02 长得一样,地址机制却暗藏区别;数码管能亮不难,难的是动态扫描不闪不拖影。这套源码把三段东西串成一条完整链路:模拟 IIC 驱动读写 24C04 EEPROM,再把读出来的数据送到数码管显示,数据掉电不丢。适合刚学完 51 基础、想拿一个完整外设项目练手的同学,也适合做小型存储方案时抄驱动。拿到手不是跑通就完事,关键是弄懂每一行时序背后的为什么。

2. IIC 协议与 24C04 读写:先啃透时序再写代码

2.1 为什么 IIC 必须开漏输出:推挽会翻车,上拉电阻取多大

网上经常有人问"IIC 为什么不能用推挽",这个问题问得很有水平。IIC 是线与结构,任何设备都能把 SDA 拉低,如果两边都用推挽输出,一个输出高一个输出低,直接形成短路,轻则通信乱码,重则烧引脚。所以 SDA 和 SCL 必须开漏,再靠外部上拉电阻把电平拉高。51 单片机的 P0 口本身是开漏,P1/P2/P3 内部有弱上拉,但驱动能力不够稳,实际调 IIC 我一般都会外接上拉电阻。

上拉电阻取值有一个经验区间,标准模式 100kHz 用 4.7kΩ,快速模式 400kHz 用 2.2kΩ 或 1kΩ,总线器件多、走线长时适当减小。

总线速率推荐上拉电阻适用场景
100kHz 标准模式4.7kΩ走线短、器件少,开发板常见
400kHz 快速模式2.2kΩ走线稍长、速率要求高
1kΩ总线电容大、多从机波形上升沿严重拖尾时

判断电阻是否合适,用示波器看 SDA 上升沿:如果波形像台阶一样缓慢爬升,说明电阻太大或总线电容偏大,优先把 4.7k 换成 2.2k。这一步在很多开发板上是被忽略的,因为 24C04 本身速率低,勉强能通,但一接第二个 IIC 从机就翻车。

2.2 起始、停止与应答位:时序图上三个最容易抄错的点

IIC 时序的核心就四个动作:起始、停止、发送字节、接收字节。写代码前先把时序图画在纸上,对照 24C04 数据手册看。

// IIC 引脚定义,STC89C52:P2.0 接 SDA,P2.1 接 SCL sbit IIC_SCL = P2^1; sbit IIC_SDA = P2^0; void IIC_Delay(void) { _nop_(); // 12MHz 晶振下约 1us,决定时钟频率 } void IIC_Start(void) // 起始条件:SCL 高电平期间 SDA 由高拉低 { IIC_SDA = 1; IIC_SCL = 1; IIC_Delay(); IIC_SDA = 0; IIC_Delay(); IIC_SCL = 0; // 拉低 SCL,准备传输数据 } void IIC_Stop(void) // 停止条件:SCL 高电平期间 SDA 由低拉高 { IIC_SDA = 0; IIC_SCL = 1; IIC_Delay(); IIC_SDA = 1; IIC_Delay(); }

起始和停止的条件必须严格遵守:SCL 高的时候 SDA 跳变才有效,SCL 低的时候 SDA 变化只是准备数据。新手最容易抄错的地方是顺序,比如起始条件写成先拉低 SDA 再拉高 SCL,从机根本识别不了。代码里 IIC_Delay 就是位周期基础,一个完整的位周期包含 SDA 建立、SCL 高电平、SCL 低电平三段,12MHz 的 51 单片机上大约 4~5us,折算下来约 200kHz,处于标准模式和快速模式之间,对 24C04 没压力。如果换 11.0592MHz 晶振,延时变化可忽略,但如果换到 STM32,必须重新评估这个延时。

应答位是第二个高频翻车点。从机接收到一个字节后,会把 SDA 拉低作为 ACK;主机读数据时,则要主动释放 SDA,让从机控制总线。所以发送字节后要等 ACK,接收字节前要释放 SDA。

unsigned char IIC_WaitAck(void) // 主机发送完一个字节后,等待从机应答 { unsigned char timeout = 255; IIC_SDA = 1; // 释放 SDA,从机才能拉低 IIC_SCL = 1; IIC_Delay(); while (IIC_SDA) // SDA 仍为高说明没有应答 { if (--timeout == 0) // 超时退出,防止死等 { IIC_SCL = 0; return 1; // 返回 1 表示无应答 } IIC_Delay(); } IIC_SCL = 0; IIC_Delay(); return 0; // 返回 0 表示应答正常 }

这段代码里我做了一个很多教程不会做的事:超时计数。学习板上的裸机 IIC 例程普遍不判超时,一旦从机异常拉死 SDA,程序就卡死在 while 循环里。加一个 255 次的超时判断,虽然简单,但能救回大量调试时间。注意 IIC_WaitAck 返回 1 表示无应答,调用时要主动检查返回值,不要默认每次都能等到 ACK。

2.3 24C04 的 512 字节地址:它与 24C02 的页选择差在哪

24C04 和 24C02 外观、引脚几乎一样,但地址机制完全不同。24C02 是 256 字节,字地址 8 位,器件地址 0xA0 | (A2<<2) | (A1<<1) | A0。24C04 是 512 字节,内部其实是两个 256 字节的块,字地址需要 9 位,最高位 A8 被挪进了器件地址的第 1 位(bit1),也就是 P0 位。

// 24C04 写一个字节 void AT24C04_WriteByte(unsigned char page, unsigned char addr, unsigned char dat) { IIC_Start(); // 起始条件 IIC_SendByte(0xA0 | (page << 1)); // 器件地址 + 页选择位 if (IIC_WaitAck()) return; // 无应答则退出 IIC_SendByte(addr); // 页内地址 0x00~0xFF if (IIC_WaitAck()) return; IIC_SendByte(dat); // 写入数据 if (IIC_WaitAck()) return; IIC_Stop(); // 停止条件 delay_ms(5); // 等待写周期完成 }

对照原理图逐行说明:0xA0 的低四位分别是 A2、A1、P0、R/W,当开发板上 A0~A2 全部接地时,基地址就是 0xA0。page 取 0 或 1,对应 24C04 的页选择位,它与内部字地址 A8 绑定,所以 page << 1 后要按位或到器件地址里。addr 是页内地址,范围 0~255。如果直接拿 24C02 的驱动来写 24C04,把 page 参数去掉、固定 0xA0,那你永远只能操作前 256 字节,后一半看着能读能写,实际全是镜像数据。

写周期延时是 24C04 最容易漏的坑。数据手册给的是 tWR 典型 5ms,意思是写入一个字节后,EEPROM 内部还要把数据固化进非易失存储,这段时间芯片不接受新操作。写完立刻读,大概率读到旧数据。所以我习惯在每次写完成后加一个 delay_ms(5),宁可多等也不能翻车。

读操作比写操作多一个重复起始条件。先写器件地址和字地址,把内部指针指到目标位置,然后再发一次起始条件和读方向的器件地址,从机才会把数据放到 SDA 上。

unsigned char AT24C04_ReadByte(unsigned char page, unsigned char addr) { unsigned char dat; IIC_Start(); IIC_SendByte(0xA0 | (page << 1)); // 写方向,先定位地址 if (IIC_WaitAck()) return 0xFF; IIC_SendByte(addr); if (IIC_WaitAck()) return 0xFF; IIC_Start(); // 重复起始条件 IIC_SendByte(0xA1 | (page << 1)); // 读方向 if (IIC_WaitAck()) return 0xFF; dat = IIC_ReceiveByte(); // 接收 8 位数据 IIC_Stop(); return dat; }

这里要提醒一个细节:按 IIC 规范,主机在读最后一个字节之前应该发送 NACK,表示"别再发了"。这个单字节读示例里直接 Stop 了,大多数情况下 24C04 能容忍,但如果你做的是连续读,最后一个字节前一定记得把 SDA 置 1,在第 9 个时钟不发应答,否则从机不知道你要结束。

3. 数码管显示:段码表、动态扫描与消影顺序

3.1 共阴共阳段码表:一个反了整屏乱码

数码管段码本质上就是一张 8 位"字体表",a~g 七个段加一个小数点 dp,对应字节的 bit0~bit7。共阳和共阴的段码完全相反,共阳极数码管公共端接高电平,某段接低电平才点亮;共阴极相反。很多开发板用共阳数码管,段码表长这样。

// 共阳极段码表:低电平点亮 code unsigned char seg_tab[] = { 0xC0, // 0 0xF9, // 1 0xA4, // 2 0xB0, // 3 0x99, // 4 0x92, // 5 0x82, // 6 0xF8, // 7 0x80, // 8 0x90, // 9 0x88, // A 0x83, // b 0xC6, // C 0xA1, // d 0x86, // E 0x8E // F };

这段表怎么验证对不对?把 0xC0 按二进制拆开:1100 0000,bit0~bit6 对应 a~g 段,bit6 是 1,说明 g 段灭,只有 a~f 亮,组成的字形正好是 0。如果用的是共阴极数码管,段码整体取反,0 就是 0x3F。判断自己板子是共阳还是共阴,最简单的办法是万用表二极管档测公共端和任一段脚,或者直接看原理图。

注意 51 单片机的 P0 口内部没有上拉,是开漏结构。如果 P0 接共阳数码管的段选,输出高电平那段全靠外部上拉排阻,开发板上通常已经集成,但自己画板一定要加 10k 排阻,否则数码管亮度会明显不均匀甚至缺笔画。

3.2 动态扫描:轮流亮背后的位选与延时考量

动态显示的原理是"轮流亮"。人眼有视觉暂留,只要刷新率够高,四个数码管轮流点亮看起来就是同时亮。相比静态显示,动态扫描省掉了 8 个锁存器,代价是程序要持续刷新,并且消影顺序必须正确。我见过太多人把消影放在送段码之后,结果前一位置数切换后留有残影。

unsigned char num_buf[4]; // 待显示数值,元素为 0~9 void display_scan(unsigned char *buf) { unsigned char i; for (i = 0; i < 4; i++) { P0 = 0xFF; // 先灭所有段,消影 P2 = (P2 & 0x0F) | (0x10 << i); // P2.4~P2.7 逐位选通 P0 = seg_tab[buf[i]]; // 再送段码 delay_short(1); // 保持约 1ms } }

这段程序里顺序是严格固定的:先 P0=0xFF 熄灭,再切位选,最后送段码。如果反着来,位选切到下一个数码管时,P0 还残留着上一个数字的段码,人眼看到的就是前一位的鬼影。位选线 P2.4~P2.7,0x10 << i 在 i=0 时选中第一个数码管,i=3 时选中第四个,理解这个位操作就可以改成任意引脚组合。

每位保持 1ms,四位数码管一轮扫描 4ms,刷新率 250Hz,完全不会闪烁。如果每位延时超过 5ms,刷新率掉到 50Hz 以下,肉眼会明显看到闪烁;延时太短则亮度不足,因为每个数码管只有 1/4 的时间在亮。亮度不均的常见原因就是四个位选延时不一致,程序里用循环就天然一致,手工写四条赋值语句反而容易出错。

3.3 读回来的数据怎么送到数码管:一次完整的数据流

把 24C04 和数码管串起来,逻辑其实很简单:先往 EEPROM 写几个数字,掉电后再读出来显示,证明数据没丢。数据流是 EEPROM → 字节变量 → 显示缓冲区 → 段码查表 → P0 口。

void main(void) { unsigned char i; // 第一次上电:写入需要保存的数据 AT24C04_WriteByte(0, 0, 1); // 第 0 页地址 0 存 1 AT24C04_WriteByte(0, 1, 6); // 第 0 页地址 1 存 6 AT24C04_WriteByte(0, 2, 8); // 第 0 页地址 2 存 8 AT24C04_WriteByte(0, 3, 4); // 第 0 页地址 3 存 4 delay_ms(5); // 写周期保护,两次写之间必须间隔 while (1) { // 每次上电读回 EEPROM 数据并显示 for (i = 0; i < 4; i++) { num_buf[i] = AT24C04_ReadByte(0, i); } display_scan(num_buf); } }

注意 num_buf 里存的是 0~9 的数值,display_scan 内部还要经过 seg_tab 查表转成段码。如果直接把读回来的字节当段码送数码管,数值 1 会显示成 0xF9 代表的字形,看起来就是乱的。这块稍微想一下就能理解:EEPROM 里存的是"数据",段码表做的是"数据到显示字形的映射",两者不要混。这套源码的典型应用就是记住用户的最终选择或计数器的掉电值,上电后自动恢复到断电前的状态。

4. 源码结构拆解:驱动分层与关键函数逐行说明

4.1 工程文件怎么组织:iic.c、at24c04.c、seg_display.c 各管什么

拿到源码先别急着烧录,看一遍文件结构再动手。规范的 51 工程会把驱动按外设拆分,这样换芯片或换板子时只需要改底层,不需要动业务逻辑。这套源码的驱动层一般拆成三个模块加主文件。

文件职责对外提供接口
iic.c / iic.h模拟 IIC 底层时序IIC_Start、IIC_Stop、IIC_SendByte、IIC_ReceiveByte、IIC_WaitAck
at24c04.c / at24c04.h24C04 读写封装AT24C04_WriteByte、AT24C04_ReadByte、AT24C04_PageWrite
seg_display.c / seg_display.h数码管扫描显示display_scan、seg_tab 段码表
main.c业务逻辑编排读写 EEPROM,刷新显示

这个分层的意义在于:IIC 时序层只关心波形,不关心操作的是 EEPROM 还是传感器;AT24C04 层只关心地址和数据,不关心信号怎么产生;显示层只关心段码和位选。你要移植到 STM32,只需要重写 iic.c 这个文件,上层函数一行不用改。

4.2 IIC_SendByte 与 IIC_ReceiveByte:逐位填充与超时判断

发送字节的代码看起来简单,但每一位的顺序都有讲究。SDA 的数据必须在 SCL 低电平时准备好,SCL 高电平时从机采样,所以标准流程是先放数据,再拉高 SCL,延时,再拉低 SCL。

void IIC_SendByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { IIC_SDA = (dat & 0x80) >> 7; // 先放最高位到 SDA IIC_SCL = 1; // SCL 拉高,从机采样 IIC_Delay(); IIC_SCL = 0; // SCL 拉低,准备下一位 IIC_Delay(); dat <<= 1; // 左移,次高位移到最高位 } }

最高位优先是 IIC 协议的规定,与串口的低位优先正好相反,写寄存器配置时尤其要小心。dat <<= 1 放在延时之后,是因为 SCL 已经拉低了,此时改变 SDA 不影响从机采样。整个循环执行完,SDA 处于最后一位的值,所以紧接着要调用 IIC_WaitAck 释放总线。

接收字节要处理的方向相反,从机在 SCL 高电平期间把数据放到 SDA,主机在高电平期间读取。主机必须在开始接收前主动把 SDA 置 1,因为 SDA 是开漏结构,主机不释放,从机拉不动线。

unsigned char IIC_ReceiveByte(void) { unsigned char i, dat = 0; IIC_SDA = 1; // 释放 SDA,交给从机控制 for (i = 0; i < 8; i++) { IIC_SCL = 1; // SCL 拉高,数据有效 IIC_Delay(); dat <<= 1; // 先左移,再判断当前位 if (IIC_SDA) dat |= 0x01; IIC_SCL = 0; IIC_Delay(); } return dat; }

注意 dat <<= 1 必须放在读 SDA 之前,这样第一次循环 dat 左移后仍是 0,读到最高位放进 bit0,第二次左移后最高位移到 bit1 位置,以此类推。如果顺序写反,收到的数会整体颠倒,这是很隐蔽的 bug。

4.3 从 51 移植到 STM32:这几行代码必须改

很多人把 51 上的模拟 IIC 直接搬到 STM32,发现通信怪异,原因是 GPIO 配置和延时系统完全变了。STM32 的 GPIO 要配成开漏输出模式,同时开启内部上拉或外接上拉电阻。千万别配成推挽输出,这就回到了 2.1 节说的线与他问题。

延时也要重新适配。51 的nop() 是 1 个机器周期,STM32 主频 72MHz 时一个空循环的时间完全不同。我一般会用 DWT 或 SysTick 做微秒级延时,把 IIC_Delay 替换成独立的 delay_us(5) 或 delay_us(2),按目标速率换算。位操作也要改,STM32 操作单个引脚建议用库函数或 BSRR 寄存器,像 51 那样直接 sbit 定义的方式不存在。

再就是上下拉策略。51 的 P0 口必须靠外部上拉,STM32 内部有可编程上拉电阻,偷懒时可以开启内部上拉,但内部上拉阻值在 30~50kΩ,适合 100kHz 标准模式,跑 400kHz 最好还是外部 2.2k 上拉。

5. 避坑手册:IIC+数码管联调的 5 个高频翻车点

5.1 通信异常:全是 0xFF、波形有台阶

现象:调用 AT24C04_ReadByte 永远返回 0xFF,或者第一次上电能写,第二次就失效。

原因:读回来全 0xFF,通常是器件地址错误或 SDA 根本没有波形。可以先量一下 IIC_Start 之后 SDA 是否被拉低,如果没有,说明引脚定义错了或者上拉电阻缺失。全是 0xFF 也有可能是应答等待失败后返回的默认值,我在 AT24C04_ReadByte 里失败就 return 0xFF,所以要区分是读到了真 0xFF 还是通信失败。

解决:用 GPIO 翻转的方式观察 IIC_Start/IIC_Stop 的时序,或者用逻辑分析仪抓 SDA/SCL 波形,确认地址字节是 0xA0 还是 0xA2。通信失败时优先查三处:引脚有没有接反、上拉电阻有没有虚焊、page 参数和器件地址的匹配关系。

现象:示波器量 SCL 和 SDA,上升沿跟缓坡一样,波形出现明显台阶。

原因:上拉电阻偏大,总线电容把上升沿拉缓了,尤其是在用杜邦线连接的时候,几十 pF 的寄生电容再乘上 4.7k 电阻,RC 时间常数就上去了。

解决:把上拉电阻从 4.7k 换成 2.2k,如果还不行就换 1k,同时尽量缩短杜邦线长度。另外检查示波器探头本身也有电容,用 10X 档位可以减少探头负载对波形的影响。

5.2 写入失败:地址页选错、写周期没等够

现象:24C04 页选择完全复制 24C02 的驱动,写 page=1 的数据,读出来的还是 page=0 的旧值。或者写进去当时能读出来,断电重启数据丢了。

原因:24C04 的页选择位在器件地址的第 1 位,不是字地址的最高位。如果按 24C02 的思路把地址写到 0x01FF,实际上字地址字节只取了低 8 位,高位的 1 被完全忽略,相当于还在第一页反复写。写周期问题则是 tWR 没等够,EEPROM 内部擦写还没完成。

解决:严格按 AT24C04_WriteByte 的签名传 page 和 addr 两个参数,器件地址用 0xA0 | (page << 1),字地址只传 0~255。写周期用延时 5ms 兜底,再进行写后读回验证。量产场景要更严谨,可以写完后发一个 ACK 轮询命令,直到器件回复 ACK 表示内部写完成,不过裸机例程里延时足够。

5.3 显示异常:拖影、乱码、亮度不均

现象:数码管切换数字时,能看到上一帧的残影,尤其在数字变化第一时间最明显。另一个常见现象是某一位明显偏暗。

原因:消影顺序不对。动态扫描时,位选切换的瞬间 P0 还保留着上一个位的段码,新的位一选通,立刻把上一帧的段码显示出来,持续几百微秒就形成鬼影。亮度不均则是位选延时不一致或者三极管驱动能力差异。

解决:严格按照「先消影 → 再位选 → 后段码」的顺序扫描。先 P0=0xFF 把所有段熄灭,再切换 P2 位选,最后才送当前位的段码。如果位选用的是 P2 直接驱动,检查每个 I/O 的灌电流能力,共阳数码管位选是低电平选通,驱动不足就把位选改成 74HC138 译码器加三极管扩流。每位保持时间用循环控制,保持恒定。

现象:数码管显示内容和 EEPROM 里存的数值对不上,比如存了 3 显示出来是 E。

原因:把读回来的数值直接当成段码送 P0 了。EEPROM 里存的是数值 0~9,而 P0 要的是段码 0xC0~0xF9,中间缺了查表这一步。

解决:加一层 seg_tab[buf[i]] 映射,确认 num_buf 存的是数值而不是段码。

现象:整片数码管闪烁,亮度一直跳。

原因:主循环里扫描间隔不均匀。如果在循环里插入了 IIC 读写操作,IIC 通信期间数码管停止刷新,每一帧的间隔被拉长且不稳定,人眼就感知到闪烁。

解决:IIC 一次性把 4 个字节读完后,再进入显示循环;或者用定时器中断做固定节拍的扫描,把显示刷新和 EEPROM 读写解耦。我一般会规定扫描周期 4ms,用定时器中断里置一个标志位,主循环只做数据读写,两件事互不干扰。

6. 验证三步走:抓波形、掉电测试、页写优化

6.1 逻辑分析仪抓 IIC 波形:怎么算合格

写完驱动别急着信任,把 SDA 和 SCL 接到逻辑分析仪上抓一次完整读写过程。采样率至少 1MHz,触发方式设为 SDA 下降沿。抓到的波形里应该能看到起始条件 SDA 先于 SCL 拉低、停止条件 SCL 先于 SDA 拉高,每个字节 8 个 SCL 脉冲,第 9 个脉冲高电平时 SDA 被从机拉低,这是 ACK。如果 SCL 频率明显高于 400kHz,说明延时太短,24C04 可能不稳定,把 IIC_Delay 加大再试。

6.2 掉电保持测试:验证数据真的写进去了

代码烧进板子后做这样一轮测试:先把一组测试数比如 1、6、8、4 写入 24C04 的 page 0,确认数码管显示 1684;然后直接断电,等几秒再上电,程序从 EEPROM 读回数据,如果数码管仍然显示 1684,说明写入和读回链路都正常。这个测试要特别注意别在写入过程中断电,写满 5ms 前断电很可能留下脏数据。如果想测试写入的健壮性,可以用示波器监控电源,在延时 5ms 的不同时刻断电,多次上电看数据是否稳定。

6.3 进阶:页写优化与多机通信

如果数据量超过一个字节,逐字节写入效率太低,24C04 支持页写,单次最多连续写 16 字节。实现页写时最关键的约束是跨页保护:写地址加上长度不能超过页边界,否则数据会回卷到本页开头,覆盖前面的内容。实际项目里常见做法是把大块数据按 16 字节切分,从页边界对齐开始,分成多次页写完成。

void AT24C04_PageWrite(unsigned char page, unsigned char addr, unsigned char *p, unsigned char len) { unsigned char i; if (addr + len > 16) // 超过页边界则截断 len = 16 - addr; IIC_Start(); IIC_SendByte(0xA0 | (page << 1)); IIC_WaitAck(); IIC_SendByte(addr); IIC_WaitAck(); for (i = 0; i < len; i++) { IIC_SendByte(p[i]); IIC_WaitAck(); } IIC_Stop(); delay_ms(5); }

页写还有一个隐藏收益:写周期从每字节 5ms 变成每页 5ms,写 16 字节的时间从 80ms 降到 5ms,这对做产品固件升级、参数存储这类场景非常重要。但页写的 ACK 时机和字节写不同,页写过程中第 16 个字节后器件进入内部写周期,此时不再响应 ACK,后续没有等待 ACK 的步骤,直接 Stop 即可。从那以后我每次写完 IIC 驱动,都强制走一遍抓波形加掉电测试的流程,宁可多看几眼波形也不愿意到现场才发现数据丢。这套源码把 IIC、EEPROM、数码管三条线一次打通,希望帮到你。

本文还有配套的精品资源,点击获取

返回列表