很多单片机的初学者容易掉入阻塞式编程的陷阱.因为阻塞式编程符合我们对现实世界的理解,一个人在一段时间内,只能做一件事情.例如要是实现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个.在这个框架下,还可以扩展按键,显示.....
这里并不是说阻塞式的程序不好,而是有一定的局限性.在写简单的应用中也很好用.很直观.
来源:oschina
链接:https://my.oschina.net/u/4256940/blog/4663163