当前位置: 首页 > news >正文

STM32F103——超声波模块

一、工程整体功能概述

本工程基于STM32F103 标准库,使用通用定时器 TIM2做精准计时,驱动 HC-SR04 超声波模块,实现距离测量;连续采集 5 次距离做均值滤波,最后通过串口打印输出距离值,测量更稳定、精度更高。

核心逻辑:TIM2 配置为1ms 定时中断,配合定时器计数器微秒级读数,拼接得到 Echo 高电平总时长,利用超声波测距公式换算成实际距离。

二、硬件接线说明

  • HC-SR04 Trig → PB11(推挽输出)
  • HC-SR04 Echo → PB10(浮空输入)
  • 模块 VCC 接 5V,GND 共地

三、main.c 源码 + 详细解析

#include "stm32f10x.h" #include "main.h" #include "stdio.h" #include "led.h" #include "tim.h" #include "usart.h" // 简易软件毫秒延时 void delay(uint16_t time) { uint16_t i = 0; while(time --) { i = 12000; while(i --); } } int main() { float length = 0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); LED_Init(); Base_TIM_Init(); my_usart_init(); HC04_Init(); Get_Length(); while(1) { // 循环读取距离并串口打印 length = Get_Length(); printf("%lf\r\n",length); } }

代码解析

  1. NVIC_PriorityGroupConfig中断分组配置,必须放在main最开头,整个工程只配置一次,规范中断优先级划分。
  2. 外设初始化顺序LED、TIM2 定时器、串口、超声波引脚依次初始化,硬件初始化规范流程。
  3. 主循环逻辑不用做其他业务,循环调用Get_Length()获取距离,通过printf串口实时打印。

四、tim.c 完整源码 + 逐行超详细解析

#include "tim.h" #include "stm32f10x.h" // 记录TIM2中断次数,每进一次代表1ms uint16_t mscount = 0; // 简易微秒延时 void delay_us(uint32_t us) { us *= 8; while(us--); } // 简易毫秒延时 void delay_ms(uint32_t ms) { while(ms--) { delay_us(1000); } } // TIM2 初始化:配置为1ms更新中断 void Base_TIM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeInitStruct; NVIC_InitTypeDef NVIC_InitStruct; // 1. 开启APB1总线TIM2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 2. 定时器基础参数配置 TIM_TimeInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频 TIM_TimeInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数 TIM_TimeInitStruct.TIM_Period = 1000-1; // 自动重装载值 TIM_TimeInitStruct.TIM_Prescaler = 72-1; // 预分频器 TIM_TimeInitStruct.TIM_RepetitionCounter = 0; // 通用定时器固定填0 // 3. 初始化TIM2、开启更新中断、默认先关闭定时器 TIM_TimeBaseInit(TIM2, &TIM_TimeInitStruct); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, DISABLE); // 4. NVIC中断优先级配置 NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStruct); } // HC-SR04 引脚初始化 void HC04_Init() { GPIO_InitTypeDef GPIO_InitStruction; // 开启GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Trig PB11 推挽输出 GPIO_InitStruction.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruction.GPIO_Pin = GPIO_Pin_11; GPIO_InitStruction.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStruction); // Echo PB10 浮空输入 GPIO_InitStruction.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStruction.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOB, &GPIO_InitStruction); } // 开启定时器:清零计数器与中断计数 void Open_TIM(void) { TIM_SetCounter(TIM2, 0); mscount = 0; TIM_Cmd(TIM2, ENABLE); } // 关闭定时器 void Close_TIM(void) { TIM_Cmd(TIM2, DISABLE); } // TIM2中断服务函数 1ms进一次 void TIM2_IRQHandler(void) { if( TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET ) { mscount++; // 清除中断标志位,必须写 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } // 获取Echo高电平总微秒时间 int Get_Echo_Time(void) { uint16_t t = 0; // 毫秒数转微秒 + 当前计数器微秒值 t = mscount * 1000; t += TIM_GetCounter(TIM2); TIM2->CNT = 0; delay_ms(50); return t; } // 采集5次距离取平均值,提高稳定性 float Get_Length(void) { uint16_t t = 0; float length = 0; float sum = 0; int i = 0; while(i != 5) { // 给Trig发送20us高电平触发信号 GPIO_SetBits(GPIOB, GPIO_Pin_11); delay_us(20); GPIO_ResetBits(GPIOB, GPIO_Pin_11); // 等待Echo拉高 while( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 0); Open_TIM(); i++; // 等待Echo拉低 while( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 1); Close_TIM(); // 读取总高电平时间 t = Get_Echo_Time(); // 超声波测距公式:距离(cm) = 高电平时间(us) / 58 length = ((float)t / 58.0); sum = sum + length; } // 5次均值滤波 length = sum / 5.0; return length; }

五、tim.h 头文件

#ifndef TIM_H_ #define TIM_H_ void Base_TIM_Init(void); void HC04_Init(void); void Open_TIM(void); void Close_TIM(void); int Get_Echo_Time(void); float Get_Length(void); #endif

