楼主 | 回复于 2018-08-28 沙发
楼主 | 回复于 2018-08-28 2#
程序1嵌入式程序框架
void main(void) {
Init();
while(1) {
TaskA();
TaskB();
TaskC();
}
}
void Interrupt_1(void) interrupt 1 {
…
}
void Interrupt_2(void) interrupt 2 {
…
}
楼主 | 回复于 2018-08-28 3#
◆ 更好地支持多任务,实时性要求能够得以保障;
◆ 程序开发更加容易,也更便于维护;
◆ 有利于提高系统的稳定性和可靠性。但是,操作系统的引入也将带来较多的系统开销:
◆ 实时操作系统往往使用定时器中断来切换任务,需要消耗不少的CPU处理时间;
◆ 实时操作系统在切换任务时需要保护当前任务的执行现场,这就需要为每个任务准备足够多的RAM空间来实现任务切换;
◆ 实时操作系统的本身也需要占用相当数量的Flash空间和RAM空间。
如果这些系统开销都在可承受的范围内,那么采用实时操作系统将是最佳的选择。但是在很多应用的场合,特别是系统的资源非常紧张的单片机应用,实时操作系统带来的系统开销往往是不可接受的。而更换速度更快、RAM更大、Flash更多的CPU意味着成本的增加,且会降低产品的竞争力。当系统中的任务不须进行非常复杂的优先级调度,而且其任务也相对简单时,引入实时操作系统似有杀鸡用牛刀之嫌。
楼主 | 回复于 2018-08-28 4#
Protothread是专为资源有限的系统设计的一种耗费资源特别少并且不使用堆栈的线程模型,其特点是:
◆ 以纯C语言实现,无硬件依赖性;
◆ 极少的资源需求,每个Protothread仅需要2个额外的字节;
◆ 可以用于有操作系统或无操作系统的场合;
◆ 支持阻塞操作且没有栈的切换。
使用Protothread实现多任务的最主要的好处在于它的轻量级。每个Protothread不需要拥有自已的堆栈,所有的Protothread共享同一个堆栈空间,这一点对于RAM资源有限的系统尤为有利。相对于操作系统下的多任务而言,每个任务都有自已的堆栈空间,这将消耗大量的RAM资源,而每个Protothread仅使用一个整型值保存当前状态。
楼主 | 回复于 2018-08-28 5#
以下是一个典型的Protothread程序示例:
程序2Protothread程序示例
PT_THREAD(radio_wake_thread(struct pt *pt)) {
PT_BEGIN(pt);
while(1) {
radio_on();
timer_set(&timer, T_AWAKE);
PT_WAIT_UNTIL(pt, timer_expired(&timer));
timer_set(&timer, T_SLEEP);
if(!communication_complete()) {
PT_WAIT_UNTIL(pt, communication_complete()‖timer_expired(&timer));
}
if(!timer_expired(&timer)) {
radio_off();
PT_WAIT_UNTIL(pt, timer_expired(&timer));
}
}
PT_END(pt);
}
楼主 | 回复于 2018-08-28 6#
程序3Protothread宏展开代码
void radio_wake_thread(struct pt *pt) {
switch(pt﹥lc) {
case 0:
while(1) {
radio_on();
timer_set(&timer, T_AWAKE);
pt﹥lc = 8;
case 8:
if(!timer_expired(&timer)) {
return;
}
timer_set(&timer, T_SLEEP);
if(!communication_complete()) {
pt﹥lc = 13;
case 13:
if(!(communication_complete() ||timer_expired(&timer))) {
return;
}
}
if(!timer_expired(&timer)) {
radio_off();
pt﹥lc = 18;
case 18:
if(!timer_expired(&timer)) {
return;
}
}
}
}
}
当Protothread程序运行到PT_WAIT_UNTIL时,判断其运行条件是否满足,若不满足,则阻塞。通过比对程序2和程序3的程序代码可以得知,Protothread的阻塞其实质就是函数返回,只不过在返回前保存了当前的阻塞位置,待下一次Protothread被调用时,直接跳到阻塞位置执行,再次判断运行条件是否满足,并执行后续程序或继续阻塞。
楼主 | 回复于 2018-08-28 7#
与操作系统下的多任务不同,操作系统下的每个任务可在任意时刻被打断并阻塞,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。
楼主 | 回复于 2018-08-28 8#
#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); }
楼主 | 回复于 2018-08-28 9#
程序接上贴
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个字节。
楼主 | 回复于 2018-08-28 10#
本文旨在解决资源紧张型应用的、多任务环境下的实时性问题。 通过借助Protothread的阻塞运行机制,成功实现了低开销的实时多任务系统。
表1 模拟运行结果运行
块
导
航
举报
请选择举报类别
- 广告垃圾
- 违规内容
- 恶意灌水
- 重复发帖