Freertos学习总结(进阶篇)

一、同步互斥与通信

一句话理解同步与互斥:我等你用完厕所,我再用厕所。
什么叫同步?就是:哎哎哎,我正在用厕所,你等会。
什么叫互斥?就是:哎哎哎,我正在用厕所,你不能进来。
能实现同步、互斥的内核方法有:任务通知 (task notification) 、队列 (queue) 、事件组 (event group) 、 信号量(semaphoe) 、互斥量 (mutex)
内核区分方法:

二、队列

队列(queue)可以用于传输数据:在任务之间、任务和中断之间。

使用队列的流程:创建队列、写队列、读队列、删除队列。

2.1 传输数据的两种方法

 

2.2 队列的阻塞访问

2.3 创建队列

队列的创建有两种方法:动态分配内存、静态分配内存
动态分配内存:xQueueCreate,队列的内存在函数内部动态分配

 静态分配内存:xQueueCreateStatic,队列的内存要事先分配好

 2.4 复位队列

  2.5 删除队列

  2.6 写队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在 ISR 中使用。

这些函数用到的参数是类似的,统一说明如下:   2.7 读队列

    2.8 查询队列

 2.9 覆盖/偷看队列

当队列长度为1时,可以使用 xQueueOverwrite() xQueueOverwriteFromISR() 来覆盖数据。 注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些函数不会被阻 塞。

如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用 " 窥视" ,也就是 xQueuePeek() xQueuePeekFromISR() 。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么" 偷看 " 时会导致阻塞;一旦队列中有数据,以后每次 " 偷看" 都会成功。

队列使用示例

本程序会创建一个队列,然后创建 2 个发送任务、 1 个接收任务:
发送任务优先级为 1 ,分别往队列中写入 100 200
接收任务优先级为 2 ,读队列、打印数值
main 函数中创建的队列、创建了发送任务、接收任务,代码如下:

 三、信号量

前面介绍的队列 (queue) 可以用于传输数据:在任务之间、任务和中断之间。
有时候我们只需要传递状态,并不需要传递具体的信息,比如:我的事做完了,通知一下你
在这种情况下我们可以使用信号量 (semaphore) ,它更节省内存。
信号:起通知作用
量:还可以用来表示资源的数量
" " 没有限制时,它就是 " 计数型信号量 "(Counting Semaphores)
" " 只有 0 1 两个取值时,它就是 " 二进制信号量 "(Binary Semaphores)
支持的动作: "give" 给出资源,计数值加 1 "take" 获得资源,计数值减 1
计数型信号:
计数:事件产生时 "give" 信号量,让计数值加 1 ;处理事件时要先 "take" 信号量,就是获得信号量,
让计数值减 1
资源管理:要想访问资源需要先 "take" 信号量,让计数值减 1 ;用完资源后 "give" 信号量,让计数值
1
二进制信号量:二进制信号量跟计数型的唯一差别,就是计数值的最大值被限定为1
两种信号量的对比

信号量跟队列的对比

3.1 信号量函数

使用信号量时,先创建、然后去添加资源、获得资源。使用句柄来表示一个信号量。

3.2 创建信号量

 

 3.3 删除信号量

  3.4 give/take

  xSemaphoreGive的函数原型如下:

 BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

xSemaphoreGive函数的参数与返回值列表如下:
pxHigherPriorityTaskWoken 的函数原型如下:

BaseType_t xSemaphoreGiveFromISR (
                                                              SemaphoreHandle_t xSemaphore ,
                                                              BaseType_t * pxHigherPriorityTaskWoken
);

 xSemaphoreGiveFromISR函数的参数与返回值列表如下:

 xSemaphoreTake的函数原型如下:

BaseType_t xSemaphoreTake (
                                                SemaphoreHandle_t xSemaphore ,
                                                TickType_t xTicksToWait
);
xSemaphoreTake函数的参数与返回值列表如下:

xSemaphoreTakeFromISR 的函数原型如下:
BaseType_t xSemaphoreTakeFromISR (
                                                                SemaphoreHandle_t xSemaphore ,
                                                                BaseType_t * pxHigherPriorityTaskWoken
);
xSemaphoreTakeFromISR函数的参数与返回值列表如下:

四、互斥量(mutex)

