资讯详情

STM32——通用定时器控制超声波传感器HCSR04

HCSR-04介绍

HC-SR04 提供超声波测距模块 2cm-400cm 测量非接触距离感知功能 非接触式距离感知功能可达到高距离精度,测距精度可达到高距离精度 3mm ;该模块包括超声波发射器、接收器和控制电路。 网上数据说这个模块精度很高,但是实际使用感觉很一般。我用89C52和STM32F103C8T6分别编写了测距程序,但精确测量范围为100cm以内,具体原因没有细究。 在这里插入图片描述

工作原理

使用IO先由口触发Trig至少10个口置高电平us,模块内部发射8个40个KHz脉冲方波,然后Trig低电平,由Echo接收方波,再根据回响电平持续的时间计算距离,原理非常简单。

程序思路

只需要一个IO口配置作为Trig,一个IO口配置作为Echo,因为当Echo收到的回响信号由低电平置高电平设置Echo为低电平。 先用SysTick软件延迟控制Trig发出高电平,然后等待回响信号Echo接收信号,即打开定时器,但我们的程序在定时器中运行,使用相同的定时器TIM2.收到信号后,清除定时器。然后得到电平的持续时间,计算距离,然后再清除定时器,打开定时器,下一个定时器中断可以正常触发。

代码实现

#include "HCSR04.h" #include "stm32f10x.h" // Device header #include "Usart.h" #include "Buzzer.h"  int HCSR04_Distance1; int HCSR04_Distance2;  #define RCC_HCSR04 RCC_APB2Periph_GPIOB  #define GPIO_HCSR04_Trig GPIOB #define PIN_HCSR04_Trig1 GPIO_Pin_8 #define PIN_HCSR04_Trig2 GPIO_Pin_5     #define GPIO_HCSR04_Echo GPIOB #define PIN_HCSR04_Echo1 GPIO_Pin_9 #define PIN_HCSR04_Echo2 GPIO_Pin_6  void HCSR04_Delay_us(uint32_t xus) { 
          SysTick->LOAD = 72 * xus;    ///设定定时器重装值  SysTick->VAL = 0x00;     //清零  SysTick->CTRL = 0x00000005;    ///设置时钟源HCLK,启动定时器  while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}
void HCSR04_Delay_ms(uint32_t xms)
{ 
        
	while(xms--)
	{ 
        
		HCSR04_Delay_us(1000);
	}
}

这里因为需要,我设置了两个超声波传感器。 首先宏定义引脚,再定义两个距离变量,以及初始化Systick软件延时函数。

void GPIO_HCSR04Init()
{ 
        
	RCC_APB2PeriphClockCmd(RCC_HCSR04, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = PIN_HCSR04_Trig1 | PIN_HCSR04_Trig2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIO_HCSR04_Trig,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = PIN_HCSR04_Echo1 | PIN_HCSR04_Echo2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_Init(GPIO_HCSR04_Echo,&GPIO_InitStructure);
}

分别初始化两种功能的引脚,一种是推挽输出,一种是下拉输入。

void TIM_HCSR04Init()
{ 
        
	//通用定时器2 3 4,APB1
	//TIM2的通道1 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

	TIM_InternalClockConfig(TIM2);//选用内部时钟
	
	TIM_TimeBaseInitTypeDef timBaseInit;
	TIM_TimeBaseStructInit(&timBaseInit);
	timBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;
	timBaseInit.TIM_CounterMode = TIM_CounterMode_Up;
	timBaseInit.TIM_RepetitionCounter = 0;
	timBaseInit.TIM_Period = 10000 - 1;
	timBaseInit.TIM_Prescaler = 7200 - 1;
	//7200分频,记10000次就是1秒
	TIM_TimeBaseInit(TIM2,&timBaseInit);
	
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动清除更新中断标志位,避免初始化后自动进入中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIM2更新中断
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef nvic_struct;
	nvic_struct.NVIC_IRQChannel = TIM2_IRQn;
	nvic_struct.NVIC_IRQChannelCmd = ENABLE;
	nvic_struct.NVIC_IRQChannelPreemptionPriority = 1;
	nvic_struct.NVIC_IRQChannelSubPriority = 1;
	
	NVIC_Init(&nvic_struct);
	
	TIM_Cmd(TIM2,ENABLE);
}

先使能定时器,之后配置时基单元,将72MHz7200分频,得到计数器每加一就是1/10000s,记10000次就是一秒。之后配置NVIC通道,开启定时器中断,优先级随便选。

void StartMode1()
{ 
        	
	GPIO_ResetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig1);//预先拉低
	GPIO_SetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig1);
	HCSR04_Delay_us(20);
	GPIO_ResetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig1);//OK
}
void StartMode2()
{ 
        	
	GPIO_ResetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig2);//预先拉低
	GPIO_SetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig2);
	HCSR04_Delay_us(20);
	GPIO_ResetBits(GPIO_HCSR04_Trig,PIN_HCSR04_Trig2);//OK
}

