基于STC89C51的超声波测距仪设计:温度补偿与盲区处理详解
2026/9/18 11:42:04 网站建设 项目流程

简介:一份基于STC89C51单片机的超声波测距仪毕业设计文档,适合机电一体化、电子信息类专业学生毕设选题或单片机测距项目入门参考。文档围绕STC89C51控制核心,结合HC-SR04超声波传感器与DS18B20温度传感器,给出从系统总体设计、硬件电路模块到软件流程与距离计算补偿的完整方案。压缩包共1个DOC文件,大小仅64KB,内容精炼,包含摘要、前言、系统设计、总结与参考文献等常见论文结构,便于快速查阅核心原理与实现步骤。资源已有201人学习,尤其适合需要参考完整设计框架、复现超声波测距实验或撰写毕业设计文档的读者。

1. 超声波测距仪不是测时间那么简单:STC89C51 的温度补偿与盲区问题

超声波测距是单片机课程设计和毕业设计里出现频率最高的题目之一,原理一句话就能说清:发射探头发出 40kHz 脉冲,遇到障碍物返回,测出往返时间再乘声速除以 2。但真正把它做成一台能稳定显示、误差可控的测距仪,很多人卡在三个地方:Echo 高电平的时间怎么测才准、温度变了读数怎么跟着变、探头正前方 4 厘米以内的物体为什么测不到。这三个问题恰恰决定了这份基于 STC89C51 单片机的超声波测距仪设计是停留在"仿真能跑",还是能通过实物验收。本文按硬件选型、定时器测时、调试排错、论文数据整理这条线展开,适合正在做课设或毕设的电子信息类学生,也适合想快速上手 HC-SR04 的 51 单片机开发者。

2. 为什么毕设选 STC89C51:HC-SR04 测距原理与硬件接线

2.1 HC-SR04 的工作时序与测量边界

HC-SR04 是目前最便宜也最容易买到的超声波测距模块,工作电压 5V,静态电流小于 2mA。它的控制时序只有两根线:Trig 输入触发信号,Echo 输出回波指示。主控给 Trig 一个大于 10us 的高电平脉冲,模块内部自动发出 8 个 40kHz 方波,然后拉高 Echo 引脚,高电平持续时间就是超声波从发射到返回的往返时间。主控只需要测量 Echo 高电平的时长,就能算出距离。

HC-SR04 官方标称测量范围 2cm 到 400cm,分辨率理论可达 3mm。实际使用中,340m/s 声速对应每厘米约 29.4us,也就是说 Echo 每拉高 294us 代表距离 5cm。STC89C51 晶振 12MHz 时机器周期 1us,用定时器直接计 Echo 高电平的 us 数,完全够用。需要特别注意的是,STC89C51 没有硬件捕获单元,不能像 STM32 那样用输入捕获自动记录高电平时间,必须靠外部电平轮询加定时器配合,这会引入指令周期误差,后面第 3 章会专门讲怎么消除这个误差。

测量边界有两层含义。第一层是量程,Echo 高电平持续时间与距离成正比,如果障碍物超过 4 米,高电平时间约 23ms,此时定时器必须配置成不回绕的 16 位模式,否则计数溢出后读到的数值是错的。第二层是盲区,模块发射完 8 个脉冲后,探头本身有机械余振,Echo 引脚在这段时间内会维持一个不稳定的电平,实测中距离小于 2cm 时读数会突然跳到一个随机大值。设计前先确认这两条边界,能省掉后面一半的调试时间。

2.2 STC89C51 的资源分配与最小系统电路

STC89C51 是宏晶科技生产的 51 内核单片机,片内 4KB Flash、512B RAM,有 3 个定时器(T0、T1、T2),支持 ISP 在线下载。相比 AT89C51,它不需要编程器,用 USB 转 TTL 串口配合 STC-ISP 软件就能烧录,这对只有一块万用板和一台电脑的毕设场景来说非常实用。做超声波测距仪,片内资源刚好够用:T0 做 Echo 计时,T1 做测温传感器的时序延时,串口留作调试输出,P2 口接 LCD1602 数据线。

硬件接线要围绕降低干扰来设计,不要直接照搬开发板的杜邦线接法。HC-SR04 的 Echo 输出是 5V 电平,STC89C51 的 I/O 口也是 5V 电平,可以直接相连,但 Echo 引脚在无回波时会保持低电平,不会产生持续灌电流,所以不需要加限流电阻。建议的引脚分配如下表:

