FreeRTOS常用API函数

一、任务创建与删除

1. xTaskCreate() 函数

作用:动态创建一个新任务,并分配所需的堆栈和任务控制块(TCB)内存。

函数原型
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,         // 任务函数指针
    const char * const pcName,         // 任务名称(字符串标识)
    configSTACK_DEPTH_TYPE usStackDepth, // 堆栈大小(以字为单位)
    void *pvParameters,                // 传递给任务的参数
    UBaseType_t uxPriority,            // 任务优先级(0为最低,数值越大优先级越高)
    TaskHandle_t *pvCreatedTask        // 任务句柄(用于后续引用该任务)
);
参数详解
  1. pvTaskCode
    任务函数的入口地址。任务必须是一个无限循环或显式调用vTaskDelete()终止的函数。
    示例任务函数形式:

    void vTaskFunction(void *pvParameters) {
        while(1) {
            // 任务逻辑
        }
    }
    
  2. pcName
    任务的字符串名称,用于调试和追踪。名称长度由configMAX_TASK_NAME_LEN定义(默认为16字符)。

  3. usStackDepth
    任务的堆栈深度,单位为字(word)。例如,若堆栈需要1024字节,且处理器为32位(4字节/字),则设为256。需根据任务需求合理分配,避免溢出或浪费内存。

  4. pvParameters
    传递给任务的参数指针,可以是任意类型的数据结构或NULL

  5. uxPriority
    任务优先级,范围从0(最低)到configMAX_PRIORITIES-1。优先级高的任务抢占低优先级任务。

  6. pvCreatedTask
    输出参数,返回任务句柄。可用于后续操作(如删除任务、修改优先级等)。若无需使用,可设为NULL

返回值
  • pdPASS:任务创建成功。
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:内存不足,无法分配堆栈或TCB。
示例
TaskHandle_t xTaskHandle = NULL;

