返回列表 发新帖我要提问本帖赏金: 80.00元(功能说明)

[uCOS/RTOS] 【每周分享】FreeRTOS信号量详讲(代码+介绍)

[复制链接]
7320|3
 楼主| 虚幻的是灵魂 发表于 2024-10-19 17:03 | 显示全部楼层 |阅读模式
本帖最后由 虚幻的是灵魂 于 2024-10-19 17:11 编辑

#申请原创#  @21小跑堂
概念
在 FreeRTOS中,信号量(Semaphore)是一种用于实现任务之间同步和资源共享的机制。它是一种计数型的同步原语,用于控制对共享资源的访问和保护。
在FreeRTOS中,包含4种类型的信号量:

1. 二进制信号量(Binary Semaphore):

二进制信号量是最基本的信号量类型。它的计数值要么为0(表示信号量已被获取),要么为1(表示信号量可用)。二进制信号量常用于实现互斥访问共享资源的场景,只允许一个任务访问资源。

在 FreeRTOS 中,你可以使用 xSemaphoreCreateBinary() 函数创建一个二进制信号量。任务可以通过 xSemaphoreTake() 函数获取信号量,通过 xSemaphoreGive() 函数释放信号量。

2. 计数型信号量(Counting Semaphore):

计数型信号量允许指定初始计数值,并支持多个任务同时获取信号量。计数型信号量的计数值表示可用的资源数量。

在 FreeRTOS 中,你可以使用 xSemaphoreCreateCounting() 函数创建一个计数型信号量。任务可以使用 xSemaphoreTake() 函数获取信号量,使用 xSemaphoreGive() 函数释放信号量。

3. 互斥信号量(Mutex Semaphore):

互斥信号量也用于实现资源的互斥访问,类似于二进制信号量。但与二进制信号量不同的是,互斥信号量允许同一个任务多次获取信号量,而不会导致死锁。在任务持有互斥信号量时,其他任务无法获取该信号量,必须等待该任务释放信号量。

在 FreeRTOS 中,你可以使用 xSemaphoreCreateMutex() 函数创建一个互斥信号量。任务可以使用 xSemaphoreTake()函数获取信号量,使用 xSemaphoreGive() 函数释放信号量。

4. 递归互斥信号量(Recursive Mutex Semaphore):

递归互斥信号量是一种特殊的信号量类型,用于解决任务在嵌套调用中对资源的重复获取。它允许同一个任务多次获取信号量而不会导致死锁。

在 FreeRTOS 中,你可以使用 xSemaphoreCreateRecursiveMutex() 函数创建一个递归互斥信号量。任务可以使用 xSemaphoreTakeRecursive() 函数多次获取信号量,并使用 xSemaphoreGiveRecursive() 函数相应地释放信号量。

开发流程
  • 创建信号量
  • 开启一个任务,用来等待信号量
  • 开启一个任务,用来发送信号量



二进制信号量
功能 描述
xSemaphoreCreateBinary 创建二进制信号量
xSemaphoreTake 等待信号
xSemaphoreGive 发送信号


信号量的创建

二进制信号量创建语法

SemaphoreHandle_t xSemaphoreCreateBinary();

返回值为信号量的句柄。


等待信号操作

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

案例一
  • 开启两个任务,分别去等待信号。
  • 开启按键扫描任务,当点击按键时,发送信号

