ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

51单片机超声波测距:数码管显示与蜂鸣器报警系统及Proteus仿真

51单片机超声波测距:数码管显示与蜂鸣器报警系统及Proteus仿真 做51单片机课设或者自己入门嵌入式很多人绕不开这个经典组合超声波测距把距离算出来数码管把数字显示出来距离太近再让蜂鸣器报警。听起来好像不复杂可真要一次性调通涉及定时器测脉宽、数码管动态扫描、Echo上升沿判断还有Proteus仿真环境里各种跟实物有差异的细节任何一个地方没对上结果就是显示乱跳、数值不准或者干脆没反应。这个项目我在实际里做过不止一次也反复在Proteus里搭过完整仿真。下面把从硬件选型、接线、代码到调试的全过程整理出来给准备做课设、准备比赛或者纯粹想搞懂超声波测距原理的朋友做个参考。文中的接线和代码都按实际仿真环境验证过照做能跑通理解之后也很容易改成自己需要的功能。1. 项目整体设计与方案取舍1.1 系统功能拆分先把这个项目拆开看其实就三个功能模块。第一是测距用超声波模块完成。51单片机给HC-SR04的Trig引脚一个至少10微秒的高电平脉冲HC-SR04内部会自动发射一串超声波同时把Echo引脚拉高。超声波遇到障碍物反射回来模块收到回波后把Echo引脚拉低Echo高电平的持续时间就是超声波从发射到接收的往返时间。单片机要做的就是精确测出这段高电平时间再按声速换算成距离。第二是显示用数码管把距离值展示出来。三位足够不带小数也能显示0到999厘米带一位小数也能显示0到99.9厘米看你需求。数码管显示有静态和动态两种方式51单片机IO口不算富余所以绝大多数人都会选择动态扫描用段选和位选两套控制线分时点亮各个数码管。第三是报警用一个蜂鸣器完成。程序里设一个阈值比如距离小于30厘米就响大于30厘米就停。报警这个功能看起来很基础但实际做的时候有一个问题蜂鸣器不能长时间阻塞在主循环里否则数码管刷新会卡顿。后面代码部分会细说怎么处理。这三个功能串起来就是一个完整的单片机应用系统传感器采集、数据处理、输出控制全都有了非常适合作为课程设计或者竞赛入门项目。1.2 为什么选这套方案芯片选择上51单片机确实老但它的优势是资料多、上手快、Proteus模型稳定。我一般用AT89C52或者STC89C52都是标准51内核2个定时器够用。如果选STM32去测超声波代码会更复杂对刚接触单片机的人并不友好。51定时器工作在1微秒计一次数的模式量出来的时间精度完全足够覆盖HC-SR04的4米量程。超声波模块选HC-SR04一是因为Proteus自带模型二是模块的实际价格也便宜课设转实物很方便。数码管选常见的4位共阴数码管在Proteus里搜7SEG-MPX4-CC就能找到。蜂鸣器选最简单的有源蜂鸣器模型给个高电平或者低电平就能响。Proteus仿真在这套方案里的价值被很多人低估了。它不只是用来画个电路图交作业更重要的是能帮你把程序逻辑先调通。比如Echo引脚到底有没有波形、Trig脉冲宽度够不够、数码管哪一位没亮这些在仿真里用逻辑笔和虚拟示波器一下就能看出来。真拿到实物板子上反而没有这么多调试手段。1.3 整机工作流程程序的主循环其实很简单按顺序跑这几步就行。先给Trig发一次触发脉冲然后等待Echo变高。Echo变高的瞬间启动定时器0开始计数Echo变低的时候停止定时器并读取计数值。计数值乘以一个系数就得到距离。接下来把距离拆成百位、十位、个位送去数码管显示。最后判断距离是否小于阈值决定蜂鸣器控制引脚的电平。整个流程跑完回到第一步继续下一次测量。这里有个细节必须注意超声波从发射到回波回来需要一段时间。如果你把测量、显示、报警全放在一个while循环里顺序执行整个循环周期可能有好几十毫秒。这没问题因为超声波测距本身不适合做高频刷新一般每秒测几次到十几次就够了。真正要注意的是等待Echo高电平的这个while循环一定要加超时保护否则一旦模块没收到回波程序会卡死在这里。代码部分会给出具体写法。2. 硬件电路设计与Proteus仿真搭建2.1 元件清单与放置方法在Proteus 8 Professional里新建工程后直接在元件模式点P搜索下面这几个关键词就能找到对应的仿真模型。元件Proteus搜索关键词数量作用主控芯片AT89C521单片机主控超声波模块HC-SR04 或 ULTRASONIC1测距传感器数码管7SEG-MPX4-CC1距离显示蜂鸣器BUZZER1报警输出NPN三极管2N3904 或 BC5471驱动蜂鸣器电阻RES5-6个上拉、限流、基极晶振CRYSTAL112MHz电容CAP2个起振电路电解电容CAP-ELEC1复位电路有一个容易踩坑的地方不同Proteus版本里超声波模块的模型名称不一样。我用的Proteus 8 Professional版本直接搜HC-SR04能出来有的版本搜不到时需要搜ULTRASONIC两者都是同一个原理Trig触发、Echo返回脉冲宽度。元件的具体放置顺序可以参考我的习惯先把单片机放中间左边放晶振和复位电路右边放超声波模块下面放数码管蜂鸣器放最边上。别小看布局布局清楚后面连线才不容易乱也不容易漏连。2.2 核心接线图与引脚分配我的引脚分配方案如下你完全可以照抄P1.0接超声波模块的TrigP1.1接超声波模块的EchoP1.2经一个10k电阻接NPN三极管基极三极管集电极接蜂鸣器负极蜂鸣器正极接5V电源发射极接地P0接数码管的a到g以及dp段选P2.0到P2.3接数码管的4个位选公共端这里要解释几个关键选择。P0口接段选很多人会直接用但必须注意51单片机的P0端口内部是漏极开路的没有上拉电阻直接驱动数码管会非常暗甚至会显示不完整。Proteus仿真里有时候不加上拉也能亮但那是仿真模型理想化导致的。正确做法是在P0口统一接一个排阻上拉一般用10k的排阻就行这样无论仿真还是转实物显示效果都稳定。位选用P2口的低四位接数码管的公共端。共阴数码管的公共端需要低电平选中也就是位选信号为低电平时那一位数码管才导通点亮。如果你用的是共阳数码管位选逻辑正好反过来程序里也要对应改成高电平选中。蜂鸣器驱动电路是很多人忽略的点。51单片机引脚输出能力有限高电平时最多提供几百微安的电流直接驱动蜂鸣器根本带不动。用NPN三极管做开关是最稳妥的方案基极串一个10k电阻接到P1.2集电极接蜂鸣器负极发射极接地。当P1.2输出高电平三极管导通蜂鸣器接通5V电源发出声音P1.2输出低电平三极管截止蜂鸣器不响。这个电路在Proteus里和实物上都一样属于标配。2.3 复位电路与晶振Proteus仿真里很多人不画复位电路和晶振也能跑因为仿真模型默认就有时钟和复位时序。但为了严谨也为了后面能顺利转实物我建议还是把这两部分画上去。晶振电路用12MHz晶振两端各接一个20到30pF的瓷片电容到地。12MHz这个频率是故意的因为51单片机的机器周期是晶振的12分频12MHz除以12正好是1MHz也就是说定时器每计数一次正好是1微秒。后面计算距离的时候可以直接把定时器计数值当成微秒数来用不需要额外换算非常省事。复位电路用经典的10uF电解电容加10k电阻接到RST引脚。上电瞬间电容充电RST引脚保持一段时间高电平完成单片机复位。这个电路是51系列单片机的标准接法照着画就行。2.4 Proteus仿真中的几个明显坑仿真和实物最大的不同就是仿真环境干净、没有干扰但这也导致很多问题在仿真里不容易提前暴露。反过来有些仿真里出现的问题实物反而不一定出现。第一个坑是仿真速度。Proteus默认的仿真速度可能跟你的主频设置不一致导致超声波模块的Echo宽度跟实际不符。这时候可以查看仿真速度设置把仿真速度调整为实时或者降低主频到合适值。12MHz在Proteus里跑实时仿真一般没问题但如果电脑CPU性能一般建议关掉动画刷新只保留必要波形观察。第二个坑是数码管的限流电阻。实物里的数码管段选引脚通常要串220到470欧姆的限流电阻防止电流过大烧坏数码管。但在Proteus里加上限流电阻后很多时候反而显示变暗这是因为仿真模型的导通阻抗和压降跟实物不一样。我的做法是仿真阶段段选不串电阻直接接P0加上拉先把逻辑调通转实物的时候再串电阻根据实际亮度调整阻值。第三个坑是超声波模型对Trig脉宽并不敏感。有的Proteus版本里超声波模块只要检测到Trig有高电平变化就会返回回波哪怕你只拉高几个微秒。但真到了实物上HC-SR04要求触发脉宽至少10微秒代码里还是按照规格书的10到20微秒来写别偷懒。3. 软件编程与核心代码实现3.1 Keil工程配置软件部分用Keil C51新建工程后选择Atmel AT89C52。如果你用的是STC89C52在Keil里没有对应的芯片选项选AT89C52即可内核完全相同寄存器地址也完全一样。编译选项这里没什么特别需要改的默认设置就行。需要注意频率设置如果你用了代码里的延时函数并且这些延时的准确性很重要就要保证单片机实际运行的晶振频率是12MHz。Keil里可以通过一个叫作LST文件或者目标选项里的Xtalsettings去设置晶振频率不同版本位置不太一样选中目标设备在Options for Target里的Target标签页设置Xtal(MHz)为12即可。这样调试时看仿真时间才是准的。3.2 超声波测距的定时器策略HC-SR04测距的本质是测量高电平脉冲宽度所以定时器必须选模式1也就是16位定时模式。TMOD寄存器设置为0x01只启动定时器0不启动定时器1。初始化代码非常简单void Timer0_Init(void) { TMOD 0x01; // 定时器0模式116位不自动重装 TH0 0; TL0 0; TR0 0; // 先不启动等Echo上升沿再启动 }很多人会把TR0一开始就置1然后不断查询溢出标志。这样不是不行但逻辑绕还容易因为处理不及时丢数据。更清晰的做法是等到Echo变高的瞬间启动定时器Echo变低的瞬间停止然后读取TH0和TL0里的值。这里有一个重要判断16位定时器最大能计65535微秒在距离上对应的最大量程大概是65535乘以0.017毫米也就是约1114厘米远远超过HC-SR04实际4米左右的量程。所以在正常使用范围内不用担心定时器溢出问题。但为了程序健壮我还是建议做一次溢出保护否则遇到障碍物很远或者没有回波的情况定时器溢出后会从0重新计数计算出来的距离就会变成一个毫无意义的随机值。溢出的一个简单处理办法是增加一个溢出标志主循环判断Echo时间过长就直接显示一个代表超量程的数值比如999而不是继续显示错误距离。3.3 超声波触发与接收代码下面是测距模块的核心驱动代码我加了一行一行的注释。sbit Trig P1^0; sbit Echo P1^1; void Delay10us(void) { unsigned char i; i 20; while (--i); } void Ultrasonic_Trigger(void) { Trig 1; Delay10us(); Trig 0; } unsigned int Ultrasonic_GetDistance(void) { unsigned int count 0; unsigned long distance 0; TH0 0; TL0 0; TR0 0; Ultrasonic_Trigger(); while (Echo 0); // 等待Echo变高高电平到来 TR0 1; // 开始计时 while (Echo 1); // 等待Echo变低回波结束 TR0 0; // 停止计时 count (unsigned int)TH0 8; count count | TL0; // 距离 时间(us) * 声速(cm/us) / 2 // 声速约340m/s换算成cm/us是0.034cm/us // 往返所以要除以2所以系数是0.017 distance (unsigned long)count * 17 / 100; if (distance 400) return 999; return (unsigned int)distance; }代码里最后这段换算很关键。很多教程直接写乘以0.017这个写法的坑是浮点数在51里计算慢而且容易出精度问题。所以我写成乘以17再除以100全部用整数运算结果是一样的但程序跑得快很多。还有一个细节count是一个unsigned int最大到65535distance我用了unsigned long因为count乘以17之后可能超过65535的表示范围必须先转成long再乘不然乘法的中间结果会溢出算出来的距离就是错的。3.4 数码管动态扫描实现四位一体的共阴数码管有7个段选引脚加1个小数点引脚还有4个位选公共端。段选控制显示的数字位选控制哪一位亮。段码表需要按照你接线的顺序来定。如果P0.0接a段P0.1接b段以此类推那么共阴数码管显示0到9的段码如下unsigned char code seg_code[] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F };这个表怎么来的以数字0为例共阴数码管中a、b、c、d、e、f点亮g和dp熄灭对应的二进制就是01011111注意这个二进制是从dp到a排列的。换算成十六进制就是0x3F。手工推可以但更高效的办法是画一张表对着编码或者直接用黑色纸打印出来一个个试。我在初学阶段吃过不少亏后来干脆把0到9的段码记熟再遇到类似项目就直接套用。动态扫描的显示函数我建议把位选也写在同一个函数里一次刷新完4位数避免主程序里到处写显示逻辑导致代码混乱。sbit P2_0 P2^0; sbit P2_1 P2^1; sbit P2_2 P2^2; sbit P2_3 P2^3; void Display_Process(unsigned int distance) { unsigned char bai 0, shi 0, ge 0; if (distance 100) { bai distance / 100; shi distance % 100 / 10; ge distance % 10; } else if (distance 10) { bai 10; // 熄灭百位 shi distance / 10; ge distance % 10; } else { bai 10; shi 10; // 熄灭十位 ge distance; } P2_0 0; P2_1 1; P2_2 1; P2_3 1; P0 seg_code[bai]; Delay1ms(); P0 0x00; // 消影 P2_0 1; P2_1 0; P2_2 1; P2_3 1; P0 seg_code[shi]; Delay1ms(); P0 0x00; P2_0 1; P2_1 1; P2_2 0; P2_3 1; P0 seg_code[ge]; Delay1ms(); P0 0x00; }这里有两个动作缺一不可。第一个是换位选前必须先把P0清零这个动作叫消影。如果不消影上一位的段码会在下一位选通的瞬间残留一下造成拖影或者亮度不均。第二个是位选切换时要保证只有一个公共端有效其他三个必须断开。段码表里我用了索引10来表示熄灭也就是先执行P0 seg_code[10]seg_code数组需要多定义一项0x00作为熄灭码。这样让不用的位完全熄灭显示效果干净很多。3.5 报警控制逻辑报警逻辑本身不难难点在于怎么跟主循环配合好。如果直接这样写while (1) { distance Ultrasonic_GetDistance(); if (distance 30) { Buzzer 1; } else { Buzzer 0; } Display_Process(distance); }表面看没问题但如果超声波测量过程卡住或者蜂鸣器一直响数码管刷新还是会受到影响。真实项目里我一般会在测得距离后立刻刷新一次数码管然后再判断报警让显示更流畅。报警我倾向于做成边沿触发判断而不是电平判断。就是只在距离从安全变成危险的那一刹那让蜂鸣器响一小段时间或者做成周期性间歇蜂鸣这样人听起来更明显也不会刺耳。最简单稳定的报警代码是这样void Alarm_Control(unsigned int distance) { if (distance 30) { if (Alarm_Flag 0) { Alarm_Flag 1; Alarm_Cnt 0; } } else { Alarm_Flag 0; Buzzer 0; } if (Alarm_Flag) { Alarm_Cnt; if (Alarm_Cnt 20) // 响一段时间 { Buzzer 1; } else if (Alarm_Cnt 40) // 停一段时间 { Buzzer 0; } else { Alarm_Cnt 0; } } }这个写法的好处是报警时蜂鸣器间歇工作持续循环既不刺耳也避免了蜂鸣器长时间高电平发热的问题。Alarm_Cnt放在主循环里累加每次循环加1具体时间取决于主循环周期这个精度在报警场景里完全够用。3.6 完整参考代码把上面的功能组装起来一个能直接在Proteus里跑通的完整程序如下。这段代码我在仿真里测试过放在这里给不想自己拼的人直接用但建议还是每一行都读懂改起来才方便。#include reg52.h sbit Trig P1^0; sbit Echo P1^1; sbit Buzzer P1^2; unsigned char code seg_code[] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x00 }; void Delay1ms(void) { unsigned char i, j; i 2; j 239; do { while (--j); } while (--i); } void Delay10us(void) { unsigned char i 20; while (--i); } void Timer0_Init(void) { TMOD 0x01; TH0 0; TL0 0; TR0 0; } void Ultrasonic_Trigger(void) { Trig 1; Delay10us(); Trig 0; } unsigned int Ultrasonic_GetDistance(void) { unsigned int count; unsigned long distance; TH0 0; TL0 0; TR0 0; Ultrasonic_Trigger(); while (Echo 0); TR0 1; while (Echo 1); TR0 0; count ((unsigned int)TH0 8) | TL0; distance (unsigned long)count * 17 / 100; if (distance 400) return 999; return (unsigned int)distance; } void Display_Process(unsigned int distance) { unsigned char bai 10, shi 10, ge 10; if (distance 100) { bai distance / 100; shi distance % 100 / 10; ge distance % 10; } else if (distance 10) { bai 10; shi distance / 10; ge distance % 10; } else { shi 10; ge distance; } P2 0xFE; P0 seg_code[bai]; Delay1ms(); P0 0x00; P2 0xFD; P0 seg_code[shi]; Delay1ms(); P0 0x00; P2 0xFB; P0 seg_code[ge]; Delay1ms(); P0 0x00; } void Alarm_Control(unsigned int distance) { static unsigned char flag 0; static unsigned int cnt 0; if (distance 30) { flag 1; } else { flag 0; Buzzer 0; } if (flag) { cnt; if (cnt 20) { Buzzer 1; } else if (cnt 40) { Buzzer 0; } else { cnt 0; } } } void main(void) { unsigned int distance; Timer0_Init(); while (1) { distance Ultrasonic_GetDistance(); Display_Process(distance); Alarm_Control(distance); } }特别注意P2 0xFE这行它同时设置了4个位选引脚0xFE对应二进制11111110也就是说只有P2.0为0其他三位为1选中第一位数码管。后面0xFD对应11111101选中第二位以此类推。这样做比单独操作sbit更简洁。4. 仿真调试、问题排查与结果校准4.1 仿真环境里的调试方法Proteus里最有用的调试工具是虚拟示波器和逻辑探针。把逻辑探针放在Echo引脚上运行仿真后再用手移动障碍物你会看到Echo引脚的高电平宽度明显变化。这个波形是判断测量逻辑是否正确的第一手证据。如果发现Echo完全没有波形先检查Trig引脚上是否有触发脉冲。用示波器放到Trig引脚上正常应该能看到周期性的高电平窄脉冲。如果没有脉冲说明主程序卡在某个while循环里最可能的就是Echo等待超时或者程序错误地卡在数码管刷新里。数码管显示方面我建议在仿真时分步测试。先只显示一个固定数字比如123确认段码和位选方向对不对。确认固定数字没问题后再接上超声波的测距结果。这样能避免把硬件接线错误和程序逻辑错误混在一起排查。4.2 高频问题诊断速查表我整理了大家在仿真和实物调试中问得最多的几个问题直接按现象查就行。现象可能原因解决方法数码管全亮或者乱码位选切换逻辑反了共阴共阳搞混确认数码管模型是CC还是CA调整位选电平数码管显示有拖影没有消影位选切换前把P0清零距离数值乱跳Echo引脚有毛刺测量时间被干扰增加软件滤波连续测三次取中值距离始终是999超声波模块没有收到回波或者超出量程用示波器观察Echo波形检查Trig脉冲宽度蜂鸣器不响三极管接反或者蜂鸣器极性错误检查蜂鸣器正极是否接5V集电极是否正确接地蜂鸣器一直响报警阈值设置太高或者标志位没有复位打印距离值确认阈值和实际距离的关系单片机程序不运行没有加复位电路或晶振配置异常在Proteus里加上复位电路设置晶振频率为12MHz编译报错找不到reg52.hKeil没有安装C51器件包检查Keil安装选项安装C51的Device Database第一条特别常见。很多人在Proteus里明明用的是共阴数码管但程序按共阳的逻辑写结果是所有数字都反着亮。我的建议是拿到元件后先双击确认关键词是CC还是CA然后从简单显示开始测试不要一次性写完再调试。4.3 测量误差与校准技巧超声波测距的精度受声速影响很大。我在代码里用的340米每秒是常温附近的经验值但实际上声速随温度变化公式大约是每秒331.4加0.6倍的摄氏温度。在20摄氏度环境下实际声速大概343米每秒跟340有差不多1%的误差。仿真环境里用340或343都行反正效果很接近但转实物后如果要求高精度最好把温度值也做成可调参数。实物中的误差比仿真大得多主要来源有三个。第一个是超声波的发射角HC-SR04发射头有大概15度的波束角如果障碍物表面不平整最先返回的未必是正对着模块的那条路径。第二个是Echo引脚的电平翻转时间模块内部比较器有响应延迟这个延迟通常在微秒级对应距离误差也就是零点几毫米影响不大。第三个是单片机的指令响应导致测脉宽本身有1到2微秒的误差对应距离误差不到0.5毫米在课设精度要求下可以忽略。如果发现数值整体偏大或者偏小最简单的校准方式是在程序末尾加一个修正量。比如模块静止放在100厘米处实测显示102厘米那就把最终结果减去2厘米。这种线性偏移校准在固定环境里非常管用。但我建议只在实物调试时做仿真环境本身没有外部干扰加了反而让人看不懂真实测量结果。注意仿真里手动移动Proteus里障碍物的移动方式是用鼠标直接拖动超声波的障碍物元件或者用一个变量控制距离。如果你的模型是通过元件属性里修改距离参数来改变回波宽度那测试时最好使用不同距离值做多组对比而不是只测一个点。5. 项目扩展与我的实战心得5.1 功能还能往哪些方向扩这个项目真正做完以后框架其实就变成了一个标准的传感器加显示加执行系统。往任何方向扩展都不需要重构代码。扩展最简单的是把数码管换成LCD1602液晶屏程序里增加一个液晶驱动文件显示内容从单纯数字变成带单位、带阈值的完整信息。课题评定的时候一个液晶屏加按键的效果比四个数码管加蜂鸣器会显得完整很多。第二个方向是增加按键设定阈值。把阈值从固定的30改成存储变量用两个独立按键加减长按可以快速调整。这个功能用51的外部中断或者查询方式都能实现关键是做好按键消抖和连按判断。第三个方向是用串口把距离数据发到上位机。STC89C52本身集成串口加一个MAX232或者直接用USB转TTL模块就能实时把距离上报到电脑上。串口打印对调试也有帮助很多时候你只看数码管根本判断不了测量逻辑到底哪里出问题但串口上一行行数据、时间、距离一目了然。更高阶一点的玩法是用定时器捕获功能或者外部中断来测量Echo高电平宽度。51单片机的外部中断只有沿触发引脚上有电平变化时才能触发配合定时器读取当前计数值可以减少主循环查询带来的延迟误差。不过对课设来说查询方式已经足够这部分内容适合想参加电赛的朋友深入研究。5.2 我对这套方案的一点经验总结这个项目我做过完整实物也在Proteus里反复仿真过最大的体会是先模块化验证再整体集成能省掉至少一半的调试时间。我见过太多同学把全部代码写完然后现象不对就开始怀疑是不是单片机坏了是不是传感器坏了结果查了一晚上发现只是段码表写错了一位。正确做法是先单独测试数码管显示把数码管跑通并固定显示一个数字。再单独测试超声波模块用逻辑笔确认Trig脉冲和Echo回波都正常。最后把两者合到一起这时如果还是出错问题范围就非常小要么是距离变量类型不对要么是动态扫描时间太长影响到测量循环。这套排查思路比对着代码一行行看要高效得多。另外一件重要的事测距等待Echo的while循环一定要做超时保护。我在实物调试时遇到过超声波模块正前方障碍物太近回波信号和发射信号混在一起模块收不到回波Echo一直是0程序就卡死在while里数码管也跟着卡住。如果加了超时判断超时后直接跳过本次测量返回一个无效值系统就能继续运行。最后一个经验是别怕用仿真里的工具。刚上手Proteus的人往往只会点运行和暂停最多用一下虚拟终端。但示波器、逻辑探针、电压表这些工具才是仿真环境比真实硬件更好用的地方。遇到波形不对直接搬出逻辑探针看引脚基本一眼就能定位问题。如果你做完这个超声波项目能把Proteus的调试工具用熟练后面再做DS18B20温度采集、DS1302时钟、红外遥控解码这类项目都会变得顺畅很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进