在多任务系统中,如果有两个或者多个任务需要使用同一个变量,那么会出现一些数据错误。
比如任务A和任务B都需要实现对某个全局变量的自加,而变量自加需要三个过程:①从内存读取数据②对数据进行自加一③向内存写入数据。如果任务A在执行完过程①后被切换带任务B,任务B会对变量值进行修改,当时间片再次分配给任务A时,任务A会继续执行上次中断在过程①的任务,当执行完过程③之后,将数据写入内存中,这时上次任务B对全局变量的数据操作将不会起作用,因为被这次操作将上次任务B写入内存的全局变量覆盖了。
这时就需要引入消息队列、信号量以及互斥量。
在消息队列初始化的时候会开辟一个消息缓冲,在HAL库中的初始化代码如下,其缓冲缓冲区的大小可以自行设定。消息缓冲区是一个环形的缓冲区,也就是在支持覆盖写入时,写到队列最后一个空间时,又会从队列的第一块空间开始写入数据。
osMessageQDef(myQueue01, 16, uint16_t);
myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);
向消息队列写数据的API如下
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
queue_id
:消息ID
info
:消息内容
millisec
:超时时间
向消息队列的写数据主要分三步操作:关闭Tick中断、写数据、打开Tick中断
关闭Tick中断:停止任务调度
写数据:其流程如下
关闭Tick中断:开启任务调度
从消息队列读数据的API如下
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
queue_id
:消息ID
millisec
:超时时间
从消息队列读数据主要分为三步操作:关闭Tick中断、写数据、打开Tick中断
关闭Tick中断:停止任务调度
读数据:其流程如下
关闭Tick中断:停止任务调度
在CubeMX中可以配置二值信号量和多值信号量,其主要作用是任务间的信号交互而不是消息的传递。
可以初始化二值信号量和和多值信号量。
/* Create the semaphores(s) */
/* definition and creation of myBinarySem01 */
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
/* definition and creation of myCountingSem01 */
osSemaphoreDef(myCountingSem01);
myCountingSem01Handle = osSemaphoreCreate(osSemaphore(myCountingSem01), 4);
释放信号量的API
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
semaphore_id
:信号量ID
释放信号量的步骤:关闭Tick中断、释放信号量、打开Tick中断
关闭Tick中断:停止任务调度
释放信号量:HAL库里面好像不能进行阻塞,而是直接返回是否释放成功。
关闭Tick中断:停止任务调度
释放信号量的API
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec)
semaphore_id
:信号量ID
millisec
:超时时间
释放信号量的步骤:关闭Tick中断、获得信号量、打开Tick中断
关闭Tick中断:停止任务调度
获得信号量:其步骤如下
关闭Tick中断:停止任务调度
互斥锁的释放和获得方法几乎相同,但是互斥锁可以实现优先级翻转。
优先级翻转:低优先级的任务获得互斥锁之后,如果高优先级的的任务也需要获得互斥锁,那么高优先级的任务和中等优先级的任务需要让拥有互斥锁的低优先级任务先执行,让低优先级的任务释放互斥锁,直到高优先级的任务获得互斥锁。