導讀:《藍橋杯單片機組》專欄文章是博主2018年參加藍橋杯的單片機組比賽所做的學習筆記,在當年的比賽中,博主是獲得了省賽一等獎,國賽二等獎的成績。成績雖談不上最好,但至少問心無愧。如今2021年回頭再看該系列文章,仍然感觸頗多。為了能更好地幫助到單片機初學者,今年特地抽出時間對當年的文章邏輯和結構進行重構,以達到初學者快速上手的目的。需要指出的是,由于本人水平有限,如有錯誤還請讀者指出,非常感謝。那么,接下來讓我們一起開始愉快的學習吧。
代碼下載可到Github<傳送門>
一、基礎理論
超聲波模塊的工作原理:單片機供給超聲波信號端Trig
一個最少10us長的高電平
觸發信號,模塊自動發射8個40khz
的方波,同時自動檢測到信號是否返回,一旦有信號返回,Echo端輸出一個高電平
,高電平持續的實踐就是超聲波從發射到返回的時間。 對應的測試距離計算方法 :(高電平時間*聲速(340m/s))/2
超聲波模塊原理圖
雖然我們板子上的不再是集成模塊了,但是原理還是一樣的。只是沒有了Trig
即不需要觸發信號,同時需要程序實現連續發送8個40khz的方波,然后計算接收端持續為1的時間即可。
二、動手實驗
程序中有幾點需要注意的:
- 40Khz的方波實現方法,方波就是占空比為
1/2
的矩形波,40k
對應25us
,所以我們可以通過發送引腳為高低電平
分別持續13us
實現40khz的方波! - 我是們是用定時器計數來實現計時的,所以還要考慮定時器溢出的問題,對應顯示的距離也應處理!
- 一般上如果我們使用成品模塊的話都會把接收引腳放到外部中斷,一旦收到低電平信號就進入外部中斷停止計時,這樣做更精確!但是不盡人意的是藍橋的板子并不是接在了外部中斷(突然讓我想起來惡心的紅外也不是接在外部中斷)!
- 不要刷太快,200ms即可!!
time*0.17
是帶一個小數點位的,別忘了小數點
這里寫圖片描述
JS2 - 超聲波發送端
用的是反相器推挽輸出,這樣可以加大發射頻率。
JS1 - 超聲波接收端
用的CX20106X
這個紅外芯片接收40KHz
的方波。這個典型電路的優點就是誤差小,1m內為mm級,2m內1cm左右,5m內3cm左右。
貼出超聲波相關的代碼。
/*
*******************************************************************************
* 文件名:sonic.c
* 描 述:
* 作 者:CLAY
* 版本號:v1.0.0
* 日 期:
* 備 注:
*
*******************************************************************************
*/
#include "config.h"
#include <intrins.h>
#include "main.h"
void Delay13us() //@11.0592MHz
{
unsigned char i;
_nop_();
_nop_();
i = 33;
while (--i);
}
void InitSonic()
{
TMOD &= 0x0F;
TMOD |= 0x10;
TF1 = 0;
TR1 = 0;
}
void SendWave()
{
u8 i = 8;
while(i--)
{
Sonic_Txd = 1;
Delay13us();
Sonic_Txd = 0;
Delay13us();
}
}
void SonicDriver()//數碼管顯示
{
u16 time, distance;
SendWave();//發送8個40Khz脈沖信號
TH1 = 0; //清零計數值準備開始
TL1 = 0;
TR1 = 1;
while((Sonic_Rxd) && (TF1==0));
TR1 = 0;
if(TF1 == 1)
{
TF1 = 0;
LedBuff[0] = 0xBF; //對應顯示橫線
LedBuff[1] = 0xBF;
LedBuff[2] = 0xBF;
LedBuff[3] = 0xBF;
}
else
{
time = (TH1 * 256) + TL1;
distance = (u16)((time * 0.17 * 12) / 11.0592); //[機器周期*定時器計時*10^(-6)](s) * 340(m/s)/2 * 10^(2); 單位厘米,且有一位小數點!
LedBuff[0] = LedChar[distance%10];
LedBuff[1] = LedChar[distance/10%10];
LedBuff[1] &= 0x7F; //點亮小數點
LedBuff[2] = LedChar[distance/100%10];
LedBuff[3] = LedChar[distance/1000%10];
}
}
需要再次強調的一段代碼
time = (TH1 * 256) + TL1;
distance = (u16)((time * 0.17 * 12) / 11.0592);
//[機器周期*定時器計時*10^(-6)](s) * 340(m/s)/2 * 10^(2); 單位厘米,且有一位小數點!
算出的是單位cm后還帶一個小數位!如果直接用12M晶振的話就是1us,一個機器周期。
distance = (u16)(time * 0.17 );
記錄一點網上提到的小錯誤:
time = (TH1 * 256) + TL1;
有人這樣寫time = TH1 << 8 | TL1
沒問題!<<
運算符優先級比|
高! 但是如果你這樣寫time = TH1 << 8 + TL1
看著是對的!但是,你可以在C語言相關編程環境下試試!得到的答案是錯的,原因也很簡單,+
的優先級比<<
高!所以很有必要自己寫程序的時候隨手加上括號,不要想當然地寫優先級!
可直接使用的程序(超聲波數據保留小數點后一位)
#include "config.h"
sbit Sonic_Txd = P1^0;
sbit Sonic_Rxd = P1^1;
u8 LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90
};
u8 LedBuff[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
u32 cnt = 0;
u8 T1RH, T1RL;
bit flag200ms = 1;
void CloseFucker();
void ConfigTimer0();
void ConfigTimer1(u16 ms);
void ShowNumber(u16 num);
void SendWave();
void main()
{
u16 time, distance=0;
EA = 1;
CloseFucker();
ConfigTimer0();
ConfigTimer1(1);
while(1)
{
if(flag200ms)
{
flag200ms = 0;
TH0 = 0;
TL0 = 0;
TF0 = 0;
SendWave();
TR0 = 1;
while((Sonic_Rxd) && (TF0==0));
TR0 = 0;
if(TF0)
{
LedBuff[3] = 0xBF;
LedBuff[2] = 0xBF;
LedBuff[1] = 0xBF;
LedBuff[0] = 0xBF;
}
else
{
time = ((u16)TH0<<8)+TL0;
distance = 0.17 * time;
ShowNumber(distance);
}
}
}
}
void Delay13us() //@11.0592MHz
{
unsigned char i;
_nop_();
_nop_();
i = 33;
while (--i);
}
void SendWave()
{
u8 i=8;
while(i--)
{
Sonic_Txd = 1;
Delay13us();
Sonic_Txd = 0;
Delay13us();
}
}
void ShowNumber(u16 num)
{
u8 buf[8];
char i;
for(i=0; i<4; i++)
{
buf[i] = num%10;
num /= 10;
}
for(i=3; i>0; i--)
{
if(buf[i] == 0)
{
LedBuff[i] = 0xFF;
}
else
{
break;
}
}
for( ; i>=0; i--)
{
LedBuff[i] = LedChar[buf[i]];
}
LedBuff[1] &= 0x7F;
}
void CloseFucker()
{
P2 = (P2&0x1F)|0xA0;
P0 = P0&0xAF;
P2 = P2&0x1F;
}
void ConfigTimer0()
{
TMOD &= 0xF0;
TMOD |= 0x01;
TR0 = 0;
TF0 = 0;
}
void ConfigTimer1(u16 ms)
{
u32 tmp;
tmp = 11059200/12;
tmp = (tmp*ms)/1000;
tmp = 65536 - tmp;
T1RH = (u8)(tmp>>8);
T1RL = (u8)tmp;
TMOD &= 0x0F;
TMOD |= 0x10;
TH1 = T1RH;
TL1 = T1RL;
ET1 = 1;
TR1 = 1;
}
void LedScan()
{
static u8 index = 0;
P2 = (P2&0x1F)|0xE0;
P0 = 0xFF;
P2 = P2&0x1F;
P2 = (P2&0x1F)|0xC0;
P0 = 0x80>>index;
P2 = P2&0x1F;
P2 = (P2&0x1F)|0xE0;
P0 = LedBuff[index];
P2 = P2&0x1F;
index++;
index &= 0x07;
}
void InterruptTimer1() interrupt 3
{
static u16 tmr200ms = 0;
TH1 = T1RH;
TL1 = T1RL;
tmr200ms++;
if(tmr200ms >= 200)
{
tmr200ms = 0;
flag200ms = 1;
}
LedScan();
}
小結:本篇文章主要介紹了單片機學習中的一個進階模塊:超聲波模塊。從基礎理論到試驗以及試驗踩坑,都有涉及。在該部分也并沒有太難的知識點,多多練習該模塊對比賽名次大有裨益。
希望大家多多支持我的原創文章。如有錯誤,請大家及時指正,非常感謝。