作用:对外声明定时器、超声波相关函数,供 main.c 等外部文件调用。

六、定时器定时原理与时间计算

1. 定时器时钟

STM32F103 主频 72MHz,TIM2 挂载在 APB1 总线,定时器时钟为 72MHz。

2. 配置参数

  • 预分频:72-1
  • 自动重装载:1000-1

3. 计算公式

定时周期分频重装载定时器时钟频率T=7200000072×1000​=0.001s=1ms结论:TIM2 每 1ms 触发一次更新中断

4. 计时思路

  • 全局变量mscount记录中断次数,代表毫秒
  • TIM_GetCounter(TIM2)读取当前计数器值,代表微秒
  • 两者拼接,得到高精度微秒级总时长

七、HC-SR04 工作原理

  1. 主控给Trig 引脚发送20us 高电平触发脉冲
  2. 模块自动发射 8 路 40KHz 超声波;
  3. 遇到障碍物反射,模块Echo 引脚拉高;
  4. 主控检测 Echo 高电平时长,用公式换算距离:距离高电平时间

八、整体程序运行流程

  1. 上电先配置中断分组,依次初始化 LED、TIM2、串口、超声波引脚;
  2. 进入死循环,反复调用Get_Length()
  3. 单次测量:发触发信号 → 等 Echo 拉高 → 开定时器 → 等 Echo 拉低 → 关定时器;
  4. 计算高电平总微秒时间,换算成距离;
  5. 连续测 5 次取平均值,串口打印输出;
  6. 循环往复,实时测距。

九、代码优点与可移植性

  1. 定时器配置标准规范,可直接移植到 TIM3/TIM4;
  2. 采用1ms 中断 + 计数器微秒读数,计时精度高;
  3. 5 次均值滤波,解决超声波测距跳变、数据不稳定问题;
  4. 分层清晰:定时器底层、超声波驱动、业务应用完全分离;
  5. 标准库写法,兼容所有 STM32F103 系列单片机;
  6. 自带软件微秒 / 毫秒延时,无需依赖其他延时函数。
http://www.jsqmd.com/news/761284/

相关文章:

  • 在Node.js后端服务中集成Taotoken调用多模型AI功能的实践
  • 如何用Pipenv简化生物信息学项目配置:基因数据分析的完整指南
  • 终极Wireshark网络嗅探工具:如何在Docker容器中快速构建完整代码质量分析环境
  • 基于Next.js构建私有ChatGPT Web应用:从部署到安全加固全指南
  • PHP调用AI模型做表单校验太慢?3步压测优化,TPS从23提升至847(附性能对比热力图)
  • SimpleMem内存池:C++高性能内存管理库的设计与实战
  • Modern JavaScript Cheatsheet包管理终极指南:npm和yarn最佳实践
  • EasyML自定义算法开发:如何扩展平台支持新的机器学习算法
  • 7个终极NW.js应用市场推广技巧:从开发到爆发式增长的完整指南
  • 替代claude code安装实战:基于快马平台开发全功能个人博客系统
  • 终极指南:CookieCutter缓存机制如何实现项目模板重复生成的极速加速
  • 基于WebView的ChatGPT桌面客户端开发:从原理到实践
  • 为什么你的Windows电脑越用越慢?3个简单步骤让Mem Reduct帮你解决内存管理难题
  • 错误日志爆炸?性能骤降37%?PHP 8.9精准管控四步法,上线前必须验证的7项配置清单
  • QT界面美化实战:用QSS给QTabWidget和QTabBar做个“换肤手术”(附完整代码)
  • 分饭机生产厂家突围:下沉渠道布局策略深度解析
  • 令R为所有实数的集合,定义标量乘法为ax=a.x 定义加法记作 圆圈包含+ 为 x圆圈包含+ =max(x,y) R连同这些运算是否构成向量空间?证明你的结论?
  • 三步轻松退出Windows预览体验计划:离线脚本解决方案
  • 开源工具包xpkit-openclaw:模块化脚本集合提升开发运维效率
  • CmBacktrace入门指南:ARM Cortex-M错误追踪库的完整介绍
  • 电气考研复试现场实录:从电机学到项目经验,我是如何用‘STAR法则’让面试官频频点头的
  • 开发者技能认证系统skillsauth:从架构设计到部署运维全解析
  • tabula-java源码剖析:从文本元素到完整表格的智能转换
  • 如何在CodeCombat编程竞赛中快速提升学习动力:终极指南
  • Cmajor语言:为实时音频与图形处理设计的高性能DSL
  • fx_cast守护进程配置:WebSocket服务器与远程连接高级用法
  • 如何快速构建高可用Redis集群:Jeecg-Boot主从复制与哨兵模式完整指南
  • Solargraph性能优化:10个提升语言服务器响应速度的关键技巧
  • 自制机器学习:掌握Sigmoid激活函数的核心原理与实战应用指南
  • OBASE技术:对象热度感知的内存分页优化实践