news 2026/7/17 3:09:49

FreeRTOS学习笔记(七)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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_ALLOCATION==1)#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 GiveTaskHandle=NULL;/* 任务句柄 */#defineGiveTask_PRIO3/* 任务优先级 */#defineGiveTask_STK_SIZE128/* 任务堆栈大小 *//* TakeTask 任务 配置 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */staticvoidTakeTask(void*pvParameters);/* 任务函数 */staticTaskHandle_t TakeTaskHandle=NULL;/* 任务句柄 */#defineTakeTask_PRIO3/* 任务优先级 */#defineTakeTask_STK_SIZE128/* 任务堆栈大小 */SemaphoreHandle_t CountSem_Handle;/*二值信号量句柄*//******************************************************************************************************//** * @brief appStartTask FreeRTOS入口函数 * @param 无 * @retval 无 */voidappStartTask(void){taskENTER_CRITICAL();/*进入临界段,关中断*/CountSem_Handle=xSemaphoreCreateCounting(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 xReturn=pdTRUE;while(1){if(key_scan()==KEY0_PRES){xReturn=xSemaphoreTake(CountSem_Handle,/*计数信号量句柄*/0);/*等待时间:0*/if(pdTRUE==xReturn){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 xReturn=pdTRUE;while(1){/*获取一个计数信号量*/if(key_scan()==KEY1_PRES){/*给出计数信号量*/xReturn=xSemaphoreGive(CountSem_Handle);if(pdTRUE==xReturn){printf("key1按键被按下,释放1个车位\r\n");}else{printf("key1按键被按下,但车场已空!\r\n");}}vTaskDelay(pdMS_TO_TICKS(20));/*延时20ms*/}}

实例串口输出结果

可以看到申请了5个车位后,再次按下按键申请车位,显示车位已满;只有释放了车位,才能再次显示申请车位成功!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 3:08:52

植物大战僵尸大喷菇实战分析:夜间关卡低成本穿透战术

这次我们来看一个很有意思的话题——《植物大战僵尸》中的大喷菇。这个植物在游戏社区里争议很大,有人说它被严重低估,也有人说它被过度神化。到底大喷菇的实际表现如何?适合什么场景?值不值得重点培养?大喷菇作为夜间…

作者头像 李华
网站建设 2026/7/17 3:01:50

微软MSDN订阅漏洞解析与正版系统获取指南

1. 微软MSDN订阅漏洞事件解析最近在技术社区引起广泛讨论的一个现象是:微软MSDN订阅网站出现了一个可能的安全漏洞,使得非订阅用户也能下载原本需要付费才能获取的Windows系统镜像和Office软件。这个漏洞最早由国外论坛MDL的用户发现并分享,随…

作者头像 李华
网站建设 2026/7/17 3:01:45

职业运动员婚姻选择分析:C罗与乔治娜案例研究

这次我们来看一个关于足球明星C罗和乔治娜婚姻关系的分析话题。这个话题虽然不属于技术领域,但涉及到公众人物的私人生活分析,需要特别注意内容边界和事实依据。 从网络搜索材料来看,这个话题可能源于足球评论员卡佩罗的某个访谈或评论。作为…

作者头像 李华
网站建设 2026/7/17 3:01:03

游戏外挂技术解析:从动作模拟到封包拦截

1. 游戏外挂技术概述游戏外挂作为一种辅助程序,其核心目标是通过技术手段改变或优化游戏体验。从技术实现角度看,外挂主要分为两大类:一类是通过模拟用户操作实现自动化功能,另一类是通过修改游戏数据包或内存数据实现功能增强。这…

作者头像 李华
网站建设 2026/7/17 2:59:03

如何利用CoreCycler实现CPU超频稳定性测试:5步专业指南

如何利用CoreCycler实现CPU超频稳定性测试:5步专业指南 【免费下载链接】corecycler Script to test single core stability, e.g. for PBO & Curve Optimizer on AMD Ryzen or overclocking/undervolting on Intel processors 项目地址: https://gitcode.com…

作者头像 李华