之前寫過一篇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中斷 }