FreeRTOS学习笔记-信号量

概述

Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore,一般用于程序同步,或限制某一资源的同时访问。

– 参考 https://blog.csdn.net/m0_37817135/article/details/109578289

二值信号量一般用于程序同步,互斥锁则一般用于资源保护。

关键函数说明

//创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void );
//返回值:NULL == 创建失败;other value: 信号量句柄

//创建互斥锁
SemaphoreHandle_t xSemaphoreCreateMutex( void );
//互斥锁创建后无需Give。

//创建递归互持锁
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );

//创建计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting( 
			UBaseType_t uxMaxCount,//最大空位
			UBaseType_t uxInitialCount //初始空位
			);

//删除信号量
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

//获取信号量
BaseType_t xSemaphoreTake( 
			SemaphoreHandle_t xSemaphore, //信号量句柄
			TickType_t xTicksToWait //如果信号量被占用所需要等待的时间,0==立即返回,portMAX_DELAY == 死等
			);
//返回值:pdPASS == 成功;pdFAIL == 失败

//获取递归互斥锁
BaseType_t xSemaphoreTakeRecursive( 
			SemaphoreHandle_t xSemaphore, //信号量句柄
			TickType_t xTicksToWait //如果信号量被占用所需要等待的时间,0==立即返回,portMAX_DELAY == 死等
			);
//返回值:pdPASS == 成功;pdFAIL == 失败

//释放信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
//返回值:pdPASS == 成功释放;pdFAIL== 失败

//释放递归互斥锁
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
//返回值:pdPASS == 成功释放;pdFAIL== 失败

//获取信号量空位
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );

课程示例

二值信号量的使用

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/semphr.h"

SemaphoreHandle_t Semaphore_iCount;
int iCount = 100;

void mytask1(void *pvParameter)
{
    xSemaphoreTake(Semaphore_iCount, portMAX_DELAY);
    for (int i = 0; i < 5; i++)
    {
        printf("task1: iCount = %d\n", iCount);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        iCount++;
    }
    xSemaphoreGive(Semaphore_iCount);
    vTaskDelete(NULL);
}
void mytask2(void *pvParameter)
{
    xSemaphoreTake(Semaphore_iCount, portMAX_DELAY);
    for (int i = 0; i < 5; i++)
    {
        printf("task2: iCount = %d\n", iCount);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        iCount++;
    }
    xSemaphoreGive(Semaphore_iCount);
    vTaskDelete(NULL);
}
void app_main(void)
{
    Semaphore_iCount = xSemaphoreCreateBinary();
    xSemaphoreGive(Semaphore_iCount);
    printf("app_main: Create and Give Semaphore_iCount \n");

    TaskHandle_t myTastHandle1 = NULL;
    TaskHandle_t myTastHandle2 = NULL;
    xTaskCreate(mytask1, "mytask1", 1024 * 5, NULL, 1, &myTastHandle1);
    xTaskCreate(mytask2, "mytask2", 1024 * 5, NULL, 1, &myTastHandle2);
    printf("app_main: Create task1 and task2 \n");

    // 10s后删除任务和信号量,退出app_main
    vTaskDelay(10000 / portTICK_PERIOD_MS);
    vSemaphoreDelete(Semaphore_iCount);
    printf("app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main \n");
}

效果

app_main: Create and Give Semaphore_iCount
app_main: Create task1 and task2
task1: iCount = 100
task1: iCount = 101
task1: iCount = 102
task1: iCount = 103
task1: iCount = 104
task2: iCount = 105
task2: iCount = 106
task2: iCount = 107
task2: iCount = 108
task2: iCount = 109
app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main

使用一个二值信号量对iCount进行保护,任务1和任务2各处理5s,屏蔽信号量效果如下:

app_main: Create and Give Semaphore_iCount
app_main: Create task1 and task2
task1: iCount = 100
task2: iCount = 100
task1: iCount = 101
task2: iCount = 102
task1: iCount = 103
task2: iCount = 104
task1: iCount = 105
task2: iCount = 106
task1: iCount = 107
task2: iCount = 108
app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main

计数信号量使用

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/semphr.h"

SemaphoreHandle_t Semaphore_parking_space;

