利用Protothread实现实时多任务系统
时间:05-30
来源:单片机与嵌入式系统应用
点击:
3 利用Protothread构造实时多任务系统
与操作系统下的多任务不同,操作系统下的每个任务可在任意时刻被打断并阻塞,Protothread仅能在程序员指定位置阻塞。用Protothread实现实时多任务,正是利用了Protothread在指定位置阻塞的特点,让出执行权限给更高优先级的任务先运行。
下面举例说明如何利用Protothread构造实时多任务系统。
系统要求:
TaskA实时任务,30 ms内响应,运行时间<20 ms;
TaskB实时任务,200 ms内响应,运行时间<40 ms;
TaskC非实时任务,响应时间无要求,运行时间>30 ms。
设计思路:
将TaskB和TaskC分成若干步,每步运行时间不超过10 ms(这个时间可视系统需求而定,例如TaskA若为40 ms内响应,则每步可扩至20 ms)。任务以3个Protothread的方式运行。首先执行TaskA,在TaskA执行完成1次后,释放执行权限,让TaskB和TaskC执行。TaskB或TaskC在每执行1步之前检查运行时间,一旦发现30 ms内不够执行1步时,阻塞运行,让出执行权限给TaskA。同样,TaskB和TaskC的调度关系也类似,先运行TaskB,完成时释放执行权限,让TaskC执行;TaskC在每执行1步之前检查运行时间,若发现200 ms内不够执行1步时,阻塞运行,让出执行权限重新交给TaskB。
源程序(Task0TimeCounter、Task1TimeCounter为计数器,每毫秒加1):
#include "ptsem.h"
#define TASKA_MAX_RUN_TIME20
#define TASKA_CYCLE_TIME30
#define TASKB_CYCLE_TIME200
#define TASK_STEP_TIME10
#define TASK0_VALID_TIME TASKA_CYCLE_TIMETASK_STEP_TIME
#define TASK1_VALID_TIME TASKB_CYCLE_TIMETASK_STEP_TIMETASKA_MAX_RUN_TIME *2
/*按照PT_WAIT_UNTIL 的宏定义扩展一个新宏:当程序进入阻塞时发送一信号,告知高优先级任务获得执行权限*/
#define LC_STEP_SET(s,n) s = __LINE__ + n; case __LINE__ + n:
#define PT_SEM_WAIT_UNTIL(pt, s, condition, n)\
do {
LC_STEP_SET((pt)﹥lc,n);
if(!(condition)) {if((s)﹥count==0)
PT_SEM_SIGNAL(pt,s);
return PT_WAITING;
}
} while(0)
struct pt TaskAPt;
struct pt TaskBPt;
struct pt TaskCPt;
struct pt_sem SemRunTaskA;
struct pt_sem SemRunTaskB;
/*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
#define TASKB_STEP(pt) \
PR_SEM_WAIT_UNTIL(pt, & SemRunTaskA,TaskOTimeCounter<=TASKO_VALID_TIME,0)
/*若200 ms内已经不够时间执行1步,则让出TaskB的执行权限*/
/*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
#define TASKC_STEP(pt) \
PT_SEM_WAIT_UNTIL(pt, &SemRunTaskB,Task1TimeCounter<=TASK1_VALID_TIME,0);\
PT_SEM_WAIT_UNTIL(pt, &SemRunTaskA,Task0TimeCounter<=TASK0_VALID_TIME,1)
int ProtothreadTaskA(struct pt *pt) {
PT_BEGIN(pt);
PT_SEM_WAIT(pt, &SemRunTaskA);/*等待其他任务让出执行权限*/
ResetTask0TimeCounter;/*对时间计数器置0*/
TaskA();/*TaskA任务*/
PT_END(pt);
}
int ProtothreadTaskB(struct pt *pt) {
PT_BEGIN(pt);
PT_SEM_WAIT(pt, &SemRunTaskB);/*等待TaskC让出执行权限*/
ResetTask1TimeCounter;/*对时间计数器置0*/
TASKB_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
TaskB_1();/*TaskB任务的第1步*/
TASKB_STEP(pt);
TaskB_2();/*TaskB任务的第2步*/
TASKB_STEP(pt);
TaskB_3();/*…*/
PT_END(pt);
}
int ProtothreadTaskC(struct pt *pt) {
PT_BEGIN(pt);
TASKC_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
TaskC_1();/*TaskB任务的第1步*/
TASKC_STEP(pt);
TaskC_2();/*TaskB任务的第2步*/
TASKC_STEP(pt);
TaskC_3();/*…*/
TASKC_STEP(pt);
TaskC_4();
PT_END(pt);
}
void main(void) {/*系统初始化*/
PT_INIT(&TaskAPt);
PT_INIT(&TaskBPt);
PT_INIT(&TaskCPt);
PT_SEM_INIT(&SemRunTaskA,1);
PT_SEM_INIT(&SemRunTaskB,1);/*运行任务*/
while(1) {
ProtothreadTaskA(&TaskAPt);
ProtothreadTaskB(&TaskBPt);
ProtothreadTaskC(&TaskCPt);
}
}
模拟运行结果如表1所列。运行结果显示,3个任务的运行情况完全满足系统的设计要求。从资源需求来看,完成此例的系统设计,共需要12个字节的RAM空间。笔者进一步对Protothread定义文件做了少许修改和优化,最终仅耗费6个字节。
与操作系统下的多任务不同,操作系统下的每个任务可在任意时刻被打断并阻塞,Protothread仅能在程序员指定位置阻塞。用Protothread实现实时多任务,正是利用了Protothread在指定位置阻塞的特点,让出执行权限给更高优先级的任务先运行。
下面举例说明如何利用Protothread构造实时多任务系统。
系统要求:
TaskA实时任务,30 ms内响应,运行时间<20 ms;
TaskB实时任务,200 ms内响应,运行时间<40 ms;
TaskC非实时任务,响应时间无要求,运行时间>30 ms。
设计思路:
将TaskB和TaskC分成若干步,每步运行时间不超过10 ms(这个时间可视系统需求而定,例如TaskA若为40 ms内响应,则每步可扩至20 ms)。任务以3个Protothread的方式运行。首先执行TaskA,在TaskA执行完成1次后,释放执行权限,让TaskB和TaskC执行。TaskB或TaskC在每执行1步之前检查运行时间,一旦发现30 ms内不够执行1步时,阻塞运行,让出执行权限给TaskA。同样,TaskB和TaskC的调度关系也类似,先运行TaskB,完成时释放执行权限,让TaskC执行;TaskC在每执行1步之前检查运行时间,若发现200 ms内不够执行1步时,阻塞运行,让出执行权限重新交给TaskB。
源程序(Task0TimeCounter、Task1TimeCounter为计数器,每毫秒加1):
#include "ptsem.h"
#define TASKA_MAX_RUN_TIME20
#define TASKA_CYCLE_TIME30
#define TASKB_CYCLE_TIME200
#define TASK_STEP_TIME10
#define TASK0_VALID_TIME TASKA_CYCLE_TIMETASK_STEP_TIME
#define TASK1_VALID_TIME TASKB_CYCLE_TIMETASK_STEP_TIMETASKA_MAX_RUN_TIME *2
/*按照PT_WAIT_UNTIL 的宏定义扩展一个新宏:当程序进入阻塞时发送一信号,告知高优先级任务获得执行权限*/
#define LC_STEP_SET(s,n) s = __LINE__ + n; case __LINE__ + n:
#define PT_SEM_WAIT_UNTIL(pt, s, condition, n)\
do {
LC_STEP_SET((pt)﹥lc,n);
if(!(condition)) {if((s)﹥count==0)
PT_SEM_SIGNAL(pt,s);
return PT_WAITING;
}
} while(0)
struct pt TaskAPt;
struct pt TaskBPt;
struct pt TaskCPt;
struct pt_sem SemRunTaskA;
struct pt_sem SemRunTaskB;
/*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
#define TASKB_STEP(pt) \
PR_SEM_WAIT_UNTIL(pt, & SemRunTaskA,TaskOTimeCounter<=TASKO_VALID_TIME,0)
/*若200 ms内已经不够时间执行1步,则让出TaskB的执行权限*/
/*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
#define TASKC_STEP(pt) \
PT_SEM_WAIT_UNTIL(pt, &SemRunTaskB,Task1TimeCounter<=TASK1_VALID_TIME,0);\
PT_SEM_WAIT_UNTIL(pt, &SemRunTaskA,Task0TimeCounter<=TASK0_VALID_TIME,1)
int ProtothreadTaskA(struct pt *pt) {
PT_BEGIN(pt);
PT_SEM_WAIT(pt, &SemRunTaskA);/*等待其他任务让出执行权限*/
ResetTask0TimeCounter;/*对时间计数器置0*/
TaskA();/*TaskA任务*/
PT_END(pt);
}
int ProtothreadTaskB(struct pt *pt) {
PT_BEGIN(pt);
PT_SEM_WAIT(pt, &SemRunTaskB);/*等待TaskC让出执行权限*/
ResetTask1TimeCounter;/*对时间计数器置0*/
TASKB_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
TaskB_1();/*TaskB任务的第1步*/
TASKB_STEP(pt);
TaskB_2();/*TaskB任务的第2步*/
TASKB_STEP(pt);
TaskB_3();/*…*/
PT_END(pt);
}
int ProtothreadTaskC(struct pt *pt) {
PT_BEGIN(pt);
TASKC_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
TaskC_1();/*TaskB任务的第1步*/
TASKC_STEP(pt);
TaskC_2();/*TaskB任务的第2步*/
TASKC_STEP(pt);
TaskC_3();/*…*/
TASKC_STEP(pt);
TaskC_4();
PT_END(pt);
}
void main(void) {/*系统初始化*/
PT_INIT(&TaskAPt);
PT_INIT(&TaskBPt);
PT_INIT(&TaskCPt);
PT_SEM_INIT(&SemRunTaskA,1);
PT_SEM_INIT(&SemRunTaskB,1);/*运行任务*/
while(1) {
ProtothreadTaskA(&TaskAPt);
ProtothreadTaskB(&TaskBPt);
ProtothreadTaskC(&TaskCPt);
}
}
模拟运行结果如表1所列。运行结果显示,3个任务的运行情况完全满足系统的设计要求。从资源需求来看,完成此例的系统设计,共需要12个字节的RAM空间。笔者进一步对Protothread定义文件做了少许修改和优化,最终仅耗费6个字节。