void vTaskExample(void *pvParameters) {
    while(1) {
        printf("Task is running.\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

// 创建任务
xTaskCreate(vTaskExample, "ExampleTask", 256, NULL, 1, &xTaskHandle);

2. vTaskDelete() 函数

作用:删除一个任务,释放其占用的内存(TCB和堆栈由空闲任务回收)。

函数原型
void vTaskDelete(TaskHandle_t xTaskToDelete);  // 任务句柄或NULL
参数详解
  • xTaskToDelete
    要删除的任务句柄。若传递NULL,则删除当前正在运行的任务自身。
关键行为
  1. 任务被标记为删除,立即退出运行状态。
  2. 任务的TCB和堆栈由空闲任务(Idle Task)自动回收(需确保空闲任务有执行机会)。
  3. 若任务持有动态分配的内存、信号量等资源,需在删除前手动释放,否则会导致内存泄漏。
示例
void vSelfDeletingTask(void *pvParameters) {
    printf("Task will delete itself after 5 runs.\n");
    for(int i=0; i<5; i++) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);  // 删除自身
}

3. 使用注意事项

  1. 资源管理
    任务删除前需释放其占用的资源(如内存、文件句柄、信号量)。FreeRTOS不会自动处理这些资源。

  2. 空闲任务的角色
    空闲任务负责清理被删除任务的TCB和堆栈。若使用vTaskDelete(),需确保空闲任务能运行(即不在其他任务中无限阻塞)。

  3. 任务自删除
    任务可通过vTaskDelete(NULL)删除自身,但需注意不要在删除后访问局部变量或执行后续代码。

  4. 动态分配与静态分配
    xTaskCreate()动态分配内存,而xTaskCreateStatic()使用预分配内存。动态分配需启用configSUPPORT_DYNAMIC_ALLOCATION=1(默认启用)。


4. 完整示例

#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t xTaskHandle = NULL;

void vExampleTask(void *pvParameters) {
    char *taskName = (char *)pvParameters;
    for(int i=0; i<3; i++) {
        printf("%s: Run count %d\n", taskName, i+1);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(xTaskHandle);  // 删除自身
}

int main() {
    xTaskCreate(vExampleTask, "Task1", 256, "Task-A", 1, &xTaskHandle);
    vTaskStartScheduler();  // 启动调度器
    return 0;
}

5. 常见问题

  • 堆栈溢出:堆栈大小不足会导致usStackDepth溢出,可使用FreeRTOS的堆栈溢出检测功能(configCHECK_FOR_STACK_OVERFLOW)。
  • 优先级反转:高优先级任务被低优先级任务阻塞,需使用互斥锁的优先级继承机制。
  • 内存不足:动态创建任务失败时检查堆大小(configTOTAL_HEAP_SIZE)。

通过合理使用xTaskCreate()vTaskDelete(),可以高效管理FreeRTOS中的任务生命周期,确保系统资源的最优利用。

二、任务挂起与恢复

1. vTaskSuspend()

功能

挂起指定任务,使其脱离调度器的管理,任务进入挂起状态(Suspended),不再参与调度。

函数原型
void vTaskSuspend(TaskHandle_t xTaskToSuspend);
  • 参数xTaskToSuspend 为目标任务的句柄。若为NULL,则挂起当前任务。
  • 返回值:无。
关键点
  • 状态变化:任务被挂起后,无论优先级高低,都不会被调度执行。
  • 资源释放:挂起时不会自动释放已持有的资源(如信号量、互斥锁),需手动处理。
  • 自身挂起:若任务挂起自己,必须由其他任务或中断恢复它,否则会永久阻塞。
  • 适用场景
    • 调试时临时冻结任务。
    • 暂停非关键任务以释放CPU资源。
    • 等待外部事件前的临时挂起。
示例
vTaskSuspend(xTaskHandle); // 挂起指定任务
vTaskSuspend(NULL);         // 挂起当前任务(自身)

2. vTaskResume()

功能

恢复被挂起的任务,使其重新进入就绪状态(Ready),参与调度。

函数原型
void vTaskResume(TaskHandle_t xTaskToResume);
  • 参数xTaskToResume 为目标任务的句柄。
  • 返回值:无。
关键点
  • 恢复条件:仅对处于挂起状态的任务有效,若任务未被挂起则无操作。
  • 调度触发:若恢复的任务优先级高于当前任务,会触发一次上下文切换。
  • 调用限制不可在中断中使用,需通过xTaskResumeFromISR()处理中断恢复。
  • 无嵌套计数:无论任务被挂起多少次,一次vTaskResume()即可恢复。
示例
vTaskResume(xTaskHandle); // 恢复指定任务

3. xTaskResumeFromISR()

功能

在**中断服务程序(ISR)**中恢复被挂起的任务,并返回是否需要手动触发上下文切换。

函数原型
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
  • 参数xTaskToResume 为目标任务的句柄。
  • 返回值
    • pdTRUE:恢复的任务优先级高于当前任务,需手动触发上下文切换。
    • pdFALSE:无需立即切换。
关键点
  • 中断安全:专为ISR设计,避免在中断中调用非ISR安全函数。
  • 手动切换:若返回pdTRUE,应在退出中断前调用portYIELD_FROM_ISR()
  • 优先级抢占:恢复高优先级任务可能导致当前任务被抢占,需评估实时性影响。
示例
BaseType_t xYieldRequired = xTaskResumeFromISR(xTaskHandle);
if (xYieldRequired == pdTRUE) {
    portYIELD_FROM_ISR(); // 手动触发上下文切换
}

4. vTaskSuspendAll()

功能

挂起调度器,阻止任务切换,但不停止当前任务的执行。所有任务保持当前状态,仅暂停调度器的决策。

函数原型
void vTaskSuspendAll(void);
  • 参数:无。
  • 返回值:无。
关键点
  • 调度器挂起
    • 任务切换被禁止,但当前任务继续执行。
    • 中断仍可触发,但ISR结束后不会切换到其他任务。
  • 嵌套支持:允许多次调用vTaskSuspendAll(),需对应次数的xTaskResumeAll()恢复。
  • 适用场景
    • 保护共享资源的原子操作(如读写全局变量)。
    • 执行时间敏感的代码段,避免任务切换干扰。
示例
vTaskSuspendAll(); // 挂起调度器
// 执行原子操作(如读写共享数据)
xTaskResumeAll();  // 恢复调度器

5. xTaskResumeAll()

功能

恢复被挂起的调度器,重新允许任务切换,并返回是否有挂起的上下文切换请求。

函数原型
BaseType_t xTaskResumeAll(void);
  • 参数:无。
  • 返回值
    • pdTRUE:恢复调度器后需要触发上下文切换。
    • pdFALSE:无需切换。
关键点
  • 嵌套处理:需与vTaskSuspendAll()成对调用,直到所有挂起请求被解除。
  • 上下文切换:恢复后可能立即发生任务抢占(由优先级决定)。
  • 资源管理:恢复前需确保共享资源操作已完成。
示例
vTaskSuspendAll();
// 临界区操作
BaseType_t xSwitchRequired = xTaskResumeAll();
if (xSwitchRequired == pdTRUE) {
    taskYIELD(); // 主动让出CPU(可选)
}

对比与总结

函数作用对象调用上下文关键特性适用场景
vTaskSuspend()单个任务任务挂起任务,需手动恢复调试、临时暂停任务
vTaskResume()单个任务任务恢复任务,不可在中断使用任务间协作
xTaskResumeFromISR()单个任务中断中断中恢复任务,需处理上下文切换ISR触发任务恢复
vTaskSuspendAll()调度器(全部任务)任务/中断挂起调度器,允许嵌套调用原子操作、保护共享资源
xTaskResumeAll()调度器(全部任务)任务/中断恢复调度器,返回是否需要切换结束临界区操作

使用注意事项

  1. 优先级与抢占

    • 恢复高优先级任务可能立即抢占当前任务,需确保代码逻辑安全。
    • 在临界区中挂起调度器时,需避免长时间阻塞,否则影响系统实时性。
  2. 中断安全

    • xTaskResumeFromISR()必须在中断中使用,且需处理返回值。
    • 在中断中避免调用vTaskSuspendAll(),可能导致不可预测的行为。
  3. 资源管理

    • 挂起任务前需释放其占用的资源(如信号量、内存),防止死锁。
    • 使用vTaskSuspendAll()时,确保临界区操作快速完成,避免中断延迟。
  4. 调度器挂起与任务挂起

    • vTaskSuspendAll()挂起的是调度器(任务仍可运行),而vTaskSuspend()挂起的是单个任务。
    • 调度器挂起期间,ISR仍可运行,但任务切换被禁止。

典型应用场景

  1. 共享资源保护

    vTaskSuspendAll(); // 禁止任务切换
    // 安全读写共享数据
    xTaskResumeAll();  // 恢复调度器
    
  2. 任务同步

    void vTaskA(void *pvParam) {
        // 等待事件
        vTaskSuspend(NULL); // 挂起自身
    }
    
    void vTaskB(void *pvParam) {
        // 触发事件后恢复TaskA
        vTaskResume(xTaskAHandle);
    }
    
  3. 中断恢复任务

    void vISR_Handler(void) {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        xHigherPriorityTaskWoken = xTaskResumeFromISR(xTaskHandle);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
    

三、中断的应用

1. 中断优先级与 FreeRTOS 的交互

FreeRTOS 需要管理中断服务函数的优先级,因为:

  • 临界区保护:当 FreeRTOS 进入临界区(如任务切换、资源分配)时,需暂时屏蔽某些中断,防止数据竞争。
  • API 安全性:若中断服务函数调用了 FreeRTOS 的 API(如 xQueueSendFromISR),必须确保这些操作不会被更高优先级的中断破坏。

2. configMAX_SYSCALL_INTERRUPT_PRIORITY 的作用

这是 FreeRTOS 配置文件中定义的阈值,用于划分中断优先级的“可管理范围”:

  • 低于等于此阈值的中断
    • 可被 FreeRTOS 屏蔽(通过 BASEPRI 寄存器)。
    • 允许调用 FreeRTOS API(如队列操作、信号量等)。
    • 称为“受管理的中断”。
  • 高于此阈值的中断
    • 不会被 FreeRTOS 屏蔽,具有最高实时性。
    • 禁止调用 FreeRTOS API(可能导致系统不一致)。
    • 称为“非受管理中断”,通常用于硬件紧急事件(如看门狗)。

3. 配置示例(以 ARM Cortex-M 为例)

假设优先级数值越小优先级越高(ARM Cortex-M 的典型配置):

// FreeRTOSConfig.h
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 5  // 阈值设为优先级 5
  • 优先级 0~4:非受管理中断,不可调用 FreeRTOS API,响应最快。
  • 优先级 5~15:受管理中断,可调用 FreeRTOS API,但可能被临界区屏蔽。

4. 常见问题与注意事项

  • 优先级数值方向:不同处理器可能定义相反(例如数值越大优先级越高),需根据硬件手册调整配置。
  • API 调用限制:若高优先级中断误用 FreeRTOS API,会导致未定义行为(如系统崩溃)。
  • 实时性权衡:非受管理中断适合实时性要求极高的场景(如电机控制),但需自行处理所有逻辑。

5. 实际应用场景

  • 受管理中断:处理传感器数据时,通过队列(xQueueSendFromISR)将数据发送给任务。
  • 非受管理中断:处理硬件故障(如电源掉电),立即执行关键操作,无需与 FreeRTOS 交互。

四、临界段的应用

1、普通代码调用临界段

**进入临界段 taskENTER_CRITICAL() **
退出临界段 taskEXIT_CRITICAL()
用途
  • 任务上下文:在普通任务代码中使用,保护共享资源的访问。
  • 作用:通过提升中断屏蔽级别(如设置BASEPRI寄存器)或禁用中断,确保当前任务独占执行。
工作机制
  • taskENTER_CRITICAL()
    保存当前中断状态(如中断屏蔽寄存器值),然后屏蔽特定优先级以下的中断(不一定是所有中断,取决于配置)。支持嵌套调用,内部通过计数器跟踪嵌套层级。
  • taskEXIT_CRITICAL()
    恢复之前保存的中断状态。仅当嵌套计数器归零时,才会实际恢复中断屏蔽级别。
示例
taskENTER_CRITICAL(); // 进入临界区
// 操作共享资源(如全局变量、外设)
taskEXIT_CRITICAL();  // 退出临界区
注意
  • 短时使用:临界区应尽量简短,避免影响实时性。
  • 不可在ISR中使用:否则可能导致中断状态错误恢复。

2、中断中调用临界段

**进入临界段 taskENTER_CRITICAL_FROM_ISR() **
退出临界段 taskEXIT_CRITICAL_FROM_ISR()
用途
  • 中断上下文:在中断服务例程(ISR)中使用,保护共享资源。
  • 作用:调整中断屏蔽级别,允许在ISR中安全访问共享资源。
工作机制
  • taskENTER_CRITICAL_FROM_ISR()
    返回当前中断屏蔽状态(如UBaseType_t类型),并临时提升中断屏蔽级别。
  • taskEXIT_CRITICAL_FROM_ISR(x)
    接受之前保存的中断状态(参数x),恢复中断屏蔽级别。
示例
void vInterruptHandler(void) {
    UBaseType_t uxSavedStatus;
    uxSavedStatus = taskENTER_CRITICAL_FROM_ISR(); // 进入临界区
    // 操作共享资源
    taskEXIT_CRITICAL_FROM_ISR(uxSavedStatus);     // 退出临界区
}
注意
  • 参数传递:必须保存taskENTER_CRITICAL_FROM_ISR()的返回值,并在退出时传递。
  • 不可在任务中使用:否则可能导致中断状态错误。

关键区别

特性任务版本(无_FROM_ISRISR版本(带_FROM_ISR
使用上下文普通任务中断服务例程(ISR)
状态保存方式自动管理(内部计数器)需手动保存返回值
嵌套支持支持支持
中断恢复机制自动恢复至进入前的状态需显式传递保存的状态

最佳实践

  1. 区分上下文

    • 任务中使用taskENTER_CRITICAL()/taskEXIT_CRITICAL()
    • ISR中使用_FROM_ISR版本。
  2. 避免长时间阻塞
    临界区内不要执行耗时操作(如等待信号量)。

  3. 嵌套处理
    确保ENTEREXIT调用次数匹配,防止状态未恢复。

  4. 替代方案
    对于复杂资源保护,优先考虑信号量(Semaphore)或互斥量(Mutex)。


底层实现(简析)

  • BASEPRI寄存器
    FreeRTOS通过设置ARM Cortex-M的BASEPRI寄存器,屏蔽低于某优先级的中断,而非完全关中断,确保高优先级中断(如系统节拍)仍可响应。
  • 可移植性
    具体实现依赖硬件,不同处理器可能通过不同方式屏蔽中断。

五、FreeRTOS 任务管理函数全解析

1. 优先级管理

动态调整任务优先级是实时系统的核心能力,以下函数帮助实现灵活调度。

1.1 获取任务优先级

  • 函数: uxTaskPriorityGet(TaskHandle_t xTask)
  • 参数:
    • xTask: 目标句柄(NULL 表示当前任务)
  • 返回值: 当前优先级数值(0 为最低)
  • 典型场景: 在任务协作时保存原优先级以便恢复
UBaseType_t original = uxTaskPriorityGet(xWorkerTask); 

1.2 设置任务优先级

  • 函数: vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)
  • 参数:
    • xTask: 目标句柄(NULL 为当前任务)
    • uxNewPriority: 新优先级(必须小于 configMAX_PRIORITIES
  • 注意: 避免在中断中频繁调用
// 紧急任务优先处理
vTaskPrioritySet(xCriticalTask, configMAX_PRIORITIES - 1);

2. 任务信息统计

监控系统任务状态与资源使用情况,用于系统优化与调试。

2.1 获取任务总数

  • 函数: uxTaskGetNumberOfTasks()
  • 返回值: 当前存在的任务总数(包含所有状态)
  • 用途: 内存分配前的数量检查
UBaseType_t taskCount = uxTaskGetNumberOfTasks();

2.2 系统状态快照

  • 函数: uxTaskGetSystemState(TaskStatus_t *arr, UBaseType_t size, uint32_t *totalTime)
  • 关键参数:
    • arr: 接收状态的结构体数组
    • size: 数组容量(需≥实际任务数)
    • totalTime: 总运行时间指针(需启用时间统计)
  • 输出字段: 任务名、状态、优先级、堆栈使用等
TaskStatus_t stats[5];
uint32_t time;
UBaseType_t activeNum = uxTaskGetSystemState(stats, 5, &time);

2.3 单任务详细信息

  • 函数: vTaskGetInfo(TaskHandle_t xTask, TaskStatus_t *info, BaseType_t getStack, eTaskState getState)
  • 参数控制:
    • getStack: pdTRUE 计算剩余堆栈
    • getState: eInvalid 跳过状态获取
  • 示例:
TaskStatus_t detail;
vTaskGetInfo(xTask, &detail, pdTRUE, eInvalid);

3. 任务句柄操作

通过名称或运行时状态获取任务引用句柄。

3.1 获取当前任务句柄

  • 函数: xTaskGetCurrentTaskHandle()
  • 典型应用: 在匿名函数中自引用
TaskHandle_t myself = xTaskGetCurrentTaskHandle();

3.2 按名称查找句柄

  • 函数: xTaskGetHandle(const char *name)
  • 注意: 名称需唯一且大小写敏感
TaskHandle_t handle = xTaskGetHandle("CAN_Process");
if(handle == NULL) {/* 错误处理 */}

4. 堆栈空间监控

预防堆栈溢出导致的内存损坏问题。

高水位线检测

  • 函数: uxTaskGetStackHighWaterMark(TaskHandle_t xTask)
  • 返回值: 历史最小剩余堆栈(单位:字)
  • 安全阈值: 建议保持至少 20% 余量
// 监控自身堆栈
UBaseType_t waterMark = uxTaskGetStackHighWaterMark(NULL);
if(waterMark < 50) vTaskResume(xCleanupTask);

5. 任务状态查询

精确诊断任务所处的调度状态。

状态枚举获取

  • 函数: eTaskGetState(TaskHandle_t xTask)
  • 返回状态:
    • eReady/eBlocked/eSuspended/eDeleted
  • 典型应用: 调试死锁时检查任务阻塞状态
if(eTaskGetState(xTask) == eBlocked) {
   // 检查等待的资源
}

6. 调试与性能分析

生成可视化报告辅助系统调优。

6.1 文本化任务列表

  • 函数: vTaskList(char *buffer)

  • 依赖配置:

    #define configUSE_TRACE_FACILITY 1
    #define configUSE_STATS_FORMATTING_FUNCTIONS 1
    
  • 输出示例:

    Task    State   Prio    Stack   Num
    IDLE    R       0       54      1
    UART_Tx B       3       120     2
    

6.2 运行时统计

  • 函数: vTaskGetRunTimeStats(char *buffer)

  • 硬件依赖: 需实现高精度计时器

  • 配置步骤:

    // 在port层配置计时器
    portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
    
  • 输出示例:

    Task      Runtime     %
    Logger    12000      15%
    Sensor    65000      81%
    

关键实践指南

优先级调整策略

  • 紧急响应: 临时提升中断服务任务的优先级
  • 优先级反转处理: 使用互斥量的优先级继承机制替代手动调整

堆栈优化技巧

  1. 通过高水位线计算实际需求:

    // 创建时分配的堆栈大小
    #define TASK_STACK_SIZE 128
    // 实际使用的峰值
    size_t used = TASK_STACK_SIZE - uxTaskGetStackHighWaterMark(NULL);
    
  2. 为中断嵌套和函数调用留出余量

统计函数最佳实践

  • 缓冲区管理: 根据任务数量动态分配内存

    UBaseType_t num = uxTaskGetNumberOfTasks();
    TaskStatus_t *buf = pvPortMalloc(num * sizeof(TaskStatus_t));
    
  • 定时输出: 在监控任务中周期性打印统计信息

    void vStatTask(void *param) {
        while(1) {
            vTaskList(debugBuffer);
            sendToHost(debugBuffer);
            vTaskDelay(pdMS_TO_TICKS(5000));
        }
    }
    

典型应用场景

场景 1:紧急任务处理

void vEmergencyHandler() {
    // 保存原优先级
    UBaseType_t normalPrio = uxTaskPriorityGet(NULL);
    
    // 提升至最高优先级
    vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
    
    // 执行关键操作
    writeCriticalData();
    
    // 恢复原优先级
    vTaskPrioritySet(NULL, normalPrio);
}

场景 2:堆栈溢出防护

void vStackMonitorTask(void *param) {
    for(;;) {
        TaskHandle_t task = xTaskGetHandle("DataProcessor");
        UBaseType_t free = uxTaskGetStackHighWaterMark(task);
        
        if(free < MIN_SAFE_STACK) {
            xTaskNotify(task, STACK_OVERFLOW_WARNING, eSetValueWithOverwrite);
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

场景 3:系统健康检查

void vHealthCheck() {
    char rtStats[512];
    TaskStatus_t allTasks[5];
    
    // 获取运行时统计
    vTaskGetRunTimeStats(rtStats);
    logToSDCard(rtStats);
    
    // 检查所有任务状态
    UBaseType_t num = uxTaskGetSystemState(allTasks, 5, NULL);
    for(int i=0; i<num; i++) {
        if(allTasks[i].eCurrentState == eSuspended) {
            sendAlert("发现挂起任务!", allTasks[i].pcTaskName);
        }
    }
}

六、延时函数

1. 相对延时函数**vTaskDelay()**

功能
  • 相对延时:从调用该函数的时刻开始,阻塞任务一段指定的时间(以系统时钟节拍 tick 为单位)。
  • 适用场景:需要简单延时的场景,不严格要求周期性执行的间隔稳定性。
函数原型
void vTaskDelay(const TickType_t xTicksToDelay);
  • 参数
    • xTicksToDelay:延时的节拍数(例如 pdMS_TO_TICKS(100) 表示延时 100ms)。
工作机制
  • 任务调用 vTaskDelay() 后,进入阻塞状态,经过 xTicksToDelay 个节拍后被唤醒。
  • 延时起点:从函数调用时开始计算,每次延时的起点可能不同。
  • 问题:若任务内部执行时间不固定,会导致循环周期不稳定(总时间 = 任务执行时间 + 延时时间)。
示例
void vTaskExample(void *pvParameters) {
    while (1) {
        // 执行任务操作
        vTaskDelay(pdMS_TO_TICKS(100)); // 每次循环总时间为执行时间 + 100ms
    }
}

2. 绝对延时函数 xTaskDelayUntil()

功能
  • 绝对延时:基于一个固定的基准时间点,周期性唤醒任务,确保任务执行间隔严格稳定。
  • 适用场景:需要精确周期性执行的任务(如传感器采样、控制循环)。
函数原型
BaseType_t xTaskDelayUntil(TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement);
  • 参数

    • pxPreviousWakeTime:指向存储上一次唤醒时间的变量指针(需初始化为当前时间)。
    • xTimeIncrement:任务周期(节拍数,如 pdMS_TO_TICKS(100) 表示 100ms 周期)。
  • 返回值

    • pdTRUE:成功延时到指定时间。
    • pdFALSE:因系统节拍计数器溢出导致计算异常(罕见,通常可忽略)。
工作机制
  • 根据 pxPreviousWakeTime 记录的基准时间,计算下一次唤醒时间(基准时间 + xTimeIncrement)。
  • 延时起点:始终基于上一次唤醒时间,确保任务周期固定(总时间 = 周期时间)。
  • 优势:即使任务执行时间波动,也能保证周期性稳定。
示例
void vPeriodicTask(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount(); // 初始化基准时间
    while (1) {
        // 执行任务操作(执行时间可能不同)
        xTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(100)); // 严格 100ms 周期
    }
}

关键区别

特性vTaskDelay()xTaskDelayUntil()
延时类型相对延时(从调用时刻开始)绝对延时(基于基准时间点)
周期稳定性不保证(总时间=执行时间+延时时间)保证(总时间=固定周期)
适用场景简单延时需求严格周期性任务(如控制循环)
参数初始化无需初始化基准时间需初始化 pxPreviousWakeTime
防溢出处理无特殊处理内部自动处理节拍计数器溢出

使用注意事项

  1. 基准时间初始化

    • 使用 xTaskDelayUntil() 前,必须将 pxPreviousWakeTime 初始化为当前时间(通过 xTaskGetTickCount())。

    • 示例:

      TickType_t xLastWakeTime = xTaskGetTickCount();
      
  2. 任务执行时间超限

    • 若任务执行时间超过 xTimeIncrementxTaskDelayUntil() 会立即唤醒任务(无阻塞),可能导致周期丢失。
    • 需优化代码或增大周期值,确保任务在周期内完成。
  3. 系统节拍配置

    • 延时时间依赖系统节拍频率(如 configTICK_RATE_HZ),需正确转换时间单位(例如使用 pdMS_TO_TICKS())。

场景对比

案例1:简单延时
void vBlinkLEDTask(void *pvParameters) {
    while (1) {
        toggleLED();
        vTaskDelay(pdMS_TO_TICKS(500)); // LED每500ms闪烁一次(相对延时)
    }
}
  • 即使 toggleLED() 执行时间有微小波动,LED闪烁间隔仍接近 500ms。
案例2:精确周期控制
void vControlLoopTask(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount();
    while (1) {
        readSensor();
        updateController();
        xTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10)); // 严格10ms控制周期
    }
}
  • 即使 readSensor()updateController() 的执行时间变化,控制循环始终以 10ms 间隔运行。

总结

  • vTaskDelay():适用于对周期稳定性要求不高的简单延时场景。
  • xTaskDelayUntil():专为需要严格周期性的任务设计,能有效避免时间累积误差,是控制循环和实时系统的首选。

七、队列

1. 队列创建与基础操作

1.1 xQueueCreate()

功能
  • 动态创建队列,分配内存空间并初始化队列结构。
  • 适用场景:需要任务间或任务与中断间通信的任何场景。
函数原型
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
参数
  • uxQueueLength:队列最大容量(元素个数)。
  • uxItemSize:每个队列元素的大小(字节)。
返回值
  • 成功:队列句柄(QueueHandle_t)。
  • 失败:返回 NULL(内存不足时)。
工作机制
  • 内部调用 pvPortMalloc 动态分配内存。
  • 队列结构包含头尾指针、元素大小、队列长度等元数据。
示例
// 创建可存储10个int型数据的队列
QueueHandle_t xIntQueue = xQueueCreate(10, sizeof(int)); 
if(xIntQueue == NULL) {
    // 错误处理
}

2. 数据发送函数

2.1 xQueueSend() / xQueueSendToBack()

功能
  • 将数据插入队列尾部(FIFO操作)。
  • 特性:当队列满时,任务可阻塞等待。
函数原型
BaseType_t xQueueSend(QueueHandle_t xQueue, 
                     const void *pvItemToSend,
                     TickType_t xTicksToWait);
参数
  • xQueue:目标队列句柄。
  • pvItemToSend:待发送数据指针。
  • xTicksToWait:最大阻塞时间(单位:tick)。
返回值
  • pdPASS:数据成功入队。
  • errQUEUE_FULL:队列满且等待超时。
示例
int sensorData = 123;
xQueueSend(xSensorQueue, &sensorData, pdMS_TO_TICKS(100)); // 最多等待100ms

2.2 xQueueSendToFront()

功能
  • 将数据插入队列头部(LIFO操作)。
  • 适用场景:需要优先处理最新消息的紧急事件。
函数原型
BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
                            const void *pvItemToSend,
                            TickType_t xTicksToWait);
差异点
  • 新数据插入位置不同,其他行为与 xQueueSend() 一致。

3. 中断安全发送函数

3.1 xQueueSendFromISR()

功能
  • 中断上下文专用的队列尾部插入函数。
  • 核心特性:非阻塞、线程安全、支持任务唤醒通知。
函数原型
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
                            const void *pvItemToSend,
                            BaseType_t *pxHigherPriorityTaskWoken);
参数
  • pxHigherPriorityTaskWoken:用于通知是否唤醒高优先级任务。
中断使用规范
void ISR_Handler() {
    BaseType_t xHPW = pdFALSE;
    xQueueSendFromISR(xQueue, &data, &xHPW);
    portYIELD_FROM_ISR(xHPW); // 必要时触发上下文切换
}

4. 数据接收函数

4.1 xQueueReceive()

功能
  • 从队列头部提取并删除数据(FIFO操作)。
函数原型
BaseType_t xQueueReceive(QueueHandle_t xQueue,
                        void *pvBuffer,
                        TickType_t xTicksToWait);
典型应用
int receivedData;
if(xQueueReceive(xQueue, &receivedData, pdMS_TO_TICKS(200)) == pdPASS) {
    process_data(receivedData);
}

5. 关键对比

发送函数对比表

特性xQueueSendxQueueSendToFrontxQueueOverwritexQueueSendFromISR
数据位置尾部头部覆盖最旧尾部
阻塞机制✔️✔️
中断安全✔️
适用上下文任务任务任务中断

接收函数对比表

特性xQueueReceivexQueuePeekxQueueReceiveFromISR
数据移除✔️✔️
阻塞机制✔️✔️
中断安全✔️

6. 最佳实践

队列设计原则

  1. 容量规划:队列长度应至少为 最大突发消息量 + 1

  2. 元素尺寸:建议使用结构体打包关联数据

    typedef struct {
        uint8_t cmd;
        uint32_t param;
    } Message_t;
    QueueHandle_t xMsgQueue = xQueueCreate(5, sizeof(Message_t));
    

中断通信模板

// 中断服务程序
void UART_ISR() {
    static BaseType_t xHPW;
    uint8_t rxByte = UART->DR;
    xQueueSendFromISR(xUARTQueue, &rxByte, &xHPW);
    portYIELD_FROM_ISR(xHPW);
}

// 任务处理
void vUARTTask(void *pvParam) {
    uint8_t data;
    while(1) {
        if(xQueueReceive(xUARTQueue, &data, portMAX_DELAY)) {
            process_uart_data(data);
        }
    }
}

7. 常见问题

队列溢出处理
  • 覆盖策略:使用 xQueueOverwrite() 确保最新数据不丢失

    float latestVoltage;
    void ADC_ISR() {
        latestVoltage = read_ADC();
        xQueueOverwrite(xVoltageQueue, &latestVoltage); // 始终保存最新值
    }
    
性能优化
  • 内存预分配:对于关键队列使用静态分配

    StaticQueue_t xQueueStruct;
    uint8_t ucQueueStorage[10 * sizeof(Message_t)];
    QueueHandle_t xStatQueue = xQueueCreateStatic(10, 
                                                 sizeof(Message_t),
                                                 ucQueueStorage,
                                                 &xQueueStruct);
    

总结

  • 队列选择:根据数据优先级选择 FIFO/LIFO,根据场景选择阻塞/非阻塞
  • 跨上下文操作:严格区分任务与中断API
  • 资源管理:动态队列需配合删除函数 vQueueDelete()

八、二值信号量

1. 创建二值信号量(动态内存分配)

函数原型
SemaphoreHandle_t xSemaphoreCreateBinary(void);
  • 功能
    动态分配内存并初始化一个 二值信号量(初始状态为 不可用,即计数值为 0)。

  • 返回值

    • 成功:信号量句柄(SemaphoreHandle_t 类型)。
    • 失败:NULL(内存不足时)。
  • 示例

    SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore == NULL) {
        // 处理创建失败
    }
    
  • 注意
    动态创建的信号量需手动删除(vSemaphoreDelete())。


2. 创建二值信号量(静态内存分配)

函数原型
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);
  • 功能
    使用预分配的静态内存初始化一个二值信号量(初始状态同样为不可用)。

  • 参数
    pxSemaphoreBuffer:指向 StaticSemaphore_t 类型变量的指针(需提前分配内存)。

  • 返回值
    信号量句柄(直接使用传入的 pxSemaphoreBuffer 地址)。

  • 示例

    StaticSemaphore_t xSemaphoreBuffer;
    SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinaryStatic(&xSemaphoreBuffer);
    
  • 适用场景
    内存受限或需严格避免动态分配的系统。


3. 释放信号量(任务中)

函数原型
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
  • 功能
    释放信号量,使其变为 可用状态(计数值 +1)。

  • 参数
    xSemaphore:信号量句柄。

  • 返回值

    • pdPASS:释放成功。
    • pdFAIL:信号量已满(二值信号量最多计数值为 1)。
  • 示例

    if (xSemaphoreGive(xSemaphore) == pdPASS) {
        // 信号量已释放
    }
    

4. 释放信号量(中断中)

函数原型
BaseType_t xSemaphoreGiveFromISR(
    SemaphoreHandle_t xSemaphore,
    BaseType_t *pxHigherPriorityTaskWoken
);
  • 功能
    在中断服务程序(ISR)中释放信号量。

  • 参数

    • xSemaphore:信号量句柄。
    • pxHigherPriorityTaskWoken
      输出参数,若释放操作唤醒了更高优先级的任务,此值会被设为 pdTRUE
  • 返回值
    xSemaphoreGive()

  • 注意事项

    • 不可阻塞:中断中必须使用此函数,而非普通 xSemaphoreGive()
    • 上下文切换:若返回 pdTRUE,需调用 portYIELD_FROM_ISR()
  • 示例

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken) == pdPASS) {
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 必要时切换任务
    }
    

