首先聲明一下實現該方案的硬件平臺: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平方米且平面盡量要求平整,否則影響測量的結果