阻塞式程式設計和非阻塞式程式設計區別

zhongvv發表於2020-10-06

       很多微控制器的初學者容易掉入阻塞式程式設計的陷阱.因為阻塞式程式設計符合我們對現實世界的理解,一個人在一段時間內,只能做一件事情.例如要是實現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了.

下面來個非阻塞式程式的案例.

#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>500)

                   {

                            usLedTmrCnt=0;

                           /*類似Led=~Led*/

                             if(LED)

                             {

                                       LED=0;

                              }

                            else

                           {

                                          LED=1;

                             }

                    }

#if 1

                                usLedTmrCnt1++;

                              if(usLedTmrCnt1>250)

                                {

                                                    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個.在這個框架下,還可以擴充套件按鍵,顯示.....

    這裡並不是說阻塞式的程式不好,而是有一定的侷限性.在寫簡單的應用中也很好用.很直觀.

相關文章