很多單片機的初學者容易掉入阻塞式編程的陷阱.因為阻塞式編程符合我們對現實世界的理解,一個人在一段時間內,只能做一件事情.例如要是實作1Hz的閃燈程式,那么先讓單片機埠拉高500ms,然后再拉低500ms,然后回圈.因為等待時間太長了,沒有打開看門狗.
下面是阻塞式編程的例子:
#include "extern.h"
/*埠定義*/
BIT LED : PA.3;
BIT LED1 : PA.4;
/*相應于main函式*/
void FPPA0 (void)
{
/*單片機內部RC震動時鐘為 IHRC默認為16M,因此,IHRC/2=8M,系統時鐘為8M*/
.ADJUST_IC SYSCLK=IHRC/2
/*埠設定為輸出 低*/
$ LED out ,low;
$ LED1 out ,low;
/*主回圈*/
while (1)
{
/*埠置高*/
LED=1;
/*延時單位1T(1個時鐘周期),延時8000=1ms,一共延時500ms*/
.delay 8000*500;
/*埠置低*/
LED=0;
/*延時500ms*/
.delay 8000*500;
}
}
OK 完美運行.
但是,當要你再加上一個指示燈,要2Hz閃爍,也就是250ms關,250ms開,然后回圈,那要怎么辦了.
OK繼續,剛剛辛辛苦苦寫好的程式得重新寫了.
/*相應于main函式*/
void FPPA0 (void)
{
/*單片機內部RC震動時鐘為 IHRC默認為16M,因此,IHRC/2=8M,系統時鐘為8M*/
.ADJUST_IC SYSCLK=IHRC/2
/*埠設定為輸出 低*/
$ LED out ,low;
$ LED1 out ,low;
/*主回圈*/
while (1)
{
/*埠置高*/
LED=1;
LED1=1;
/*延時單位1T(1個時鐘周期),延時8000=1ms,一共延時250ms*/
.delay 8000*250;
LED=1;
LED1=0;
/*延時單位1T(1個時鐘周期),延時8000=1ms,一共延時250ms*/
.delay 8000*250;
LED=0;
LED1=1;
/*延時單位1T(1個時鐘周期),延時8000=1ms,一共延時250ms*/
.delay 8000*250;
LED=0;
LED1=0;
/*延時單位1T(1個時鐘周期),延時8000=1ms,一共延時250ms*/
.delay 8000*250;
}
}
OK,完美運行,要加上按鍵呢?怎么辦,感覺越來越麻煩了.
其實,單片機干起事情來比我們想象得要快得多.例如8M的主頻,1T的單片機,在1S鐘之內就可以執行8,000,000條指令.在很多時候,我們幾乎可以忽略單片機在執行兩條指令的時間差,也就是說雖然單片機程式是順序執行的,在人看來效果和并行執行沒有任何差別.不能讓單片機在等待.LED開/關其實就是一條指令的事.做好定時就OK了,那定時怎么做呢?用定時中斷,配置定時器每100us產生一次中斷,那么計數10次就是1ms.在while回圈里查詢這個1ms標記并進行計數就可以進行定時了.
下面來個非阻塞式程式的案例.
#include "extern.h"
#define HIGH 1
#define LOW 0
#define DISABLE 0
#define ENABLE 1
#define EMPTY 0
#define FULL 1
#define ON 1
#define OFF 0
/*燈*/
BIT LED : PA.3;
/*按鍵*/
BIT LED1 : PA.4;
word usLedTmrCnt;
word usLedTmrCnt1;
/*定時時間是否到了cinit*/
bit FLAG_NMS;
/*計數值cinit*/
byte count;
/*定時器初始化cinit*/
word T16COUNTER;
/***************************************/
void TIME16_Init(void)
{
/*計數值清零*/
T16COUNTER =488;
/*ms標記reset*/
FLAG_NMS =0;
/*使能定時器*/
$ INTEN T16;
/*關中斷*/
INTRQ = 0;
/*停止定時器*/
T16M.5 =0;
STT16 T16COUNTER;
/*計算方法16M/*/
$ T16M IHRC,/1,BIT11;
}
void FPPA0 (void)
{
.ADJUST_IC SYSCLK=IHRC/2, IHRC=16MHz, VDD=3.5V,init_ram;
$ CLKMD IHRC/2,En_IHRC,En_ILRC,En_WatchDog;
$ LED OUT,LOW;
$ LED1 OUT,LOW;
TIME16_Init();
engint;
while (1)
{
wdreset;
/*1ms定時時間到*/
if ( FLAG_NMS )
{
usLedTmrCnt++;
if(usLedTmrCnt>499)
{
usLedTmrCnt=0;
/*類似Led=~Led*/
if(LED)
{
LED=0;
}
else
{
LED=1;
}
}
#if 1
usLedTmrCnt1++;
if(usLedTmrCnt1>249)
{
usLedTmrCnt1=0;
/*類似Led1=~1Led*/
if(LED1)
{
LED1=0;
}
else
{
LED1=1;
}
}
#endif
/*清除標記*/
FLAG_NMS=0;
}
}
}
void Interrupt ( void )
{
pushaf;
if ( Intrq.T16 )
{
Intrq.T16 = 0;
STT16 T16COUNTER;
if ( count>0 )
{
count--;
}
else
{
count = 9;
/*1ms*/
FLAG_NMS= 1;
}
}
popaf;
}
給LED 和LED1各分配一個計數器,可以設定1-65535ms的翻轉時間.LED亮/滅只需要一條指令.其他的時候就查詢一下1ms標記,有就計數,當計數值溢位的時候就把LED進行翻轉.就這么簡單.各自獨立,沒有長時間的等待,看門狗也打開了.在埠和記憶體允許的情況下還可以添加N個.在這個框架下,還可以擴展按鍵,顯示.....
這里并不是說阻塞式的程式不好,而是有一定的局限性.在寫簡單的應用中也很好用.很直觀.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/163928.html
標籤:其他
上一篇:第十一屆藍橋杯單片機模擬題
