多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

STC8H驱动HC-SR04超声波测距与LCD1602显示实战解析

STC8H驱动HC-SR04超声波测距与LCD1602显示实战解析 简介这是一份面向嵌入式初学者的STC单片机与HC-SR04超声波测距综合示例工程演示了通过LCD1602液晶屏实时显示测量距离的方法相关代码可复用于智能小车避障、液位监测等自动化和物联网场景。资源共15个文件压缩包仅38KB以C源文件main.c、lcd1602.c、delay.c、头文件、Keil工程文件uvproj/uvopt、备份文件及ReadMe说明为主结构简洁便于直接打开学习或移植。代码覆盖HC-SR04触发与回波时间测量、距离换算、LCD1602驱动与动态显示刷新等关键流程适合正在学习51/STC单片机、传感器接口或字符液晶显示的读者。已有1936人浏览学习通过该项目可快速掌握超声波测距原理及常见外设驱动编写思路为后续物联网项目打下基础。1. 超声波测距的RAR工程包到底改什么STC8H、HC-SR04、LCD1602三者怎么配合拿到这套“STC单片机HC-SR04超声波测距程序-LCD1602屏显示”压缩包时别急着烧录先看里面的main.c、lcd1602.c、STC8.H三个文件。工程的核心逻辑很简单STC8H向HC-SR04发出一个高电平触发脉冲传感器将40kHz超声波打向目标回波回来后在Echo引脚上输出一个高电平脉冲这个高电平宽度就是超声波往返的时间最终换算成距离后交给LCD1602显示。真正容易被忽略的是Echo脉冲宽度的计量方式。2cm到400cm量程对应的Echo宽度只有116微秒到23.3毫秒普通16位定时器在12T模式下完全覆盖不需要中断翻转就能读完但如果改用1T模式计数23.3毫秒就会把65535个计数全部吃光必须处理溢出。另外这套代码里的lcd1602驱动不是I2C而是直接驱动并行总线的版本。想移植到STC89C52RC或带PCF8574转接板的液晶屏时寄存器定义和引脚初值都得跟着换。2. 硬件接线与HC-SR04、LCD1602的时序约束2.1 HC-SR04的触发与回波时序如何进入STC8H定时器HC-SR04一共四个引脚VCC、Trig、Echo、GND。Trig是输入Echo是输出。使用时先让Trig引脚保持20微秒左右的高电平模块内部会发出8个40kHz的方波随后Echo输出高电平直到接收探头捕获到回波为止。Echo高电平持续的时间正比于目标到探头的往返距离。时序参数典型值说明Trig高电平宽度10us到20us低于10us时触发不可靠最小测量距离2cm对应Echo脉宽约116us最大测量距离400cm对应Echo脉宽约23.3ms建议测量间隔60ms以上避开余振和二次回波叠加这个时间窗口决定了定时器的选择。以11.0592MHz晶振为例把STC8H的内部定时器配置为传统8051的12T模式一个计数脉冲是12 / 11.0592MHz约等于1.085us。16位定时器最大计数65535对应测量窗口约71ms比HC-SR04的23.3ms极限大出三倍也就是说一次测距根本不会触发溢出中断。#include STC8.H void Timer0_Init(void) { TMOD (TMOD 0xF0) | 0x01; // 定时器0工作在方式116位不自动重装 AUXR 0x7F; // T0x12清0使用12T分频模式 TH0 0; // 计数初值清零 TL0 0; TR0 0; // 测距时再启动 }这里最关键的是AUXR 0x7F这一句。STC8H的定时器默认可能是1T模式如果不主动清掉T0x12位1us都不到就计数一次回波还没结束定时器已经溢出。换成12T模式后一个计数脉冲约1.085us400cm回波宽度约23.3ms换算出来大约21400个计数普通16位计数寄存器装得下。若坚持使用1T模式则必须在代码里额外处理TF0溢出次数用32位变量拼接计数值代码量会成倍上升。2.2 LCD1602四位总线初始化与引脚分配LCD1602本身是并行接口摘要描述里提到的I2C或SPI其实是指加装了PCF8574这类串转并转接板后的形态。这份工程使用的是最直接的并行方式只接D4到D7四根数据线RS、RW、E三根控制线总共占用七个IO口。STC8H引脚外接器件引脚方向作用P2.0HC-SR04 Trig输出触发超声波P2.1HC-SR04 Echo输入接收回波脉宽P0.0LCD1602 RS输出寄存器选择P0.1LCD1602 RW输出读写选择接地可只写P0.2LCD1602 E输出使能信号P0.4到P0.7LCD1602 D4到D7输出数据线工程初始化时把RW直接拉低让液晶始终处于写状态省去读忙判定的逻辑。E引脚每次写数据都要先拉高再拉低形成一个下降沿锁存数据。这样虽然比8位并行模式多两步发送操作但节省了四个IO口对后面扩展按键和多路超声波模块有实际意义。void Lcd_WriteNibble(unsigned char dat) { P0 (P0 0x0F) | (dat 0xF0); E 1; // E先拉高 _nop_(); E 0; // 产生下降沿数据写入液晶 } void Lcd_WriteCmd(unsigned char cmd) { RS 0; Lcd_WriteNibble(cmd); Lcd_WriteNibble(cmd 4); Delay_1ms(2); }写命令和写数据共用同一个半字节发送过程区别只在RS电平。初始化序列依旧保留4位模式切换必需的三次0x03时序然后发送0x28进入两行5x7点阵模式再用0x0C关闭光标、0x06控制地址自动加一、0x01清屏。不少刚接触LCD1602的开发者会直接跳到最后四条命令结果屏幕只在第一行显示内容或出现乱码就是因为缺少了前面向8位模式复位的握手过程。2.3 3.3V核心板与5V供电的HC-SR04混接HC-SR04的VCC建议接5V但部分STC8H最小系统板跑到3.3V。模块的Echo输出在5V供电时高电平接近5VSTC8H的IO口并不都具备5V容忍能力直接接上轻则读数异常重则损坏引脚。常见做法是在Echo引脚串联一个10k和一个20k的分压电阻中间抽头接单片机。5V经过分压后得到约3.33V满足STC8H的高电平输入要求。Trig侧是模块输入3.3V信号落在HC-SR04的逻辑阈值之内多数情况下可以直连如果出现偶发不触发就在Trig线上串一个1k电阻限流再把模块和单片机的GND牢固共地。提示排错时最先怀疑地线。超声波模块和单片机不共地Echo输出就是悬浮电平液晶显示的距离会来回跳。3. 定时器计数与LCD1602显示实现拆分3.1 测距函数measure_once的完整写法把测距拆成三个动作触发、等待Echo上升沿、测量高电平宽度。#include STC8.H #include delay.h #include lcd1602.h sbit TRIG P2^0; sbit ECHO P2^1; unsigned int Measure_Once(void) { unsigned int ticks 0; TRIG 0; delay_10us(2); // 拉低一段时间保证触发稳定 TRIG 1; delay_10us(2); // 高电平保持约20us TRIG 0; while (ECHO 0) // 等待Echo上升沿 { TF0 0; TH0 0; TL0 0; TR0 1; // 提前开定时器超时判断用 delay_10us(1); // 延时是为了避免死循环卡死 TR0 0; if (TF0) { return 0; // 超时没有回波按无目标处理 } } TH0 0; TL0 0; TR0 1; // 从Echo拉高开始计时 while (ECHO 1) { if (TF0) // 溢出中断标志理论上一测距周期内不会溢出 { TF0 0; break; } } TR0 0; ticks ((unsigned int)TH0 8) | TL0; return ticks; }这段代码里最容易被新手改成死循环的地方是第一个等待Echo上升沿。Trig拉高之后如果目标在量程之外或探头发射异常Echo可能一直保持低电平。此时定时器只是用来做超时保护而真正计数高电平宽度的起点放在Echo变为高电平之后也就是TH0 0; TL0 0; TR0 1;之后的第二个while里。3.2 计数值换算成厘米并显示到LCD1602有了计数ticks换算距离要用到12T模式下的时间基准。当前工程用的11.0592MHz晶振每个tick约1.085us。声波在20摄氏度空气中的速度约为343m/s往返一次的距离计算公式为距离 时间 x 声速 / 2最后再除以100换算成厘米。float Ticks_To_Cm(unsigned int ticks) { // 1个tick 1.085us // 往返时间对应的厘米数 ticks * 1.085 / 58 return (float)ticks * 1.085f / 58.0f; } void Show_Distance(float cm) { char buf[8]; unsigned int int_part (unsigned int)cm; unsigned int dec_part (unsigned int)((cm - int_part) * 10.0f); buf[0] 0 (int_part / 100) % 10; buf[1] 0 (int_part / 10) % 10; buf[2] 0 int_part % 10; buf[3] .; buf[4] 0 dec_part % 10; buf[5] c; buf[6] m; buf[7] \0; LCD_ShowString(0, 1, buf); }直接用sprintf在Keil C51里也能跑但会引入约1KB到2KB的库代码对STC8H这种容量有限的单片机没有必要。上面这种拆位写法每显示一位都直接取模运算代码执行时间固定也不会因为printf的重定向占掉串口资源。主循环里每100ms调用一次测距和刷新正好满足HC-SR04的测量间隔要求。3.3 连续测距时最容易忽略的Echo残留问题测量周期里Trig低电平和两次测量之间的延时一样重要。如果上一次Echo还没有完全拉低立刻再次触发模块会把同一个目标回波再算一次导致显示值频繁跳变。void main(void) { unsigned int ticks; float distance; Timer0_Init(); LCD_Init(); LCD_ShowString(0, 0, Distance:); while (1) { if (ECHO 1) // 上一次回波残留先等它结束 { continue; } ticks Measure_Once(); if (ticks 0) { LCD_ShowString(0, 1, 400 cm); } else { distance Ticks_To_Cm(ticks); Show_Distance(distance); } delay_ms(100); } }我在调试其他HC-SR04项目时经常看到这个坑单独跑一次测距完全正常放进while循环后数值忽大忽小最后查下来都是因为连续触发时没有检查Echo引脚状态把上一次测量周期中未结束的脉宽当成新数据读了进去。4. 声速补偿、滤波和HC-SR04常见故障对照4.1 温度对声速的影响与补偿公式固定使用/58换算在室温下没有大问题但它假设声速恒定为343m/s。实际声速会随温度变化公式近似为float Sound_Speed_By_Temperature(float temp_c) { // 空气中声速近似公式摄氏温度代入 return 331.4f 0.607f * temp_c; } float Ticks_To_Cm_Compensated(unsigned int ticks, float temp_c) { float sound_speed Sound_Speed_By_Temperature(temp_c); float time_sec (float)ticks * 1.085e-6f; // 声波走了往返距离除2之后再换算成厘米 return time_sec * sound_speed / 2.0f * 100.0f; }从0摄氏度到50摄氏度声速从331.4m/s增加到361.7m/s变化幅度约为9%。在400cm量程上这个误差最大可以到35cm左右。如果设备放在室外或者靠近发热源固定系数会让远距离数据明显偏离真实值。工程里预留一个温度变量就行把DS18B20读出的温度传进Sound_Speed_By_Temperature显示距离时替换原来的换算函数。4.2 五次去极值平均过滤异常回波HC-SR04在实际环境中经常会受到边角反射、电线杆回波、风扇叶片等干扰单次测量结果可能存在几个厘米甚至几十厘米的毛刺。我一般会连续采集五次排序后取中间的三个值做平均。unsigned int Median_Filter_5(void) { unsigned char i, j; unsigned int temp, values[5]; for (i 0; i 5; i) { values[i] Measure_Once(); delay_ms(20); // 保证两次测量之间Echo复位 } for (i 0; i 4; i) // 冒泡排序 { for (j i 1; j 5; j) { if (values[i] values[j]) { temp values[i]; values[i] values[j]; values[j] temp; } } } return (values[1] values[2] values[3]) / 3; }这里的20ms延时配合主循环的100ms整体刷新率会降到每秒2次左右。对显示距离来说足够但如果要做运动目标的跟踪就得并行采集或者改用滑动窗口中位滤波来保持刷新率。4.3 HC-SR04排障对照表现象检查位置常见修复方式LCD1602无显示或有方块对比度电压、E信号极性V0接10k电位器中间抽头缓慢调整距离显示固定在最大量程Echo接线、回波未返回确认Echo引脚是P2.1探头对准平面目标距离跳变不规律供电和地线在VCC与GND之间并联100uF电解电容近距离读数偏大Trig脉宽不足增大到20us以上稳定性更好远距离读数偏小声速未补偿带入温度变量重新换算实际项目中还有个不算罕见的问题探头前面有网罩或遮挡物时超声波会在这个固定位置产生强反射无论后面目标在哪显示值都被网罩锁定。处理方式是把探头开孔做大网罩孔直径尽量大于15mm并让网罩表面平整光滑。5. 用逻辑分析仪校验回波脉宽并向三路超声波扩展5.1 抓取Trig和Echo波形验证定时器换算系数如果没有示波器一支几十元的逻辑分析仪就能完成回波校验。把分析仪通道1接Trig通道2接Echo采样率设置在16MHz以上触发条件设为Trig上升沿抓一次完整的触发周期。测量结果应该是Trig高电平约20us随后Echo经过约24us延迟后拉高高电平持续时间对应目标距离的往返时间。目标放在50cm处理论上Echo宽度约2.92ms换算出来就是50cm左右。如果逻辑分析仪读出的脉宽明显偏小优先检查Trig高电平时间是否足够如果脉宽稳定但换算距离偏大把抖动放到长期曲线中来看通常会找到某个方向倾斜的反射面。这个验证过程也能确认定时器计数是否和示波器读值一致找出晶振偏差带来的系统误差。5.2 三路测距轮询与后续改造方向多路超声波最稳妥的做法不是并行触发而是让每个传感器错开测距时间。三路HC-SR04分别占用P2.2和P2.3、P2.4和P2.5每次只触发一路测量完等Echo完全拉低后再启动下一路每路间隔控制在80ms以上unsigned int dist[3]; dist[0] Measure_Once(); delay_ms(80); dist[1] Measure_Once(); delay_ms(80); dist[2] Measure_Once(); delay_ms(80);三路轮询的顺序里还要考虑物理布置不要让几个探头正对彼此否则A探头的超声波会在未到达目标前就被B探头当作回声接收。把探头间距拉开到30cm以上或者将安装角度错开三路读数才能互不干扰。后续想进一步提升可以考虑把Echo引到外部中断引脚用捕获方式读取脉宽主循环就可以在等待回波的间隙去刷新LCD或处理串口指令代价是代码从简单轮询变成状态机这也是许多工业测距模块在单片机上采用的最终形态。本文还有配套的精品资源点击获取
返回列表