STM32遇上FreeRTOS实时操作系统(一)
前言
emmm,这里主要简单讲了关于FreeRTOS系统框架的一些知识,和怎么用微处理器跑FreeRTOS的一个学习过程
一、实时操作系统是什么?
实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行、管理系统资源,并为开发应用程序提供一致的基础。
实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。我们刚刚开始记住“实时”就可以了,其他的后面再学。
了解完实时操作系统的定义,就来数一数目前主流的实时操作系统了。常见的实时操作系统有FreeRTOS、uCOS 等,国内比较著名的实时操作系统有RT-thread 和Huawei LiteOS 等。这次说的主要是FreeRTOS系统,因为它是世界上主流的实时操作系统之一,并且开源,小巧,免费,经过多年的风风雨雨,它已经有充足的资料提供给他人查阅,不用担心学习的过程中找不到资料。
二、stm32和FreeRTOS之间的点点滴滴
1.FreeRTOS介绍
FreeRTOS是当下热门的操作系统之一,并且开源免费,相较于ucos这个系统来说代码量比较小,能够移植到大部分微处理器上,特别适合新入门的学习。
FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。任务调度机制是嵌入式实时操作系统的一个重要概念,也是其核心技术。对于可剥夺型内核,优先级高的任务一旦就绪就能剥夺优先级较低任务的CPU使用权,提高了系统的实时响应能力。不同于μC/OS-II,FreeRTOS对系统任务的数量没有限制,既支持优先级调度算法也支持轮换调度算法,因此FreeRTOS采用双向链表而不是采用查任务就绪表的方法来进行任务调度。(任务调度这些有兴趣可以看看计算机操作系统那本书了解了解。)
2.CMSIS RTOS的介绍
与操作系统不同,CMSIS-RTOS是ARM公司为统一操作系统、降低嵌入式门槛而发布的操作系统标准软件接口(API)。通俗讲,CMSIS-RTOS将操作系统(不管是FREE-RTOS还是RTX等)屏蔽起来,然后提供CMSIS-RTOS接口函数给最终使用者调用。如此以来,最终使用者只需要学习CMSIS-ROTS即可,从而降低学习门槛。(不过,目前只有FREE-RTOS和RTX能够支持CMSIS-RTOS)。
CMSIS-RTOS 是实时操作系统的通用 API。它提供了标准化的编程接口,它只是封装了RTX/embos,以后还可能封装freeRTOS(已经封装了~~~),uc/os(好像也已经封装了)等等第三方OS。总的来说吧,用CMSIS提供的接口而不用FreeRTOS的接口的原因是CMSIS可以减少在不同实时操作系统开发之间的工作量,使得多平台之间的移植变得高效便捷。
目前的CMSIS-RTOS V2 版本的接口有如下特性:
- 支持Cortex-M 内存保护单元(MPU);
- 支持多处理器系统;
- 支持DMA 功能;
- 能够避免死锁,支持优先级反转。
- 支持多种操作系统。
3.关于开发环境STM32CubeIDE
STM32CubeIDE是ST公司推出的一个多功能的集成开发工具,集成了TrueSTUDIO和STM32CubeMX,它是STM32Cube软件生态系统的一部分。
利用这个开发环境我们只需要简单的图形界面配置就可以快速生成一些初始化代码,不需要重头写初始化代码了。(具体一些的STM32CubeIDE就不在这里细说了,大家可以在各种论坛找到stm322cube ide的安装方法和使用方法。)(其实最重要的是免费!免费!免费!懂了吧?)
先行必备知识
FreeRTOS知识
在操作系统中,任务是竞争系统资源的最小运行单元。FreeRTOS 是一个支持多任务的操作系统。在 FreeRTOS 中,任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其它任务运行,任何数量的任务可以共享同一个优先级。
FreeRTOS可以创建多个任务,但是对于单核cpu来说,在任意给定时间,实际上只有一个任务被执行,这样就可以把任务分成2个状态,即运行状态和非运行状态。当任务处于运行状态时,处理器就执行该任务的代码。处于非运行态的任务,它的所有寄存器状态都保存在自己的任务堆栈中,当调度器将其恢复到运行态时,会从上一次离开运行态时正准备执行的那条指令开始执行。
在FreeRTOS中任务的特性
- 简单易用
- 没有使用限制
- 支持抢占、支持优先级
- 每个任务都有自己的堆栈,对RAM消耗较多
FreeRTOS的任务函数
void StartTask(void *argument) //StartTask is the name of task
{
//user's codes begin
...
//user's codes end
for(;;)
{
//user's codes begin
...
//user's codes end
osDelay(1);
}
}
任务中包含了一个for 循环,也可以使用while循环,但是请注意FreeRTOS任务函数一般不允随意跳出
循环,如果要关闭任务要使用VTaskDelete()来删除任务。
关于任务的状态
在FreeRTOS 中,任务有五种状态:运行态、就绪态、阻塞态、挂起态和删除态。
运行态(Running):说明当前CPU 正在执行该任务,单核处理器任何时刻只有一个任务处于运行态。处于就绪态就说明该任务正在占用CPU。
就绪态(Ready):说明任务已经就绪,可以被任务调度器调用去执行,但是调度只会去执行目前优先级最高的任务。
阻塞态(Blocked):阻塞态的任务不会被任务调度器的调度,并且任务阻塞是有一定时间的,当超过时间结束会退出阻塞状态。处于阻塞状态的作用除了可以使任务处于暂停状态,还有一个作用是灵活调度,当任务处于阻塞状态,调度器就会去执行其它任务,提高效率。比如当某个任务调用TaskDelay 毫秒延时函数时,该任务就会阻塞,同时调度器自动运行其它处于就绪态的任务,等待延时结束时重新进入就绪态。
挂起态(Suspend):处于挂起态的任务同样不会被任务调度器调度,但是对比阻塞态它没有超时时间,当调用当明确的分别调用vTaskSuspend()和xTaskResume() 函数后,任务才会进入或退出挂起状态。
删除态:任务删除之后任务控制块TCB 会保留一段时间,等待内核检查和回收资源,任务也不能再被调度,此时任务处于删除态,由于任务也实际不存在,所以官方的文档中只描述了运行态、就绪态、阻塞态和挂起态这四个状态,并没有把删除态列入任务的状态中。
FreeRTOS任务管理调度的方法
为了满足处理器多任务并发进行的需求,需要通过系统调度来合理安排各个任务占有CPU的时间。任务管理和调度是RTOS的核心功能。
在一般的系统中,任务可以分为Running态和非Running态,而非Running态还可以再细分。(不懂请看任务的状态这部分)
FreeRTOS系统中,任务主要有以下两种调度方法:
抢占式调度:当有新的任务就绪(ready,且优先级大于等于当前任务的优先级时,当前任务就会被抢占;需要用户自己通过configUSE_PREEMPTION配置。
时间片调度:同处于ready态的最高优先级的任务会轮流运行固定的时间片;通过configUSE_TIME_SLICING配置,默认开启
关于任务优先级
FreeRTOS 中任务可以设置优先级,这种方式是全抢占式调度,同时任务优先级的个数可以由用户配置。任务的优先级由数字决定的,数字越大优先级越高,数字越小代表优先级越低。
可以通过FreeRTOSConfig.h 文件中的configMAX_PRIORITIES 宏去配置任务优先级,用户实际可以使用的优先级范围是0到configMAX_PRIORITIES–1。比如我们配置此宏定义为5,那么用户可以使用的优先级号是0,1,2,3,4。假如使能了 configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏(在 FreeRTOSConfig.h 文件定义),一般强制限定最大可用优先级数目为 32与此同时还有一个容易弄混的概念是中断优先级,中断优先级和任务优先级是没有任何关系的,中断优先级的数字越大代表优先级越低,数字越小代表优先级越高。中断的级别永远高于任务级别。当任务开始执行的时候中断发生,微处理器会立刻执行中断服务程序。
当FreeRTOSConfig.h 文件中的configUSE_TIME_SLICING 宏配置为1 或者没有配置此
宏的时候是开启时间片调度的,多个任务可以共用一个优先级,也就是处于相同优先级的任
务会轮流切换执行。当有更高的优先级处于就绪状态就会去处理更高优先级的任务。
在FreeRTOS 中,任务优先级个数可以由用户配置,可以通过FreeRTOSConfig.h文件中的configMAX_PRIORITIES 宏进行配置的,而CMSIS-RTOS 所提供的接口对freertos中的优先级个数已经定义,在CMSIS-RTOS V2 中的configMAX_PRIORITIES 为56,其中用户可用优先级个数为49个,有5个优先级保留。1 个ISR 延迟线程,该线程优先级最高,不能被用户使用。还有一个osPriorityNone优先级,该优先级是无优先级,未初始化,不能使用。
CMSIS中线程优先级的官方定义:
typedef enum {
osPriorityNone = 0, ///< No priority (not initialized).
osPriorityIdle = 1, ///< Reserved for Idle thread.