量:值为 0 1
互斥:用来实现互斥访问
它的核心在于:谁上锁,就只能由谁开锁。
很奇怪的是, FreeRTOS 的互斥锁,并没有在代码上实现这点:
即使任务 A 获得了互斥锁,任务 B 竟然也可以释放互斥锁。
谁上锁、谁释放:只是约定。
4.1 互斥量使用场景
在多任务系统中,任务 A 正在使用某个资源,还没用完的情况下任务 B 也来使用的话,就可能导致问题。比如对于串口,任务A正使用它来打印,在打印过程中任务 B 也来打印,客户看到的结果就是 A B 的信息混杂在一起。
上述问题的解决方法是:任务 A 访问这些全局变量、函数代码时,独占它,就是上个锁。这些全局变量、函数代码必须被独占地使用,它们被称为临界资源。
互斥量也被称为互斥锁,使用过程如下:
互斥量初始值为 1
任务 A 想访问临界资源,先获得并占有互斥量,然后开始访问
任务 B 也想访问临界资源,也要先获得互斥量:被别人占有了,于是阻塞
任务 A 使用完毕,释放互斥量;任务 B 被唤醒、得到并占有互斥量,然后开始访问临界资源
任务 B使用完毕,释放互斥量
正常来说:在任务 A 占有互斥量的过程中,任务 B 、任务 C 等等,都无法释放互斥量。
但是 FreeRTOS 未实现这点:任务 A 占有互斥量的情况下,任务 B 也可释放互斥量。
4.2  互斥量函数
互斥量是一种特殊的二进制信号量。
使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。
4.2.1 创建互斥量函数
创建互斥量的函数有 2 种:动态分配内存,静态分配内存,函数原型如下:
4.2.2   其他函数
要注意的是,互斥量不能在 ISR 中使用。
各类操作函数,比如删除、 give/take(信号量) ,跟一般是信号量是一样的。

 

五、事件组(event group)

学校组织秋游,组长在等待:
张三:我到了
李四:我到了
王五:我到了
组长说:好,大家都到齐了,出发!
秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的。
在这个日常生活场景中:
出发:要等待这 3 个人都到齐,他们是 " " 的关系
交报告:只需等待这 3 人中的任何一个,他们是 " " 的关系
FreeRTOS 中,可以使用事件组 (event group) 来解决这些问题。

5.1.1  事件组概念与操作

5.1.2 事件组的操作  

5.2 事件组函数  

5.2.1 创建
使用事件组之前,要先创建,得到一个句柄;使用事件组时,要使用句柄来表明使用哪个事件组。
有两种创建方法:动态分配内存、静态分配内存。函数原型如下:

5.2.2 删除  

对于动态创建的事件组,不再需要它们时,可以删除它们以回收内存。

vEventGroupDelete可以用来删除事件组,函数原型如下:

 

5.2.3 设置事件  

可以设置事件组的某个位、某些位,使用的函数有 2 个:
在任务中使用 xEventGroupSetBits()
ISR 中使用 xEventGroupSetBitsFromISR()
有一个或多个任务在等待事件,如果这些事件符合这些任务的期望,那么任务还会被唤醒。
函数原型如下:

 

值得注意的是, ISR 中的函数,比如队列函数 xQueueSendToBackFromISR 、信号量函数
xSemaphoreGiveFromISR ,它们会唤醒某个任务,最多只会唤醒 1 个任务。
但是设置事件组时,有可能导致多个任务被唤醒,这会带来很大的不确定性。所以
xEventGroupSetBitsFromISR 函数不是直接去设置事件组,而是给一个 FreeRTOS 后台任务 (daemon task)发送队列数据,由这个任务来设置事件组。
如果后台任务的优先级比当前被中断的任务优先级高, xEventGroupSetBitsFromISR 会设置
*pxHigherPriorityTaskWoken pdTRUE
如果 daemon task 成功地把队列数据发送给了后台任务,那么 xEventGroupSetBitsFromISR 的返回值就是pdPASS
5.2.4 等待事件
使用 xEventGroupWaitBits 来等待事件,可以等待某一位、某些位中的任意一个,也可以等待多位;等到期望的事件后,还可以清除某些位
先引入一个概念: unblock condition 。一个任务在等待事件发生时,它处于阻塞状态;当期望的时间发生时,这个状态就叫"unblock condition" ,非阻塞条件,或称为 " 非阻塞条件成立 " ;当 " 非阻塞条件成立"后,该任务就可以变为就绪态。

 函数参数说明列表如下:

 

你可以使用 xEventGroupWaitBits() 等待期望的事件,它发生之后再使用 xEventGroupClearBits()
来清除。但是这两个函数之间,有可能被其他任务或中断抢占,它们可能会修改事件组。
可以使用设置 xClearOnExit pdTRUE ,使得对事件组的测试、清零都在 xEventGroupWaitBits()
函数内部完成,这是一个原子操作。
5.2.5 同步点 
有一个事情需要多个任务协同,比如:
任务 A :炒菜
任务 B :买酒
任务 C :摆台
A B C 做好自己的事后,还要等别人做完;大家一起做完,才可开饭
使用 xEventGroupSync() 函数可以同步多个任务:
可以设置某位、某些位,表示自己做了什么事
可以等待某位、某些位,表示要等等其他任务
期望的时间发生后, xEventGroupSync() 才会成功返回。
xEventGroupSync 成功返回后,会清除事件
xEventGroupSync 函数原型如下:

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值