1.前言

FreeRTOS是小型多任务嵌入式操作系统,硬实时性。本章主要讲述任务相关特性及调度相关的知识。

2. 任务的总体特点

  • 任务的状态

(1)任务有两个状态,运行态和非运行态

(2)任务由非运行态转入运行态为切入,相反为切出

  • 设置优先级

(1)最高优先级在FreeRTOSConfig.h 中 设 定 的 编 译 时 配 置 常 量configMAX_PRIORITIES中进行设置。
注:FreeRTOS 本身并没有限定这个常量的最大值,但这个值越大,则内核花销的内存空间就越多。建议将此常量设为能够用到的最小值

(2)任意数量的任务可以共享同一个优先级也可以为每个任务唯一指定一个优先级

(3)低优先级号表示任务的优先级低
(4)有效的优先级号范围从 0 到(configMAX_PRIORITES – 1)
(5)调度器保证总是在所有可运行的任务中选择具有最高优先级的任务,并使其进入运行态
(6)如果被选中的优先级上具有不止一个任务,调度器会让这些任务轮流执行

(7)调度器总是选择所有能够进入运行态的任务中具有最高优先级的任务

  • 改变任务优先级

可以通过vTaskPrioritySet()  来改变任务的优先级

  • 任务时间片

(1)两个任务被创建在同一个优先级上,并且一直是可运行的。所以每个任务都执行一个”时间片”,任务在时间片起始时刻进入运行态,在时间片结束时刻又退出运行态

(2)调度器需要在每个时间片结束时能够调度自己, tick中断可以完成此目的

(3)tick中断频率可以通过FreeRTOSConfig.h 中的编译时配置常量configTICK_RATE_HZ 进行配置,如设为100HZ,则时间片长度为10ms

3.任务状态机

3.1 任务状态机

图 完整的任务状态机

  • 阻塞态(blocked)

(1)如果一个任务正在等待某个事件,则称这个任务处于”阻塞态(blocked)”。阻塞态是非运行态的一个子状态。

(2)任务进入阻塞态一般等待以下两种事件:定时事件和同步事件

(3)任务可以在进入阻塞态等待同步事件时指定一个等待超时时间,这样可以有效地实现阻塞状态下同时等待两种类型的事件,

比如说,某个任务可以等待队列中有数据到来,但最多只等10ms。如果10ms 内有数据到来,或是10ms 过去了还没有数据到来,这两种情况下该任务都将退出阻塞态。

  • 挂起状态(suspended)

(1)处于挂起状态的任务对调度器而言是不可见的

(2)让一个任务进入挂起状态的唯一办法就是调用vTaskSuspend() API 函数

(3)把一个挂起状态的任务唤醒的唯一途径就是调用vTaskResume() 或TaskResumeFromISR() API 函数

(4)大多数应用程序中都不会用到挂起状态

  • 就绪状态(ready)

(1)如果任务处于非运行状态,但既没有阻塞也没有挂起,则这个任务处于就绪(ready,准备或就绪)状态

(2)处于就绪态的任务能够被运行,但只是”准备(ready)”运行,而当前尚未运行

3.2.利用阻塞态实现延迟

 void vTaskDelay( portTickType xTicksToDelay );

调用上面的函数的任务会进入阻塞态,xTicksToDelay表示delay的tick周期数,portTICK_RATE_MS代表每毫秒有多少个tick周期

4. 空闲任务

  • 空闲任务的特点

(1)在调度器启动的时候vTaskStartScheduler会自动创建一个空闲任务

(2)空闲任务具有最低的优先级0,保证不会影响其它更高优先级的任务进入就绪态

(3)运行在最低优先级可以保证一旦有更高优先级的任务进入就绪态,空闲任务就会立即切出运行态

(4)空间任务可以获得的执行时间量,是系统处理能力裕量的一个度量指

  • 空闲任务钩子函数