void car_in_task(void *pvParameter)
{
    while (1)
    {
        printf("parking_space = %d\n", uxSemaphoreGetCount(Semaphore_parking_space));
        BaseType_t r = xSemaphoreTake(Semaphore_parking_space, 0);
        if (r == pdPASS)
        {
            printf("One car in \n");
        }
        else
        {
            printf("No space \n");
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void car_out_task(void *pvParameter)
{
    while (1)
    {
        vTaskDelay(6000 / portTICK_PERIOD_MS);
        xSemaphoreGive(Semaphore_parking_space);
        printf("One car out \n");
    }
}
void app_main(void)
{
    Semaphore_parking_space = xSemaphoreCreateCounting(5, 5);
    xTaskCreate(car_in_task, "car_in_task", 1024 * 5, NULL, 1, NULL);
    xTaskCreate(car_out_task, "car_out_task", 1024 * 5, NULL, 1, NULL);
}

效果

parking_space = 5
One car in
parking_space = 4
One car in
parking_space = 3
One car in
parking_space = 2
One car in
parking_space = 1
One car in
parking_space = 0
No space
One car out
parking_space = 1
One car in
parking_space = 0
No space
parking_space = 0
No space

互斥锁的使用

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/semphr.h"

SemaphoreHandle_t Semaphore_iCount;
int iCount = 0;

void mytask1(void *pvParameter)
{
    BaseType_t iRet;
    while (1)
    {
        iRet = xSemaphoreTake(Semaphore_iCount, 0);
        if (iRet == pdPASS)
        {
            for (int i = 0; i < 5; i++)
            {
                printf("task1: iCount = %d\n", iCount);
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                iCount++;
            }
            xSemaphoreGive(Semaphore_iCount);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        else
        {
            printf("task1: didn't take \n");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
}
void mytask2(void *pvParameter)
{
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    while (1)
        ;
}
void mytask3(void *pvParameter)
{
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    BaseType_t iRet;
    while (1)
    {
        iRet = xSemaphoreTake(Semaphore_iCount, 1);//注意参数等待时间,必须大于0,否则任务1获取互斥锁也无法继承任务3的优先级。
        if (iRet == pdPASS)
        {
            for (int i = 0; i < 5; i++)
            {
                printf("task3: iCount = %d\n", iCount);
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                iCount++;
            }
            xSemaphoreGive(Semaphore_iCount);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        else
        {
            printf("task3: didn't take \n");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
}
void app_main(void)
{
    //Semaphore_iCount = xSemaphoreCreateBinary();
    Semaphore_iCount = xSemaphoreCreateMutex();//互斥锁

    //xSemaphoreGive(Semaphore_iCount);

    vTaskSuspendAll();
    xTaskCreatePinnedToCore(mytask1, "mytask1", 1024 * 5, NULL, 1, NULL, 0); //最后一个参数是Core ID
    xTaskCreatePinnedToCore(mytask2, "mytask2", 1024 * 5, NULL, 2, NULL, 0);
    xTaskCreatePinnedToCore(mytask3, "mytask3", 1024 * 5, NULL, 3, NULL, 0);
    xTaskResumeAll();
}

效果

task1: iCount = 0
task1: iCount = 1
task3: didn’t take
task1: iCount = 2
task3: didn’t take
task1: iCount = 3
task3: didn’t take
task1: iCount = 4
task3: didn’t take
task3: iCount = 5
E (12285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12285) task_wdt: - IDLE (CPU 0)
E (12285) task_wdt: Tasks currently running:
E (12285) task_wdt: CPU 0: mytask2
E (12285) task_wdt: CPU 1: IDLE
E (12285) task_wdt: Print CPU 0 (current core) backtrace
task3: iCount = 6
task3: iCount = 7
task3: iCount = 8
task3: iCount = 9
E (17285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (17285) task_wdt: - IDLE (CPU 0)
E (17285) task_wdt: Tasks currently running:
E (17285) task_wdt: CPU 0: mytask2
E (17285) task_wdt: CPU 1: IDLE
E (17285) task_wdt: Print CPU 0 (current core) backtrace
task3: iCount = 10
task3: iCount = 11
task3: iCount = 12
task3: iCount = 13
task3: iCount = 14
E (22285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (22285) task_wdt: - IDLE (CPU 0)
E (22285) task_wdt: Tasks currently running:
E (22285) task_wdt: CPU 0: mytask2
E (22285) task_wdt: CPU 1: IDLE
E (22285) task_wdt: Print CPU 0 (current core) backtrace

任务1在获取信号量的同时继承了任务3的优先级,临时提高自身优先级,等任务1释放信号量以后,优先级降低,任务2死循环,任务1再不会被调度.
任务2死循环不但让任务1,还有加入看门狗的IDLE任务也无法执行,因此每隔5s会打印任务看门狗异常。
实际测试发现互斥锁,在take的时候等待时间为0则任务1无法继承任务3的优先级,待查。

递归锁的使用

递归锁用于保护一次任务中需要级联调用的多个资源,如下图,任务1中先获取A资源,在A中又使用B资源,最后需要先释放B、再释放A。
在这里插入图片描述

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/semphr.h"

SemaphoreHandle_t Semaphore_iCount;

void mytask1(void *pvParameter)
{
    while (1)
    {
        xSemaphoreTakeRecursive(Semaphore_iCount, 0);
        for (int i = 0; i < 3; i++)
        {
            printf("-----------\n");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        xSemaphoreTakeRecursive(Semaphore_iCount, 0);
        for (int i = 0; i < 5; i++)
        {

            printf("------------------------\n");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        xSemaphoreGiveRecursive(Semaphore_iCount);
        for (int i = 0; i < 3; i++)
        {
            printf("-----------\n");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        xSemaphoreGiveRecursive(Semaphore_iCount);
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void mytask2(void *pvParameter)
{
    BaseType_t iRet;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    while (1)
    {
        iRet = xSemaphoreTakeRecursive(Semaphore_iCount, 0);
        if (iRet == pdPASS)
        {
            xSemaphoreGiveRecursive(Semaphore_iCount);
            printf(">>>>>>>>>>>>>>>>>>>>>\n");
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    Semaphore_iCount = xSemaphoreCreateRecursiveMutex(); //创建递归互斥锁

    xTaskCreatePinnedToCore(mytask1, "mytask1", 1024 * 5, NULL, 1, NULL, 0); //最后一个参数是Core ID
    xTaskCreatePinnedToCore(mytask2, "mytask2", 1024 * 5, NULL, 2, NULL, 0);
}

效果

-----------
-----------
-----------
------------------------
------------------------
------------------------
------------------------
------------------------
-----------
-----------
-----------
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
-----------
-----------
-----------
------------------------
------------------------
------------------------
------------------------
------------------------
-----------
-----------
-----------
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>

任务1在2级递归调用互斥锁没有全部释放的时候,任务2无法获取互斥锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值