4.1 理論分析
4.1.1超聲波概述
人類耳朵能聽到的聲波頻率為20Hz~20000Hz,當聲波的振動頻率小于20Hz或大于20KHz時,我們便聽不見了,因此,我們把頻率高于20000赫茲的聲波稱為“超聲波”(ultrasonic),通常的超聲波頻率為1兆赫茲~5兆赫茲,它的方向性好,穿透能力強,易于獲得較集中的聲能,在水中傳播距離遠,可用于測距、測速、清洗、焊接、碎石、殺菌消毒等,在醫學、軍事、工業、農業上有很多的應用,
超聲波發生器可以分為兩大類:一類是用電氣方式產生超聲波,一類是用機械方式產生超聲波,
電氣方式包括壓電型、磁致伸縮型和電動型等;機械方式有加爾統笛、液哨和氣流旋笛等,它們所產生的超聲波的頻率、功率和聲波特性各不相同,因而用途也各不相同,目前較為常用的是壓電式超聲波發生器,
壓電式超聲波發生器實際上是利用壓電晶體的諧振來作業的,其外觀結構與內部結構分別如圖所示,該傳感器有兩個壓電陶瓷(壓電晶片)和一個共鳴器(共振板), 當其兩極外加脈沖信號,且頻率等于壓電陶瓷的固有振蕩頻率時,壓電陶瓷將會發生共振,并帶動共鳴器振動產生超聲波,反之,如果兩電極間未外加電壓,當共振板接收到超聲波時,將迫使壓電陶瓷振動,將機械能轉換為電信號,這時它就成為超聲波接收器了,

4.1.2超聲波測距作業原理
目前超聲波測距的方法有多種:如時間差測距法、相位測距法、聲波幅值測距法,最常用就是時間差測距法,本文的測距方法就是時間差測距法,
超聲波發射器向某一方向發射超聲波,在發射的同時開始計時,超聲波在空氣中傳播,途中碰到障礙物就立即回傳,超聲波接收器收到反射波就立即停止計時,根據時間差和超聲波的速度可以估算出發射位置到障礙物位置的距離,超聲波在空氣中的傳播速度為340 m/ s,根據計時器記錄的時間t,就可以計算出發射點距障礙物的距離S ,即:S = 340t / 2,這就是所謂的時間差測距法,

超聲波指向性強,在介質中傳播的距離較遠,利用超聲波測距往往比較迅速、方便、計算簡單、易于做到實時控制,并且在測量精度方面能達到工業實用的要求,因此在移動機器人的研制上也得到了廣泛的應用,
超聲波測距傳感器規格很多,測驗距離也從遠到近都有,價格相差也較大,一般機器人愛好者使用的都是測量范圍在最小幾厘米到最大幾米之間,超聲波傳感器一般作用距離較短,普通的有效探測距離都在5-10m之間,精度約5毫米,但是會有一個最小探測盲區,一般在幾十毫米,因為聲波的特性,所以超聲波傳感器受環境影響比較小,使用場合比較廣泛,

超聲波測距的優點在于測量范圍較大,且不是使用光學信號,所以被測物體的顏色對于測量結果沒有影響,但還是常常會受到外界環境的其它因素影響,依靠聲速測距,所以對于一些影響聲速的因素較敏感,比如溫度、風等,最大允許角度較小,在不同的室溫下,聲速是不同的,因而會給測距帶來誤差,所以一般的模塊測距精度都難以小于5毫米,但很自然的會想到采用溫度補償的方式,也即額外再添加一個溫度傳感器,根據不同溫度下的聲速來計算,從而可以補償溫度變化帶來的誤差,表1列出了幾種不同溫度下的聲速,在使用時,如果溫度變化不大,則可認為聲速是基本不變的,如果測距精度要求很高,則應通過溫度補償的方法加以校正,
| 溫度 | -30 | -20 | -10 | 0 | 10 | 20 | 30 | 100 |
|---|---|---|---|---|---|---|---|---|
| 聲速m/s | 313 | 319 | 325 | 323 | 338 | 344 | 349 | 386 |
根據以上資料可以進行線性擬合,得到如下公式:
V = 331.4 + 0.607 T ( 攝 氏 溫 度 ) V = 331.4 + 0.607 T (攝氏溫度) V=331.4+0.607T(攝氏溫度)
式中, T T T為實際溫度單位為 ℃ ℃ ℃, V V V為超聲波在介質中的傳播速度單位為 m / s m/s m/s,
根據測得的實際溫度矯正環境中的真實聲速,以減小測量誤差,這樣的模塊測距精度就可以達到1~2毫米了,而且同時能當做溫度傳感器來使用,但價格也會稍高,
超聲波測距傳感器可能是機器人中運用最為廣泛的傳感器之一了,它不僅便宜,擁有豐富的庫,而且測量結果也較為可靠,我們都知道蝙蝠夜行時躲避障礙物的原理,下面以一款市面上最為成熟和常見的超聲波測距模塊HC-SR04為例來說明,

