☰
Hello,World
2026/10/13 22:58:38 网站建设 项目流程

基于 STM32L431 的 HC-SR04 超声波与 ST188 红外光电传感器实验总结

文章目录

  • 基于 STM32L431 的 HC-SR04 超声波与 ST188 红外光电传感器实验总结
    • 一、前言
    • 二、HC-SR04 超声波测距实验
      • 2.1 HC-SR04 工作原理
    • 三、超声波距离公式推导
    • 四、HC-SR04 程序实现过程
    • 五、HC-SR04 实验中的重要认识
    • 六、HC-SR04 实验中容易出现的问题
    • 七、ST188 红外光电传感器实验
    • 八、ST188 GPIO 初始化
    • 九、读取 ST188 状态
    • 十、ST118_Handle() 处理函数
    • 十一、HAL_GetTick() 和 HAL_Delay() 的区别
    • 十二、两个实验中发现的 STM32 程序规律
    • 十三、输入、处理、输出思想
    • 十四、目前常用的 STM32 HAL 函数总结
      • 1. HAL_Init()
      • 2. HAL_GPIO_Init()
      • 3. HAL_GPIO_ReadPin()
      • 4. HAL_GPIO_WritePin()
      • 5. HAL_GetTick()
      • 6. HAL_Delay()
      • 7. printf()
    • 十五、串口调试的重要性
    • 十六、STM32 调试时总结的排查规律
    • 十七、不要完全相信代码注释
    • 十八、轮询、中断和定时器的理解
    • 十九、HC-SR04 的进一步改进
    • 二十、ST188 的进一步扩展
    • 二十一、实验心得
    • 二十二、对 STM32 学习规律的理解
    • 二十三、两个实验的共同程序模型
      • HC-SR04
      • ST188
    • 二十四、总结

一、前言

本次实验主要完成了两个传感器实验:

  1. HC-SR04 超声波测距传感器实验
  2. ST188 红外光电传感器实验

虽然两个传感器的工作方式不同,但是完成实验以后可以发现,它们在 STM32 程序设计中存在非常明显的共同规律:

传感器产生信号 → STM32 采集信号 → 程序进行处理 → LCD、串口、LED、蜂鸣器等输出结果。

HC-SR04 更偏向于“时间测量 + 物理量计算”,而 ST188 更偏向于“数字状态读取 + 条件判断”。

通过这两个实验,我对 STM32 的 GPIO、定时器、HAL 库、串口调试以及传感器程序的分层设计有了更加清晰的认识。


二、HC-SR04 超声波测距实验

2.1 HC-SR04 工作原理

HC-SR04 是一种常见的超声波测距模块。

它的基本工作流程为:

STM32 ↓ TRIG 发送触发信号 ↓ HC-SR04 发射超声波 ↓ 超声波碰到物体 ↓ 产生反射 ↓ HC-SR04 接收到回波 ↓ ECHO 输出高电平脉冲 ↓ STM32 测量高电平持续时间 ↓ 根据时间计算距离

HC-SR04 并不会直接向 STM32 输出距离。

STM32 真正获得的是:

超声波从发射到反射回来所经历的时间。

因此程序首先要测出 ECHO 高电平脉冲的持续时间,然后利用声速和传播时间计算距离。


三、超声波距离公式推导

设:

  • 被测物体与超声波模块之间的距离为d
  • 空气中的声速为v
  • 超声波往返传播时间为t

超声波实际传播过程为:

传感器 → 被测物体 → 传感器

因此超声波实际传播的总路程为:

2 d 2d2d

根据速度公式:

s = v t s=vts=vt

因此有:

2 d = v t 2d=vt2d=vt

所以:

d = v t 2 d=\frac{vt}{2}d=2vt​

在室温约 20℃ 的情况下,空气中的声速大约为:

v = 343 m / s v=343m/sv=343m/s

将单位换算为厘米:

343 m / s = 34300 c m / s 343m/s=34300cm/s343m/s=34300cm/s

又因为:

1 s = 1000000 μ s 1s=1000000\mu s1s=1000000μs

所以:

v = 0.0343 c m / μ s v=0.0343cm/\mu sv=0.0343cm/μs

代入距离公式:

d = 0.0343 t 2 d=\frac{0.0343t}{2}d=20.0343t​

得到:

d = 0.01715 t d=0.01715td=0.01715t

也可以写成:

d ( c m ) ≈ t ( μ s ) 58.3 d(cm)\approx \frac{t(\mu s)}{58.3}d(cm)≈58.3t(μs)​

因此在程序中经常可以看到下面两种写法:

distance=time_us/58.3;

或者:

