"); //-->
框架实现多线程切换;
构件封装了完整的事务函数;
状态模式是基于状态图的编程方式。
一、 文件目录说明:
1、 FrameWork :存放所有的多线程框架文件。
2、 Includes: 头文件
3、 Src: 用户源文件,main.c
4、 OBJ: 工程文件
5、 COMMON: ARM芯片初始化文件
6、 其它目录是软件构件:
TMR_FW:计时器构件
CLK_FW:实时时钟构件
COMM_FW:串口驱动构件
DISP_FW:显示驱动构件
KEY_FW:按健驱动构件
二、 多线程框架文件说明:
1、 FrameWork.c : 调度函数和线程初始化
2、 FrameWork.h:头文件
3、 EventQueue.c:事件队列
4、 EventQueue.h:头文件
5、 FSM.c:事件分发(有限状态机)
6、 FSM.h: 头文件
7、 EventDefer.c:事件延迟(可选是否编译)
8、 EventDefer.h:头文件
9、 FW_CFG.h:框架配置文件
三、 调度原理:
1、 用一个字节变量的每一位代表一个任务是否就绪,1为就绪,0为休眠。
2、 这个字节从高位到低位代表的任务,优先级也从高到低。
3、 通过查表从就绪的任务中找出最高优先级的任务并执行,同时清就绪标志。
就绪表ActObjReadySet
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
位:7 6 5 4 3 2 1 0
任务号:8 7 6 5 4 3 2 1
上表表示有两任务:任务8和任务6 就绪。
因为8位优先级高,我们来查表:
PRIORITY_TABLE[]= {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
ready = ActObjReadySet;// 10100000
if (ready != 0) {
if ((ready & 0xF0) != 0U) {
prionum = PRIORITY_TABLE[ready >> 4] + 4;
}else{
prionum = PRIORITY_TABLE[ready];
}
}
查表结果为4 ,4+4= 8
计算结果为8,所以优先级为8的任务先执行,并清就绪位,完成后再次计算结果为6,优先级为6的任务再执行。
四、 线程间通信:
1、事件队列
线程间通信通过事件队列来完成,
void EvtPostQue(INT8U prio,Event sig,Param par)
void EvtPostQueISR(INT8U prio,Event sig,Param par)
上面两个函数用于事件发送,一个是在线程中,一个是在中断中发送。有三个参数:优先级、事件信号、传递参数(可选)。
事件发送后,放在队列中,同时让对应的线程就绪。
从队列中取出事件信号由调度函数(ActObjScheduler())来完成,用户不用干预,只需发送事件信号就可以了。
2、事件延迟(可选编译项):
BOOLEAN EvtDefer(INT8U prio,Event sig,Param par):向延迟队列发送一个事件信号。(延迟一个事件)
void EvtRecall(INT8U prio):如果延迟队列中有事件,则取出放到事件队列中去处理。(唤醒一个事)
五、在应用中使用框架:
1、在工程文件中加入框架源文件,并修改FW_CFG.h,做配置。
2、main.c中相关函数的调用:
int main (void)
{
InitialMCU();
ActObjInit();
… …
InitialObj();
while(1){
ActObjScheduler();//调度
}
}
void InitialObj(void) { //初始化所用线程
Fsm *a;
ActObjInitTran(1,(State)&COMM1RxTx);//初始化线程1。
ActObjInitTran(2,(State)&COMM2RxTx); //初始化线程2。
ActObjInitTran(3,(State)&COMM3RxTx); //初始化线程3。
ActObjInitTran(4,(State)&COMM4RxTx); //初始化线程4。
… …
}
void COMM1RxTx(Fsm *me)
{
switch (SIG(me)) {
case COMRX_SIG:
… …
}
… …
KEIL UVISION3 工程参考
附录:函数说明
FrameWork.c
1、void ActObjInit(void)
初始化线程和事件队列。
2、void ActObjScheduler(void),调度
找出就绪线程中的最高优先级的线程。
EventQueue.c
3、void EvtPostQue(INT8U prio,Event sig)
向队列发送事件信号,如果当前q->EvtQNRead == 0,
则(((Fsm *)(me_))->Sig_) = sig,否则放在队列中
4、void EvtPostQueISR(INT8U prio,Event sig)
同上个函数,在中断中调用。
FSM.C
5、void Fsm_dispatch(Fsm *me) 事件分发函数
在ActObjScheduler()中调用,参数是一个指向当前线程的指针。
FSM.H
1、 TRAN(target_) 宏替换
状态转换, target_ 是指向当前状态处理器函数的指针。
例如:TRAN(&Obj1State1);
状态转换就是把 *me->State__ 的值换一个函数指针。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。