5. 获取信号量(任务中)

函数原型
BaseType_t xSemaphoreTake(
    SemaphoreHandle_t xSemaphore,
    TickType_t xTicksToWait
);
  • 功能
    尝试获取信号量。若信号量不可用,任务可选择阻塞等待。

  • 参数

    • xSemaphore:信号量句柄。
    • xTicksToWait
      阻塞时间(单位:时钟节拍),portMAX_DELAY 表示无限等待(需启用 INCLUDE_vTaskSuspend)。
  • 返回值

    • pdTRUE:成功获取信号量。
    • pdFALSE:超时或信号量无效。
  • 示例

    if (xSemaphoreTake(xSemaphore, pdMS_TO_TICKS(100)) == pdTRUE) {
        // 成功获取信号量
    } else {
        // 超时处理
    }
    

6. 获取信号量(中断中)

函数原型
BaseType_t xSemaphoreTakeFromISR(
    SemaphoreHandle_t xSemaphore,
    BaseType_t *pxHigherPriorityTaskWoken
);
  • 功能
    在中断服务程序中尝试获取信号量。

  • 参数

    • xSemaphore:信号量句柄。
    • pxHigherPriorityTaskWoken:输出参数(同 xSemaphoreGiveFromISR())。
  • 返回值

    • pdTRUE:成功获取信号量。
    • pdFALSE:信号量不可用。
  • 注意事项

    • 禁止阻塞xTicksToWait 必须为 0(中断中不能等待)。
    • 使用频率低:中断中获取信号量的场景较少,通常更推荐用 GiveFromISR 通知任务处理。
  • 示例

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (xSemaphoreTakeFromISR(xSemaphore, &xHigherPriorityTaskWoken) == pdTRUE) {
        // 成功获取信号量
    }
    