模块/器件信号引脚STC89C51 引脚说明
HC-SR04TrigP1.0触发脉冲输出
HC-SR04EchoP1.1回波时间输入,必须用定时器测
LCD1602RS / RW / ENP2.0 / P2.1 / P2.2控制线
LCD1602D4-D7P2.4-P2.74 位数据模式
DS18B20DQP3.7温度采集,用于声速补偿
独立按键S1P3.2切换显示模式(距离/温度)

供电电路要特别注意。HC-SR04 发射瞬间电流可达 30mA 以上,如果用开发板 USB 口直接供电,LCD1602 背光一开,电压跌落会导致 Echo 波形出现毛刺。常用的做法是 5V 电源进板后,在 HC-SR04 的 VCC 与 GND 之间并联一个 100uF 电解电容和一个 104 瓷片电容,前者稳住瞬态电流,后者滤高频干扰。STC89C51 的 EA 引脚必须接高电平(接 VCC),否则单片机从外部 ROM 取指,程序跑不起来,这是新手最容易漏的地方。

2.3 温度测量:声速补偿的前提

超声波在空气中的传播速度不是固定值,工程上常用近似公式 c = 331.4 + 0.6·T(T 为摄氏温度),0℃ 时约 331.4m/s,30℃ 时约 349.4m/s,相差约 5.4%。不做温度补偿的测距仪,在冬夏温差大的环境下会稳定地多测或少测几个厘米,这在答辩演示时很难解释。补偿方案我一般用 DS18B20 数字温度传感器,它单总线协议只需一根数据线,精度 ±0.5℃,足够满足声速补偿需求。

DS18B20 的接入代码要处理好时序,因为 STC89C51 没有专门的单总线控制器,所有时序都必须用延时函数精确模拟。初始化应答是协议的第一步,代码如下:

sbit DQ = P3^7; bit ds18b20_reset(void) { bit presence; DQ = 0; // 主机拉低总线,产生复位脉冲 delay_us(480); // 复位脉冲宽度 480us~960us DQ = 1; // 释放总线,等待从机应答 delay_us(60); // 应答窗口 60us~240us presence = DQ; // 读取电平:0 表示从机存在,1 表示无应答 delay_us(410); // 确保复位时序总长超过 960us return presence; }

逻辑说明:DS18B20 是单总线器件,所有操作都由主机发起,复位是每次通信的第一步。presence 读到的 0 表示传感器在线,如果读到 1,说明接线有问题或者器件损坏,后续读写就没有意义,程序应在主循环里给出错误提示而不是继续算距离。delay_us 函数在 12MHz 晶振下可写成空循环嵌套,每层循环 1us,具体延时长度要和系统时钟严格对应,否则时序漂移会导致传感器偶发返回 85℃ 的错误值。

3. 用定时器测 Echo 高电平:最小测距代码与温度补偿实现

3.1 T1 的模式配置与溢出处理

测距核心是准确测量 Echo 引脚的高电平持续时间。STC89C51 的定时器 T1 工作在模式 1 时是 16 位计数器,最大计数值 65535,在 12MHz 晶振下,1 个机器周期等于 1us,所以最大可测时间 65535us,对应距离约 11 米,覆盖 HC-SR04 量程有余。配置代码要放在系统初始化里,并确保 T0 和 T1 的模式控制字互不影响:

void timer1_init(void) { TMOD &= 0x0F; // 清空 TMOD 高 4 位,保留 T0 的模式位 TMOD |= 0x50; // T1 模式1:16位定时器,仅由 TR1 控制启停 TH1 = 0; // 计数值清零 TL1 = 0; TR1 = 0; // 先不启动,Echo 变高时再启动 }

参数说明:TMOD 寄存器高 4 位控制 T1,0x50 的二进制是 0101 0000,其中 bit6(C/T)为 0 表示定时模式,计数脉冲来自内部机器周期;bit5(GATE)为 1 表示由 TR1 和 INT1 引脚共同控制,这里 GATE 置 0 更合适,只由 TR1 控制,避免外部中断引脚电平影响计时启停。清空高 4 位前先做掩码TMOD &= 0x0F,防止覆盖 T0 的配置,这是多定时器共用一个寄存器时的标准写法。

3.2 发射、等待回波与时间换算

主测距逻辑分四步:拉高 Trig 10us 发脉冲、等待 Echo 变高、启动 T1 计时、等待 Echo 变低停止计时。这里有个常见错误:直接用 while 死等 Echo 变低,如果测量范围内没有障碍物,Echo 会一直保持低电平,程序就卡死在等待里,表现为主界面假死。所以我习惯给等待加超时判断,用 T1 的溢出标志 TF1 做天然超时,一石二鸟:

unsigned int measure_distance_cm(void) { unsigned int ticks; unsigned int timeout; TRIG = 1; // 拉高触发引脚 delay_us(10); // 脉冲宽度 10us,必须大于模块要求的最小 10us TRIG = 0; // 拉低,触发结束 timeout = 0; while (!ECHO && timeout < 20000) { // 等待 Echo 变高,超时约 20ms timeout++; } if (!ECHO) { return 0xFFFF; // 无回波,返回特殊值表示超量程 } TH1 = 0; TL1 = 0; // 计数值清零 TR1 = 1; // Echo 已变高,启动定时器 while (ECHO && !TF1); // 等待 Echo 变低,或定时器溢出 TR1 = 0; // 停止计数,此刻 TH1:TL1 就是高电平持续 us 数 if (TF1) { // 如果溢出,说明距离超过约 11 米 TF1 = 0; // 清除溢出标志,下次重新判断 return 0xFFFE; } ticks = (TH1 << 8) | TL1; // 合并 16 位计数值 return ticks; }

逻辑说明:Echo 变高前的等待为什么要单独做超时?因为模块无回波时 Echo 引脚永远保持低电平,若直接启动 T1 再等变低,程序会永远停在 while(ECHO && !TF1) 这一行。这里的先等变高、再计时的结构,配合 20ms 的软件超时,能把"无回波"和"超量程"两种情况区分开。返回值 0xFFFF 和 0xFFFE 是两个哨兵值,主函数里看到这两个值就显示"---"而不是错误的距离数字。

ticks 的单位是 us,距离换算公式为 d(cm) = ticks × 声速 / 2。如果固定用 340m/s 声速,公式变成 d(cm) = ticks × 0.017,即 ticks / 58。注意,这个除法如果用整数运算,ticks 小于 58 时结果为 0,所以主函数里要先用 32 位中间变量累加再取整,不能直接整除。

3.3 温度补偿公式的整数运算实现

float 运算在 STC89C51 上不是不能用,但 51 内核没有硬件浮点单元,一次除法要占几百个指令周期,对于每秒 20 次的测量循环来说负担不大,可如果还要驱动 LCD,主循环会被拖慢。我用整数运算做补偿:声速公式 c = 331.4 + 0.6·T 改写为 c 的 10 倍值,得到整数表达式:

// 根据 DS18B20 读到的温度值计算补偿后的距离(单位 mm) unsigned int calc_distance_mm(unsigned int ticks, int temperature) { unsigned long speed_x10; // 声速的 10 倍值,单位 0.1m/s unsigned long distance_x10; if (temperature < -10) temperature = -10; // 限制补偿范围 if (temperature > 50) temperature = 50; speed_x10 = 3314UL + 6UL * (unsigned long)(temperature); // 距离 = 时间(s) * 声速(m/s) / 2 // ticks 单位 us,声速单位 0.1m/s // 结果先放大 10 倍再除,避免浮点 distance_x10 = (unsigned long)ticks * speed_x10 / 20000UL; return (unsigned int)distance_x10; }

参数说明:speed_x10 的意义是声速的 10 倍,25℃ 时声速 346.4m/s,speed_x10 就是 3464。distance_x10 计算式的推导:ticks 是 us 数,乘以 0.1m/s 为单位的速度,再除以 2 得到半程距离,单位是 0.1mm;除以 20000 后得到 mm。这个式子里所有中间变量都是 unsigned long,避免 16 位乘法溢出。限制 temperature 范围是因为 DS18B20 在刚上电或接线接触不良时会读出 85℃ 或 0℃ 以下的异常值,不做范围钳制会让补偿公式输出离谱距离。整套计算只用整数乘除,配合 12MHz 晶振,单次换算时间远小于模块 60ms 的最大测量周期。

4. 波形与读数对齐:超声波测距仪的三类调试问题和修正方法

4.1 没有示波器时,先用串口把原始时间打出来

HC-SR04 和单片机之间的时序问题,很多人一上来就对着 LCD 显示的数字猜,效率很低。最有效的调试手段是把 Echo 高电平的 ticks 原始值通过串口发到电脑,直接观察原始时间数据。STC89C51 的串口在模式 1 下波特率由 T2 决定,初始化代码如下:

void uart_init(void) { SCON = 0x50; // 模式1:8位UART,允许接收 TMOD &= 0xCF; // 清空 T2 相关位(TMOD 第6位是 T2 的 C/T 位保留位) T2CON = 0x34; // T2 作为波特率发生器,16位自动重装 RCAP2H = 0xFF; // 波特率 = 晶振 / (32 * (65536 - RCAP2)) RCAP2L = 0xFD; // 12MHz 晶振下 0xFFFD 对应约 9600 波特 TR2 = 1; } void uart_send_ticks(unsigned int ticks) { SBUF = (char)(ticks >> 8); // 先发高字节 while (!TI); TI = 0; SBUF = (char)(ticks & 0xFF); // 再发低字节 while (!TI); TI = 0; }

调试方法是:把测距函数返回的 ticks 直接通过串口发到电脑串口助手,用十六进制显示。Echo 高电平时间的 us 数 = 高字节×256 + 低字节。比如收到 0x01 0x2C,表示 300us,对应距离约 5.1cm,和卷尺实测接近就说明定时器计时正确。如果发现 ticks 数值呈现规律性跳变,比如总是在某个固定值附近多 12 或 24 个计数,那是 while 轮询 ECHO 引脚引入的指令周期误差;如果跳变是随机的几百到几千,就要怀疑 Trig 脉冲宽度不够或模块供电不稳。

4.2 读数跳变:毛刺、误触发与软件滤波

排除供电问题后,读数跳变的另一个常见来源是 Echo 引脚上的噪声毛刺被单片机误判成回波信号的起点或终点。HC-SR04 的 Echo 在模块接收到回波前一直保持低电平,但环境中的电机火花、继电器吸合会在电源线上产生尖峰,传导到 Echo 引脚。硬件上可以在 Echo 与 GND 之间并联一个 103 电容(10nF)做低通滤波,软件上则要加滤波算法。

软件滤波我一般选滑动中值滤波而不是平均值滤波。原因是:超声波测距的异常值往往是回声误触发导致的明显离群值,比如真实距离 20cm 时突然跳出一个 120cm 的读数,中值滤波能完全排除这种野值,而平均值滤波会把野值按权重混进结果里。简单的滑动中值实现如下:

#define FILTER_N 5 unsigned int filter_distance(unsigned int new_sample) { static unsigned int buf[FILTER_N]; unsigned int tmp; unsigned char i, j; // 滑动窗口:新数据进队尾,最老数据出队 for (i = 0; i < FILTER_N - 1; i++) { buf[i] = buf[i + 1]; } buf[FILTER_N - 1] = new_sample; // 冒泡排序,取中间值 for (i = 0; i < FILTER_N; i++) { for (j = i + 1; j < FILTER_N; j++) { if (buf[j] < buf[i]) { tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp; } } } return buf[FILTER_N / 2]; }

参数 FILTER_N 选 5 是因为:窗口太小(3)抗不住连续两个野值,窗口太大(7 或 9)会让移动物体测距时响应明显变慢。中值滤波的代价是每次测量都要做一次 5 元素排序,在 12MHz 主频下耗时约 20us,不影响测量节奏。注意排序前先做窗口滑动,保证 buf 里始终是最新 5 次数据。如果显示值仍然缓慢漂移,再在输出前加一级加权平均,权重倾向新值,比如out = (out * 3 + buf[2]) / 4,这样既有平滑效果又没有明显滞后。

4.3 盲区缩小:从 4 厘米到 2 厘米能做哪些事

前面提到 HC-SR04 官方盲区约 2cm,实测很多模块盲区在 3cm 到 4cm 之间,表现为近距离放一张纸片,读数不是变小而是突然跳到几十厘米。原因是发射脉冲结束后的余振信号被接收电路误判为回波。在硬件不改动的前提下,软件能做一件事:从 Echo 上升沿开始,强制忽略前 250us(对应约 4.3cm)的下降沿,让余振信号先稳定下来:

// Echo 变高后,先做 250us 的拒绝窗口 while (ECHO && !TF1) { if (ticks_ignore < 250) { // 延时 1us,累积拒绝窗口 } }

这段逻辑要配合定时器实现,不能简单地空转延时,否则会把回波时间一起拖长。正确做法是启动定时器后轮流检查 TH1:TL1 当前值,小于 250 时只更新状态不做停止判断。代价是模块真正能可靠测到的最小距离变成约 4cm 左右,但如果盲区标称值是 3cm,这 250us 的拒绝窗口反而能把 3cm 处不确定的读数变成稳定的最大量程输出,整体表现更好。想真正测到 2cm,只剩换模块方案:用收发分体的超声波探头(如 TCT40-16T/R)自己搭发射接收电路,接收信号经运放放大后用比较器整形再送单片机,但这样做放大倍数和比较器阈值都要反复调试,工作量至少翻一倍,不是毕设时间预算内划算的投入。

5. 毕设文档里的测距仪:数据表、误差分析和一个降低代码空间的技巧

5.1 设计说明书里必须出现的三张表

毕业设计说明书和课设报告最大的区别在于,老师重点关注的不只是"能不能跑",而是"这个设计是否有可验证的精度、有可追溯的误差来源"。以下三张表建议在测试环节就顺手记录,不要等在电脑前临时编数据。第一张是距离测试表,固定温度(记录室温),在 10cm、25cm、50cm、100cm、200cm 五个点用卷尺(精度 1mm)做基准,每个点测 10 次记录极值;第二张是温度补偿对照表,在室内空调环境下测 18℃ 和 28℃ 两组数据,对比补偿前后的误差百分比;第三张是从 3cm 到 5cm 逐毫米测试,记录盲区范围和跳变临界点,这张表最能体现你对盲区问题的真实理解。

距离测试表每行建议包含:基准距离、实测均值、最大偏差、最小偏差、相对误差。最终取 50cm 处最大的相对误差值作为整个系统的标称精度。HC-SR04 模块在 50cm 处实测相对误差通常在 1% 到 3% 之间,这个数据区间写进报告是可信的。

5.2 误差分析怎么描述才显得扎实

论文里的误差分析不能写成"模块精度不高所以有误差"这种一句话带过。可操作的分析框架是分三项展开。第一项是系统误差,来自 Trig 脉冲宽度误差和定时器启停的指令延迟,实测方法:固定障碍物位置,用逻辑分析仪抓 Trig 和 Echo 波形,统计两个波形的上升沿、下降沿之间的时间差与代码读回 ticks 的差值;如果 ticks 总是比实测长 X us,就在程序里校正。第二项是声速模型误差,标准声速公式在干燥空气下成立,湿度、气压都会让实际声速偏离公式,所以测试表里要记录温湿度;第三项是量化误差,定时器分辨率 1us,对应距离量化步长约 0.17mm,这一项在 1 米内可以忽略,在 4 米处则累积到约 0.7mm。

这三项来源写清楚后再给出合成公式,误差分析部分就能立得住。不要忽略软件滤波对误差的影响:中值滤波会把快速移动物体的瞬时值拉平,所以"动态测距误差"和"静态测距误差"要分开写,静态测静态的,动态测动态的,这也是答辩时老师爱问的点。

5.3 用一个查找表替换浮点开销

最后一个技巧是处理温度补偿时的代码空间优化。如果把声速公式直接写成浮点表达式,Keil 会链接进约 1KB 的浮点库,对 4KB 的 STC89C51 来说很紧张。更干净的做法是离线算好温度与声速的映射表,用查表插值代替实时浮点计算。温度范围 -10℃ 到 50℃,间隔 1℃ 共 61 个点,每个点存 0.1m/s 单位的声速值,用 code 关键字放到程序 Flash 里:

code unsigned int speed_table[61] = { 3254, 3260, 3266, 3272, 3278, 3284, 3290, 3296, 3302, 3308, // -10℃ 到 -1℃ 3314, 3320, 3326, 3332, 3338, 3344, 3350, 3356, 3362, 3368, // 0℃ 到 9℃ 3374, 3380, 3386, 3392, 3398, 3404, 3410, 3416, 3422, 3428, // 10℃ 到 19℃ // 后续每行按 3314 + 6*T 递推 }; unsigned int get_speed_x10(int temperature) { if (temperature < -10) temperature = -10; if (temperature > 50) temperature = 50; return speed_table[temperature + 10]; // 温度偏移 -10℃ 后索引从 0 开始 }

查表比浮点快一个数量级,而且代码体积只增加约 122 字节,非常适合 STC89C51 这种小型片内 Flash。如果嫌 1℃ 间隔不够平滑,可把间隔加密到 0.5℃,表长 121 项,仍然很小。最终主循环里距离计算就变成ticks * get_speed_x10(temp) / 20000UL一次整数乘除,和上一章代码的公式完全一致,但已经不依赖任何浮点库。这里有个边界条件要验证:speed_table 的每一项必须满足(ticks * speed) / 20000在 2cm 到 400cm 范围内不产生 16 位溢出,实际计算中 ticks 最大约 23000,speed 最大约 3500,相乘约 8000 万,必须用 unsigned long 承接,这一点在写注释时务必标注清楚,不然移植到别的工程会踩坑。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询