news 2026/9/3 5:27:00

FreeRTOS学习(10)(互斥量+递归锁+事件组)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FreeRTOS学习(10)(互斥量+递归锁+事件组)

对于上一张讲过的优先级反转的的问题的时候我们引入了互斥量(也叫互斥锁)的概念,其实互斥量就是特俗的信号量。

那互斥量怎么去使用呢?

很简单,和信号量一样创建用xSemaphoreCreateMutex(),获取用xSemaphoreTake(),释放用xSemaphoreGive(),保持保护区域尽量短,FreeRTOS会自动处理优先级反转。

互斥量是一种特殊的二进制信号量但是特殊的点是二进制信号量和计数信号量获取者和释放者可以不是同一个任务,但是互斥信号量获取者和释放者必须是同一个人。

函数

创建互斥量的函数有2种:动态分配内存,静态分配内存,函数原型如下:

/* 创建一个互斥量,返回它的句柄。 * 此函数内部会分配互斥量结构体 * 返回值: 返回句柄,非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateMutex( void ); /* 创建一个互斥量,返回它的句柄。 * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针 * 返回值: 返回句柄,非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );

要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:

#define configUSE_MUTEXES 1

要注意的是,互斥量不能在ISR中使用。

各类操作函数,比如删除、give/take,跟一般是信号量是一样的。

/* * xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量 */ void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ); /* 释放 */ BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 获得 */ BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

但是在使用时也会遇到别的问题比如在嵌套或者递归函数中遇到了信号量冲突

比如任务A拿也互斥量M,但是任务A内部的嵌套函数需要信号量M但是拿不到,这样就会阻塞卡死

这是就需要用到递归锁

递归锁允许同一任务多次获取同一锁,通过内部计数机制避免自我死锁,必须使用TakeRecursive/GiveRecursive配套函数,获取N次就必须释放N次!

其他使用方式就和互斥锁一样,最重要的就是,获取N次就必须释放N次!获取N次就必须释放N次!获取N次就必须释放N次!

递归锁一般互斥量
创建xSemaphoreCreateRecursiveMutexxSemaphoreCreateMutex
获得xSemaphoreTakeRecursivexSemaphoreTake
释放xSemaphoreGiveRecursivexSemaphoreGive
/* 创建一个递归锁,返回它的句柄。* * 此函数内部会分配互斥量结构体* * 返回值: 返回句柄,非NULL表示成功* */ SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ); */ 释放 */ BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore ); */ 获得 */ BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

我们每次建立一个任务只能触发一个任务函数,那有没有什么方法可以一个任务,触发多个函数话或者信号量呢。

有的兄弟有的

这个就是事件组

使用事件组必须要引用的头文件

#include "event_groups.h"

主要特点:

  • 每个事件由事件位(bit)表示,最多32个事件(32位)

  • 支持"或"(任意事件发生)和"与"(所有事件发生)两种等待模式

  • 事件标志被设置后不会自动清除,需要手动清除

  • 线程安全,可在任务和中断中使用

事件组的使用主要分为4个主要的部分就是:创建,设置,等待和是释放

首先是创建事件组

// 创建事件组 EventGroupHandle_t xEventGroupCreate(void); // 示例 EventGroupHandle_t xEventGroup = xEventGroupCreate(); if (xEventGroup == NULL) { // 创建失败处理 }

其次设置事件组

c // 在任务中设置事件位 EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet); // 在中断中设置事件位(带中断安全版本) BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken);

然后是等待事件组

EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, // pdTRUE: 等待成功后清除事件位 const BaseType_t xWaitForAllBits, // pdTRUE: 等待所有位; pdFALSE: 等待任意位 TickType_t xTicksToWait);

最后是释放事件组也叫清除事件组

// 清除指定事件位 EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear); // 在中断中清除事件位 BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear); // 获取当前事件位值(不修改) EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup); EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);

就这四部使用起来也比较简单给大家一个一般的使用格式

// 定义事件位(建议使用1左移方式) #define BIT_0 (1 << 0) #define BIT_1 (1 << 1) #define BIT_2 (1 << 2) // 创建事件组 EventGroupHandle_t xEventGroup; void vTask1(void *pvParameters) { EventBits_t uxBits; for (;;) { // 等待BIT_0或BIT_1任意一个事件发生 uxBits = xEventGroupWaitBits( xEventGroup, // 事件组句柄 BIT_0 | BIT_1, // 等待的事件位 pdTRUE, // 成功等待后清除这些位 pdFALSE, // 任意一个事件即可 portMAX_DELAY); // 无限等待 if ((uxBits & BIT_0) != 0) { // BIT_0被设置 } if ((uxBits & BIT_1) != 0) { // BIT_1被设置 } } } void vTask2(void *pvParameters) { for (;;) { // 执行某些操作后设置事件 xEventGroupSetBits(xEventGroup, BIT_0); vTaskDelay(pdMS_TO_TICKS(1000)); } }

第一步宏定义的操作可有可无但是这样会大大提高代码的可读性

对于这个串代码进行变种就能变成任务的与操作

// 必需的头文件 #include "FreeRTOS.h" #include "task.h" #include "event_groups.h" // 可选:用于调试输出 #include <stdio.h> // 定义事件位 #define BIT_0 (1 << 0) #define BIT_1 (1 << 1) #define BIT_2 (1 << 2) // 事件组句柄 EventGroupHandle_t xEventGroup; void vSenderTask(void *pvParameters) { for (;;) { // 设置事件位 xEventGroupSetBits(xEventGroup, BIT_0); // 任务延迟 vTaskDelay(pdMS_TO_TICKS(1000)); } } void vReceiverTask(void *pvParameters) { EventBits_t uxBits; for (;;) { // 等待事件位 uxBits = xEventGroupWaitBits( xEventGroup, BIT_0 | BIT_1, pdTRUE, // 清除事件位 pdFALSE, // 等待任意位 portMAX_DELAY // 无限等待 ); if (uxBits & BIT_0) { printf("BIT_0 received\n"); } } } int main(void) { // 创建事件组 xEventGroup = xEventGroupCreate(); // 创建任务 xTaskCreate(vSenderTask, "Sender", configMINIMAL_STACK_SIZE, NULL, 1, NULL); xTaskCreate(vReceiverTask, "Receiver", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 启动调度器 vTaskStartScheduler(); return 0; }

然后也可以改成在中断中使用的样子

// 必需的头文件 #include "FreeRTOS.h" #include "task.h" #include "event_groups.h" // 可选:硬件相关头文件 #include "stm32f4xx.h" // 示例:STM32 MCU EventGroupHandle_t xEventGroup; // 中断服务程序 void TIM2_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 在中断中设置事件位 xEventGroupSetBitsFromISR( xEventGroup, BIT_0, &xHigherPriorityTaskWoken ); // 如果需要,执行上下文切换 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } }

我在配置的过程中遇到了一些错误,在这分享给大家

七、常见错误排查

错误1:未找到头文件

错误信息:fatal error: event_groups.h: No such file or directory 解决方案:确保include路径正确 在MX或IDE中正确设置include路径 例如:-I../FreeRTOS/Source/include

错误2:未启用事件组功能

//确保在FreeRTOSConfig.h中启用 #define configUSE_EVENT_GROUPS 1 // 必须为1

错误3:链接错误

错误:undefined reference to `xEventGroupCreate'

解决方案:确保链接了FreeRTOS库文件

在Makefile中添加:freertos.c 或相应的库文件

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

YimMenu游戏辅助工具全方位使用指南

YimMenu游戏辅助工具全方位使用指南 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu 想要在GTA5在线模式中…

作者头像 李华
网站建设 2026/9/3 3:40:41

语义填空模型解释:损失函数设计原理

语义填空模型解释&#xff1a;损失函数设计原理 1. 引言&#xff1a;BERT 智能语义填空服务的技术背景 近年来&#xff0c;预训练语言模型在自然语言理解任务中取得了突破性进展。其中&#xff0c;BERT&#xff08;Bidirectional Encoder Representations from Transformers&…

作者头像 李华
网站建设 2026/9/2 23:43:12

工业通信调试工具:Modbus TCP协议测试实战指南

工业通信调试工具&#xff1a;Modbus TCP协议测试实战指南 【免费下载链接】ModBusTcpTools 一个Modbus的C#开发示例&#xff0c;运用HslCommunication.dll组件库实现&#xff0c;包含了一个服务端的演示和一个客户端演示&#xff0c;客户端可用于进行Modbus测试&#xff0c;详…

作者头像 李华
网站建设 2026/9/2 23:56:48

YimMenu游戏辅助工具深度解析与实战应用

YimMenu游戏辅助工具深度解析与实战应用 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu YimMenu作为一款…

作者头像 李华
网站建设 2026/9/3 2:00:23

FST ITN-ZH镜像更新!支持FunASR实时语音识别联合调用

FST ITN-ZH镜像更新&#xff01;支持FunASR实时语音识别联合调用 你是不是也经常遇到这样的问题&#xff1a;开会时录音了一大段讲话&#xff0c;回头听要花一两个小时&#xff1b;做访谈想整理成文字稿&#xff0c;手动打字累到手酸&#xff1b;或者在开发语音助手、智能客服…

作者头像 李华