當然啦,如果環境比較復雜,建議使用帶有溫度補償的模塊,這里推薦US-100模塊,該超聲波模塊沒有自帶溫度補償,以340m/s的室溫下聲速作為參斬訓準,同個擴展庫捕捉脈沖寬度獲得超聲波來回的時間后,通過簡單的運算獲得距離,
4.1.3 HC-SR04 測距原理
HC-SR04超聲波測距模塊可提供2cm-400cm的非接觸式距離感測功能,HC-SR04模塊性能穩定,測度距離精確,能和國外的SRF05、SRF02等超聲波測距模塊相媲美,模塊高精度,盲區(2cm)超近,測距精度可達3mm(這個是廠家介紹,一般測距要3cm以上,精度有時要到達5-6mm),包括發射器、接收器與控制電路,它是一種壓電式傳感器,利用電致伸縮現象而制成,在壓電材料切片上(如石英晶體、壓電陶瓷、鈦酸鉛鋇等)施加交變電壓,使它產生電致伸縮振動而產生超聲波,當外加交變電壓的頻率等于晶片的固有頻率而產生共振,這時產生的超聲波最強,壓電式超聲波接收器一般是利用超聲波發生器的逆效應進行作業的,其結構和超聲波發生器基本相同,有時就用同一個換能器兼作發生器和接收器兩種用途,當超聲波作用到壓電晶片上時使晶片伸縮,在晶片的兩個界面上便產生交變電荷后轉換成電壓經放大送到測量電路,最后記錄或顯示出來,
HC-SR04超聲波測距模塊的基本作業原理:
(1)采用IO口TRIG 觸發測距,給最少10us的高電平信號,
(2)模塊自動發送8個40khz的方波,自動檢測是否有信號回傳;
(3)有信號回傳,通過IO口ECHO輸出一個高電平,高電平持續的時間就是超聲波從發射到回傳的時間,測驗距離=(高電平時間*聲速(340M/S))/2,
HC-SR04測距時序圖如下:

以上時序圖表明你只需要提供一個 10uS 以上脈沖觸發信號,該模塊內部將發出 8 個 40kHz 周期電平并檢測回波,一旦檢測到有回波信號則輸出回響信號,回響信號的脈沖寬度與所測的距離成正比,由此通過發射信號到收到的回響信號時間間隔可以計算得到距離,建議測量周期為 60ms 以上,以防止發射信號對回響信號的影響,
【注1】此模塊不宜帶電連接, 若要帶電連接, 則先讓模塊的 GND 端先連接, 否則會影響模塊的正常作業,
【注2】測距時,被測物體的面積不少于 0.5 平方米且平面盡量要求平整,否則影響測量的結果,
4.2 實驗詳解
4.2.1實驗目的
1.通過實驗掌握STM32 芯片定時器的配置方法
2.掌握超聲波的測距原理
4.2.2實驗設備
硬體:PC 機一臺;STM32開發板一套; 超聲波模塊一個
軟體:Windows 10系統,Keil5集成開發環境
4.2.3硬體連接
HC-SR04模塊電路圖如下圖所示:

TL074:四路低噪聲 JFET 輸入通用運算放大器,放大接收信號及控制,
MAX232:MAX232芯片是美信(MAXIM)公司專為RS-232標準串口設計的單電源電平轉換芯片,使用+5v單電源供電,

超聲波模塊利用232芯片能夠輸出正負壓信號給超聲波發生器件供電,以此來達到最大電壓差(約13~14V)給超聲波器件供電,增大超聲波發送功率,
STC11:STC單片機,處理邏輯信號,
STM32與HC-SR04模塊連接示意圖如下:

4.2.4 Lib_V3.5.0庫實作
根據HC-SR04 測距原理,測距流程如下:TRIG發一個10us以上的高電平,ECHO等待高電平輸出,一有輸出就可以開定時器計時,當ECHO檢測到低電平時就可以讀定時器的值,此時就為此次測距的時間,方可算出距離,具體流程圖如下:

距離的計算公式如下:
距 離 = ( 高 電 平 時 間 ? 聲 速 ( 340 M / S ) ) / 2 ( 單 位 : m ) 距離 = (高電平時間*聲速(340M/S))/2(單位:m) 距離=(高電平時間?聲速(340M/S))/2(單位:m)
接下來看看代碼是如何實作的,這里只講HC-SR04 測距相關的內容,
1.GPIO和定時器初始化
/**
* @brief 初始化定時器3
* @param None
* @retval None
*/
void HCRS04_Tim3_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_ICInitTypeDef TIM3_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//TIM_DeInit(TIM3);//復位TIM3定時器
TIM_TimeBaseStructure.TIM_Period = 7199;
TIM_TimeBaseStructure.TIM_Prescaler = 199;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, & TIM_TimeBaseStructure);
//初始化Timer3_CH1(PA6)為PWM輸出
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈沖寬度調制模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = 7150; //設定待裝入捕獲比較暫存器的脈沖值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根據TIM_OCInitStruct中指定的引數初始化外設TIMx
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //CH1預裝載使能
//初始化Timer3_CH2(PA7)為脈寬捕獲
TIM3_ICInitStructure.TIM_Channel = TIM_Channel_2; //選擇輸入端
TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM3_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM3, &TIM3_ICInitStructure);
}
/**
* @brief 初始化超聲波IO口
* @param None
* @retval None
*/
void HCRS04_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; /*PA6--TRIG1*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /*復用推挽輸出*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; /*PA7--ECHO3*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;/*浮空輸入模式*/
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief 初始化超聲波中斷
* @param None
* @retval None
*/
void HCRS04_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//使能定時器3通道2捕獲中斷
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占優先級1級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優先級0級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
TIM_ITConfig(TIM3, TIM_IT_CC2,ENABLE); //不允許更新中斷,允許CC2IE捕獲中斷
TIM_Cmd(TIM3, ENABLE); //使能TIM3
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
//TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
}
關于GPIO的初始化沒啥好說的,PA6做輸出,對應的是TRIG,用于觸發測距信號;PA7作為輸入,對應的是ECHO,需要進行輸入捕獲來獲取高電平的時間,
這里需要配置定時器3,這里使用PWM輸出來觸發測距信號,
根據前面的引數配置,我們可以算出TIM的輸出周期:
T I M = 1 / ( T c l k / ( p s c + 1 ) ) ? ( a r r + 1 ) TIM=1/(Tclk/(psc+1))*(arr+1) TIM=1/(Tclk/(psc+1))?(arr+1)
arr=7199 psc=199 Tclk=72Mhz
T I M = 1 / ( 72 M h z / ( 200 ) ) ? ( 7200 + 1 ) = 20 m s TIM=1/(72Mhz/(200))*(7200+1)=20ms TIM=1/(72Mhz/(200))?(7200+1)=20ms
占空比為 7150 / 7200 = 99 7150/7200=99% 7150/7200=99,因此完全能達到觸發的要求,
2.定時器的通道2進行脈寬捕獲,獲取高電平時間
/**
* @brief This function handles TIM3 interrupt request.
* @param None
* @retval None
*/
void TIM3_IRQHandler(void)
{
if ((TIM3CH2_CAPTURE_STA & 0X80) == 0) //還未成功捕獲
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) //捕獲2發生捕獲事件
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2); //清除中斷標志位
if (TIM3CH2_CAPTURE_STA & 0X40) //捕獲到一個下降沿
{
TIM3CH2_CAPTURE_DOWNVAL = TIM_GetCapture2(TIM3);//記錄下此時的定時器計數值
if (TIM3CH2_CAPTURE_DOWNVAL < TIM3CH2_CAPTURE_UPVAL)
{
TIM3_T2 = 65535;
}
else
TIM3_T2 = 0;
tempup = TIM3CH2_CAPTURE_DOWNVAL - TIM3CH2_CAPTURE_UPVAL + TIM3_T2; //得到總的高電平的時間
TIM3CH2_CAPTURE_STA = 0; //捕獲標志位清零
TIM_OC2PolarityConfig(TIM3, TIM_ICPolarity_Rising); //設定為上升沿捕獲
}
else //發生捕獲時間但不是下降沿,第一次捕獲到上升沿,記錄此時的定時器計數值
{
TIM3CH2_CAPTURE_UPVAL = TIM_GetCapture2(TIM3); //獲取上升沿資料
TIM3CH2_CAPTURE_STA |= 0X40; //標記已捕獲到上升沿
TIM_OC2PolarityConfig(TIM3, TIM_ICPolarity_Falling);//設定為下降沿捕獲
}
//計算距離
distance = tempup * 0.034; //標準值0.034
}
}
}
定時器的周期是20ms,因此最終的得到的距離是:
距 離 = ( T I M 3 的 計 數 器 ? 20 / 1000 s ? 聲 速 ( 340 m / s ) ) / 200 = T I M 3 的 計 數 器 ? 0.034 ( 單 位 : c m ) 距離 = (TIM3的計數器*20/1000s*聲速(340m/s))/200 = TIM3的計數器*0.034(單位:cm) 距離=(TIM3的計數器?20/1000s?聲速(340m/s))/200=TIM3的計數器?0.034(單位:cm)
3.最后,給出主函式的代碼
/**
* @brief main
* @param None
* @retval
*/
int main(void)
{
/*時鐘初始化*/
SysTick_Init();
/* USART1 配置模式為 115200 8-N-1,中斷接收 */
USART1_Config();
/* 超聲波初始化*/
HCRS04_Config();
printf("超聲波測距\r\n");
while(1)
{
Delay_ms(1000);
printf("Distance: %f cm\r\n",distance);
}
}
到此,HC-SR04 的測距基本就實作了,筆者在測驗的時候發現,實際測驗程序中有個別資料會例外偏小,這是由于HC-SR04 的“余震”導致的,具體解釋如下:
1.電路串擾,超聲波發射時的瞬間電流很大,例如某種工業級連續測距產品瞬間電流會有15A,通常的產品也能達到1A,瞬間這么大的電流會對電源有一定影響,并干擾接收電路,通過改善電源設計可以緩解這種情況,但在低成本設計中很難根除,所以每次發射完畢,接收電路還需要一段時間穩定作業狀態,因此,在沒穩定前,HC-SR04 的測的距離和實際的距離差別較大,
2.探頭的余震,即使是分體式的,發射頭作業完后還會繼續震一會,這是物理效應,也就是余震,這個余震信號也會向外傳播,如果你的設計是發射完畢后立刻切換為接收狀態(無盲區),那么這個余震波會通過殼體和周圍的空氣,直接到達接收頭、干擾了檢測,
注:通常的測距設計里,發射頭和接收頭的距離很近,在這么短的距離里超聲波的檢測角度是很大的,可達180度,
2.殼體的余震,就像敲鐘一樣,能量仍來自發射頭,發射結束后,殼體的余震會直接傳導到接收頭,當然這個時間很短,但已形成了干擾,另外,在不同的環境溫度下,殼體的硬度和外形會有所變化,其余震有時長、有時短、有時干擾大、有時干擾小,這是設計工業級產品時必須要考慮的問題,
總之,就是HC-SR04 的各種不穩定因素導致測量精度偏差較大,消除上述現象最常用的方法就是在檢測的時候多次回圈檢測,取平均值,
重新修改定時器3的內容,代碼如下:
/**
* @brief This function handles TIM3 interrupt request.
* @param None
* @retval None
*/
void TIM3_IRQHandler(void)
{
if ((TIM3CH2_CAPTURE_STA & 0X80) == 0) //還未成功捕獲
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) //捕獲2發生捕獲事件
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2); //清除中斷標志位
if (TIM3CH2_CAPTURE_STA & 0X40) //捕獲到一個下降沿
{
TIM3CH2_CAPTURE_DOWNVAL = TIM_GetCapture2(TIM3);//記錄下此時的定時器計數值
if (TIM3CH2_CAPTURE_DOWNVAL < TIM3CH2_CAPTURE_UPVAL)
{
TIM3_T2 = 65535;
}
else
TIM3_T2 = 0;
tempup = TIM3CH2_CAPTURE_DOWNVAL - TIM3CH2_CAPTURE_UPVAL + TIM3_T2; //得到總的高電平的時間
TIM3CH2_CAPTURE_STA = 0; //捕獲標志位清零
TIM_OC2PolarityConfig(TIM3, TIM_ICPolarity_Rising); //設定為上升沿捕獲
}
else //發生捕獲時間但不是下降沿,第一次捕獲到上升沿,記錄此時的定時器計數值
{
TIM3CH2_CAPTURE_UPVAL = TIM_GetCapture2(TIM3); //獲取上升沿資料
TIM3CH2_CAPTURE_STA |= 0X40; //標記已捕獲到上升沿
TIM_OC2PolarityConfig(TIM3, TIM_ICPolarity_Falling);//設定為下降沿捕獲
}
//計算距離
sum += tempup * 0.034; //標準值0.034
if(count == 5)
{
distance = sum/5.0;
count = 0;
sum = 0.0;
}
else
{
count ++;
}
}
}
}
當然啦,有興趣的朋友可以深入研究,還可以更精度更高的測距模塊,
4.2.5 HAL庫實作
我們在串口的例子的基礎上進行配置,
串口通信(HAL庫)
這里需要配置TIM,關于TIM的內容,請參考筆者以前的博文:
TIM
由STM32與HC-SR04模塊的硬體連接關系圖,PA6做輸出,對應HC-SR04模塊的TRIG,用于觸發測距信號;PA7作為輸入,對應HC-SR04模塊的ECHO,需要進行輸入捕獲來獲取高電平的時間,那么接下來來一一配置,
1.設定RCC
設定高速外部時鐘HSE,選擇外部時鐘源,