main.c
  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_handler;
  10. TaskHandle_t            task_key_handler;
  11. TaskHandle_t            task1_handler;
  12. TaskHandle_t            task2_handler;
  13. SemaphoreHandle_t                         sema_handler;

  14. void task1(void *pvParameters) {
  15.     BaseType_t result;
  16.     while(1) {
  17.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  18.         if(result == pdTRUE) {
  19.             printf("task1\n");
  20.         } else {
  21.             printf("task1 Error\n");
  22.         }
  23.     }
  24. }

  25. void task2(void *pvParameters) {
  26.     BaseType_t result;
  27.     while(1) {
  28.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  29.         if(result == pdTRUE) {
  30.             printf("task2\n");
  31.         } else {
  32.             printf("task2 Error\n");
  33.         }
  34.     }
  35. }

  36. void task_key(void *pvParameters) {
  37.     FlagStatus pre_state = RESET;
  38.     BaseType_t result;
  39.     while(1) {
  40.         FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
  41.         if(SET == state && pre_state == RESET) {
  42.             // 当前高电平, 上一次为低电平,按下
  43.             pre_state = state;

  44.             result = xSemaphoreGive(sema_handler);
  45.         } else if(RESET == state && pre_state == SET) {
  46.             // 当前高电平, 上一次为低电平,抬起
  47.             pre_state = state;
  48.         }
  49.         vTaskDelay(20);
  50.     }
  51. }

  52. void start_task(void *pvParameters) {
  53.     taskENTER_CRITICAL();

  54.     xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
  55.     xTaskCreate(task1, "task1", 64, NULL, 3, &task1_handler);
  56.     xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
  57.     vTaskDelete(task_handler);

  58.     taskEXIT_CRITICAL();
  59. }

  60. void Usart0_recv(uint8_t *data, uint32_t len)
  61. {
  62.     printf("recv: %s\n", data);
  63. }

  64. static void GPIO_config() {
  65.     // 时钟初始化
  66.     rcu_periph_clock_enable(RCU_GPIOA);
  67.     // 配置GPIO模式
  68.     gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  69. }

  70. int main(void)
  71. {
  72.     NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
  73.     systick_config();
  74.     GPIO_config();
  75.     Usart0_init();

  76.     sema_handler = xSemaphoreCreateBinary();
  77.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
  78.     vTaskStartScheduler();

  79.     while(1) {}
  80. }

观察,两个任务是否获得信号。

改变两个任务的优先级,观察两个任务信号的获取情况。

案例二

在案例1的基础上,通过串口接收,来发送信号。

串口发送信号

  1. void Usart0_recv(uint8_t *data, uint32_t len)
  2. {
  3.     printf("recv: %s\n", data);
  4.     xSemaphoreGiveFromISR(sema_handler, NULL);
  5. }

xSemaphoreGiveFromISR中断中发送信号



计数型信号量
功能 描述
xSemaphoreCreateCounting 创建计数型信号量
xSemaphoreTake 等待信号
xSemaphoreGive 发送信号


信号量的创建
  1. SemaphoreHandle_t xSemaphoreCreateCounting(        const UBaseType_t uxMaxCount,
  2.                                                 const UBaseType_t uxInitialCount);
参数说明:



  • const UBaseType_t uxMaxCount最大计数值。
  • const UBaseType_t uxInitialCount初始化当前计数值。

返回值为信号量的句柄。


等待信号操作

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

案例一
  • 开启两个任务,等待信号,接收到信号后,处理耗时操作
  • 开启按键扫描,点击按键时发送信号

