uCOSIII基础知识

uCOSIII基础知识笔记

文章来源于正点原子:正点原子UCOSIII教程

文章目录

  • uCOSIII基础知识笔记
    • 一、RTOS背景简介
      • 1.前后台系统
      • 2. RTOS系统
    • 二、系统初始化
    • 三、任务管理
      • 1.什么是任务
      • 2.任务的5种状态
      • 3.用户程序不能使用的优先级
      • 4.相关任务函数
      • 5.调度
        • 5.1 任务调度点
        • 5.2 时间片轮转调度与函数
      • 6.系统内部任务
    • 四、时间管理
      • 1.延时、获取与设置时间
        • 1.1 任务的延时
        • 1.2 获取与设置系统时间
      • 2.软件定时器
    • 五、资源管理/同步
      • 1.信号量
      • 2.优先级反转
      • 3.任务内嵌信号量
    • 六、消息传递

一、RTOS背景简介

1.前后台系统

 早期的时候嵌入式并没有“嵌入式操作系统”的概念,直接跑裸机(轮询系统)。“嵌入式操作系统”通常可以把程序分为前台系统后台系统。(应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为。前台程序通过中断来处理事件,后台程序则掌管整个嵌入式系统软、硬件资源的分配、管理以及任务的调度,是一个系统管理调度程序。即wihle(1)中为后台系统,中断为前台系统

2. RTOS系统

  RTOS全称为:Real Time OS(即实时操作系统)。在实时操作系统中,要实现的功能划分为多个任务,一个任务实现一个功能,每个任务都是一个简单程序,通常都有一个死循环。
RTOS操作系统有: UCOS, FreeRTOS, RTX, RT-Thread, DJYOS等。(核心内容:实时内核)
  RTOS可分为可剥夺型和不可剥夺型。可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务。μCOSII为不可剥夺型,而μCOSIII是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。(绝大部分是用C来写得,极少数与处理器密切相关部分使用汇编)μCOSIII任务处理如下图:(此图来源正点原子
uCOSIII基础知识_第1张图片

二、系统初始化

  使用μCOSIII之前必须初始化系统。函数OSInit()用来完成系统初始化,且必须先于其他μCOSIII函数调用,完了之后调用OSStart()函数开启系统。

int main(void)
{
   OS_ERR err;
   .......................
   //其他初始化函数,一般为外设初始化函数
   OSInit(&err);		//初始化UCOSIII
   ..........................
   //其他函数,一般为创建任务函数
   OSStart(&err);  //开启UCOSIII
   while(1);
}

三、任务管理

1.什么是任务

  在裸机系统中,main()函数中的无限循环部分。在多任务系统中,根据不同的功能,把整个系统分割成多个独立的且带无限循环的函数,这些函数称为任务,通常称之为线程。任务/线程实际是一个函数,函数内主体无限循环并且无退出和返回值。
μCOSIII中任务组成有三部分:
  1)任务堆栈: 上下文切换的时候用来保存任务当前环境,即压栈进行现场保护(是一个结构体数组

//任务堆栈大小	
#define START_STK_SIZE 		128
//任务堆栈创建
CPU_STK START_TASK_STK[START_STK_SIZE];

  2)任务控制块: 用来记录任务各个属性/与任务相关信息(是一个结构体

//任务控制块创建
OS_TCB StartTaskTCB;

  3)任务函数: 用户自己编写的处理函数。(是一个函数

//开始任务函数创建
void App_start_task(void *p_arg)
{
	while(1){
	}
}

2.任务的5种状态

  • 休眠: 任务已经在CPU的flash中了,但是还不受μCOSIII管理。
  • 就绪: 系统为任务分配任务控制块,并且任务在就绪表中登记,已经具备运行条件,只要再获得CPU控制权,便可立即执行。
  • 运行: 任务已经获得CPU使用权,正在运行。
  • 等待: 正在运行的任务需要等待一段时间,或者等待某个事件,这个任务就进入了等待状态,此时系统会把CPU使用权转交给其他任务。
  • 中断服务: 发生中断时,当前正在运行的任务就会被挂起,CPU转而去执行服务函数。
    任务状态转换图如图:(此图来源正点原子
    uCOSIII基础知识_第2张图片

3.用户程序不能使用的优先级

  这些优先级都分配给了μCOSIII的5个系统任务。

  • 优先级0: 中断服务服务管理任务 OS_IntQTask()
  • 优先级1: 时钟节拍任务 OS_TickTask()
  • 优先级2: 定时任务 OS_TmrTask()
  • 优先级OS_CFG_PRIO_MAX-2: 统计任务 OS_StatTask()
  • 优先级OS_CFG_PRIO_MAX-1: 空闲任务 OS_IdleTask()

4.相关任务函数

4.1 任务堆栈初始化函数
  把任务初始数据存放到任务堆栈的工作叫任务堆栈初始化。由创建任务初始化函数调用。由于不同CPU对于寄存器和堆栈的操作方式不同,移植过程需要用户根据CPU来编写堆栈初始化。初始化函数为:

CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,
                         void          *p_arg,
                         CPU_STK       *p_stk_base,
                         CPU_STK       *p_stk_limit,
                         CPU_STK_SIZE   stk_size,
                         OS_OPT         opt)
{

4.2 任务创建函数
  μCOSIII中要想使用任务,就要先创建一个任务,创建任务函数为OSTaskCreate (),原型如下:

void  OSTaskCreate (OS_TCB        *p_tcb,        //指向任务控制块OS_TCB
                    CPU_CHAR      *p_name,       //指向任务的任务名字,可以给每个任务取个名字
                    OS_TASK_PTR    p_task,       //执行任务代码,即任务函数
                    void          *p_arg,        //传递给任务的参数(一般给0,表示不传参)
                    OS_PRIO        prio,         //任务优先级,数值越低优先级越高,用户不能使用系统任务使用的那些优先级
                    CPU_STK       *p_stk_base,   //指向任务堆栈的基地址
                    CPU_STK_SIZE   stk_limit,    //任务堆栈的栈深度,用来检测和确保堆栈不溢出
                    CPU_STK_SIZE   stk_size,     //任务堆栈大小
                    OS_MSG_QTY     q_size,       //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                    OS_TICK        time_quanta,  //当使能时间片轮转时的时间片长度,为0时为默认长度(时钟频率/10),(单位为时钟节拍数)
                    void          *p_ext,        //用户补充的存储区,一般是一个数据结构,用来拓展TCB
                    OS_OPT         opt,          //任务选项,有如下选项可设置
                    			//OS_OPT_TASK_NONE      (OS_OPT)(0x0000u)       表示没有任何选项
                    			//OS_OPT_TASK_STK_CHK   (OS_OPT)(0x0001u)       指定是否允许检测该任务的堆栈
								//OS_OPT_TASK_STK_CLR   (OS_OPT)(0x0002u)       指定是否清除该任务堆栈
								//OS_OPT_TASK_SAVE_FP   (OS_OPT)(0x0004u)       指定是否存储浮点寄存器,CPU需要有浮点运算硬件并且有专用代码保存浮点寄存器
								//OS_OPT_TASK_NO_TLS    (OS_OPT)(0x0008u)       指定不需要TLS支持的任务
                    OS_ERR        *p_err)       //存放该函数的返回错误码
{

4.3 任务删除函数
  OSTaskDel() 函数用来删除任务,删除任务不是说删除任务代码,而是注销任务,在有些应用中我们只需要某个任务只运行一次,运行完成后就将其删除掉,比如外设初始化任务,OSTaskDel()函数原型如下:

void  OSTaskDel (OS_TCB  *p_tcb,	//指向要删除的任务OS_TCB(控制块),也可以传递NULL指针来删除调用OSTaskDel()函数的任务自身
                 OS_ERR  *p_err)	//保存返回错误码
{

4.4 任务挂起函数
  有时候有些任务需要停止运行,但是以后还要运行,所以不能删除任务,这样可以使用OSTaskSuspend()函数挂起任务,以便后续使用,函数原型如下:

void   OSTaskSuspend (OS_TCB  *p_tcb,   //指向要挂起的任务OS_TCB(控制块),也可以传递NULL指针来将调用该函数的任务挂起
                      OS_ERR  *p_err)   //保存返回错误码
{

4.5 任务挂起解除函数
  OSTaskResume() 函数用来恢复被OSTaskSuspend()函数挂起的任务,是唯一能恢复被挂起任务的函数。函数原型如下:

void  OSTaskResume (OS_TCB  *p_tcb,   //指向要解挂的任务OS_TCB(控制块),指向一个NULL是无效的,因为该任务正在运行,不需要解挂
                    OS_ERR  *p_err)   //保存返回错误码
{

4.6 任务切换函数
  当要切换到其他任务时,将保存当前任务的现场到当前任务的堆栈中,主要时CPU寄存器值,然后恢复新的现场去执行新的任务,这个过程叫任务切换。任务切换有两种(都是使用汇编写的,因为切换速度要快):
 1)任务级调度函数:OSCtxSw()
 2)中断级切换函数:OSIntCtxSw()

5.调度

  调度器,决定了任务的运行顺序。根据其重要性每个任务都被分配了一个优先级。uC/OS-III 支持多个任务拥有相同的优先级。

5.1 任务调度点
  • 释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度。
  • 任务等待的事情还没发生(等待信号量,消息队列等)。
  • 任务取消等。
  • 创建任务。
  • 删除任务。
  • 删除一个内核对象。
  • 任务改变自身的优先级或者其他任务的优先级。
  • 任务通过调用OSTaskSuspend()*将自身挂起。
  • 任务解挂某个挂起的任务。
  • 退出所有的嵌套中断。
  • 通过OSSchedUnlock()给调度器解锁。
  • 任务调用OSSchedRoundRObinYield()放弃其执行时间片。
  • 用户调用OSSched()。
5.2 时间片轮转调度与函数

  时间片轮转调度器用于时间片轮转调度,使用函数OS_SchedRoundRobin(),此函数由OSTimeTick()或者OS_IntQTask()调用(在os_core.c中定义)
5.2.1 OSSchedRoundRobinCfg()函数
  如果想使用μCOSIII的时间片轮转调度,就要将OS_CFG_SCHED_ROUND_ROBIN_EN置1,且需要调用函数OSSchedRoundRobinCfg(),函数原型如下:

void  OSSchedRoundRobinCfg (CPU_BOOLEAN   en,      //使能时间片轮转调度
                            OS_TICK       dflt_time_quanta,  //设置两个时间片之间为几个节拍,设为0时为默认值OSCfg_TickRate_Hz / 10
                            OS_ERR       *p_err)   //存储返回的错误码
{

5.2.2 OSSchedRoundRobinYield()函数
  调用该函数使任务放弃本次时间片,去执行下一个任务。函数原型如下:

void  OSSchedRoundRobinYield (OS_ERR  *p_err)   //存储返回的错误码

6.系统内部任务

  系统内部任务一共有5个,分别如下:

  • 空闲任务: μCOSIII创建的第一个任务,且是必须创建的任务。(注: 初始化时自动创建,无需用户手动创建)
  • 时钟节拍任务: 也是必须创建的任务。
  • 统计任务: 用于统计CPU使用率和各个任务的堆栈使用量,由宏OS_CFG_STAT_TASK_EN开启或关闭。
  • 定时任务: 给用户提供定时服务(部分内涵调度器),由宏OS_CFG_TMR_EN控制是否使用。
  • 中断任务: 由宏OS_CFG_ISR_POST_DEFERRED控制是否使用。

四、时间管理

1.延时、获取与设置时间

1.1 任务的延时

  为了使高优先级的任务不至于独占CPU使用权,给其他优先级较低的任务获取CPU使用权的机会。这就要在空闲任务外的所有任务中合适的位置调用系统提供的延时函数 ,让当前任务暂停一段时间,并进行任务切换去执行其他任务。延时任务有两种,分别为:OSTimeDly()OSTimeDlyHMSM()
  1)OSTimeDly()函数
 该函数有三种工作模式:相对延时模式周期延时模式绝对延时模式。函数原型如下:

/*opt选项有:
//OS_OPT_TIME_DLY        相对模式
//OS_OPT_TIME_TIMEOUT    和OS_OPT_TIME_DLY模式一样
//OS_OPT_TIME_MATCH      绝对模式
//OS_OPT_TIME_PERIODIC   周期模式
*/
void  OSTimeDly (OS_TICK   dly,     //延时的时间片,这里我用的一个时间片5ms
                 OS_OPT    opt,     //选择工作模式(相对模式,绝对模式,周期模式),有选项如上
                 OS_ERR   *p_err)   //存储返回的错误码
{

  2)OSTimeDlyHMSM()函数
 该函数只在相对延时模式下工作,但是可以设置为小时,分钟,秒,毫秒延时。函数原型如下:

/*       选项                   时间单位        可设范围
OS_OPT_TIME_HMSM_STRICT         hours          (0...99)
                                minutes        (0...59)
                                seconds        (0...59)
                                milliseconds   (0...999)
OS_OPT_TIME_HMSM_NON_STRICT     hours          (0...999)
                                minutes        (0...9999)
                                seconds        (0...65535)
                                milliseconds   (0...4294967295)
*/
void  OSTimeDlyHMSM (CPU_INT16U   hours,     //小时
                     CPU_INT16U   minutes,   //分钟
                     CPU_INT16U   seconds,   //秒
                     CPU_INT32U   milli,     //毫秒
                     OS_OPT       opt,       //选项,有选项如上
                     OS_ERR      *p_err)     //存储返回的错误码
{

  3)取消任务的延时
 延时任务可以在其他任务中调用函数OSTimeDlyResume()取消延时而进入就绪状态,函数中最后会引发一次任务调度。函数原型如下:

void  OSTimeDlyResume (OS_TCB  *p_tcb,    //要取消延时的任务控制块
                       OS_ERR  *p_err)    //存储返回的错误码
{
{
1.2 获取与设置系统时间

  μCOSIII中定义一个CPU_INT32U类型的全局变量OSTickCtr来记录系统时钟节拍数,在调用OSI你太()时初始化为0,以后每发生1个小时节拍,OSTickCtr+1。

  • OSTimeSet() 允许用户改变(设置)当前系统时钟节拍计数器的值(注:慎用)。
  • OSTimeGet() 用来获取当前节拍计数器的值。
  • OSTimeTick()函数
      当时基中断发生时, 时基 ISR 必须调用这个函数。 uC/OS-III 用这个函数更新时基计数值。 OSTimeTick()是 uC/OS-III 的内部函数。(来源于uCOS-III中文翻译.pdf)

2.软件定时器

  定时器为递减计数器,需设置 OS_CFG.H 中的 OS_CFG_TMR_EN 为 1 时软件定时器服务被使能。当递减计数值为0时,会触发某种动作执行,即通过回调函数执行相应的操作。回调函数是用户自己定义的,当定时器满(定时器递减到0)时可以被调用。(注:不要用回调函数调用如下函数OSTimeDly(),OSTimeDlyHMSM(),OS???Pend(),或其它能导致该定时器任务被阻塞或被删除的函数),软件定时器API函数如下:

函数名 功能
OSTmrCreate() 创建和设置定时器模式
OSTmrDel() 删除一个定时器
OSTmrRemainGet() 获得定时器的剩余期限值
OSTmrStart() 开始定时器运行
OSTmrStateGet() 获得定时器当前的状态
OSTmrStop() 暂停定时器

2.1 创建定时器
  定时器可以被设置为 3 种模式: 一次性定时模式无初始定时周期模式(没有初始的定时),有初始定时周期模式(有初始的定时)。创建软件定时器函数原型如下:

//定义定时器
OS_TMR tmr;
//创建定时器
void  OSTmrCreate (OS_TMR                *p_tmr,         //用户定义的定时器
                   CPU_CHAR              *p_name,        //定时器名字
                   OS_TICK               dly,            //初始延时,即定时器第一个周期大小(节拍数)
                   OS_TICK               period,         //重复周期,即第一个周期以外的后面的周期,
                   OS_OPT                opt,            //定时器模式(、周期定时器)
                   OS_TMR_CALLBACK_PTR   p_callback,     //定时器回调函数
                   void                  *p_callback_arg,//一般设为0
                   OS_ERR                *p_err)         //存储返回的错误码
{

2.2 删除定时器
函数原型:

CPU_BOOLEAN  OSTmrDel (OS_TMR  *p_tmr,    //用户定义的定时器
                       OS_ERR  *p_err)    //存储返回的错误码
{

2.3 获得定时器的剩余期限值
函数原型:

OS_TICK  OSTmrRemainGet (OS_TMR  *p_tmr,   //用户定义的定时器
                         OS_ERR  *p_err)   //存储返回的错误码
{

2.4 开启定时器
函数原型如下:

CPU_BOOLEAN  OSTmrStart (OS_TMR  *p_tmr,     //用户定义的定时器
                         OS_ERR  *p_err)     //存储返回的错误码
{

2.5 暂停定时器
函数原型如下:

/*   选项
OS_OPT_TMR_NONE           //直接停止,不做任何动作
OS_OPT_TMR_CALLBACK       //停止后调用一次回调函数
OS_OPT_TMR_CALLBACK_ARG   //给回调函数一个新的参数
*/
CPU_BOOLEAN  OSTmrStop (OS_TMR  *p_tmr,          //用户定义的定时器
                        OS_OPT   opt,            //选择模式
                        void    *p_callback_arg, //停止后给回调函数一个新的参数
                        OS_ERR  *p_err)          //存储返回的错误码
{

五、资源管理/同步

1.信号量

  信号量是一个锁定机制 ,代码需要获得钥匙(信号)才能访问共享资源,否则该代码段被上锁。信号量用于控制对共享资源的保护,但是现在基本用来做任务同步 。通常有两种类型的信号量:二值信号量和多值信号量,μCOSIII由OS_SEM_CTR来设置信号量计数最大值,OS_SEM_CTR>0时信号量可用。只有任务才允许使用信号量, ISR 是不允许的,但将信号量用于标记任务时可以被 ISR 调用。 。信号量是内核对象,通过数据类型 OS_SEM 定义。信号量相关API函数如下:

函数名 功能
OSSemCreate() 创建一个信号量
OSSemDel() 删除一个信号量
OSSemPend() 等待某个信号量
OSSemPendAbort() 取消等待某个信号量
OSSemPost() 释放或标记或提交一个信号量
OSSemSet() 强制设置信号量计数值

1.1 创建信号量
函数原型:

//定义信号量,用于访问共享资源区
OS_SEM   Task_SEM;
//创建信号量
void  OSSemCreate (OS_SEM      *p_sem,    //用户创建的信号量(结构体)
                   CPU_CHAR    *p_name,   //信号量名字
                   OS_SEM_CTR   cnt,      //设置信号量的初始值
                   OS_ERR      *p_err)    //存储返回的错误码
{

1.2 删除信号量
函数原型:

/*     选项                    功能
OS_OPT_DEL_NO_PEND    当没有任务请求信号量时删除
OS_OPT_DEL_ALWAYS     不管还有没有任务请求,直接删除
*/
OS_OBJ_QTY  OSSemDel (OS_SEM  *p_sem,   //用户创建的信号量(结构体)
                      OS_OPT   opt,     //选项,如上
                      OS_ERR  *p_err)   //存储返回的错误码
{

1.3 请求信号量
函数原型:

/*      选项                     模式
OS_OPT_PEND_BLOCKING      信号量无效的时候,挂起任务
OS_OPT_PEND_NON_BLOCKING  信号量无效的时候,任务直接返回
*/
OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,    //用户创建的信号量(结构体)
                       OS_TICK   timeout,  //等待超时时间,过了就继续往下运行,如果设为0则永久等待知道等到
                       OS_OPT    opt,      //模式选项,如上
                       CPU_TS   *p_ts,     //时间戳,记录接收到信号量的时刻,给NULL/0则不要求获取时间戳
                       OS_ERR   *p_err)    //存储返回的错误码
{

1.4 取消等待信号量
函数原型:

/*      选项                             模式
OS_OPT_PEND_ABORT_1         仅终止等待该信号量优先级最高的任务
OS_OPT_PEND_ABORT_ALL       终止所有等待该信号量的任务
OS_OPT_POST_NO_SCHED        禁止在该函数内执行任务调度操作
*/
OS_OBJ_QTY  OSSemPendAbort (OS_SEM  *p_sem,   //用户创建的信号量(结构体)
                            OS_OPT   opt,     //模式选项,如上
                            OS_ERR  *p_err)   //存储返回的错误码
{

1.5 发送/释放信号量
函数原型:

/*
OS_OPT_POST_1         仅向请求此信号量的最高优先级任务发送该信号量
OS_OPT_POST_ALL       向所有等待该信号量的任务发送
OS_OPT_POST_NO_SCHED  本函数不引起任务调度
*/
OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem,   //用户创建的信号量(结构体)
                       OS_OPT   opt,     //模式选项,如上
                       OS_ERR  *p_err)   //存储返回的错误码
{

2.优先级反转

  uC/OS-III 支持一种特殊类型的二值信号量叫做 mutex,用于解决优先级反转问题。mutex 是一个内核对象,它被数据类型 OS_MUTEX 所定义。只有任务才可以使用 mutex(ISR 不可以使用 mutex)。 mutex的API如下:

函数名 功能
OSMutexCreate() 创建一个 mutex
OSMutexDel() 删除一个 mutex
OSMutexPend() 等待一个 mutex
OSMutexPendAbort() 任务取消等待 mutex
OSMutexPost() 释放 mutex

2.1 创建互斥信号量
函数原型:

void  OSMutexCreate (OS_MUTEX  *p_mutex,
                     CPU_CHAR  *p_name,
                     OS_ERR    *p_err)
{

2.2 删除互斥信号量
函数原型:

OS_OBJ_QTY  OSMutexDel (OS_MUTEX  *p_mutex,
                        OS_OPT     opt,
                        OS_ERR    *p_err)
{

2.3 请求互斥信号量
函数原型:

void  OSMutexPend (OS_MUTEX  *p_mutex,
                   OS_TICK    timeout,
                   OS_OPT     opt,
                   CPU_TS    *p_ts,   //时间戳,记录发送、终止或删除信号量的时刻,
                   OS_ERR    *p_err)
{

2.4 取消等待互斥信号量
函数原型:

OS_OBJ_QTY  OSMutexPendAbort (OS_MUTEX  *p_mutex,
                              OS_OPT     opt,
                              OS_ERR    *p_err)
{

2.5 释放互斥信号量
函数原型:

void  OSMutexPost (OS_MUTEX  *p_mutex,
                   OS_OPT     opt,
                   OS_ERR    *p_err)
{

3.任务内嵌信号量

  μCOSIII中每个任务都有内建 的信号量,通过内建信号量不仅可以简化信号量通信的代码,而且比使用独立信号量更有效。这里没有信号量创建函数,任务内建信号量API如下:

函数名 功能
OSTaskSemPend() 等待一个任务信号量
OSTaskSemPendAbort() 取消等待
OSTaskSemPost() 发送信号量给任务
OSTaskSemSet() 设置信号量计数值

3.1 请求任务信号量
函数原型:

/*      选项                     模式
OS_OPT_PEND_BLOCKING      信号量无效的时候,挂起任务
OS_OPT_PEND_NON_BLOCKING  信号量无效的时候,任务直接返回
*/
OS_SEM_CTR  OSTaskSemPend (OS_TICK   timeout,  //等待超时时间,过了就继续往下运行,如果设为0则永久等待知道等到
                           OS_OPT    opt,      //模式选项,
                           CPU_TS   *p_ts,     //时间戳,记录发送、终止或删除信号量的时刻,
                           OS_ERR   *p_err)    //存储返回的错误码
{

3.3 发送/释放信号量
函数原型:

/*
OS_OPT_POST_NONE      不指定特定选项
OS_OPT_POST_NO_SCHED  禁止在本任务内执行任务调度操作
*/
OS_SEM_CTR  OSTaskSemPost (OS_TCB  *p_tcb,   //要释放的任务的内建信号量的任务的任务控制块
                           OS_OPT   opt,     //模式选项,
                           OS_ERR  *p_err)   //存储返回的错误码
{

六、消息传递

你可能感兴趣的:(μC/OS-III,操作系统)