关键对比与使用场景

函数适用环境阻塞能力典型场景
xSemaphoreCreateBinary任务动态内存分配的信号量创建
xSemaphoreCreateBinaryStatic任务静态内存分配的信号量创建
xSemaphoreGive任务任务间同步或资源释放
xSemaphoreGiveFromISR中断中断通知任务处理事件
xSemaphoreTake任务可阻塞任务等待资源或事件
xSemaphoreTakeFromISR中断不可阻塞中断中快速检查资源(极少使用)

典型应用示例

任务与中断同步
// 全局信号量句柄
SemaphoreHandle_t xSemaphore;

// 中断服务程序
void vISR() {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 高优先级任务处理中断事件
void vTask(void *pvParams) {
    while (1) {
        if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
            // 处理中断触发的操作
        }
    }
}

注意事项

  1. 初始状态
    动态创建的二进制信号量初始为不可用(计数值 0),需手动释放一次(xSemaphoreGive())才能被获取。
  2. 优先级继承
    二值信号量无优先级继承机制,若需避免优先级反转,应使用互斥量(xSemaphoreCreateMutex())。
  3. 中断安全
    中断中必须使用 FromISR 后缀的函数,否则可能导致未定义行为。

九、计数型信号量

1. 创建计数信号量(动态内存分配)

函数原型
SemaphoreHandle_t xSemaphoreCreateCounting(
    UBaseType_t uxMaxCount,   // 信号量的最大计数值
    UBaseType_t uxInitialCount // 信号量的初始计数值
);
  • 功能
    动态分配内存并创建一个 计数信号量,用于管理多个资源的访问或事件计数。

  • 参数

    • uxMaxCount:信号量能达到的最大值,代表资源池的总容量。
    • uxInitialCount:初始可用资源数量(必须 ≤ uxMaxCount)。
  • 返回值

    • 成功:返回信号量句柄。
    • 失败:返回 NULL(内存不足时)。
  • 示例

    // 创建最多允许5个资源,初始有3个可用的计数信号量
    SemaphoreHandle_t xCountingSem = xSemaphoreCreateCounting(5, 3);
    if (xCountingSem == NULL) {
        // 处理创建失败
    }
    
  • 典型场景
    管理共享资源池(如连接池、缓冲区块)。


2. 创建计数信号量(静态内存分配)

函数原型
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
    UBaseType_t uxMaxCount,
    UBaseType_t uxInitialCount,
    StaticSemaphore_t *pxSemaphoreBuffer // 预分配的静态内存块
);
  • 功能
    使用静态内存创建计数信号量,避免动态内存分配。

  • 参数

    • pxSemaphoreBuffer:指向 StaticSemaphore_t 类型变量的指针,需预先分配内存。
  • 返回值
    直接使用传入的静态内存地址作为信号量句柄。

  • 示例

    StaticSemaphore_t xSemaphoreBuffer;
    SemaphoreHandle_t xSemaphore = xSemaphoreCreateCountingStatic(
        5, 3, &xSemaphoreBuffer);
    
  • 适用场景
    内存受限系统或需避免动态分配的情况。


3. 获取信号量的当前计数值

函数原型
UBaseType_t uxSemaphoreGetCount(
    SemaphoreHandle_t xSemaphore // 信号量句柄
);
  • 功能
    返回当前信号量的计数值,反映可用资源的数量。

  • 返回值
    当前计数值(范围:0 ~ uxMaxCount)。

  • 示例

    UBaseType_t count = uxSemaphoreGetCount(xCountingSem);
    printf("当前可用资源:%d\n", count);
    
  • 用途

    • 监控资源使用情况。
    • 调试时检查信号量状态。
    • 动态调整任务行为(如根据资源余量切换模式)。

4. 操作计数信号量的核心函数

获取信号量(Take)
BaseType_t xSemaphoreTake(
    SemaphoreHandle_t xSemaphore,
    TickType_t xTicksToWait // 阻塞时间
);
  • 行为
    若计数值 > 0,减1并返回成功;否则阻塞等待直到超时或有资源释放。
  • 中断安全版本
    xSemaphoreTakeFromISR()(注意中断中禁止阻塞)。
释放信号量(Give)
BaseType_t xSemaphoreGive(
    SemaphoreHandle_t xSemaphore
);
  • 行为
    若计数值 < uxMaxCount,增1并返回成功;否则返回失败。
  • 中断安全版本
    xSemaphoreGiveFromISR()

5. 典型应用场景与示例

场景:管理缓冲区块
#define BUFFER_POOL_SIZE 5

// 初始化:5个缓冲区全部可用
SemaphoreHandle_t xBufferSem = xSemaphoreCreateCounting(BUFFER_POOL_SIZE, BUFFER_POOL_SIZE);

// 任务获取缓冲区
void vTaskConsumer(void *pvParam) {
    while (1) {
        if (xSemaphoreTake(xBufferSem, portMAX_DELAY) == pdTRUE) {
            // 使用缓冲区
            process_buffer();
            xSemaphoreGive(xBufferSem); // 释放缓冲区
        }
    }
}

// 监控资源使用
void vMonitorTask(void *pvParam) {
    while (1) {
        UBaseType_t free = uxSemaphoreGetCount(xBufferSem);
        log("可用缓冲区:%d", free);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

6. 关键注意事项

  1. 计数值溢出
    释放信号量时,若计数值已达 uxMaxCountxSemaphoreGive() 返回 pdFAIL,需处理此类情况(如丢弃或记录告警)。

  2. 优先级反转风险
    计数信号量 不支持优先级继承,若需避免优先级反转,应使用互斥量(xSemaphoreCreateMutex())。

  3. 中断中的操作
    在中断服务程序(ISR)中必须使用 FromISR 版本的函数,并处理可能的上下文切换:

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xCountingSem, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    
  4. 静态创建的持久性
    静态信号量的生命周期与分配的静态内存一致,无需手动删除。


7. 动态 vs 静态创建对比

特性动态创建 (xSemaphoreCreateCounting)静态创建 (xSemaphoreCreateCountingStatic)
内存管理FreeRTOS 内部分配用户预先分配内存
灵活性适合不确定数量的信号量适合固定数量的信号量
内存泄漏风险需手动调用 vSemaphoreDelete() 释放无动态内存,无需删除
适用场景通用场景无动态内存系统或严格内存控制

总结

  • xSemaphoreCreateCounting():动态创建计数信号量,灵活但需注意内存管理。
  • xSemaphoreCreateCountingStatic():静态创建,适合资源受限环境。
  • uxSemaphoreGetCount():监控信号量状态,辅助调试和资源管理。

十、互斥型信号量

1. 互斥量的核心作用

互斥量(Mutex)用于 保护共享资源,确保同一时间只有一个任务可以访问临界区,防止数据竞争和优先级反转。
关键特性

  • 优先级继承:当低优先级任务持有互斥量时,若高优先级任务等待该互斥量,低优先级任务会临时继承高优先级,避免优先级反转。
  • 所有权机制:只有获取(Take)互斥量的任务才能释放(Give)它,确保资源访问的严格互斥。

2. 动态创建互斥量:xSemaphoreCreateMutex()

函数原型
SemaphoreHandle_t xSemaphoreCreateMutex(void);
  • 功能
    动态分配内存并初始化一个 互斥量,初始状态为 未锁定(可用)。

  • 返回值

    • 成功:互斥量句柄(SemaphoreHandle_t 类型)。
    • 失败:NULL(内存不足时)。
  • 示例

    SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
    if (xMutex == NULL) {
        // 处理创建失败
    }
    
  • 内存管理
    动态创建的互斥量需手动删除(vSemaphoreDelete()),避免内存泄漏。


3. 静态创建互斥量:xSemaphoreCreateMutexStatic()

函数原型
SemaphoreHandle_t xSemaphoreCreateMutexStatic(
    StaticSemaphore_t *pxMutexBuffer // 预分配的静态内存块
);
  • 功能
    使用用户提供的静态内存初始化互斥量,避免动态内存分配。

  • 参数
    pxMutexBuffer:指向 StaticSemaphore_t 类型变量的指针,需预先分配内存。

  • 返回值
    直接返回传入的静态内存地址作为互斥量句柄。

  • 示例

    StaticSemaphore_t xMutexBuffer;
    SemaphoreHandle_t xMutex = xSemaphoreCreateMutexStatic(&xMutexBuffer);
    
  • 适用场景
    内存受限系统或需严格避免动态分配的场景。


4. 互斥量的操作函数

获取互斥量(Take)
BaseType_t xSemaphoreTake(
    SemaphoreHandle_t xMutex,
    TickType_t xTicksToWait // 阻塞时间(portMAX_DELAY 表示无限等待)
);
  • 行为
    • 若互斥量可用(未锁定),立即获取并返回 pdTRUE
    • 若已被其他任务占用,任务将阻塞,直到超时或互斥量释放。
  • 中断中禁止使用
    互斥量 不能在中断服务程序(ISR) 中获取,必须使用任务上下文。
释放互斥量(Give)
BaseType_t xSemaphoreGive(SemaphoreHandle_t xMutex);
  • 行为
    • 只有持有互斥量的任务才能释放它,返回 pdTRUE
    • 其他任务尝试释放将返回 pdFAIL

5. 优先级继承机制

场景示例
  1. 低优先级任务(L) 获取互斥量并执行临界区操作。
  2. 高优先级任务(H) 尝试获取互斥量,因被占用而阻塞。
  3. FreeRTOS 临时提升 L 的优先级至与 H 相同,使其尽快释放互斥量。
  4. L 释放互斥量后,优先级恢复原值,H 立即抢占执行。
设计意义
  • 避免优先级反转导致的高优先级任务被无限延迟。
  • 确保系统实时性,但会增加低优先级任务的执行时间。

6. 使用示例

保护共享资源(如全局变量)
SemaphoreHandle_t xMutex;

// 任务1
void vTask1(void *pvParams) {
    while (1) {
        if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            // 访问共享资源
            modify_shared_data();
            xSemaphoreGive(xMutex);
        }
    }
}

// 任务2
void vTask2(void *pvParams) {
    while (1) {
        if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
            // 访问共享资源
            read_shared_data();
            xSemaphoreGive(xMutex);
        } else {
            // 处理超时(如重试或日志记录)
        }
    }
}