main.c
  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_handler;
  10. TaskHandle_t            task_key_handler;
  11. TaskHandle_t            task1_handler;
  12. TaskHandle_t            task2_handler;
  13. SemaphoreHandle_t                 sema_handler;

  14. void task1(void *pvParameters) {
  15.     BaseType_t result;
  16.     while(1) {
  17.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  18.         if(result == pdTRUE) {
  19.             printf("task1 %ld\n", uxSemaphoreGetCount(sema_handler));
  20.         } else {
  21.             printf("task1 Error\n");
  22.         }
  23.         vTaskDelay(2500);
  24.     }
  25. }

  26. void task2(void *pvParameters) {
  27.     BaseType_t result;
  28.     while(1) {
  29.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  30.         if(result == pdTRUE) {
  31.             printf("task2 %ld\n", uxSemaphoreGetCount(sema_handler));
  32.         } else {
  33.             printf("task2 Error\n");
  34.         }
  35.         vTaskDelay(2500);
  36.     }
  37. }

  38. void task_key(void *pvParameters) {
  39.     FlagStatus pre_state = RESET;
  40.     BaseType_t result;
  41.     while(1) {
  42.         FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
  43.         if(SET == state && pre_state == RESET) {
  44.             // 当前高电平, 上一次为低电平,按下
  45.             pre_state = state;

  46.             result = xSemaphoreGive(sema_handler);
  47. //                        if(result == pdTRUE) {
  48. //                                printf("semaphore give success\n");
  49. //                        } else {
  50. //                                printf("semaphore give error\n");
  51. //                        }
  52.         } else if(RESET == state && pre_state == SET) {
  53.             // 当前高电平, 上一次为低电平,抬起
  54.             pre_state = state;
  55.         }
  56.         vTaskDelay(20);
  57.     }
  58. }

  59. void start_task(void *pvParameters) {
  60.     taskENTER_CRITICAL();

  61.     xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
  62.     xTaskCreate(task1, "task1", 64, NULL, 3, &task1_handler);
  63.     xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
  64.     vTaskDelete(task_handler);

  65.     taskEXIT_CRITICAL();
  66. }

  67. void Usart0_recv(uint8_t *data, uint32_t len)
  68. {
  69.     printf("recv: %s\n", data);
  70. }

  71. static void GPIO_config() {
  72.     // 时钟初始化
  73.     rcu_periph_clock_enable(RCU_GPIOA);
  74.     // 配置GPIO模式
  75.     gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  76. }

  77. int main(void)
  78. {
  79.     NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
  80.     systick_config();
  81.     GPIO_config();
  82.     Usart0_init();

  83.     sema_handler = xSemaphoreCreateCounting(100, 0);
  84.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
  85.     vTaskStartScheduler();

  86.     while(1) {}
  87. }

观察,两个任务是否获得信号。

频繁点击按键,观察效果。

案例二

在案例一的基础上,通过串口接收,来发送信号。

串口发送信号

  1. void Usart0_recv(uint8_t *data, uint32_t len)
  2. {
  3.     printf("recv: %s\n", data);
  4.     xSemaphoreGiveFromISR(sema_handler, NULL);
  5. }

xSemaphoreGiveFromISR中断中发送信号





互斥信号量
功能 描述
xSemaphoreCreateMutex 创建互斥信号量
xSemaphoreTake 等待信号
xSemaphoreGive 发送信号

信号量的创建

互斥信号量创建语法

SemaphoreHandle_t xSemaphoreCreateMutex();

返回值为信号量的句柄。

等待信号操作

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

案例一
  • 开启两个任务,分别去等待信号。
  • 开启按键扫描任务,当点击按键时,发送信号

main.c
  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_handler;
  10. TaskHandle_t            task_key_handler;
  11. TaskHandle_t            task1_handler;
  12. TaskHandle_t            task2_handler;
  13. SemaphoreHandle_t                         sema_handler;

  14. void task1(void *pvParameters) {
  15.     BaseType_t result;
  16.     while(1) {
  17.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  18.         if(result == pdTRUE) {
  19.             printf("task1\n");
  20.         } else {
  21.             printf("task1 Error\n");
  22.         }
  23.     }
  24. }

  25. void task2(void *pvParameters) {
  26.     BaseType_t result;
  27.     while(1) {
  28.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  29.         if(result == pdTRUE) {
  30.             printf("task2\n");
  31.         } else {
  32.             printf("task2 Error\n");
  33.         }
  34.     }
  35. }

  36. void task_key(void *pvParameters) {
  37.     FlagStatus pre_state = RESET;
  38.     BaseType_t result;
  39.     while(1) {
  40.         FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
  41.         if(SET == state && pre_state == RESET) {
  42.             // 当前高电平, 上一次为低电平,按下
  43.             pre_state = state;

  44.             result = xSemaphoreGive(sema_handler);
  45.         } else if(RESET == state && pre_state == SET) {
  46.             // 当前高电平, 上一次为低电平,抬起
  47.             pre_state = state;
  48.         }
  49.         vTaskDelay(20);
  50.     }
  51. }

  52. void start_task(void *pvParameters) {
  53.     taskENTER_CRITICAL();

  54.     xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
  55.     xTaskCreate(task1, "task1", 64, NULL, 3, &task1_handler);
  56.     xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
  57.     vTaskDelete(task_handler);

  58.     taskEXIT_CRITICAL();
  59. }

  60. void Usart0_recv(uint8_t *data, uint32_t len)
  61. {
  62.     printf("recv: %s\n", data);
  63. }

  64. static void GPIO_config() {
  65.     // 时钟初始化
  66.     rcu_periph_clock_enable(RCU_GPIOA);
  67.     // 配置GPIO模式
  68.     gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  69. }

  70. int main(void)
  71. {
  72.     NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
  73.     systick_config();
  74.     GPIO_config();
  75.     Usart0_init();

  76.     sema_handler = xSemaphoreCreateBinary();
  77.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
  78.     vTaskStartScheduler();

  79.     while(1) {}
  80. }

