基于uCOSII的单任务和多任务LED显示一、uCOSII简介uCOS II是一个微型的实时操作系统,包括了一个操作系统最基本的一些特性,如任务调度、任务通信、内存管理、中断管理、定时管理等。
而且这是一个代码完全开放的实时操作系统,简单明了的结构和严谨的代码风格,非常适合初涉嵌入式操作系统的人士学习。
二、设计目的通过实验,学习在uC/OSII操作系统中单任务控制LED闪烁和多个任务控制LED之间的切换和同步,以及多任务控制程序的编写方法。
三、裸机程序和uCOSII的运行流程对比3.1 裸机程序的运行流程裸机主函数的运行流程:这个是我们写一般的单片机程序的流程,就是在主函数中用死循环执行功能函数,然后加上中断。
3.2 uCOSII 的运行流程uCOSII是一个操作系统,但是说到底也是一个支持任务切换的裸机程序。
在初始化变量OSInit函数中,初始化所有全局变量,数据结构,创建最低优先级空闲任务OSTaskIde,并创建6个空数据链表:空任务控制块链表,空事件控制块链表,空列队控制块链表,空标志组链表,空内存控制块链表,空闲定时器控制块链表创建任务OSTaskCreate函数,一般创建一个最高优先级任务TaskStart任务,任务调度后,在这个任务中再创建其他的任务,初始化硬件,并打开中断。
进入多任务管理阶段OSStart函数,将就绪表中最高优先级任务的栈指针加载到SP中,并强制中断返回。
任务调度工作就是查找就绪表中优先级最高的任务,实现任务的切换。
简单来说,裸机程序在主函数中通过死循环执行各种函数,最终达到实现各种功能函数的目的。
而uCOSII系统,通过不断的产生定时中断,或则任务主动放弃CPU,然后进行任务之间的调度,相当于不断循环执行不同的任务,最终实现各种任务。
四、工作原理4.1uCOSII 的内核管理4.1.1 ucos 的文件结构: 应用软件UCOS-11配置(与应用相关)OS_CFG.H INCLUDE.HUcos(与处理器无关的代码)OS_CORE.COS_FLAG.COS_MBOX.COS_MEM.COS_MUTEX.COS_Q.COS_SEM.COS_TASK.COS_TIME.CUCOS_11.C UCOS_11.H UCOS 移植(与处理器相关代码)OS_CPU.H,OS_CPU_A.ASM,OS_CPU_C.C硬件4.1.2 临界段: OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
它们可以用不同的方法去实现,用定义(#define )常数OS_CRITICAL_METHOD(1,2,3)来选择用哪种方法来实现。
当OS_CRITICAL_METHOD=1时,表示用处理器指令关中断,完成OS_ENTER_CRITIACL,用开中断完成OS_EXIT_CRITICAL.利用这种方法有点小问题,即是调用UCOS 功能函数之前,中断是关掉的,从UCOS 返回后,中断就打开了。
当OS_CRITICAL_METHOD=2时,这种方法是在堆栈中保存中断的开关状态,然后再关中断。
在实现OS_EXIT_CRITICAL 时,只需简单的从堆栈中弹出原来中断的开关状态。
利用这种方法,不论用户在调用函数之前中断是开着的还是关着的,函数的进入和返回状态都得到了保护。
当OS_CRITICAL_METHOD=3时,一些编译器提供了扩展功能,用户可以得到当前处理器的状态字,并保存在C 函数的局部变量中,这个变量可以用于恢复PSW 。
在STM32中,我们一般都是用第三种模式。
4.1.3 任务的状态:任务状态之间的转换如下图:特别值得注意的是,在任务执行的过程中可以被中断打断。
4.1.4任务控制快:系统必须为每个任务创建一个保存与该任务有关的相关信息的数据结构,这个数据结构就叫做该任务的任务控制块(TCB)。
当任务的CPU使用权被剥夺时UCOS利用它来保存任务的状态。
4.1.5就绪表:每个就绪的任务都放在就绪表中,就绪表中有两个变量,OSRdyGRP和OSRdyTbl。
OSRdyGrp中每一位表示8组任务中每一组是否有进入就绪态的任务。
当有任务进入就绪态时,就绪表OSRdyTbl[]中相应元素的相应位也置为1。
4.1.6任务调度OS_SCHED():Ucos总是进行进入就绪态任务中优先级最高的任务,确定哪个任务优先级最高,这一工作就是有调度器完成的。
4.1.7任务切换OS_TASK_SW() :需恢复该任务在CPU使用权被剥夺时保存下来的全部寄存器的值,之后,运行被切换的任务。
4.1.8给调度器上锁和开锁:给调度器上锁函数OSSchedlock()用于禁止任务调度,直到任务完成后,调用调度器开锁函数OSSchedUnlock()为止。
这两函数必须成对使用。
4.1.9空闲任务OS_TaskIdle():每个程序必须在初始化时建立一个空闲任务,这个任务没有其他任务进入就绪态时,投入运行,空闲任务永远处于就绪态。
空闲任务始终设为最低优先级。
OSTaskIdle()可以调用OSTaskIdleHook()让CPU进入STOP指令,从而进入低功耗模式。
当应用系统有电池供电时,这种方式特别有用。
4.2uCOSII 的任务管理4.2.1建立任务OSTaskCreat() :如果想让UCOS管理用户的任务,必须先建立任务。
可以通过将任务的地址和其他参数传递到以下两个函数之一来建立任务。
当调用OSTaskCreat()时,需要四个OSTaskCreate(void(*task)(void*pd),void*pdata,OS_STK*ptos,INTU prio)Task:是指向任务代码的指针,pdata:是任务开始执行是,传递给任务的参数的指针,ptos:是分配给任务的堆栈的栈顶指针,prio是分配给任务的优先级。
4.2.2任务堆栈OS_STK():每个任务都有自己的堆栈,堆栈必须申明为OS_STK类型,并且由连续的内存空间组成。
可以静态分配堆栈空间,也可以动态分配堆栈空间。
但是一般我们都是分配静态堆栈空间。
4.2.3删除任务OSTaskDel() :有时需要删除任务,删除任务,是说任务返回并处于休眠态,并不是说任务的代码被删除了。
4.3时间管理4.3.1任务延迟函数OSTimeDly():Ucos提供一个可以被任务调用而将任务延时一段特定时间的功能函数,即OSTimeDly().任务调用OSTimeDly()后,一旦规定的时间期满或者有其他的任务通过调用OSTimeDlyResume()取消了延时,他就会进入就绪状态。
只有当该任务在所有就绪态任务中具有最高的优先级,它才会立即运行。
4.3.2按时,分,秒延时函数OSRimeDLyHMSM():OSTimeDly()一样,调用OSRimeDlyHMSM()函数也会是UCOS进行一次任务调度,并且执行下一个优先级最高的就绪任务。
当OSTimeDlyHMSM()后,一旦规定的时间期满,或者有OSTimeDlyResume(),它就会马上处于就绪态。
同样,只有当该任务在所有就绪态任务中具有最高的优先级,他才开始运行。
五、设计方案:5.1单任务在stm32的平台上移植ucosII,并且建立一个单任务,任务控制LED1以10HZ的频率闪烁。
5.2多任务在stm32的平台上移植ucosII,并且建立三个任务,三个任务分别控制LED1,LED2,LED3以10HZ,20HZ,50HZ的的频率闪烁。
六、程序流程图多任务控制LED闪烁:七、遇到的问题7.1编译器报错,显示PUBLIC未定义由于我们移植的核心代码用IAR编译的,由于编译器的不同,所以要把PUBLIC改成EXPORT。
7.2编译报错,显示,栈未定义由于每个任务都要有独立的栈来保存局部变量,从本质上讲也就是将CPU 寄存器的值保存到RAM中。
在uCOS中,每一个任务都有一个独立的任务堆栈。
八、主要代码8.1主函数#include "includes.h" //包含所有头文件OS_STK startup_task_stk[STARTUP_TASK_STK_SIZE]; //定义起始任务堆栈int main(void){BSP_Init(); //板级初始化OSInit(); //系统初始化,创建空闲任务OSTaskCreate(Task_Start,(void *)0,&startup_task_stk[STARTUP_TASK_STK_SIZE-1], STARTUP_TASK_PRIO);OS_ENTER_CRITICAL();OSStart(); //把控制权交给操作系统,进行任务调度return 0;}8.2用户应用函数#include "includes.h"OS_STK task_led2_stk[TASK_LED2_STK_SIZE]; //任务2堆栈OS_STK task_led3_stk[TASK_LED3_STK_SIZE]; //任务3堆栈OS_STK task_led4_stk[TASK_LED4_STK_SIZE]; //任务4堆栈void Task_Start(void *p_arg) //task1{(void)p_arg; //未用到传递参数,防止编译器报错SysTick_init();OSTaskCreate(Task_LED2,(void *)0, //&task_led2_stk[TASK_LED2_STK_SIZE-1], TASK_LED2_PRIO);OSTaskCreate(Task_LED3,(void *)0, //&task_led3_stk[TASK_LED3_STK_SIZE-1], TASK_LED3_PRIO);OSTaskCreate(Task_LED4,(void *)0, //&task_led4_stk[TASK_LED4_STK_SIZE-1], TASK_LED4_PRIO);while (1){LED1( ON );OSTimeDlyHMSM(0, 0,0,100);LED1( OFF);OSTimeDlyHMSM(0, 0,0,100);}}//任务2void Task_LED2(void *p_arg){(void)p_arg;SysTick_init();while (1){LED2( ON );OSTimeDlyHMSM(0, 0,0,200);LED2( OFF);OSTimeDlyHMSM(0, 0,0,200);}}//任务3void Task_LED3(void *p_arg){(void)p_arg;SysTick_init();while (1){//LED3( ON );OSTimeDlyHMSM(0, 0,0,300);//LED3( OFF);OSTimeDlyHMSM(0, 0,0,300);}}//任务4void Task_LED4(void *p_arg){(void)p_arg;SysTick_init();while (1){LED3( ON );OSTimeDlyHMSM(0, 0,0,40);LED3( OFF);OSTimeDlyHMSM(0, 0,0,40);}}娄宇庭2014年5月5 日。