Freertos的同步与互斥、队列、信号量、时间组的解析

声明:

这个是本人学习(韦东山)过程中的笔记和理解。

1.同步与互斥

比如两个人上厕所但只有一个位置,同一时间只能有一个人使用。这叫做互斥

在车赛中,一个人负责软件,一个人负责硬件。软件必须等硬件把板子焊好才可以调试,这就叫同步

在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也得等着,这就叫互斥。经理B跟同事A说:你用完会议室就提醒我。这就是使用“同步”来实现“互斥”。

  • 互斥:资源独占(会议室一次仅一人使用)。

  • 同步:依赖协作(B需等待A通知后使用)。

有缺陷同步的例子:

如A要计算1->1000000消耗的时间,然后通过B把时间打印出来。

  • 可以通过全局变量的方法来实现同步,就是B要等到A中的全局变量为1,而A要运行到结束才会把值置1,这样的话A本来只需要运行1s,B就可以拿到,但是经过调度,花在A和B上运行同样的时间,实际2s后B才能拿到。

  • 所以用全局变量的方法效率很低。

  • 我们可以等A运行完毕,然后唤醒任务B,这样效率就大大提高。

  • 使用同步的时候,让那些等待的任务阻塞,不要让他们参与CPU的调度。

  • 当要传递信息的时候,不可能A的信息没写好,B就过来读取他,此时得到的是一个错误的值。

 Freertos的解决方案:

正确性。

效率:等待者进入阻塞

1.队列:相当于一个传送带,把做好的东西放到上面

2.事件组:把做好的事情设置为1.

3.信号量:放的是计数值

2. 队列(有阻塞,唤醒的环形缓冲区)

在实际过程中:

A是放数据的,B是接收数据的。B要接收数据但是队列中没有数据

1.阻塞,超时唤醒

2.阻塞,任务A发送数据到队列中,A不知道数据是给B的,A只会敲敲队列,队列知道要发送给B,因为队列有一个链表指向B。

同样A要发送数据,但是队列已经满了,此时

1.阻塞,超时唤醒

2.阻塞,任务B读取队列中的数据,B会敲敲队列,队列中空出位置,唤醒A发送数据,因为阻塞队列有一个A。

队列的本质

1.环形buffer

2.两个链表,一个(如果A想发送但队列满了,此时他就会进发送队列)放发送者,一个放接收者(如果B想接收但队列为空,此时他就会进接收队列)

如果任务B转到运行状态,   --while(1){读取队列}--,但是队列中没有值,此时,他会从就绪链表里面移除,添加到 (队列的)receiver list链表和阻塞(delay list)链表中。

此时A唤醒接收链表里面的第一个任务,就是任务B,此时B进入就绪链表。

但是如果一直没有被唤醒,并且超过了读取队列的超时时间的话,也会将从receiver list链表和阻塞(delay list)链表中删除,并且进入就绪链表。

就像韦东山中,控制挡球板运动

1.通过红外接收按键

2.通过旋转编码器

按键按下,红外接收产生中断产生一个值,放进队列A,挡球板的任务接收到这个值就会运动

2.同理,旋转编码器中断接收到中断,产生的值放进队列B,然后编码器任务拿到队列b的值并且处理,然后再放到队列A。

挡球板移动的任务(读取队列A数据的任务)每次读取数据都会使挡球板运动固定个单位,如果编码器速度快,就会向队列A中多写几个数据,此时挡球板就可以一直运动。

用环形缓冲区的时候,读取的任务不会阻塞(会一直占用CPU资源)。用队列之后读取的任务会阻塞,其他任务就会执行,提高cpu效率。

对于写队列读队列,就是利用中断来写队列,写入队列的数据可以是个结构体,再通过任务来读队列,每次速度和方向是怎么确定的呢?比如方向是传入到队列的数据,速度就是传入数据的个数,如果一直读取到数据,挡球板就会一直运动。

旋转编码器的消抖问题:

在编码器快到达最左端的时候,像左滑动编码器,确始终抖动,滑不到最左边。

延时两毫秒没用,如果抖动大于2ms呢?

像上面一样,每次上升的间隔<2ms,认为是抖动,直接返回不处理。

应用:接收到的数据可以放在队列中,然后用接收函数来都队列。

3.队列集

如果像上面一样,每一个中断就写一个驱动程序,就会使用很多的栈空间。

我们可以使用轮询或者是队列集的方式来优化。(轮询如果要阻塞的话,效果不好)用队列集

4.用队列集增加姿态控制

在初始化的时候创建队列,所以我们要把各个函数创建队列的函数初始化,放在最开始,因为游戏一开始就要初始化,就要给各个创建队列。

我们先在初始化里面创建队列,然后队列集里面添加读队列,然后创建任务,把读到的数据传进姿态控制的队列。

任务参数的作用:就是当很多创建的任务都要用到同一个函数时,此时这个函数的参数就起到作用。

