【STM32】入门(十三):FreeRTOS

【STM32】STM32单片机总目录

1、FreeRTOS简述

完全免费:FreeRTOS是完全免费的实时操作系统;
源码简单:只需 3 个 RTOS 移植通用的源文件和 1 个微控制器专用的源文件;
镜像较小:具有最小 ROM、RAM 和处理开销。RTOS 内核二进制映像通常介于 6 K 到 12 K 字节之间;
MISRA :源码符合 MISRA 编码标准,MISRA 是由汽车产业软件可靠性协会(MISRA)提出的C语言开发标准;
pc-lint:源码可以使用pc-lint进行合规性检查;https://pclintplus.com/
中文网站:https://www.freertos.org/zh-cn-cmn-s/index.html

2、源码惯例

2.1 命名惯例

RTOS 内核和演示应用程序源代码使用以下惯例:
1)变量

uint32_t 类型变量以 ul 为前缀,其中“u”表示“unsigned” ,“l”表示“long”。
uint16_t 类型变量以 us 为前缀,其中“u”表示“unsigned” , “s”表示“short”。
uint8_t  类型变量以 uc 为前缀,其中“u”表示“unsigned” , “c”表示“char ”。
非 stdint 类型的变量以 x 为前缀。例如,BaseType_t 和 TickType_t,二者分别是可移植层定义的定义类型,
	主要架构的自然类型或最有效类型,以及用于保存 RTOS ticks 计数的类型。
非 stdint 类型的未签名变量存在附加前缀 u。例如,UBaseType_t(未签名 BaseType_t)类型变量以 ux 为前缀。
size_t 类型变量也带有 x 前缀。
枚举变量以 e 为前缀
指针以附加 p 为前缀,例如,指向 uint16_t 的指针将以 pus 为前缀。
根据 MISRA 指南,未限定标准 char 类型仅可包含 ASCII 字符,并以 c 为前缀。
根据 MISRA 指南,char * 类型变量仅可包含指向 ASCII 字符串的指针,并以 pc 为前缀。

2)函数

文件作用域静态(私有)函数以 prv 为前缀。
根据变量定义的相关规定,API 函数以其返回类型为前缀,并为 void 添加前缀 v。
API 函数名称以定义 API 函数文件的名称开头。例如,在 tasks.c 中定义 vTaskDelete,并且具有 void 返回类型。

3)宏

宏以定义宏的文件为前缀。前缀为小写。例如,在 FreeRTOSConfig.h 中定义 configUSE_PREEMPTION。
除前缀外,所有宏均使用大写字母书写,并使用下划线来分隔单词。

2.2 数据类型

1)仅使用 stdint.h 类型和 RTOS 自带的 typedef,但以下情况除外:
char
根据 MISRA 指南,仅在未限定字符类型包含 ASCII 字符方可使用未限定字符类型。

char *
根据 MISRA 指南,仅在未限定字符指针指向 ASCII 字符串时方可使用未限定字符指针。使用需要 char * 参数的标准库函数时,无需抑制良性编译器警告,此举尤其考虑到将一些编译器默认为未限定 char 类型是签名的,而其他编译器默认未限定 char 类型是未签名的。

2)针对每个移植定义四种类型。
TickType_t
如果 configUSE_16_BIT_TICKS 设置为非零 (true) ,则将 TickType_t 定义为未签名的 16 位类型。如果 configUSE_16_BIT_TICKS 设置为零 (false),则将 TickType_t 定义为未签名的 32 位类型。请参阅 API 文档的自定义章节查看完整信息。
32 位架构应始终将 configUSE_16_BIT_TICKS 设置为 0。

BaseType_t
架构中最有效、最自然的类型。例如,在 32 位架构上,BaseType_t 会被定义为 32 位类型。在 16 位架构上,BaseType_t 会被定义为 16 位类型。如果将 BaseType_t 定义为 char,则须特别注意确保将签名字符用于可能为负的函数返回值来指示错误。

UBaseType_t
未签名的 BaseType_t。

StackType_t
意指架构用于存储堆栈项目的类型。通常是 16 位架构上的 16 位类型和 32 位架构上的 32 位类型,但也有例外情况。供 FreeRTOS 内部使用。

3、概念

3.1 多任务原理

每个执行程序都是受操作系统控制的任务(或线程)。如果一个操作系统能够以这种方式执行多个任务, 则可称其为多任务操作系统。
使用多任务操作系统可以简化原本复杂的软件应用程序的设计 :

