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. 自带软件微秒 / 毫秒延时,无需依赖其他延时函数。
相关推荐
你怎么知道我是队长4 小时前
计算机系统基础22---计算机的基本组成---IO控制方式
单片机·嵌入式硬件
風清掦4 小时前
【STM32学习笔记-12】Unix 时间戳、BKP 备份寄存器与 RTC 实时时钟
笔记·stm32·单片机·嵌入式硬件·学习·实时音视频·unix
hoiii1875 小时前
基于STM32的扫地机器人源码工程
stm32·单片机·机器人
feasibility.7 小时前
嵌入式系统的“能量-执行”拓扑学:电池、舵机、电机与电调的深层关系
科技·嵌入式硬件·电机·拓扑学·舵机·电池·电调
振南的单片机世界7 小时前
EXTI边沿检测:上升沿、下降沿、双边沿,硬件自动捕捉
stm32·单片机·嵌入式硬件
Max_uuc8 小时前
【感知心法】别相信你的传感器!撕碎“所读即所得”的 API 幻觉,论物理世界的“全员撒谎”与状态观测器的绝对凝视
单片机
踏着七彩祥云的小丑9 小时前
嵌入式——认识电子元器件——符号
单片机·嵌入式硬件
莎士比亚的文学花园21 小时前
stm32——平衡小车
stm32·单片机·嵌入式硬件
Hello_Embed21 小时前
STM32CubeIDE 创建第1个工程
stm32·单片机·嵌入式·ai编程