按照Protothreads的定义,lc_t类型就是unsignedshort类型。每个任务分配一个pt结构。将pt结构修改以后,还必须对Protothreads提供的一些功能函数做一些修改。比如,可以将PT_INIT(&pt)更改为PT_INIT(&pt,10,0),表示该任务10 ms执行一次,且ready的初始值为O。队列的实现使用指针数组。
在时间触发模式的系统中,定时器中断作为系统一个固定的时间片,在具体实现中可以设置成CTC模式。这个时间片的选择必须依据具体的应用,设置得过大会对系统调度的时效性造成比较大的影响,过小又会给调度器造成明显的负担,而且压缩任务的执行时间会使程序流程的可预测性受到影响。因为本文所涉及任务的周期大多是若干ms,所以可以将定时器中断设置为1 ms。ISR的执行流程大致如下:每一次定时中断,将任务的count值减1,直到count为O时表明该任务的间隔时间已到可以执行了,并且将初值重新赋给count,以重新开始下轮计数。具体程序如下:
调度函数快速轮询各个任务的TCB。因为定时器中断会定期更新任务的TCB信息,所以调度函数就可以根据TCB中ready的值来判断是否需要执行某任务。执行任务过后清零该ready值。
如果任务task_XXX在执行过程中发生中断,ready值没有被清零,待中断返回后会继续执行之前的任务,但是这样会使得下一时隙任务的执行延迟,造成系统的安全隐患,所以应当尽量避免长任务的出现。而如果在任务执行中出现条件阻塞(如PT_WAIT_UNTIL),则正好可以发挥Protothreads提供的并行处理能力,并且在处理类似键盘扫描的状态机任务时具有很好的逻辑性和清晰度。当然,这样做的前提是:这里的任务的实时性要求不高,允许出现一定的时延。
整个main()函数定义3个任务task_A、task_B和task_C,并且分别给每个任务分配一个结构体pt_A、pt_B和pt_C。3个任务的执行周期分别是10 ms、15 ms和2ms。调度函数处于一个大循环中。具体实现如下所示:
4 总结和展望
Protothreads为嵌入式系统提供了很好的并行处理能力,而且非常易于操作;在时间触发模式的系统中,Pro—tothreads依然能够发挥其巨大的作用。在本文中笔者的设计很好地达到了实际的要求,最大程度上简化了设计和维护。当然,应用Protothreads更加巧妙的设计方法和理念还需要不断地实践和总结。