FreeRTOS 信号量与消息队列学习
图片引用CSDN内容: 链接
1. 什么是信号量?
有小伙伴就在思考,要是我任务之间要通信怎么办?例如一个任务负责按键扫描,怎么通知其他的任务我按下了按键了呢?并且其他任务需要等待按键按下才做一些工作。这个时候就要引出信号量了。队列,信号量,消息队列等等,都是差不多的使用原理,可以参考下面的这个动图 知乎参考链接
我们直接创建一个工程来试试吧,可以看到仓库的2_FreeRTOS_Semaphore
工程。我们就以二值信号为例来讲。我们创建一个按键扫描的任务,然后创建一个接受按键信号然后打印信息的任务。接着设定一个信号量。生成工程后,我们移植上KEY按键的程序,改一下task中函数的内容,
void KeyTaskFunc(void *argument)
{
/* USER CODE BEGIN KeyTaskFunc */
/* Infinite loop */
for(;;)
{
if(KeyScan(0))
{
osSemaphoreRelease(KeyBinarySem01Handle);
}
osDelay(1);
}
/* USER CODE END KeyTaskFunc */
}
void LogTaskFunc(void *argument)
{
/* USER CODE BEGIN LogTaskFunc */
/* Infinite loop */
for(;;)
{
// infinite wait
if(osSemaphoreAcquire(KeyBinarySem01Handle, 100) == osOK)
printf("key pressed\r\n");
osDelay(500);
}
/* USER CODE END LogTaskFunc */
}
按照仓库的例程,编译烧录重启后,按下按键KEY,就会出现打印消息了.
2. 互斥信号量与互斥是什么?
使用信号量的时候,会出现一个问题,那就是优先级翻转的问题,以下图为例,系统中有3个不同优先级的任务H/M/L,最高优先级任务H和最低优先级任务L通过 信号量机制,共享资源。目前任务L占有资源,锁定了信号量,Task H运行后将被阻塞,直到Task L释放信号量后,Task H才能够退出阻塞状态继续运行。但是Task H在等待Task L释放信号量的过 程中,中等优先级任务M抢占了任务L,从而延迟了信号量的释放时间,导致Task H阻塞了更长时 间,这种现象称为优先级倒置或优先级反转(翻转)。
怎么解决呢,那就是引入互斥量,即当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任 务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。
做个实验吧~
我们实验来试一下用常规信号量和互斥量的实验区别吧,首先来到仓库的工程FryPi/2.software/2.Advanced/0.FreeRTOS/3_FreeRTOS_Mutex
。
我们设置了3个任务,优先级从高到底分别是Task: H, M和L. 那我们再创建二值信号量和互斥量来分别试一下都有什么效果。
首先测试正常的二值信号量,代码如下:
void Task_H_Func(void *argument)
{
for(;;)
{
osSemaphoreAcquire(BinarySem01Handle, osWaitForever);
// osMutexAcquire(Mutex01Handle, osWaitForever);
printf("High Task get mutex, start\r\n");
HAL_Delay(1000);
printf("High Task give mutex, end\r\n");
osSemaphoreRelease(BinarySem01Handle);
// osMutexRelease(Mutex01Handle);
osDelay(1000);
}
}
void Task_M_Func(void *argument)
{
for(;;)
{
printf("Middle Task use cpu, but do nothing\r\n");
osDelay(1000);
}
}
void Task_L_Func(void *argument)
{
for(;;)
{
osSemaphoreAcquire(BinarySem01Handle, osWaitForever);
// osMutexAcquire(Mutex01Handle, osWaitForever);
printf("Low Task get mutex, start\r\n");
HAL_Delay(3000);
printf("Low Task give mutex, end\r\n");
osSemaphoreRelease(BinarySem01Handle);
// osMutexRelease(Mutex01Handle);
osDelay(1000);
}
}
即让L_Task低优先级任务和H_Task高优先级任务都进行信号量的获取和释放,那么出现的现象就可能会出现下面的情况:居然中优先级别的任务占用了CPU,让高优先的任务等待了很久低优先任务释放信号量!
为了解决这个问题,我们直接将二值信号量,更换为互斥量,那么就解决了,即把工程中BinarySem01Handle
更换为Mutex01Handle
, 修改注释了的部分.
改完后烧录测试,可以发现,在低优先一直在等待的时候,中优先级没有打断他,等他释放了信号之后,高优先级任务获取到mutex开始执行。
3. 实用的消息队列
我们在OV-Watch手表的项目中,其实并没有使用到信号量和互斥量,用得比较多的就是消息队列,还能够在线程之间传递数据,十分方便。跟信号量的使用方式差不多的,大家可以直接看工程进行学习,直接学会看API会用就可以啦~