7. 动态 vs 静态互斥量对比

特性动态互斥量 (xSemaphoreCreateMutex)静态互斥量 (xSemaphoreCreateMutexStatic)
内存分配FreeRTOS 从堆中动态分配用户预先提供静态内存块
灵活性适合动态创建/销毁的场景适合生命周期固定的互斥量
内存泄漏风险需手动调用 vSemaphoreDelete() 释放无需释放,内存由用户管理
适用场景通用场景无动态内存系统或严格内存控制

8. 注意事项

  1. 禁止在中断中使用互斥量
    互斥量的 Take/Give 操作只能在任务上下文中进行。若需在中断中同步,使用二值信号量或任务通知。

  2. 避免嵌套获取
    同一任务重复获取已持有的互斥量会导致死锁(除非使用递归互斥量 xSemaphoreCreateRecursiveMutex())。

  3. 最小化临界区时间
    持有互斥量期间应尽快完成操作,避免长时间阻塞高优先级任务。

  4. 错误处理

    • 检查 xSemaphoreCreateMutex() 的返回值,处理内存不足情况。
    • xSemaphoreTake() 中使用超时参数,防止任务永久阻塞。

9. 常见问题

Q1:互斥量与二值信号量的区别?
  • 互斥量
    • 支持优先级继承,解决优先级反转。
    • 必须由获取者释放,严格绑定任务所有权。
  • 二值信号量
    • 无优先级继承,任何任务均可释放。
    • 适用于任务同步或事件通知。
Q2:为什么动态互斥量初始化为可用状态?

互斥量设计为初始未锁定状态,确保首次获取时可直接访问资源,符合“默认资源可访问”的直观逻辑。

Q3:如何处理互斥量被意外释放?
  • 代码审查:确保只有获取者才能释放。
  • 使用断言(如 configASSERT())检查 xSemaphoreGive() 的调用者是否为持有者。

总结

  • xSemaphoreCreateMutex():动态创建互斥量,灵活但需注意内存管理。
  • xSemaphoreCreateMutexStatic():静态创建,适合资源受限环境。
  • 优先级继承:确保高优先级任务实时性,避免优先级反转。

十一、队列集

1. 队列集合的核心作用

队列集合用于 多路复用(Multiplexing) 场景,任务可以阻塞等待多个队列或信号量中的任意一个事件,避免轮询多个队列的开销。
适用场景

  • 任务需要同时监听多个数据源(如多个队列或信号量)。
  • 实现高效的资源管理,减少任务频繁切换。

2. 创建队列集合:xQueueCreateSet()

函数原型
QueueSetHandle_t xQueueCreateSet(UBaseType_t uxEventQueueLength);
  • 功能
    创建一个队列集合,指定集合中可容纳的 队列/信号量数量(注意:不是队列中元素的数量)。

  • 参数
    uxEventQueueLength:集合中可添加的最大队列或信号量数量。

  • 返回值

    • 成功:队列集合句柄(QueueSetHandle_t)。
    • 失败:NULL(内存不足或参数无效)。
  • 示例

    QueueSetHandle_t xQueueSet = xQueueCreateSet(3); // 最多容纳3个队列/信号量
    

3. 将队列/信号量加入集合:xQueueAddToSet()

函数原型
BaseType_t xQueueAddToSet(
    QueueSetMemberHandle_t xQueueOrSemaphore, // 队列或信号量句柄
    QueueSetHandle_t xQueueSet               // 队列集合句柄
);
  • 功能
    将指定的队列或信号量添加到队列集合中。

  • 返回值

    • pdPASS:添加成功。
    • pdFAIL:添加失败(集合已满或句柄无效)。
  • 示例

    QueueHandle_t xQueue1 = xQueueCreate(5, sizeof(int));
    QueueHandle_t xQueue2 = xQueueCreate(5, sizeof(int));
    xQueueAddToSet(xQueue1, xQueueSet); // 将队列1加入集合
    xQueueAddToSet(xQueue2, xQueueSet); // 将队列2加入集合
    

4. 从集合中移除队列/信号量:xQueueRemoveFromSet()

函数原型
BaseType_t xQueueRemoveFromSet(
    QueueSetMemberHandle_t xQueueOrSemaphore, // 队列或信号量句柄
    QueueSetHandle_t xQueueSet               // 队列集合句柄
);
  • 功能
    从队列集合中移除指定的队列或信号量。

  • 返回值

    • pdPASS:移除成功。
    • pdFAIL:移除失败(句柄未在集合中)。
  • 示例

    xQueueRemoveFromSet(xQueue1, xQueueSet); // 从集合中移除队列1
    

5. 等待集合中的事件(任务中):xQueueSelectFromSet()

函数原型
QueueSetMemberHandle_t xQueueSelectFromSet(
    QueueSetHandle_t xQueueSet,
    TickType_t xTicksToWait // 阻塞时间(portMAX_DELAY 表示无限等待)
);
  • 功能
    阻塞任务,直到队列集合中任意一个队列/信号量有数据可用(或超时)。

  • 返回值

    • 有效句柄:有数据可读的队列/信号量句柄。
    • NULL:超时或集合为空。
  • 示例

    QueueSetMemberHandle_t xActivated = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
    if (xActivated == xQueue1) {
        // 处理队列1的数据
        int data;
        xQueueReceive(xQueue1, &data, 0);
    }
    

6. 等待集合中的事件(中断中):xQueueSelectFromSetFromISR()

函数原型
QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
    QueueSetHandle_t xQueueSet
);
  • 功能
    在中断服务程序(ISR)中非阻塞地检查队列集合是否有事件触发。

  • 返回值

    • 有效句柄:有数据可读的队列/信号量句柄。
    • NULL:无事件触发。
  • 注意事项

    • 不可阻塞:中断中必须立即处理。
    • 上下文切换:若需要唤醒任务,需手动调用 portYIELD_FROM_ISR()
  • 示例

    void vISR() {
        QueueSetMemberHandle_t xActivated = xQueueSelectFromSetFromISR(xQueueSet);
        if (xActivated != NULL) {
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            // 处理中断中的数据(如发送任务通知)
            vTaskNotifyGiveFromISR(xTaskHandle, &xHigherPriorityTaskWoken);
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
    }
    

7. 队列集合的典型应用场景

场景:多路数据监听
void vDataProcessorTask(void *pvParams) {
    QueueSetHandle_t xQueueSet = xQueueCreateSet(3);
    QueueHandle_t xSensorQueue = xQueueCreate(5, sizeof(SensorData));
    QueueHandle_t xCommandQueue = xQueueCreate(5, sizeof(Command));
    xQueueAddToSet(xSensorQueue, xQueueSet);
    xQueueAddToSet(xCommandQueue, xQueueSet);

    while (1) {
        QueueSetMemberHandle_t xActivated = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
        if (xActivated == xSensorQueue) {
            SensorData data;
            xQueueReceive(xSensorQueue, &data, 0);
            process_sensor_data(data);
        } else if (xActivated == xCommandQueue) {
            Command cmd;
            xQueueReceive(xCommandQueue, &cmd, 0);
            handle_command(cmd);
        }
    }
}

8. 关键注意事项

  1. 集合容量限制
    创建队列集合时指定的 uxEventQueueLength 必须 ≥ 实际添加的队列/信号量数量,否则添加失败。

  2. 队列/信号量的类型
    可以添加普通队列、二值信号量、计数信号量,但 互斥量(Mutex)不能加入队列集合

  3. 数据读取
    xQueueSelectFromSet() 仅返回有数据可读的队列句柄,仍需调用 xQueueReceive()xSemaphoreTake() 读取数据。

  4. 性能影响
    队列集合适合低频事件监听,高频场景可能因频繁切换上下文导致性能下降。

  5. 资源释放
    动态创建的队列集合需调用 vQueueDelete() 释放内存。


9. 队列集合 vs 事件组

特性队列集合(Queue Set)事件组(Event Group)
适用对象队列、信号量二进制事件标志
同步机制多路复用等待位掩码组合等待
数据传递可传递数据(队列)仅标志状态,无数据
资源开销较高(需管理多个队列)较低(单变量存储标志)
实时性适合异步数据流适合轻量级事件通知

总结

  • xQueueCreateSet():创建队列集合,指定最大容量。
  • xQueueAddToSet()/xQueueRemoveFromSet():动态管理集合成员。
  • xQueueSelectFromSet():任务中阻塞等待多个事件。
  • xQueueSelectFromSetFromISR():中断中非阻塞检查事件。

十二、事件标志组

1. 事件组的核心作用

事件组用于 多任务间的位掩码(Bitwise)同步,通过 24 位(默认)或自定义位数的标志位,实现以下功能:

  • 多事件等待:任务可同时等待多个事件中的任意一个或全部触发。
  • 轻量级通信:无需传递数据,仅通过标志位传递状态。
  • 高效同步:支持原子操作,减少临界区需求。

2. 创建事件组

2.1 动态创建:xEventGroupCreate()

EventGroupHandle_t xEventGroupCreate(void);
  • 功能:动态分配内存并初始化一个事件组,所有标志位初始化为 0。

  • 返回值:事件组句柄,失败返回 NULL

  • 示例

    EventGroupHandle_t xEventGroup = xEventGroupCreate();
    

2.2 静态创建:xEventGroupCreateStatic()

EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);
  • 功能:使用静态内存初始化事件组。

  • 参数pxEventGroupBuffer 指向预分配的 StaticEventGroup_t 类型内存。

  • 示例

    StaticEventGroup_t xEventGroupBuffer;
    EventGroupHandle_t xEventGroup = xEventGroupCreateStatic(&xEventGroupBuffer);
    

