過去在研究 Arduino 時,曾使用 HC-SR04 超音波測距進行距離實測:Arduino筆記(19):HC-SR04超音波測距模組,這篇在我的部落格中瀏覽量一直排名很前面,表示多數學習自動控制的朋友都想瞭解如何使用 HC-SR04 進行測距,現在改用 STM32F1 的開發板來實做測距功能。
模組工作原理:
TRIG 引腳負責「觸發」送出超音波,一開始將這個引腳設定為高電位 10 us,此時 HC-SR04 發送 8 個 40 kHz 週期的音波;在發送了一個音波後,ECHO 引腳會變為高電位,這時 STM32 開始定時器計時,ECHO 引腳用於距離測量,在檢測到回傳的超音波後,ECHO 引腳的變為低電位,這時再讀取定時器的時間,兩個時間差就是音波來回的時間。
距離=(ECHO 高電位時間 x 超音波速度(空氣中的聲速 340 m/sec)/2
以下先不呼叫函式,直接以音波在空氣中傳遞的大約速度,算出來回測得的時間,乘以空氣中傳遞聲音的速度,算出距離。程式中有一行 lengthTemp = ((float)t/58.2); 各位會不會覺得奇怪,為什麼要除以 58.2 ? 那是因為在攝氏零度之海平面音速約為 331.5公尺/秒,每升高 1 攝氏度,音速就增加 0.607 公尺/秒,可以列出一個公式:
• 音速 c = 331.5 + 0.607 * t (其中 t 為攝氏溫度)。
以攝氏 20 度為例:音速約為 331.5 + 0.607 * 20 = 343.64 公尺/秒
- 音速公尺/秒 換算成 公分/微秒:343.64 * 100 / 1000000 = 0.034364 公分/微秒,亦即
- 音速每公分需要 29.1 微秒: 1 / 0.034364 = 29.1 微秒/公分
- 來回的距離,單程距離需再將測得的距離 / 2 ,得到 / 29.1 在除以 2 = / 58.2
HC-SR04.c 的程式內容如下:
HC-SR04.h 程式內容如下:
[超音波測距 HC-SR04]
HC-SR04 技術規格:- 電源:DC5V/2mA
- 輸出電位( 1/ 0):5V/ 0V
- 精度:3mm
- 距離範圍:2 ~ 450cm
- 有效的角度:<15℃
- 觸發輸入信號:10uS TTL pulse
- ECHO輸出信號: Input TTL lever signal and the range in proportion
- 接線方式:VCC、trig (控制端)、echo (接收端)、GND
模組工作原理:
- 採用I/O觸發測距,給至少為 10us 的高電位信號
- 模組自動發送 8 個 40KHZ 的方波,自動檢測是否有信號返回
- 有信號返回,通過 I/O 輸出一高電位,高電位持續的時間就是超音波從發射到返回的時間
- 測試距離 = (高電位時間 x 聲速 (343.2 米/秒)) / 2
TRIG 引腳負責「觸發」送出超音波,一開始將這個引腳設定為高電位 10 us,此時 HC-SR04 發送 8 個 40 kHz 週期的音波;在發送了一個音波後,ECHO 引腳會變為高電位,這時 STM32 開始定時器計時,ECHO 引腳用於距離測量,在檢測到回傳的超音波後,ECHO 引腳的變為低電位,這時再讀取定時器的時間,兩個時間差就是音波來回的時間。
距離=(ECHO 高電位時間 x 超音波速度(空氣中的聲速 340 m/sec)/2
以下先不呼叫函式,直接以音波在空氣中傳遞的大約速度,算出來回測得的時間,乘以空氣中傳遞聲音的速度,算出距離。程式中有一行 lengthTemp = ((float)t/58.2); 各位會不會覺得奇怪,為什麼要除以 58.2 ? 那是因為在攝氏零度之海平面音速約為 331.5公尺/秒,每升高 1 攝氏度,音速就增加 0.607 公尺/秒,可以列出一個公式:
• 音速 c = 331.5 + 0.607 * t (其中 t 為攝氏溫度)。
以攝氏 20 度為例:音速約為 331.5 + 0.607 * 20 = 343.64 公尺/秒
- 音速公尺/秒 換算成 公分/微秒:343.64 * 100 / 1000000 = 0.034364 公分/微秒,亦即
- 音速每公分需要 29.1 微秒: 1 / 0.034364 = 29.1 微秒/公分
- 來回的距離,單程距離需再將測得的距離 / 2 ,得到 / 29.1 在除以 2 = / 58.2
[材料]
- STM32F103C8T6 主板 x 1
- OLED SSD1306 顯示器 x 1
- STLINK V2 模擬下載器 x 1
- HC-SR04 超音波測距 x 1
- 麵包板 x 1
- 連接線 x N 條
[接線與電路圖]
HC-SR04 分別接 3.3v 及 GND,中間的接腳 TRIG 和 ECHO 分別接在 STM32F103x 的 PA0、PA1,與 SSD1306 連接的方式如下:STM32F103x | SSD1306 OLED | HC-SR04 |
---|---|---|
3.3v | VDD | VCC |
GND | GND | GND |
PB8 | SCK/SCL | - |
PB9 | SDA | - |
PA0 | - | TRIG |
PA1 | - | ECHO |
[程式]
主程式 main.c 如下:#include "stm32f10x.h" #include "delay.h" #include "oled.h" #include "hcsr04.h" uint16_t Second; int main (void) { u32 vtg; u32 distance; delay_init(); //延時函數初始化 OLED_Init(); //初始化OLED HCSR04_Init(); OLED_Clear(0); //清除螢幕 OLED_ShowString(2, 2, "Dist.:",16); while (1) { distance = HCSR04GetLength(); OLED_ShowNum(52, 2, distance/100, 4, 16); //整數位 OLED_ShowString(85, 2, ".",16); vtg = distance % 100; //小數點後兩位 if (vtg < 10) { OLED_ShowNum(93, 2, vtg, 2, 16); OLED_ShowString(93, 2,"0",16); //個位數時前面補0 } else { OLED_ShowNum(93, 2, vtg, 2, 16); } delay_ms(200); } }
HC-SR04.c 的程式內容如下:
#include "stm32f10x.h" #include "hcsr04.h" #include "delay.h" u16 msHcCount = 0; //ms計數 void HCSR04_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定義計時器結構體 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //IO 初始化 GPIO_InitStructure.GPIO_Pin = HCSR04_TRIG; //發送電位引腳 Trig GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出 GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG); GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO; //返回電位引腳 Echo GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO); //計時器初始化 使用基本計時器TIM3 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //啟動TIM3 RCC時鐘 TIM_DeInit(TIM3); TIM_TimeBaseStructure.TIM_Period = 65535; //(1000-1); //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值 計數到1000為1ms TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //設置用來作為TIMx時鐘頻率除數的預分頻值 1M的計數頻率 1US計數 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //不分頻 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位 TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除更新中斷,避免立即產生中斷 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //啟用計時器更新中斷 NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //設定中斷服務 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //主要優先順序 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //次要優先順序 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //啟用中斷 NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM3,DISABLE); } //計時器中斷服務程式 void TIM3_IRQHandler(void) //TIM3中斷 { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查TIM更新中斷是否發生 { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIM更新中斷標誌 msHcCount++; } } //開始計數 static void StartTimer() //打開計時器 { TIM_SetCounter(TIM3,0); //清除計數 msHcCount = 0; TIM_Cmd(TIM3, ENABLE); //啟用TIM3外設 } static void StopTimer() //關閉計時器 { TIM_Cmd(TIM3, DISABLE); //啟用TIMx外設 } //獲取計時器時間 float GetEchoTimer(void) { u32 t = 0; t = msHcCount*1000; //得到MS t += TIM_GetCounter(TIM3); //得到US TIM3->CNT = 0; //將TIM3計數寄存器的計數值清零 delay_ms(50); return t; } //取五次數據平均值 float HCSR04GetLength(void) { u32 t = 0; int i = 0; float lengthTemp = 0; float sum = 0; float distance; for(i=0;i<5;i++){ TRIG_SEND = 1; //發送口高電平輸出 delay_us(20); TRIG_SEND = 0; while(ECHO_RECEIVE == 0); //等待接收口高電平輸出 StartTimer(); //打開計時器 while(ECHO_RECEIVE == 1); StopTimer(); //關閉計時器 t = GetEchoTimer(); //獲取時間,解析度為1US lengthTemp = ((float)t/58.2); //cm sum = lengthTemp + sum ; } distance = 100*sum/5.0; return distance; }
HC-SR04.h 程式內容如下:
#ifndef __HCSR04_H #define __HCSR04_H #define HCSR04_TRIG GPIO_Pin_0 #define HCSR04_ECHO GPIO_Pin_1 #define HCSR04_PORT GPIOA #define TRIG_SEND PAout(0) #define ECHO_RECEIVE PAin(1) void HCSR04_Init(void); static void OpenTimerForHc(void); void TIM3_IRQHandler(void); float GetEchoTimer(void); float HCSR04GetLength(void); #endif完整的程式請參考 Github:13. HC-SR04 measure distance
張貼留言