(1)通过空闲任务钩子函数(或称回调,hook, or call-back),可以直接在空闲任务中添加应用程序相关的功能,空闲任务钩子函数会被空闲任务每循环一次就自动调用一次

(2)空闲任务钩子函数被用于:

  ---执行低优先级,后台或需要不停处理的功能代码。

  ---测试系统处理裕量

  ---将处理器配置到低功耗模式——提供一种自动省电方法,使得在没有任何应用功能需要处理的时候,系统自动进入省电模式。

  • 空闲任务钩子函数必须遵从以下规则

(1)绝不能阻塞或挂起。

  空闲任务只会在其它任务都不运行时才会被执行(除非有应用任务共享空闲任务优先级)。

  以任何方式阻塞空闲任务都可能导致没有任务能够进入运行态!
(2)如果应用程序用到了vTaskDelete() 函数,则空闲钩子函数必须能够尽快返回。

  因为在任务被删除后,空闲任务负责回收内核资源。如果空闲任务一直运行在钩子函数中,则无法进行回收工作。

5.任务的调度

  • 优先级抢占式调度

每个任务都被赋予了一个优先级,这个优先级不能被内核本身改变(只能被任务修改)。

”抢占式”是指当任务进入就绪态或是优先级被改变时,如果处于运行态的任务优先级更高,则该任务总是抢占当前运行的任务

  • 单调速率调度

单调速率调度(Rate Monotonic Scheduling, RMS)是一种常用的优先级分配技术。其根据任务周期性执行的速率来分配一个唯一的优先级。

具有最高周期执行频率的任务赋予高最优先级;具有最低周期执行频率的任务赋予最低优先级。这种优先级分配方式被证明了可以最大化整个应用程序的可调度性(schedulability),但是运行时间不定以及并非所有任务都具有周期性,会使得对这种方式的全面计算变得相当复杂

  • 协作式调度

只可能在运行态任务进入阻塞态或是运行态任务显式调用 taskYIELD()时,才会进行上下文切换。

任务永远不会被抢占,而具有相同优先级的任务也不会自动共享处理器时间。协作式调度的这作工作方式虽然比较简单,但可能会导致系统响应不够快

  • 混合调度

这需要在中断服务例程中显式地进行上下文切换,从而允许同步事件产生抢占行为,但时间事件却不行。

这样做的结果是得到了一个没有时间片机制的抢占式系统。或许这正是所期望的,因为获得了效率,并且这也是一种常用的调度器配置

6.任务调度举例

  • 两个优先级相同的任务时间片执行图如下:

图 优先级相同的两个任务的时间片执行序列

  • 加入了tick中断及tick中断调度器调度下个任务图示如下:

图 对执行流程进行扩展以显示tick中断的执行

  • 由于task2比task1优先级高,因此task1永远得不到执行:

图 Task2比Task1优先级高时的任务运行序列

  • 用vTaskDelay()代替空循环后的执行流程,task1得到了执行,如下图:

图 用vTaskDelay()代替空循环后的执行流程

  • continuous 1与continuous 2具有优先级1,periodic具有优先级2,periodic使用vTaskDelayUntil,调度图如下(注意心跳中断在t5时刻实际是时间片到时调度器执行):

  • 开始任务1具有最高优先级,任务1结束前将任务2设为最高优先级,任务2在结束时将自身设为最低优先级,执行序列如下:

  • idle在任务1进入阻塞态后,会执行对任务2的内存清除工作

  • 三个任务(事件唤醒)的抢占执行过程

6. 一些重要的API

API 含义 重要参数说明 返回值
void ATaskFunction( void *pvParameters ); 任务的原型 参数必须是void * 返回值必须是void
portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
  const signed portCHAR * const pcName,
  unsigned portSHORT usStackDepth,
  void *pvParameters,
  unsigned portBASE_TYPE uxPriority,
  xTaskHandle *pxCreatedTask );
创建任务
  • uxPriority

表示任务的优先级,最好设置成实际任务需要的最小值,以避免浪费

 
void vTaskStartScheduler( void )
开始调度一个任务执行,第一次调度时会自动创建一个空闲任务  
void vTaskDelete( TaskHandle_t xTaskToDelete )
删除一个任务
  • xTaskToDelete

要删除任务的句柄

 
void vTaskDelay( const TickType_t xTicksToDelay )
让当前任务进入阻塞态,并保持多少个tick
  • xTicksToDelay
用来指定任务在调用vTaskDelay()到切出阻塞态整个过程包含多少个tick
 
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )
用于实现一个固定执行周期的需求求(当你需要让你的任务以固定频率周期性执行的时候)
  • pxPreviousWakeTime

保存了任务上一次离开阻塞态(被唤醒)的时刻这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。pxPreviousWakeTime 指向的变量值会在API 函数vTaskDelayUntil()调用过程中自动更新,应用程序除了该变量第一次初始化外(通过调用xTaskGetTickCount()初始化),通常都不要修改它的值

  • xTimeIncrement

此参数命名时同样是假定vTaskDelayUntil()用于实现某个任务以固定频率周期性执行 —— 这个频率就是由xTimeIncrement 指定的。xTimeIncrement 的单位是心跳周期, 可以使用常量

portTICK_RATE_MS 将毫秒转换为心跳周期。

 
void vApplicationIdleHook( void )
空闲钩子函数    
void vTaskPrioritySet( xTaskHandle pxTask, 
    unsigned portBASE_TYPE uxNewPriority )
改变任务优先级
  • pxTask

被修改优先级的任务句柄(即目标任务)

  • uxNewPriority

目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优
先级(configMAX_PRIORITIES – 1),则会被自动封顶为最大值。常
量 configMAX_PRIORITIES 是在 FreeRTOSConfig.h 头文件中设置
的一个编译时选项。

 
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ); 
用于查询一个任务的优先级 
  • pxTask

被查询任务的句柄(目标任务)

返回值 被查询任务的当前优先级 
void vTaskDelete( xTaskHandle pxTaskToDelete ) 
删除任务
  • pxTaskToDelete

被删除任务的句柄(目标任务) 。任务可以通过传入 NULL 值来删除自己

 
taskYIELD 
调用此函数的任务转入就绪态,同时另一个任务转入运行态    

7.参考文档

[1] FreeRTOS中文实用教程

freeRTOS中文实用教程1--任务的更多相关文章

  1. freeRTOS中文实用教程6--错误排查

    1.前言 本章主要是为刚接触FreeRTOS 的用户指出那些新手通常容易遇到的问题.这里把最主要的篇幅放在栈溢出以及栈溢出侦测上 2.printf-stdarg.c 当调用标准C 库函数时,栈空间使用 ...

  2. freeRTOS中文实用教程5--内存管理

    1.前言 不同的嵌入式系统具有不同的内存配置和时间要求.所以单一的内存分配算法只可能适合部分应用程序. FreeRTOS 将内存分配作为可移植层面(相对于基本的内核代码部分而言).这使得不同的应用程序 ...

  3. freeRTOS中文实用教程4--资源管理互斥

    1.前言 访问一个被多任务共享,或是被任务与中断共享的资源时,需要采用”互斥”技术以保证数据在任何时候都保持一致性.这样做的目的是要确保任务从开始访问资源就具有排它性,直至这个资源又恢复到完整状态 F ...

  4. freeRTOS中文实用教程4--资源管理概述

    1.前言 多任务系统中存在一种潜在的风险.当一个任务在使用某个资源的过程中,即还没有完全结束对资源的访问时,便被切出运行态,使得资源处于非一致,不完整的状态 2.并发抢占导致错误的场景 (1)访问外设 ...

  5. freeRTOS中文实用教程3--中断管理之中断嵌套

    1.前言 最新的 FreeRTOS 移植中允许中断嵌套.中断嵌套需要在 FreeRTOSConfig.h 中设置configKERNEL_INTERRUPT_PRIORITY 和configMAX_S ...

  6. freeRTOS中文实用教程3--中断管理之计数信号量

    1.前言 在中断不频繁的系统中,使用二值信号量没有问题,但是中断频繁发生时,则会有中断丢失的问题. 因为中断发生时延迟任务执行,延迟任务执行的过程中,如果又来了两次中断,则只会处理第一次,第二次将会丢 ...

  7. freeRTOS中文实用教程2--队列

    1.前言 freeRTOS中所有任务的通信和同步机制都是基于队列来实现. 2.队列的特点 图 队列的读写操作 队列的数据存储 (1)队列可以保存有限个具有确定长度的数据单元,队列可以保存的最大单元数目 ...

  8. freeRTOS中文实用教程3--中断管理之中断服务例程中使用队列

    1.前言 消息队列不仅可以用于事件通信,还可以用来传递数据 2.实例说明消息队列的执行过程 3.主要API API名称 说明 参数 返回值 xQueueSendFromISR()完全等同于 xQueu ...

  9. freeRTOS中文实用教程3--中断管理之延迟中断处理

    1.前言 嵌入式实时操作系统需要对整个系统环境产生的事件作出响应.可以采用中断方式也可以采用轮询方式来进行处理.如果采用中断方式,则希望ISR(中断服务例程)的处理时间越短越好. 注:必须说明的是,只 ...

