什么是队列
队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任
务间传递信息。
为什么不使用全局变量?
如果使用全局变量,兔子(任务1)修改了变量 a ,等待树獭(任务3)处理,但树獭处理速度很
慢,在处理数据的过程中,狐狸(任务2)有可能又修改了变量 a ,导致树獭有可能得到的不是
正确的数据。
在这种情况下,就可以使用队列。兔子和狐狸产生的数据放在流水线上,树獭可以慢慢一个个依
次处理。
关于队列的几个名词:
队列项目:队列中的每一个数据;
队列长度:队列能够存储队列项目的最大数量;
创建队列时,需要指定队列长度及队列项目大小。
队列特点
1. 数据入队出队方式
通常采用先进先出(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取。
也可以配置为后进先出(LIFO)方式,但用得比较少。
2. 数据传递方式
采用实际值传递,即将数据拷贝到队列中进行传递,也可以传递指针,在传递较大的数据的时候
采用指针传递。
3. 多任务访问
队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息
4. 出队、入队阻塞
当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。
阻塞时间如果设置为:
0:直接返回不会等待;
0~port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不
再等待;
port_MAX_DELAY:死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;
队列相关 API 函数
- 创建队列
参数:
uxQueueLength:队列可同时容纳的最大项目数 。
uxItemSize:存储队列中的每个数据项所需的大小(以字节为单位)。
返回值: 如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法分配 ,则返回 NULL。 - 写队列
参数:
xQueue:队列的句柄,数据项将发送到此队列。
pvItemToQueue:待写入数据
xTicksToWait:阻塞超时时间
返回值:
如果成功写入数据,返回 pdTRUE,否则返回 errQUEUE_FULL。 - 读队列
参数:
xQueue:待读取的队列
pvItemToQueue:数据读取缓冲区
xTicksToWait:阻塞超时时间
返回值:
成功返回 pdTRUE,否则返回 pdFALSE。
cubeMX配置创建
在Queues里面点击Add即可然后配置对应的参数即可!
二值信号量
什么是信号量?
信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代
码段不被并发调用。
信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以
用来表示资源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有两个状
态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。
信号量也是队列的一种。
什么是二值信号量?
二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我们用它来进行互斥访问或任务同步。
互斥访问:比如门钥匙,只有获取到钥匙才可以开门
任务同步:比如我录完视频你才可以看视频
二值信号量相关 API 函数
- 创建二值信号量
参数:
无
返回值:
成功,返回对应二值信号量的句柄;
失败,返回 NULL 。 - 获取二值信号量
参数:
xSemaphore:要获取的信号量句柄
xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;
返回值:
成功,返回 pdPASS ;
失败,返回 errQUEUE_FULL 。
cubMX创建
点击Binary Semaphore即可创建二值信号量,然后选择创建的方式(动态,静态)即可创建!
计数型信号量
什么是计数型信号量?
计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型
信号量被创建的时候确定的。
计数型信号量相关 API 函数
参数:
uxMaxCount:可以达到的最大计数值 uxInitialCount:创建信号量时分配给信号量的计数值
返回值:
成功,返回对应计数型信号量的句柄;
失败,返回 NULL 。
cubMX创建
在Counting Semaphores栏点击Add即可,唯一和二值信号量不同的是,计数型信号量需提前Config parameters栏搜索的 USE_COUNTING_SEMAPHORES 设置为 Enabled 才可以创建!!!
操作完之后,Counting Semaphores栏里面的Add按键就会变成蓝色,可点击!
互斥量
什么是互斥量?
在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步,而互斥型信号量用于资源保护。互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级反转现象。
什么是优先级翻转?
互斥量就是靠这个以达到保护资源的目的!
假设创建的是普通的二值信号量,TaskH和TaskL均为获取信号量,TaskM为普通其他任务(比如打印发送串口信息),任务优先级由高到底为H->M->L,假如在某一个时刻,TaskL在运行过程中,突然TaskH运行了,但是信号量大小只有一个并且已经被TaskL获取了,所以TaskH就会被阻塞无法运行,TaskL就会继续运行,但是如果运行过程中TaskM突然要运行,因为TaskM任务不是获取信号量,所以TaskM就会中断TaskL,然后等待TaskM完成后TaskL才能再继续运行,那这样的话TaskH就只能更迟的运行起来了!这样的话就使得优先级高的反而不能及时地运行!
因此,有了互斥量,假设上面创建的不是二值信号量,而是互斥信号量,在TaskL运行的过程中,虽然TaskH还是无法打断TaskL,但是在TaskL运行过程中(没有释放出互斥信号量)系统就会将TaskL提升到TaskH(无法获取信号量被阻塞的任务)的优先级!这样TaskM就无法打断TaskL,等到TaskL完成后(释放互斥信号量)TaskH就会立即运行!这样就达到了效果!
互斥量相关 API 函数
互斥信号量不能用于中断服务函数中!
cubMX创建
在Mutexes栏里面点击Add然后选择创建方式(静态或动态)即可创建!!!
代码示例解析(便于理解)
创建的是互斥量:
运行结果:
(TaskM无法打断TaskL)
假设创建的是二值信号量,运行结果对比(只有创建的信号量类型不同,其他均相同):(TaskM可以打断TaskL)