阻塞式编程和非阻塞式编程区别

放肆的年华 提交于 2020-10-08 13:57:38

       很多单片机的初学者容易掉入阻塞式编程的陷阱.因为阻塞式编程符合我们对现实世界的理解,一个人在一段时间内,只能做一件事情.例如要是实现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个.在这个框架下,还可以扩展按键,显示.....

    这里并不是说阻塞式的程序不好,而是有一定的局限性.在写简单的应用中也很好用.很直观.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!