观察,两个任务是否获得信号。

改变两个任务的优先级,观察两个任务信号的获取情况。

案例二

在案例一的基础上,通过串口接收,来发送信号。

串口发送信号

  1. void Usart0_recv(uint8_t *data, uint32_t len)
  2. {
  3.     printf("recv: %s\n", data);
  4.     xSemaphoreGiveFromISR(sema_handler, NULL);
  5. }

xSemaphoreGiveFromISR中断中发送信号





计数型信号量
功能 描述
xSemaphoreCreateCounting 创建计数型信号量
xSemaphoreTake 等待信号
xSemaphoreGive 发送信号


信号量的创建

  1. SemaphoreHandle_t xSemaphoreCreateCounting(        const UBaseType_t uxMaxCount,
  2.                                                 const UBaseType_t uxInitialCount);

参数说明:

  • const UBaseType_t uxMaxCount最大计数值。
  • const UBaseType_t uxInitialCount初始化当前计数值。

返回值为信号量的句柄。


等待信号操作

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

案例一
  • 开启两个任务,等待信号,接收到信号后,处理耗时操作
  • 开启按键扫描,点击按键时发送信号

main.c
  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_handler;
  10. TaskHandle_t            task_key_handler;
  11. TaskHandle_t            task1_handler;
  12. TaskHandle_t            task2_handler;
  13. SemaphoreHandle_t                 sema_handler;

  14. void task1(void *pvParameters) {
  15.     BaseType_t result;
  16.     while(1) {
  17.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  18.         if(result == pdTRUE) {
  19.             printf("task1 %ld\n", uxSemaphoreGetCount(sema_handler));
  20.         } else {
  21.             printf("task1 Error\n");
  22.         }
  23.         vTaskDelay(2500);
  24.     }
  25. }

  26. void task2(void *pvParameters) {
  27.     BaseType_t result;
  28.     while(1) {
  29.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  30.         if(result == pdTRUE) {
  31.             printf("task2 %ld\n", uxSemaphoreGetCount(sema_handler));
  32.         } else {
  33.             printf("task2 Error\n");
  34.         }
  35.         vTaskDelay(2500);
  36.     }
  37. }

  38. void task_key(void *pvParameters) {
  39.     FlagStatus pre_state = RESET;
  40.     BaseType_t result;
  41.     while(1) {
  42.         FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
  43.         if(SET == state && pre_state == RESET) {
  44.             // 当前高电平, 上一次为低电平,按下
  45.             pre_state = state;

  46.             result = xSemaphoreGive(sema_handler);
  47. //                        if(result == pdTRUE) {
  48. //                                printf("semaphore give success\n");
  49. //                        } else {
  50. //                                printf("semaphore give error\n");
  51. //                        }
  52.         } else if(RESET == state && pre_state == SET) {
  53.             // 当前高电平, 上一次为低电平,抬起
  54.             pre_state = state;
  55.         }
  56.         vTaskDelay(20);
  57.     }
  58. }

  59. void start_task(void *pvParameters) {
  60.     taskENTER_CRITICAL();

  61.     xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
  62.     xTaskCreate(task1, "task1", 64, NULL, 3, &task1_handler);
  63.     xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
  64.     vTaskDelete(task_handler);

  65.     taskEXIT_CRITICAL();
  66. }

  67. void Usart0_recv(uint8_t *data, uint32_t len)
  68. {
  69.     printf("recv: %s\n", data);
  70. }

  71. static void GPIO_config() {
  72.     // 时钟初始化
  73.     rcu_periph_clock_enable(RCU_GPIOA);
  74.     // 配置GPIO模式
  75.     gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  76. }

  77. int main(void)
  78. {
  79.     NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
  80.     systick_config();
  81.     GPIO_config();
  82.     Usart0_init();

  83.     sema_handler = xSemaphoreCreateCounting(100, 0);
  84.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
  85.     vTaskStartScheduler();

  86.     while(1) {}
  87. }

