STM32F103C8T6驱动HC-SR04超声波模块测距打印在串口上

引言

HC-SR04超声波测距模块因其成本低、精度高、使用简单,被广泛应用于机器人避障、液位检测等领域。本文将结合STM32F103C8T6的驱动代码,详细解析HC-SR04的工作原理及实现方法,帮助开发者快速上手。


源码链接:https://share.weiyun.com/RJG4fPjy
HC-SR04模块工作原理
  1. 触发信号:向Trig引脚发送至少10μs的高电平脉冲,触发模块发射8个40kHz超声波。

  2. 回波接收:模块自动检测回波信号,Echo引脚输出高电平的时间,其持续时间与距离成正比。

  3. 编程思路:先向Trig引脚发送高电平,后延时至少10μs,然后拉低发送向Trig引脚低电平结束驱动信号,HC-SR04模块开始发射8个40kHz超声波,然后检测Echo引脚输出高电平的时间,测量距离=(声速343m/s ✖ Echo引脚高电平的时间/s)÷2

  4. 距离计算
    公式:

    距离=高电平时间×340 m/s2距离=2高电平时间×340m/s​

    简化后为:

    距离(cm)=高电平持续的时间(μs)58距离(cm)=58高电平时间(μs)​

驱动代码解析
1. 外设TIM定时器的初始化timer.c源文件
#include "delay.h"
#include "timer.h"
#include "adcx.h"
#include "led.h"
#include "string.h"
#include "ultrasonic.h"

extern u16 msHcCount;
void TIM4_Int_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); 			//时钟使能
	
	//定时器TIM4初始化
	TIM_TimeBaseStructure.TIM_Period = arr; 						//设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 						//设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		//设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  	//TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 				//根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ClearFlag(TIM4, TIM_FLAG_Update);  
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); 						//使能指定的TIM4中断,允许更新中断
	
	//中断优先级NVIC设置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  				//TIM4中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 		//先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  			//从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure); 								//初始化NVIC寄存器

	TIM_Cmd(TIM4,DISABLE);     //禁用TIM4					 
}

//定时器4中断服务程序
void TIM4_IRQHandler(void)   //TIM4中断		
{
   if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)  
   {
       TIM_ClearITPendingBit(TIM4, TIM_IT_Update  ); 
       msHcCount++;
   }
}

外设TIM定时器的初始化timer.h头文件

#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"


void TIM4_Int_Init(u16 arr,u16 psc);		//定时器4初始化


#endif

HC-SR04超声波模块初始化ultrasonic.c源文件

#include "ultrasonic.h"
#include "timer.h"

/*****************************************

   项目:	         HC-SR04超声波传感器实验  
   MCU:             STM32F103C8T6
   关注优快云作者:    单片机阿伟
   时间:            2025-1-20

*****************************************/



//超声波计数
u16 msHcCount;

void Ultrasonic_Init(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		
		RCC_APB2PeriphClockCmd (ULTRASONIC_GPIO_CLK, ENABLE );			// 打开连接 超声波传感器 的单片机引脚端口时钟
		GPIO_InitStructure.GPIO_Pin = ULTRASONIC_TRIG_GPIO_PIN;			// 配置连接 传感器TRIG 的单片机引脚模式
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;						// 设置为推挽输出
		GPIO_Init(ULTRASONIC_GPIO_PORT, &GPIO_InitStructure);				// 初始化 

		GPIO_InitStructure.GPIO_Pin = ULTRASONIC_ECHO_GPIO_PIN;			// 配置连接 传感器ECHO 的单片机引脚模式
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;				// 设置为浮空输入输入
		GPIO_Init(ULTRASONIC_GPIO_PORT, &GPIO_InitStructure);				// 初始化 
	
		TIM4_Int_Init(1000-1,72-1);  //定时器溢出一次为t=1ms,  t=(arr+1)*(psc+1)/72M=1ms
}


//打开定时器4
static void OpenTimerForHc()  
{
   TIM_SetCounter(TIM4,0);  //设置CNT的值,默认从0开始向上计数,当CNT的值大于arr则溢出
   msHcCount = 0;
   TIM_Cmd(TIM4, ENABLE);   //打开定时器4
}

//关闭定时器4
static void CloseTimerForHc()    
{
   TIM_Cmd(TIM4, DISABLE); 
}



//获取定时器4计数器值
u32 GetEchoTimer(void)
{
   u32 t = 0;
   t = msHcCount*1000;  //转换成us
   t += TIM_GetCounter(TIM4);  //CNT计数值每1us加1,所以总的时间要加上计数器的值
   TIM4->CNT = 0;   //计数器的值置0
   delay_ms(50);   
   return t;
}
 


//通过定时器4计数器值推算距离
/**************************

原理:Echo高电平持续的时间就是T

 计算距离distance=(声波的速度(343M/S)*Echo从0到1的时间T/ms)/2
 换算
 熟读=(34300cm/1000000us)/2/1≈58cm/us
 
 distance_cm=(1000Tus)/58cm

**************************/