distance=time_us*0.0343/2;

两种方式本质上完全相同。

例如,当 ECHO 高电平持续时间为:

1166 μs

则距离为:

d = 1166 58.3 d=\frac{1166}{58.3}d=58.31166​

约为:

20 c m 20cm20cm


四、HC-SR04 程序实现过程

HC-SR04 测距的第一步是让 STM32 向 TRIG 引脚发送一个触发脉冲。

核心代码类似:

HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);delay_us(2);HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);delay_us(10);HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);

其中需要注意:

TRIG 高电平持续时间需要不小于 10 μs

HC-SR04 收到触发信号以后开始发射超声波。

接下来程序等待 ECHO 引脚变成高电平。

while(HAL_GPIO_ReadPin(ECHO_GPIO_Port,ECHO_Pin)==GPIO_PIN_RESET){}

当 ECHO 变为高电平以后开始计时。

然后继续等待 ECHO 变为低电平。

while(HAL_GPIO_ReadPin(ECHO_GPIO_Port,ECHO_Pin)==GPIO_PIN_SET){}

最后获得 ECHO 高电平持续时间:

t_us=timer_us_get();

再计算距离:

distance=t_us*0.0343f/2.0f;

完整过程可以概括为:

产生 TRIG 触发脉冲 ↓ 等待 ECHO 上升沿 ↓ 开始计时 ↓ 等待 ECHO 下降沿 ↓ 停止计时 ↓ 得到超声波往返时间 ↓ 利用 d = vt / 2 ↓ 计算距离 ↓ LCD / 串口显示

五、HC-SR04 实验中的重要认识

通过 HC-SR04 实验,我认识到一个很重要的问题:

传感器输出的数据,不一定就是最终需要的物理量。

HC-SR04 输出的是:

时间

而我们真正想得到的是:

距离

因此中间需要进行转换:

ECHO 脉冲 ↓ 时间 ↓ 物理公式 ↓ 单位换算 ↓ 距离

其实很多传感器都是类似的。

例如温度传感器可能是:

电压 → 温度

光敏传感器可能是:

ADC 数值 → 光照强弱

土壤湿度传感器可能是:

ADC 数值 → 土壤湿润程度

压力传感器可能是:

电压 → 压力

因此学习传感器时,不能只会调用函数读取数据,还必须知道:

STM32 读取到的原始信号到底代表什么。


六、HC-SR04 实验中容易出现的问题

HC-SR04 实验中最容易出现的问题之一是计时。

程序可能认为:

1 个计数 = 1 μs

但是如果 STM32 的系统时钟或者定时器分频配置错误,那么实际一个计数可能并不是 1 μs。

这时最终距离会出现明显误差。

因此遇到测距结果明显偏大或者偏小时,可以按照下面的顺序检查:

系统时钟 ↓ 定时器时钟 ↓ Prescaler 分频 ↓ 计数频率 ↓ 一个计数对应多少时间

另一个需要注意的问题是电平。

HC-SR04 通常使用 5V 供电,ECHO 输出电平可能接近 5V。

而很多 STM32 GPIO 工作电压为 3.3V,因此连接时应该确认对应 GPIO 是否能够承受该电平。

必要时可以增加:

分压电路

或者:

电平转换电路

此外,超声波测距还会受到目标物体角度、材质、环境温度以及其他反射物体的影响。


七、ST188 红外光电传感器实验

ST188 属于反射式红外光电传感器。

其基本原理为:

红外发射管 ↓ 发射红外光 ↓ 目标物体 ↓ 反射红外光 ↓ 光电接收器 ↓ 电信号 ↓ 比较 / 整形 ↓ 数字高低电平 ↓ STM32 GPIO

因此,与 HC-SR04 相比,ST188 的处理过程更加简单。

STM32 不需要测量传播时间,只需要判断传感器输出是:

0

还是:

1

然后根据结果进行相应处理。

本次实验工程中,ST188 实际连接在 STM32L431 的:

PD14

也就是:

GPIOD GPIO_PIN_14

八、ST188 GPIO 初始化

ST188 需要把对应 GPIO 配置为输入模式。

代码如下:

#defineST118_PinGPIO_PIN_14#defineST118_GPIO_PortGPIODvoidST118_Init(void){GPIO_InitTypeDef GPIO_InitStruct;__HAL_RCC_GPIOD_CLK_ENABLE();GPIO_InitStruct.Pin=ST118_Pin;GPIO_InitStruct.Mode=GPIO_MODE_INPUT;GPIO_InitStruct.Pull=GPIO_NOPULL;HAL_GPIO_Init(ST118_GPIO_Port,&GPIO_InitStruct);}

通过这段代码,可以总结出 STM32 GPIO 初始化的基本规律:

开启 GPIO 时钟 ↓ 选择 GPIO 引脚 ↓ 设置 GPIO 模式 ↓ 设置上下拉 ↓ HAL_GPIO_Init()

例如普通输入可以使用:

GPIO_MODE_INPUT

普通推挽输出:

GPIO_MODE_OUTPUT_PP

如果要使用外部中断,则可能使用:

GPIO_MODE_IT_RISING

或者:

GPIO_MODE_IT_FALLING

因此 STM32 的很多 GPIO 程序其实具有非常相似的结构。


九、读取 ST188 状态

ST188 状态读取函数比较简单:

uint8_tST118_Read_State(void){returnHAL_GPIO_ReadPin(ST118_GPIO_Port,ST118_Pin);}

真正完成输入读取的函数就是:

HAL_GPIO_ReadPin();

这个实验中比较值得学习的是:

程序没有把初始化、读取、判断、显示和蜂鸣器控制全部写在 main() 中。

而是进行了分层。

ST118_Init() ↓ 负责初始化 ST118_Read_State() ↓ 负责读取传感器状态 ST118_Handle() ↓ 负责状态判断和输出处理

这样的程序结构比把所有代码都写在 main() 中更加清晰。


十、ST118_Handle() 处理函数

主程序中可以不断调用:

while(1){ST118_Handle();}

具体的状态读取和处理放在:

ST118_Handle();

中完成。

核心逻辑类似:

voidST118_Handle(void){uint8_tst118_state;staticuint32_ttickstart;if((HAL_GetTick()-tickstart)>=1000){tickstart=HAL_GetTick();st118_state=ST118_Read_State();if(st118_state==1){beep_open();}else{beep_close();}}}

这里使用了:

HAL_GetTick();

HAL_GetTick()可以获得 STM32 从系统启动到当前时刻所经过的大致毫秒数。

因此:

HAL_GetTick()-tickstart>=1000

表示:

距离上一次执行已经过去约 1000 ms

也就是:

1 秒

十一、HAL_GetTick() 和 HAL_Delay() 的区别

以前写程序时最容易想到的是:

HAL_Delay(1000);

它确实可以延时 1 秒。

但是它有一个问题:

程序在这 1 秒期间基本处于等待状态

例如:

while(1){ST118_Read_State();HAL_Delay(1000);}

程序执行到:

HAL_Delay(1000);

以后就会等待。

而使用:

HAL_GetTick();

则可以写成:

if(HAL_GetTick()-tickstart>=1000){tickstart=HAL_GetTick();ST118_Read_State();}

这样程序不会长时间阻塞。

主循环还可以同时处理:

LCD 刷新 串口通信 其他传感器 按键 LED 网络通信

因此这种写法属于一种比较基础的:

非阻塞定时思想。

这也是以后学习状态机、FreeRTOS 等内容的重要基础。


十二、两个实验中发现的 STM32 程序规律

完成这两个实验以后,我发现 STM32 传感器程序通常可以抽象为:

main() │ ├─ HAL_Init() │ ├─ SystemClock_Config() │ ├─ GPIO 初始化 │ ├─ UART 初始化 │ ├─ LCD 初始化 │ ├─ Sensor_Init() │ └─ while(1) │ └─ Sensor_Handle() │ ├─ 获取传感器数据 ├─ 数据处理 ├─ 状态判断 └─ 显示 / 报警 / 通信

因此以后拿到一个新的传感器工程,我不会再从几百个文件开始看。

而是首先寻找三个内容:

Sensor_Init()
Sensor_Read()
Sensor_Handle()

也就是:

初始化 ↓ 读取 ↓ 处理

通过这三个函数通常就可以比较快地理解整个传感器程序。


十三、输入、处理、输出思想

两个实验虽然传感器完全不同,但是都符合:

输入 ↓ 处理 ↓ 输出

HC-SR04:

ECHO 脉冲 ↓ 测量时间 ↓ d = vt / 2 ↓ 得到距离 ↓ LCD / UART

ST188:

红外反射 ↓ 数字电平 ↓ GPIO 读取 ↓ if 判断 ↓ LCD / 蜂鸣器 / UART

以后即使学习其他传感器,例如:

温度传感器 湿度传感器 光照传感器 土壤湿度传感器 气体传感器 RFID

整体程序思路通常依然是:

初始化 ↓ 采集 ↓ 处理 ↓ 输出

十四、目前常用的 STM32 HAL 函数总结

通过这两个实验,目前经常使用的 HAL 函数主要有以下几个。

1. HAL_Init()

