在网上众多的单片机学习资料中,最基础的实验无疑于点亮LED了,即控制单片机的I/O的电平的变化。
如同如下实例代码一般
void main(void)
{
LedInit() ;
While(1)
{
LED = ON ;
DelayMs(500) ;
LED = OFF ;
DelayMs(500) ;
}
}
程序很简单,从它的结构可以看出,LED先点亮500MS,然后熄灭500MS,如此循环下去,形成的效果就是LED以1HZ的频率进行闪烁。下面让我们分析上面的程序有没有什么问题。
看来看出,好像很正常的啊,能有什么问题呢?这个时候我们应该换一个思路去想了。试想,整个程序除了控制LED = ON ; LED = OFF; 这两条语句外,其余的时间,全消耗在了DelayMs(500)这两个函数上。而在实际应用系统中是没有哪个系统只闪烁一只LED就其它什么事情都不做了的。因此,在这里我们要想办法,把CPU解放出来,让它不要白白浪费500MS的延时等待时间。宁可让它一遍又一遍的扫描看有哪些任务需要执行,也不要让它停留在某个地方空转消耗CPU时间。
从上面我们可以总结出
(1)
无论什么时候我们都要以实际应用的角度去考虑程序的编写。
(2)
无论什么时候都不要让CPU白白浪费等待,尤其是延时(超过1MS)这样的地方。
下面让我们从另外一个角度来考虑如何点亮一颗LED。
首先定义LED的接口
#define LED
P0
然后为亮灭常数定义一个宏,假设当P0输出为低电平时候LED亮,P0输出为高电平时,LED熄灭。
#define LED_ON()
LED = 0x00 ;
//所有LED亮
#define LED_OFF()
LED = 0xff ;
//所有LED熄灭
下面到了重点了,究竟该如何释放CPU,避免其做延时空等待这样的事情呢。很简单,我们为系统产生一个1MS的时标。假定LED需要亮500MS,熄灭500MS,那么我们可以对这个1MS的时标进行计数,当这个计数值达到500时候,清零该计数值,同时把LED的状态改变。
unsigned int g_u16LedTimeCount = 0 ;
//LED计数器
unsigned char g_u8LedState = 0 ;
//LED状态标志, 0表示亮,1表示熄灭
void LedProcess(void)
{
if(0 == g_u8LedState)
//如果LED的状态为亮,则点亮LED
{
LED_ON() ;
}
else
//否则熄灭LED
{
LED_OFF() ;
}
}
void LedStateChange(void)
{
if(g_bSystemTime1Ms)
//系统1MS时标到
{
g_bSystemTime1Ms = 0 ;
g_u16LedTimeCount++ ;
//LED计数器加一
if(g_u16LedTimeCount >= 500)
//计数达到500,即500MS到了,改变LED的状态。
{
g_u16LedTimeCount = 0 ;
g_u8LedState
= ! g_u8LedState ;
}
}
}
上面有一个变量没有提到,就是g_bSystemTime1Ms 。这个变量可以定义为位变量或者是其它变量,在我们的定时器中断函数中对其置位,其它函数使用该变量后,应该对其复位(清0) 。
|