关于多线程工作
定时中断实现任务切换
如图 4A 所示,CPU 在空闲任务循环等待,定时中断将 CPU 周期性唤回,根据任务设计
了不同的响应频度,满足条件的任务将获得CPU资源,CPU为不同任务“关照”完成后,再
次返回空闲任务,如此周而复始,对于各个任务而言,好像各自拥有一个独立的CPU,各自
独立运行。用这种思想构建的程序框架,最大的好处是任务很容易裁剪,系统能够做得很复
杂。
在充分考虑单片机中断特性(在哪里中断就返回到哪里)后,实际可行的任务切换如图
4B所示,定时中断可能发生在任务调度,随机任务执行的任何时候,图中最大的框框所示,
不管中断在何时发生,它都会正常返回,定时中断所产生的影响只在任务调度模块起作用,
即依次让不同的任务按不同的节拍就绪。任务调度会按一定的优先级执行就绪任务。
总结不同的任务需要CPU关照的频度,选择最快的那个频度来设定定时器中断的节拍,
一般选择 200Hz,或者 100Hz 都可以。另外再给每个任务设定一个节拍控制计数器 C,也就
是定时器每中断多少次后执行任务一次。例如取定时中断节拍为 200Hz,给任务设定的 C=10,
则任务执行频度为 200/10=20Hz,如果是数码管扫描,按 40Hz 不闪烁规律,则任务节拍控制
计数器 C=5 即可。在程序设计中,C 代表着任务运行的节拍控制参数,我们习惯用 delay 来
描述,不同的任务用task0,task1……来描述。
明天继续写如何用代码实现!2009-6-29
下面我们来用代码实现以上多任务程序设计思想。
首先是任务切换
while(1)
{
if(task_delay[0]==0) task0(); //task0就绪,
if(task_delay[1]==0) task1(); //task1就绪,
……
}
很显然,执行任务的条件是任务延时量task_delay=0,那么任务延时量谁来控制呢?定时
器啊!定时器中断对任务延时量减一直到归零,标志任务就绪。当没有任务就绪时,任务切
换本身就是一个Idle 任务。
void timer0(void) interrupt 1
{
if(task_delay[0]) task_delay[0]--;
if(task_delay[1]) task_delay[1]--;
……
}
例如 timer0 的中断节拍为 200Hz,task0_delay 初值为 10,则 task0()执行频度为
200/10=20Hz。
有了以上基础,我们来设计一个简单多任务程序,进一步深入理解这种程序设计思想。
任务要求:用单片机不同 IO 脚输出 1Hz,5Hz,10Hz,20Hz 方波信号,这个程序很短,将
直接给出。
#include "reg51.h"
#define TIME_PER_SEC 200 //定义任务时钟频率,200Hz
#define CLOCK 22118400 //定义时钟晶振,单位Hz
#define MAX_TASK 4 //定义任务数量
extern void task0(void); //任务声明
extern void task1(void);
extern void task2(void);
extern void task3(void);
sbit f1Hz = P1^0; //端口定义
sbit f5Hz = P1^1;
sbit f10Hz = P1^2;
sbit f20Hz = P1^3;
unsigned char task_delay[4]; //任务延时变量定义
//定时器0初始化
void timer0_init(void)
{
unsigned char i;
for(i=0;i<MAX_TASK;i++) task_delay=0; //任务延时量清零
TMOD = (TMOD & 0XF0) | 0X01; //定时器 0工作在模式 1, 16Bit定时器模
式
TH0 = 255-CLOCK/TIME_PER_SEC/12/256;
TL0 = 255-CLOCK/TIME_PER_SEC/12%256;
TR0 =1;
ET0 =1; //开启定时器和中断
}
// 系统 OS定时中断服务
void timer0(void) interrupt 1
{
unsigned char i;
TH0 = 255-CLOCK/TIME_PER_SEC/12/256;
TL0 = 255-CLOCK/TIME_PER_SEC/12%256;
for(i=0;i<MAX_TASK;i++) if(task_delay) task_delay--;
//每节拍对任务延时变量减1 ,减至 0 后,任务就绪。
}
/*main主函数*/
void main(void)
{
timer0_init();
EA=1;//开总中断
while(1)
{
if(task_delay[0]==0) {task0(); task_delay[0] = TIME_PER_SEC/ 2;}
//要产生 1hz 信号,翻转周期就是 2Hz,以下同
if(task_delay[1]==0) {task1(); task_delay[1] = TIME_PER_SEC/10;}
//要产生 5hz 信号,翻转周期就是 10Hz,以下同
if(task_delay[2]==0) {task2(); task_delay[2] = TIME_PER_SEC/20;}
if(task_delay[3]==0) {task3(); task_delay[3] = TIME_PER_SEC/40;}
}
}
void task0(void)
{
f1Hz = !f1Hz;
}
void task1(void)
{
f5Hz = !f5Hz;
}
void task2(void)
{
f10Hz = !f10Hz;
}
void task3(void)
{
f20Hz = !f20Hz;
}
(他的程序粘贴下来我自己改了一下)
#include <reg51.h>
#define TIME_PER_SEC 200 //定义任务时钟频率,200Hz
#define CLOCK 22118400 //定义时钟晶振,单位Hz
#define MAX_TASK 4 //定义任务数量
extern void task0(void); //任务声明
extern void task1(void);
extern void task2(void);
extern void task3(void);
sbit f1Hz = P1^0; //端口定义
sbit f5Hz = P1^1;
sbit f10Hz = P1^2;
sbit f20Hz = P1^3;
unsigned char task_delay[4]; //任务延时变量定义
//定时器0初始化
void timer0_init(void)
{
unsigned char i;
for(i=0;i<MAX_TASK;i++) task_delay[i]=0; //任务延时量清零
TMOD = (TMOD & 0XF0) | 0X01; //定时器 0工作在模式 1, 16Bit定时器模式
TH0 = 220;
TL0 = 30; //定时器0定时周期10us
TR0 =1;
ET0 =1; //开启定时器和中断
}
// 系统 OS定时中断服务
void timer0(void) interrupt 1
{
unsigned char i;
TH0 = 220;
TL0 = 30;
for(i=0;i<MAX_TASK;i++)
{
if(task_delay[i])
task_delay[i]--;
if(i==MAX_TASK)
i=0;
}
//每节拍对任务延时变量减1 ,减至 0 后,任务就绪。
}
/*main主函数*/
void main(void)
{
timer0_init();
EA=1;//开总中断
while(1)
{
if(task_delay[0]==0) {task0(); task_delay[0] = TIME_PER_SEC/ 2;} //执行一次后不断填充新值
//要产生 1hz 信号,翻转周期就是 2Hz,以下同
if(task_delay[1]==0) {task1(); task_delay[1] = TIME_PER_SEC/10;} //执行一次后不断填充新值
//要产生 5hz 信号,翻转周期就是 10Hz,以下同
if(task_delay[2]==0) {task2(); task_delay[2] = TIME_PER_SEC/20;} //执行一次后不断填充新值
if(task_delay[3]==0) {task3(); task_delay[3] = TIME_PER_SEC/40;} //执行一次后不断填充新值
}
}
void task0(void)
{
f1Hz = !f1Hz;
}
void task1(void)
{
f5Hz = !f5Hz;
}
void task2(void)
{
f10Hz = !f10Hz;
}
void task3(void)
{
f20Hz = !f20Hz;
}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。