什么是RTOS
RTOS 是实时操作系统(Real-Time Operating System)的缩写。它是一种专门用于实时任务处理的操作系统,用于管理和调度实时任务,并提供与硬件和外部设备的交互接口。
实时操作系统可以根据任务的时间要求和优先级,对任务进行调度和执行,以满足实时性的需求。它提供了任务管理、任务调度、中断处理、资源管理、通信机制等功能,使开发者能够方便地开发和管理实时应用程序。
操作系统
操作系统是一个控制程序,作为硬件和应用程序之间的桥梁,主要是和硬件打交道,负责协调分配计算资源和内存资源给不同的应用程序使用,并防止系统出现故障。面对来自不同应用程序的大量且互相竞争的资源请求,操作系统通过一个调度算法和内存管理算法尽可能把资源公平,有效率地分配给不同的程序。
实时???
实时性主要分为:硬实时和软实时
- 硬实时:要求任务在严格的时间限制内完成,并绝对不能错过截止时间。即使有一次时间违规,系统的正确性和可靠性也可能受到严重威胁。
- 软实时:任务有时间约束,但允许偶尔的时间违规。软实时任务的主要目标是在大部分情况下满足时间约束,但偶尔的延迟可能会被接受。
实时性的要求高主要是为了确保系统能够快速、准确地感知和响应各种情况,以提供安全性、稳定性和高效性能。
比如汽车的安全气囊系统,一旦检测到碰撞,系统必须在几毫秒的时间范围内完成气囊的充气,以在乘客撞击前提供必要的保护。任何延迟或错误可能导致安全气囊无法正常充气,从而无法起到保护作用,造成灾难性的后果。
和裸机开发的不同
裸机开发 | 基于RTOS的开发 |
---|---|
在没有操作系统支持的情况下直接编写代码来控制STM32。需要手动编写任务调度、同步和通信机制,以及处理中断和定时器等底层硬件操作。 | 更高级别的抽象和便利,使开发过程更加简化和高效。适用于需要处理多任务和并发操作的应用程序,同时提供了丰富的功能和组件来管理任务和资源。 |
现在市面上常见的RTOS有freeRTOS、RT-Thread、µC/OS等等,本节课主要介绍freeRTOS。
freeRTOS
相关背景:
FreeRTOS是一个热门的嵌入式设备用即时操作系统核心,它最初由Richard Barry于2003年左右开发,并由Barry的公司Real Time Engineers Ltd进行后续的开发和维护。2017年,该公司将FreeRTOS项目的管理权交给了亚马逊网络服务(Amazon Web Services,AWS)。Barry继续作为AWS团队的一员继续开发FreeRTOS。
优势
- FreeRTOS的设计小巧且简易,整个核心代码只有3到4个C文件,为了让代码容易阅读、移植和维护,大部分的代码都是以C语言编写,只有一些函数采用汇编语言编写。
- 开源,可免费使用
任务的配置和创建
CUBE MX的配置
- 根据自己的开发板选择型号
- 注意自己手上的是C6还是C8,可以给芯片打个光看一下
- RCC
- SYS,(这里有一点点不同,需要选择一个定时器作为时基)
以选TIM1
为例
- 在middleware中选择
FREERTOS
在下拉菜单中选择CMSIS_V1
- 配置时钟频率72MHz
- 在Project Manager中进行最后的配置(这部分和以前一样),生成代码,打开project
两种任务配置的方法
直接敲代码
- Task永远不会返回(
return
),实现需要套在一个死循环内,如果循环结束了需要调用vTaskDelete()
进行删除
void myTask(void *pvParameters){
for(;;){
/* 任务实现的代码 */
vTaskDelay(/*下一次执行的最短时间间隔*/);
/* 让任务进入阻塞状态 */
/* 也可以用osDelay()函数来实现 */
}
vTaskDelete( NULL );
// NULL表示删除当前的任务,也可通过传入其他任务句柄来删除其他任务
}
我们以翻转一个标志位为例为例:
int flag_a = 0;
/* 在程序前面定义 */
void flip_flag(void* parameter) {
int* p = parameter;
for (;;) {
*p = 1;
*p = 0;
vTaskDelay(1);
}
}
完成函数的定义之后,就可以创建任务
创建任务需要调用如下函数:
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask);
参数 | 含义 |
---|---|
pvTaskCode |
任务函数指针 |
pcName |
任务名称,只在debug的时候有用,需要具有描述性。但这个参数不会被freeRTOS使用 |
usStackDepth |
栈深度 |
pvParameters |
参数指针,可以在传入任务后转换为需要的数据类型 |
uxPriority |
任务优先级 |
pxCreatedTask |
传出任务句柄的位置,用于在系统运行的时候改变任务的优先级或者删除 |
RTOS的标识符命名具有一定的规律,可以在下面这篇博客中进一步了解:
freeRTOS名称规范
在MX_FREERTOS_Init()
函数内创建任务:
xTaskCreate(flip_flag, "task_0", 128, &flag_a, 0, NULL);
使用Keil进行仿真
在使用之前,需要修改一下keil的配置
- 关闭代码优化
- 将优化级别改为
-O0
- 将优化级别改为
- 配置仿真参数
- 将debug页面左边的Use Stimulator和Limit Speed to Real Time选上
- 在下面的Dialog DLL中填入
DARMSTM.DLL
,Parameter中填入-pSTM32F103C6
(如果手上的板子是C8就填C8)
配置完成后,就可以进入debug模式
打开逻辑分析器后,将变量添加到里面,并将我们需要查看的标志位添加到逻辑分析器中
运行后即可看到标志位flag_a
的变化,说明任务可以正常执行。
同一个函数也可以用来创建多个任务,
int flag_b = 0;
xTaskCreate(flip_flag, "task_1", 128, &flag_b, 0, NULL);
两个任务都可以被执行
CUBE MX同样为我们提供了比较简便的配置方式
- 在CUBE MX中进行配置
任务需要填入的参数与之前的相同,优先级改为Normal
。
再次生成工程后,在freertos.c
可以找到cube自动生成的代码,我们在StartTask02()
的循环中写程序即可,内容与前面的flip_flag()
函数类似。
且在MX_FREERTOS_Init()
中,cube已经帮我们添加了创建任务的语句,只需要修改传入的参数
注意:在用cube重新生成工程之后,需要再次修改debug的配置
多任务调度
在实际开发的过程中,系统需要执行多个任务来维持自身的稳定,并对外界变化或指令进行相应。不同任务的执行时间和容忍的延迟各有不同,但我们使用的单片机是单核心的,在任意时刻只会有一个任务被执行,因此我们需要对任务进行合理的调度,来满足任务对于实时性的要求。
三种调度算法
- 先来先服务(FCFS)调度算法
按照任务到达的先后顺序进行调度,先到达的任务先执行。这种算法简单直观,但可能导致长任务优先的问题。
- 时间片轮转算法
将任务按照轮询顺序进行调度,每个任务执行一定的时间片(时间片轮转),然后切换到下一个任务。这种算法能够公平地分配CPU时间,但可能导致上下文切换开销较大。
可以试着将
flip_flag()
函数中的延迟注释掉,观察逻辑分析器的波形有什么变化 - 优先级调度算法
为每个任务分配优先级,并按照优先级进行调度。具有较高优先级的任务优先执行,但可能导致低优先级任务的饥饿问题。
可以通过下面的图了解一下
用Cubemx生成的FreeRTOS默认有七个优先级,这七个优先级定义在了cmsis_os.h
中。与中断优先级不同,任务的优先数越大,则优先级越高。