操作系统的多任务处理和任务间通信功能允许将复杂的应用程序 分割成一组更小、更易于管理的任务。
通过分割,您可以更轻松地执行软件测试、分解团队内部工作以及复用代码。
复杂的时序和排序细节可以从应用程序代码中移除,由操作系统负责。

常规处理器一次只能执行一个任务,但通过任务间快速切换, 多任务操作系统可以使每个任务看起来像是同时在执行。 如下图所示, 该图展示了与时间相关的三个任务的执行模式。 任务名称采用颜色编码,并写在左手边。 时间从左向右移动, 彩色线条显示了在任何特定时间正在执行的任务。 上方展示了所感知的并发执行模式, 下方展示了实际的多任务执行模式。
在这里插入图片描述

3.2 任务调度

调度器是内核中负责决定在任何特定时间应执行哪些任务的部分。内核可以在任务生命周期内多次挂起并且稍后恢复一个任务。

调度策略是调度器用来决定在任何时间点执行哪个任务的算法。 非实时多用户系统的策略极有可能使每个任务具有"公平"比例的处理时间。

任务除了被迫被内核挂起之外,还可以选择将自己挂起。 如果它想要延迟(睡眠)一段固定时间,或者等待(阻塞)资源 变为可用(例如串行端口)或将要发生的事件(例如按键),它将执行此操作。 阻塞的或正在睡眠的任务无法执行,并且不会分配任何处理时间。
在这里插入图片描述

请参阅上图中的数字:

在 (1) 处,任务 1 正在执行。
在 (2) 处,内核挂起(换出)任务 1......
......在 (3) 处,恢复任务 2。
在 (4) 处,任务 2 正在执行,为独占访问,会锁定一个处理器外围设备。
在 (5) 处,内核挂起任务 2......
......在 (6) 处,恢复任务 3。
任务 3 尝试访问相同的处理器外围设备,发现其被锁定,任务 3 无法继续,因此在 (7) 处挂起。
在 (8) 处,内核恢复任务 1。
等等。
接下来任务 2 执行时 (9),完成对处理器外围设备的访问,因此解锁。
再下来任务 3 执行时 (10),发现现在可以访问处理器外围设备,于是开始执行,直到被内核挂起为止。

3.3 上下文切换

当一个任务执行时,它会利用处理器/微控制器寄存器,并像其他程序一样访问 RAM 和 ROM。这些资源(处理器寄存器,堆栈等)一起组成了任务执行上下文。
在这里插入图片描述

一个任务是一段有顺序的代码——它不知道什么时候会被内核挂起或恢复, 甚至不知道什么时候自己被挂起或恢复过。请参考如下示例: 一个任务在即将执行将两个处理器寄存器内包含的数值相加之前被挂起。 当该任务被挂起时,其他任务会执行,还可能会修改处理器寄存器的数值。恢复时, 该任务不会知道处理器寄存器已经被修改过了——如果它使用经修改过的数值, 那么求和会得到一个错误的数值。

为了防止这种类型的错误,任务在恢复时必须有一个与挂起之前相同的上下文 。通过在任务挂起时保存任务的上下文,操作系统内核负责确保 上下文保持不变。任务恢复时,其保存的上下文在执行之前由操作系统内核 恢复。保存被挂起的任务的上下文和恢复被恢复的任务的上下文的过程被称为 上下文切换。

3.4 实时应用程序

实时操作系统 (RTOS) 也实现了多任务处理—— 但它们的目标与非实时系统有着巨大差异。不同的目标反映在调度策略中。设计实时/嵌入式系统 是为了对现实世界事件作出及时响应。现实世界中发生的事件 存在截止时间,因此实时/嵌入式系统必须在此之前响应, RTOS 调度策略必须确保遵守这些截止时间的要求。
为实现这一目标,软件工程师必须首先为每个任务分配优先级。然后,RTOS 的调度策略 简单地确保能够执行的最高优先级任务,是被给予处理时间的任务。如果同等优先级的任务准备同时运行, 这可能需要在它们之间“公平”分享处理时间。

示例:

最基本的例子就是包含键盘和 LCD 的实时系统。用户应在合理时间内收到 各按键的视觉反馈——如果用户在此期间无法看到按键已被接受,则该软件产品的使用感会很差。如果最长接受时间为 100 毫秒,则任何介于 0 和 100 毫秒之间的响应都是可接受的。此功能可作为一个具有以下结构体的自主任务来实现:

 void vKeyHandlerTask( void *pvParameters )
 {
     // 按键处理是一个连续的过程,因此任务是使用无限循环实现的(就像大多数实时任务一样)。
     for( ;; )
     {
         // 等待键按下
         // 处理键值
     }
 }