float UltrasonicGetLength(void)
{
   u32 t = 0;
   int i = 0;
   float lengthTemp = 0;  //临时存储测得距离的变量
   float sum = 0;
   while(i!=5)  //取5次测量的距离
   {
      TRIG_Send = 1;      //先发送20us的高电平,告诉HC-SR04要开始测量
      delay_us(20);
      TRIG_Send = 0;      //低电平释放
      while(ECHO_Reci == 0); //while循环等待Echo为高电平      
      OpenTimerForHc();      //一旦Echo为高电平,打开定时器开始计时
      i = i + 1;
      while(ECHO_Reci == 1); //while循环等待Echo为低电平
      CloseTimerForHc();     //若为低电平,则结束一次时间的计时    
      t = GetEchoTimer();    //获取时间   
      lengthTemp = ((float)t/58.0);//计算一次测量的距离cm
      sum = lengthTemp + sum ;    //求和
        
    }
    lengthTemp = sum/5.0;  //求5次的平均值
    return lengthTemp;     //返回距离
}

HC-SR04超声波模块初始化ultrasonic.h头文件

#ifndef __ULTRASONIC_H
#define	__ULTRASONIC_H
#include "stm32f10x.h"
#include "adcx.h"
#include "delay.h"
#include "math.h"


/*****************************************

   项目:	         HC-SR04超声波传感器实验  
   MCU:             STM32F103C8T6
   关注优快云作者:    单片机阿伟
   时间:            2025-1-20

*****************************************/


/***************根据自己需求更改****************/
// ULTRASONIC GPIO宏定义

#define		ULTRASONIC_GPIO_CLK								RCC_APB2Periph_GPIOA
#define 	ULTRASONIC_GPIO_PORT							GPIOA
#define 	ULTRASONIC_TRIG_GPIO_PIN					GPIO_Pin_0	
#define 	ULTRASONIC_ECHO_GPIO_PIN					GPIO_Pin_1	

#define 	TRIG_Send  PAout(0)
#define 	ECHO_Reci  PAin(1)

/*********************END**********************/

void Ultrasonic_Init(void);
float UltrasonicGetLength(void);

void OpenTimerForHc(void);
void CloseTimerForHc(void); 
u32 GetEchoTimer(void);

#endif /* __ADC_H */

主函数:main.c

#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "ultrasonic.h"
#include "timer.h"

/*****************************************

   项目:	         HC-SR04超声波传感器实验  
   MCU:             STM32F103C8T6
   关注优快云作者:    单片机阿伟
   时间:            2025-1-20

*****************************************/

float distance;

int main(void)
{ 
	
  SystemInit();//配置系统时钟为72M	
	delay_init(72);
	LED_Init();
	LED_On();
	Ultrasonic_Init();
	USART1_Config();//串口初始化
	
//	OLED_Init();
	printf("Start \n");
	delay_ms(1000);
    while(1)
    {
        distance = UltrasonicGetLength();
        printf("\r\n距离:%f cm\n",distance);
        LED_Toggle();
        delay_ms(50);	//延时50ms

    }
	
}

  • 关键点

    • Trig引脚需输出高电平触发信号,Echo引脚需检测回波高电平。

    • 定时器4配置为1ms溢出周期,用于精确计时。


2. 时间测量函数

 GetEchoTimer.c

u32 GetEchoTimer(void) {
    u32 t = msHcCount * 1000;     // 溢出次数转微秒
    t += TIM_GetCounter(TIM4);    // 加上当前计数值
    TIM4->CNT = 0;                // 重置计数器
    delay_ms(50);                 // 防止硬件干扰
    return t;
}
  • 原理

    • msHcCount记录定时器溢出次数,每次溢出对应1ms。

    • TIM_GetCounter(TIM4)获取当前周期内的计数值(1μs/计数)。

    • 总时间 = 溢出时间 + 当前计数值。

  • 注意delay_ms(50)用于等待模块稳定,避免连续触发。


3. 距离计算函数

c

复制

float UltrasonicGetLength(void) {
    float sum = 0;
    for (int i=0; i<5; i++) {     // 取5次测量平均值
        TRIG_Send = 1;            // 触发信号
        delay_us(20);
        TRIG_Send = 0;
        while (ECHO_Reci == 0);   // 等待回波高电平
        OpenTimerForHc();         // 启动定时器
        while (ECHO_Reci == 1);   // 等待回波低电平
        CloseTimerForHc();        // 停止定时器
        sum += GetEchoTimer() / 58.0; // 计算单次距离
    }
    return sum / 5.0;             // 返回平均值
}

最后运行看效果

  • 优化

    • 多次采样:取5次测量平均值,减少噪声干扰。

    • 触发时序:严格遵循10μs以上触发信号(代码中为20μs)。


常见问题及解决方法
  1. 测量结果不稳定

    • 原因:环境噪声或被测物体表面不平。

    • 解决:增加软件滤波(如中值滤波)或硬件屏蔽。

  2. 超出最大测距范围

    • 原因:默认R3电阻为392,最大测距4.5m。

    • 解决:更换R3为472电阻,并调整探测角度(需硬件修改)。

  3. 回波信号无响应

    • 检查点

      • 模块供电是否为5V。

      • Echo引脚是否接触不良。


代码优化建议
  1. 中断优化

    • 使用输入捕获功能(如TIM的IC模式)替代轮询Echo引脚,减少CPU占用。

    // 示例:配置TIM输入捕获
    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿触发
    TIM_ICInit(TIM4, &TIM_ICInitStructure);

结语

本文从硬件配置、代码实现到优化方向,详细解析了HC-SR04超声波模块的驱动方法。开发者可根据实际需求调整代码参数,并结合硬件优化进一步提升测距稳定性。完整代码及工程文件可通过文章开头处获取链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值