基于STM32的超声波雷达项目【可拟合构建平面地图】(代码开源)
????????前言:本文為手把手教學(xué)基于STM32的超聲波雷達項目——HC-SR04雷達。本次項目采用的是STM32作為MCU,搭配常用的HC-SR04超聲波模塊與舵機SG90實現(xiàn)模擬雷達檢測的效果。模擬了雷達圖UI可以擬合構(gòu)建當(dāng)前環(huán)境下的平面地圖(超低配版SLAM構(gòu)圖)。本項目可能還存在可以改進與升級的地方,歡迎大家交流討論。(文末有代碼開源!)
????????實驗硬件:STM32F103ZET6;8針2.4寸TFT-LCD(320×240);HC-SR04;SG90
????????硬件實物圖:
????????效果圖:
引腳連接:
LCD顯示引腳:
VCC --> 3.3V
GND --> GND
CS --> PB11
Reset --> PB12
DC --> PB10
SDI --> PB15
SCK --> PB13
LED --> ?PB9(控制LCD背光,可以同PWM調(diào)節(jié)改變LCD亮暗)
HC-SR04引腳:
VCC --> 5.0V
GND --> GND
Trig --> PA5
Echo --> PA0
SG90引腳:
VCC --> 5.0V
GND --> GND
PWM(信號線) --> PA6
一、雷達簡介
????????雷達,是英文Radar的音譯,源于radio detection and ranging的縮寫,意思為“無線電探測和測距”,即用無線電的方法發(fā)現(xiàn)目標并測定它們的空間位置。因此,雷達也被稱為“無線電定位”。雷達是利用電磁波探測目標的電子設(shè)備。雷達發(fā)射電磁波對目標進行照射并接收其回波,由此獲得目標至電磁波發(fā)射點的距離、距離變化率(徑向速度)、方位、高度等信息。
? ? ? ? 本項目是利用超聲波HC-SR04模塊配合SG90舵機(舵機負責(zé)帶動HC-SR04旋轉(zhuǎn))實現(xiàn)類似電磁波雷達的效果。配合TFT-LCD上的雷達UI界面畫出障礙物距離點,可以模擬類似于雷達的效果圖。同時,也實現(xiàn)了擬合構(gòu)建平面地圖的效果。(也可以擴展為避障小車的一部分)
二、HC-SR04與SG90舵機
2.1 HC-SR04模塊
?????????超聲波是振動頻率高于20KHZ的機械波。它具有頻率高、波長短、繞射現(xiàn)象小、方向性好、能夠成為射線而定向傳播等特點,應(yīng)用廣泛,適合大學(xué)生、工程師、技術(shù)人員以及電子愛好者等操作。(本項目中借用超聲波HC-SR04模塊代替電磁波去測距)
? ? ? ? 新版HC-SRO4,性能遠超老版HC-SRO4、US-015,在測距精度高于老版HC-SRO4和US-015的情況下,測距范圍更遠,可達6米,遠超一般超聲波測距模塊。采用CS-100A超聲波測距SOC芯片,高性能,工業(yè)級,寬電壓、低價格,只有普通超聲波測距模塊一半的價格,而性能遠超普通超聲波測距模塊。性能與US-025相同,均采用CS100A芯片,接口完全兼容。
工作原理:
? ? ? ? 1、采用IO觸發(fā)測量距離,給至少10us的高電平;
? ? ? ? 2、模塊自動發(fā)送8個40khz的方波,自動檢測是否有信號返回;
? ? ? ? 3、有信號返回、通過lo輸出一高電平、高電平持續(xù)的時間就是超聲波從發(fā)射到返回的時間,測試距離=(高電平時間*聲速(340M/s)/2);
本模塊有一個接口:4Pin供電及通信接口。為2.54mm間距的彎排針,如圖所示:從左到右依次編號1、2、3、4,它們的定義如下∶
1號Pin:接VCC電源(直流3V-5.5V)。
2號Pin:接外部電路的Trig端,向此管腳輸入一個10uS以上的高電平,可觸發(fā)模塊測距。
3號Pin:接外部電路的Echo端,當(dāng)測距結(jié)束時,此管腳會輸出一個高電平,電平寬度為超聲波往返時間之和。
4號Pin:接外部電路的地。
2.2 SG90舵機
????????舵機是一種位置(角度)伺服的驅(qū)動器,適用于那些需要角度不斷變化并可以保持的控制系統(tǒng)。在高檔遙控玩具,如飛機、潛艇模型,遙控機器人中已經(jīng)得到了普遍應(yīng)用。(本項目中使用舵機SG90去帶動HC-SR04模塊運動,實現(xiàn)多角度的動態(tài)實時測距)
SG90舵機外觀:
工作原理:
? ? ? ? 舵機的控制一般需要一個20ms左右(50Hz)的時基脈沖,該脈沖的高電平部分一般為0.5ms-2.5ms范圍內(nèi)的角度控制脈沖部分,總間隔為2ms。以180度角度伺服為例,那么對應(yīng)的控制關(guān)系是這樣的:
?特別注意:市面上有180°舵機和360°舵機,兩者有所區(qū)別,讀者朋友購買的時候需要注意一下。
180°舵機版本:可以控制旋轉(zhuǎn)角度、有角度定位。上電后舵機自動復(fù)位到0°,通過一定參數(shù)的脈沖信號控制它的角度。
360°舵機版本:不可控制角度,只能順時針旋轉(zhuǎn)、逆時針旋轉(zhuǎn)、停止、調(diào)節(jié)轉(zhuǎn)速。無角度定位,上電不會復(fù)位到0°。因為這是360°任意旋轉(zhuǎn)的,沒有0。通過一定參數(shù)的脈沖信號控制它的選擇。
????????編程思路:讀者朋友控制舵機的時候,只需要使用定時器去產(chǎn)生PWM調(diào)節(jié)。用PWM調(diào)節(jié)出對應(yīng)ms數(shù)的脈沖即可實現(xiàn)對舵機的固定角度控制。
三、TFT-LCD屏幕
? ? ? ? 本項目中TFT-LCD作為雷達圖顯示的重要載體,需要使用TFT-LCD中各種API函數(shù)去首先模仿常規(guī)雷達圖繪制,之后需要借助TFT-LCD屏幕去顯示超聲波掃描的角度與障礙物的距離顯示。最后的重點,自己設(shè)計可以根據(jù)舵機角度(決定HC-SR04的測量角度)和HC-SR04測得的距離換算值去畫點,乃至畫線。
????????關(guān)于TFT-LCD使用與編程有不熟練的朋友,可以去閱讀本人的另一篇文章,希望可以對您有所幫助。文章地址:【強烈推薦】基于STM32的TFT-LCD各種顯示實現(xiàn)(內(nèi)容詳盡含代碼)_混分巨獸龍某某的博客-CSDN博客_tftlcd顯示屏引腳
四、CubeMX配置
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug設(shè)置成Serial Wire(否則可能導(dǎo)致芯片自鎖);
3、GPIO配置:將PB9,PB10,PB11,PB12都設(shè)置為GPIO_OUTPUT,速度為:Hight;
?4、SPI配置:配置使用SPI2作為TFT-LCD通訊方式(可以考慮GPIO模擬SPI)
5、GPIO配置:PA5接到了HC-SR04的TRIG觸發(fā)引腳,默認輸出低電平
6、TIM2配置:設(shè)置定時器TIM2每1us向上計數(shù)一次,通道1為上升沿捕獲并連接到超聲波模塊的ECHO引腳,記得開啟定時器中斷(涉及到捕獲中斷+定時器溢出中斷)。
7、TIM1配置:HC-SR04的使用都需要us級的延遲函數(shù),HAL庫自帶只有ms的,所以需要自己設(shè)計一個定時器;
8、TIM3配置:使用TIM3的Channel1產(chǎn)生PWM信號(控制SG90);
數(shù)據(jù)參數(shù)意義:
????????此時產(chǎn)生PWM波形頻率:72M / (719 +1)/ (1999+1) = 50Hz
? ? ? ??定時器周期:1/50 = 20ms
9、時鐘樹配置:
10、工程配置
五、代碼與解析
5.1?超聲波HC-SR04模塊代碼
????????其實,超聲波HC-SR04的驅(qū)動就是基于GPIO口的調(diào)用。同時,由于超聲波測距模塊是基于超聲波的物理性質(zhì),去進行距離測量,故此其精度受到很多因素影響。(這里為短距離雷達測距模擬,精度足夠無需考慮補償)
HC-SR04.h:
#ifndef HCSR04_H_ #define HCSR04_H_#include "main.h" #include "delay.h"typedef struct {uint8_t edge_state;uint16_t tim_overflow_counter;uint32_t prescaler;uint32_t period;uint32_t t1; // 上升沿時間uint32_t t2; // 下降沿時間uint32_t high_level_us; // 高電平持續(xù)時間float distance;TIM_TypeDef* instance;uint32_t ic_tim_ch;HAL_TIM_ActiveChannel active_channel; }Hcsr04InfoTypeDef;extern Hcsr04InfoTypeDef Hcsr04Info;/*** @description: 超聲波模塊的輸入捕獲定時器通道初始化* @param {TIM_HandleTypeDef} *htim* @param {uint32_t} Channel* @return {*}*/ void Hcsr04Init(TIM_HandleTypeDef *htim, uint32_t Channel);/*** @description: HC-SR04觸發(fā)* @param {*}* @return {*}*/ void Hcsr04Start();/*** @description: 定時器計數(shù)溢出中斷處理函數(shù)* @param {*} main.c中重定義void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)* @return {*}*/ void Hcsr04TimOverflowIsr(TIM_HandleTypeDef *htim);/*** @description: 輸入捕獲計算高電平時間->距離* @param {*} main.c中重定義void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)* @return {*}*/ void Hcsr04TimIcIsr(TIM_HandleTypeDef* htim);/*** @description: 讀取距離 * @param {*}* @return {*}*/ float Hcsr04Read();#endif /* HCSR04_H_ */HC-SR04.c:
#include "hc-sr04.h"Hcsr04InfoTypeDef Hcsr04Info;/*** @description: 超聲波模塊的輸入捕獲定時器通道初始化* @param {TIM_HandleTypeDef} *htim* @param {uint32_t} Channel* @return {*}*/ void Hcsr04Init(TIM_HandleTypeDef *htim, uint32_t Channel) {/*--------[ Configure The HCSR04 IC Timer Channel ] */// MX_TIM2_Init(); // cubemx中配置Hcsr04Info.prescaler = htim->Init.Prescaler; // 72-1Hcsr04Info.period = htim->Init.Period; // 65535Hcsr04Info.instance = htim->Instance; // TIM2Hcsr04Info.ic_tim_ch = Channel;if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_1){Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_1; // TIM_CHANNEL_4}else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_2){Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_2; // TIM_CHANNEL_4}else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_3){Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_3; // TIM_CHANNEL_4}else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_4){Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_4; // TIM_CHANNEL_4}else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_4){Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_4; // TIM_CHANNEL_4}/*--------[ Start The ICU Channel ]-------*/HAL_TIM_Base_Start_IT(htim);HAL_TIM_IC_Start_IT(htim, Channel); }/*** @description: HC-SR04觸發(fā)* @param {*}* @return {*}*/ void Hcsr04Start() {HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);DelayUs(10); // 10us以上HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); }/*** @description: 定時器計數(shù)溢出中斷處理函數(shù)* @param {*} main.c中重定義void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)* @return {*}*/ void Hcsr04TimOverflowIsr(TIM_HandleTypeDef *htim) {if(htim->Instance == Hcsr04Info.instance) // TIM2{Hcsr04Info.tim_overflow_counter++;} }/*** @description: 輸入捕獲計算高電平時間->距離* @param {*} main.c中重定義void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)* @return {*}*/ void Hcsr04TimIcIsr(TIM_HandleTypeDef* htim) {if((htim->Instance == Hcsr04Info.instance) && (htim->Channel == Hcsr04Info.active_channel)){if(Hcsr04Info.edge_state == 0) // 捕獲上升沿{// 得到上升沿開始時間T1,并更改輸入捕獲為下降沿Hcsr04Info.t1 = HAL_TIM_ReadCapturedValue(htim, Hcsr04Info.ic_tim_ch);__HAL_TIM_SET_CAPTUREPOLARITY(htim, Hcsr04Info.ic_tim_ch, TIM_INPUTCHANNELPOLARITY_FALLING);Hcsr04Info.tim_overflow_counter = 0; // 定時器溢出計數(shù)器清零Hcsr04Info.edge_state = 1; // 上升沿、下降沿捕獲標志位}else if(Hcsr04Info.edge_state == 1) // 捕獲下降沿{// 捕獲下降沿時間T2,并計算高電平時間Hcsr04Info.t2 = HAL_TIM_ReadCapturedValue(htim, Hcsr04Info.ic_tim_ch);Hcsr04Info.t2 += Hcsr04Info.tim_overflow_counter * Hcsr04Info.period; // 需要考慮定時器溢出中斷Hcsr04Info.high_level_us = Hcsr04Info.t2 - Hcsr04Info.t1; // 高電平持續(xù)時間 = 下降沿時間點 - 上升沿時間點// 計算距離Hcsr04Info.distance = (Hcsr04Info.high_level_us / 1000000.0) * 340.0 / 2.0 * 100.0;// 重新開啟上升沿捕獲Hcsr04Info.edge_state = 0; // 一次采集完畢,清零__HAL_TIM_SET_CAPTUREPOLARITY(htim, Hcsr04Info.ic_tim_ch, TIM_INPUTCHANNELPOLARITY_RISING);}} }/*** @description: 讀取距離 * @param {*}* @return {*}*/ float Hcsr04Read() {// 測距結(jié)果限幅if(Hcsr04Info.distance >= 500){Hcsr04Info.distance = 500; //元器件資料說是600cm最高距離,這里保守一點}return Hcsr04Info.distance; }????????由于利用中斷去讀取定時測算的脈沖距離,所以這里需要重寫定時器的中斷服務(wù)函數(shù)。(這部分放在main.c最后即可)
/* USER CODE BEGIN 4 */ /*** @description: 定時器輸出捕獲中斷* @param {TIM_HandleTypeDef} *htim* @return {*}*/ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //捕獲回調(diào)函數(shù) {Hcsr04TimIcIsr(htim); }/*** @description: 定時器溢出中斷* @param {*}* @return {*}*/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) //在中斷回調(diào)函數(shù)中添加用戶代碼 {Hcsr04TimOverflowIsr(htim); } /* USER CODE END 4 */5.2 SG90舵機模塊代碼
HAL_TIM_PwM_Start ( &htim3,TIM_CHANNEL_1); //PWM初始化定時器while(1) //while循環(huán)中放入 {__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,i); //舵機運動//i的取值代表舵機指向的角度(0°~180°) }注意事項:
? ? ? ??模擬舵機SG90的使用就是利用PWM的計數(shù)數(shù)值去控制舵機所指的角度。
????????通過修改句柄__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,value)里的參數(shù)value可以實現(xiàn)舵機角度控制。
這里以SG90舵機為例給大家講解:
? ? ? ? 已知0.5ms指向0°位置,2.5ms指向180°位置。
? ? ? ? 比如需要指向m°處:
? ? ? ? 2.5-0.5=2ms ?--> ?對應(yīng)于180°
? ? ? ? value/(1999+1)*20=0.5+(m/180)× 2
5.3 TFT-LCD顯示代碼?
5.3.1 TFTLCD驅(qū)動顯示
????????LCD顯示部分其實都是非常基礎(chǔ)的操作,不熟悉的可以去看看筆者另一篇文章了解一下。作者這里把工程中不一樣的地方指出來一下。【強烈推薦】基于STM32的TFT-LCD各種顯示實現(xiàn)(內(nèi)容詳盡含代碼)_混分巨獸龍某某的博客-CSDN博客_lcd顯示屏顯示代碼
5.3.2 雷達GUI設(shè)計
這部分代碼主要繪制出常規(guī)雷達圖的UI界面,需要注意的是畫出多組同心半圓。
其中,難點就是需要自己設(shè)計出函數(shù):可以用角度與長度(半徑)去畫出斜線。
? ? ? ? API函數(shù):void Radarline(double k,int r)
radar.h:
#ifndef __RADAR_H #define __RADAR_Hvoid Radarline(double k,int r); void radar_picture();#endifradar.c:
#include "radar.h" #include "LCDAPI.h" #include "lcd.h" #include "math.h" #include "hc-sr04.h"void Radarline(double k,int r) {double x,y;x=160+r*(double)cos(k/180*3.1415926); y=200-r*(double)sin(k/180*3.1415926);Gui_DrawLine(160,200,x,y,GREEN);}void radar_picture() {Gui_DrawFont_GBK24(307,98,GREEN,BLACK,"°");Gui_DrawFont_GBK24(263,58,GREEN,BLACK,"°");Gui_DrawFont_GBK24(181,28,GREEN,BLACK,"°");Gui_DrawFont_GBK24(110,40,GREEN,BLACK,"°");Gui_DrawFont_GBK24(35,90,GREEN,BLACK,"°");//雷達圖Gui_Circle(160,200,152,GREEN);Gui_Circle(160,200,114,GREEN);Gui_Circle(160,200,76,GREEN);Gui_Circle(160,200,38,GREEN);Radarline(30,170);Radarline(60,170);Radarline(90,170);Radarline(120,170);Radarline(150,170);rectangle(0,200,320,240,BLACK);Gui_DrawLine(0,200,320,200,GREEN);//數(shù)據(jù)信息Gui_DrawFont_GBK24(0,212,GREEN,BLACK,"距離");Gui_DrawFont_GBK24(50,219,GREEN,BLACK,":");Gui_DrawFont_GBK24(0,0,GREEN,BLACK,"角度");Gui_DrawFont_GBK24(50,7,GREEN,BLACK,":");Gui_DrawFont_GBK24(117,211,GREEN,BLACK,"CM"); Gui_DrawFont_GBK24(100,0,GREEN,BLACK,"°");Gui_DrawFont_GBK16(175,205,GREEN,BLACK,"25");Gui_DrawFont_GBK16(210,205,GREEN,BLACK,"50");Gui_DrawFont_GBK16(245,205,GREEN,BLACK,"75");Gui_DrawFont_GBK16(285,205,GREEN,BLACK,"100");Gui_DrawFont_GBK16(290,100,GREEN,BLACK,"30");Gui_DrawFont_GBK16(247,60,GREEN,BLACK,"60");Gui_DrawFont_GBK16(165,30,GREEN,BLACK,"90");Gui_DrawFont_GBK16(85,42,GREEN,BLACK,"120");Gui_DrawFont_GBK16(10,92,GREEN,BLACK,"150");Gui_DrawFont_GBK24(190,0,GREEN,BLACK,"超聲波雷達"); }5.4 main函數(shù)代碼
代碼核心:
? ? ? ? 協(xié)調(diào)處理SG90舵機和HC-SR04超聲波模塊,根據(jù)SG90舵機的轉(zhuǎn)動角度特性,i=50是0°,i=250是180°,200的差值對應(yīng)給了180°。注意我們的畫線函數(shù)需要知道2個點起始點與終止點,起始點肯定是我們測距的起始點,也是雷達圖上的圓心(160,200),現(xiàn)在問題的關(guān)鍵就變成了尋找終止點,終止點的計算可以通過直角坐標系利用三角函數(shù)轉(zhuǎn)換得到。求得終止點之后就可以使用常規(guī)的畫線亦或是畫點函數(shù)去實時反饋環(huán)境距離情況了。
main.c:
int main(void) {/* USER CODE BEGIN 1 */int i=50;double k;int d;/* 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_SPI2_Init();MX_TIM1_Init();MX_TIM2_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */Lcd_Init();LCD_LED_SET;LCD_RST_SET;Lcd_Clear(BLACK);//雷達圖radar_picture(); Hcsr04Init(&htim2, TIM_CHANNEL_1);Hcsr04Start();HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //初始化/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(i==50){ for(i=50;i<250;i++){ Hcsr04Start();d = (int)Hcsr04Read()*152/100;if(d>152) //距離越界{d=155;}LCD_Showdecimal(63,215,Hcsr04Read(),3,2,16);LCD_ShowNum(65,5,(double)(i-50)/200*180,3);__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,i); //舵機轉(zhuǎn)動 HAL_Delay(100);Radarline2((double)((i-50)*180/200),d);// Radarline((double)((i-50)*180/200),(int)Hcsr04Read()*152/300); }}if(i==250){ for(i=250;i>50;i--){ Hcsr04Start();d = (int)Hcsr04Read()*152/100;if(d>152) //距離越界{d=155;}LCD_Showdecimal(63,215,Hcsr04Read(),3,2,16);LCD_ShowNum(65,5,(double)(i-50)/200*180,3);__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,i); //舵機轉(zhuǎn)動 HAL_Delay(100); Radarline2((double)((i-50)*180/200),d);}} }/* USER CODE END 3 */ }六、實驗效果
超聲波雷達
七、項目代碼
代碼地址:?基于STM32的超聲波雷達項目(TFT-LCD)-嵌入式文檔類資源-CSDN文庫
如果積分不夠的朋友,點波關(guān)注,評論區(qū)留下郵箱,作者無償提供源碼和后續(xù)問題解答。求求啦關(guān)注一波吧?!!!
總結(jié)
以上是生活随笔為你收集整理的基于STM32的超声波雷达项目【可拟合构建平面地图】(代码开源)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怪物之心无法触发_《勇者斗恶龙 怪兽篇:
- 下一篇: 那些好用的firefox扩展插件分享