这里是Trig触发函数

void HCSR04Init()
{ 
        
	GPIO_HCSR04Init();
	TIM_HCSR04Init();
}
//集成一个函数,用来main函数初始化用
void HCSR04_GetDistance1()
{ 
        
	unsigned int time = 0;
	
	TIM_SetCounter(TIM2,0x0000);
	
	StartMode1();

	while(GPIO_ReadInputDataBit(GPIO_HCSR04_Echo , PIN_HCSR04_Echo1 ) == 0);
	TIM_Cmd(TIM2,ENABLE);	//Usart_SendStr("TIM2 ON ");
	while(GPIO_ReadInputDataBit(GPIO_HCSR04_Echo , PIN_HCSR04_Echo1 ) == 1);
	TIM_Cmd(TIM2,DISABLE);	//Usart_SendStr("TIM2 OFF ");
	
	//单位cm
	//v = 340m/s = 34000cm/s = 34000cm/10^6us = 0.034cm/us
	//s = vt/2
	time = TIM_GetCounter(TIM2);
	HCSR04_Distance1 = (int)time * 340 / 100 / 2;//cm
	
	TIM_SetCounter(TIM2,0x0000);
}
void HCSR04_GetDistance2()
{ 
        
	unsigned int time = 0;
	
	TIM_SetCounter(TIM2,0x0000);
	
	StartMode2();

	while(GPIO_ReadInputDataBit(GPIO_HCSR04_Echo , PIN_HCSR04_Echo2 ) == 0);
	TIM_Cmd(TIM2,ENABLE);	//Usart_SendStr("TIM2 ON ");
	while(GPIO_ReadInputDataBit(GPIO_HCSR04_Echo , PIN_HCSR04_Echo2 ) == 1);
	TIM_Cmd(TIM2,DISABLE);	//Usart_SendStr("TIM2 OFF ");
	
	//单位cm
	//v = 340m/s = 34000cm/s = 34000cm/10^6us = 0.034cm/us
	//s = vt/2
	time = TIM_GetCounter(TIM2);
	HCSR04_Distance2 = (int)time * 340 / 100 / 2;//cm
	
	TIM_SetCounter(TIM2,0x0000);
}

这里是计算距离的函数,这里有一个点要,不要在计算距离的代码中掺杂有的操作,例如串口打印,这个bug我找了好久,Echo一直收不到回响信号,因此我企图用串口打印来找到程序究竟卡在哪里,就如程序中的TIM2 ON 和TIM2 OFF那样,结果就只有TIM2 ON,运行不到OFF处。之后我一次性把打印都注释掉,只留下打印距离的,结果就成功了。。。因为这个bug我找了好几个小时。。。o(╥﹏╥)o

void TIM2_IRQHandler()
{ 
        
	static int LightADTime = 0;//控制LightAD检测的间隔 
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == 1)
	{ 
        
		LightADTime++;
		if(LightADTime == 10)// 
		{ 
        
			
			//这里是配合光敏传感器AD转换和看门狗中断的程序,后面会再补充
			LightADTime = 0;
		}		
		TIM_SetCounter(TIM2,0x0000);
			
		HCSR04_GetDistance1();			
		HCSR04_GetDistance2();
		
		if((HCSR04_Distance1 <= 20 || HCSR04_Distance1 >= 1000) )
		{ 
        
			BuzzerOn();
		}
		else if((HCSR04_Distance2 <= 80 || HCSR04_Distance2 >= 1000) )
		{ 
        
			BuzzerOn();
		}
		else BuzzerOff();	
		
		TIM_Cmd(TIM2,ENABLE);
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

定时器中断函数,记得要先判断是不是进入的这条定时器中断通道,如果是,在中断函数执行之后要把,不然会一直循环,出不去。 BuzzerOn是控制有源蜂鸣器的函数,蜂鸣器是低电平触发。

结语

要注意的点就是不要在计算距离的时候执行有一定时长的操作,否则会一直收不到回响信号,甚至浪费你几个小时时间找bug。 还有很多博客没写。。 如果有小伙伴知道为什么我的程序精度不高欢迎在评论区告知我(✪ω✪)

标签: 推挽式传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台