随机推荐

  1. Dependency Walker使用说明[转]

    在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字——动态链接库.现在就让我们走进这些神奇的活动大陆,找出它们隐藏已久的秘密吧! 初窥门径:Windows的基石 随便打开一个系统目录, ...

  2. 【CF715E】Complete the Permutations(容斥,第一类斯特林数)

    [CF715E]Complete the Permutations(容斥,第一类斯特林数) 题面 CF 洛谷 给定两个排列\(p,q\),但是其中有些位置未知,用\(0\)表示. 现在让你补全两个排列 ...

  3. 【BZOJ1800】[AHOI2009]飞行棋(暴力)

    [BZOJ1800][AHOI2009]飞行棋(暴力) 题面 BZOJ 洛谷 题解 预处理一下前缀和就可以\(O(1)\)计算两点间的距离了,直接\(O(n^4)\)暴力枚举即可. #include& ...

  4. luogu3305/bzoj3130 费用流 (二分答案+dinic)

    Bob肯定想挑一个流量最大的边,然后把所有的费用都加给它呗 那Alice就让流量最大的边尽量小呗 那就二分一下答案再dinic呗 #include<bits/stdc++.h> #defi ...

  5. [模板]Link-Cut-Tree动态树

    做法以后再补,先写一些注意事项. 做法以后也不补了,直接看这个吧.https://www.cnblogs.com/candy99/p/6271344.html 1.rotate其实是最容易写错的地方( ...

  6. tf 数据读取

    tf.train.batch( tensors, batch_size, num_threads=1, capacity=32, enqueue_many=False, shapes=None, dy ...

  7. JDK源码分析(3)HashMap

    JDK版本 HashMap简介 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,Hash ...

  8. 最长公共子序列LCS(POJ1458)

    转载自:https://www.cnblogs.com/huashanqingzhu/p/7423745.html 题目链接:http://poj.org/problem?id=1458 题目大意:给 ...

  9. 树莓派上使用KickThemOut对局域网内的设备进行ARP欺骗

    安装KickThemOut工具 $ git clone https://github.com/k4m4/kickthemout.git $ cd kickthemout/ $ sudo -H pip ...

  10. 【洛谷P3792】由乃与大母神原型和偶像崇拜

    题目大意:维护一个序列,支持单点修改和查询一段区间能不能组成连续的一段数. 题解:查询区间能不能组成一段连续的数这个操作较为复杂,很难在较小时间复杂度内直接维护.这里采用线段树维护区间哈希的策略,即: ...