一、工程整体功能概述
本工程基于 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);
}
}
代码解析
NVIC_PriorityGroupConfig中断分组配置,必须放在main最开头,整个工程只配置一次,规范中断优先级划分。- 外设初始化顺序LED、TIM2 定时器、串口、超声波引脚依次初始化,硬件初始化规范流程。
- 主循环逻辑不用做其他业务,循环调用
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 工作原理
- 主控给 Trig 引脚 发送 20us 高电平触发脉冲;
- 模块自动发射 8 路 40KHz 超声波;
- 遇到障碍物反射,模块 Echo 引脚 拉高;
- 主控检测 Echo 高电平时长,用公式换算距离:距离高电平时间
八、整体程序运行流程
- 上电先配置中断分组,依次初始化 LED、TIM2、串口、超声波引脚;
- 进入死循环,反复调用
Get_Length(); - 单次测量:发触发信号 → 等 Echo 拉高 → 开定时器 → 等 Echo 拉低 → 关定时器;
- 计算高电平总微秒时间,换算成距离;
- 连续测 5 次取平均值,串口打印输出;
- 循环往复,实时测距。
九、代码优点与可移植性
- 定时器配置标准规范,可直接移植到 TIM3/TIM4;
- 采用 1ms 中断 + 计数器微秒读数,计时精度高;
- 5 次均值滤波,解决超声波测距跳变、数据不稳定问题;
- 分层清晰:定时器底层、超声波驱动、业务应用完全分离;
- 标准库写法,兼容所有 STM32F103 系列单片机;
- 自带软件微秒 / 毫秒延时,无需依赖其他延时函数。