利用模板建网站,企业邮箱注册申请步骤,济南建设网建筑市场信用信息管理平台,营销型wordpress模板摘抄于大学期间记录在QQ空间的一篇自学笔记#xff0c;当前清理空间#xff0c;本来想直接删除掉的#xff0c;但是感觉有些舍不得#xff0c;因此先搬移过来。
一、UC/OS_II体系结构
二、UC/OS_II中的任务
1、任务的基本概念 在UCOS-II中#xff0c;通常把一个大型任…摘抄于大学期间记录在QQ空间的一篇自学笔记当前清理空间本来想直接删除掉的但是感觉有些舍不得因此先搬移过来。
一、UC/OS_II体系结构
二、UC/OS_II中的任务
1、任务的基本概念 在UCOS-II中通常把一个大型任务分解成多个小任务小任务对应的程序实体就叫做“任务”实际上是一个线程UCOS-II就是一个能对这些小任务的运行进行管理和调度的多任务操作系统。 从任务的存储结构来看UCOS-II由三个组成部分任务程序代码任务的执行部分、任务堆栈用来保存任务工作环境、任务控制块用来保存任务属性。 UCOS-II的任务分为用户任务由应用程序设计者为了解决应用问题而编写和系统任务由系统提供为应用程序来提供某种服务。为了管理上的方便UCOS把每一个任务都作为一个节点然后把它们连接成一个任务链表。目前UCOS-II最多可以对64个任务进行管理。
2、任务的状态 嵌入式系统中只有一个CPU因此一个具体时刻只能允许一个任务占用CPU根据任务是否占用CPU任务在UCOS-II中可能处于下列五种状态之一且可以在不同状态之间的转换。 3、用户任务代码结构 备注main函数中首先初始化ucos-ii在创建3个任务每一个任务为一个函数其格式如上可以被中断的用户代码、关闭中断、不可中断的代码、开启中断、可以被中断的代码。
4、两个系统任务 Ucos-ii预定义了两个为应用程序服务的系统任务空闲任务和统计任务其中空闲任务是每个应用程序必须使用而统计任务是应用程序可以根据实际需要来选择使用。
1空闲任务
系统经常会在某个时间内无用户任务可运行而处于空闲状态为了使cpu没有用户任务执行时有事可做特提供了一个空闲任务OSTaskIdle()的系统任务
系统任务只有一行代码pdatapdata因此什么都没有做但是软件不可栓出这个任务。
2统计任务
Ucos-ii提供了统计任务OSTaskStart()此任务每秒计算一次cpu在单位时间内被使用的时间并把计算结果以百分比的形式存放在变量OSCPUUsage中以便应用程序通过访问它来了解cpu的利用率。
如果用户程序要使用这个统计任务者必须把定义在系统头文件OS_CFG .H中的系统配置常数OS_TASK_STAT_EN设置为1并且必须在创建统计任务之前调用函数OSStatInit()对统计任务进行初始化。
5、任务的优先级 Ucos-ii的每个任务都必须具有一个唯一的优先级别把任务的优先权分为64个优先级别每一个优先级别用一个数字来表示数字0表示任务的优先级别最高数字越大表示任务的优先级越低。 用户程序可以在文件OS_CFG .H中通过给表示最低优先级别的常数OS_LOWEST_PRIO赋值的方法来说明应用程序优先级的数目该常数一旦被定义意味着系统可以供使用的优先级为0、1、2直到OS_LOWEST_PRIO共OS_LOWEST_PRIO1个任务同时系统把最低优先级OS_LOWEST_PRIO自动赋值给空闲任务如果应用程序还使用了统计任务者系统会把OS_LOWEST_PRIO-1自动赋值给统计任务因此用户任务可以使用的优先级别为0、1、2到OS_LOWEST_PRIO-2。
6、任务堆栈 为了满足任务切换和响应中断时保存cpu寄存器中的内容及存储任务私用数据的需要每个任务都应该配有自己的堆栈。
1任务堆栈的定义
为了定义任务堆栈在文件OS_CPU .H中专门定义了一个数据类型OS_STK
Typedef unsigned int OS_STK;
这样在应用程序中定义任务堆栈的栈区即定义一个OS_STK类型的数组即可如下
#define TASK_STK_SIZE 512 //定义堆栈的长度1024字节
#OS_STK TaskStk[TASK_STK_SIZE]; //定义数组作为任务堆栈
调用OSTaskCreate()来创建一个任务把数组指针作为此函数参数完成任务和任务堆栈的关联如下
#define MyTaskStkN 64
OS_STK MyTaskStk[MyTaskStkN];
Void main(void) {//……………OSTaskCreate(yTask, //指向任务的指针void ( * task) (void * pd)MyTaskAgu,//传递任务的参数void *pdata, MyTaskStk[MyTaskStkN-1],//任务堆栈栈顶OS_STK *ptos,20 //指定任务优先级INT8U prio);//…………….
} 注意堆栈增长方向随系统使用处理器不同而不同有的处理器是增长方向向上有的处理器增长方向向下如图 增长方向向下
OSTaskCreate(MyTask,MyTaskAgu,MyTaskStk[MyTaskStkN-1],20);
增长方向向上
OSTaskCreate(MyTask,MyTaskAgu,MyTaskStk[0],20); 2 任务堆栈的初始化
任务堆栈的初始化由UC/OS-II在创建任务函数OSTaskCreate()中通过调用任务堆栈初始化函数OSTaskStkInit()来完成任务堆栈初始化工作其原型如下 OS_STK *OSTaskStkInit( //前三个参数跟创建任务函数参数一样 void (* task)(void *pd), void *pdato, OS_STK *ptos, INT16U opt
);
7、任务控制块 任务控制块负责把任务代码和任务堆栈进行关联使任务控制块、任务代码、任务堆栈成为一个整体并系统要通过这个任务控制块来感知和管理一个任务UCOS-II把系统所有任务的控制块链接为两条链表通过对这两条链表管理各任务控制块。
1 任务控制块的结构
任务控制块是一个结构类型数据当用户调用OSTaskCreate()创建一个用户任务时此函数会对任务控制块中所有成员赋予与该任务相关的数据并驻留在RAM中。
任务控制块结构定义如下 /*其中成员OSCBStat用来存放任务的当前状态该成员变量可能值 OS_STAT_RDY 表示任务处于就绪状态 OS_STAT_SEM 表示任务处于等待信号量状态 OS_STAT_MBOX 表示任务处于等待消息邮箱状态 OS_STAT_Q 表示任务处于等待消息队列状态 OS_STAT_SUSPEND 表示任务处于被挂起状态 OS_STAT_MUTEX 表示任务处于等待互斥信号量状态
*/ 2 任务控制块链表
UCOS-II用两条链表来管理任务控制块一条是空任务块链表OSTCBFreeList其中所有任务控制块还没有分配给任务应用程序调用OSInit()系统初始化时创建另一条是任务块链表OSTCBList(其中所有任务控制块已经分配给任务应用程序调用OSTaskCreate()创建任务时创建)。
系统调用函数OSInit()对系统进行初始化时先在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],这样每个数组元素就是一个任务控制块然后把这些控制块连接成如下一个链表 UC/OS-II初始化时建立的空任务链表的元素个数
OS_MAX_TASKS(用户任务的最大数目)OS_N_SYS_TASKS(系统任务数目其值为1或者2为2时表示一个空闲任务一个统计任务) 应用程序调用系统函数OSTaskCreate()或OSTaskCreateExt()创建一个任务时系统就会将空任务控制块链表头指针OSTCBFreeList指向任务控制块分配给该任务在给任务控制块中各成员赋值后就按任务控制块链表的头指针OSTCBList将其加入到任务控制块链表中。如下图创建了两个用户任务并使用了两个系统任务 3 任务控制块初始化
当用户程序调用函数OSTaskCreate()创建一个任务时这个函数会调用系统函数OSTCBInit()来为任务控制块进行初始化。这个函数首先为被创建任务从任务控制块链表获取一个任务控制块然后用任务的属性对任务控制块各成员赋值最后把这个任务控制块链入到任务控制块链表的头部。
初始化任务控制块函数OSTCBInit()原型 INT8U OSTCBInit( INT8U prio, //任务优先级保存在OSTCBPrio中 OS_STK *ptos, //任务堆栈栈顶指针保存在OSTCBStkPtr中 OS_STK *pbos, //任务堆栈栈底指针保存在OSTCBStkBottom中 INT16U id, //任务的标识符保存在OSTCBId中 INT16U stk_size, //任务堆栈的长度保存在OSTCBStkSize中 void *pext, //任务控制块的扩展指针保存在OSTCBExtPtr中 INT16U opt //任务控制块的选择项保存在OSTCBOpt中
)
4 任务控制块的删除
UC/OS-II允许用函数OSTaskDel()删除一个任务实质上把该任务从任务控制块链表中删掉并把它归于这个空任务控制块链表。 Uc/os-ii还定义了一个OS_TCB * OSTCBCur,专门存放当前正在运行的任务的任务控制块指针。
8、任务就绪表 多任务操作系统的核心就是任务调度调度就是通过一个算法在多个任务中来确定哪个任务来运行这项工作的函数就叫在调度器。UCOS-II任务调度器思想是每时每刻总让优先级最高的就绪任务处于运行状态为了保证这一点它在系统或用户任务调用昔日函数及执行中断服务程序结束时总是调用调度器来确定应该运行的任务并运行它。
1 任务就绪表的结构
系统总是从处于就绪状态的任务中来选择一个任务运行为了使系统直到任务就绪情况UC/OS-II在RAM中设立了一个记录表系统中的每个任务都在这个表中占据一个位置并用这个位置的状态1或者0来表示任务是否处于就绪状态。
UC/OS-II用数组INT8U OSRdyTbl[]来充当这个任务就绪表在这个任务就绪表中以任务优先级别高低顺序为每个任务安排了一个二进制位并规定该位值为1表示对应的任务处于就绪状态否则非就绪状态。
由于每个任务的就绪状态只占一位因此OSRdyTbl[]数组的一个元素可表达8个任务的就绪状态即一个数组元素描述了8个任务的就绪状态把这个8个任务看出一个任务组如果OSRdyTbl[]数组有8个元素者可表示64个任务状态UC/OS-II又定义变量INT8U OSRdyCrp使该变量的每一个位都对应OSRdyTbl[]的一个任务组并规定如果某任务组中有任务就绪者变量OSRdyCrp把该任务组对应的位置为1否则为0。
例如; OSRdyGrp11100101,那么OSRdyTbl[0]、OSRdyTbl[2]、OSRdyTbl[5]、OSRdyTbl[6]、OSRdyTbl[7]任务组中有任务就绪。
任务就绪表如下INT8U OSRdyTbl[] 任务组如下INTU8 OSRdyGrp 2 任务就绪表的操作
任务优先级、任务组和任务就绪表的关系如下图 把优先级为prio的任务置为就绪状态
OSRdyGrp | OSMapTbl[prio3]; //提取三四五位得到是哪一个任务组
OSRdyTbl[prio3] | OSMapTbl[prio0x07];//提取任务在数组元素中对应位置 其中OSMapTbl[]是UC/OS-II为加快运算速度定义的一个数组它各元素值为 OSMapTbl[0]00000001B OSMapTbl[4]00010000B OSMapTbl[1]00000010B OSMapTbl[5]00100000B OSMapTbl[2]00000100B OSMapTbl[6]01000000B OSMapTbl[3]00001000B OSMapTbl[7]10000000B 如果要使一个优先级为prio的任务脱离就绪状态 if((OSRdyTbl[prio3]-OSMapTbl[prio0x07])0) OSRdyGrp-OSMapTbl[prio3]; 从任务就绪表中获取优先级别最高的就绪任务 yOSUnMapTal[OSRdyGrp]; //获取优先级别的D5、D4、D3位 xOSUnMapTal[OSRdyTbl[y]]; //获取优先级别的D2、D1、D0位 prio(y3)x; //获得就绪任务的优先级别 或 yOSUnMapTbl[OSRdyGrp]; prio(INT8U)((y3)OSUnMapTbl[OSRdyTbl[y]]); 其中OSUnMapTbl[]是用来提高查找速度的数组。
9、任务的调度 任务调度器的主要工作有两项一是在任务就绪表中查找具有最高优先级的就绪任务二是实现任务的切换。UC/OS-II有两种调度器一种是任务级的调度器由OSSched()函数实现调度另一种是中断级的调度器由OSIntExt()来实现调度。任务调度器把任务切换的工作分为两个步骤第一步获得待运行任务的TCB指针第二步是进行断点数据的切换。 1获得待运行就绪任务控制块的指针 因为被中止任务的任务控制块指针存放在全局变量OSTCBCur中所以调度器这部分的工作主要是获得待运行任务的任务控制块指针。 任务调度器OSSched()源代码如下 调度器OSSched()源码解析 UC/OS-II允许应用程序通过调用函数OSSchedLock()和OSSchedUnlock()给调度器上锁和解锁。调度器每被上锁一次变量OSLockNesting就加1调度器每被解锁一次变量OSLockNesting,就减1。调度器OSSched()在确认未被上锁并且不是中断服务程序调用调度器的情况下首先任务就绪表中查得的最高优先级别就绪任务的优先级别OSPrioHighRdy,然后确认这个就绪任务不是当前正在运行的任务OSPrioCur是存放正在运行任务的优先级变量的条件下用数组OSTCBPrioTble[OSPrioHighRdy]的值即待运行就绪任务的任务控制块指针赋给指针变量OSTCBHighRdy。
于是下面可以根据OSTCBHighRdy和OSTCBCur这两个指针分别指向待运行任务控制块和当前任务控制块的指针在宏OS_TASK_SW()中实施任务切换。 2任务切换宏OS_TASK_SW()
10、任务的创建 1OSTaskCreate()创建任务 OSTaskCreate()函数对待创建任务的优先级进行一系列判断确认该优先级合法未被使用之后调用函数OSTaskStkInit()和OSTCBInit()对任务堆栈和任务控制块进行初始化。初始化成功后把任务计数器加1判断UC/OS-II的核是否在运行状态如果OSRunning的值为1者调用OSSched()进行任务调度。 2OSTaskCreateExt()创建任务其原型 1 创建任务一般方法
Void main(void)
{ …… OSInit(); //对UC/OS-II进行初始化 ……. OSTaskCreate(TaskStart,….); //创建起始任务TaskStart OSStart(); //开启多任务调度
} Void TaskStart(void *pdata) { ……. //此处安装并启动UC/OS-II时钟 OSStatInit(); //初始化统计任务 …….. //此处创建其他任务 for(;;) { 起始任务TaskStart代码
}
}
注意UC/OS-II不允许在中断服务程序中创建任务。
11、任务管理函数 所谓挂起一个任务就是停止这个任务的运行。用户任务可通过调用系统提供的函数OSTaskSuspend()来挂起自身或者除空闲任务之外的其他任务且只能在其他任务中通过调用恢复函数OSTaskReume()使其恢复为就绪状态。
1 挂起任务
INT8U OSTaskSuspend(INT8U prio);
参数prio为带挂起任务的优先级别若要挂起自身参数为OS_PRIO_SELF
返回值成功返回OS_NO_ERR否则返回错误具体情况
功能描述一系列判断待挂起的任务是这个函数的任务本身如果是本身必须删除该任务在任务就绪表中的就绪标识并在任务控制块成员OSTCBStart中做了挂起记录之后引发一次任务调度如果带挂起任务不是调用函数的任务本身那么只需要删除任务就绪表中被挂起任务的就绪标志并在任务控制块成员OSTCBStart做了挂起记录即可。
2 恢复任务
INT8U OSTaskResume(INT8U prio);
参数prio为带挂起任务的优先级别
返回值成功返回OS_NO_ERR否则返回错误具体情况
功能描述判断任务确实是已存在的挂起任务同时又不是一个等待任务任务控制块成员OSTCBDly0时就清除任务控制块成员OSTCBStat中的挂起记录并使任务就绪最后调用调度器OSSched()进行任务调度。
3 任务优先级别的修改
INT8U OSTaskChangePrio(
INT8U oldprio, //任务现在的优先级别
INT8U newprio //需要修改的优先级别
);
返回值成功返回OS_NO_ERR否则返回错误具体情况
4 任务的删除
所谓删除一个任务就是把该任务置于睡眠状态即先把任务控制块从任务控制块链表中删除并归还给空任务控制块链表然后再任务就绪表中把该任务的就绪状态置0.
INT8U OSTaskDel(INT8U prio);
参数要删除任务的优先级别若要删除自身参数为OS_PRIO_SELF
5 请求删除任务函数
通常为了防止删除占用资源的任务UC/OS-II提供了一个信号OSTCBDelReq请求删除任务函数OSTaskDelReq()这样提出删除任务请求的任务和被删除任务的双方都使用该函数来访问OSTCBDelReq这个信号从而根据这个信号的状态来决定各自的行为。
INT8U OSTaskDelReq(INT8U prio);
参数提出删除任务请求的任务在调用此函数时参数为待删除任务的优先级被删除任务在调用此函数时参数为OS_PRIO_SELF
功能描述删除任务请求方调用此函数目的是查看被删除的任务控制块是否还在如果不在认为被删除任务已经被删除否则令被删除任务的任务控制块成员OSTCBDelReq的值为OS_TASK_DEL_REQ表示该任务在合适的时候删除自己。
例如 被删除任务调用此函数时判断参数为OS_PRIO_SELF时就会返回自己任务控制块成员OSTCBDelReq的值若该值为OS_TASK_DEL_REQ意味有其他任务发来栓出自己的请求那么就应该在适当的时候调用OSTaskDel(OS_PRIO_SELF)来删除自己。
例如 6 查询任务信息函数
INT8U OSTaskQuery( INT8U prio, //待查询任务的优先级别 OS_TCB *pdata //存储任务信息的结构
); 返回值成功将返回OS_NO_ERR并把查询得到的任务信息存放在结构OS_TCB类型变量中。
12、UC/OS-II的初始化和任务启动
1UC/OS-II的初始化 在使用uc/os-ii的所有服务之前必须调用OSInit()函数对自身的运行环境进行初始化。 OSInit()对UC/OS-II的所有全局变量和数据结构进行初始化同时创建空闲任务OSTaskIdlc或者统计任务,其对数据结构进行初始化主要是创建包括空任务控制块链表在内的5个空数据缓冲区同时为了快速的查询任务控制块链表中的各个元素OSInit()还得创建数组OSTCBPrioTbl[OS_LOWEST_PRIO1]。经过初始化后系统数据结构如下 2UC/OS-II的启动 UC/OS-II进行任务的管理是从调用启动函数OSStart()开始的当然前提条件是调用之前至少创建了一个用户。
void OSStart (void)
{ if (O SRunning OS_FALSE) //若OS是未处于运行状态
{ OS_SchedNew(); //查找最高优先级 OSPrioCur OSPrioHighRdy; OSTCBHighRdy OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ }
}
OSStartHighRdy()在多任务系统启动函数OSStart()中调用实现的功能是设置系统运行标位OSRunningTRUE将就绪表中最高优先级任务的栈指针加载到SP中并强制中断返回这样就绪的最高优先级任务就如同从中断里返回到运行态一样使得整个系统得以运作。 三、UC/OS_II的中断和时钟
1、UC/OS-II的中断 UC/OS-II系统响应中断的过程是系统接收到中断请求后如果此时CPU处于中断允许状态系统就会中止当前任务按照中断向量转而运行中断服务子程序当中断服务子程序运行结束后系统就会根据情况返回到被中止的任务继续运行或者进行一次调度运行到另一个更高优先级的就绪任务。中断可嵌套UCOS-II定义了一个全局变量OSIntNesting来记录中断嵌套的层数。 在编写中断服务程序时要用到两个重要的函数OSIntEnter()和OSIntExit()。一个中断服务子程序的流程图如下 1 进入中断服务函数OSIntEnter
void OSIntEnter (void)
{ if (OSRunning OS_TRUE) { if (OSIntNesting 255u) { OSIntNesting; //中断嵌套层数计数器加1 }}}
说明此函数经常在中断服务程序被中断任务的断点数据之后运行用户中断服务代码之前调用统计嵌套层数因此叫做进入中断服务函数。
2 退出中断服务函数OSIntExit: 说明此函数判断中断嵌套层数为0调度器未被锁定且从任务就绪表中查找到得最高优先级就绪任务又不是被中断的任务的条件下调用OSIntCtxSw进行任务切换否则返回被中断的服务子程序。
3 中断级任务切换函数OSIntCtxSw:
中断级任务切换函数OSIntCtxSw()与任务级切换函数OSCtxSw()的一样通常由汇编代码编写如下 OSCtxSw ;任务级切换函数 LDR R0, NVIC_INT_CTRL LDR R1, NVIC_PENDSVSET STR R1, [R0] ;设置ICSR寄存器产生pendSV异常 BX LR OSIntCtxSw ;中断级任务调用 LDR R0, NVIC_INT_CTRL LDR R1, NVIC_PENDSVSET STR R1, [R0] BX LR
4 临界段
CPU只有在中断开放期间才能响应中断请求而在其他时间不能响应中断请求。为增加代码移植性UCOS-II用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个宏来实现中断的开放和关闭而把与系统硬件相关的关中断和开中断指令分别封装在这两个宏中。
2、UC/OS-II的时钟 用硬件定时器产生一个周期为ms级的周期性中断来实现系统时钟最小的时钟单位就是两次中断之间的时间间隔这个最小时钟单位叫做时钟节拍Time Tick。硬件定时器以时钟节拍为周期定时的产生中断该中断服务程序叫做OSTickISR()如下 Void OSTickISR(void) { ………; //保存CPU寄存器; OSIntEnter(); //进入中断服务函数 If(OSIntNesting1) { OSTCBCur-OSTCBStkPtrSP; //保存堆栈指针
}
OSTimeTick(); //调用节拍处理函数
………; //此处清除中断
………; //此处开中断
OSIntExit(); //退出中断服务函数
………; //恢复CPU寄存器中断返回
}
1 时钟节拍服务函数OSTimeTick
时钟中断服务程序中调用了OSTimeTick()此函数做了两件事情一是给计数器OSTime加1二是遍历任务控制块链表中的所有任务控制块把各个任务控制块中用来存放任务延时时限成员变量OSTCBDly减1并使该项为0同时又不被挂起的任务进入就绪状态即在每个时钟节拍了解每个任务的延时状态使其中已经到了延时时限的非挂起任务进入就绪状态。其源码如下 2 时钟节拍钩子函数OSTimeTickHook
OSTimeTick()是系统调用的函数为了方便应用程序设计人员在系统调用的函数中插入一些自己的工作UC/OS-II提供了时钟节拍服务函数的钩子函数OSTimeTickHook(). 3 获取和设置系统时钟
系统定义了一个INT32U类型的全局变量OSTime来记录系统发生的时钟节拍数OSTime在应用程序调用OSStart()时被初始化为0以后每发生1个时钟节拍其值就被加1。应用程序调用OSTimeGet()可获取OSTime的值调用OSTimeSet可设置OSTime Void OSTimeGet(void); //返回值为OSTime Void OSTimeSet(INT32U ticks); //参数为OSTime的设置值
4 任务的延时
为使高优先级的任务不至于独占CPUUC/OS-II规定除了空闲任务之外的所有任务必须在任务中合适的位置调用系统提供的函数OSTimeDly()使当前任务的运行延时一段时间并进行一次任务调度以让出CPU使用权。 调用了函数OSTimeDly()或OSTimeDlyHMSM()的任务当规定的延时时间期满或有其他任务通过调用函数OSTimeDlyResume()取消延时是它会立即进入就绪状态。