STM32 智能垃圾桶项目笔记(二):超声波测距功能实现 - 指南
2025-10-01 12:35 tlnshuju 阅读(0) 评论(0) 收藏 举报本系列笔记是笔者学习 B 站 up 主 “技术探索者” STM32 系列视频所作的记录,不理解的地方推荐观看视频~
目录
- 一、前言
- 二、超声波测距功能实现(基于已有驱动)
- 2.1 定时触发方案选择:为何不用 HAL_Delay?
- 2.2 50ms 非阻塞定时触发逻辑实现
- 2.3 中断回调函数优化:添加距离计算与打印
- 2.4 预期测试步骤(模块到货后补充)
- 三、总结
一、前言
上一篇笔记我们完成了超声波模块(HC-SR04)的底层驱动开发,包括 1μs 延时函数
、Trig 触发函数
和 Echo 中断回调函数
。本次笔记的核心是 基于已有驱动实现 “实时测距” 功能—— 通过定时发送触发信号,让模块持续测距,并在串口打印测量结果。
二、超声波测距功能实现(基于已有驱动)
要实现 “实时测距”,核心需解决两个问题:如何定时发送触发信号(避免阻塞)、如何在测距完成后输出结果。下面分步骤拆解实现逻辑。
2.1 定时触发方案选择:为何不用 HAL_Delay?
上一篇已实现 SR04_Trigger()
触发函数,要让模块持续测距,需定期调用该函数。常见的定时方案有两种:HAL_Delay()
阻塞延时 和 HAL_GetTick()
非阻塞定时,二者差异如下:
方案 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
HAL_Delay() | 基于 SysTick 中断,阻塞当前程序直到延时结束 | 用法简单,无需额外变量 | 阻塞主循环,影响其他任务(如后续蓝牙数据处理) | 简单单任务、无实时性要求 |
HAL_GetTick() | 读取 SysTick 累计的毫秒数(uwTick 变量),通过时间差判断定时是否结束 | 非阻塞,不影响主循环其他逻辑 | 需额外变量记录上次触发时间,需处理 uwTick 溢出 | 多任务、实时性要求高的场景 |
选择 HAL_GetTick () 的核心原因:
智能垃圾桶后续需添加蓝牙通信、电机开盖等功能,若用 HAL_Delay(50)
阻塞 50ms,会导致蓝牙数据接收、电机控制响应延迟。而 HAL_GetTick()
仅通过时间差判断,主循环可同时处理其他逻辑,兼顾 “实时测距” 与 “功能扩展性”。
补充:HAL_GetTick()
本质是 SysTick 定时器每 1ms 触发一次中断,累计全局变量 uwTick,函数本身仅读取该变量(无阻塞),因此安全性和效率更高。
2.2 50ms 定时触发逻辑实现
2.2.1 核心思路
- 定义变量
SR04_tick
记录上次触发的毫秒数(初始化为 0); - 主循环中持续判断:当前毫秒数(
HAL_GetTick()
)与SR04_tick
的差值是否大于 50ms; - 若满足条件,更新
SR04_tick
为当前毫秒数,并调用SR04_Trigger()
发送触发信号。
2.2.2 代码实现(main.c 中)
首先需在 main.c 顶部定义全局变量(或静态变量),确保 SR04_tick
不会被主循环重置:
// 全局变量:记录超声波上次触发的毫秒数(初始化为0)
uint32_t SR04_tick = 0;
int main(void)
{
while (1)
{
// 50ms 定时触发超声波测距
if (HAL_GetTick() - SR04_tick > 50) // 时间差 >50ms,触发一次测距
{
SR04_tick = HAL_GetTick(); // 更新上次触发时间,避免重复触发
SR04_Trigger(); // 发送 10μs 触发信号,启动测距
}
}
}
2.2.3 关键细节说明
- 为何选择 50ms 间隔:HC-SR04 模块的最小测距周期约为 20ms(需等待前一次回声信号完全结束),50ms 间隔既能保证 “实时性”(每秒更新 20 次距离),又能避免 “前次回声未结束,下次触发导致数据紊乱”。
- uwTick 溢出处理:
HAL_GetTick()
的 uwTick 是 32 位变量,最大值为 4294967295ms(约 49.7 天),溢出后会自动归零。若项目需长期运行,可添加溢出判断(如if (HAL_GetTick() < SR04_tick)
则认为溢出,直接更新SR04_tick
),代码如下:
if ((HAL_GetTick() - SR04_tick > 50) || (HAL_GetTick() < SR04_tick))
{
SR04_tick = HAL_GetTick();
SR04_Trigger();
}
2.3 中断回调函数优化:添加距离计算与打印
上一篇的中断回调函数仅完成距离计算,本次需在 “测距完成后”(Echo 下降沿)打印距离,方便通过串口观察结果。
2.3.1 优化后的中断回调函数(driver_sr04.c 中)
// 全局变量:存储测量距离(单位:cm),供外部读取(如蓝牙发送)
uint32_t distance_cm = 0;
/**
* @brief GPIO 中断回调函数(处理 Echo 引脚电平变化)
* @param GPIO_Pin:触发中断的引脚(此处为 PA8)
* @retval 无
* @note 上升沿:启动 TIM4 计数;下降沿:停止计数、计算距离、打印结果
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint32_t count = 0; // 静态变量:暂存 TIM4 计数值(避免每次重置)
// 仅处理 Echo 引脚(PA8)的中断
if (GPIO_Pin == GPIO_PIN_8)
{
// 1. Echo 上升沿:超声波开始返回,启动 TIM4 计数
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == GPIO_PIN_SET)
{
HAL_TIM_Base_Start(&htim4); // 启动 TIM4(1μs 计数)
__HAL_TIM_SET_COUNTER(&htim4, 0); // 清零计数值,确保计时准确
}
// 2. Echo 下降沿:超声波返回结束,计算距离并打印
else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == GPIO_PIN_RESET)
{
HAL_TIM_Base_Stop(&htim4); // 停止 TIM4 计数
count = __HAL_TIM_GetCounter(&htim4); // 读取总计时(单位:μs)
// 距离计算:count(μs) × 声速(340m/s) / 2(往返距离) → 转换为 cm
distance_cm = count * 340 / 2 * 0.000001 * 100;
count = 0; // 重置计数值,准备下次测量
// 串口打印距离(格式:distance : XX cm,便于调试工具识别)
printf("distance : %u cm\r\n", distance_cm);
}
}
}
2.3.2 关键细节说明
- 静态变量 count:用
static
修饰确保每次中断回调时不会重置,避免因变量重新初始化导致的计数错误。 - 打印格式添加
\r\n
:部分串口调试工具(如 SecureCRT、SSCOM)需\r\n
才能换行,若仅用\n
可能导致输出错乱,统一添加确保兼容性。 - 距离计算公式验证:
count
单位为 μs(10⁻⁶ 秒),声速 340m/s = 34000cm/s,因此公式可简化为:distance_cm = count * 34000 / 2 / 1000000 = count * 0.017
,与原公式结果一致,可根据习惯选择简化版。
2.4 预期测试步骤(模块到货后补充)
给 STM32 上电,串口工具每隔 50ms 收到一行数据,如图所示:
用手或物体靠近超声波模块(2cm~400cm 范围内),观察打印的
distance
值是否随距离减小而减小,远离时增大,误差应在 ±0.3cm 内。
三、总结
本次笔记基于上一篇的超声波底层驱动,实现了 “非阻塞定时测距” 与 “串口距离打印”,核心收获包括:
- 掌握
HAL_GetTick()
非阻塞定时的原理与应用,理解其相较于HAL_Delay()
的优势; - 优化中断回调函数,实现 “测距完成即打印”,形成完整的调试链路;
- 明确后续测试步骤与问题排查方向,为模块到货后的验证做好准备。
下一篇将开启蓝牙模块的学习,逐步实现 “无线数据传输” 功能,推动智能垃圾桶项目向 “无线控制” 迈进。请关注 Hello_Embed,后续笔记将持续更新!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923851.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!