实验一、任务创建与删除
1、uC/OS-II介绍
对于操作系统的学习,创建任务和删除任务是最为基础的工作,uC/OS-II以源代码的形式发布,是开源软件, 但并不意味着它是免费软件。可以将其用于教学和私下研究;但是如果将其用于商业用途,那么必须通过Micrium获得商用许可。
uC/OS-II属于抢占式内核,最多可以支持64个任务,分别对应优先级0~63,每个任务只能对应唯一的优先级,其中0为最高优先级。63为最低级,系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。 uC/OS-II提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等。
系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一个整型变量做累加运算;另一个是系统任务,它的优先级为次低,该任务负责统计当前cpu的利用率。
μC/OS-II可管理多达63个应用任务,并可以提供如下服务,本章将针对以下服务分别以例程的方式来介绍 1)信号量 2)互斥信号量 3)事件标识 4)消息邮箱 5)消息队列 6)任务管理
7)固定大小内存块管理 8)时间管理
2、任务创建与删除
想让uC/OS-II管理用户的任务,用户必须要先建立任务,在开始多任务调度(即调用OSStart())前,用户必须建立至少一个任务。uC/OS-II提供了两个函数来创建任务:OSTaskCreate()或OSTaskCreateExt()。可以使用其中任意一个即可,其函数原型如下:
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
INT8U OSTaskCreateExt (void(*task)(void *pd),void *pdata,SD_STK *ptos,INT8U prio,INT16U id,OS_STK *pbos,INT32U stk_size,void *pext,INT16U opt) task:任务代码指针
pdata:任务的参数指针
ptos:任务的堆栈的栈顶指针 prio:任务优先级
id:任务特殊的标识符(uC/OS-II中还未使用) pbos:任务的堆栈栈底的指针(用于堆栈检验) stk_size:堆栈成员数目的容量(宽度为4字节) pext:指向用户附加的数据域的指针
opt:是否允许堆栈检验,是否将堆栈清零,任务是否要进行浮点操作等等
删除任务,是说任务将返回并处于休眠状态,任务的代码不再被uC/OS-II调用,而不是删除任务代码。删除任务主要是把任务控制块从OSTCBList链表中移到OSTCBFreeList。uC/OS-II提供了两个函数来删除任务:OSTaskDel()或OSTaskDelReq()。 INT8U OSTaskDel (INT8U prio) //删除任务
INT8U RequestorTask (INT8U prio) //请求删除其他任务 prio:需要删除任务的优先级
3、实验分析
本次实验创建两个任务,任务一每隔两秒秒打印一次“AppTask1”,任务二每隔1s打印一次“AppTask2”,打印6次后打印“删除任务2”并删除任务。其源代码如下: int main (void) {
SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration();
OSInit(); //usos ii初始化 AppTaskCreate();//创建任务 OSStart(); //开始任务调度
}
static void AppTaskCreate(void) {
INT8U err;
OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK )&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1
OSTaskNameSet(APP_TASK1_PRIO, \&err);
OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2
OSTaskNameSet(APP_TASK2_PRIO, \&err); }
//任务1
static void AppTask1 (void *p_arg) {
while(1) {
printf(\ LED1(0);
OSTimeDlyHMSM(0,0,1,0); LED1(1);
OSTimeDlyHMSM(0,0,1,0); }
}
//任务2
static void AppTask2 (void *p_arg) {
INT8U i;
for(i=0;i<6;i++) {
printf(\\\r\\n\
OSTimeDlyHMSM(0,0,0,1); }
printf(\删除任务2\\r\\n\
OSTaskDel(APP_TASK2_PRIO); //删除任务2 }
如下图是串口打印的数据:
实验二、任务调度
本节我们主要介绍的是uC/OS-II任务调度。 1、uC/OS-II任务调度原理
在uC/OS-II创建的任务中,每个任务都是一个无限循环的函数,实现任务的切换需要操作系统完成。用户任务创建后,调用OSStart()开始进行任务调度。任务调度始终会运行就绪列表中优先级最高的任务。
如下图是任务状态的转换表图,正在运行的任务通过调用OS..Pend和延时函数进入等待状态,同时将其从就绪列表中删除,并加入到等待列表中,此时在就绪列表中查找优先级最高的任务设置为当前任务并运行。正在运行的任务通过调用OS..Post或OS..Resume函数使得正在等待挂起的任务进入就绪态,若有任务处于延时等待中,在心跳时钟是延时值减一,当减为零时延时等待任务充等待状态切换到就绪态,同时将其从等待列表中删除,并加入到就绪列表中。如果此任务优先级高于正在运行的任务优先级,则进行任务切换。
下面是系统定时器中断源代码
void SysTick_Handler(void) {
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断 OSIntNesting++;
OS_EXIT_CRITICAL(); //恢复全局中断标志
OSTimeTick(); //延时计数值减一,判断计数时间到后进行任务切换 OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务 //就绪了,则执行一次任务切换 }
2、实验分析
本次实验创建两个任务,任务一循环从串口打印“AppTask1”后延时2s,LED1闪烁一次,任务二循环从串口打印“AppTask2”后延时1s,LED2闪烁一次。其源代码如下: int main (void) {
SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration(); OSInit(); //usos ii初始化 AppTaskCreate();//创建任务 OSStart(); //开始任务调度 }
static void AppTaskCreate(void) {
INT8U err;
OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK )&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1 OSTaskNameSet(APP_TASK1_PRIO, \&err);
OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2 OSTaskNameSet(APP_TASK2_PRIO, \&err); }
//任务1
static void AppTask1 (void *p_arg) {
while(1) {
printf(\ LED1(0);
OSTimeDlyHMSM(0,0,1,0); LED1(1);
OSTimeDlyHMSM(0,0,1,0); } }
//任务2
static void AppTask2 (void *p_arg) {
while(1) {
printf(\\\r\\n\ LED2(0);
OSTimeDlyHMSM(0,0,0,500); LED2(1);
OSTimeDlyHMSM(0,0,0,500); } }
如下图是串口打印的数据,通过修改OSTimeDlyHMSM()的延时值,其中该延时函数的四个参数分别代表时分秒毫秒,可以看到打印速度的变化及两个字符串打印出来的比例。