過去在研究 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


張貼留言