FreeRTOS 二值信号量和计数信号量

1、二值信号量和计数信号量

二值信号量

概念

  • 长度为1的队列

  • 无需关心存储内容,只需关注是否为空即可

应用场景

  • 任务与任务,任务与中断的同步

阻塞

  • 任务获取信号量时,若信号量为空,则进入阻塞状态(立即,定时,永久)

  • 任务释放信号量时,若信号量已满,则返回错误

计数信号量

概念

  • 长度大于1的队列

  • 无需关心存储内容,只需关注是否为空即可

应用场景

  • 事件计数:在这个场合中, 每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减一)来处理事件。在这种场合中创建的计数型信号量初始计数值为 0

  • 资源管理:在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100

阻塞

  • 任务获取信号量时,若信号量为空,则进入阻塞状态(立即,定时,永久)

  • 任务释放信号量时,若信号量已满,则返回错误

2、相关API函数

2.1、创建

SemaphoreHandle_t xSemaphoreCreateBinary(void)

  • 创建二值信号量

参数

说明

void

返回值

说明

NULL

创建二值信号量失败

其他值

创建二值信号量成功

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount)

  • 创建计数信号量

参数

说明

uxMaxCount

计数信号最大值,当信号量值等于此值的时候释放信号量会失败

uxInitialCount

计数信号量初始值

返回值

说明

NULL

计数型信号量创建失败

其他值

计数型信号量创建成功,返回计数型信号量句柄

2.2、释放

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)

  • 释放二值/计数信号量

参数

说明

xSemaphore

待释放信号量。该参数是一个 SemaphoreHandle_t 类型的变量,并且在使用前一定先创建。

返回值

说明

pdPASS

信号量释放成功

pdFAIL

信号量释放失败,如果信号量没有被第一时间获取,再次释放信号量时就会失败

BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHighPriorityWoken)

  • 中断里释放二值/计数信号量

参数

说明

xSemaphore

待释放信号量。该参数是一个 SemaphoreHandle_t 类型的变量,并且在使用前一定先创建

pxHighPriorityWoken

如果有多个任务等待获取信号量而进入阻塞态,调用该函数可以使任务解除阻塞态。 如果任务队列中存在优先级比解除阻塞态的任务优先级高,或者两者的优先级等同,那么 pxHighPriorityWoken 的值就会被设为 pdTRUE。如果 xpHighPriorityWoken 为 pdTRUE,则退出中断服务函数之前必须进行一次任务切换

返回值

说明

pdTRUE

信号量释放成功

pdFAIL

信号量释放失败,因为该信号量已经被释放,但还未被获取

2.3、获取

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)

  • 获取二值/计数信号量

参数

说明

xSemaphore

待获取的信号量

xBlockTime

阻塞时间

返回值

说明

pdTRUE

获取信号量成功

pdFAIL

获取超时或者失败

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHighPriorityWoken)

  • 中断里获取二值/计数信号量

参数

说明

xSemaphore

待获取的信号量

pxHighPriorityWoken

标记退出中断服务函数之前是否需要进行任务切换。该值由函数去设置,用户仅提供 存储的变量。如果 pxHighPriorityWoken 的值为 pdTRUE,则退出中断函数前必须进行一次任务切换

返回值

说明

pdPASS

获取信号量成功

pdFALSE

获取信号量失败

3、实验

3.1、实验设计

  • 创建一个开始任务,在开始任务里面,创建二值信号量、task 1和task 2两个任务后,删除开始任务;

  • task 1运行10次后,释放一个信号量;

  • task 2一直获取该信号量,获取到后,打印信息;

3.2、实验程序

k_task_handle_t task1_handle;                  //task 1 句柄
#define TSK1_PRIO                  3           //task 1 优先级
#define TASK1_STK_SIZE             (1*1024)    //task 1 分配的堆栈大小

k_task_handle_t task2_handle;                  //task 2 句柄
#define TSK2_PRIO                  2           //task 2 优先级
#define TASK2_STK_SIZE             (1*1024)    //task 2 分配的堆栈大小

k_task_handle_t start_task_handle;             //start task 句柄
#define START_TSK_PRIO             1           //start task 优先级
#define START_TSK_STK_SIZE         (1*1024)    //start task 分配的堆栈大小

#define TEST_TIME_QUANTA 100

k_sem_handle_t    g_usSem;  //信号量

void task1(void)
{
        uint8_t count = 0;
        while(1)
        {
                count++;
                my_printf("task 1 run %d times!!!\r\n", count);
                
                if(count == 10)
                {
                        if(0 != csi_kernel_sem_post(g_usSem))
                        {
                                my_printf("---task 1 give sem fail!!!\r\n");
                        }
                        else 
                        {
                                my_printf("---task 1 give sem sucess!!!\r\n");
                        }
                        
                        count = 0;
                }
                
                csi_kernel_delay_ms(1000);
        }
}

void task2(void)
{ 
        while(1)
        {
                if(0 != csi_kernel_sem_wait(g_usSem, -1))
                {
                        my_printf("task 2 take sem fail!!!\r\n");
                }
                else 
                {
                        my_printf("task 2 take sem sucess,task 2 is working now!!!\r\n");
                }
        }        
}

void start_task(void)
{
    //进入临界区
    taskENTER_CRITICAL();    
    
    //创建二值信号量
    g_usSem = csi_kernel_sem_new(1, 0);
    if (g_usSem == NULL) 
    {
        printf("fail to create semaphore.\n");
    }       

    //创建task 1
    csi_kernel_task_new((k_task_entry_t)task1, 
                                     "task1", 
                                       NULL, 
                                  TSK1_PRIO, 
                           TEST_TIME_QUANTA, 
                                       NULL, 
                             TASK1_STK_SIZE, 
                               &task1_handle);

    if (task1_handle == NULL) 
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 1!\r\n");
    }

    //创建task 2
    csi_kernel_task_new((k_task_entry_t)task2, 
                                     "task2", 
                                         NULL, 
                                    TSK2_PRIO, 
                             TEST_TIME_QUANTA, 
                                         NULL, 
                               TASK2_STK_SIZE, 
                               &task2_handle);

    if (task2_handle == NULL) 
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 2!\r\n");
    }

        //删除开始任务
    if(0 != csi_kernel_task_del(csi_kernel_task_get_cur()))
    {
         my_printf("Fail to delete start_task!\r\n");
    }

     //退出临界区
     taskEXIT_CRITICAL();            
}


void freertos_demo(void)
{

    my_printf("\r\n-->this is freertos task test demo!!!\r\n");  
    //系统初始化
    csi_kernel_init();

    //创建开始任务
    csi_kernel_task_new((k_task_entry_t)start_task, 
                                      "start_task", 
                                                 0, 
                                    START_TSK_PRIO, 
                                                 0, 
                                                 0, 
                                START_TSK_STK_SIZE, 
                               &start_task_handle);

    //任务调度开始
    csi_kernel_start();
}

3.3、实验现象

  • start task在创建后,创建好task 1 task 2即被删除

  • 初始阶段task 1运行, task 2由于此时获取到信号量还没有释放,处于堵塞状态

  • task 1运行10次后,释放一个信号量。task 2获取到信号量后,执行了打印动作

  • 此时task 2运行受到了信号量的控制,未获取信号量前即堵塞,充分释放了CPU的使用权

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值