3. 清除事件位

3.1 任务中清除:xEventGroupClearBits()

EventBits_t xEventGroupClearBits(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToClear // 需清除的位掩码(如 0x01 | 0x04)
);
  • 功能:原子清除指定事件位。

  • 返回值:清除前的事件组值。

  • 示例

    xEventGroupClearBits(xEventGroup, 0x01); // 清除位0
    

3.2 中断中清除:xEventGroupClearBitsFromISR()

BaseType_t xEventGroupClearBitsFromISR(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToClear
);
  • 功能:中断安全版本,需配合 portYIELD_FROM_ISR()

  • 返回值pdPASS 表示请求已提交(实际清除由守护任务处理)。

  • 示例

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xEventGroupClearBitsFromISR(xEventGroup, 0x01);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    

4. 设置事件位

4.1 任务中设置:xEventGroupSetBits()

EventBits_t xEventGroupSetBits(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet // 需设置的位掩码
);
  • 功能:原子设置指定事件位,并唤醒等待该位的任务。

  • 返回值:设置后的事件组值。

  • 示例

    xEventGroupSetBits(xEventGroup, 0x02); // 设置位1
    

4.2 中断中设置:xEventGroupSetBitsFromISR()

BaseType_t xEventGroupSetBitsFromISR(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet,
    BaseType_t *pxHigherPriorityTaskWoken
);
  • 功能:中断安全版本,可能触发上下文切换。

  • 示例

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xEventGroupSetBitsFromISR(xEventGroup, 0x02, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    

5. 等待事件位:xEventGroupWaitBits()

EventBits_t xEventGroupWaitBits(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToWaitFor, // 等待的位掩码
    BaseType_t xClearOnExit,           // 退出时是否清除这些位
    BaseType_t xWaitForAllBits,        // 等待所有位还是任一位置位
    TickType_t xTicksToWait            // 阻塞超时时间
);
  • 功能:阻塞任务,直到指定事件位满足条件。

  • 返回值:满足条件的事件位值(超时返回当前值)。

  • 示例

    // 等待位0或位1被设置,退出时不清除位
    EventBits_t bits = xEventGroupWaitBits(xEventGroup, 0x03, pdFALSE, pdFALSE, portMAX_DELAY);
    

6. 多任务同步:xEventGroupSync()

EventBits_t xEventGroupSync(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet,      // 同步成功后设置的位
    const EventBits_t uxBitsToWaitFor,  // 需等待的位
    TickType_t xTicksToWait
);
  • 功能:同步多个任务,等待指定位被其他任务设置后,再设置自己的位并唤醒。

  • 工作流程

    1. 检查 uxBitsToWaitFor 是否已全部置位。
    2. 若未满足,阻塞等待。
    3. 当条件满足时,设置 uxBitsToSet 位,并清除 uxBitsToWaitFor 位。
  • 典型场景:多任务协作完成某阶段后触发下一步。

  • 示例

    // 任务A:等待位0和位1,成功后设置位2
    xEventGroupSync(xEventGroup, 0x04, 0x03, portMAX_DELAY);
    

7. 事件组的典型应用场景

场景1:多传感器数据就绪通知
// 定义事件位
#define TEMP_READY_BIT  (1 << 0)
#define HUMID_READY_BIT (1 << 1)

// 传感器任务设置对应位
void vTempTask(void *pvParam) {
    while (1) {
        read_temperature();
        xEventGroupSetBits(xEventGroup, TEMP_READY_BIT);
    }
}

// 处理任务等待任意数据就绪
void vProcessTask(void *pvParam) {
    while (1) {
        EventBits_t bits = xEventGroupWaitBits(xEventGroup, 
            TEMP_READY_BIT | HUMID_READY_BIT, 
            pdTRUE,  // 退出时清除位
            pdFALSE, // 等待任一位置位
            portMAX_DELAY);
        if (bits & TEMP_READY_BIT) process_temp();
        if (bits & HUMID_READY_BIT) process_humid();
    }
}
场景2:多任务阶段同步
// 任务1、2、3完成后同步
void vTask1(void *pvParam) {
    xEventGroupSync(xEventGroup, 0x01, 0x07, portMAX_DELAY); // 设置位0,等待位0-2
    // 后续操作...
}

8. 关键注意事项

  1. 位掩码范围
    FreeRTOS 默认支持 24 位(configUSE_16_BIT_TICKS=0 时为 24 位,否则 8 位),可通过 configEVENT_GROUP_BITS 自定义。

  2. 中断安全操作

    • 中断中必须使用 FromISR 函数。
    • xEventGroupSetBitsFromISR() 可能唤醒高优先级任务,需处理上下文切换。
  3. 性能优化

    • 避免高频设置/清除位,尤其是跨任务或中断场景。
    • 使用 xClearOnExit 参数减少额外清除操作。
  4. 与队列/信号量的对比

    特性事件组队列/信号量
    数据传递无数据,仅状态位可传递数据
    多事件处理支持多条件组合单条件(如数据到达)
    资源开销低(单个变量)较高(队列缓冲)

总结

  • 创建与销毁:动态(xEventGroupCreate)与静态(xEventGroupCreateStatic)初始化。
  • 位操作Set/Clear 用于修改标志位,Wait 用于阻塞等待条件。
  • 同步机制Sync 实现多任务阶段同步。
  • 中断安全FromISR 后缀函数保障中断上下文安全。

十三、任务通知

1. xTaskNotify()

函数原型

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, 
                        uint32_t ulValue, 
                        eNotifyAction eAction );

功能:向指定任务发送通知,并可选更新其通知值。
参数

  • xTaskToNotify:目标任务的句柄(TaskHandle_t类型)。
  • ulValue:设置的通知值(根据eAction决定如何应用)。
  • eAction:操作类型(见下表)。
    返回值pdPASS(成功)或pdFAIL(参数错误)。
eAction 类型说明
eNoAction仅标记通知,不修改通知值。
eSetBitsulValue按位或到原值。
eIncrement原值递增1(忽略ulValue)。
eSetValueWithOverwrite直接覆盖原值。
eSetValueWithoutOverwrite仅当任务未处于通知等待状态时覆盖。

2. xTaskNotifyAndQuery()

函数原型

BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, 
                                uint32_t ulValue, 
                                eNotifyAction eAction, 
                                uint32_t *pulPreviousNotifyValue );

功能:发送通知并返回修改前的通知值。
参数

  • pulPreviousNotifyValue:指向存储原通知值的变量。
    返回值pdPASSpdFAIL

3. xTaskNotifyGive()

函数原型

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

功能:递增目标任务的通知值(通知值 += 1),等效于信号量Give操作。
返回值:始终pdPASS


4. xTaskNotifyFromISR()

函数原型

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, 
                               uint32_t ulValue, 
                               eNotifyAction eAction, 
                               BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本的xTaskNotify()
参数

  • pxHigherPriorityTaskWoken:若发送导致更高优先级任务就绪,此参数会被设为pdTRUE
    返回值pdPASSpdFAIL
    注意:可能需要调用portYIELD_FROM_ISR()

5. xTaskNotifyAndQueryFromISR()

函数原型

BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, 
                                       uint32_t ulValue, 
                                       eNotifyAction eAction, 
                                       uint32_t *pulPreviousNotifyValue, 
                                       BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本的xTaskNotifyAndQuery()
参数:同上。


6. vTaskNotifyGiveFromISR()

函数原型

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, 
                             BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本的xTaskNotifyGive()
示例

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(xTaskHandle, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 必要时切换上下文

7. ulTaskNotifyTake()

函数原型

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, 
                           TickType_t xTicksToWait );

功能:等待通知并操作通知值(清零或递减)。
参数

  • xClearCountOnExitpdTRUE(清零)或pdFALSE(减1)。
  • xTicksToWait:最大阻塞时间。
    返回值:取到的通知值(uint32_t类型)。

8. xTaskNotifyWait()

函数原型

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, 
                            uint32_t ulBitsToClearOnExit, 
                            uint32_t *pulNotificationValue, 
                            TickType_t xTicksToWait );

功能:等待通知并灵活控制通知值的位域。
参数

  • ulBitsToClearOnEntry:等待前清除的位(如0xFFFF)。
  • ulBitsToClearOnExit:退出后清除的位。
  • pulNotificationValue:存储接收到的通知值。
    返回值pdTRUE(成功)或pdFALSE(超时)。

对比总结

函数上下文关键操作
xTaskNotify()任务发送通知,可设置值/位/递增
xTaskNotifyAndQuery()任务发送通知,同时查询原值
xTaskNotifyGive()任务模拟信号量Give(值递增1)
xTaskNotifyFromISR()中断中断安全发送,支持上下文切换标记
vTaskNotifyGiveFromISR()中断中断安全Give,无返回值
ulTaskNotifyTake()任务模拟信号量Take(等待并清零/减1)
xTaskNotifyWait()任务复杂位操作等待

示例场景

轻量级信号量(任务间同步)
// 发送端(任务或中断)
xTaskNotifyGive(xReceiverTask); // 或 vTaskNotifyGiveFromISR()

// 接收端
uint32_t ulCount = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
位事件通知
// 发送端(设置第0位)
xTaskNotify(xReceiverTask, 0x01, eSetBits);

// 接收端
uint32_t ulNotifiedValue;
xTaskNotifyWait(0, 0xFFFFFFFF, &ulNotifiedValue, portMAX_DELAY);
if (ulNotifiedValue & 0x01) {
    // 处理事件
}

