信号量
信号量类似于裸机开发中的标志位,用于任务间通信的变量,或者成为标志位。
临界资源:任何时刻只能被一个任务访问的资源
递归信号量和互斥量都实现继承优先级机制,降低优先级反转的危害
二值信号量运行机制
- 类似于互斥量,但是没有互斥量的优先级继承机制
- 偏向于同步功能,任务与任务的同步,任务与中断的同步
- 等效于一个只有一个消息的队列,只存在两种状态:有消息或者无消息
- 信号被获取时为0,被释放时为1
- 二值信号量无效时,有其他任务获取信号量,则任务进入阻塞态
- 某时刻信号量被中断或任务释放时,其他进入阻塞态的最高优先级任务进入就绪态
计数信号量运行机制
1 2 3
|
#define configUSE_COUNTING_SEMAPHORES 1
|
- 当事件发生时,信号量被释放,计数值加一
- 当事件被处理时,信号量被取走,计数值减一
- 信号计数值表示还未处理事件数量
- 使用完资源必须返还信号量,信号量为0时表示无资源可用
- 可用于资源管理,允许多个任务获取信号量进行资源访问,当访问个数到达最大时,其他任务进入阻塞态,直到有任务释放资源,类似于二值信号量访问资源的过程
互斥信号量运行机制
1 2 3 4 5
|
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
|
- 特殊的二值信号量,拥有优先级继承机制
- 信号量创建之初为满,使用临界资源时,先获取信号量使其为空,防止其他任务使用临界资源。
- 低优先级任务申请互斥量,高优先级任务申请不到进入阻塞态,低优先级任务临时继承高优先级的优先级。
- 适用于可能引发优先级翻转的场景
- 可递归使用,形成递归互斥量,任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。
- 互斥量不能在中断使用,优先级继承只在普通任务中有效,同一时期只能有一个任务获取互斥量进行资源访问
递归信号量运行机制
- 获取递归信号量的任务可以重复获取该量,拥有该量的所有权
- 递归几次就要返还几次,有点类似任务的挂起和恢复之间的关系
信号常见函数
xSemaphoreCreateBinary()
创建二值信号量,返回一个句柄,函数原型xQueueGenericCreate()
xSemaphoreCreateCounting()
创建计数信号量,返回一个句柄,原型xQueueGenericCreate()
xSemaphoreGive()
释放信号量,可用于二值信号量、互斥信号量、计数信号量的任务释放,不能用于递归信号量,原型xQueueGenericSend()
xSemaphoreGiveFromISR()
用于中断的释放信号量,只能用于二值信号量、计数信号量,原型xQueueGiveFromISR()
xSemaphoreTake()
获取信号量,可用于二值信号量、互斥信号量、计数信号量的任务获取,不能用于递归信号量,原型xQueueGenericReceive()
,该操作类似任务访问队列消息的过程。
xSemaphoreTakeFromISR()
,用于中断的获取信号量,只能用于二值信号量、计数信号量
xSemaphoreCreateMutex()
创建互斥量,返回一个句柄,只能被同一个任务获取,再次获取会失败。原型xQueueCreateMutex()
,原型的原型为xQueueGenericCreate()
prvInitialiseMutex()
初始化互斥量
xSemaphoreCreateRecursiveMutex()
创建递归互斥量,可以被同一个任务获取很多次,获取前先释放。原型xQueueCreateMutex()
xSemaphoreDelete()
信号量删除
xSemaphoreTakeRecursive()
获取递归互斥量,原型xQueueTakeMutexRecursive()
,该函数不能获取由xSemaphoreCreateMutex()
创建的互斥量
xSemaphoreGiveRecursive()
释放递归互斥量,原型xQueueGiveMutexRecursive()
创建二值信号量
设计1个Button
任务,通过按键发送控制LED0
的开关,二值量操作信息通过串口显示
二值量句柄,创建一个引用,并不是创建一个真实的二值量
1 2 3 4 5
| SemaphoreHandle_t Binary_Handle=NULL;
static void Receive_Semaph_Task(void *paramter); static void Send_Semaph_Task(void *paramter);
|
创建一个真实的二值量
1 2
| Binary_Handle = xSemaphoreCreateBinary();
|
接收数据任务控制LED0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static void Receive_Semaph_Task(void *paramter) { BaseType_t xReturn=pdPASS; uint32_t r_queue; while (1) { xReturn = xSemaphoreTake(Binary_Handle,portMAX_DELAY); if(xReturn==pdPASS) { printf("二值信号量获取成功\r\n"); } LED0_Turn(); } }
|
Button控制发送数据任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| static void Send_Semaph_Task(void *paramter) { BaseType_t xReturn=pdTRUE; while (1) { if (Key_BTN(BTN1)==KEY_ON) { xReturn = xSemaphoreGive(Binary_Handle); if (xReturn==pdTRUE) { printf("二值信号量释放成功\r\n"); }else { printf("二值信号量释放失败\r\n"); } }
if (Key_BTN(BTN2)==KEY_ON) { xReturn = xSemaphoreGive(Binary_Handle); if (xReturn==pdTRUE) { printf("二值信号量释放成功\r\n"); }else { printf("二值信号量释放失败\r\n"); } } vTaskDelay(20); }
}
|
创建计数二值量
设计2个Button
任务,通过按键BTN1
申请车位,按键BTN2
释放车位,最大车位5
开启宏定义
1 2
| #define configUSE_COUNTING_SEMAPHORES 1
|
计数信号量句柄,创建一个引用,并不是创建一个真实的计数信号量
1 2 3 4 5 6 7 8
| SemaphoreHandle_t CountSem_Handle=NULL;
static void Take_Task(void *paramter); static void Give_Task(void *paramter);
static TaskHandle_t Take_Task_Handle=NULL; static TaskHandle_t Give_Task_Handle=NULL;
|
创建一个真实的计数信号量
1 2 3
| CountSem_Handle =xSemaphoreCreateCounting(5, 5);
|
BTN1控制申请车位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static void Take_Task(void *paramter) { BaseType_t xReturn=pdTRUE; while (1) { if (Key_BTN(BTN1)==KEY_ON) { xReturn = xSemaphoreTake(CountSem_Handle, 0); if (xReturn==pdTRUE) { printf("BTN1按下,申请车位成功\r\n"); }else { printf("BTN1按下,车位已满,申请车位失败\r\n"); } } vTaskDelay(20);
}
}
|
BTN2释放车位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static void Give_Task(void *paramter) { BaseType_t xReturn=pdTRUE; while (1) { if (Key_BTN(BTN2)==KEY_ON) { xReturn = xSemaphoreGive(CountSem_Handle); if (xReturn==pdTRUE) { printf("BTN2按下,释放一个车位\r\n"); }else { printf("BTN2按下,无车位可被释放\r\n"); } } vTaskDelay(20); } }
|
创建二值信号量优先级翻转
创建Low
、Mid
、High
三个任务,其中Low
获取信号后,被Mid
打断,但是Mid
未执行完,执行完后,Low
释放信号后,High
才能获取信号,在此之前High
进入阻塞态。
二值信号量句柄,创建一个引用,并不是创建一个真实的二值信号量
1 2 3 4 5 6 7
| static TaskHandle_t LowPriority_Task_Handle=NULL; static TaskHandle_t MidPriority_Task_Handle=NULL; static TaskHandle_t HighPriority_Task_Handle=NULL;
SemaphoreHandle_t Binary_Handle=NULL;
|
创建一个真实的二值量
1 2
| Binary_Handle = xSemaphoreCreateBinary();
|
Low任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static void LowPriority_Task(void *paramter) { static uint32_t i; BaseType_t xReturn=pdPASS;
while (1) { printf("Low任务获取二值信号量\r\n"); xReturn = xSemaphoreTake(Binary_Handle,portMAX_DELAY); if(xReturn==pdPASS) { printf("Low任务获取成功\r\n"); } for ( i = 0; i < 2000000; i++) { taskYIELD(); }
printf("Low任务释放二值信号量\r\n"); xReturn = xSemaphoreGive(Binary_Handle);
LED0_Turn(); vTaskDelay(500); } }
|
Mid任务
1 2 3 4 5 6 7 8
| static void MidPriority_Task(void *paramter) { while (1) { printf("Mid任务运行中\r\n"); vTaskDelay(500); } }
|
High任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static void HighPriority_Task(void *paramter) { BaseType_t xReturn = pdTRUE; while (1) { printf("High任务获取二值信号量\r\n"); xReturn = xSemaphoreTake(Binary_Handle,portMAX_DELAY);
if (xReturn==pdTRUE) { printf("High任务获取成功\r\n"); }
LED0_Turn(); xReturn = xSemaphoreGive(Binary_Handle); vTaskDelay(500); } }
|
注意调整AppTaskCreate
中的优先级顺序
创建互斥量
开启宏定义
1 2 3 4
| #define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
|
互斥句柄,创建一个引用,并不是创建一个真实的互斥
1 2 3 4 5 6 7
| static TaskHandle_t LowPriority_Task_Handle=NULL; static TaskHandle_t MidPriority_Task_Handle=NULL; static TaskHandle_t HighPriority_Task_Handle=NULL;
SemaphoreHandle_t MutexSem_Handle=NULL;
|
创建一个真实的互斥量
1 2
| MutexSem_Handle = xSemaphoreCreateMutex();
|
Low任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static void LowPriority_Task(void *paramter) { static uint32_t i; BaseType_t xReturn=pdPASS;
while (1) { printf("Low任务获取互斥量\r\n"); xReturn = xSemaphoreTake(MutexSem_Handle,portMAX_DELAY); if(xReturn==pdPASS) { printf("Low任务获取成功\r\n"); } for ( i = 0; i < 2000000; i++) { taskYIELD(); }
printf("Low任务释放互斥量\r\n"); xReturn = xSemaphoreGive(MutexSem_Handle);
LED0_Turn(); vTaskDelay(1000); } }
|
Mid任务
1 2 3 4 5 6 7 8
| static void MidPriority_Task(void *paramter) { while (1) { printf("Mid任务运行中\r\n"); vTaskDelay(500); } }
|
High任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static void HighPriority_Task(void *paramter) { BaseType_t xReturn = pdTRUE; while (1) { printf("High任务获取互斥量\r\n"); xReturn = xSemaphoreTake(MutexSem_Handle,portMAX_DELAY);
if (xReturn==pdTRUE) { printf("High任务获取成功\r\n"); }
LED0_Turn(); printf("High释放互斥量\r\n"); xReturn = xSemaphoreGive(MutexSem_Handle); vTaskDelay(1000); } }
|
注意调整AppTaskCreate
中的优先级顺序
在低优先级任务运行的时候,中优先级任务无法抢占低优先级的任务,低优先级任务的优先级发生翻转。