一、概念
在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步,
而互斥型信号量用于资源保护。
互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级反转现
象。
优先级反转:
系统中有
3
个不同优先级的任务
H/M/L
,最高优先级任务
H
和最低优先级任务
L
通过 信号量机制,共享资源。目前任务L
占有资源,锁定了信号量,
Task H
运行后将被阻塞,直到
Task L释放信号量后,
Task H
才能够退出阻塞状态继续运行。但是
Task H
在等待
Task L
释放信号量的过程中,中等优先级任务M
抢占了任务
L
,从而延迟了信号量的释放时间,导致
Task H
阻塞了更长时 间,这种现象称为优先级倒置或反转。
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任
务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。
不过这个高优先级的任务
会将低优先级任务的优先级提升到与自己相同的优先级。
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。
二、没有使用互斥量的时候
配置中、高、低三个优先级
osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
TaskHHandle = osThreadCreate(osThread(TaskH), NULL);
osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
TaskMHandle = osThreadCreate(osThread(TaskM), NULL);
osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
TaskLHandle = osThreadCreate(osThread(TaskL), NULL);
void StartTaskH(void const * argument)
{
for(;;)
{
xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);
printf("TaskH:我开始进入厕所,发功中。。\r\n");
HAL_Delay(1000);
printf("TaskH:我上完厕所了,真舒服。。。\r\n");
xSemaphoreGive(myBinarySemHandle);
osDelay(1000);
}
}
void StartTaskM(void const * argument)
{
for(;;)
{
printf("TaskM:我就是为了占用资源,带女朋友兜风\r\n");
osDelay(1000);
}
}
void StartTaskL(void const * argument)
{
for(;;)
{
xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);
printf("TaskL:我开始进入厕所,发功中。。\r\n");
HAL_Delay(3000);
printf("TaskL:我上完厕所了,真舒服。。。\r\n");
xSemaphoreGive(myBinarySemHandle);
osDelay(1000);
}
}
互斥量实验(接上半部分)
首先删除二值信号量
创建互斥量
void MX_FREERTOS_Init(void) {
osMutexDef(myMutex);
myMutexHandle = osMutexCreate(osMutex(myMutex));
osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
TaskHHandle = osThreadCreate(osThread(TaskH), NULL);
osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
TaskMHandle = osThreadCreate(osThread(TaskM), NULL);
osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
TaskLHandle = osThreadCreate(osThread(TaskL), NULL);
}
void StartTaskH(void const * argument)
{
for(;;)
{
xSemaphoreTake(myMutexHandle,portMAX_DELAY);//句柄变为myMutexHandle
printf("TaskH:我开始进入厕所,发功中。。\r\n");
HAL_Delay(1000);
printf("TaskH:我上完厕所了,真舒服。。。\r\n");
xSemaphoreGive(myMutexHandle);
osDelay(1000);
}
}
void StartTaskM(void const * argument)
{
for(;;)
{
printf("TaskM:我就是为了占用资源,带女朋友兜风\r\n");
osDelay(1000);
}
}
void StartTaskL(void const * argument)
{
for(;;)
{
xSemaphoreTake(myMutexHandle,portMAX_DELAY);
printf("TaskL:我开始进入厕所,发功中。。\r\n");
HAL_Delay(3000);
printf("TaskL:我上完厕所了,真舒服。。。\r\n");
xSemaphoreGive(myMutexHandle);
osDelay(1000);
}
}
运行结果:
通过引入互斥量,可以实现资源的保护功能。
事件标志组
一、概念
事件标志位
:表明某个事件是否发生,联想:全局变量
flag
。通常按位表示,每一个位表示一个事件(高8
位不算)
事件标志组
是一组事件标志位的集合, 可以简单的理解事件标志组,就是一个整数。 事件标志组本质是一个 16
位或
32
位无符号的数据类型
EventBits_t
,由
configUSE_16_BIT_TICKS 决定。
虽然使用了
32
位无符号的数据类型变量来存储事件标志, 但其中的高
8
位用作存储事件标志组的
控制信息,低
24
位用作存储事件标志 ,所以说一个事件组最多可以存储
24
个事件标志!
二、实验
实验需求:创建一个事件标志组和两个任务( task1 和 task2),task1 检测按键,如果检测到 KEY1 和 KEY2 都按过,则执行 task2 。
CubeMX配置:配置任务1和任务2
代码部分:
Task1:
void MX_FREERTOS_Init(void) {
osThreadDef(Task1, StartTask1, osPriorityNormal, 0, 128);
Task1Handle = osThreadCreate(osThread(Task1), NULL);
osThreadDef(Task2, StartTask2, osPriorityNormal, 0, 128);
Task2Handle = osThreadCreate(osThread(Task2), NULL);
eventgroup_handle = xEventGroupCreate();//用动态方式创建事件标志组
}
void StartTask1(void const * argument)
{
for(;;)
{
//等待KEY1按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
{
osDelay(20);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
{
xEventGroupSetBits(eventgroup_handle,0x01);//设置事件标志位(句柄,设置按位置);
}
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)== GPIO_PIN_RESET);
}
//等待KEY2按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
{
osDelay(20);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
{
xEventGroupSetBits(eventgroup_handle,0x02);//2为事件标志位
}
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)== GPIO_PIN_RESET);
}
osDelay(1);
}
}
Task2:
void StartTask2(void const * argument)
{
EventBits_t event_bit = 0;//定义event_bit用于承接xEventGroupWaitBits(等待事件标志位返回值)
for(;;)
{
event_bit = xEventGroupWaitBits(eventgroup_handle,0x01|0x02,pdTRUE,pdFALSE,portMAX_DELAY);//为pdFALSE时为逻辑或,形象理解就是一个领导通过,那么就全部通过。
printf("返回值:%#x,请假成功,可以去大保健了!\r\n",event_bit);
osDelay(1);
}
/* USER CODE END StartTask2 */
}
实验结果:pdFALSE——等待的事件位有一个为1(逻辑或) ,为pdTRUE——所有等待事件位全为1(逻辑与)。实验现象为pdFALSE的截图。