Abstract: 超声波测距模块HC-SR04模块的代码,提供两种思路
目录:
模块介绍
懒得写,看这里。
代码部分
思路一
- 用 15us 的高电平启动模块
- 使用间断计时的方法,用每隔一段时间(这里是 10us)触发一次的中断进行计时
- 用计时的值和声速算出距离
HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);
#endif
HCSR04.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#define Trig_Pin GPIO_Pin_7 //PB7
#define Echo_Pin GPIO_Pin_6 //PB6,TIM4_CH1
uint16_t Count=0; //计数值
uint64_t Time=0;
uint64_t Time_end=0;
/**
* @brief 初始化函数
* @param 无
* @retval 无
*/
void HCSR04_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = Trig_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = Echo_Pin; //TIM4_CH1
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, Trig_Pin); //Trig拉低
TIM_InternalClockConfig(TIM4);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //时基单元设置
TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1); //1us
TIM_TimeBaseInitStructure.TIM_Period = (10-1); //10us溢出
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //开启更新中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //指定中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM4,ENABLE); //TIM4使能
}
/**
* @brief 产生触发信号
* @param 无
* @retval 无
*/
void HCSR04_Start(void)
{
GPIO_SetBits(GPIOB, Trig_Pin);
Delay_us(15);
GPIO_ResetBits(GPIOB, Trig_Pin);
}
/**
* @brief 启动HC-SR04模块并测量距离
* @param 无
* @retval uint16_t Distance 测量结果
*/
uint16_t HCSR04_GetDistance(void)
{
HCSR04_Start();
uint32_t Distance = 0;
while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==0); //等待低电平结束
Time=0; //计时清零
while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==1); //等待高电平结束
Time_end=Time; //记录结束时的时间
Distance=(Time_end/2)*3.46;
return Distance;
}
/**
* @brief TIM4中断函数,每10ms Time加1
* @param 无
* @retval 无
*/
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)
{
Time++;
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
思路二
- 用 15us 的高电平启动模块
- 使用不间断计时的方法,当检测到模块的返回信号时开始计时,直到返回信号结束,读取出计数器的值
- 用计数的值和声速算出距离
HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);
#endif
HCSR04.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#define Trig_Pin GPIO_Pin_7 //PB7
#define Echo_Pin GPIO_Pin_6 //PB6,TIM4_CH1
uint16_t Count=0; //计数值
/**
* @brief 初始化函数
* @param 无
* @retval 无
*/
void HCSR04_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = Trig_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = Echo_Pin; //TIM4_CH1
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, Trig_Pin); //Trig拉低
TIM_InternalClockConfig(TIM4);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //时基单元设置
TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1); //1us
TIM_TimeBaseInitStructure.TIM_Period = (50000-1); //50ms溢出
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
}
/**
* @brief 产生触发信号
* @param 无
* @retval 无
*/
void HCSR04_Start(void)
{
GPIO_SetBits(GPIOB, Trig_Pin);
Delay_us(15);
GPIO_ResetBits(GPIOB, Trig_Pin);
}
/**
* @brief 启动HC-SR04模块并测量距离
* @param 无
* @retval uint16_t Distance 测量结果
*/
uint16_t HCSR04_GetDistance(void)
{
HCSR04_Start();
uint32_t Distance = 0;
TIM_SetCounter(TIM4,0);
while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==0); //等待低电平结束
TIM_Cmd(TIM4,ENABLE);
while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==1); //等待高电平结束
Count=TIM_GetCounter(TIM4);
TIM_Cmd(TIM4,DISABLE);
Distance=(Count/2)*0.346;
return Distance;
}
值得注意的点
HC-SR04模块使用 DC 5V 供电,但是也有其他电压的产品
模块检测距离时需要注意反射超声波的物体的大小和面倾角,若物体太小或面倾角过大将不能得到准确的结果
考虑到 STM32 GPIO 口的电压耐受,建议使用 FT (即 5V 耐受)的引脚,例如这里使用的PB6、PB7引脚,也有人使用普通引脚,这里不做尝试
这里两种思路的代码,从精度上看几乎一样,但是频繁进中断
不太优雅,所以更偏向于思路二思路一的代码需要额外配置中断优先级组,如在主函数中加上
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
也可以使用定时器的输入捕获实现功能,但是精度不理想且受外部影响较大,这里就不考虑了