注意事项

  1. 不可替代队列/信号量:任务通知仅支持单任务接收,多任务通信需用传统IPC。
  2. ISR限制:中断中必须使用FromISR版本,且不得阻塞。
  3. 性能优势:比队列/信号量更快,适合高频轻量通信。

十四、软件定时器

1. 定时器创建函数

1.1 xTimerCreate()

函数原型

TimerHandle_t xTimerCreate( 
    const char * const pcTimerName,        // 定时器名称(调试用)
    const TickType_t xTimerPeriodInTicks,  // 周期(滴答数)
    const UBaseType_t uxAutoReload,        // 是否自动重载(pdTRUE/pdFALSE)
    void * const pvTimerID,                // 用户标识ID(用于区分多定时器)
    TimerCallbackFunction_t pxCallbackFunction ); // 回调函数

功能:动态创建软件定时器(使用 FreeRTOS 堆内存)。
返回值:成功返回定时器句柄,失败返回 NULL
注意:需启用 configSUPPORT_DYNAMIC_ALLOCATION = 1


1.2 xTimerCreateStatic()

函数原型

TimerHandle_t xTimerCreateStatic(
    const char * const pcTimerName,
    const TickType_t xTimerPeriodInTicks,
    const UBaseType_t uxAutoReload,
    void * const pvTimerID,
    TimerCallbackFunction_t pxCallbackFunction,
    StaticTimer_t *pxTimerBuffer );  // 静态分配的内存块

功能:静态创建定时器(需预先分配内存)。
返回值:成功返回句柄,失败返回 NULL(通常因 pxTimerBuffer 未对齐)。
注意:需启用 configSUPPORT_STATIC_ALLOCATION = 1


2. 定时器控制函数

2.1 xTimerStart()

函数原型

BaseType_t xTimerStart( 
    TimerHandle_t xTimer,        // 定时器句柄
    TickType_t xTicksToWait );   // 阻塞时间(发送到定时器队列的等待时间)

功能:启动或重启定时器(若已启动,则重置计数)。
返回值pdPASS(成功发送命令)或 pdFAIL(队列满且超时)。
场景:在任务中启动定时器。


2.2 xTimerStartFromISR()

函数原型

BaseType_t xTimerStartFromISR(
    TimerHandle_t xTimer,
    BaseType_t *pxHigherPriorityTaskWoken );  // 是否触发任务切换

功能:中断安全版本,启动或重启定时器。
返回值pdPASSpdFAIL
注意:若返回 pxHigherPriorityTaskWoken = pdTRUE,需调用 portYIELD_FROM_ISR()


2.3 xTimerStop()

函数原型

BaseType_t xTimerStop( 
    TimerHandle_t xTimer,
    TickType_t xTicksToWait );

功能:停止定时器(若未启动则无操作)。
返回值:同 xTimerStart()


2.4 xTimerStopFromISR()

函数原型

BaseType_t xTimerStopFromISR(
    TimerHandle_t xTimer,
    BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本,停止定时器。
注意:处理方式同 xTimerStartFromISR()


3. 定时器重置与周期修改

3.1 xTimerReset()

函数原型

BaseType_t xTimerReset(
    TimerHandle_t xTimer,
    TickType_t xTicksToWait );

功能:重置定时器(等同于 xTimerStart(),若定时器未运行则启动)。
场景:需要重新开始计时的情况(如响应外部事件)。


3.2 xTimerResetFromISR()

函数原型

BaseType_t xTimerResetFromISR(
    TimerHandle_t xTimer,
    BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本的重置操作。


3.3 xTimerChangePeriod()

函数原型

BaseType_t xTimerChangePeriod(
    TimerHandle_t xTimer,
    TickType_t xNewPeriod,       // 新周期(滴答数)
    TickType_t xTicksToWait );

功能:修改定时器周期并自动重置计时。若定时器未运行,则启动。
场景:动态调整定时器频率。


3.4 xTimerChangePeriodFromISR()

函数原型

BaseType_t xTimerChangePeriodFromISR(
    TimerHandle_t xTimer,
    TickType_t xNewPeriod,
    BaseType_t *pxHigherPriorityTaskWoken );

功能:中断安全版本修改周期。


关键操作对比

函数上下文功能是否启动定时器
xTimerStart()任务启动/重启是(若已停止)
xTimerReset()任务重置计数器(等同于重启)是(若已停止)
xTimerChangePeriod()任务修改周期并重置计数器是(若已停止)
xTimerStop()任务停止

使用注意事项

  1. 定时器服务任务:所有定时器操作通过队列发送命令,需确保 configUSE_TIMERS = 1 且定时器服务任务优先级合理。
  2. 阻塞时间xTicksToWait 是发送命令到定时器队列的等待时间,非定时器本身阻塞。
  3. 回调函数限制:定时器回调函数在服务任务中执行,需简短且不可阻塞。
  4. 中断安全:在 ISR 中必须使用 FromISR 系列函数,并根据返回值处理任务切换。

示例代码

创建并启动自动重载定时器
// 回调函数
void vTimerCallback(TimerHandle_t xTimer) {
    uint32_t *pCount = (uint32_t*)pvTimerGetTimerID(xTimer);
    (*pCount)++;
}

// 创建定时器
uint32_t timerID = 0;
TimerHandle_t xTimer = xTimerCreate(
    "MyTimer", 
    1000 / portTICK_PERIOD_MS,  // 1秒周期
    pdTRUE,                     // 自动重载
    &timerID,                   // 传递计数变量地址
    vTimerCallback
);

// 启动定时器
if (xTimerStart(xTimer, 0) == pdPASS) {
    // 成功
}
在中断中修改周期
void vInterruptHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xTimerChangePeriodFromISR(xTimer, 2000 / portTICK_PERIOD_MS, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

### 实时操作系统 RTOS 的常用函数及其功能说明 实时操作系统(RTOS)提供了许多核心函数来支持任务管理、同步机制、中断处理以及内存分配等功能。以下是常见的 RTOS 函数分类及具体描述: #### 1. **任务管理** 这些函数用于创建、删除、挂起和恢复任务,调整优先级等。 - `os_task_create()` 创建一个新的任务并将其加入到就绪队列中[^1]。 - `os_task_delete()` 删除指定的任务,并释放其占用的资源。 - `os_task_suspend()` 挂起某个任务使其无法被调度器选中运行。 - `os_task_resume()` 恢复之前已被挂起的任务,允许其重新参与调度。 - `os_task_priority_set()` 设置或更改任务优先级以影响调度顺序。 #### 2. **时间管理** 提供延迟、定时器管理和超时控制的功能。 - `os_delay_ms(uint32_t ms)` 当前任务延时一段时间(单位为毫秒),在此期间不会消耗 CPU 资源。 - `os_timer_create()` 创建一个软件定时器,可以周期性触发回调函数。 - `os_timer_start()` 和 `os_timer_stop()` 启动或停止已定义好的软件定时器。 #### 3. **信号量与互斥锁** 用于保护共享资源,防止竞争条件的发生。 - `os_semaphore_create()` 初始化一个计数型信号量对象[^2]。 - `os_semaphore_take(os_semaphore_t *sem, uint32_t timeout)` 尝试获取信号量;如果不可用,则等待直到超时或者成功获得。 - `os_mutex_create()` 创建一个互斥锁对象用来串行化访问临界区代码。 - `os_mutex_lock(os_mutex_t *mutex)` 和 `os_mutex_unlock(os_mutex_t *mutex)` 锁定/解锁互斥锁,确保同一时刻只有一个任务能进入受保护区域。 #### 4. **消息传递** 通过邮箱或队列实现不同任务之间的数据交换。 - `os_queue_create(size_t item_size, size_t queue_length)` 构建固定长度的消息队列以便存储待发送的数据项。 - `os_queue_send(os_queue_t *queue, void *item, uint32_t timeout)` 往队列里放入一条新消息,可能阻塞直至有空间可用。 - `os_queue_recv(os_queue_t *queue, void *buffer, uint32_t timeout)` 接收来自队列的一条消息存入缓冲区内,可能会因为空而阻塞。 #### 5. **事件标志组** 允许多个独立布尔状态组合成单一实体供监听使用。 - `os_event_group_create()` 新建一组事件标志位集合。 - `os_event_group_wait_bits(os_event_group_t *event_group, uint32_t bits_to_wait_for, bool clear_on_exit, bool wait_all, TickType_t block_time)` 等待某些特定比特位置变为设定的状态才继续执行后续逻辑。 #### 6. **内存池管理** 动态申请和释放块状连续地址区间的服务接口。 - `os_memory_pool_create(void *start_address, size_t block_count, size_t block_size)` 配置一块预划分好大小相等的小分区作为可重用缓存池。 - `os_memory_pool_alloc(os_memory_pool_t *pool, uint32_t timeout)` 请求从内存池取出下一个空闲单元指针返回给调用者。 - `os_memory_pool_free(os_memory_pool_t *pool, void *block)` 把先前借用出去的一个区块归还回所属池子里面去。 --- ```c // 示例:创建任务并设置优先级 void task_function(void *pvParameters) { while(1){ os_delay_ms(100); // 延迟100ms } } int main() { os_task_handle_t handle; if (os_task_create(&handle, NULL, task_function, "TaskName", configMINIMAL_STACK_SIZE, tskIDLE_PRIORITY + 1)) { printf("Task created successfully.\n"); } else { printf("Failed to create Task.\n"); } vTaskStartScheduler(); // 开始调度 return 0; } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值