超声波测距方案
来源 : 未知 2018-03-26
首先声明一下实现该方案的硬件平台:STM32F103RCT6+HC-SR04超声波测距模块。
1.应用场景
应用于智能小车测距可以及时发现前方的障碍物,使智能小车可以及时转向,避开障碍物。
2.HC-SR04超声波测距模块的使用说明
2.1 HC-SR04超声波测距的的基本参数
图1 HC-SR04超声波测距模块的产品介绍
图2 HC-SR04超声波测距模块的电气参数
以上图片是某宝提供的模块说明书内容,但在实际调试过程中发现有些参数和他说明书里面提供的参数还是有差别的,主要体现在这两方面:1.HC-SR04超声波测距模块的最大测距只有3米多,达不到4m,最小测距低于3cm就测不准了2.该超声波模块的供电电压可以是3.3V,3.3V供电,该模块也能正常工作。
2.2HC-SR04超声波测距的工作原理
图3超声波测距模块的实物图
我们从图三可以看到该超声波测距模块总共有4个引脚,分别是VCC,TRIG,ECHO,GND这四个引脚,其中VCC-GND是该模块的电源引脚,TRIG引脚为触发信号输入,ECHO为回响信号输出,需要我们控制的是TRIG和ECHO这两个引脚。
图4 HC-SR04超声波测距模块的基本工作原理
图5 HC-SR04测距模块的基本时序
根据图4和图5我们可以很清晰的知道HC-SR04的工作原理:
1.给超声波模块上电,这里我使用的3.3V供电。
2..给TRIG引脚(触发信号输入)一个至少持续10us的高电平信号,触发这个模块开始进行测距,这里我选择通过STM32的GPIO口给TRIG这个引脚一个持续20us的高电平信号。
3.HC-SR04超声波测距模块的TRIG引脚接受到这个触发测距信号后,模块会自动发射出8个40Khz的方波,并且同时ECHO引脚(回响信号输出)会自动由0变为1,这里我使用STM32的GPIO口作为输入检测到ECHO引脚为高电平时则开启定时器开始计数。
4.当超声波接受到测距返回信号后,ECHO引脚会自动由1变为0,STM32检测到ECHO引脚有下降沿产生时,定时器则停止计数。定时器记下的时间就是超声波从发射到返回的总时长。
5.距离计算公式:ECHO高电平的持续时间*声速(340M/S)/2=距离
3.硬件连接
VCC---------->3.3V
GND---------->GND
TRIG--------->PB5
ECHO--------->PB6
4.软件实现
1. //超声波测距
2.
3. #include "hcsr04.h"
4.
5. #define HCSR04_PORT GPIOB
6. #define HCSR04_CLK RCC_APB2Periph_GPIOB
7. #define HCSR04_TRIG GPIO_Pin_5
8. #define HCSR04_ECHO GPIO_Pin_6
9.
10. #define TRIG_Send PBout(5)
11. #define ECHO_Reci PBin(6)
12.
13. u16 msHcCount = 0;//ms计数
14.
15. void Hcsr04Init()
16. {
17. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //生成用于定时器设置的结构体
18. GPIO_InitTypeDef GPIO_InitStructure;
19. RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
20.
21. //IO初始化
22. GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG; //发送电平引脚
23. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
25. GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
26. GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
27.
28. GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO; //返回电平引脚
29. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
30. GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
31. GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);
32.
33. //定时器初始化 使用基本定时器TIM6
34. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //使能对应RCC时钟
35. //配置定时器基础结构体
36. TIM_DeInit(TIM2);
37. TIM_TimeBaseStructure.TIM_Period = (1000-1); //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到1000为1ms
38. TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //设置用来作为TIMx时钟频率除数的预分频值 1M的计数频率 1US计数
39. TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
40. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
41. TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
42.
43. TIM_ClearFlag(TIM6, TIM_FLAG_Update); //清除更新中断,免得一打开中断立即产生中断
44. TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //打开定时器更新中断
45. hcsr04_NVIC();
46. TIM_Cmd(TIM6,DISABLE);
47. }
48.
49.
50. //tips:static函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明
51. static void OpenTimerForHc() //打开定时器
52. {
53. TIM_SetCounter(TIM6,0);//清除计数
54. msHcCount = 0;
55. TIM_Cmd(TIM6, ENABLE); //使能TIMx外设
56. }
57.
58. static void CloseTimerForHc() //关闭定时器
59. {
60. TIM_Cmd(TIM6, DISABLE); //使能TIMx外设
61. }
62.
63.
64. //NVIC配置
65. void hcsr04_NVIC()
66. {
67. NVIC_InitTypeDef NVIC_InitStructure;
68. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
69.
70. NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; //选择串口1中断
71. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占式中断优先级设置为1
72. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应式中断优先级设置为1
73. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
74. NVIC_Init(&NVIC_InitStructure);
75. }
76.
77.
78. //定时器6中断服务程序
79. void TIM6_IRQHandler(void) //TIM3中断
80. {
81. if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
82. {
83. TIM_ClearITPendingBit(TIM6, TIM_IT_Update ); //清除TIMx更新中断标志
84. msHcCount++;
85. }
86. }
87.
88.
89. //获取定时器时间
90. u32 GetEchoTimer(void)
91. {
92. u32 t = 0;
93. t = msHcCount*1000;//得到MS
94. t += TIM_GetCounter(TIM6);//得到US
95. TIM6->CNT = 0; //将TIM2计数寄存器的计数值清零
96. Delay_Ms(50);
97. return t;
98. }
99.
100.
101. //一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
102. //为了消除余震的影响,取五次数据的平均值进行加权滤波。
103. float Hcsr04GetLength(void )
104. {
105. u32 t = 0;
106. int i = 0;
107. float lengthTemp = 0;
108. float sum = 0;
109. while(i!=5)
110. {
111. TRIG_Send = 1; //发送口高电平输出
112. Delay_Us(20);
113. TRIG_Send = 0;
114. while(ECHO_Reci == 0); //等待接收口高电平输出
115. OpenTimerForHc(); //打开定时器
116. i = i + 1;
117. while(ECHO_Reci == 1);
118. CloseTimerForHc(); //关闭定时器
119. t = GetEchoTimer(); //获取时间,分辨率为1US
120. lengthTemp = ((float)t/58.0);//cm
121. sum = lengthTemp + sum ;
122.
123. }
124. lengthTemp = sum/5.0;
125. return lengthTemp;
126. }
127.
128.
129. /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
130. ** 函数名称: Delay_Ms_Ms
131. ** 功能描述: 延时1MS (可通过仿真来判断他的准确度)
132. ** 参数描述:time (ms) 注意time<65535
133. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
134. void Delay_Ms(uint16_t time) //延时函数
135. {
136. uint16_t i,j;
137. for(i=0;i<time;i++)
138. for(j=0;j<10260;j++);
139. }
140. /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
141. ** 函数名称: Delay_Ms_Us
142. ** 功能描述: 延时1us (可通过仿真来判断他的准确度)
143. ** 参数描述:time (us) 注意time<65535
144. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
145. void Delay_Us(uint16_t time) //延时函数
146. {
147. uint16_t i,j;
148. for(i=0;i<time;i++)
149. for(j=0;j<9;j++);
150. }
Main函数的实现
1. #include "hcsr04.h"
2.
3. int main()
4. {
5.
6. float length;
7.
8. GPIO_cfg();
9. NVIC_cfg();
10. USART_cfg();
11. printf("串口初始化成功!\n");
12.
13. Hcsr04Init();
14. printf("超声波初始化成功!\n");//测试程序是否卡在下面两句上面
15.
16. length = Hcsr04GetLength();
17. printf("距离为:%.3f\n",length);
18.
19.
20. }
5.测试结果
图6 HC-SR04超声波测距模块测试结果
在这里初始化TIM6为每1ms进入一次中断进行时间计数
图7定时器初始化
HCSR04超声波测距模块的时序实现代码
图8 超声波测距模块时序实现代码
6.注意事项
1.如果要连续多次测量距离,要注意测量周期为 60ms以上, 以防止发射信号对回响信号的影响。
2.此模块不宜带电连接, 若要带电连接, 则先让模块的GND端先连接, 否则会影响 模块的正常工作。
3.测距时,被测物体的面积不少于0.5平方米且平面尽量要求平整,否则影响测量的结果