💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
【87.1 信手拈来的软件定时器。】 初学者会疑惑,51单片机只有2个定时器T0和T1,是不是太少了一点?2个定时器怎能满足实际项目的需要,很多项目涉及到的定时器往往十几个,怎么办?这个问题的奥秘就在本节的内容。 51单片机内置的2个定时器T0和T1,是属于硬件定时器,硬件定时器是一个母体,它可以孕育出N个软件定时器,实际项目中,我们需要多少个定时器只需要从同一个硬件定时器中断里构造出对应数量的软件定时器即可。构造N个软件定时器的框架如下: //“软件定时器1”的相关变量 volatile unsigned char vGu8TimeFlag\_1=0; volatile unsigned int vGu16TimeCnt\_1=0; //“软件定时器2”的相关变量 volatile unsigned char vGu8TimeFlag\_2=0; volatile unsigned int vGu16TimeCnt\_2=0; //“软件定时器3”的相关变量 volatile unsigned char vGu8TimeFlag\_3=0; volatile unsigned int vGu16TimeCnt\_3=0; void main() { vGu8TimeFlag\_1=0; vGu16TimeCnt\_1=1000; //“软件定时器1”的定时时间是1000ms vGu8TimeFlag\_1=1; vGu8TimeFlag\_2=0; vGu16TimeCnt\_2=500; //“软件定时器2”的定时时间是500ms vGu8TimeFlag\_2=1; vGu8TimeFlag\_3=0; vGu16TimeCnt\_3=250; //“软件定时器3”的定时时间是250ms vGu8TimeFlag\_3=1; while(1) //主循环 { if(0==vGu16TimeCnt\_1) //“软件定时器1”的时间到了 { ...在这里执行具体的功能代码 } if(0==vGu16TimeCnt\_2) //“软件定时器2”的时间到了 { ...在这里执行具体的功能代码 } if(0==vGu16TimeCnt\_3 //“软件定时器3”的时间到了 { ...在这里执行具体的功能代码 } } } void T0\_time() interrupt 1 //每1ms中断一次的定时中断函数 { if(1==vGu8TimeFlag\_1&&vGu16TimeCnt\_1>0) //在定时中断里衍生出“软件定时器1” { vGu16TimeCnt\_1--; } if(1==vGu8TimeFlag\_2&&vGu16TimeCnt\_2>0) //在定时中断里衍生出“软件定时器2” { vGu16TimeCnt\_2--; } if(1==vGu8TimeFlag\_3&&vGu16TimeCnt\_3>0) //在定时中断里衍生出“软件定时器3” { vGu16TimeCnt\_3--; } //按上面的套路继续写,可以衍生出N个“软件定时器”,只要不超过单片机的RAM和ROM。 } 【87.2 练习例程。】 现在根据上述程序框架,编写3个LED灯闪烁的程序。第1个LED灯的一闪一灭的周期是2秒,第2个LED灯的一闪一灭的周期是1秒,第3个LED灯一闪一灭的周期是0.5秒。这3个灯的闪烁频率是不一样的,因此需要3个软件定时器。该例子其实也是一个多任务并行处理的典型例子,这3个LED灯就代表3个不同的任务,它们之间是通过switch这个关键语句进行多任务并行处理的。switch的精髓在于根据某个特定条件切换到对应的步骤(或称“跳转到对应的步骤”)。 ![](https://img.kancloud.cn/0f/49/0f49cc4e7b34f0e8c13dd7e514906c88_214x279.png) 图87.2.1 灌入式驱动8个LED \#include "REG52.H" \#define BLINK\_TIME\_1 1000 //时间是1000ms \#define BLINK\_TIME\_2 500 //时间是500ms \#define BLINK\_TIME\_3 250 //时间是250ms sbit P0\_0=P0^0; sbit P0\_1=P0^1; sbit P0\_2=P0^2; //“软件定时器1”的相关变量 volatile unsigned char vGu8TimeFlag\_1=0; volatile unsigned int vGu16TimeCnt\_1=0; //“软件定时器2”的相关变量 volatile unsigned char vGu8TimeFlag\_2=0; volatile unsigned int vGu16TimeCnt\_2=0; //“软件定时器3”的相关变量 volatile unsigned char vGu8TimeFlag\_3=0; volatile unsigned int vGu16TimeCnt\_3=0; unsigned char Gu8Step\_1=0; //软件定时器1的switch切换步骤 unsigned char Gu8Step\_2=0; //软件定时器2的switch切换步骤 unsigned char Gu8Step\_3=0; //软件定时器3的switch切换步骤 void main() { TMOD=0x01; //设置定时器0为工作方式1 TH0=0xfc; //产生1ms中断的TH0初始值 TL0=0x66; //产生1ms中断的TL0初始值 EA=1; //开总中断 ET0=1; //允许定时0的中断 TR0=1; //启动定时0的中断 while(1) //主循环 { //软件定时器1控制的LED灯闪烁 switch(Gu8Step\_1) { case 0: if(0==vGu16TimeCnt\_1) { P0\_0=0; vGu8TimeFlag\_1=0; vGu16TimeCnt\_1=BLINK\_TIME\_1; vGu8TimeFlag\_1=1; Gu8Step\_1=1; } break; case 1: if(0==vGu16TimeCnt\_1) { P0\_0=1; vGu8TimeFlag\_1=0; vGu16TimeCnt\_1=BLINK\_TIME\_1; vGu8TimeFlag\_1=1; Gu8Step\_1=0; } break; } //软件定时器2控制的LED灯闪烁 switch(Gu8Step\_2) { case 0: if(0==vGu16TimeCnt\_2) { P0\_1=0; vGu8TimeFlag\_2=0; vGu16TimeCnt\_2=BLINK\_TIME\_2; vGu8TimeFlag\_2=1; Gu8Step\_2=1; } break; case 1: if(0==vGu16TimeCnt\_2) { P0\_1=1; vGu8TimeFlag\_2=0; vGu16TimeCnt\_2=BLINK\_TIME\_2; vGu8TimeFlag\_2=1; Gu8Step\_2=0; } break; } //软件定时器3控制的LED灯闪烁 switch(Gu8Step\_3) { case 0: if(0==vGu16TimeCnt\_3) { P0\_2=0; vGu8TimeFlag\_3=0; vGu16TimeCnt\_3=BLINK\_TIME\_3; vGu8TimeFlag\_3=1; Gu8Step\_3=1; } break; case 1: if(0==vGu16TimeCnt\_3) { P0\_2=1; vGu8TimeFlag\_3=0; vGu16TimeCnt\_3=BLINK\_TIME\_3; vGu8TimeFlag\_3=1; Gu8Step\_3=0; } break; } } } void T0\_time() interrupt 1 //定时器0的中断函数,每1ms单片机自动执行一次此函数 { if(1==vGu8TimeFlag\_1&&vGu16TimeCnt\_1>0) //在定时中断里衍生出“软件定时器1” { vGu16TimeCnt\_1--; } if(1==vGu8TimeFlag\_2&&vGu16TimeCnt\_2>0) //在定时中断里衍生出“软件定时器2” { vGu16TimeCnt\_2--; } if(1==vGu8TimeFlag\_3&&vGu16TimeCnt\_3>0) //在定时中断里衍生出“软件定时器3” { vGu16TimeCnt\_3--; } TH0=0xfc; //重装初值,不能忘 TL0=0x66; //重装初值,不能忘 }