HC-SR04超声波测距模块可提供2cm到400cm的非接触式距离感测功能,测距精度可达3mm。
(1)给TRIG引脚至少10us的高电平信号触发测距。
(2)模块自动发送8个40KHz的方波,自动检测是否有信号返回
(3)有信号返回,通过Echo引脚输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间。
(4)测量距离 = (高电平的持续时间 * 声速(340m/s))/ 2。
ultrasonic | STM32 |
VCC | VCC(5V) |
Trig | PB5 |
Echo | PB6 |
GND | GND |
在了解了上述内容后,我们便可以根据上述知识便可以编写驱动程序了。触发信号Trig很简单直接通过IO输出和延时给一个大于10us的高电平即可触发。Echo引脚需要接收并记录高电平的持续时间,大致有3种实现的思路:
(1)在发送触发信号后一直等待Echo引脚的响应(变为高电平)并打开定时器计时直到Echo变为低电平,关闭定时器记录下计时时间。
(2)在发送触发信号后,Echo响应后(上升沿)触发外部中断,开启定时器计时直到Echo变为低电平,关闭定时器记录下计时时间。
(3)在发送触发信号后,通过定时器的输入捕获引脚抓取Echo引脚上升沿,开启定时器计时直到Echo变为低电平,关闭定时器记录下计时时间。
第一种方式完全在主进程中完成,比较占用主进程资源,比较适合于超声波模块的测试应用。第二种和第三种方式在主进程中发送触发信号中断中处理接收计算距离比较适合于实际项目的应用。
下面为第二种方式的驱动程序:
#ifndef __HC_SR04_H
#define __HC_SR04_H
#include "stm32f10x.h"
#define HC_SR04_TRIG_CLK RCC_APB2Periph_GPIOA
#define HC_SR04_TRIG_PORT GPIOA
#define HC_SR04_TRIG_PIN GPIO_Pin_4
#define HC_SR04_ECHO_CLK RCC_APB2Periph_GPIOA
#define HC_SR04_ECHO_PORT GPIOA
#define HC_SR04_ECHO_PIN GPIO_Pin_5
#define HC_SR04_ECHO_EXTI_PORT_SOURCE GPIO_PortSourceGPIOA
#define HC_SR04_ECHO_EXTI_PIN_SOURCE GPIO_PinSource5
extern float Distance;
void HC_SR04_init(void);
void HC_SR04_start(void);
#endif /* __HC_SR04_H*/
#include "jsn-sr04t.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
float Distance;
void HC_SR04_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(HC_SR04_TRIG_CLK, ENABLE); //使能PC端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
GPIO_InitStructure.GPIO_Pin = HC_SR04_TRIG_PIN; // 脉冲触发端口(Trig)配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(HC_SR04_TRIG_PORT, &GPIO_InitStructure); //根据设定参数初始化端口
//GPIO_ResetBits(HC_SR04_TRIG_PORT,HC_SR04_TRIG_PIN); //端口初始化为低电平
GPIO_InitStructure.GPIO_Pin = HC_SR04_ECHO_PIN; // 回波接收端口(Echo)配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(HC_SR04_ECHO_PORT, &GPIO_InitStructure); //根据设定参数初始化端口
//接收端口 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
TIM3_Int_Init(49999,7199); //初始化TIM3定时器,计数一次为1/10000S(0.1ms),每500ms触发一次定时中断
//TIM_Cmd(TIM3,DISABLE);
}
//发送20us的脉冲触发信号
void HC_SR04_start(void)
{
GPIO_SetBits(HC_SR04_TRIG_PORT,HC_SR04_TRIG_PIN);
delay_us(20);
GPIO_ResetBits(HC_SR04_TRIG_PORT,HC_SR04_TRIG_PIN);
delay_ms(10);
}
void EXTI9_5_IRQHandler(void)
{
delay_us(10);
if(EXTI_GetITStatus(EXTI_Line5) != RESET)
{
//while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5) == RESET);
TIM_SetCounter(TIM3,0); //计数清零
TIM_Cmd(TIM3,ENABLE); //使能TIM3定时器
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)); //等待电平变为低电平
TIM_Cmd(TIM3,DISABLE); //关闭定时器
Distance = TIM_GetCounter(TIM3)*340/200.0; //计算距离:cnt * 1/10000 * 340 / 2(单位:m)
//printf("Counter:%d\n",TIM_GetCounter(TIM3));
printf("Distance:%f cm\r\n",Distance);
EXTI_ClearITPendingBit(EXTI_Line5);
}
}