STM32微控制器連線HC_SR04超聲波模組測距

小木刺客發表於2018-05-21

原文連結:https://blog.csdn.net/m0_37655357/article/details/72934643

首先,先來看一下這個模組的基本功能和原理。

HC-SR04超聲波測距模組可提供約2cm400釐米的非接觸式距離感測功能,測距精度可達高到3毫米;模組包括超聲波發射器,接收器與控制電路像智慧小車的測距以及轉向,或是一些專案中,常常會用到。智慧小車測距可以及時發現前方的障礙物,使智慧小車可以及時轉向,避開障礙物。

注意是5v輸入,但是我用stm32的3.3v輸入也是沒有問題的。

二,工作原理

      1.給超聲波模組接入電源和地
      。2.給脈衝觸發引腳(trig)輸入一個長為20us的高電平方波

      3.輸入方波後,模組會自動發射8個40KHz的聲波,與此同時回波引腳(echo)端的電平會由0變為1;(
      當此時應該啟動定時器計時)4.當超聲波返回被模組接收到時,回波引腳端的電平會由1變為0;(此時應該停止定時器計數),定時器記下的這個時間即為超聲波由發射到返回的總時長
      5 。根據聲音在空氣中的速度為344米/秒,即可計算出所測的距離。

      要學習和應用感測器,學會看懂感測器的時序圖是很關鍵的,所以我們來看一下HC-SR04的時序觸發圖。

    

 

    我們來分析一下這個時序圖,先由觸發訊號啟動HC-RS04測距模組,也就是說,主機要先傳送至少為10us的高電平,觸發HC-RS04,模組內部發出訊號是感測器自動迴應的,我們不用去管它。輸出迴響訊號是我們需要關注的。訊號輸出的高電平就是超聲波發出到重新返回接收所用的時間。用定時器,可以把這段時間記錄下來,算出距離,別忘了結果要除於2,因為總時間是傳送和接收的時間總和。

下面是親測可用的驅動程式。

晶片型號為STM32F103ZET6,超聲波測距後通過串列埠列印到電腦上面。

驅動和測距;

[cpp]  檢視純文字 
  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; //毫秒計數  
  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. //提示:靜態函式的作用域僅限於定義它的原始檔內,所以不需要在標頭檔案裡宣告  
  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.         如果 (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); //得到美國  
  95.           TIM6-> CNT = 0;  //將TIM2計數暫存器的計數值清零  
  96.                 Delay_Ms(50);  
  97.         返回 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.         (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); //釐米  
  121.             sum = lengthTemp + sum;  
  122.           
  123.     }  
  124.         lengthTemp = sum / 5.0;  
  125.         返回 長度Temp;  
  126. }  
  127.   
  128.   
  129. / * :::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::: 
  130. **函式名稱:Delay_Ms_Ms 
  131. **功能描述:延時1MS(可通過模擬來判斷他的準確度)           
  132. **引數描述:time(ms)注意時間<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)注意時間<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. }  

但是關於USART的函式我就不往上寫了,這個簡單的串列埠列印大家應該都會寫。下面簡單貼一下我的主函式吧。


[cpp]  檢視純文字 
  1. / * 
  2. 教訓:實驗前一定要檢查引腳連線是否正確,萬不可搞錯,不然又要燒壞晶片!!!! 
  3. 2017年6月8日 
  4. * /  
  5.   
  6. #include“hcsr04.h”  
  7. #include“chao_usart.h”  
  8.   
  9. int  main()  
  10. {  
  11.       
  12.         浮動 長度;  
  13.           
  14.         GPIO_cfg();  
  15.       NVIC_cfg();  
  16.         USART_cfg();      
  17.         printf(“串列埠初始化成功!\ n” );  
  18.       
  19.         Hcsr04Init();     
  20.         printf(“超聲波初始化成功!\ n” ); //測試程式是否卡在下面兩句上面  
  21.   
  22.         長度= Hcsr04GetLength();  
  23.         printf(“距離為:%。3f \ n” ,長度);  
  24.       
  25.       
  26. }  


實驗結果:

 

好了,其實這個模組很簡單,但是要是把他用的很好的話還是比較困難的,比如用超聲波做一個四軸定高的程式,還是有一定的挑戰性的。

寫這篇博​​客的目的不僅僅是介紹這個模組的使用,其實這種使用介紹網上一搜一大把,我只是想紀錄一下,我在做這個模組的時候遇到的一些其他的問題。

其中有一個小插曲,就是當吧寫好的程式燒進去之後,執行時總是出現每次返回一個同樣的比正常值小的多的資料,比如說0.034釐米,這明顯是一個錯誤的資料,但是剛開始的時候,不知道為什麼

總是這樣,多次復位從新上電總是這一個資料。讓我很是苦惱。但是幸運的是,在這樣的情況中間,他又會有時出現一兩個正常的的資料,讓你有點摸不著頭腦。

上網查了一下才慢慢明白這種現象叫做“餘震”,網上關於餘震的解釋大致有三種:

  如圖1所示,探頭的餘震。即使是分體式的,發射頭工作完後還會繼續震一會,這是物理效應,也就是餘震。這個餘震訊號也會向外傳播。如果你的設計是發射完畢後立刻切換為接收狀態(無盲區),那麼這個餘震波會通過殼體和周圍的空氣,直接到達接收頭,干擾了檢測(注:通常的測距設計裡,發射頭和接收頭的距離很近,在這麼短的距離裡超聲波的檢測角度是很大的,可達180度)
  。2,殼體的餘震。就像爆鍾一樣,能量仍來自發射頭。發射結束後,殼體的餘震會直接傳導到接收頭,當然這個時間很短,但已形成了干擾。另外,在不同的環境溫度下,殼體的硬度和外形會有所變化,其餘震有時長,有時短,有時干擾大,有時干擾小,這是設計工業級產品時必須要考慮的問題
  3,電路串擾。超聲波發射時的瞬間電流很大,例如某種工業級連續測距產品瞬 電流會有15A,通常的產品也能達到1A,瞬間這麼大的電流會對電源有一定影響,並干擾接收電路。通過改善電源設計可以緩解這種情況,但在低成本設計中很難根除。所以每次發射完畢,接收電路還需要一段時間穩定工作狀態。在此期間,其輸出的訊號很難使用。

 

消除上述現象的方法之一就是在檢測的時候多次迴圈檢測,取平均值,也就是加權平均濾波,一個簡單的濾波處理就是下面這一段:

[cpp]  檢視純文字 
  1.               u32 t = 0;  
  2. int  i = 0;  
  3. float  lengthTemp = 0;  
  4. float  sum = 0;  
  5. (i!= 5)  
  6. {  
  7. TRIG_Send = 1;      //傳送口高電平輸出  
  8. Delay_Us(20);  
  9. TRIG_Send = 0;  
  10. while (ECHO_Reci == 0);      //等待接收口高電平輸出  
  11.     OpenTimerForHc();        //開啟定時器  
  12.     i = i + 1;  
  13.     while (ECHO_Reci == 1);  
  14.     CloseTimerForHc();        //關閉定時器  
  15.     t = GetEchoTimer();        //獲取時間,解析度為1US  
  16.     lengthTemp =((float )t / 58.0); //釐米  
  17.     sum = lengthTemp + sum;  
  18.   
  19.   
  20. lengthTemp = sum / 5.0;  
  21. 返回 長度Temp;  

加了這個之後,基本上就沒有出現餘震現象了。

還有一點就是測試程式前一定要檢查引腳有沒有接錯,不管多有把握,也要看一遍,不然很容易出大事的,一個晶片也許就因為你的大意給GG了。切記,這個應該也算我們這個行業的基本素養吧。

相關文章