抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

信号量

信号量类似于裸机开发中的标志位,用于任务间通信的变量,或者成为标志位。

临界资源:任何时刻只能被一个任务访问的资源

递归信号量和互斥量都实现继承优先级机制,降低优先级反转的危害

二值信号量运行机制

  1. 类似于互斥量,但是没有互斥量的优先级继承机制
  2. 偏向于同步功能,任务与任务的同步,任务与中断的同步
  3. 等效于一个只有一个消息的队列,只存在两种状态:有消息或者无消息
  4. 信号被获取时为0,被释放时为1
  5. 二值信号量无效时,有其他任务获取信号量,则任务进入阻塞态

  1. 某时刻信号量被中断或任务释放时,其他进入阻塞态的最高优先级任务进入就绪态

计数信号量运行机制

1
2
3
//使用计数信号量需要开启宏定义
//开启计数信号量
#define configUSE_COUNTING_SEMAPHORES 1
  1. 当事件发生时,信号量被释放,计数值加一
  2. 当事件被处理时,信号量被取走,计数值减一
  3. 信号计数值表示还未处理事件数量
  4. 使用完资源必须返还信号量,信号量为0时表示无资源可用
  5. 可用于资源管理,允许多个任务获取信号量进行资源访问,当访问个数到达最大时,其他任务进入阻塞态,直到有任务释放资源,类似于二值信号量访问资源的过程

互斥信号量运行机制

1
2
3
4
5
//使用互斥信号量需要开启宏定义
//开启互斥量
#define configUSE_MUTEXES 1
//开启递归互斥量
#define configUSE_RECURSIVE_MUTEXES 1
  1. 特殊的二值信号量,拥有优先级继承机制
  2. 信号量创建之初为满,使用临界资源时,先获取信号量使其为空,防止其他任务使用临界资源。
  3. 低优先级任务申请互斥量,高优先级任务申请不到进入阻塞态,低优先级任务临时继承高优先级的优先级。

  1. 适用于可能引发优先级翻转的场景
  2. 可递归使用,形成递归互斥量,任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。
  3. 互斥量不能在中断使用,优先级继承只在普通任务中有效,同一时期只能有一个任务获取互斥量进行资源访问

递归信号量运行机制

  1. 获取递归信号量的任务可以重复获取该量,拥有该量的所有权
  2. 递归几次就要返还几次,有点类似任务的挂起和恢复之间的关系

信号常见函数

  1. xSemaphoreCreateBinary()​​创建二值信号量,返回一个句柄,函数原型xQueueGenericCreate()​​
  2. xSemaphoreCreateCounting()​​创建计数信号量,返回一个句柄,原型​xQueueGenericCreate()​​
  3. xSemaphoreGive()​​释放信号量,可用于二值信号量、互斥信号量、计数信号量的任务释放,不能用于递归信号量,原型xQueueGenericSend()​​
  4. xSemaphoreGiveFromISR()​​用于中断的释放信号量,只能用于二值信号量、计数信号量,原型xQueueGiveFromISR()​​
  5. xSemaphoreTake()​获取信号量,可用于二值信号量、互斥信号量、计数信号量的任务获取,不能用于递归信号量,原型xQueueGenericReceive()​,该操作类似任务访问队列消息的过程。
  6. xSemaphoreTakeFromISR()​​,用于中断的获取信号量,只能用于二值信号量、计数信号量
  7. xSemaphoreCreateMutex()​创建互斥量,返回一个句柄,只能被同一个任务获取,再次获取会失败。原型xQueueCreateMutex()​,原型的原型为xQueueGenericCreate()
  8. prvInitialiseMutex()​初始化互斥量
  9. xSemaphoreCreateRecursiveMutex()​创建递归互斥量,可以被同一个任务获取很多次,获取前先释放。原型xQueueCreateMutex()
  10. xSemaphoreDelete()​信号量删除
  11. xSemaphoreTakeRecursive()​获取递归互斥量,原型xQueueTakeMutexRecursive()​,该函数不能获取由xSemaphoreCreateMutex()​创建的互斥量
  12. 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
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); //等待时间: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​中的优先级顺序

在低优先级任务运行的时候,中优先级任务无法抢占低优先级的任务,低优先级任务的优先级发生翻转。