Abstract: 超声波测距模块HC-SR04模块的代码,提供两种思路

模块介绍

懒得写,看这里

代码部分

思路一

  • 用 15us 的高电平启动模块
  • 使用间断计时的方法,用每隔一段时间(这里是 10us)触发一次的中断进行计时
  • 用计时的值和声速算出距离

HCSR04.h

1
2
3
4
5
6
7
8
#ifndef __HCSR04_H
#define __HCSR04_H

void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);

#endif

HCSR04.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#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

1
2
3
4
5
6
7
8
#ifndef __HCSR04_H
#define __HCSR04_H

void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);

#endif

HCSR04.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#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);

  • 也可以使用定时器的输入捕获实现功能,但是精度不理想且受外部影响较大,这里就不考虑了