学习STC51单片机13(芯片为STC89C52RC)

我去,兄弟们我们今天来学习一个牛逼 的硬件,它叫超声波测距模块HC---SR04

硬件:HC---SR04

哎,想当初最想要玩的就是这个模块,科技感十足,那现在就让我们玩玩吧

超声波测距传感器

原理就是说需要给Trig 10us的高电平,相当于是一个信号,到模块内部,模块内部有脉冲模块一直向着外部发送超声波,此时echo由低电平变成了高电平,然后如果有障碍物了,那么返回到echo里面会由高电平转换为低电平,计算的就是echo接收的第一个波到最后一个波,由高电平到最后一个波结束变成低电平的时间

因为你可以拿 0001(1) 0010(2) 0100(3) 1000(4) 这个几个二进制数从1转换到4,你一个个转换会发现就是有这个规律 :二进制左移一位相当于x2的一次方 左移8位相当于左移2的8次方

超声波的时序图:(这也是我们第一次接触时序图)

这个是我们的最终代码,其中有很多的优化细节 都是为了测试硬件和提高稳定性的,所以有些代码不是功能的实现,而是细节的提升

#include "reg52.h"

#include <intrins.h> // 包含_nop_()函数

sbit D5 = P3^7; // 黄灯(距离<10cm时亮)

sbit D6 = P3^6; // 蓝灯(距离≥10cm时亮)

sbit Trig = P1^5; // 超声波触发引脚

sbit Echo = P1^6; // 超声波接收引脚

// 精确延时函数

void Delay10us() //@11.0592MHz

{

nop(); nop(); nop(); nop(); nop();

nop(); nop(); nop(); nop(); nop();

}

void Delay50ms() //@11.0592MHz

{

unsigned char i, j;

i = 90;

j = 163;

do

{

while (--j);

} while (--i);

}

void Delay100ms() //@11.0592MHz

{

unsigned char i, j;

i = 183;

j = 105;

do

{

while (--j);

} while (--i);

}

// 定时器0初始化(16位模式)

void Time0_Init()

{

TMOD &= 0xF0; // 清零T0模式位

TMOD |= 0x01; // 设置T0为模式1(16位定时器)

TH0 = 0; // 初始化计数寄存器

TL0 = 0;

TR0 = 0; // 初始不启动定时器

}

// 触发超声波测距

void Trigger_HC_SR04()

{

Trig = 0;

nop();

Trig = 1; // 发送至少10μs的高电平触发信号

Delay10us();

Trig = 0;

}

// 主函数

void main()

{

unsigned int Time; // 超声波飞行时间(μs)

unsigned int Distance; // 距离(cm)

unsigned int timeout; // 超时计数器

unsigned char i; // 定义循环变量(解决编译错误)

Time0_Init(); // 初始化定时器

// 上电自检:LED交替闪烁3次

for(i=0; i<3; i++) { // 修正后的for循环

D5 = 0; D6 = 1; // 黄灯亮

Delay100ms();

D5 = 1; D6 = 0; // 蓝灯亮

Delay100ms();

}

// 默认状态:蓝灯亮(表示距离≥10cm)

D5 = 1;

D6 = 0;

Delay100ms(); // 等待传感器稳定

while(1)

{

// 确保Echo初始为低电平(否则可能是传感器异常)

if(Echo == 1) {

D5 = 1; D6 = 1; // 双灯亮表示异常

Delay100ms();

continue;

}

// 触发测距

Trigger_HC_SR04();

// 等待Echo变高(添加超时:约10ms)

timeout = 10000;

while(Echo == 0 && --timeout);

if(timeout == 0) {

D5 = 1; D6 = 1; // 超时错误:双灯亮

Delay50ms();

continue;

}

// 启动定时器

TH0 = 0;

TL0 = 0;

TR0 = 1;

// 等待Echo变低

timeout = 65535;

while(Echo == 1 && --timeout);

TR0 = 0;

// 计算距离(仅在未超时的情况下)

if(timeout != 0) {

// 计算时间(晶振补偿:11.0592MHz需要乘以1.085)

Time = (TH0 * 256 + TL0) * 1.085;

// 计算距离(单位:cm)

Distance = Time * 17 / 1000; // 等价于Time * 0.017

// 根据距离控制LED

if(Distance < 10) { //如果 timeout 没有减到 0(即 Echo 信号在超时之前成功从高电平变回低电平)

D5 = 0; D6 = 1; // 蓝灯亮:距离大于小于10cm

} else {

D5 = 1; D6 = 0; // 黄灯亮:距离大于10cm

}

} else {

D5 = 1; D6 = 1; // 超时错误:双灯亮

}

Delay50ms(); // 测量间隔,避免频繁触发 手册规定两次间隔至少60ms(哎我这边写50也无所谓的)

}

}

当然我这个是优化过了的代码,因为之前的代码不稳定,容易实现不了功能你们可以拿去试试水

旧代码

这个代码有几点 需要重要解释的就是计算

首先时间的计算,本质就是计算echo的高电平到低电平的时间

那么我们定时器的TH0 和TL0 是发挥作用的,因为他们记录了次数,每次是1.085us,因为晶振的频率是11.059Mhz,我们定时器的模式又是16位,所以我们又要知道TH0和TL0是8位寄存器,高八位给TH0,低八位给TL0,记录的都是次数,我们将低八位让给TL0 其实就是这个效果啦