对于一个红外的三个键值控制3辆车,每当按下一个按钮的时候,红外按键会向队列写3个值,分别给到3辆车,只有那辆对应键值的车才会动,此时我们梳理一下程序。

后来就是每个car把接收到的任务读一遍,属于自己的就运动,不属于就静止(判断)。

5.信号量的本质

信号:提醒的作用;量:计数。

左边是队列:它需要有写队列,读队列,计数值,读队列的链表,写队列的链表,还有长度。

右边是信号量:长度、计数值还有接收链表。

写队列:1.把值copy进队列。2.cnt++。3.唤醒等待接收的队列。

读队列:1.把值copy进任务。2.cnt--。3.唤醒发送队列。

信号量的give(写入):1.cnt++。2.唤醒接收队列。

信号量的take(获取):1.cnt--。(只需要获取,不需要干别的)。

5.1 计数信号量

 

 最后实现第一辆车子第二辆车子先行,等有一辆车子到达终点后第3辆车子才动。

5.2 二进制信号量

二进制信号量最大值是1,初始值为0。

 但是存在问题,就是如果任务1优先级为2,任务2优先级为3,任务3优先级为4.

此时任务1首先开始运行,任务1拿到二值信号量,然后任务2开始运行,抢占了任务1,任务1进入就绪态,任务3开始运行,因为任务3没有拿到任务1的信号量进入了阻塞。这就造成了优先级高的抢占不了优先级低的。

例子:

当调用函数启动发送,然后会调用这个函数,这个函数如果发送完成就会释放信号量,(阻塞,等待发送完成)这个就会得到信号量,发送完成。

6.优先级反转

此时就要对车的任务重新编写,因为车1车3需要读取信号量,车2不用。

7.互斥量解决优先级反转

使用互斥量,任务3运行时会提高任务1的优先级使其优先级等于任务3,然后任务1优先级就比任务2高,然后任务1释放互斥量,任务3运行。

同时使用互斥量解决临界区的问题:像互斥量上锁也只能由他开锁,可以解决临界区的问题。

8.事件组

对于队列,当写入队列的时候只能唤醒一个任务。

当读队列的时候也只能唤醒一个写入的任务。

有没有一种广播机制,一个任务(写入某种数据)可以唤起多个任务(接收)

等待的任务A,B。

他的构成就是:高8位是确定or/and的关系,后八位是满足任务开始的关系。

时间组是一个链表。当A和B是如图所示的任务时,写bit2时,任务A、B都不运行,写bit7时,任务B运行,写bit0时,任务A、B都运行。当写入任务时,会遍历整个链表。

 第23个程序,就是先创建一个事件组,然后car1设置写入bit0,car2、car3设置bit0启动,此时car1到达终点后,car2,car3启动了。

第24个程序,就是先创建一个事件组,然后car1,car2设置写入bit0,car3设置bit0启动,此时car1或car2到达终点后,car3启动了。

第25个程序,就是先创建一个事件组,然后car1设置写入bit0,car2设置写入bit7,car3设置bit0和bit7同时启动,此时car1且car2到达终点后,car3启动了。

程序就是在上面信号量的程序上面修改就行了。创建信号量->创建时间组。然后按上述的逻辑写一下就行了


如果我们创建一个读I2C(读MPU6050的动作)的任务,读到数据写到队列中,然后vtaskdelay(50),此时cpu会访问这个任务,I2C很慢,会占用大量CPU资源。怎么办呢?

使用事件组,我们可以利用中断,当MPU6050检测到晃动,产生中断,写事件组。MPUTask()开始没有接收到事件时会阻塞。接收到事件之后就会开始读I2C并且写队列,挡球板可以开始运动。

9.任务通知

 好像全是接收方的状态。

这个 分简化版和专业版。

下面说说简化版:

比如有任务A和任务B.

任务A发出通知给任务B(A喜欢B,给B买礼物),

任务B不取出通知(任务B处于不在等待通知的状态)(相当于B现在不喜欢A,东西放在那里,不去管他),,但是A给B发了通知,所以B也处于状态,

任务A一直送礼物(cnt一直++);当任务B想要接受消息的时候,,此时就会看到A的消息。

实战:1.先把句柄准备好,待会要利用句柄发通知。

在任务1中给任务2发通知,此时任务2通知值+1,给任务3发消息,通知值为100.

任务3一直接收通知值,如果通知值是对的,就触发下面的功能。

10.软件定时器

定时器的删除,开始,复位都是通过写队列来完成的,开始创建定时器任务,然后开始调度

创建的是一次性定时器,时间超时就会执行回调函数。

那么软件定时器到底是什么?在接收到start,reset或者period队列信号后运行,在不运行的时候他是不是像普通任务一样一直等待队列呢?

调用这个函数相当于蜂鸣器的开启和定时停止,一开始开启蜂鸣器并设置频率,然后启动定时器的倒计时并修改周期为time_ms,时间到后就会调用回调函数停止蜂鸣器。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值