观察,两个任务是否获得信号。

频繁点击按键,观察效果。

案例二

在案例一的基础上,通过串口接收,来发送信号。

串口发送信号

  1. void Usart0_recv(uint8_t *data, uint32_t len)
  2. {
  3.     printf("recv: %s\n", data);
  4.     xSemaphoreGiveFromISR(sema_handler, NULL);
  5. }

xSemaphoreGiveFromISR中断中发送信号




互斥信号量
功能 描述
xSemaphoreCreateMutex 创建互斥信号量
xSemaphoreTake 等待信号
xSemaphoreGive 发送信号


信号量的创建


互斥信号量创建语法

SemaphoreHandle_t xSemaphoreCreateMutex();

返回值为信号量的句柄。

等待信号操作

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

案例一

开启两个任务,同时等待和发送信号,观察任务调用

main.c

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_handler;
  10. TaskHandle_t            task1_handler;
  11. TaskHandle_t            task2_handler;
  12. SemaphoreHandle_t                 sema_handler;

  13. void task1(void *pvParameters) {
  14.     BaseType_t result;
  15.     while(1) {
  16.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  17.         if(result == pdTRUE) {
  18.             printf("task1\n");
  19.         } else {
  20.             printf("task1 Error\n");
  21.         }
  22.         xSemaphoreGive(sema_handler);
  23.         vTaskDelay(2000);
  24.     }
  25. }

  26. void task2(void *pvParameters) {
  27.     BaseType_t result;
  28.     while(1) {
  29.         result = xSemaphoreTake(sema_handler, portMAX_DELAY);
  30.         if(result == pdTRUE) {
  31.             printf("task2\n");
  32.         } else {
  33.             printf("task2 Error\n");
  34.         }
  35.         xSemaphoreGive(sema_handler);
  36.         vTaskDelay(2000);
  37.     }
  38. }

  39. void start_task(void *pvParameters) {
  40.     taskENTER_CRITICAL();
  41.    
  42.     xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
  43.     xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
  44.     vTaskDelete(task_handler);

  45.     taskEXIT_CRITICAL();
  46. }

  47. void Usart0_recv(uint8_t *data, uint32_t len)
  48. {
  49.     printf("recv: %s\n", data);
  50. }


  51. int main(void)
  52. {
  53.     NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
  54.     systick_config();
  55.     Usart0_init();

  56.     sema_handler = xSemaphoreCreateMutex();
  57.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
  58.     vTaskStartScheduler();

  59.     while(1) {}
  60. }



递归互斥信号量
功能 描述
xSemaphoreCreateRecursiveMutex 创建递归互斥信号量
xSemaphoreTakeRecursive 等待信号
xSemaphoreGiveRecursive 发送信号


信号量的创建

互斥信号量创建语法

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex();

返回值为信号量的句柄。


等待信号操作

BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime );

1、SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。

2、TickType_t xBlockTime表示要等待的时间。通常我们一直等到有信号到来,这里我们可以填写portMAX_DELAY

3、返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL


发送信号操作

BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xSemaphore);
  • SemaphoreHandle_t xSemaphore表示要等待的哪个信号量句柄。
  • 返回值类型为BaseType_t,表示成功或者失败,取值为pdPASS和pdFAIL

示例代码:

main.c

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "semphr.h"
  8. #include "Usart0.h"

  9. TaskHandle_t            task_Handler;
  10. TaskHandle_t            task1_Handler;
  11. TaskHandle_t            task2_Handler;
  12. SemaphoreHandle_t                 sema_handler;

  13. void task1(void *pvParameters) {
  14.         BaseType_t result;
  15.     while(1) {
  16.         printf("task1        take 0\n");
  17.                                 xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
  18.                         
  19.                                 printf("task1        take 1\n");
  20.                                 xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
  21.                                 
  22.                                 printf("task1        give\n");
  23.                                 xSemaphoreGiveRecursive(sema_handler);
  24.                                 vTaskDelay(1000);
  25.     }
  26. }

  27. void task2(void *pvParameters) {
  28.         BaseType_t result;
  29.     while(1) {
  30.                                 printf("task2        take 0\n");
  31.                                 xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
  32.                         
  33.                                 printf("task2        take 1\n");
  34.                                 xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
  35.                                 
  36.                                 printf("task2        give\n");
  37.                                 xSemaphoreGiveRecursive(sema_handler);
  38.                                 
  39.                                 vTaskDelay(1000);
  40.     }
  41. }

  42. void start_task(void *pvParameters) {
  43.     taskENTER_CRITICAL();

  44.     xTaskCreate(task1, "task1", 128, NULL, 2, &task1_Handler);
  45.         xTaskCreate(task2, "task2", 128, NULL, 2, &task2_Handler);
  46.     vTaskDelete(task_Handler);

  47.     taskEXIT_CRITICAL();
  48. }

  49. void Usart0_recv(uint8_t *data, uint32_t len)
  50. {
  51.     printf("recv: %s\n", data);
  52. }

  53. static void GPIO_config() {
  54.     // 时钟初始化
  55.     rcu_periph_clock_enable(RCU_GPIOA);
  56.     // 配置GPIO模式
  57.     gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  58. }

  59. int main(void)
  60. {
  61.     systick_config();
  62.     GPIO_config();
  63.     Usart0_init();
  64.                
  65.         sema_handler = xSemaphoreCreateRecursiveMutex();
  66.         if(sema_handler == NULL) {
  67.                 printf("create error\r\n");
  68.         }
  69.     xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_Handler);
  70.     vTaskStartScheduler();

  71.     while(1) {}
  72. }

比较

四种信号量类型的常见应用场景的比较:

1. 二进制信号量:

应用场景:二进制信号量适用于任务间的互斥、同步和事件通知。例如,当多个任务需要共享一个资源时,可以使用二进制信号量来保证同一时间只有一个任务访问该资源。

示例应用:多个任务竞争访问共享打印机资源。


2. 计数信号量:

应用场景:计数信号量适用于任务间的资源共享和限制、任务同步和事件通知。它可以表示一组可用资源的数量,任务可以通过获取计数信号量来申请和释放这些资源。

示例应用:限制同时执行的任务数量、任务间的生产者-消费者模式。


3. 互斥信号量:

应用场景:互斥信号量用于互斥访问共享资源的场景。它确保在任意给定时间只有一个任务可以访问共享资源,避免了数据竞争和不一致性。

示例应用:多个任务竞争访问共享数据结构、临界区保护。


4. 递归互斥信号量:

应用场景:递归互斥信号量适用于同一任务需要多次获取互斥资源的场景。它允许同一任务在获取资源后再次获取,而不会引起死锁。

示例应用:任务递归调用、嵌套临界区保护。


需要根据具体的应用需求选择合适的信号量类型。如果需要简单的互斥访问,互斥信号量可能是最合适的选择。如果需要限制资源数量或任务同步,计数信号量可以派上用场。而对于同一任务需要多次获取资源的情况,递归互斥信号量提供了便利。











本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

打赏榜单

21小跑堂 打赏了 80.00 元 2024-10-25
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

详解FreeRTOS中信号量的概念,内容整理完整,开发流程介绍详细,步骤清晰,段落设计合理,可读性较高  发表于 2024-10-25 17:44
聪聪哥哥 发表于 2024-10-26 14:37 | 显示全部楼层
这个帖子的源代码能分享一下吗?
博创欧阳 发表于 2024-10-27 19:44 | 显示全部楼层
讲的很清楚,期待下周的更新
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

118

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部