HAL_Init();

用于初始化 HAL 库以及系统相关功能。


2. HAL_GPIO_Init()

HAL_GPIO_Init(GPIOx,&GPIO_InitStruct);

用于配置 GPIO。


3. HAL_GPIO_ReadPin()

HAL_GPIO_ReadPin(GPIOx,GPIO_PIN_x);

用于读取 GPIO 当前高低电平。

例如 ST188:

HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_14);

4. HAL_GPIO_WritePin()

HAL_GPIO_WritePin(GPIOx,GPIO_PIN_x,GPIO_PIN_SET);

或者:

HAL_GPIO_WritePin(GPIOx,GPIO_PIN_x,GPIO_PIN_RESET);

用于控制 GPIO 输出高低电平。

HC-SR04 的 TRIG 就需要使用这个函数。


5. HAL_GetTick()

HAL_GetTick();

获取系统运行时间,单位通常为毫秒。

适合实现:

周期任务 非阻塞延时 简单软件定时

6. HAL_Delay()

HAL_Delay(1000);

用于阻塞式延时。

例如:

HAL_Delay(1000);

就是延时约 1 秒。


7. printf()

printf("state = %d\r\n",state);

或者:

printf("distance = %.2f cm\r\n",distance);

虽然printf()并不是 HAL 专用函数,但是在 STM32 实验中非常常用。

一般可以通过串口重定向,将输出发送到电脑串口助手。


十五、串口调试的重要性

通过实验发现,串口是 STM32 调试中非常重要的工具。

例如:

printf("state = %d\r\n",state);

可以观察 ST188 当前状态。

而:

printf("distance = %.2f cm\r\n",distance);

则可以观察 HC-SR04 当前距离。

假设 LCD 没有正确显示,但是串口能够正常输出:

distance = 20.03 cm

那么可以判断:

传感器读取正常 距离计算正常

问题更可能出现在:

LCD 显示程序

这样就可以快速缩小故障范围。

因此我认为串口调试最大的价值就是:

可以看到程序内部正在发生什么。


十六、STM32 调试时总结的排查规律

刚开始做 STM32 实验时,如果传感器没有工作,很容易首先怀疑:

代码是不是写错了?

但是实际上问题可能来自多个方面。

现在我更倾向于按照以下顺序排查:

电源是否正常 ↓ GND 是否共地 ↓ 传感器引脚是否接对 ↓ GPIO 端口是否正确 ↓ GPIO 模式是否正确 ↓ 传感器是否真的产生输出 ↓ STM32 是否读取到信号 ↓ 数据处理是否正确 ↓ LCD / 串口是否正确输出

这种方式比直接修改代码更加有效。


十七、不要完全相信代码注释

ST188 实验中有一个很值得注意的问题。

工程中实验名称使用:

ST188

但是部分函数和文件仍然采用:

ST118

例如:

ST118_Init();
ST118_Read_State();
ST118_Handle();

另外旧代码注释中可能出现其他 GPIO 描述,但是实际宏定义使用:

#defineST118_PinGPIO_PIN_14#defineST118_GPIO_PortGPIOD

因此真正运行的是:

PD14

这让我认识到:

注释只是辅助说明,最终应该相信真正执行的代码、宏定义、原理图以及实际测试结果。

调试 STM32 时应该重点检查:

#define 宏定义 GPIO 初始化代码 函数调用关系 实际硬件接线 实际运行现象

十八、轮询、中断和定时器的理解

ST188 当前实验主要采用的是:

周期轮询

也就是隔一段时间执行:

ST118_Read_State();

这种方式的优点是:

程序简单 容易理解 方便调试

但是如果以后需要更快响应,可以使用:

EXTI 外部中断

例如当 ST188 状态发生:

0 → 1

或者:

1 → 0

时直接触发中断。

这样 STM32 不需要一直询问传感器状态。

HC-SR04 也可以进一步优化。

目前常见程序可能采用:

软件等待 ECHO

更进一步可以使用:

定时器输入捕获

分别捕获:

ECHO 上升沿

和:

ECHO 下降沿

通过硬件直接计算脉冲宽度。

这样可以减少 CPU 占用,并提高时间测量精度。


十九、HC-SR04 的进一步改进

HC-SR04 可以进一步增加温度补偿。

空气中的声速会受到温度影响。

近似公式为:

v = 331.3 + 0.606 T v=331.3+0.606Tv=331.3+0.606T

其中:

T = 环境温度

单位为:

℃

因此距离公式可以写成:

d = v t 2 d=\frac{vt}{2}d=2vt​

即:

