freeRTOS的特点

1. 互斥量的核心在于:谁上锁,就只能由谁开锁。很奇怪的是,FreeRTOS的互斥锁,并没有在代码上实现这点,只是要求程序员按照这样的惯例写代码。任务A占有互斥量的情况下,任务B也可释放互斥量。要注意的是,互斥量不能在ISR中使用。

而如果用户指定的阻塞超时时间不为 0,则当前任务会因为等待互斥量有效而进入阻塞状态,在将任务添加到延时列表之前,会判断当前任务和拥有互斥量的任务优先级哪个更高,如果当前任务优先级高,则拥有互斥量的任务继承当前任务优先级,也就是我们说的优先级继承机制。

1.1 递归锁
1.1.1 死锁的概念
日常生活的死锁:我们只招有工作经验的人!我没有工作经验怎么办?那你就去找工作啊!

假设有2个互斥量M1、M2,2个任务A、B:
A获得了互斥量M1
B获得了互斥量M2
A还要获得互斥量M2才能运行,结果A阻塞
B还要获得互斥量M1才能运行,结果B阻塞
A、B都阻塞,再无法释放它们持有的互斥量
死锁发生!

1.1.2 自我死锁
假设这样的场景:
任务A获得了互斥锁M
它调用一个库函数
库函数要去获取同一个互斥锁M,于是它阻塞:任务A休眠,等待任务A来释放互斥锁!
死锁发生!

1.1.3 函数
怎么解决这类问题?可以使用递归锁(Recursive Mutexes),它的特性如下:
任务A获得递归锁M后,它还可以多次去获得这个锁
"take"了N次,要"give"N次,这个锁才会被释放
谁持有递归锁,必须由谁释放。
在这里插入图片描述
在这里插入图片描述

使用互斥量的两个任务是相同优先级时的注意事项。

2. FreeRTOS中很多API函数都有两套:一套在任务中使用,另一套在ISR中使用。后者的函数名含有"FromISR"后缀。

为什么要引入两套API函数?
很多API函数会导致任务计入阻塞状态:
运行这个函数的任务进入阻塞状态
比如写队列时,如果队列已满,可以进入阻塞状态等待一会
ISR调用API函数时,ISR不是"任务",ISR不能进入阻塞状态
所以,在任务中、在ISR中,这些函数的功能是有差别的

FreeRTOS使用两套函数,而不是使用一套函数,是因为有如下好处:
2.1 使用同一套函数的话,需要增加额外的判断代码、增加额外的分支,是的函数更长、更复杂、难以测试
2.2 在任务、ISR中调用时,需要的参数不一样,比如:
2.2.1 在任务中调用:需要指定超时时间,表示如果不成功就阻塞一会
2.2.2 在ISR中调用:不需要指定超时时间,无论是否成功都要即刻返回
2.2.3 如果强行把两套函数揉在一起,会导致参数臃肿、无效
2.3 移植FreeRTOS时,还需要提供监测上下文的函数,比如is_in_isr()

2.4 有些处理器架构没有办法轻易分辨当前是处于任务中,还是处于ISR中,就需要额外添加更多、更复杂的代码

使用两套函数可以让程序更高效,但是也有一些缺点,比如你要使用第三方库函数时,即会在任务中调用它,也会在ISR总调用它。这个第三方库函数用到了FreeRTOS的API函数,你无法修改库函数。这个问题可以解决:

2.5 把中断的处理推迟到任务中进行(Defer interrupt processing),在任务中调用库函数
2.6 尝试在库函数中使用"FromISR"函数:
2.6.1 在任务中、在ISR中都可以调用"FromISR"函数
2.6.2 反过来就不行,非FromISR函数无法在ISR中使用
2.7 第三方库函数也许会提供OS抽象层,自行判断当前环境是在任务还是在ISR中,分别调用不同的函数

3. FreeRTOS的5种内存管理方法

文件优点缺点
heap_1.c分配简单,时间确定只分配、不回收
heap_2.c动态分配、最佳匹配碎片、时间不定
heap_3.c调用标准库函数速度慢、时间不定
heap_4.c相邻空闲内存可合并可解决碎片问题、时间不定
heap_5.c在heap_4基础上支持分隔的内存块可解决碎片问题、时间不定

heap_5.c 方案允许用户使用多个非连续内存堆空间,每个内存堆的起始地址和大小由
用户定义。这种应用其实还是很大的,比如做图形显示、GUI 等,可能芯片内部的 RAM
是不够用户使用的,需要外部 SDRAM,那这种内存管理方案则比较合适。

获取当前内剩余存大小 xPortGetFreeHeapSize()

4. 所谓"任务通知",你可以反过来读"通知任务"。

我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。

任务通知的优势:
4.1.1 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有大的优势。
4.1.2 更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。

任务通知的限制:
4.2.1 不能发送数据给ISR
ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
4.2.2 数据只能给该任务独享
使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数据。使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。
在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源的数据发给多个任务。
4.2.3 无法缓冲数据
使用队列时,假设队列深度为N,那么它可以保持N个数据。
使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
4.2.4 无法广播给多个任务
使用事件组可以同时给多个任务发送事件。
使用任务通知,只能发个一个任务。
4.2.5 如果发送受阻,发送方无法进入阻塞状态等待
假设队列已经满了,使用xQueueSendToBack()给队列发送数据时,任务可以进入阻塞状态等待发送完成。
使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

发送任务、接收任务的代码和执行流程如下:
A:发送任务优先级最高,先执行。连续存入3个字符、发出3次任务通知:通知值累加为3
B:发送任务阻塞,让接收任务能执行
C:接收任务读到通知值为3,并把通知值清零
D:把3个字符依次读出、打印
E:再次读取任务通知,阻塞
在这里插入图片描述
运行结果如下图所示:
在这里插入图片描述

5.FreeRTOS的邮箱概念跟别的RTOS不一样,这里的邮箱称为"橱窗"也许更恰当:

5.1 它是一个队列,队列长度只有1
5.2 写邮箱:新数据覆盖旧数据,在任务中使用xQueueOverwrite(),在中断中使用xQueueOverwriteFromISR()。
既然是覆盖,那么无论邮箱中是否有数据,这些函数总能成功写入数据。
5.3 读邮箱:读数据时,数据不会被移除;在任务中使用xQueuePeek(),在中断中使用xQueuePeekFromISR()。
这意味着,第一次调用时会因为无数据而阻塞,一旦曾经写入数据,以后读邮箱时总能成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值