互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合,,互斥信号量适合用于那些需要互斥访问的应用中。就好比你定义了串口1和串口2,使用串口1时不想使用串口2,使用串口2时不想使用串口1,这时候就可以使用互斥量。
#include "semphr.h" //头文件
SemaphoreHandle_t MuxSem_Handle; //定义互斥量句柄
MuxSem_Handle = xSemaphoreCreateMutex(); //互斥量创建函数
void vSemaphoreDelete(BinarySem_Handle); //删除互斥信号量函数
互斥信号量、二值信号量和计数信号量它们的api函数都是一样的,创建、删除、获取、释放。唯一不同的地方就是它们的创建函数,其他的调用方法都是一样的。
创建互斥信号量首先定义头文件和句柄,然后调用xSemaphoreCreateMutex()函数来创建互斥信号量,最后的删除函数只需要把互斥信号量的句柄传进去就好了。
xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
xSemaphoreGive(MuxSem_Handle); //释放信号量
互斥信号量的获取函数有两个参数,第一个是互斥信号量的句柄,第二个是阻塞时间。阻塞时间我给的是portMAX_DELAY,这是freertos的一个宏定义,代表一个最大阻塞时间,也可以理解为一直阻塞。
释放函数只需要把互斥信号量的句柄传进去就好了。除此之外,互斥量的获取和释放是不能在中断里使用的,他不像二值信号量和计数信号量一样有中断里可以使用的获取和释放函数,这和它的机制有关。
互斥信号量的机制是当你获取一个互斥信号量句柄时,必须释放以后其他的任务才可以获取句柄,如果你一直不释放,就会导致当前任务一直阻塞在这。这就导致其他任务无法获取句柄,任务则无法执行,我们都知道中断里是不能进行阻塞的,所以互斥信号量不可以在中断里使用。
我们在stm32c8t6上设置两个按键key1和key2,按下key1时获取和释放高优先级的互斥量,按下key2时释放低优先级的互斥量。
TaskHandle_t startTask_handler; //总任务的句柄
TaskHandle_t ledTask_handler; //led的句柄
TaskHandle_t lowTask_handler; //低优先级的句柄
TaskHandle_t highTask_handler; //高优先级的句柄
TaskHandle_t midTask_handler; //中等优先级的句柄
void startTask(void *arg)
{
BaseType_t xReturn = pdFALSE; //创建接收值
taskENTER_CRITICAL(); //临界区
MuxSem_Handle = xSemaphoreCreateMutex();
xReturn = xTaskCreate(midTask,"midTask",64,NULL,2,&midTask_handler); //创建中等任务
xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler); //创建led任务
xReturn = xTaskCreate(lowTask,"lowTask",64,NULL,1,&lowTask_handler); //创建低优先级任务
xReturn = xTaskCreate(highTask,"highTask",64,NULL,3,&highTask_handler); //创建高优先级任务
if(xReturn == pdTRUE)
{
printf("Task create ok\n");
}
else
{
printf("Task create error\n");
}
vTaskDelete(startTask_handler); //删除开始任务
taskEXIT_CRITICAL(); //临界区
}
首先创建5个任务句柄,分别是开始任务、led任务、低优先级任务、中等优先级任务和高优先级任务。开始任务startTask(void *arg)是用来创建其他4个函数的。
void ledTask(void *arg)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
}
}
led任务函数的作用就好比一个心跳包,让led不停闪烁,可以通过led的状态来判断我们的程序是否运行正常。
void highTask(void *arg)
{
BaseType_t xReturn = pdFALSE;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
{
xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
if(xReturn == pdTRUE)
{
printf("highTask is running \n");
}
else
{
printf("high error\n");
}
xReturn = xSemaphoreGive(MuxSem_Handle); //释放信号量
if(xReturn == pdTRUE)
{
printf("highTask is give ok \n");
}
else
{
printf("highTask is give error \n");
}
}
vTaskDelay(200);
}
}
当key1被按下时,也就是PA0被按下时,获取互斥信号量,获取之后直接释放,代码逻辑也比较简单。
void midTask(void *arg)
{
while(1)
{
printf("midTask is running\n");
vTaskDelay(2000);
}
}
中优先级任务和led任务一样起到一个心跳包的作用,每隔2秒在串口打印一次信息。
void lowTask(void *arg)
{
BaseType_t xReturn = pdFALSE;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
{
xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
if(xReturn == pdTRUE)
{
printf("lowTask is running \n");
}
else
{
printf("low error\n");
}
xReturn = xSemaphoreGive(MuxSem_Handle); //获取信号量
if(xReturn == pdTRUE)
{
printf("lowTask is give ok \n");
}
else
{
printf("lowTask is give error \n");
}
}
vTaskDelay(200);
}
}
低优先级任务的代码逻辑和高优先级任务的逻辑是一致的,按下key2也就是PA1时,获取互斥信号量并释放。
刚开始先复位,串口打印Task create ok,代表所有任务创建成功。我们可以看到2-4行,中等优先级任务每隔2秒打印一次,打印了3次。随后连续按下2次key1,高优先级任务分别释放和获取了两次信号量。之后又连续按下两次key2,低优先级任务分别释放和获取了两次,我们的任务是可以正常运行的。
还是一样在stm32c8t6上面设置两个按键,当按下key2时,低优先级任务被获取和释放,但是按下key1时,高优先级任务只获取不释放了,来看看效果。
void highTask(void *arg)
{
BaseType_t xReturn = pdFALSE;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
{
xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
if(xReturn == pdTRUE)
{
printf("highTask is running \n");
}
else
{
printf("high error\n");
}
// xReturn = xSemaphoreGive(MuxSem_Handle); //释放信号量
// if(xReturn == pdTRUE)
// {
// printf("highTask is give ok \n");
// }
// else
// {
// printf("highTask is give error \n");
// }
}
vTaskDelay(200);
}
}
我们的操作很简单,只需要把高优先级释放互斥量的那一段代码给屏蔽了,其他的任务无需改变。
刚开始先复位,串口打印Task create ok,说明所有任务都创建成功了。第2-3行,中等优先级任务打印了两句话,随后按下两次key2,我们可以看到低优先级任务都可以正常执行获取和释放。
当我们按下key1时,高优先级任务进行了获取,但到了后面,再怎么按下key1和key2,串口都无法打印这两个任务的信息了。因为高优先的任务没有释放互斥量,就导致这两个任务就被阻塞了,无法执行,所以只有中等优先级任务在打印。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "queue.h"
#include "led.h"
#include "stdlib.h"
#include "semphr.h"
SemaphoreHandle_t MuxSem_Handle; //二值信号量句柄
TaskHandle_t startTask_handler; //总任务的句柄
TaskHandle_t ledTask_handler; //led的句柄
TaskHandle_t lowTask_handler; //低优先级的句柄
TaskHandle_t highTask_handler; //高优先级的句柄
TaskHandle_t midTask_handler; //中等优先级的句柄
void ledTask(void *arg)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
}
}
void lowTask(void *arg)
{
BaseType_t xReturn = pdFALSE;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
{
xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
if(xReturn == pdTRUE)
{
printf("lowTask is running \n");
}
else
{
printf("low error\n");
}
xReturn = xSemaphoreGive(MuxSem_Handle); //获取信号量
if(xReturn == pdTRUE)
{
printf("lowTask is give ok \n");
}
else
{
printf("lowTask is give error \n");
}
}
vTaskDelay(200);
}
}
void midTask(void *arg)
{
while(1)
{
printf("midTask is running\n");
vTaskDelay(2000);
}
}
void highTask(void *arg)
{
BaseType_t xReturn = pdFALSE;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
{
xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); //获取信号量
if(xReturn == pdTRUE)
{
printf("highTask is running \n");
}
else
{
printf("high error\n");
}
// xReturn = xSemaphoreGive(MuxSem_Handle); //释放信号量
// if(xReturn == pdTRUE)
// {
// printf("highTask is give ok \n");
// }
// else
// {
// printf("highTask is give error \n");
// }
}
vTaskDelay(200);
}
}
void startTask(void *arg)
{
BaseType_t xReturn = pdFALSE; //创建接收值
taskENTER_CRITICAL(); //临界区
MuxSem_Handle = xSemaphoreCreateMutex();
xReturn = xTaskCreate(midTask,"midTask",64,NULL,1,&midTask_handler); //创建中等任务
xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler); //创建led任务
xReturn = xTaskCreate(lowTask,"lowTask",64,NULL,2,&lowTask_handler); //创建低优先级任务
xReturn = xTaskCreate(highTask,"highTask",64,NULL,3,&highTask_handler); //创建高优先级任务
if(xReturn == pdTRUE)
{
printf("Task create ok\n");
}
else
{
printf("Task create error\n");
}
vTaskDelete(startTask_handler); //删除开始任务
taskEXIT_CRITICAL(); //临界区
}
int main(void)
{
LED_Init();
usrt1_init(9600);
xTaskCreate(startTask,"startTask",512,NULL,1,&startTask_handler); //创建开始任务
vTaskStartScheduler();
}