现在假设实时系统也在执行数据处理功能。必须对输入进行采样、过滤并每 2 毫秒执行一次控制周期。为使过滤器正确运行,采样时间规则必须精确到 0.5 毫秒。该功能可以实现为一个具有以下结构的自主任务:

 void vControlTask( void *pvParameters )
 {
     for( ;; )
     {
        // 暂停等待2毫秒,从上一个周期开始
		// 对输入进行采样
		// 过滤采样的输入
		// 执行控制算法
		// 输出结果
     }
 }

软件工程师必须将控制任务的最高优先级分配为:

控制任务的截止时间比按键处理任务的截止日期更严格。
错过截止时间对控制任务的影响要大于对按键处理程序任务的影响。

3.5 实时调度

下图演示了实时操作系统如何调度上一页中定义的任务。RTOS 自身创建了一个任务:空闲任务,该任务仅在没有其他任务执行时才会执行。RTOS 空闲任务总是处于就绪态。

在这里插入图片描述
请参阅上图:

开始时,这两个任务都不能运行:vControlTask 等待合适的时间来开始新的控制周期, vKeyHandlerTask 等待按键操作。处理器时间分配给了 RTOS 空闲任务。
在时间 t1 处,一个按键事件发生。vKeyHandlerTask 可以执行,其优先级高于 RTOS 空闲任务,因此获得了处理器时间。
在时间 t2 处,vKeyHandlerTask 已完成按键处理并更新 LCD。 在按下另一个键之前该任务无法继续执行,因此将自己挂起,RTOS 空闲任务恢复执行。
在时间 t3 处,定时器事件指示执行下一个控制循环的时间到了。vControlTask 现在可以执行,因为优先级最高的任务被立刻分配了处理器时间。
在时间 t3 和 t4 之间,vControlTask 仍在执行时,发生了按键事件。vKeyHandlerTask 可以执行,但由于其优先级低于 vControlTask,因此未获得任何处理器时间。
在 t4 处, vControlTask 完成了控制周期的处理,并且直到下一次定时事件的发生前不能重新开始运行,进入阻塞态 。vKeyHandlerTask 现在成为可以运行的最高优先级的任务,因此获得处理器时间以处理先前的按键事件。
在 t5 处,按键事件处理完成,并且 vKeyHandlerTask 进入阻塞态等待下一次按键事件。再一次,两个任务都未进入就绪态, RTOS 空闲任务获得 处理器时间。
在 t5 与 t6 之间,定时事件发生并处理,没有进一步的按键事件发生。
在 t6 处发生按键事件,但在 vKeyHandlerTask 完成按键处理之前,发生了定时事件。此时两个任务都可以执行。由于 vControlTask 具有更高的优先级,因此 vKeyHandlerTask 在完成按键操作之前被挂起,vControlTask 获得处理器时间。
在 t8 处,vControlTask 完成控制周期的处理,然后进入阻塞态等待下一次事件。vKeyHandlerTask 再次 成为运行的最高优先级任务,因此获得处理器时间,以便完成按键处理。

3.6 滴答

休眠时,RTOS 任务将指定需要“唤醒”的时间。
阻塞时,RTOS 任务可以指定希望等待的最长时间。

FreeRTOS 实时内核通过滴答计数变量测量时间。定时器中断(RTOS 滴答 中断)以严格的时间精度增加滴答数——允许实时内核以所选择的定时器中断频率的分辨率来测量时间 。

每次滴答数增加时,实时内核必须检查是否现在是解除阻塞或唤醒任务的时间。 在滴答 ISR 期间唤醒或解除阻塞的任务的优先级 可能高于被中断任务的优先级。 在这种情况下,滴答 ISR 应该返回到新唤醒/未阻塞任务——有效地中断了一个任务, 但返回到另一个任务。 如下图所示:
在这里插入图片描述

请参阅上图中的数字:

在 (1) 处,RTOS 空闲任务正在执行。
在 (2) 处,RTOS 滴答发生,控制权转移到滴答 ISR (3)。
RTOS 滴答 ISR 使 vControlTask 准备就绪,并且由于 vControlTask 的优先级高于 RTOS 空闲任务, 因此将上下文切换到 vControlTask 的上下文。
由于执行上下文现在是 vControlTask 的上下文,退出 ISR (4) 会将控制权返回给 vControlTask, 后者开始执行 (5)。

以这种方式发生的上下文切换称为抢占式,因为中断的任务被抢占而不是主动挂起。
TODO:

https://www.freertos.org/zh-cn-cmn-s/implementation/main.html

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭老二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值