目录
一、基础介绍
1.1 调度策略
1.1.1 调度方式
1.1.2 调度器
1.2 任务以及优先级
1.2.1 任务与协程
1.2.2 任务状态
1.2.3 任务优先级
1.2.4 任务优先级分配方案
1.3 任务间通信 - 信号量
1.3.1 信号量
1.3.2 任务间计数信号量的实现
1.3.3 中断方式计数信号量的实现
1.3.4 计数信号量API函数
1.4 任务间通信 - 消息队列
1.4.1 消息队列
1.4.2 任务间消息队列的实现
1.4.3 中断方式消息队列的实现
1.4.4 消息队列常用API
1.5 任务间通信 - 互斥信号量
1.5.1 互斥信号量概念
1.5.2 优先级翻转
1.5.3 互斥信号量的实现
1.5.4 常用互斥锁API
一、基础介绍
1.1 调度策略
1.1.1 调度方式
抢占式调度、时间片调度、合作式调度;
其中以抢占式、时间片为主,少见合作式;
(1).抢占式调度:任务有优先级,程序被高优先级先抢占,或者遇到阻塞式API函数;
(2).时间片调度:每个任务优先级相同,任务运行固定的时间片;
1.1.2 调度器
调度器会使用相关的调度算法,来决定当前应该做哪个任务;
调度器的共同特性:
(1).可以区分就绪态、挂起态任务;
(2).可以激活就绪态任务,让它变成正在运行的运行态任务;
(3).不同调度器之间最大区别是如何分配就绪态任务间的完成时间;
操作系统核心是调度器和任务的切换;
1.抢占式调度器:
需要实现抢占式调度器,可以先在FreeRTOS配置文件FreeRTOSConfig.h中禁止使用时间片调度,这时候每个任务都需要配置不同优先级;
优先级值越大,优先等级越高;
比如,现在有任务Task1,Task2,Task3,他们的优先级分别是1,2,3;
此时会先运行Task3,直到它遇到系统阻塞式的API函数(比如延迟、标志位等),这时候Task3被挂起,到了Task2运行,以此类推;
2.时间片调度器:
这个可以理解为,在同优先级的情况下。把时间拆分成时间片,比如现在有Task1,Task2,Task3,那么时间片先都为5个系统时钟,Task1运行5个系统时钟然后到Task2,Task2也运行5个,到Task3。不管你干完了没有,你的时间片用完了,就该到下一个任务运行。并且在这过程中,发生了阻塞,那么这个时间片过完了,也到下一个任务,并不会因为阻塞而等待;
1.2 任务以及优先级
1.2.1 任务与协程
!目前FreeRTOS 的协程功能在较新的版本中已被标记为 “过时”(deprecated),官方推荐使用轻量级任务(Lightweight Tasks) 或软件定时器替代。
协程与标准任务的区别:
- 调度方式:任务是抢占式的(优先级决定执行权),协程是协作式的(需主动让出 CPU)。
- 资源占用:协程栈开销小,适合内存紧张的系统;任务需要独立栈空间,内存消耗更大。
- 实时性:任务适合强实时需求,协程适合对响应时间要求不高的场景。
适用场景:
- 周期性执行但实时性要求不高的任务(如状态监测、日志打印)。
- 内存受限的小型嵌入式系统。
- 简化复杂状态机的实现(避免使用大量全局变量保存状态)。
1.2.2 任务状态
运行:正在执行,正在占用处理器;
就绪:任务准备执行,但是因为目前有更高优先级的任务在执行。这个不同于阻塞和挂起;
阻塞:任务在等待某个触发条件,一般进入阻塞态有个超时周期,超时后解除阻塞;
挂起:任务被强制暂停,不会参与调度,且无法被任何事件(包括超时)唤醒。
1.2.3 任务优先级
FreeRTOS最高优先级是通过 FreeRTOSConfig.h文件中的 configMAX_PRIORITIES进行配置的,实际可以用的优先级范围是 [0,configMax_PRIORITIES-1];
比如configMAX_PRIORITIES=5,那么优先级可以填 0,1,2,3,4;
优先级0表示的是空闲任务,一般建议configMAX_PRIORITIES<32;
1.2.4 任务优先级分配方案
IRQ任务:通过中断服务程序进行触发的任务,这种任务优先级最高;
高优先级后台任务:如按键检测、触摸检测、USB消息处理、串口消息处理;
低优先级:比如LED数码管显示,不需要实时性很强的任务;
这里记得把IRQ任务、高优先级任务设置成阻塞式,这样才能释放CPU使用权,让低优先级任务有机会执行;
1.3 任务间通信 - 信号量
1.3.1 信号量
信号量最初是为了共享资源建立一个标志,该标志表示占用情况;
像是停车场剩余车位,比如刚开始初始值设置为50个空闲位置。有车进来就-1,假如减到0了,也就是没车位了,那么需要停车场内有车出去,新来的车才能停进来,要不就等着;
功能:(1).任务之间 或者 中断函数与任务之间 的同步;
(2).多个共享资源的管理;
1.3.2 任务间计数信号量的实现
任务间信号量,是指各个任务间使用信号量实现任务同步 或者 资源共享功能;
其实很好理解,有点像我们平时写代码时的标志位;
目前有两个任务 Task1、Task2,计数信号量可用资源为1;
任务Task1运行时,用xSemaphoreTake获取信号量资源,假如资源没被占用,那么Task1直接获取资源,或者Task2占用了信号量,那么Task1转到阻塞态,等待资源可用。Task1在获取资源,并且使用后,通过xSemaphoreGive释放资源;
在运行Task2时,也是按上面的逻辑;
1.3.3 中断方式计数信号量的实现
FreeRTOS中断方式信号量的实现,是指中断函数 与 FreeRTOS任务之间使用信号量;
目前假设有一个任务Task1,和一个串口接受中断;
信号量初始值为0,串口中断调用xSemaphoreGiveFromISR 释放信号量,Task1调用xSemaphoreTake获取信号量资源;
运行流程:
(1).Task1 调用xSemaphoreTake获取信号量,但是初始值是0,没资源可以用,进入阻塞态;
(2).串口接受中断接受到新数据,在中断服务函数中调用xSemaphoreGiveFromISR释放信号量资源,信号量数值+1,Task1从阻塞态到就绪态,获取信号量后,信号量计数值-1,变成0;
(3).再次循环时,Task1调用xSemaphoreTake没资源可用,再进入阻塞,再等串口接受新数据;
注意事项:
(1).中断函数执行时间要短,防止其他低于中断优先级的异常没及时响应;
(2).不要在中断中实现消息处理,应该在中断服务函数中发送消息通知,然后在任务中进行消息处理。并且要注意这个任务优先级,使退出中断服务函数后,任务可以及时执行;
(3).中断服务程序中一定要调用专用于中断的信号量设置函数,即以FromISR结尾的函数;
1.3.4 计数信号量API函数
单片机 - FreeRTOS 常用的API(未完)-CSDN博客
1.4 任务间通信 - 消息队列
1.4.1 消息队列
- 指通过RTOS内核提供的服务,任务或者中断子程序可用把一个消息(FreeRTOS中传递的是实际数据)放入到队列,同时任务或者中断子程序 可用从中获取;
- 支持 FIFO 和 LIFO 的数据存取方式;
1.4.2 任务间消息队列的实现
假设消息队列可以放10个消息;
创建了Task1 和 Task2 两个任务,Task1放数据,Task2取数据;
采用FIFO方式;
假如Task1放数据的速度,比Task2取数据的速度快,消息会放满,FreeRTOS的消息存放函数xQueueSend支持超时等待;
同样的,取的速度大于存的速度,消息获取函数xQueueReceive支持超时等待;
1.4.3 中断方式消息队列的实现
假设消息队列可以放10个消息;
创建任务Task1,和串口接收中断;
采用FIFO;
串口中断接受到消息后进行中断,往队列里放数据。Task1从队列里取数据,
(1).串口中断放数据速度 超过 Task1取数据的速度,则会造成存放过满,但是中断服务程序的消息队列发送函数 xQueueSendFromISR 不支持超时设置,所以发送前要用 xQueueIsQueueFullFromISR 检测消息队列是否满了;
(2).假如是取数据的速度 超过 放数据的速度。这时候消息队列是空的,Task1没东西取,而且Task1 获取消息的函数是 xQueueReceive ,这个函数可以设置超时等待,没消息Task1就等着,直到队列中有消息,或者是超过我们设定的时间;
1.4.4 消息队列常用API
单片机 - FreeRTOS 常用的API(ing)-CSDN博客
1.5 任务间通信 - 互斥信号量
1.5.1 互斥信号量概念
主要作用是对资源实现互斥访问,类似二值信号。互斥信号与二值信号的区别是 互斥信号可以防止优先级翻转;
1.5.2 优先级翻转
假设目前有3个任务 Task1,Task2,Task3,优先级分别是3,2,1,Task1优先级最高;
任务Task1 和 Task3互斥访问串口打印 printf,采用二值信号实现互斥访问;
Task3通过二值信号调用printf,被Task1抢占了,开始做Task1;
运行过程:
(1).Task1在运行时需要printf,这时候Task3正在用printf,就会把Task1挂起,等Task3释放printf;
(2).调度器的作用下,Task3运行过程中,Task2就绪,把Task3给抢了。优先级翻转出现在这;
(3).Task3因为被Task2抢了任务,所以没法及时释放printf,这时候变成得等待Task2干完之后,才能释放printf,然后才到Task1;
1.5.3 互斥信号量的实现
首先,我们引入互斥信号量这一步,是为了解决1.5.2 中的优先级翻转问题;
互斥信号量的实现是这样的:
比如Task1和Task2的优先级分别是1和3,首先先是Task1在执行,这时候Task2也需要占用这个资源。那么会先把Task1的优先级抬到3,也就是和Task2的优先级一样,这时候就算有一个优先级是2的任务,也无法抢占Task1;然后是Task2被挂起,Task1释放资源,Task2等待资源,这里Task1释放完资源,又会回到1的优先级;
1.5.4 常用互斥锁API
单片机 - FreeRTOS 常用的API(ing)-CSDN博客