1、模块介绍

HC-SR04超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能,测距精度可达高到的非接触式距离感测功能,测距精度可达高到3mm,模块包括超声波发射器、接收器与控制电路。

7d456a92843bee0166be1ea3a59638b8.jpg

2、管脚定义

  • Vcc: +5V电源供电

  • Trig: 输入触发信号(可以触发测距)

  • Echo:传出信号回响(可以传回时间差)

  • Gnd: 接地

3、工作原理

{C2F071FC-C632-4865-8389-BD2C7997318C}.png

STM32单片机给超声波模块Trig脚一个大于10uS的高电平脉冲,模块内部会发出8个40KHz的脉冲,此时模块的Echo管脚会输从低电平跳转到高电平,当模块发送出去的脉冲遇到障碍物反射回来,Echo管脚会从高电平跳转为低电平。因此Echo管脚输出的高电平时间即为脉冲从发射到遇到障碍物返回模块所需要的时间。

因此距离distance =( 高电平持续时间*声速(340m/s))/ 2。

4、编程思路

超声波模块测距最主要的工作就是获取Echo管脚的高电平时间。因此我们可以使用STM32单片机的定时器和外部中断功能对Echo管脚的高电平时间进行统计。

       外部中断(EXTI):STM32单片机有20个中断线,每个GPIO管脚都可以被设置成输入线。通过外部中断,我们可以使单片机的某个管脚检测超声波模块Echo信号,当信号发生跳变的时候,触发中断,使程序执行相应的中断函数使能或者关闭定时器。因此我们可以使用外部中断这个功能,去检测超声波模块Echo管脚电平的变化。然后利用定时器去统计Echo管脚两次电平变化的时间,从而计算出距离。

图片1-yiei.png

EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。代码例程中使用的管脚为PA1,因此我们需要配置单片机的EXTI1中断。(具体配置可见代码)。

       定时器:Stm32单片机有8个定时器,定时器的基本功能就是用来计时的。因此我们可以配置定时器去统计Echo电平两次变化持续的时间。例程中是将定时器配置成每1us计数一次,并且每100us,触发一次定时器中断,在中断函数里面定义了count变量进行累积。

       因此,我们只要利用外部中断和定时器功能,就可以统计出Echo管脚输出的高电平时间。当超声波模块发送脉冲,Echo管脚输出高电平,外部中断检测到了Echo管脚的电平变化,此时打开定时器。当超声波模块接收到返回的脉冲,Echo管脚从高电平跳转为低电平,此时关闭定时器。定时器统计的时间,即为脉冲从发出去再到接收到花费的总时间。有了总时间之后,就可以计算距离。

5、驱动代码

#include "bsp_hcsr04.h"                
#include "bsp_delay.h"

uint32_t Count = 0;
uint32_t Time = 0;
uint8_t  Flag = 0;
uint32_t Length = 0;

static void EXTI_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
	
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn ;	

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;	
	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
    NVIC_Init(&NVIC_InitStructure);
}
void EXTI_Config()
{
	EXTI_InitTypeDef EXIT_InitStructure;
	
	EXIT_InitStructure.EXTI_Line = EXTI_Line1;
	
	EXIT_InitStructure.EXTI_LineCmd = ENABLE;
	
	EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	
	EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
	
	EXTI_NVIC_Config();
	
	EXTI_Init(&EXIT_InitStructure);	
}

static void TIM_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
	
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn ;	

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;	
	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
    NVIC_Init(&NVIC_InitStructure);
	
}

void Timer_Init()
{
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);        		//选择APB1总线下的定时器Timer3	
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数模式,此处为向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100;		              		 //ARR,自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 71;		           			 //PSC,预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;		   			 //高级计时器特有,重复计数
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);												//使能中断
	
	TIM_NVIC_Config();
	
	TIM_Cmd(TIM2, ENABLE);																						// 使能定时器TIM3
 
}

void HCSR04_Init()
{

	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(Trig_RCC, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	

	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = Trig_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Trig_Port, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = Echo_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Echo_Port, &GPIO_InitStruct);

	EXTI_Config();
	Timer_Init();	
	
	GPIO_ResetBits(Trig_Port, Trig_Pin);//Trig引脚给低电平,为后续启动做准备
	
}

//Trig发出信号
void HC_SR04_Start()
{
	GPIO_SetBits(GPIOA, GPIO_Pin_0); 
	DelayUs(45);
	GPIO_ResetBits(GPIOA, GPIO_Pin_0); 
	DelayUs(70);
}
//定时器使能
void TIM2_Open()
{
	TIM_Cmd(TIM2,ENABLE);
	Count = 0;
}

//定时器关闭
void TIM2_Close()
{
	TIM_Cmd(TIM2,DISABLE);
}

void TIM2_IRQHandler(void)		// 定时器3的中断函数
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) // 检查定时器3的更新中断标志位是否被设置
    {
        if (GPIO_ReadInputDataBit(Echo_Port, Echo_Pin) == 1) // 读取回声传感器端口上的引脚状态
        {
            Count++; // 如果引脚状态为高电平,则增加时间计数器
        }
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);		// 清空标志位,表示中断已经处理完毕
    }
}

//每一个Count为100us,再加上计时器中每一个计数为1us
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line1) == SET)
	{
		if(Flag == 0)
		{
			TIM2_Open();
			Flag = 1;
		}
		else
		{
			TIM2_Close();
			Time = Count*100 + TIM_GetCounter(TIM2);
			Flag = 0;
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}


uint32_t HCSR04_GetLength(void)
{
	HC_SR04_Start();

	Length = Time/58;   //cm
 
	return Length;
}

完整代码下载: