1 信号量
1.1 概念
信号量可理解为一种资源计数器,常用于以下场景:
同步任务:一个任务等待另一个任务完成某操作后再继续执行。
互斥访问:确保同一时间只有一个任务访问共享资源(如变量、外设、内存区等)。
比喻理解:停车场模型
空车位:信号量的计数值,表示可用资源数。
停车(占用车位):任务获取信号量,计数值减1。
驶离(让出车位):任务释放信号量,计数值加1。
无空车位:任务等待(阻塞)直到有资源释放。
1.2 队列与信号量对比
1.目的不同
信号量的核心是状态或资源计数。
它是一个计数器,用于管理对共享资源的访问权或标识某个事件是否发生。
信号量传递的是一种权限或通知,而不是具体的数据内容。
可以把它理解为钥匙或通行证。
队列的核心是数据传递。
它是一个用于在任务之间或任务与中断之间安全传递数据的通道。
队列传递的是具有实际意义的消息(数据包、命令、结构体等)。
可以把它理解为管道或信箱。
2.关键行为差异
数据承载
信号量:不承载任何用户数据。xSemaphoreGive 和 xSemaphoreTake 操作不涉及数据的拷贝。
队列:每次操作都涉及数据的拷贝。xQueueSend 会将数据复制到队列存储区,xQueueReceive 会从存储区复制数据出来。
发送/释放的阻塞行为
信号量:xSemaphoreGive 操作几乎从不阻塞(除非是某些特殊类型的信号量且队列已满,对于二值和计数信号量,通常不阻塞)。
队列:xQueueSend 操作可能会阻塞。如果队列已满,发送任务可以选择等待,直到队列有空间。
所有权
信号量:通常没有发送者和接收者的固定关系。任何一个任务都可以释放一个信号量,而任何另一个等待该信号量的任务都可以获取它。它是一种广播或匿名的通知机制。
队列:具有明确的生产者-消费者关系。数据从一端进入,从另一端取出。虽然多个任务可以向同一个队列发送或接收,但每个数据项只被一个消费者取走。
中断服务程序(ISR)中的使用
两者都可以在中断中使用(使用 FromISR 结尾的API函数)。
关键区别:互斥信号量由于其优先级继承机制与任务相关,因此绝对不能在中断中使用。而普通信号量和队列则可以在中断中安全地释放或发送。
2 二值信号量
2.1 概念
2.1.1 核心特性
计数值只有两种状态:0(无效)或1(有效)
基于队列实现:本质是一个队列长度为1的队列
主要用于任务同步:通知事件发生
可能引起优先级翻转:不适用于严格互斥访问
2.1.2 工作状态
初始状态(创建后):通常为0(无效)
释放后:变为1(有效)
获取后:变为0(无效)
再次释放:如果当前为0则变为1,如果当前已为1则释放失败
2.1.3 典型应用场景
中断与任务同步
任务间简单事件通知
多任务启动顺序控制
2.2 相关函数
2.2.1 xSemaphoreCreateBinary()
SemaphoreHandle_t xSemaphoreCreateBinary(void);
功能:动态创建二值信号量,FreeRTOS自动分配内存。
返回值:
成功:返回信号量句柄
失败:返回NULL(内存不足)
初始状态:创建后信号量为无效状态(计数值为0)
2.2.2 xSemaphoreCreateBinaryStatic()
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);
功能:静态创建二值信号量,用户提供内存空间。
参数:
pxSemaphoreBuffer:指向StaticSemaphore_t类型变量的指针,用于存储信号量结构
返回值:
成功:返回信号量句柄
失败:返回NULL(参数为NULL)
初始状态:创建后信号量为无效状态
2.2.3 xSemaphoreGive()
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
功能:在任务中释放二值信号量,使计数值从0变为1。
参数:
xSemaphore:信号量句柄
返回值:
pdPASS:释放成功
pdFAIL:释放失败(信号量已为1,或参数无效)
工作逻辑:
检查信号量当前状态
如果为0,则设置为1,唤醒等待任务
如果为1,则返回失败(因为二值信号量不能超过1)
2.2.4 xSemaphoreGiveFromISR()
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken);
功能:在中断服务程序中释放二值信号量。
参数:
xSemaphore:信号量句柄
pxHigherPriorityTaskWoken:用于通知是否有更高优先级任务被唤醒
初始值设为pdFALSE
如果释放操作唤醒了更高优先级的任务,会被设为pdTRUE
返回值:
pdPASS:释放成功
pdFAIL:释放失败
2.2.5 xSemaphoreTake()
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
功能:在任务中获取二值信号量,使计数值从1变为0。
参数:
xSemaphore:信号量句柄
xTicksToWait:等待超时时间
0:立即返回,不等待
portMAX_DELAY:无限等待(需配置INCLUDE_vTaskSuspend为1)
具体节拍数:等待指定时间
返回值:
pdTRUE:获取成功
pdFALSE:获取失败(超时或信号量无效)
工作逻辑:
检查信号量状态
如果为1:立即获取,计数值置0,返回成功
如果为0:根据等待时间决定是否阻塞
阻塞时:任务进入阻塞态,等待信号量释放
超时:返回失败
2.2.6 xSemaphoreTakeFromISR()
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken);
功能:在中断服务程序中获取二值信号量(使用较少)。
参数:
xSemaphore:信号量句柄
pxHigherPriorityTaskWoken:同xSemaphoreGiveFromISR
返回值:
pdTRUE:获取成功
pdFALSE:获取失败(信号量为0)
注意:中断中获取信号量通常不推荐,因为中断应该尽可能短。常见做法是在中断中释放信号量,在任务中获取。
2.3 示例实验
/* 二值信号量句柄,全局变量 */
QueueHandle_t semphore_handle;
/* FreeRTOS 演示任务入口函数 */
void freertos_demo(void)
{
/* 1. 创建二值信号量 */
semphore_handle = xSemaphoreCreateBinary();
/* 2. 检查信号量是否创建成功 */
if(semphore_handle != NULL)
{
printf("二值信号量创建成功\r\n");
}
/* 3. 创建启动任务(start_task) */
xTaskCreate((TaskFunction_t ) start_task, // 任务函数指针
(char * ) "start_task", // 任务名称
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,// 任务堆栈大小
(void * ) NULL, // 任务参数
(UBaseType_t ) START_TASK_PRIO, // 任务优先级
(TaskHandle_t * ) &start_task_handler );// 任务句柄指针
/* 4. 启动FreeRTOS调度器 */
vTaskStartScheduler();
}
/* 启动任务:负责创建其他三个任务 */
void start_task( void * pvParameters )
{
/* 进入临界区,防止任务创建过程被中断打断 */
taskENTER_CRITICAL();
/* 创建任务1:按键扫描与信号量释放任务 */
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
/* 创建任务2:信号量获取与处理任务 */
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
/* 创建任务3:备用任务(代码中未具体实现) */
xTaskCreate((TaskFunction_t ) task3,
(char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
/* 5. 删除启动任务自身(任务创建完成后不再需要) */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/* 任务1:按键扫描与信号量释放任务 */
void task1( void * pvParameters )
{
uint8_t key = 0;
BaseType_t err;
while(1)
{
/* 扫描按键(0表示不支持连续按键) */
key = key_scan(0);
/* 如果检测到KEY0被按下 */
if(key == KEY0_PRES)
{
/* 检查信号量句柄是否有效 */
if(semphore_handle != NULL)
{
/* 释放二值信号量 */
err = xSemaphoreGive(semphore_handle);
/* 检查释放结果 */
if(err == pdPASS)
{
printf("二值信号量释放成功\r\n");
}
else
{
printf("二值信号量释放失败\r\n");
}
}
}
/* 延时500个系统节拍(通常为500ms) */
vTaskDelay(500);
}
}
/* 任务2:信号量获取与处理任务 */
void task2( void * pvParameters )
{
uint32_t i = 0; // 失败计数器
BaseType_t err;
while(1)
{
/* 尝试获取二值信号量,portMAX_DELAY表示无限等待直到获取成功 */
err = xSemaphoreTake(semphore_handle, portMAX_DELAY);
/* 检查获取结果 */
if(err == pdTRUE)
{
printf("二值信号量获取成功\r\n");
}
else
{
/* 理论上不会执行到这里,因为使用了无限等待 */
printf("二值信号量获取失败,失败次数:%d\r\n", ++i);
}
}
}
**实现功能:**系统启动时创建一个二值信号量,然后建立三个任务:启动任务(负责创建其他任务)、任务1(按键扫描)和任务2(信号量等待处理)。当用户按下KEY0按键时,任务1会释放信号量;任务2则一直等待该信号量,一旦获取到就打印提示信息。
3 计数型信号量
3.1 概念
3.1.1 核心特性
计数值可大于1:表示可用资源的数量
资源池管理:适合管理有限数量的同类资源
事件计数:记录事件发生的次数
无数据传递:只计数,不传递数据
3.1.2 工作状态
创建时:指定最大计数值和初始计数值
释放(Give):计数值+1(不超过最大值)
获取(Take):计数值-1(不小于0)
计数值>0:有可用资源
计数值=0:无可用资源
3.1.3 典型应用场景
资源池管理:UART端口池、内存块池
事件计数:记录中断发生次数
生产者-消费者:缓冲区空/满状态管理
限流控制:限制同时执行的任务数量
3.2 相关函数
3.2.1 xSemaphoreCreateCounting()
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount, // 最大计数值
UBaseType_t uxInitialCount // 初始计数值
);
功能:动态创建计数型信号量,FreeRTOS自动分配内存。
参数:
uxMaxCount:信号量能达到的最大值(资源总数)
uxInitialCount:创建时的初始计数值(初始可用资源数)
返回值:
成功:返回信号量句柄
失败:返回NULL(内存不足)
关键特性:
计数值范围:0 ≤ 计数值 ≤ uxMaxCount
初始值可以设为0(表示无资源)或任意≤最大值的数
如果初始值=最大值,表示所有资源初始可用
3.2.2 xSemaphoreCreateCountingStatic()
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
UBaseType_t uxMaxCount, // 最大计数值
UBaseType_t uxInitialCount, // 初始计数值
StaticSemaphore_t *pxSemaphoreBuffer // 静态内存缓冲区
);
功能:静态创建计数型信号量,使用用户提供的静态内存。
参数:
uxMaxCount:最大计数值
uxInitialCount:初始计数值
pxSemaphoreBuffer:指向StaticSemaphore_t类型变量的指针
返回值:
成功:返回信号量句柄
失败:返回NULL(参数无效)
3.2.3 uxSemaphoreGetCount()
UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore);
功能:获取计数型信号量当前的计数值。
参数:
xSemaphore:信号量句柄
返回值:
当前信号量的计数值(UBaseType_t类型)
注意:
这是一个宏定义,实际上调用的是uxQueueMessagesWaiting()
返回值表示当前可用的资源数量
可用于调试、监控资源使用情况
3.3 示例实验
/* 计数型信号量句柄,全局变量 */
QueueHandle_t Count_semphore_handle;
/* FreeRTOS 演示任务入口函数 */
void freertos_demo(void)
{
/* 1. 创建计数型信号量:最大计数值100,初始计数值0 */
Count_semphore_handle = xSemaphoreCreateCounting(100, 0);
/* 2. 检查信号量是否创建成功 */
if(Count_semphore_handle != NULL)
{
printf("信号量创建成功\r\n");
}
/* 3. 创建启动任务(start_task) */
xTaskCreate((TaskFunction_t ) start_task, // 任务函数指针
(char * ) "start_task", // 任务名称
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,// 任务堆栈大小
(void * ) NULL, // 任务参数
(UBaseType_t ) START_TASK_PRIO, // 任务优先级
(TaskHandle_t * ) &start_task_handler );// 任务句柄指针
/* 4. 启动FreeRTOS调度器 */
vTaskStartScheduler();
}
/* 启动任务:负责创建其他三个任务 */
void start_task( void * pvParameters )
{
/* 进入临界区,防止任务创建过程被中断打断 */
taskENTER_CRITICAL();
/* 创建任务1:按键扫描与信号量释放任务 */
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
/* 创建任务2:信号量获取与处理任务 */
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
/* 创建任务3:备用任务(代码中未具体实现) */
xTaskCreate((TaskFunction_t ) task3,
(char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
/* 5. 删除启动任务自身(任务创建完成后不再需要) */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/* 任务1:按键扫描与信号量释放任务 */
void task1( void * pvParameters )
{
uint8_t key = 0;
BaseType_t err;
while(1)
{
/* 扫描按键(0表示不支持连续按键) */
key = key_scan(0);
/* 如果检测到KEY0被按下 */
if(key == KEY0_PRES)
{
/* 检查信号量句柄是否有效 */
if(Count_semphore_handle != NULL)
{
/* 释放计数型信号量(计数值+1) */
err = xSemaphoreGive(Count_semphore_handle);
/* 检查释放结果 */
if(err == pdPASS)
{
printf("二值信号量释放成功\r\n"); // 注意:这里打印有误,应为"计数型信号量"
}
else
{
printf("二值信号量释放失败\r\n"); // 注意:这里打印有误,应为"计数型信号量"
}
}
}
/* 延时500个系统节拍(通常为500ms) */
vTaskDelay(500);
}
}
/* 任务2:信号量获取与处理任务 */
void task2( void * pvParameters )
{
BaseType_t err;
while(1)
{
/* 尝试获取计数型信号量,portMAX_DELAY表示无限等待直到获取成功 */
err = xSemaphoreTake(Count_semphore_handle, portMAX_DELAY);
/* 检查获取结果 */
if(err == pdTRUE)
{
/* 获取成功后,打印当前信号量计数值 */
printf("信号量获取成功%d\r\n", (int)uxSemaphoreGetCount(Count_semphore_handle));
}
}
}
**实现功能:**系统启动时创建了一个最大计数值为100、初始值为0的计数型信号量,随后建立三个任务:启动任务(负责创建其他任务后自删除)、任务1(按键扫描)和任务2(信号量获取与计数显示)。当用户按下KEY0按键时,任务1会释放信号量(计数值+1);任务2则一直等待获取信号量,一旦成功获取(计数值-1),就打印当前信号量的剩余计数值。这样系统实现了对按键事件次数的计数和显示,每次按键都会增加计数值,任务2每处理一个事件就减少计数值并显示当前未处理的事件数。
4 优先级翻转
4.1 概念
4.1.1 定义
优先级翻转(Priority Inversion)是实时操作系统中一个经典问题,指高优先级任务被低优先级任务阻塞,导致低优先级任务或中等优先级任务先于高优先级任务执行的现象。
4.1.2 核心矛盾
在实时系统中,任务通常按优先级进行调度,但当多个任务共享资源(如信号量保护的临界区)时,就可能出现优先级翻转,破坏系统的实时性保证。
4.1.3 问题严重性
在硬实时系统中,优先级翻转可能导致:
任务截止时间错过(Deadline Miss)
系统响应时间不可预测
可能导致系统故障或安全风险
4.2 示例实验
/* 二值信号量句柄,全局变量,用于任务间同步 */
QueueHandle_t semphore_handle;
/* FreeRTOS 演示任务入口函数 */
void freertos_demo(void)
{
/* 1. 创建二值信号量 */
semphore_handle = xSemaphoreCreateBinary();
/* 2. 检查信号量是否创建成功 */
if(semphore_handle != NULL)
{
printf("二值信号量创建成功\r\n");
}
/* 3. 立即释放一次信号量,使得初始状态为"有效",以便后续任务可以获取 */
xSemaphoreGive(semphore_handle);
/* 4. 创建启动任务(start_task) */
xTaskCreate((TaskFunction_t ) start_task, // 任务函数指针
(char * ) "start_task", // 任务名称
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,// 任务堆栈大小
(void * ) NULL, // 任务参数
(UBaseType_t ) START_TASK_PRIO, // 任务优先级
(TaskHandle_t * ) &start_task_handler );// 任务句柄指针
/* 5. 启动FreeRTOS调度器 */
vTaskStartScheduler();
}
/* 启动任务:负责创建三个不同优先级的任务 */
void start_task( void * pvParameters )
{
/* 进入临界区,防止任务创建过程被中断打断 */
taskENTER_CRITICAL();
/* 创建低优先级任务(low_task) */
xTaskCreate((TaskFunction_t ) low_task,
(char * ) "low_task",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &low_handler );
/* 创建中优先级任务(middle_task) */
xTaskCreate((TaskFunction_t ) middle_task,
(char * ) "middle_task",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &middle_handler );
/* 创建高优先级任务(high_task) */
xTaskCreate((TaskFunction_t ) high_task,
(char * ) "task3", // 注意:任务名称为"task3",应为"high_task"
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &high_handler );
/* 6. 删除启动任务自身(任务创建完成后不再需要) */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/* 低优先级任务:获取信号量后长时间占用,模拟资源持有 */
void low_task( void * pvParameters )
{
while(1)
{
printf("低优先级任务获取\r\n");
/* 获取二值信号量(无限等待) */
xSemaphoreTake(semphore_handle, portMAX_DELAY);
printf("低优先级任务正在执行\r\n");
/* 模拟长时间占用资源(3000ms) */
delay_ms(3000);
printf("低优先级任务释放信号量\r\n");
/* 释放信号量 */
xSemaphoreGive(semphore_handle);
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
/* 中优先级任务:不依赖信号量,周期性执行 */
void middle_task (void * pvParameters )
{
while(1)
{
printf("中优先级任务获取\r\n");
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
/* 高优先级任务:获取信号量后短时间占用 */
void high_task( void * pvParameters )
{
while(1)
{
printf("高优先级任务获取\r\n");
/* 获取二值信号量(无限等待) */
xSemaphoreTake(semphore_handle, portMAX_DELAY);
printf("高优先级任务正在执行\r\n");
/* 模拟短时间占用资源(1000ms) */
delay_ms(1000);
printf("高优先级任务释放信号量\r\n");
/* 释放信号量 */
xSemaphoreGive(semphore_handle);
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
**实现功能:**系统创建了三个不同优先级的任务(低、中、高),它们共享一个二值信号量(初始化为有效状态)。低优先级任务首先获取信号量并长时间占用(3秒),在此期间,如果高优先级任务就绪并尝试获取同一个信号量,它将被阻塞;而此时中优先级任务(不依赖信号量)如果就绪,将抢占CPU并运行,导致高优先级任务无法及时执行,从而形成优先级翻转------即中优先级任务先于高优先级任务执行,违背了任务的优先级设定。
5 互斥信号量
5.1 概念
互斥信号量是一种特殊的同步机制,其核心是具有优先级继承的二值信号量。它专为多任务环境中的互斥访问而设计,可以有效减轻优先级翻转问题,但不适合用于中断环境。
核心特性:
二值状态:与二值信号量类似,只有0(被占用)和1(可用)两种状态
优先级继承:当高优先级任务等待低优先级任务释放信号量时,低优先级任务的优先级会临时提升到与高优先级任务相同
任务专用:只能在任务中使用,不能在中断服务函数中使用
自动初始化:创建后信号量自动处于释放状态(可用)
5.2 优先级继承机制
当一个互斥信号量被低优先级任务持有时,如果高优先级任务尝试获取这个信号量:
高优先级任务被阻塞
低优先级任务的优先级临时提升到与高优先级任务相同
低优先级任务快速执行并释放信号量
低优先级任务恢复原优先级
高优先级任务获取信号量并执行
5.3 相关函数
5.3.1 动态创建函数
SemaphoreHandle_t xSemaphoreCreateMutex(void);
功能:动态分配内存创建互斥信号量
返回值:成功返回信号量句柄,失败返回NULL
特点:创建后信号量自动处于释放状态(可用)
5.3.2 静态创建函数
SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxMutexBuffer);
功能:使用用户提供的静态内存创建互斥信号量
参数:pxMutexBuffer - 指向StaticSemaphore_t类型变量的指针
返回值:成功返回信号量句柄,失败返回NULL
5.4 为什么互斥信号量不能用于中断
无任务优先级:中断没有任务优先级,而互斥信号量的优先级继承机制依赖于任务优先级
中断不能阻塞:中断服务函数必须快速执行,不能设置阻塞时间等待信号量
5.5 示例实验
/* 互斥信号量句柄,全局变量,用于任务间互斥访问共享资源 */
QueueHandle_t mutex_semphore_handle;
/* FreeRTOS 演示任务入口函数 */
void freertos_demo(void)
{
/* 1. 创建互斥信号量 */
mutex_semphore_handle = xSemaphoreCreateMutex();
/* 2. 检查信号量是否创建成功 */
if(mutex_semphore_handle != NULL)
{
printf("二值信号量创建成功\r\n"); // 注意:这里打印有误,应为"互斥信号量"
}
/* 3. 释放信号量(实际上互斥信号量创建后已自动释放,此操作多余但无害) */
xSemaphoreGive(mutex_semphore_handle);
/* 4. 创建启动任务(start_task) */
xTaskCreate((TaskFunction_t ) start_task, // 任务函数指针
(char * ) "start_task", // 任务名称
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,// 任务堆栈大小
(void * ) NULL, // 任务参数
(UBaseType_t ) START_TASK_PRIO, // 任务优先级
(TaskHandle_t * ) &start_task_handler );// 任务句柄指针
/* 5. 启动FreeRTOS调度器 */
vTaskStartScheduler();
}
/* 启动任务:负责创建三个不同优先级的任务 */
void start_task( void * pvParameters )
{
/* 进入临界区,防止任务创建过程被中断打断 */
taskENTER_CRITICAL();
/* 创建低优先级任务(low_task) */
xTaskCreate((TaskFunction_t ) low_task,
(char * ) "low_task",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &low_handler );
/* 创建中优先级任务(middle_task) */
xTaskCreate((TaskFunction_t ) middle_task,
(char * ) "middle_task",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &middle_handler );
/* 创建高优先级任务(high_task) */
xTaskCreate((TaskFunction_t ) high_task,
(char * ) "task3", // 注意:任务名称为"task3",应为"high_task"
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &high_handler );
/* 6. 删除启动任务自身(任务创建完成后不再需要) */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/* 低优先级任务:获取互斥信号量后长时间占用,模拟资源持有 */
void low_task( void * pvParameters )
{
while(1)
{
printf("低优先级任务获取\r\n");
/* 获取互斥信号量(无限等待) */
xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
printf("低优先级任务正在执行\r\n");
/* 模拟长时间占用资源(3000ms) */
delay_ms(3000);
printf("低优先级任务释放信号量\r\n");
/* 释放互斥信号量 */
xSemaphoreGive(mutex_semphore_handle);
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
/* 中优先级任务:不依赖信号量,周期性执行 */
void middle_task (void * pvParameters )
{
while(1)
{
printf("中优先级任务获取\r\n");
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
/* 高优先级任务:获取互斥信号量后短时间占用 */
void high_task( void * pvParameters )
{
while(1)
{
printf("高优先级任务获取\r\n");
/* 获取互斥信号量(无限等待) */
xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
printf("高优先级任务正在执行\r\n");
/* 模拟短时间占用资源(1000ms) */
delay_ms(1000);
printf("高优先级任务释放信号量\r\n");
/* 释放互斥信号量 */
xSemaphoreGive(mutex_semphore_handle);
/* 延时1000个系统节拍 */
vTaskDelay(1000);
}
}
实现功能: 系统创建了三个不同优先级的任务(低、中、高),它们共享一个互斥信号量。当低优先级任务获取互斥信号量并长时间占用(3秒)时,如果高优先级任务尝试获取同一个信号量,它将被阻塞,但此时互斥信号量的优先级继承机制会启动:低优先级任务的优先级会被临时提升到与高优先级任务相同,从而防止中优先级任务抢占CPU。这样,低优先级任务可以快速完成并释放信号量,然后高优先级任务立即获得执行权。