FreeRTOS学习笔记(七) FreeRTOS学习笔记七在STM32F407ZGT6上用计数信号量实现控制资源访问前言在上一篇中我们提到了信号量的核心是“控制资源访问”与“任务同步”并使用实例用二值信号量实现了一个任务同步的实例。在这一篇中我们围绕计数信号量和资源管理展开。在上一篇的实验结果中可以明显的发现串口输出的同步信息数量明显少于中断次数这是由于TIM2中断更新时间短。在使用二值信号量同步任务和中断时信号的传输还没有完成新的中断又发生了。显然二值信号量无法保存多次中断的状态而采用计数信号量可以解决这个问题。什么是计数信号量其特点是什么计数型信号量与二值信号量类似二值信号量相当于队列长度为 1 的队列因此二值信号 量只能容纳一个资源这也是为什么命名为二值信号量。计数信号量相当于长度大于1的队列因此计数型信号量能够容纳多个资源故其具有如下特点1.维护一个计数值表示可用资源的数量或已发生事件的次数。2.当任务获取信号量时计数值递减当释放信号量时计数值递增。3.计数值可以大于1当计数值为0时尝试获取信号量的任务可能会被阻塞。计数信号量的使用场景是什么计数型信号量主要用于事件的计数和资源管理。1.事件计数在这种场合下每次事件发生后在处理函数中释放计数型信号量**计数型信号量的资源数加 1其他等待事件发生的任务获取计数型信号量计数型信号量的资源数减 1**。这么等待事件的任务在事件一发生就可以在成功获取到计数型信号量并执行相应的操作。在这种场合 下计数型信号量的资源数一般在创建时设置为 0。2.资源管理在这种场合下计数型信号量的资源数代表着共享资源的可用数量。以停车场停车为例 停车场有固定数量车位。入口显示屏实时显示剩余车位数。每当有车辆进入剩余车位减1车辆离开时剩余车位加1。在这里的剩余车位数就是计数信号量的计数值如果停的车申请计数信号量的任务数超过了计数值最后一个开进来的车没有车位申请失败只有当之前有车开出去计数信号量被释放后进入的车才能开进来申请成功。计数信号量具体如何使用计数信号量相关 API 函数函数描述xSemaphoreCreateCounting()使用动态方式创建计数信号量xSemaphoreCreateCountingStatic()使用静态方式创建计数信号量xSemaphoreGive()释放信号量xSemaphoreGiveFromISR()在中断中释放信号量xSemaphoreTake()获取信号量xSemaphoreTakeFromISR()在中断中获取信号量vSemaphoreDelete()删除信号量根据上面的表中的函数可以发现计数信号量的释放和获取与二值信号量完全相同故在这里不多做阐述具体来看一下创建计数信号量的宏其定义如下#if(configSUPPORT_DYNAMIC_ALLOCATION1)#definexSemaphoreCreateCounting(uxMaxCount,uxInitialCount)xQueueCreateCountingSemaphore((uxMaxCount),(uxInitialCount))#endif该宏有两个参数uxMaxCount和uxInitialCount用于给创建的计数信号量指定最大计数值和初值实际创建计数信号量的函数是xQueueCreateCountingSemaphore函数创建成功会返回句柄失败会返回NULL值。还有一个使用静态方式创建计数信号量xSemaphoreCreateCountingStatic()在使用该宏创建计数信号量时需要由用户分配所需内存。接下来还是和之前一样举一个实例来加深对计数信号量使用方式的理解。这里使用了上面停车场的例子设计实例模拟停车场的运行。创建两个任务优先级都为3一个是获取信号量任务另一个是接收信号量任务。初始化5个可用的信号量开启任务调度器让两个任务独立运行。在获取信号量任务中如果按下KEY0按键模拟停车场停车操作在串口输出相应的信息。在释放信号量任务中如果按下KEY1按键模拟停车场取车操作在串口输出相应的信息。具体代码如下appTask.c/*FreeRTOS配置*//* GiveTask 任务 配置 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */staticvoidGiveTask(void*pvParameters);/* 任务函数 */staticTaskHandle_t GiveTaskHandleNULL;/* 任务句柄 */#defineGiveTask_PRIO3/* 任务优先级 */#defineGiveTask_STK_SIZE128/* 任务堆栈大小 *//* TakeTask 任务 配置 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */staticvoidTakeTask(void*pvParameters);/* 任务函数 */staticTaskHandle_t TakeTaskHandleNULL;/* 任务句柄 */#defineTakeTask_PRIO3/* 任务优先级 */#defineTakeTask_STK_SIZE128/* 任务堆栈大小 */SemaphoreHandle_t CountSem_Handle;/*二值信号量句柄*//******************************************************************************************************//** * brief appStartTask FreeRTOS入口函数 * param 无 * retval 无 */voidappStartTask(void){taskENTER_CRITICAL();/*进入临界段关中断*/CountSem_HandlexSemaphoreCreateCounting(5,5);if(CountSem_Handle!NULL){printf(计数信号量创建成功\r\n);}xTaskCreate((TaskFunction_t)TakeTask,/* 任务函数 */(constchar*)TakeTask,/* 任务名称 */(uint16_t)TakeTask_STK_SIZE,/* 任务堆栈大小 */(void*)NULL,/* 传入给任务函数的参数 */(UBaseType_t)TakeTask_PRIO,/* 任务优先级 */(TaskHandle_t*)TakeTaskHandle);/* 任务句柄 */xTaskCreate((TaskFunction_t)GiveTask,/* 任务函数 */(constchar*)GiveTask,/* 任务名称 */(uint16_t)GiveTask_STK_SIZE,/* 任务堆栈大小 */(void*)NULL,/* 传入给任务函数的参数 */(UBaseType_t)GiveTask_PRIO,/* 任务优先级 */(TaskHandle_t*)GiveTaskHandle);/* 任务句柄 */taskEXIT_CRITICAL();/*退出临界段开中断*/vTaskStartScheduler();/*开启任务调度器*/}/** * brief TakeTask * param pvParameters : 传入参数 * retval 无 */staticvoidTakeTask(void*pvParameters){/*定义一个创建信息返回值默认为pdPASS*/BaseType_t xReturnpdTRUE;while(1){if(key_scan()KEY0_PRES){xReturnxSemaphoreTake(CountSem_Handle,/*计数信号量句柄*/0);/*等待时间0*/if(pdTRUExReturn){printf(key0按键按下成功申请到停车位\r\n);}else{printf(key0按键按下车位已满\r\n);}}vTaskDelay(pdMS_TO_TICKS(20));/*延时20ms*/}}/** * brief GiveTask * param pvParameters : 传入参数 * retval 无 */staticvoidGiveTask(void*pvParameters){/*定义一个创建信息返回值默认为pdPASS*/BaseType_t xReturnpdTRUE;while(1){/*获取一个计数信号量*/if(key_scan()KEY1_PRES){/*给出计数信号量*/xReturnxSemaphoreGive(CountSem_Handle);if(pdTRUExReturn){printf(key1按键被按下释放1个车位\r\n);}else{printf(key1按键被按下但车场已空\r\n);}}vTaskDelay(pdMS_TO_TICKS(20));/*延时20ms*/}}实例串口输出结果可以看到申请了5个车位后再次按下按键申请车位显示车位已满只有释放了车位才能再次显示申请车位成功