本文介绍制作过程中用到的 HC-SR04 模块
实物图片
![file](https://oision.top/wp-content/uploads/2020/07/image-1594906573721.png)
电气参数
![file](https://oision.top/wp-content/uploads/2020/07/image-1594905727858.png)
工作原理
- 采用 IO 触发测距,给至少 10us 的高电平信号;
- 模块自动发送 8 个 40KHz 的方波,自动检测是否有信号返回;
- 有信号返回,通过 I0 输出一高电平,高电平持续的时间就是
- 超声波从发射到返回的时间,测试距离 = ( 高电平时间*声速 ) / 2;
与单片机连接
Vcc -> 5V
Trig -> PB8
Echo -> PB8
Gnd -> G
程序部分
HC-SR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
#include <stm32f10x.h>
#include <delay.h>
// 硬件连接不同可在此处修改
#define HCSR04_PORT GPIOB
#define HCSR04_CLK RCC_APB2Periph_GPIOB
#define HCSR04_TRIG GPIO_Pin_8
#define HCSR04_ECHO GPIO_Pin_7
#define uint unsigned int
void HC_SR04_Init(void);
float Count_Dist(void);
#endif /*__HCSR04_H*/
HC-SR04.c
#include <HC-SR04.h>
//1.采用 IO 触发测距,给至少 10us 的高电平信号;
//2.模块自动发送 8 个 40KHz 的方波,自动检测是否有信号返回;
//3.有信号返回,通过 I0 输出一高电平,高电平持续的时间就是
//4.超声波从发射到返回的时间,测试距离 = ( 高电平时间*声速 ) / 2;
//记录定时器溢出次数
uint count = 0;
//设置中断优先级
void NVIC_Config(void){
NVIC_InitTypeDef NVIC_InitStructer;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructer.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructer.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructer.NVIC_IRQChannel = TIM2_IRQn;
NVIC_Init(&NVIC_InitStructer);
}
/*初始化HC-SR04的GPIO以及定时器TIM2*/
void HC_SR04_Init(void){
GPIO_InitTypeDef GPIO_InitStructer;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructer;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/*TRIG 触发信号 PB8*/
GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructer.GPIO_Pin = HCSR04_TRIG;
GPIO_Init(HCSR04_PORT, &GPIO_InitStructer);
/*ECOH 回响信号 PB7*/
GPIO_InitStructer.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置为浮空输入
GPIO_InitStructer.GPIO_Pin = HCSR04_ECHO;
GPIO_Init(HCSR04_PORT, &GPIO_InitStructer);
/*定时器 TIM2 初始化*/
TIM_DeInit(TIM2);
TIM_TimeBaseInitStructer.TIM_Period = 999; //定时周期为1000
TIM_TimeBaseInitStructer.TIM_Prescaler = 71; //分频系数72
TIM_TimeBaseInitStructer.TIM_ClockDivision = TIM_CKD_DIV1; //不分频
TIM_TimeBaseInitStructer.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructer);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // Clears the TIMx's pending flags.
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // Enables or disables the specified TIM interrupts.
NVIC_Config();
TIM_Cmd(TIM2, DISABLE); //关闭定时器使能
}
float Count_Dist(void){
float distance = 0, sum = 0;
uint16_t time;
uint i = 0;
delay_init(); //在 delay.h 中
while(i < 5){
GPIO_SetBits(HCSR04_PORT, HCSR04_TRIG);
delay_us(20);
GPIO_ResetBits(HCSR04_PORT, HCSR04_TRIG);
while(GPIO_ReadInputDataBit(HCSR04_PORT, HCSR04_ECHO) == 0);
TIM_Cmd(TIM2, ENABLE); //打开定时器
i += 1;
while(GPIO_ReadInputDataBit(HCSR04_PORT, HCSR04_ECHO) == 1); //回响信号消失
TIM_Cmd(TIM2,DISABLE); //关闭定时器
time = TIM_GetCounter(TIM2); //获取 TIM2 计数值
distance = (time + count*1000)/58.0; //通过回响信号计算距离
sum = distance + sum; //将五次结果相加
TIM2->CNT = 0; //将TIM2计数寄存器的计数值清零
count = 0; //中断溢出次数清零
delay_ms(10);
}
distance = sum/5; //取5次的平均值
return distance;
}
void TIM2_IRQHandler(void) { //中断函数
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志
count++;
}
}
代码讲解
关于distance = (time + count*1000)/58.0;
:
这个我最开始也没有搞懂,后来认真想了下明白了:声音在空气中的传播速度约为 343m/s,也就是 34300cm/s. 又因为 1s = 1000000us,可以得到声音的传播速度是 0.0343cm/us. 所以我们可以得到声音传播 1cm 所需要的时间约为 29.154us. 我们设接收到的时间为 n(us),是两倍的时间,那单程的时间就是 n/2(us). 就可以得到单程距离是 n/(2*29.154) = n/58.3 也就是我们用来计算的公式。