基于Arduino与HC-SR04的超声波测距系统:TFT显示与声光报警实现
1. 项目概述如果你手头正好有一块Arduino Mega2560、一个HC-SR04超声波传感器和一块2.8寸的TFT屏幕却不知道如何把它们组合成一个能直观显示距离的实用系统那么这篇文章就是为你准备的。我最近刚完成了一个类似的项目它不仅能实时测量距离还能根据距离的远近在屏幕上动态显示“安全”、“注意”、“危险”三种状态并用RGB LED和蜂鸣器进行声光报警。整个过程就像搭积木一样把硬件连起来再把代码烧录进去一个功能完整的距离监测仪就诞生了。无论是想为你的机器人增加避障眼睛还是想做一个简单的安防距离报警器这个项目都是一个绝佳的起点。它不涉及复杂的电路理论核心在于理解各个模块如何通过Arduino“对话”以及如何用代码指挥它们协同工作。接下来我会带你从零开始一步步复现这个系统并分享我在搭建和调试过程中踩过的坑和总结的技巧。2. 系统核心设计与硬件选型解析2.1 整体系统架构与工作流程这个项目的核心逻辑非常清晰是一个典型的“感知-处理-显示”嵌入式系统闭环。超声波传感器HC-SR04负责“感知”它持续向外界发射超声波脉冲并接收回波。Arduino Mega2560作为“大脑”负责“处理”它控制传感器的发射时序计算声波往返时间并根据公式换算出实际距离。最后2.8寸TFT屏幕、RGB LED和蜂鸣器共同承担“显示”与“交互”的任务将冰冷的数字转化为直观的图形、颜色和声音。具体的工作流程是这样的Arduino先给HC-SR04的Trig引脚一个短暂的高电平脉冲至少10微秒触发其发射一组40kHz的超声波。紧接着Arduino监听Echo引脚一旦检测到高电平即收到回波就开始计时直到高电平结束。这个高电平的持续时间就是超声波从发射到返回所经历的时间。我们知道声音在空气中的速度约为340米/秒受温湿度影响但常温下常取343米/秒或简化计算值那么距离就等于时间 × 声速 / 2。除以2是因为这个时间是往返时间。计算出距离后程序会将其与预设的阈值进行比较。在我的代码里我设定了三个区间大于等于75厘米为“安全区”25厘米到75厘米之间为“注意区”小于25厘米则为“危险区”。系统会根据所在区间在TFT屏幕上显示对应的文字和动态图形比如“安全”时显示绿色对勾和射线“危险”时显示红色感叹号和闪烁的警示标志同时控制RGB LED发出相应颜色的光绿、黄、红并在危险区触发蜂鸣器鸣叫。提示为什么选择25厘米和75厘米作为阈值这主要基于HC-SR04的有效测距范围2cm-400cm和常见应用场景。25厘米以内通常意味着物体已非常接近需要立即预警25-75厘米是缓冲区域提示注意75厘米以外相对安全。你可以根据你的具体应用比如小车避障、停车辅助轻松调整这些阈值。2.2 关键硬件组件选型与考量选择这些硬件并非偶然每一件都有其背后的考量。1. 主控板Arduino Mega2560为什么是Mega2560而不是更常见的Uno主要原因在于引脚资源和内存。这个项目需要驱动TFT屏幕至少占用6个以上的数字IO用于SPI或并行通信、超声波传感器2个IO、RGB LED3个PWM引脚和蜂鸣器1个IO。Uno的14个数字IO和6个PWM引脚可能会捉襟见肘尤其是当TFT屏幕使用并行接口时。Mega2560拥有54个数字IO和15个PWM引脚为后续扩展比如增加更多传感器或按钮留足了空间。此外其更大的Flash256KB和SRAM8KB内存也能更好地支撑TFT图形库的运行避免内存溢出导致的程序崩溃。2. 显示模块2.8英寸 TFT LCD (HX8347驱动芯片)选择这款屏幕是因为它在价格、尺寸和易用性之间取得了很好的平衡。2.8寸的尺寸足够显示清晰的图形和文字信息又不会像更大屏幕那样耗电和占用过多空间。HX8347是一款比较常见的驱动芯片有成熟的Arduino库如MCUFRIEND_kbv支持省去了自己编写底层驱动的麻烦。需要注意的是市场上有些兼容屏的驱动芯片ID可能不标准代码中if (ID 0xD3D3) ID 0x9481;这一行就是为了解决某些HX8347屏幕的识别问题而做的强制指定这是一个非常实用的避坑技巧。3. 测距传感器HC-SR04超声波模块这是Arduino生态中最经典、最廉价的超声波测距模块。其工作原理简单精度对于大多数非高精度应用如避障、粗略测距已经足够。它的有效测距范围标称2cm-400cm实际使用中在3cm到200cm范围内有较好的线性度和稳定性。需要注意的是它对被测物体的材质和角度比较敏感对于吸音材料如布料或表面不平整的物体测量结果可能会不准确甚至失效。4. 辅助输出设备RGB LED与无源蜂鸣器RGB LED用于提供一目了然的状态光提示。我选用的是共阴极RGB LED这意味着三个颜色的阴极是连在一起的需要接地而三个阳极分别通过限流电阻连接到Arduino的PWM引脚。通过PWM调节各引脚的电压0-255可以混合出任何颜色。 无源蜂鸣器与有源蜂鸣器的区别在于它内部没有振荡电路需要外部提供一定频率的方波信号才能发声。这正好给了我们编程控制的灵活性可以通过tone()函数指定频率来发出不同音调的声音用于制作更丰富的报警提示音。3. 电路搭建与硬件连接详解3.1 面包板布局与电源规划在开始插线之前一个好的面包板布局规划能事半功倍也能减少后续调试时短路或接触不良的烦恼。我的习惯是将面包板视为一个微型PCB进行功能分区。首先处理电源。将面包板两侧的电源轨通常标有“”和“-”用跳线横向连接起来这样整个面包板的上、下两排就都成了共用的5V和GND总线。然后从Arduino Mega2560的5V引脚和任意一个GND引脚分别用跳线引到面包板的“”和“-”电源轨上。务必确保连接牢固电源接触不良是导致整个系统工作不稳定的最常见原因。接下来是元件分区。我把面包板中间的区域分成几个部分左侧区域放置HC-SR04超声波传感器。它的四个引脚VCC, Trig, Echo, GND排成一排正好可以跨接在面包板的中沟两侧。中部区域放置RGB LED和无源蜂鸣器。RGB LED的四个引脚共阴极和R、G、B阳极需要仔细辨别通常最长的引脚是共阴极。蜂鸣器有正负极之分长脚为正。右侧区域预留给TFT屏幕的转接排针如果屏幕是直接插在Arduino上则不需要在面包板上占用位置。注意所有连接到Arduino数字引脚的元件特别是LED和蜂鸣器必须串联限流电阻直接连接可能会因电流过大损坏Arduino的IO口或元件本身。对于普通的红色/绿色LED220Ω到1kΩ的电阻都是安全的。RGB LED的每个颜色通道最好都单独串联一个220Ω电阻。3.2 逐线连接指南与引脚定义下面是每个模块连接到Arduino Mega2560的具体引脚定义和连接方法。你可以完全按照这个来也可以根据你的喜好调整但务必在代码中同步修改相应的引脚宏定义。1. HC-SR04超声波传感器连接VCC- 面包板5V电源轨红色跳线GND- 面包板GND电源轨黑色跳线Trig (触发)- Arduino数字引脚 30黄色跳线Echo (回波)- Arduino数字引脚 31绿色跳线2. 共阴极RGB LED连接共阴极 (最长引脚)- 面包板GND电源轨黑色跳线红色阳极 (R)- 串联一个220Ω电阻后连接到 ArduinoPWM引脚 22红色跳线绿色阳极 (G)- 串联一个220Ω电阻后连接到 ArduinoPWM引脚 23绿色跳线蓝色阳极 (B)- 串联一个220Ω电阻后连接到 ArduinoPWM引脚 26蓝色跳线3. 无源蜂鸣器连接正极 (长脚)- 串联一个220Ω电阻后连接到 Arduino数字引脚 34橙色跳线负极 (短脚)- 面包板GND电源轨黑色跳线4. 2.8寸 TFT屏幕连接这款屏幕通常通过排针直接插在Arduino Mega2560上。其引脚定义是标准的LCD_RD- Arduino 数字引脚 0 (但通常库会重新映射实际可能不用接)LCD_WR- Arduino 数字引脚 1LCD_RS- Arduino 数字引脚 2LCD_CS- Arduino 数字引脚 3LCD_RST- Arduino 数字引脚 4DB0-DB15- 分别连接到 Arduino 的数字引脚 5 到 19。背光 (LED)- 通过一个电阻连接到5V。VCC- 5VGND- GND重要提示MCUFRIEND_kbv库已经为Mega2560预定义了这些引脚映射所以你通常不需要手动连接这几十根线只需要将屏幕的排针直接插入Mega2560的对应位置即可确保方向正确屏幕的排针应对齐Mega2560的引脚排母。这是使用这类“屏蔽”式TFT屏幕最大的便利。连接完成后强烈建议你对照电路图或上述列表用万用表的通断档逐一检查关键连接特别是电源和地线确保没有虚焊或插错孔。硬件连接的可靠性是项目成功的基石。4. 软件环境配置与核心代码解析4.1 Arduino IDE库安装与屏幕驱动修复硬件准备就绪后我们转向软件。首先确保你安装了最新版的Arduino IDE。接下来需要安装两个核心图形库。打开Arduino IDE依次点击工具 - 管理库...打开库管理器。在搜索框中输入Adafruit GFX找到由Adafruit提供的“Adafruit GFX Library”点击安装。这是几乎所有Adafruit显示屏的基础图形库提供了画点、线、圆、矩形和显示文字的基本函数。再次搜索MCUFRIEND_kbv找到并安装“MCUFRIEND_kbv by David Prentice”。这个库是针对MCUFRIEND系列TFT屏蔽包括我们使用的HX8347驱动芯片屏幕的硬件专用驱动库它建立在Adafruit GFX库之上。安装完成后你可能会遇到一个特定问题某些HX8347驱动的屏幕在初始化时无法被正确识别ID导致白屏或无法显示。这就是代码中那段if (ID 0xD3D3) ID 0x9481;语句的用武之地。tft.readID()函数会尝试读取屏幕的驱动芯片ID如果返回了一个奇怪的ID如0xD3D3我们就手动将其强制设置为HX8347的一个已知ID0x9481。这是一个非常经典的“驱动修复”技巧如果你的屏幕不亮首先检查串口监视器输出的ID是什么并尝试类似的强制指定。4.2 主程序框架与核心函数剖析项目的代码结构分为一个主文件.ino和一个头文件Variable_Setup.h。这种拆分让代码更清晰易于管理。我们先看主程序的骨架。// 主程序 (Distance_Display.ino) #include Variable_Setup.h // 包含所有引脚定义、变量声明和函数原型 void setup() { Serial.begin(9600); // 初始化串口用于调试输出 // 设置各引脚模式 pinMode(buzzerPin, OUTPUT); pinMode(bPin, OUTPUT); pinMode(gPin, OUTPUT); pinMode(rPin, OUTPUT); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); // TFT屏幕初始化包含驱动修复 uint16_t ID tft.readID(); if (ID 0xD3D3) ID 0x9481; // 关键修复步骤 tft.begin(ID); tft.setRotation(5); // 设置屏幕显示方向根据你的安装方式调整 // 打印屏幕分辨率用于调试 Serial.println(tft.width()); Serial.println(tft.height()); // 绘制静态界面背景、标题框等 startupDisplay(); } void loop() { final_result(); // 核心功能函数测距、判断、显示 delay(70); // 控制循环速度约14Hz刷新率 }setup()函数完成了所有硬件初始化和静态界面绘制。loop()函数则不断循环执行final_result()这个函数整合了测距、状态判断和动态显示的全部逻辑。delay(70)决定了系统的刷新频率大约每秒14次。这个值需要权衡太短可能导致传感器来不及响应太长则显示不够实时。70ms对于HC-SR04和这个简单的图形界面是一个比较均衡的值。4.3 关键功能函数深度解读现在我们深入最核心的三个函数final_result()测距与逻辑、displayer()动态图形绘制和setRGB()LED控制。1.final_result()- 测距与状态决策中枢这个函数是系统的大脑它严格按照HC-SR04的时序要求进行测距并根据结果决策。void final_result() { noTone(buzzerPin); // 先停止蜂鸣器避免持续鸣叫 // 产生触发脉冲 digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); // 至少10微秒的高电平 digitalWrite(trigPin, LOW); // 测量高电平脉冲持续时间即回波时间 duration pulseIn(echoPin, HIGH); // 计算距离单位厘米 // 声速取34300 cm/s除以2得到单程时间对应的距离 // 简化公式距离 (时间/2) / 29.1 ≈ 时间 * 0.0343 / 2 distance (duration / 2) / 29.1; // 整数版本 distance2 (duration / 2) / 29.1; // 浮点数版本用于显示小数 // 状态判断逻辑 if (distance 75 || distance 0) { // 安全或无效距离 safeState 1; riskyState 0; dangerState 0; setRGB(0, 255, 0); // RGB LED亮绿色 displayer(safeState, riskyState, dangerState, distance2); } else if (distance 25) { // 注意区域 safeState 0; riskyState 1; dangerState 0; setRGB(255, 255, 0); // RGB LED亮黄色 displayer(safeState, riskyState, dangerState, distance2); } else { // 危险区域 safeState 0; riskyState 0; dangerState 1; setRGB(255, 0, 0); // RGB LED亮红色 displayer(safeState, riskyState, dangerState, distance2); tone(buzzerPin, 950); // 触发蜂鸣器报警950Hz频率 } }这里有几个关键点pulseIn(echoPin, HIGH)这个函数会阻塞程序直到检测到指定引脚变为高电平然后开始计时直到其变回低电平。它返回微秒级的时间长度。这是Arduino测量脉冲宽度的标准方法。距离计算公式距离 (高电平时间 / 2) / 29.1。为什么是29.1推导一下声速340m/s 34000cm/s 0.0343 cm/μs。距离 时间(μs) * 速度(cm/μs) / 2 时间 * 0.0343 / 2 时间 / 58.2。而时间/2 / 29.1正好等于时间 / 58.2。所以这个29.1是推导后的一个常数。你也可以直接用duration * 0.01715来计算结果是一样的。状态判断if-else if-else结构清晰地将距离映射到三个状态。注意第一个条件distance 0用于过滤掉无效测量有时传感器会返回0或负值。2.displayer()- TFT屏幕动态图形绘制引擎这是代码中最有趣但也最复杂的部分它根据不同的状态在屏幕上绘制动态的图形界面。void displayer(int safe, int risky, int danger, float ultradis) { // ... 局部变量定义用于图形坐标计算... // 1. 清空动态区域 tft.fillRect(10, 150, 200, 85, BLACK); // 清空雷达图区域 tft.fillRect(180, 70, 127, 24, GREY); // 清空数字显示区域 tft.fillRect(96, 126, 110, 16, LIGHT_BLUE); // 清空状态文字区域 tft.fillRect(220, 112, 86, 124, GREY); // 清空右侧符号区域 // 2. 计算动态图形的参数例如“注意”和“危险”状态下的指示条位置 float scale ultradis / 75; // 将距离映射到0-1的比例 float val_limit (scale * 150) 45; // 将比例映射到屏幕上的具体X坐标 // 3. 根据状态绘制 if (safe 1) { // 绘制绿色射线和“SAFE”文字 for (int x 0; x 5; x) { tft.drawLine(45, 180, 200, v, WHITE); // 从中心点向外发散的白线 v 5; } tft_msg(180, 70, 3, NULL, dis); // 显示距离数字 tft_signs(96, 126, 2, NULL, SAFE, GREEN); // 显示绿色“SAFE” // 绘制绿色对勾符号... } else if (risky 1) { // 绘制根据距离变化的指示条和“CAUTION”文字 for (int x 0; x 5; x) { tft.drawLine(45, 180, val_limit, v, WHITE); // 射线终点随距离变化 v 5; } tft.fillRoundRect(val_limit, 170, 8, 45, 3, GREY); // 绘制一个移动的灰色方块作为“指针” tft_msg(180, 70, 3, NULL, dis); tft_signs(96, 126, 2, NULL, CAUTION, YELLOW); // 绘制黄色三角形警示符号... } else if (danger 1) { // 类似“注意”状态但绘制红色“DANGEROUS”和红色感叹号 // 并触发 tone(buzzerPin, 950); } }这个函数的核心思想是局部刷新。为了不让屏幕闪烁我们没有在每次循环中都重绘整个界面tft.fillScreen()而是只重绘那些需要变化的部分距离数字、状态文字、动态图形和警示符号。静态的背景、边框和标题在startupDisplay()中只绘制一次。val_limit这个变量是关键它将实际距离ultradis线性映射到屏幕上的一个X坐标45到195之间从而让图形元素如射线终点、指示块能够随距离变化而移动实现了动态效果。3.setRGB()与自定义文本显示函数setRGB(int r, int g, int b)函数非常简单就是通过analogWrite()向三个PWM引脚写入0-255的值来控制RGB LED的颜色和亮度。tft_msg()和tft_signs()是我封装的两个用于显示文本的函数它们统一设定了字体、颜色、大小和位置避免了在代码中多次重复设置这些属性让displayer()函数更简洁。tft_signs()多了一个颜色参数用于显示不同颜色的状态文字。4.4 头文件变量、常量与函数声明Variable_Setup.h头文件汇集了所有全局设置修改引脚或颜色定义只需要来这里非常方便。// Variable_Setup.h #include Adafruit_GFX.h #include MCUFRIEND_kbv.h MCUFRIEND_kbv tft; // 声明TFT对象 // 字体库根据需要包含 #include Fonts/FreeSans9pt7b.h // ... 其他字体 // 16位颜色定义RGB565格式 #define BLACK 0x0000 #define RED 0xF800 // ... 其他颜色 // 引脚定义 #define trigPin 30 #define echoPin 31 int buzzerPin 34; int rPin 22, gPin 23, bPin 26; // 全局变量 int safeState, riskyState, dangerState; long duration, distance; // duration: 微秒 distance: 厘米整数 float distance2; // 距离浮点数用于显示 // 函数声明 void setRGB(int r, int g, int b); void tft_msg(int x, int y, int sz, const GFXfont *f, String msg); void tft_signs(int x, int y, int sz, const GFXfont *f, String msg, uint16_t color); void startupDisplay(); void displayer(int safe, int risky, int danger, float ultradis); void final_result();颜色定义采用的是RGB565格式16位这是TFT屏幕常用的格式。0xF800代表红色R31, G0, B00x07E0代表绿色R0, G63, B00x001F代表蓝色。你可以用在线RGB565转换工具来定义自己喜欢的颜色。5. 系统调试、优化与问题排查实录5.1 上电调试与常见故障排除硬件连接和代码上传完成后第一次上电总是最令人激动的时刻。如果一切顺利你会看到TFT屏幕亮起显示静态界面RGB LED可能闪烁一下后熄灭。当你用手在超声波传感器前移动时屏幕上的数字应该变化LED颜色和屏幕图形也会相应改变。但事情往往不会一帆风顺。下面是我在调试过程中遇到的一些典型问题及解决方法问题1TFT屏幕白屏或花屏没有任何显示。检查1电源与连接。首先确认屏幕的VCC和GND是否牢固连接到5V和GND。用万用表测量屏幕排针的电压。检查2屏幕驱动ID。打开Arduino IDE的串口监视器波特率9600查看启动时打印的信息。你会看到tft.width()和tft.height()的值以及可能的初始化信息。如果屏幕ID显示为0x0或0xD3D3这样的值说明驱动识别有问题。这时尝试修改代码中的强制指定行例如将if (ID 0xD3D3) ID 0x9481;改为if (ID 0x0) ID 0x9341;0x9341是另一种常见驱动ILI9341的ID。你需要根据你的屏幕型号搜索其正确的驱动ID。检查3库冲突。确保只安装了MCUFRIEND_kbv库如果之前安装过其他TFT库如UTFT可能会产生冲突。尝试在Arduino IDE的库管理器中暂时禁用或删除其他TFT库。问题2超声波传感器读数不稳定或始终为0/一个极大值。检查1电源干扰。HC-SR04对电源噪声比较敏感。尝试在传感器的VCC和GND引脚之间并联一个10uF-100uF的电解电容可以起到滤波作用。检查2物体材质与角度。传感器对光滑坚硬的表面反射效果最好。尝试用一块平整的木板或硬纸板作为测试物体并正对传感器。检查3测量超时。pulseIn()函数默认会等待1秒钟。如果1秒内没有收到回波它会返回0。在空旷环境下超过400cm可能就收不到回波了此时duration为0计算出的距离也是0。这是正常现象。你可以通过pulseIn(echoPin, HIGH, 30000UL)设置一个超时时间例如30000微秒对应约5米超时后函数会返回0。检查4代码逻辑。确保trigPin和echoPin的定义与实物连接一致。确保在loop()中两次测量之间有足够的延迟delay(70)给传感器留出“安静”的时间避免上一次的回波干扰下一次的触发。问题3RGB LED颜色不对或不亮。检查1共阴/共阳接法。确认你的RGB LED是共阴极还是共阳极。我的代码是针对共阴极的。对于共阴极公共端接GND阳极通过电阻接IO口输出高电平PWM值255时该颜色最亮。如果是共阳极公共端需要接5V阳极通过电阻接IO口此时需要输出低电平PWM值0来点亮逻辑是反的。你需要修改setRGB函数中的逻辑或者将电路改为共阴极接法。检查2限流电阻。确认每个颜色通道都串联了电阻。没有电阻直接连接可能会烧毁LED或损坏Arduino引脚。检查3PWM引脚。确认rPin,gPin,bPin连接的是Arduino Mega2560上标有“~”的PWM引脚如22, 23, 26。问题4蜂鸣器不响或一直响。检查1有源 vs 无源。确认你使用的是无源蜂鸣器。有源蜂鸣器给电就响无法通过tone()函数控制音调。无源蜂鸣器通常看起来是两个引脚一样长的或标有“”和“-”而有源的底部可能封有电路。检查2引脚连接。确认正极通常有“”标记或较长的引脚通过电阻接到了buzzerPin34负极接到了GND。检查3tone()与noTone()。在final_result()函数开头我调用了noTone(buzzerPin)来停止之前的鸣叫。只有在“危险”状态才会执行tone(buzzerPin, 950)。如果蜂鸣器一直响可能是noTone()没有被执行或者电路接成了有源蜂鸣器的方式。5.2 性能优化与功能扩展建议当基本功能实现后你可以考虑以下优化和扩展让项目更上一层楼1. 增加测量滤波提高稳定性原始的测距值可能会有跳动。可以在代码中增加一个简单的滑动平均滤波。例如创建一个数组存储最近10次的测量值每次显示时取平均值。const int numReadings 10; float readings[numReadings]; int readIndex 0; float total 0; float average 0; void smoothDistance(float newDistance) { total total - readings[readIndex]; // 减去最旧的读数 readings[readIndex] newDistance; // 存入新读数 total total readings[readIndex]; // 加上新读数 readIndex (readIndex 1) % numReadings; // 循环索引 average total / numReadings; // 计算平均值 // 后续使用 average 代替 distance2 进行显示和判断 }在final_result()中调用smoothDistance(distance2)并用average更新显示。2. 增加温度补偿提高精度声速受温度影响较大。公式v 331.4 0.6 * T其中T为摄氏温度。你可以增加一个DS18B20或DHT11温湿度传感器实时测量环境温度并用修正后的声速计算距离。float temperature 25.0; // 假设当前温度25°C实际应从传感器读取 float speedOfSound 331.4 0.6 * temperature; // m/s distance2 (duration / 1000000.0) * (speedOfSound * 100) / 2.0; // 单位厘米这样在温差大的环境下测量精度会显著提升。3. 扩展人机交互增加按键添加一两个按键用于切换显示模式如切换单位cm/inch、校准传感器或调整报警阈值。添加SD卡模块将距离数据连同时间戳一起记录到SD卡中用于后期分析。无线传输增加一个蓝牙如HC-05或Wi-Fi模块如ESP8266将距离数据发送到手机APP或电脑上位机实现远程监控。4. 优化显示效果更丰富的图形利用Adafruit_GFX库的函数可以绘制更复杂的图形比如一个模拟的雷达扫描图或者一个随着距离变化而填充的进度条。显示历史曲线在屏幕一侧开辟一个区域绘制最近几十次测量值的折线图直观显示距离变化趋势。多语言支持将状态文字“SAFE”、“CAUTION”、“DANGEROUS”定义为字符串变量可以方便地切换成中文或其他语言。调试嵌入式项目耐心和系统性的排查是关键。从电源开始到信号再到逻辑一步步缩小范围。利用好串口打印Serial.println()是调试代码的利器可以把关键变量如duration、distance、屏幕ID的值打印出来帮助你理解程序实际运行的状态。