Bootstrap

uCOS-III 的特点、任务状态、启动

一、 uCOS-III  特点

        uCOS-III是一个可裁剪、可固化、可剥夺的多任务系统,没有任务数目的限制,是 uCOS的第三代内核,uCOS-III有以下几个重要的特性:

  1. 可剥夺多任务管理:uCOS-III和 uCOS-II 一样都属于可剥夺的多任务内核,总是执行当前就绪的最高优先级任务。
  2. 同优先级任务的时间片轮转调度:这个是 uCOS-III 和 uCOS-II 一个比较大的区别,uCOS-III允许一个任务优先级被多个任务使用,当这个优先级处于最高就绪态的时候,uCOS-III 就会轮流调度处于这个优先级的所有任务,让每个任务运行一段由用户指定的时间长度,叫做时间片。
  3. 极短的关中断时间: uCOS-III 可以采用锁定内核调度的方式而不是关中断的方式来保护临界段代码,这样就可以将关中断的时间降到最低,使得 uCOS-III 能够非常快速的响应中断请求。
  4. 任务数目不受限制:uCOS-III 本身是没有任务数目限制的,但是从实际应用角度考虑,任务数目会受到 CPU 所使用的存储空间的限制,包括代码空间和数据空间。
  5. 优先级数量不受限制:uCOS-III 支持无限多的任务优先级。
  6. 内核对象数目不受限制:uCOS-III 允许定义任意数目的内核对象。内核对象指任务、信号量、互斥信号量、事件标志组、消息队列、定时器和存储块等。
  7. 软件定时器:用户可以任意定义“单次”和“周期”型定时器,定时器是一个递减计数器,递减到零就会执行预先定义好的操作。每个定时器都可以指定所需操作,周期型定时器在递减到零时会执行指定操作,并自动重置计数器值。
  8. 同时等待多个内核对象:uCOS-III 允许一个任务同时等待多个事件。也就是说,一个任务能够挂起在多个信号量或消息队列上,当其中任何一个等待的事件发生时,等待任务就会被唤醒。
  9. 直接向任务发送信号:uCOS-III 允许中断或任务直接给另一个任务发送信号,避免创建和使用诸如信号量或事件标志等内核对象作为向其他任务发送信号的中介,该特性有效地提高了系统性能。
  10. 直接向任务发送消息:uCOS-III 允许中断或任务直接给另一个任务发送消息,避免创建和使用消息队列作为中介。
  11. 任务寄存器:每个任务都可以设定若干个“任务寄存器”,任务寄存器和 CPU 硬件寄存器是不同的,主要用来保存各个任务的错误信息,ID 识别信息,中断关闭时间的测量结果等。
  12. 任务级时钟节拍处理:uCOS-III 的时钟节拍是通过一个专门任务完成的,定时中断仅触发该任务。将延迟处理和超时判断放在任务级代码完成,能极大地减少中断延迟时间。
  13. 防止死锁:所有 uCOS-III 的“等待”功能都提供了超时检测机制,有效地避免了死锁。
  14. 时间戳:uCOS-III 需要一个 16 位或 32 位的自由运行计数器(时基计数器)来实现时间测量,在系统运行时,可以通过读取该计数器来测量某一个事件的时间信息。例如,当 ISR 给任务发送消息时,会自动读取该计数器的数值并将其附加在消息中。当任务读取消息时,可得到该消息携带的时标,这样,再通过读取当前的时标,并计算两个时标的差值,就可以确定传递这条消息所花费的确切时间。

二、 uCOS-III  任务状态

uCOS-III的任务状态转换要比uCOS-II复杂的多。在uCOS-III中任务状态被记录在任务TCB中的一个变量 TaskState 中,其取值范围如下:

大部分的服务函数的实现都参考了任务状态转换图(如下图),任何一个任务可以处于8种状态中的任何一种。

uCOS-III系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。uCOS-III的任务状态通常分为以下几种:

  • 就绪( OS_TASK_STATE_RDY):该任务在就绪列表中, 就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。
  • 延时(OS_TASK_STATE_DLY):该任务处于延时调度状态。
  • 等待( OS_TASK_STATE_PEND): 任务调用 OSQPend()、 OSSemPend()这类等待函数, 系统就会设置一个超时时间让该任务处于等待状态, 如果超时时间设置为 0,任务的状态,无限期等下去,直到事件发生。 如果超时时间为 N(N>0),在N 个时间内任务等待的事件或信号都没发生,就退出等待状态转为就绪状态。
  • 运行(Running):该状态表明任务正在执行, 此时它占用处理器, uCOS调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态,其实运行态的任务也是处于就绪列表中的。
  • 挂起( OS_TASK_STATE_SUSPENDED): 任务通过调用 OSTaskSuspend()函数能够挂起自己或其他任务,调用 OSTaskResume()是使被挂起的任务回复运行的唯一的方法。挂起一任务意味着该任务再被恢复运行以前不能够取得 CPU 的使用权,类似强行暂停一个任务。
  • 延时+挂起(OS_TASK_STATE_DLY_SUSPENDED) : 任务先产生一个延时,延时没结束的时候被其他任务挂起,挂起的效果叠加, 当且仅当延时结束并且挂起被恢复了,该任务才能够再次运行。
  • 等待+挂起( OS_TASK_STATE_PEND_SUSPENDED): 任务先等待一个事件或信号的发生(无限期等待) ,还没等待到就被其他任务挂起,挂起的效果叠加,当且仅当任务等待到事件或信号并且挂起被恢复了,该任务才能够再次运行。
  • 超时等待+挂起( OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED): 任务在指定时间内等待事件或信号的产生, 但是任务已经被其他任务挂起。
  • 删除(OS_TASK_STATE_DEL): 任务被删除后的状态,任务被删除后将不再运行,除非重新创建任务。

三、 uCOS-III  启动

1、 uCOS-III启动和初始化
在使用 uCOS-III的时候我们要按照一定的顺序初始化并打开 uCOS-III,我们可以按照下面的顺序:

  • 先调用 OSInit(&err); 函数初始化 uCOS-III。(必须在调用其他uCOS-III的API函数之前调用)       

 2、创建任务,一般我们在 main()函数中只创建一个 start_task 任务,其他任务都在 start_task任务中创建,在调用OSTaskCreate()函数创建任务的时候一定要调用 OS_CRITICAL_ENTER()函数进入临界区,任务创建完以后调用 OS_CRITICAL_EXIT()函数退出临界区。

3、最后调用 OSStart(&err)函数开启 uCOS-III。

注意:我们在调用 OSStart()开启 uCOS-III之前一定要至少创建一个任务,其实我们在调用OSInit()函数初始化 uCOS-III的时候已经创建了一个空闲任务。

       空闲任务的作用还是很大的,它是一个无限的死循环,因为其优先级是最低的,所以任何优先级比它高的任务都能抢占它从而取得 CPU 的使用权,为什么系统要空闲任务呢?因为 CPU 是不会停下来的,即使啥也不干, CPU 也不会停下来,此时系统就必须保证有一个随时处于就绪态的任务,而且这个任务不会抢占其他任务,当且仅当系统的其他任务处于阻塞中,系统才会运行空闲任务,这个任务可以做很多事情,任务统计,钩入用户自定义的钩子函数实现用户自定义的功能等,但是需要注意的是,在钩子函数中用户不允许调用任何可以使空闲任务阻塞的函数接口,空闲任务是不允许被阻塞的。
 

;