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