導(dǎo)讀:《藍(lán)橋杯單片機(jī)組》專欄文章是博主2018年參加藍(lán)橋杯的單片機(jī)組比賽所做的學(xué)習(xí)筆記,在當(dāng)年的比賽中,博主是獲得了省賽一等獎,國賽二等獎的成績。成績雖談不上最好,但至少問心無愧。如今2021年回頭再看該系列文章,仍然感觸頗多。為了能更好地幫助到單片機(jī)初學(xué)者,今年特地抽出時間對當(dāng)年的文章邏輯和結(jié)構(gòu)進(jìn)行重構(gòu),以達(dá)到初學(xué)者快速上手的目的。需要指出的是,由于本人水平有限,如有錯誤還請讀者指出,非常感謝。那么,接下來讓我們一起開始愉快的學(xué)習(xí)吧。
代碼下載可到Github<傳送門>
一、基礎(chǔ)理論
超聲波模塊的工作原理:單片機(jī)供給超聲波信號端Trig
一個最少10us長的高電平
觸發(fā)信號,模塊自動發(fā)射8個40khz
的方波,同時自動檢測到信號是否返回,一旦有信號返回,Echo端輸出一個高電平
,高電平持續(xù)的實(shí)踐就是超聲波從發(fā)射到返回的時間。 對應(yīng)的測試距離計(jì)算方法 :(高電平時間*聲速(340m/s))/2
超聲波模塊原理圖
雖然我們板子上的不再是集成模塊了,但是原理還是一樣的。只是沒有了Trig
即不需要觸發(fā)信號,同時需要程序?qū)崿F(xiàn)連續(xù)發(fā)送8個40khz的方波,然后計(jì)算接收端持續(xù)為1的時間即可。
二、動手實(shí)驗(yàn)
程序中有幾點(diǎn)需要注意的:
- 40Khz的方波實(shí)現(xiàn)方法,方波就是占空比為
1/2
的矩形波,40k
對應(yīng)25us
,所以我們可以通過發(fā)送引腳為高低電平
分別持續(xù)13us
實(shí)現(xiàn)40khz的方波! - 我是們是用定時器計(jì)數(shù)來實(shí)現(xiàn)計(jì)時的,所以還要考慮定時器溢出的問題,對應(yīng)顯示的距離也應(yīng)處理!
- 一般上如果我們使用成品模塊的話都會把接收引腳放到外部中斷,一旦收到低電平信號就進(jìn)入外部中斷停止計(jì)時,這樣做更精確!但是不盡人意的是藍(lán)橋的板子并不是接在了外部中斷(突然讓我想起來惡心的紅外也不是接在外部中斷)!
- 不要刷太快,200ms即可!!
time*0.17
是帶一個小數(shù)點(diǎn)位的,別忘了小數(shù)點(diǎn)
這里寫圖片描述
JS2 - 超聲波發(fā)送端
用的是反相器推挽輸出,這樣可以加大發(fā)射頻率。
JS1 - 超聲波接收端
用的CX20106X
這個紅外芯片接收40KHz
的方波。這個典型電路的優(yōu)點(diǎn)就是誤差小,1m內(nèi)為mm級,2m內(nèi)1cm左右,5m內(nèi)3cm左右。
貼出超聲波相關(guān)的代碼。
/*
*******************************************************************************
* 文件名: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()//數(shù)碼管顯示
{
u16 time, distance;
SendWave();//發(fā)送8個40Khz脈沖信號
TH1 = 0; //清零計(jì)數(shù)值準(zhǔn)備開始
TL1 = 0;
TR1 = 1;
while((Sonic_Rxd) && (TF1==0));
TR1 = 0;
if(TF1 == 1)
{
TF1 = 0;
LedBuff[0] = 0xBF; //對應(yīng)顯示橫線
LedBuff[1] = 0xBF;
LedBuff[2] = 0xBF;
LedBuff[3] = 0xBF;
}
else
{
time = (TH1 * 256) + TL1;
distance = (u16)((time * 0.17 * 12) / 11.0592); //[機(jī)器周期*定時器計(jì)時*10^(-6)](s) * 340(m/s)/2 * 10^(2); 單位厘米,且有一位小數(shù)點(diǎn)!
LedBuff[0] = LedChar[distance%10];
LedBuff[1] = LedChar[distance/10%10];
LedBuff[1] &= 0x7F; //點(diǎn)亮小數(shù)點(diǎn)
LedBuff[2] = LedChar[distance/100%10];
LedBuff[3] = LedChar[distance/1000%10];
}
}
需要再次強(qiáng)調(diào)的一段代碼
time = (TH1 * 256) + TL1;
distance = (u16)((time * 0.17 * 12) / 11.0592);
//[機(jī)器周期*定時器計(jì)時*10^(-6)](s) * 340(m/s)/2 * 10^(2); 單位厘米,且有一位小數(shù)點(diǎn)!
算出的是單位cm后還帶一個小數(shù)位!如果直接用12M晶振的話就是1us,一個機(jī)器周期。
distance = (u16)(time * 0.17 );
記錄一點(diǎn)網(wǎng)上提到的小錯誤:
time = (TH1 * 256) + TL1;
有人這樣寫time = TH1 << 8 | TL1
沒問題!<<
運(yùn)算符優(yōu)先級比|
高! 但是如果你這樣寫time = TH1 << 8 + TL1
看著是對的!但是,你可以在C語言相關(guān)編程環(huán)境下試試!得到的答案是錯的,原因也很簡單,+
的優(yōu)先級比<<
高!所以很有必要自己寫程序的時候隨手加上括號,不要想當(dāng)然地寫優(yōu)先級!
可直接使用的程序(超聲波數(shù)據(jù)保留小數(shù)點(diǎn)后一位)
#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();
}
小結(jié):本篇文章主要介紹了單片機(jī)學(xué)習(xí)中的一個進(jìn)階模塊:超聲波模塊。從基礎(chǔ)理論到試驗(yàn)以及試驗(yàn)踩坑,都有涉及。在該部分也并沒有太難的知識點(diǎn),多多練習(xí)該模塊對比賽名次大有裨益。
希望大家多多支持我的原創(chuàng)文章。如有錯誤,請大家及時指正,非常感謝。