關於HC04超聲波模塊測距的思考(51版)


之前寫過一篇HC04的使用文章,當時是使用stm32來實現的,原文鏈接

后來又多次使用51來驅動這個模塊,有時候有測距需要,使用了幾次,總是感覺我上次那個程序不是很好,

所以這次對它進行了改進。雖然上一次也使用了多次測量取平均值,但是內有排除中間會有錯誤數據的情況。

之前的程序是這樣的(測距部分) :

u32 t = 0;  
int i = 0;  
float lengthTemp = 0;  
float sum = 0;  
while(i!=5)  
{  
TRIG_Send = 1;      //發送口高電平輸出  
Delay_Us(20);  
TRIG_Send = 0;  
while(ECHO_Reci == 0);      //等待接收口高電平輸出  
    OpenTimerForHc();        //打開定時器  
    i = i + 1;  
    while(ECHO_Reci == 1);  
    CloseTimerForHc();        //關閉定時器  
    t = GetEchoTimer();        //獲取時間,分辨率為1US  
    lengthTemp = ((float)t/58.0);//cm  
    sum = lengthTemp + sum ;  
}   
lengthTemp = sum/5.0;  
return lengthTemp;  

 

就是當超出測量范圍的時候(3.4米),數據肯定是不准確的。還有就是當因為某些原因模塊沒有接收到返回的超聲波,也會導致錯誤的數據,

就算是取平均值,如果中間有一個很大的數據的話,計算的結果也是不精確的。

利用51再次使用這個模塊的程序如下:

/* 
獲取當前距離 
2018.3.5  誤差在1cm以內  
*/  
float get_distence()  
{  
    unsigned long int time_buf = 0;     //總耗時  
    float distence = 0;     //計算一次距離  
    float sum = 0;          //多次計算的總距離  
    uchar i = 0;  
  
    while(i < NUM)  
    {  
        time_flag = 0;  //先清除標志  
        sr04_start();   //開始測距  
        while(!ECHO);   //等待發出40khz脈沖,觸發信號之后,echo會變成高電平  
        time_0_start(); //當把trig拉高10us之后,模塊即開始發出8個40khz的脈沖,與此同時,echo變為高電平時,打開定時器。  
        while(ECHO);    //等待回響信號,收到回響信號,echo會變低電平  
        TR0 = 0;        //關閉定時器  
  
        if(time_flag != 0)  //超出測量范圍  
            continue;       //不進行計算,放棄這次測量,從新測量  
  
        else                //time_flag = 0,沒有超出測量范圍  
        {  
            time_buf = (TH0 * 256) + TL0;  
            distence = time_buf * 0.0168;//(單位:cm)雖然聲速340m/s,發現使用0.0168更精確,可能和溫度有關  
            sum+= distence;  
            i++;  
        }       
    }     
    return (sum/NUM) ;  //取NUM次平均值  
}   

 

 

其實就是在進行計算前先判斷一下定時器是否產生中斷,如果產生中斷,就放棄本次數據,再次測量。這樣測出的數據算是很精確了,唯一的誤差就是聲速的計算上面,導致會有1-2cm的誤差。

16位的定時器,12M的晶振,定時器模式1,從0開始計數,最大到65535,一次溢出需要的時間是0.065s,聲速為340m/s。那么溢出時的距離為22.1m,已經遠遠超過了超聲波模塊的測量范圍(0-5m),所以只要產生一次溢出,就可以認為是超出測量范圍.。

所以在定時器中斷函數中 :

void TIME0() interrupt 1  
{  
//  TF0 = 0;       //模式1硬件自動清零  
    TH0 = 0;  
    TL0 = 0;  
    time_flag++;  
}  

 

其中對一些操作進行了簡單的封裝。開始測距函數,就是按照要求拉高TRIG引腳

/*start*/  
void  sr04_start()  
{  
     TRIG = 1;    
     delay_10us(5); //拉高50us  
     TRIG = 0;  
}  

 

還有就是定時器開始函數,在這個函數里面需要把計數清零:

/*開啟定時器0,打開之前先清除之前的計數,不然會累計計數*/  
void time_0_start()  
{     
    TH0 = 0;       //打開前計數清零  
    TL0 = 0;  
    TR0 = 1;       //打開定時器  
}  

然后就是模塊的 初始化函數了,在初始化的時候其實就是對定時器的初始化,順便把TRIG和ECHO引腳置0;

void sr04_init()  
{     
    TRIG = 0;  
    ECHO = 0;  
  
    TMOD |= 0X01;   //定時器0模式設置1  
    TH0 = 0;    //從0開始計數  
    TL0 = 0;  
    TR0 = 0;    //關閉定時器  
    EA = 1;     //開總中斷  
    ET0 = 1;    //允許定時器0中斷  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM