title: FreeRTOS
date: 2025-05-21 14:16:45
cover: "https://cdn.jsdelivr.net/gh/Kingjt656/tuchuang@main/img/202506021523138.png"
tags:
 - FreeRTOS
 - 实时操作系统

FreeRTOS

一.FreeRTOS任务与中断服务程序

1.cubemx配置注意事项

1746930754125

1746930957686

可挂起的系统服务请求,这个中断用于上下文切换,在这个中断的ISR里决定哪个任务占用CPU,这一项的抢占优先级为15,最低优先级,只有在没有其他ISR的时候进行上下文的切换

1746931258951

Time base应该设置为最高的优先级 0 ,这样FreeRTOS就无法避免HAL库的基础时钟中断

2.优先级管理和中断

注意:在FreeRTOS中,中断优先级的数值越小,优先级越高;

而FreeRTOS的任务优先级是,任务优先级数值越小,任务优先级越低;

1746931983496

优先级0 ~ 4的中断是不受FreeRTOS的管理的,称为FreeRTOS不可屏蔽中断;优先级5 ~ 15 称为FreeRTOS可屏蔽中断

1746934996605

eg:如果采用中断方式进行ADC数据采集时, 只需要在ADC的中断里将数据读到缓冲区,而对数据进行滤波或者频谱计算等耗时的工作,应该在任务函数中运行。这就涉及到中断ISR与任务函数之间的同步问题了,这就是进程间的通信问题

3.中断屏蔽和临界代码段

1746960166187

在某些时候,任务的某段代码可能很关键,需要连续执行完,不希望被其他任务或中断打断,这种程序段称为临界段。

1747038838836

我们可以使用taskDISABLE_INTERRUPTS()来屏蔽MCU的部分中断,然后使用taskENABLE_INTERRUPTS()就可以解除屏蔽

在临界代码段内,FreeRTOS会暂停任务调度,正在执行的任务是不会被更高优先级的任务抢占的,能够保证代码执行的连续性

4.在ISR函数中使用FreeRTOS API函数

1747039685433

注意:在ISR中绝对不能使用任务级API函数,但是在任务函数中使用中断级API函数。而且,在FreeRTOS不能管理的高优先级中断的ISR里,连中断几API函数也不能使用

进入和退出临界区之间不仅会屏蔽中断,也会关闭任务调度,使得中间的代码无法被其他的任务打断

注意:我们不能在禁用中断的这个代码段 或者是临界区代码段,调用触发任务调度的函数如:vTaskDelay()或者是申请信号量进行进程间同步的函数,因为发生任务调度时就会打开中断,从而就会失去定义中断、屏蔽代码段或临界代码段的意义

二、进程间通信与消息队列

1.进程间通信的基本概念以及消息队列的原理使用

①消息队列是信号量 和 互斥量的基础

②任务 和 ISR可以统称为进程

1747210322563

1747210416864

1747210616550

二值信号量是没有优先级的继承机制,可能会出现优先级翻转问题

互斥量:互斥量 和 递归互斥量

互斥量可以用于互斥性共享资源的访问,互斥量具有优先级的继承机制,可以减轻优先级翻转问题

1747216525889

2.队列的特点和基本操作

1747218372214

队列的存储单元可以设置任意大小,可以存储任意数据类型,例如存储一个复杂结构体的数据,队列存储数据采用数据复制的方式,如果数据量比较大,复制数据会占用较大的存储空间,所以如果传递的是比较大的数据:比如比较长的字符串或大的结构体,可以在队列的存储单元里存储需要传递数据的指针

1747220340932

image-20250521142152925

队列满时,才可以使用这个xQueueOverwrite,但是这个函数只能用于队列长度为1的队列,在队已满时,它会覆盖队列原来的数据

其中xQueueReceive可以读取数据并删除已经读取的数据

而xQueuePeek也可以读取数据,但是不能删除数据

1747364953722

第一个vTaskDelay,让它每300ms只允许传入一个键值,也起到一个消抖的作用

我们还需要考虑没有扫描到按键的情况,我们需要给它一个5ms的延时,让它处于阻塞状态,然后让其他的任务得以调度

注意:在每个任务函数中,无论任务如果运行,都必须有一个延时,让其他的任务进行调度

三、信号量

有的时候进程间需要传递的只是一个标志,用于进程间同步 或 对一个共享资源的互斥性访问,这个时候就可以使用信号量和互斥量;信号量和互斥量的实现都是基于队列的。

信号量更适合用于进程间同步,而互斥量更适合用于共享资源的互斥性访问

1747552095441

1747552153542

1、二值信号量

二值信号量就是只有一个项的队列。二值信号量就像一个标志,要么是空、要么是满,适合用于进程间同步的通信。所以说相当于只有0 和 1两种值

2、计数型信号量

计数型信号量就是有固定长度的队列,每个项是一个标志。计数型信号量常用于多个共享资源的访问控制

1747565788527

如果计数信号量的值为0,那么(以上4张桌子)的资源都被占用了,再有(客人)想进店,就需要等待;在任务中申请信号量时,可以设置超时等待时间,在等待时,任务就进入阻塞状态;当一个(客人)用餐结束就可以释放,也就是Give信号量,计数信号量的值加1,表示可用资源的数量增加了一个,这个适合就可以供其他的任务或者ISR来进行使用