d = ( 331.3 + 0.606 T ) t 2 d=\frac{(331.3+0.606T)t}{2}d=2(331.3+0.606T)t​

实际系统中可以增加:

DS18B20

或者:

SHT31

读取环境温度,然后动态修正声速。

除此之外还可以加入:

中值滤波 多次平均 定时器输入捕获 距离报警 非阻塞状态机 物联网数据上传

二十、ST188 的进一步扩展

ST188 虽然只能输出简单的有无遮挡状态,但是通过增加程序和传感器数量,可以实现很多功能。

例如可以使用两个 ST188。

设两个传感器之间距离为:

L LL

物体通过两个传感器的时间差为:

Δ t \Delta tΔt

那么物体速度可以近似计算为:

v = L Δ t v=\frac{L}{\Delta t}v=ΔtL​

因此可以进一步实现:

物体计数 车辆检测 简单测速 方向判断 闯红灯检测 流水线物料检测

如果再连接 Wi-Fi 或 NB-IoT 模块,还可以实现:

ST188 ↓ STM32 ↓ 串口 ↓ Wi-Fi / NB-IoT ↓ 服务器 ↓ 网页 / 手机端

这样就可以从一个简单的传感器实验扩展成一个物联网系统。


二十一、实验心得

通过这两个实验,我最大的收获并不是记住了某一个函数或者某一段代码,而是逐渐理解了 STM32 控制传感器的整体思路。

以前第一次打开 STM32 工程时,看到:

main.c gpio.c usart.c sensor.c handle.c stm32l4xx_hal_xxx.c

会感觉文件很多,不知道应该先看哪个。

现在我一般会先找到:

main()

看看整个系统初始化了哪些模块。

然后再寻找:

Sensor_Init()

了解传感器怎样初始化。

接着寻找:

Sensor_Read()

看看 STM32 怎样获得传感器数据。

最后寻找:

Sensor_Handle()

了解程序怎样处理数据并控制其他设备。

这种阅读程序的方法明显比从头到尾逐行看代码效率高很多。


二十二、对 STM32 学习规律的理解

通过实验,我发现学习 STM32 不能只背 HAL 函数。

真正重要的是理解:

硬件是什么 ↓ 产生什么信号 ↓ STM32 使用哪个外设采集 ↓ 程序怎样处理 ↓ 最后怎样输出

例如 HC-SR04:

超声波 ↓ ECHO 脉冲宽度 ↓ GPIO + 定时器 ↓ 距离公式 ↓ 距离

ST188:

红外反射 ↓ 数字高低电平 ↓ GPIO ↓ if 判断 ↓ 有无遮挡

以后学习 I2C 传感器时可能变成:

传感器 ↓ I2C ↓ 读取寄存器 ↓ 原始数据 ↓ 数据换算 ↓ 物理量

SPI 传感器也是类似。

所以学习 STM32 的核心并不是:

每一种传感器都重新学习一套程序。

而应该理解:

不同传感器只是在输入方式、通信方式和数据处理方式上有所不同。


二十三、两个实验的共同程序模型

最后可以把两个实验统一抽象为:

硬件初始化 ↓ 传感器初始化 ↓ 获取原始信号 ↓ 数据处理 ↓ 状态 / 数值判断 ↓ LCD / UART / LED / 蜂鸣器

其中:

HC-SR04

输入: ECHO 脉冲 处理: 测量时间 计算距离 输出: LCD / 串口

ST188

输入: GPIO 高低电平 处理: 判断有无遮挡 输出: LCD / 蜂鸣器 / 串口 / LED

二十四、总结

HC-SR04 和 ST188 虽然属于完全不同的两种传感器,但是通过这两个实验可以比较系统地认识 STM32 传感器开发的基本方法。

HC-SR04 让我进一步掌握了:

GPIO 输出 GPIO 输入 微秒级延时 定时测量 超声波测距 物理公式换算

ST188 则让我进一步掌握了:

GPIO 输入 状态判断 HAL_GetTick() 非阻塞定时 驱动层和业务层分离 LCD / 蜂鸣器 / 串口联动

通过实验我逐渐认识到:

STM32 学习最重要的不是死记代码,而是理解硬件信号与软件处理之间的关系。

以后面对一个新的传感器,可以首先思考:

它输出的是什么? ↓ STM32 应该使用什么外设读取? ↓ 读到的原始数据代表什么? ↓ 需要怎样换算或判断? ↓ 结果应该如何显示或上传?

只要把这条思路理清楚,大多数传感器实验都可以按照:

初始化 → 采集 → 处理 → 输出

的规律进行分析和编程。

这也是这两次 STM32 传感器实验给我最大的收获。@TOC

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

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

立即咨询