FreeRTOS介绍(一)
FreeRTOS介绍(一)
简介
在裸机开发中我们所使用的开发板上面搭载的微处理器都是单核的,因此当我们想要让单片机同时控制多个外设时据需要花费大量的精力去处理多任务之间的切换以及隔离问题。我们早期使用的编程方法主要是时间片轮转和伪调度器架构两种编程思想来处理这些问题。我们接触到了FreeRTOS之后多任务的处理操作就变得相对简单很多了,操作系统的作用是协调系统资源以及处理多任务,但是FreeRTOS和我们熟知的操作系统不同,它适配资源受限的单片机上,用于协调多任务的并发执行,我们之前在裸机开发时使用的编程思想和FreeRTOS的底层实现有诸多相似之处,后面我们会在FreeRTOS底层实现原理中进行更深入的介绍
任务调度器实现原理
由于FreeRTOS是运行在单核处理器上的,单核处理器同一时刻只能运行一个任务,因此我们如果想要让单片机同时处理多个任务首要解决的应该是上下文切换的问题。FreeRTOS的上下文切换主要依赖以下几个机制
寄存器
程序运行的本质就是CPU对内存、外设寄存器等存储元件的读写与计算,但是CPU并不能直接对内存里的数据进行计算,实际在进行计算时需要将内存中的数据移动到CPU周边的寄存器中,存储运算数据的寄存器是通用寄存器,例如在STM32F103中就有R0~R1213个32位通用寄存器,当然并非所有的寄存器都是用来保存计算数据的,其中有一个在FreeRTOS中发挥着重要作用的寄存器,就是程序计数寄存器简称PC寄存器,PC寄存器中保存着CPU中将要执行的指令所在的Flash地址,每当CPU执行完一条指令之后PC寄存器都会自动指向下一条命令。
结合上面的内容我们不难推出CPU执行的操作主要受寄存器控制,CPU每次都会根据PC寄存器中保存的地址去内存中取指令,然后将对应的数据从内存中放入通用寄存器中,对寄存器中的数据进行计算之后再将结果写回到内存中。如果我们能够尝试修改寄存器的工作方式,那么就能够让CPU实现多任务处理
在任何一个瞬间,CPU只能拥有一组寄存器状态,这也就决定了它只能执行一条命令
时间片
在上面的介绍中我们知道了CPU同一时间只能处理一个任务,但是在宏观层面上我们又想要达到多任务并行的效果,FreeRTOS为了满足我们的需求选择将时间进行切片,由于CPU的运行速度快,因此我们可以尝试将较长的一段时间切分成多个较短的时间,然后将时间片分发给不同的任务,CPU按照指定的顺序依次不同任务,当该任务的时间片耗尽之后就切换到下一个任务,这样就能够实现类似于多任务并行的效果了。
大多数情况下我们选择SysTick作为系统的时钟源,在FreeRTOS中,一个时间片就等于一个Tick(系统滴答)的长度,而系统滴答定时器每秒钟会产生1000次中断,因此一个时间片的长度就是1ms
任务栈
上面的介绍中我们知道了不同任务之间都会有自己的时间片,在时间片持续期间CPU会执行当前任务中的指令,然后时间片耗尽了CPU又会跳转到下一条指令去执行指令。
这种任务切换需要解决数据存储的问题,这就像我们平时打游戏一样,假设我们正在打一个游戏副本,打到一半的时候好友邀请我们去打另一个副本,但是我们无法在短期内结束当前副本,因此我们就可以将当前的副本内容进行存档,和好朋友打新副本打到一半他突然掉线了,在等待的过程中我们就可以打开刚才保存的副本,从上次保存的进度继续玩。
在FreeRTOS中要想在不同的任务之间反复横跳都需要进行上下文切换,为了解决上下文切换的问题,FreeRTOS选择用任务栈来进行”存档“,每一个任务都有自己专属的任务栈,每次切换到新任务时都会让对应的任务栈进行出栈操作,将对应的数据写入CPU的寄存器中,该任务的时间片耗尽之后会又会进行压栈,压栈结束之后再跳转到下一个任务中继续执行
作为用户我们可以在使用FreeRTOS时自己手动配置任务栈的大小
任务状态介绍
在FreeRTOS中任何一个任务在任意时刻必定只能处于一下四个核心状态之一,分别是运行态、就绪态、阻塞态和挂起态,在官方文档中对应的状态转换图如下

由于该图是英文版本的看起来比较难受,因此我让codex生成一张中文版的状态转换图来帮助记忆

作用
任务状态的作用主要是为了解决任务中延时的问题,假设一个任务中使用了延时函数,该任务也会一占用时间片,其它任务就无法及时响应,如果延时的任务能够老老实实的延时,而不占用时间片的话,那么系统的时效性还能够再提升,我们也就可以放心的在任务中使用延时
接下来就让我们详细的了解一下每个状态的作用
就绪态
当我们使用osThreadNew函数创建任务后,任务就会被FreeRTOS认为处于就绪态,等待被分配时间片运行,当程序运行了osKernelStart系统内核启动,FreeRTOS的任务调度器会从正在排队的就绪态任务中取出一个,分配时间片然后该任务就可以开始运行
运行态
上一阶段中处于就绪态的任务获得时间片之后就可以开始运行,此时任务状态就叫做运行态,当处于运行态的任务时间片耗尽之后FreeRTOS的调度器就又会将其送回就绪态排队,并给下一个就绪态的任务分配时间片让其进入运行态运行
运行态和就绪态的不断循环就实现了任务依次分配时间片运行
阻塞态
为了解决我们之前提到的任务调用延时函数会空耗CPU的类似问题,FreeRTOS又创建了阻塞态,当一个任务处于运行态时,如果运行了osDelay函数,那么不管当前的时间片有没有耗尽,任务都会直接进入阻塞态,让出CPU的运行资源,调度器便会继续为下一个处于就绪态的任务分配时间片运行,处于阻塞态的任务不会再参与时间片的分配,直到osDelay的延时时间结束。
这就解决了任务在延时时占用CPU资源的问题,在这里需要辨析的是,想要让任务进入阻塞态就一定要使用osDelay延时函数,HAL_Delay函数的底层实现原理和FreeRTOS中的延时函数底层的实现原理并不相同,HAL_Delay函数实现延时依靠的是死循环轮询 SysTick 变量,这个过程实现需要一直占用CPU
挂起态
如果某一个任务接下来一段时间不再需要运行了,并且也不需要等待任何时间超时或数据时间,那就可以通过手动调用osThreadSuspend函数的方式让任务进入挂起态,进入挂起态的任务将不再参与任务调度,我们后续可以根据需求再唤起对应任务
总结
本次先介绍这些内容,后续的还会更新更多内容
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)