2.時鐘配置
筆者的板子使用的外部晶振為8MHz,選擇外部時鐘HSE 8MHz ,PLL鎖相環9倍頻后為72MHz,系統時鐘來源選擇為PLL,設定APB1分頻器為 /2,這時候定時器的時鐘頻率為72Mhz,本文筆者使用的定時器是TIM3,TIM3掛在APB1上,不同的定時器掛在不同總線上的,

【注】TIM3的時鐘源有兩個選項
選項1 :Internal Clock 內部時鐘
選項2 : ETR2 外部觸發輸入(ETR)(僅適用TIM2,3,4)
3.GPIO配置
上文使用PWM最為輸出來觸發測距信號,這里通過控制GPIO的高低電平來觸發測距信號,

將PA6配置為輸出即可,
4.TIM3配置
本文要使用TIM3的通道2,因此需要將其使能,每個通道有很多模式,通道2選擇輸入捕獲模式,當對應的通道打開后,對應的GPIO也會被使能,

【注】如果使能通道前通道中GPIO使用過,STM32CubeMX會自動將GPIO配置為重映射的GPIO,舉個例子,當PB0被占用了,那么四個GPIO會重映射到PC6-PC9,
TIM引數配置如下:
- Counter setting
Prtscaler (定時器分頻系數) : 199
Counter Mode(計數模式) :Up(向上計數模式)
Counter Period(自動重裝載值) : 7199
CKD(時鐘分頻因子) : No Division 不分頻
選項: 可以選擇二分頻和四分頻
auto-reload-preload(自動重裝載) : Enable 使能
- TRGO Output (TRGO) Parameters
Master/Slave Mode(MSM bit):Disable
TRGO:定時器的觸發信號輸出 在定時器的定時時間到達的時候輸出一個信號(如:定時器更新產生TRGO信號來觸發ADC的同步轉換,)
- Input Capture Channel (CH2)
Polarity Selection(輸入捕獲模式): Rising Edge(上升沿捕獲)
IC Selection (映射模式):Direct(直接映射)
Prescaler Division Ratio(分頻模式):No division(不分頻)
Input Filter(配置輸入濾波器):(不濾波)

根據前面的引數配置,arr=999 psc=0 Tclk=72Mhz ,
T I M = 1 / ( 72 M h z / ( 200 ) ) ? ( 7199 + 1 ) = 20 m s TIM=1/(72Mhz/(200))*(7199+1)=20ms TIM=1/(72Mhz/(200))?(7199+1)=20ms
和使用Lib_V3.5.0庫的計算結果是一樣的,
還需要開啟中斷,

好了,到這里,配置就完成了,生成工程就行了,
下面看看測距的代碼是如何實作的,
1.初始化GPIO、TIM3等
這部分是自動生成的,這里就不貼出來了
2.TRIG發一個10us以上的高電平
static void HC_SR04_Delayus ( __IO uint32_t ulCount )
{
uint32_t i;
for ( i = 0; i < ulCount; i ++ )
{
uint8_t uc = 12; //設定值為12,大約延1微秒
while ( uc -- ); //延1微秒
}
}
static void HC_SR04_Start_signal(void)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
//延時20us
HC_SR04_Delayus(20);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
}
通過GPIO的讀寫來觸發測距信號,延時就用個簡單延時就可以,不需要太精確,
3.輸入捕獲,得到高電平的時間
/**
* @brief This function handles TIM3 Capture Callback.
* @param None
* @retval None
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
TIM_IC_InitTypeDef sConfigIC = {0};
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if(htim->Instance == htim3.Instance)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
if(sCapture.ChannelEdge == 0) //捕獲上升沿
{
sCapture.RisingTime = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2); //獲取上升沿時間點
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;//切換捕獲極性
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2); //切換捕獲極性后需重新啟動
sCapture.ChannelEdge = 1; //上升沿、下降沿捕獲標志位
}
else if(sCapture.ChannelEdge == 1) //捕獲下降沿
{
sCapture.FallingTime = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2); //獲取下降沿時間點
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;//切換捕獲極性
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2); //切換捕獲極性后需重新啟動
sCapture.HighTime = sCapture.FallingTime < sCapture.RisingTime ? sCapture.FallingTime + 0xffff - sCapture.RisingTime + 1 : sCapture.FallingTime - sCapture.RisingTime;
//清零
sCapture.ChannelEdge = 0;
sCapture.FinishFlag = 1;
//高電平持續時間 = 下降沿時間點 - 上升沿時間點
//計算超聲波測量距離
distance = (float)sCapture.HighTime * 0.034;
}
}
}
}
4.最后在主函式中列印輸出距離資訊,
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
/*使能定時3*/
// 啟動輸入捕獲并開啟中斷
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
printf("超聲波測距\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HC_SR04_Start_signal();
/* 完成測量高電平脈寬 */
if(sCapture.FinishFlag)
{
printf("Distance: %f cm\r\n",distance);
sCapture.FinishFlag = 0;
HAL_Delay(1000);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
注意一定開啟輸入捕獲中斷,其他就沒事好說了,
4.2.6實驗現象
不管是使用何種庫,結果都是一樣的,


代碼獲取方法
1.長按下面二維碼,關注公眾號[嵌入式實驗樓]
2.在公眾號回復關鍵詞[STM32F1]獲取資料

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286475.html
標籤:其他
上一篇:在物聯網中解讀投資回報(一)