10101011 要变成1010101100000000(这个是一个二进制格式),那么我们该怎么办呢其实就是用到了这个方法

二进制左移一位相当于x2的一次方 左移8位相当于左移2的8次方

你看这个图就非常明显了,DEC是十进制这个数为65535是2的16次方,也就是16位2进制能有多少种搭配个数,那我们这个 前面8位数TH0的位置 后面8位数TL0的位置,所以我们可以根据这个信息来获取次数,我们通过二进制的数据来转换成10进制的次数,那么这个次数就可以与每次1.085us进行相乘得出总的时间

还有一个要注意的是距离的计算,有个公式叫做距离=速度 x 时间 但是由于我们这个超声波的话是这样的,时间多付出一倍,所以距离要/2的,一来一回

声音的速度为340m/s

随后根据公式就可以算出距离了。

最后我们来一些Tips:

对于第一个timeout设置为10ms是为什么?????

10ms 就是一个"兼顾大家"的折中值​ ,既不过短(避免误判),也不过长(避免卡死),属于​经验性参数​ 。它的合理性可以从这几个角度理解

1. ​ ​"大家"是谁?​

  • ​硬件层面​ :超声波模块(如 HC-SR04)的典型响应时间在 ​微秒级到几毫秒​ (例如,测距 1 米时,Echo 变高延迟约 5.8ms,但实际传感器内部电路响应可能更快)。
  • ​软件层面​ :嵌入式系统通常需要 ​快速失败​ (避免因硬件故障导致整个系统僵死)。
  • ​用户体验​ :10ms 对人类来说几乎无感(LED 报错时用户不会觉得"卡顿")。

就是Trig高电平进去之后,我们这个就要进行判断了,因为:超声波模块(如 HC-SR04)的典型响应时间在 ​微秒级到几毫秒​

所以

  • 只要 Echo 仍然为 0(低电平)​并且​ timeout 自减后仍不为 0,就继续循环。
  • 如果 Echo 变为 1(高电平)​或者​ timeout 减到 0,循环终止。

Echo 高电平的持续时间(从第一个有效回波触发变高,到最后一个回波处理完毕变低)直接对应超声波往返的时间​而这个时间可以用来计算距离

这段代码有点意思:

timeout = 65535;

while(Echo == 1 && --timeout);

TR0 = 0;

  • 如果 Echo 正常变低(测距完成),循环退出。
  • 如果 Echo 因硬件故障一直保持高电平,timeout 会递减至 0,强制退出循环(避免死等)。

​4. 为什么 timeout 初始值设为 65535?​

  • ​65535(0xFFFF)是 16 位无符号整数的最大值​ ,确保超时时间足够长(约 71ms @11.095MHz 51单片机)。
  • ​覆盖最大测距​
    • HC-SR04 最大测距约 5 米,对应 Echo 高电平时间 ≈ 29ms(580µs/cm × 500cm)。
    • 设置 timeout = 65535 可确保即使测最远距离也不会误触发超时。

5米最大测距​ = 硬件限制(Echo 最大 29ms) + 声波衰减。

3. 实例验证​

​场景1:正常测距( Echo 按预期变低)​
  • Echo 从 1 → 0(耗时 5ms),timeout 从 65535 减到 60000(未到 0)。
  • ​结果​ :timeout != 0 → 进入 if 分支,计算距离。
​场景2:超时异常( Echo 保持高电平)​
  • Echo 始终为 1,timeout 从 65535 递减至 0(耗时约 65ms)。
  • ​结果​ :timeout == 0 → 进入 else 分支,双灯亮报错。
​场景3:干扰导致 Echo 异常变低​
  • Echo 因干扰短暂变低,但 timeout 未减到 0(如剩余 10000)。
  • ​结果​ :timeout != 0 → 仍进入 if 分支,但计算的距离可能无效(需软件滤波处理)。
相关推荐
O。o.尊都假都6 分钟前
STM32之温湿度传感器(DHT11)
stm32·单片机·嵌入式硬件
阿图灵37 分钟前
文章记单词 | 第102篇(六级)
学习·学习方法
无垠的广袤2 小时前
【萤火工场GD32VW553-IOT开发板】ADC电压表
c++·单片机·嵌入式硬件·物联网
一年春又来2 小时前
AI-02a5a8.神经网络-与学习相关的技巧-超参数的验证
人工智能·神经网络·学习
MingYue_SSS2 小时前
一些较好的学习方法
经验分享·笔记·嵌入式硬件·学习·学习方法
zhangrelay2 小时前
ROS云课-一键配置-250523无错版
学习
胡耀超2 小时前
从逻辑视角学习信息论:概念框架与实践指南
学习·安全·网络安全·信息与通信·数据科学·信息论
qq_393828223 小时前
Excel多合一文件合并工具
学习·excel·软件需求
..过云雨3 小时前
结课作业自选01. 内核空间 MPU6050 体感鼠标驱动程序(二)(完整实现流程)
linux·开发语言·嵌入式硬件
茶茶敲代码3 小时前
前端vscode学习
学习