发新帖本帖赏金 80.00元(功能说明)我要提问
返回列表
打印
[uCOS/RTOS]

【每周分享】FreeRTOS信号量详讲(代码+介绍)

[复制链接]
3206|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 虚幻的是灵魂 于 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
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;
SemaphoreHandle_t                         sema_handler;

void task1(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task1\n");
        } else {
            printf("task1 Error\n");
        }
    }
}

void task2(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task2\n");
        } else {
            printf("task2 Error\n");
        }
    }
}

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

            result = xSemaphoreGive(sema_handler);
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

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

    taskEXIT_CRITICAL();
}

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

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

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

    while(1) {}
}

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

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

案例二

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

串口发送信号

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

xSemaphoreGiveFromISR中断中发送信号



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


信号量的创建
SemaphoreHandle_t xSemaphoreCreateCounting(        const UBaseType_t uxMaxCount,
                                                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
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;
SemaphoreHandle_t                 sema_handler;

void task1(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task1 %ld\n", uxSemaphoreGetCount(sema_handler));
        } else {
            printf("task1 Error\n");
        }
        vTaskDelay(2500);
    }
}

void task2(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task2 %ld\n", uxSemaphoreGetCount(sema_handler));
        } else {
            printf("task2 Error\n");
        }
        vTaskDelay(2500);
    }
}

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

            result = xSemaphoreGive(sema_handler);
//                        if(result == pdTRUE) {
//                                printf("semaphore give success\n");
//                        } else {
//                                printf("semaphore give error\n");
//                        }
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

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

    taskEXIT_CRITICAL();
}

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

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

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

    while(1) {}
}

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

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

案例二

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

串口发送信号

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

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
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;
SemaphoreHandle_t                         sema_handler;

void task1(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task1\n");
        } else {
            printf("task1 Error\n");
        }
    }
}

void task2(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task2\n");
        } else {
            printf("task2 Error\n");
        }
    }
}

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

            result = xSemaphoreGive(sema_handler);
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

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

    taskEXIT_CRITICAL();
}

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

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

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

    while(1) {}
}

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

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

案例二

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

串口发送信号

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

xSemaphoreGiveFromISR中断中发送信号





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


信号量的创建

SemaphoreHandle_t xSemaphoreCreateCounting(        const UBaseType_t uxMaxCount,
                                                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
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;
SemaphoreHandle_t                 sema_handler;

void task1(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task1 %ld\n", uxSemaphoreGetCount(sema_handler));
        } else {
            printf("task1 Error\n");
        }
        vTaskDelay(2500);
    }
}

void task2(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task2 %ld\n", uxSemaphoreGetCount(sema_handler));
        } else {
            printf("task2 Error\n");
        }
        vTaskDelay(2500);
    }
}

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

            result = xSemaphoreGive(sema_handler);
//                        if(result == pdTRUE) {
//                                printf("semaphore give success\n");
//                        } else {
//                                printf("semaphore give error\n");
//                        }
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

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

    taskEXIT_CRITICAL();
}

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

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

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

    while(1) {}
}

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

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

案例二

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

串口发送信号

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

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

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;
SemaphoreHandle_t                 sema_handler;

void task1(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task1\n");
        } else {
            printf("task1 Error\n");
        }
        xSemaphoreGive(sema_handler);
        vTaskDelay(2000);
    }
}

void task2(void *pvParameters) {
    BaseType_t result;
    while(1) {
        result = xSemaphoreTake(sema_handler, portMAX_DELAY);
        if(result == pdTRUE) {
            printf("task2\n");
        } else {
            printf("task2 Error\n");
        }
        xSemaphoreGive(sema_handler);
        vTaskDelay(2000);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();
   
    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);
    vTaskDelete(task_handler);

    taskEXIT_CRITICAL();
}

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


int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    Usart0_init();

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

    while(1) {}
}



递归互斥信号量
功能 描述
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

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "Usart0.h"

TaskHandle_t            task_Handler;
TaskHandle_t            task1_Handler;
TaskHandle_t            task2_Handler;
SemaphoreHandle_t                 sema_handler;

void task1(void *pvParameters) {
        BaseType_t result;
    while(1) {
        printf("task1        take 0\n");
                                xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
                        
                                printf("task1        take 1\n");
                                xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
                                
                                printf("task1        give\n");
                                xSemaphoreGiveRecursive(sema_handler);
                                vTaskDelay(1000);
    }
}

void task2(void *pvParameters) {
        BaseType_t result;
    while(1) {
                                printf("task2        take 0\n");
                                xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
                        
                                printf("task2        take 1\n");
                                xSemaphoreTakeRecursive(sema_handler, portMAX_DELAY);
                                
                                printf("task2        give\n");
                                xSemaphoreGiveRecursive(sema_handler);
                                
                                vTaskDelay(1000);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

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

    taskEXIT_CRITICAL();
}

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

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    systick_config();
    GPIO_config();
    Usart0_init();
               
        sema_handler = xSemaphoreCreateRecursiveMutex();
        if(sema_handler == NULL) {
                printf("create error\r\n");
        }
    xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_Handler);
    vTaskStartScheduler();

    while(1) {}
}

比较

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

1. 二进制信号量:

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

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


2. 计数信号量:

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

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


3. 互斥信号量:

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

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


4. 递归互斥信号量:

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

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


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











使用特权

评论回复

打赏榜单

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

评论
21小跑堂 2024-10-25 17:44 回复TA
详解FreeRTOS中信号量的概念,内容整理完整,开发流程介绍详细,步骤清晰,段落设计合理,可读性较高 

相关帖子

沙发
聪聪哥哥| | 2024-10-26 14:37 | 只看该作者
这个帖子的源代码能分享一下吗?

使用特权

评论回复
板凳
博创欧阳| | 2024-10-27 19:44 | 只看该作者
讲的很清楚,期待下周的更新

使用特权

评论回复
发新帖 本帖赏金 80.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

115

帖子

1

粉丝