堵塞编程
如下是一个最普通的LED闪烁代码
void delay_ms(unsigned int xms)
{
unsigned int x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
void main(void)
{
while(1)
{
LED = 0;
delay_ms(500);
LED = 1;
delay_ms(500);
}
}
上面代码的缺点是显而易见的,单片机近乎100%的性能都消耗在delay中,大量时间单片机都在空跑等待。
如果整个程序的任务仅仅是闪烁一颗灯,那么这样的代码还能接受。但如果这是一个较复杂的应用呢?闪烁LED灯这个任务仅仅是做为程序上电运行指示的作用,在这之后还有按键、数码管、各种传感器驱动、业务逻辑等等。又怎么办呢?
非堵塞式编程
分析任务目标我们知道,让LED灯闪烁,我们要做的是每隔一定时间去反转一下LED控制口的电平。
想象一下,假如你现在在上网,同时你手上有一个任务,每隔10分钟去评论一条微博,你会怎么做?
先评论第1条微博,然后一直等在那里?等10分钟?再去评论第二条微博?
你肯定不会这样。在评论完第一条微博后你会去干其它事情,时不时看一下时间,10分钟有没有到,如果到了10分钟,再去评论第二条微博。这就是非堵塞式编程。
程序实现上述思路如下:
//LED灯驱动时基
unsigned int LED_ScanCnt = 0;
//LED灯电平翻转间隔时间
#define LED_FLASH_TIM 500
//时基提供
void timer_Deal(void)
{
static unsigned char BaseCnt = 100;
if(--BaseCnt)return;//100个循环LED_ScanCnt自加1
BaseCnt = 100;
LED_ScanCnt ++;
}
void LED_Flash(void)
{
//如果LED_ScanCnt计数器时间未到,退出该函数,出去做其它事情
if(LED_ScanCnt < LED_FLASH_TIM) return;
//否则清零计数器,并翻转LED控制电平
LED_ScanCnt = 0;
LED = ~LED;
}
void main(void)
{
while(1)
{
timer_Deal();
LED_Flash();
//其它进程..
}
}
以上代码程序是一直在循环运行的,不会停在某一刻不停delay。
然而上面代码还是有很大的缺陷,那就是时间是不可控的,没修改一次代码,LED_ScanCnt自加1所需要的时间就不同,如果我想要让LED以精确的1Hz频率闪烁,我还需要用示波器去调节宏LED_FLASH_TIM的值。现在的大多数单片机都有定时器,要解决时间不可控的缺陷,我们只需要用定时器中断替代timer_Deal()就可以了,这就是时基法编程。
//LED灯驱动时基
unsigned int LED_ScanCnt = 0;
//LED灯电平翻转间隔时间
#define LED_FLASH_TIM 500
void LED_Flash(void)
{
//如果LED_ScanCnt计数器时间未到,退出该函数,出去做其它事情
if(LED_ScanCnt < LED_FLASH_TIM) return;
//否则清零计数器,并翻转LED控制电平
LED_ScanCnt = 0;
LED = ~LED;
}
void main(void)
{
//定时器初始化....
while(1)
{
LED_Flash();
}
}
//定时器中断1ms
void Timer0_Interrupt() interrupt 1
{
LED_ScanCnt ++;
}