logo资料库

简易0S设计.pdf

第1页 / 共25页
第2页 / 共25页
第3页 / 共25页
第4页 / 共25页
第5页 / 共25页
第6页 / 共25页
第7页 / 共25页
第8页 / 共25页
资料共25页,剩余部分请下载后查看
多任务抢占式调度器 本文档以 ARM9(三星 2410/2440)为平台,介绍一个多任务抢占式调度器------抢占式 任务调度,提供延时,挂起,恢复任务操作。最精简化,没有加入信号量邮箱等同步通信机制。 只实现一个基本任务调度器的功能。 虽然不能称为操作系统,但已体现了小型嵌入式操作系统的精髓。OS 代码不到 1.5K, 核心函数只有几个,思路简单明了。比起 UCOS,更适合用作多任务系统原理的学习入门。 对初学者来说,看 UCOS 的源代码很容易迷糊。 回想初学嵌入式多任务系统时,什么都不懂,Jean J.Labrosse 的经典之作《嵌入式实 时操作系统 uc/osII》看得我一头雾水。事实上,使我对多任务的原理印象最深的是网上的 一篇文章----《建立一个属于自己的 AVR 的 RTOS》。 学习就应该这样,循序渐进。把一步步把简单的东西弄懂了,便没有复杂的了,所谓 水到渠成。 这篇文章是面对初学者的,把很多问题简化了。希望对刚接触嵌入式多任务系统的兄 弟有所帮助。 必定存在不少 bug,欢迎指正。 1
多任务抢占式调度器 简易多任务 OS 设计 -------ARM9 上运行的简单多任务调度器 By lisuwei http://group.ednchina.com/999/20520.aspx 这里提供一个简易的多任务抢占任务调度器供大家学习。虽然太简单不实用,但对理 解多任务抢占式调度的原理是很有益处的。 先直观地看一下多任务系统中的代码与单任务程序(前后台系统)的区别。 Main.c int Main(void) { TargetInit(); // 初始化目标板 OSInit(); // 初始化操作系统 OSTaskCreate(Task0,&StackTask0[StackSizeTask0 - 1],PrioTask0); // 创建一个任务 Uart_Printf("Ready to start OS\n"); OSStart(); // 运行操作系统 return 0; // 程序不会运行至此 } void Task0(void) { TargetStart(); // 设置中断向量,启动操作系统的硬件定时器中断 Uart_Printf("Start OS\n"); // 创建其他任务 OSTaskCreate(Task1,&StackTask1[StackSizeTask1 - 1],PrioTask1); OSTaskCreate(Task2,&StackTask2[StackSizeTask2 - 1],PrioTask2); OSTaskCreate(Task3,&StackTask3[StackSizeTask2 - 1],PrioTask3); while(1) { Uart_Printf("Task0\n"); OSTimeDly(100); //1 } } void Task1(void) { while(1) { Uart_Printf("Task1\n"); OSTimeDly(300); //3 } } 秒运行一次 秒运行一次 2
多任务抢占式调度器 void Task2(void) { while(1) { Uart_Printf("Task2\n"); OSTaskSuspend(PrioTask2); // } } void Task3(void) { while(1) { Uart_Printf("Resume Task2\n"); OSTaskResume(PrioTask2); // OSTimeDly(800); } } 使自己进入挂起状态 恢复任务 2 程序中创建了四个任务,任务 0 每 1 秒运行一次,任务 1 每 3 秒运行一次,任务 2 运 行一次即把自己挂起,任务 3 每 8 秒运行一次并把任务 2 恢复。 在终端的运行结果如下图: 3
多任务抢占式调度器 什么是多任务系统? 就像我们用电脑时可以同时听歌,上网,编辑文档等。在多任务系统中,可以同时 执行多个并行任务,各个任务之间互相独立。通过操作系统执行任务调度而实现宏观上 的“并发运行”。从宏观上不同的任务并发运行,好像每个任务都有自己的 CPU 一样。 其实在单一 CPU 的情况下,是不存在真正的多任务机制的,存在的只有不同的任务 轮流使用 CPU,所以本质上还是单任务的。但由于 CPU 执行速度非常快,加上任务切换 十分频繁并且切换的很快,所以我们感觉好像有很多任务同时在运行一样。这就是所谓 的多任务机制。 多任务的最大好处是充分利用硬件资源,如在单任务时(大循环结构,如大部分 51 程序)遇到 delay 函数时,CPU 在空转;而在多任务系统,遇到 delay 或需等待资源时系 统会自动运行下一个任务,等条件满足再回来运行先前的任务,这样就充分利用了 CPU, 提高了效率。 任务有下面的特性: l 动态性。任务并不是随时都可以运行的,而一个已经运行的任务并不能保证 一直占有 CPU 直到运行完。一般有就绪态,运行态,挂起态等。 运行态。一个运行态的任务是一个正在使用 CPU 的任务。任何时刻有且只有 一个运行着的任务。 就绪态。一个就绪态任务是可运行的,等待占有 CPU 的任务释放 CPU。 挂起态。某些条件不满足而挂起不能运行的状态。 任务三种状态转换图 下面我们来分析代码是如何实现状态转换的。 RTOS.h INT32U OSRdyTbl; /* 就绪任务表 */ 上面定义一个 32 位变量,每一位代表一个任务,0 表示挂起状态,1 表示就绪状 态。它记录了各任务的就绪与否状态,称它为就绪表。OSRdyTbl 定义为 32 位变 量,对应 32 个任务。当然,定义为 64 位的话,便最多能支持 64 个任务。 4
多任务抢占式调度器 这样,可以定义两个宏,实现把任务的状态变为就绪或挂起态。 RTOS.h /* 在就绪表中登记就绪任务 */ #define OSSetPrioRdy(prio) \ { \ OSRdyTbl |= 0x01<
多任务抢占式调度器 什么是抢占式调度? 调度的概念,通俗的说就是系统在多个任务中选择合适的任务执行。系统如何知道 何时该执行哪个任务?可以为每个任务安排一个唯一的优先级别,当同时有多个任务就 绪时,优先运行优先级较高的任务。 同时,任务的优先级也作为任务的唯一标识号。代码中都是对标识号来完成对任务 的操作的。如 OSDelPrioRdy(prio),OSSetPrioRdy(prio)等。 不同的优先级对应就绪表中的每一位。低位对应高优先级。优先级 0 的优先权最高, 优先级 31 的优先权最低。 在程序中要为每一个任务分配一个唯一的优先级。 Main.c ///**************定义任务优先级*************/// #define PrioTask0 0 #define PrioTask1 1 #define PrioTask2 2 #define PrioTask3 3 所谓“抢占式调度”是指:一旦就绪状态中出现优先权更高的任务,便立即剥夺当 前任务的运行权,把 CPU 分配给更高优先级的任务。这样 CPU 总是执行处于就绪条件 下优先级最高的任务。 在程序中查找最高优先级的任务代码如下: RTOS.h /* 在就绪表中查找更高级的就绪任务 */ #define OSGetHighRdy() \ { \ for( OSNextTaskPrio = 0; \ (OSNextTaskPrio < OS_TASKS) && (!(OSRdyTbl & (0x01<
多任务抢占式调度器 多任务系统的时间管理 与人一样,多任务系统也需要一个“心跳”来维持其正常运行,这个心跳叫做时钟 节拍,通常由定时器产生一个固定周期的中断来充当,频率一般为 50-100Hz。在 TargetInit.c 文件中有下面的定时器 0 初始化函数,T0 用作系统心跳计时,产生时钟节拍。 RTOS.h #define OS_TICKS_PER_SEC 100 /* 设置一秒内的时钟节拍数*/ TargetInit.c void StartTicker(INT32U TicksPerSec) { rTCFG0 = 99; //Prescaler0 = 99 rTCFG1 = 0x03; //Select MUX input for PWM Timer0:divider=16 rTCNTB0 = 31250 / TicksPerSec; // rTCON |= (1<<1); //Timer 0 manual update rTCON = 0x09; //Timer 0 auto reload on //Timer 0 output inverter off // //Timer 0 start */ BIT_CLR(rINTMSK, BIT_TIMER0); // Enable WatchDog interrupts } 清"Timer 0 manual update" 设置中断频率 OSTimeDly 函数就是以时钟节拍为基准来延时的。这个函数完成功能很简单,就是先 挂起当起当前任务,设定其延时节拍数,然后进行任务切换,在指定的时钟节拍数到来之 后,将当前任务恢复为就绪状态。任务必须通过 OSTimeDly 或 OSTaskSuspend 让出 CPU 的使用权,使更低优先级任务有机会运行。 RTOS.c void OSTimeDly(INT32U ticks) { if( ticks > 0 ) /* 当延时有效 */ { OS_ENTER_CRITICAL(); OSDelPrioRdy(OSPrioCur); /* TCB[OSPrioCur].OSTCBDly = ticks; /* OS_EXIT_CRITICAL(); OSSched(); /* } } 把任务从就绪表中删去 */ 设置任务延时节拍数 */ 重新调度 */ 7
多任务抢占式调度器 在 T0 的中断服务函数中,依次对各个延时任务的延时节拍数减 1。若发现某个任务 的延时节拍数变为 0,则把它从挂起态置为就绪态。 RTOS.c void TickInterrupt(void) { static INT8U i; OSTime ++; //Uart_SendByte('I'); for(i = 0; i < OS_TASKS; i++) /* { if(TCB[i].OSTCBDly ) { TCB[i].OSTCBDly --; if(TCB[i].OSTCBDly == 0) /* { OSSetPrioRdy(i); /* } } } rSRCPND |= BIT_TIMER0; rINTPND |= BIT_TIMER0; } 刷新各任务时钟 */ 当任务时钟到时,必须是由定时 器减时的才行*/ 使任务可以重新运行 */ 系统自身创建了一个空闲任务,并设它为最低优先级,当系统没有任何任务就绪时, 则运行这个任务,让 CPU“有事可干”。用户程序可以在这个任务中加入一些“无关紧要” 的功能,如统计 CPU 使用率等。 RTOS.c void IdleTask(void) { IdleCount = 0; while(1) { IdleCount++; //Uart_Printf("IdleCount %d\n",IdleCount); } } TIPS: 不要在空闲任务中运行有可能使任务挂起的函数。空闲任务应该一直处于就绪状态。 8
分享到:
收藏