查看文章 |
最小的多线程框架
摘要:本文介绍一种方法,在8位MCU上进行任务切换,代码编译后大约100字节,可以代替原来的前后台系统。 关键词:多任务,线程,就绪,调度 引言 因为资源和成本的原因,前后台系统是8位MCU上的主流,本文介绍的方法可以在8位MCU上进行任务切换,代码编译后大约100字节,这100字节也会从原来纯前后台系统改到这种框架下节约的代码来补偿,也就是说,提高了性能,而没有增加代码长度,同时也不需要改变原来的编程方式,只是对原有的函数进行调度。可以在1K ROM,64BYTE的RAM上运行。 一、调度原理: 1、 用一个字节变量的每一位代表一个任务是否就绪,1为就绪,0为休眠。 2、 这个字节从高位到低位代表的任务,优先级也从高到低。 3、 通过查表从就绪的任务中找出最高优先级的任务并执行,同时清就绪标志。
就绪表ActObjReadySet
位: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的任务再执行。 二、任务就绪方法 任务就绪是一个宏,写成宏是因为在某一些MCU的编译器中规定不能在中断中调用函数。 #define ActObjSet(prio) (ActObjReadySet |= (1<<(prio-1))) //置就绪标志 比如在定时器中让优先级为5的任务就绪: ActObjSet(5); 实际操作为:ActObjReadySet |=0x10; (编译成汇编代码只一条指令) 把就绪表的第4位置1。 三、任务运行方法 任务运行方法有两种,一种是switch 一种是函数指针。 因为有些8位机的C编译器不支持函数指针,所以本文只介绍switch方式。(注:作者在ARM的多线程框中用的是函数指针)。 在调度原理中我们计算出了优先级号码prionum switch(prionum){ case 0: break; case 8://最高优先级 //任务8的函数放在这里 break; case 7: //任务7的函数放在这里 break; … … 四、任务就绪表上电初始化: ActObjReadySet = 0; 在调度前把就绪表清0就可以了。 五、完整的任务调度函数: void ActObjScheduler(void) { INT8U prionum,ready; prionum = 0; ready = ActObjReadySet; if (ready != 0) { if ((ready & 0xF0) != 0U) {//找出就绪表的最高优先级的任务 prionum = PRIORITY_TABLE[ready >> 4] + 4; }else{ prionum = PRIORITY_TABLE[ready]; } ready = READY_CLR_AND[prionum]; OS_ENTER_CRITICAL();//关中断 ActObjReadySet &= ready;//清就绪位 OS_EXIT_CRITICAL();//开中断 switch(prionum){ case 0: break; case 8://最高优先级 //任务8 break; case 7: //任务7 break; …… case 2: //任务2 break; case 1: //任务1 break; } } } 六、程序编写方法 1、主函数 void main(void) { InitialMCU(); ActObjReadySet = 0; while(1){ ActObjScheduler(); } } 2、中断函数 void ISR_Timer(void) { TmrCtr ++; if(TmrCtr > 5){//40ms TmrCtr = 0; ActObjSet(8); //让定时执行的任务就绪 } } void ISR_AD(void) { _adf = 0; ADValue = _adrh; ActObjSet(3);//让计算任务就绪 } 3、任务函数 和其它函数没有区别 void AlarmOut() { if(AlarmOutctr > 0){ AlarmOutctr --; PFD_OUT = !PFD_OUT; TmrStart(4,15);//1s }else{ ConctrolStat = END_STAT; PFD_OUT = 0; } } 七、使用任务调度的优势 1、多个线程同时就绪时,高优先级先执行。 2、高优先级线程,最长等待时间是上一个正执行线程的完成时间 3、因为主循环时间最长时是最长线程的执行时间,所以有些中断中执行的代码可以移到任务中。 4、可以减少条件语句。 5、使软件结构更合理,清晰。
八、结语: 本文介绍的方法在HOLTEK系列8位MCU和NXP的LPC900中有数十个项目的应用。并且在这基础上把switch改为函数指针,加上事件队列和事件延迟后,在LPC2000的ARM上成功应用。
注:本文中任务和线程等同。 |
本文引用通告地址:http://haven.spaces.eepw.com.cn/articles/trackback/item/34072