CH_SR04
一、简介
1.产品特点
HC_SR04超声波测距模块可提供2cm-400cm的非接触式测距感测功能,测距精度高达3mm;模块包括超声波发射器,接收器与控制电路。
基本工作原理:
(1)采用IO口TRIG触发测距,需要给最少10us的高电平。
(2)模块自动发送8个40kHz的方波,自动检测是否有信号返回。
(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续时间就是超声波从发射到返回的时间。
距离计算公式:uS/58=厘米,uS/148=英尺,距离=高电平时间*声速(340m/s)/2。
建议测量周期为60ms以上,以防止发射信号对回波信号的影响。
注:此模块不易带电连接,若要带电连接,则需将模块的GND先连接,否则容易接线错误影响正常使用。测距时,被测物体的面积不少于0.5平方米且平面尽量要求平整,否则影响测量结果。
2.实物图
VCC:供5V电源
GND:为地线
TRIG:触发控制信号输入
MCHO:回响信号输出
实物图
3.电气特性
电气参数 |
HC-SR04超声波模块 |
工作电压 |
DC 5V |
工作电流 |
15mA |
工作频率 |
40kHz |
最远射程 |
4m |
最近射程 |
2cm |
测量角度 |
15° |
输入触发信号 |
10uSTTL脉冲 |
输出回响信号 |
输出TTL电平信号 |
规格尺寸 |
45*20*15mm |
4.时序图
时序图
以上时序图表明只需要提供一个10us以上的脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号,回响信号的脉冲宽度与所测的距离成正比。
视频演示
以上对HC_SR04超声波测距模块的介绍在说明书中都有介绍。以下开始对HC_SR04进行配置。
二、程序设计
1.准备
硬件:
(1)单片机最小系统(笔者用的是 STM32F103ZET6 开发板)
(2)HC-SR超声波测距模块
(3)0.96寸OLED屏
(4)杜邦线
主芯片相关外设:
(1)RCC时钟
(2)GPIO
(3)TIM定时器
(4)EXTI外部中断
(5)Systick系统滴答定时器
开发环境: KEIL_5
2.硬件连接
HC-SR04超声波模块 | |
TRIG |
PD_0 |
MCHO |
PD_1 |
3.驱动编写
编程思路:
使能GPIO,AFIO,TIM,EXTI。
①TRIG为主机发送触发信号端,可将对应io配置为输出模式,并拉低电平。触发信号:io口输出高电平持续10us以上再输出低电平。
②MCHO为从机输出回响信号端,由主机接收信号,可将对应io配置为输入模式。配置外部中断,检测io电平变化。
③使用TIM6,在MCHO上升沿时启动定时器,记录MCHO高电平持续时间;在下降沿时关闭定时器,并清空计数器,保存高电平持续时间。
④使用TIM7,定时发送触发信号。
⑤配置中断,设置中断优先级,编写中断服务函数。
⑥main函数负责向oled输出实测距离。
(1)GPIO引脚和外部中断配置
void HC_SR04_PinInit(void)
{
//1.配置GPIO模式
GPIO_InitTypeDef hc_sr04_gpioInit;
hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_1;
hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_IPD; //下拉输入模式
hc_sr04_gpioInit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD,&hc_sr04_gpioInit);
hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_0;
hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出
GPIO_Init(GPIOD,&hc_sr04_gpioInit);
HC_SR04_TRIG_0;//拉低电平
//2.配置外部中断
EXTI_InitTypeDef hc_sr04_extiInit;
hc_sr04_extiInit.EXTI_Line = EXTI_Line1;
hc_sr04_extiInit.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
hc_sr04_extiInit.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//双边沿对齐
hc_sr04_extiInit.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&hc_sr04_extiInit);
//3.选择外部中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource1);//选择PD1作为外部中断线
//EXTI_GenerateSWInterrupt(EXTI_Line1);//产生一个软件中断
EXTI_ClearFlag(EXTI_Line1);//清除挂起标志位
}
(2)TIM配置
/*
\brief: 基本TIM初始化
\param: TIMx: where x can be 1 to 17 to select the TIM peripheral.
psc:预分频系数
arr:重装载值
\retval: none
*/
static void TIMx_Init(TIM_TypeDef* TIMx,uint16_t psc,uint16_t arr)
{
//1.复位TIM
TIM_DeInit(TIMx);
//2.配置TIM
TIM_TimeBaseInitTypeDef TIMx_timeBaseInit;
TIMx_timeBaseInit.TIM_Prescaler = psc-1; //设置预分频系数为psc
TIMx_timeBaseInit.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIMx_timeBaseInit.TIM_RepetitionCounter = 0; //重复计数值
TIMx_timeBaseInit.TIM_Period = arr; //重装载值
TIMx_timeBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIMx,&TIMx_timeBaseInit);
//3.中断配置
TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); //使能更新中断
TIM_ClearFlag(TIMx,TIM_FLAG_Update); //清除挂起标志
//4.失能定时器
TIM_Cmd(TIMx,DISABLE);
}
(2)配置中断优先级
/*
\brief: 配置中断优先级
\param: NVIC_IRQChannel:中断号
PreemptionPriority:抢占优先级
SubPriority:子优先级
\retval: none
*/
static void nvic_InitConfig(uint8_t IRQChannel,uint8_t PreemptionPriority,uint8_t SubPriority)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);//配置优先级分组
NVIC_InitTypeDef ch_sr04_NVIC_init;
ch_sr04_NVIC_init.NVIC_IRQChannel = IRQChannel;
ch_sr04_NVIC_init.NVIC_IRQChannelPreemptionPriority = PreemptionPriority;
ch_sr04_NVIC_init.NVIC_IRQChannelSubPriority = SubPriority;
ch_sr04_NVIC_init.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&ch_sr04_NVIC_init);
}
(3)初始化HC_SR04
使用外部中断需要开启AFIO时钟。
触发信号发送周期建议大于60ms,此处为200ms定时发送触发信号。
/*
\brief: CH_SR04超声测距模块初始化配置
\retval: none
*/
void HC_SR04_Init(void)
{
//1.打开外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM6
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);//使能TIM7
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);//使能GPIOD
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO
//2.GPIO初始化
HC_SR04_PinInit();
//3.TIM初始化
TIMx_Init(TIM6,72,0xFFFF); //72分频 1us 用于记录MCHO高电平时间
TIMx_Init(TIM7,720,20000); //200ms发一次脉冲
TIM_Cmd(TIM7,ENABLE);
//4.配置中断优先级
nvic_InitConfig(EXTI1_IRQn,1,0);
nvic_InitConfig(TIM6_IRQn,2,0);
nvic_InitConfig(TIM7_IRQn,3,0);
}
//触发信号
void HC_SR04_TriggerSignal(void)
{
HC_SR04_TRIG_1;
Delay_us(20); //延时10us以上
HC_SR04_TRIG_0;
}
(4)中断服务函数
为了使用us计数,将TIM6进行72分频,最大计数值为65535us,而MCHO高电平最大持续时间为400*58=23200us,所以将TIM6的重装载寄存器的值设为0xFFFF,完全够用于记录MCHO的高电平持续时间。如果TIM一次计数到溢出所用的时间小于23200us,可定义一个变量记录一次回响信号TIM6进中断的次数,再进行时间计算。
本文中的tim6_IT_count就显得有些多余。
uint16_t tim6_IT_count=0;
//ECHO 中断线服务函数
void EXTI1_IRQHandler(void)
{
if(RESET != EXTI_GetITStatus(EXTI_Line1))
{
EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位
if(HC_SR04_ECHO()) //上升沿
{
TIM_Cmd(TIM6,ENABLE); //启动定时器
}
else //下降沿
{
TIM_Cmd(TIM6,DISABLE); //关闭定时器
Dist_cm = (tim6_IT_count*0xFFFF+TIM_GetCounter(TIM6))/58.0;//计算距离
TIM_SetCounter(TIM6,0x00); //清空计数器
tim6_IT_count=0; //计数数清零
}
}
}
//基本定时器TIM6 辅助MCHO高电平期间计数
void TIM6_IRQHandler(void)
{
if(RESET != TIM_GetITStatus(TIM6,TIM_IT_Update))
{
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清除标志位
tim6_IT_count++;
}
}
//基本定时器TIM7 定时发送CH_SR04载波发送
void TIM7_IRQHandler(void)
{
if(RESET != TIM_GetITStatus(TIM7,TIM_IT_Update))
{
TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除标志位
HC_SR04_TriggerSignal();//触发信号
}
}
三、实验结果
测量值不会很准确,当测量距离小于2cm时,或者测量斜面时,数值会跳动很大。如果需要增加精准度,可通过排序取中间值或其他方式减小误差。
四、代码
hc_sr04.c
#include "hc_sr04.h"
/*
配置GPIO
\pin: ECHO - PD1 触发控制信号
TRIG - PD0 回响信号
*/
#define HC_SR04_ECHO() GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_1)//读取输入电平
#define HC_SR04_TRIG_0 GPIO_ResetBits(GPIOD,GPIO_Pin_0) //写0
#define HC_SR04_TRIG_1 GPIO_SetBits(GPIOD,GPIO_Pin_0) //写1
float Dist_cm;//测量距离
void HC_SR04_PinInit(void)
{
//1.配置GPIO模式
GPIO_InitTypeDef hc_sr04_gpioInit;
hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_1;
hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_IPD; //下拉输入模式
hc_sr04_gpioInit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD,&hc_sr04_gpioInit);
hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_0;
hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出
GPIO_Init(GPIOD,&hc_sr04_gpioInit);
HC_SR04_TRIG_0;//拉低电平
//2.配置外部中断
EXTI_InitTypeDef hc_sr04_extiInit;
hc_sr04_extiInit.EXTI_Line = EXTI_Line1;
hc_sr04_extiInit.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
hc_sr04_extiInit.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//双边沿对齐
hc_sr04_extiInit.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&hc_sr04_extiInit);
//3.选择外部中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource1);//选择PD1作为外部中断线
//EXTI_GenerateSWInterrupt(EXTI_Line1);//产生一个软件中断
EXTI_ClearFlag(EXTI_Line1);//清除挂起标志位
}
/*
\brief: 基本TIM初始化
\param: TIMx: where x can be 1 to 17 to select the TIM peripheral.
psc:预分频系数
arr:重装载值
\retval: none
*/
static void TIMx_Init(TIM_TypeDef* TIMx,uint16_t psc,uint16_t arr)
{
//1.复位TIM
TIM_DeInit(TIMx);
//2.配置TIM
TIM_TimeBaseInitTypeDef TIMx_timeBaseInit;
TIMx_timeBaseInit.TIM_Prescaler = psc-1; //设置预分频系数为psc
TIMx_timeBaseInit.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIMx_timeBaseInit.TIM_RepetitionCounter = 0; //重复计数值
TIMx_timeBaseInit.TIM_Period = arr; //重装载值
TIMx_timeBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIMx,&TIMx_timeBaseInit);
//3.中断配置
TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); //使能更新中断
TIM_ClearFlag(TIMx,TIM_FLAG_Update); //清除挂起标志
//4.失能定时器
TIM_Cmd(TIMx,DISABLE);
}
/*
\brief: 配置中断优先级
\param: NVIC_IRQChannel:中断号
PreemptionPriority:抢占优先级
SubPriority:子优先级
\retval: none
*/
static void nvic_InitConfig(uint8_t IRQChannel,uint8_t PreemptionPriority,uint8_t SubPriority)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);//配置优先级分组
NVIC_InitTypeDef ch_sr04_NVIC_init;
ch_sr04_NVIC_init.NVIC_IRQChannel = IRQChannel;
ch_sr04_NVIC_init.NVIC_IRQChannelPreemptionPriority = PreemptionPriority;
ch_sr04_NVIC_init.NVIC_IRQChannelSubPriority = SubPriority;
ch_sr04_NVIC_init.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&ch_sr04_NVIC_init);
}
/*
\brief: CH_SR04超声测距模块初始化配置
\retval: none
*/
void HC_SR04_Init(void)
{
//1.打开外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM6
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);//使能TIM7
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);//使能GPIOD
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO
//2.GPIO初始化
HC_SR04_PinInit();
//3.TIM初始化
TIMx_Init(TIM6,72,0xFFFF); //72分频 1us 用于记录MCHO高电平时间
TIMx_Init(TIM7,720,20000); //200ms发一次脉冲
TIM_Cmd(TIM7,ENABLE);
//4.配置中断优先级
nvic_InitConfig(EXTI1_IRQn,1,0);
nvic_InitConfig(TIM6_IRQn,2,0);
nvic_InitConfig(TIM7_IRQn,3,0);
}
//触发信号
void HC_SR04_TriggerSignal(void)
{
HC_SR04_TRIG_1;
Delay_us(20); //延时10us以上
HC_SR04_TRIG_0;
}
uint16_t tim6_IT_count=0;
//ECHO 中断线服务函数
void EXTI1_IRQHandler(void)
{
if(RESET != EXTI_GetITStatus(EXTI_Line1))
{
EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位
if(HC_SR04_ECHO()) //上升沿
{
TIM_Cmd(TIM6,ENABLE); //启动定时器
}
else //下降沿
{
TIM_Cmd(TIM6,DISABLE); //关闭定时器
Dist_cm = (tim6_IT_count*0xFFFF+TIM_GetCounter(TIM6))/58.0;//计算距离
TIM_SetCounter(TIM6,0x00); //清空计数器
tim6_IT_count=0; //计数数清零
}
}
}
//基本定时器TIM6 辅助MCHO高电平期间计数
void TIM6_IRQHandler(void)
{
if(RESET != TIM_GetITStatus(TIM6,TIM_IT_Update))
{
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清除标志位
tim6_IT_count++;
}
}
//基本定时器TIM7 定时发送CH_SR04载波发送
void TIM7_IRQHandler(void)
{
if(RESET != TIM_GetITStatus(TIM7,TIM_IT_Update))
{
TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除标志位
HC_SR04_TriggerSignal();//触发信号
}
}
hc_sr04.h
#ifndef _HC_SR04_H_
#define _HC_SR04_H_
#include "stm32f10x.h"
#include "systick.h"
extern float Dist_cm;//测量距离 cm
void HC_SR04_Init(void);
#endif
main.c
#include "stm32f10x.h"
#include "hc_sr04.h"
#include "systick.h"
#include "oled.h"
#include <stdio.h>
#include <string.h>
/*
超声测距实验
*/
int main(void)
{
/* 相关外设初始化 */
HC_SR04_Init(); //hc_sr04初始化
systick_config(); //系统滴答
OLED_Init(); //oled初始化
uint8_t buff[10];
while(1)
{
snprintf((char *)buff,10,"%0.1f%s",Dist_cm,"cm"); //拼接字符串
OLED_Display_String(20,16,12,24,buff); //oled显示字符串
OLED_Refresh(); //刷新函数
OLED_GRAM_Init(); //初始化oled缓存
memset(buff,0,10); //清零buff
}
}
其他代码:略。
源码下载:
链接:https://pan.baidu.com/s/1mfZma1C0xWdlEr6bNz4KCg?pwd=1234
提取码:1234
文章如有错误,还望在评论区指正!文章来源:https://www.toymoban.com/news/detail-580711.html
2023/1/13文章来源地址https://www.toymoban.com/news/detail-580711.html
到了这里,关于【STM32篇】驱动HC_SR04超声波测距模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!