image-20250522145256923

注意:对于FromISR的函数,大部分都有一个参数,用来试图解决优先级翻转问题

image-20250522145450506

使用portYIELD_FROM_ISR函数,在可能的情况下进行一次任务调度

image-20250522145645413

只有在信号量不为空的时候,才需要释放信号量

四、互斥量

1747567070728

互斥量是对信号量的优化;信号量存在优先级翻转的问题,使得系统的实时性变差。互斥量引入了优先级集成机制,可以减缓优先级翻转问题,但是不能完全消除;

1747570441679

1、二值信号量和互斥量的区别

1747570673697

注意:互斥量不可以在ISR中使用,因为互斥量是具有任务的优先级的继承机制,而ISR不是任务。另外,ISR函数中不能设置阻塞等待时间,而获取互斥量时经常是需要等待的。

2、递归互斥量

1747571101136

1747572966977

 注意:xSemaphoreGive(xSemaphore) 这个函数不仅可以释放二值信号量,也可以释放计数信号量 和 互斥量,所以说xSemaphore可以是这三种对象的句柄

同样的,xSemaphoreTake(xSwemaphore,xBlockTime)不仅可以用于获取二值信号量,还可以用于获取计数信号量 和 互斥量

image-20250522165304687

获取信号量也有一个返回值,如果获取成功,则获取信号量函数的值为pdTRUE

image-20250522190204337

image-20250522194807750

Task_Middle is running打印两次,其中它是每500ms发送一次,我们可以看到低优先级获取信号量和释放信号量之间Task_Middle发送了两次消息,那在Task_Low释放了二值信号量之后,Task_High就立马抢占了运行,发送了一条Task_High Get token ,这个例子可以看到明显的优先级翻转现象

优先级翻转现象导致了高优先级的任务不能计时运行,违背了抢占式任务调度的设计初衷,对系统的实时性是不利的。

image-20250522201544169

由于使用互 斥量,所以说高优先级任务在试图获取互斥量时,如果互斥量被Task_Low占用着,拿FreeRTOS会将它Task_Low的优先级临时提高到Task_High的优先级,这样在Task_Low占用互斥量的运行期间,Task_Middle就无法抢占CPU运行。

只有在Task_Low释放互斥量之后,Task_High才能抢占CPU立即运行

因此,使用互斥量就避免了高优先级任务被中等优先级任务插队运行的情况。

五、事件组

1、事件组概念

事件组使用与多个事件触发一个或多个任务的运行,可以实现事件的广播,还可以实现多个任务的同步运行

image-20250522203146760

如果要处理多个事件,一般情况下,需要分解为多个任务,设置多个信号量;其次,可能有多个任务等待同一个事件的发生,但是在事件发生时,只能解除最高优先级的任务的阻塞状态,而不能同时解除多个任务的组设状态,也就是说 队列信号量 具有排他性,不能解决某些特定的问题

image-20250522205153402

事件组允许任务等待一个或多个事件的组合,事件组会解除所有等待同一事件的任务的阻塞状态

image-20250522210219714

image-20250522211223957

①事件标志只能是0 或 1,用单独的一个位来存储。

②如果一个事件位被设置为1,则表示这个事件发生了。反之,为0表示事件未发生

③32位的事件组最多可以处理24个事件。

④事件组具有广播功能,可以使多个任务同时解除阻塞后运行

image-20250523213107048

2. 事件组的多任务同步使用

 通过事件组进行多任务同步,在事件组的条件成立时,多个任务的阻塞状态可以同时解除,利用事件组的这个特性,可以实现多任务的同步。

image-20250523213608254

image-20250523215423638

多个任务在某个同步点同步运行

函数xEventGroupSync可以替代这两个函数实现一步操作,用于实现多任务之间的同步。

六、 任务通知

使用任务通知,不需要创建任何的中间对象,可以直接从任务向任务,或者从ISR向任务发送通知,任务通知可以模拟二值信号量,计数信号量或者长度为1的消息队列。使用任务通知通常效率更高,消耗内存更少。

image-20250524140353049

image-20250524140813177

image-20250524140835507

注意:发送者可以是任务和ISR,接收者只能是任务,不能是ISR

image-20250524142519872

注意:任务通知的局限性:不能向ISR发送通知,只能是任务或ISR函数向任务发送通知

1. 任务通知相关函数

image-20250524145718905

image-20250524150111607

2. 任务通知传递数据

使用任务通知来传递数据,使用中断方式进行ADC,然后通过任务通知,将ADC转换结果作为通知值发送给另一个任务加以显示

七、 软件定时器

image-20250525174932172

定时器被创建后,有休眠 和 运行 两种状态;并且,处于休眠状态的定时器不会执行其回调函数;

image-20250525175245866

image-20250525180256017

image-20250525180557935

image-20250526130513490

八、 韦东山笔记

1、 各类方法的对比

image-20250528111144244

image-20250528112020186

image-20250528112610479

后续大家可以根据韦东山老师的文档进行知识点的深挖和练习!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