1. 创建第一个RTOS任务

任务目标

在STM32裸机工程基础上,集成FreeRTOS并创建第一个任务:控制板载LED以500ms间隔闪烁。通过该案例掌握xTaskCreate()任务创建、vTaskStartScheduler()启动调度器以及vTaskDelay()延时函数的核心用法。

环境与依赖

  • 硬件:任意STM32开发板(以STM32F103C8T6为例),板载LED接PC13
  • 软件:STM32CubeIDE 或 Keil MDK-ARM
  • FreeRTOS版本:V10.4.6+(本案例使用CMSIS-OS v1封装层,也可直接使用原生API)
  • 需提前完成:STM32基础时钟配置(72MHz)、GPIO驱动(LED引脚已初始化为推挽输出)

完整代码实现

以下代码为最小可复现的FreeRTOS任务创建与调度示例,直接替换main.c中对应部分即可:

/* main.c - FreeRTOS第一个任务:LED闪烁 */
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f1xx_hal.h"

/* 任务函数声明 */
void vLEDTask(void *pvParameters);

/* 任务句柄(可留空,用于任务管理) */
TaskHandle_t xLEDTaskHandle = NULL;

/* 硬件初始化(由CubeMX生成,此处仅保留调用) */
void SystemClock_Config(void);   /* CubeMX生成:72MHz时钟 */
static void MX_GPIO_Init(void);  /* CubeMX生成:PC13推挽输出 */

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    /* 创建LED任务:优先级1,栈大小128字(512字节) */
    xTaskCreate(vLEDTask, "LED", 128, NULL, 1, &xLEDTaskHandle);

    /* 启动调度器(不会返回) */
    vTaskStartScheduler();

    /* 调度器启动失败才会到达此处 */
    while (1) { }
}

/* LED闪烁任务:每500ms翻转一次电平 */
void vLEDTask(void *pvParameters)
{
    (void)pvParameters;  /* 未使用参数,消除编译警告 */

    for (;;)
    {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        vTaskDelay(pdMS_TO_TICKS(500));  /* 延时500ms,任务进入阻塞态 */
    }
}

关键配置说明

  • FreeRTOSConfig.h:需确保configUSE_PREEMPTION为1(抢占式调度),configTICK_RATE_HZ设为1000(1ms时基),configMINIMAL_STACK_SIZE不小于128。
  • 中断优先级:若使用CubeMX生成,需在HAL_Init()后调用HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4),且FreeRTOS管理的中断优先级不得高于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(默认5)。
  • 堆大小:configTOTAL_HEAP_SIZE建议设为4096字节以上,本任务实际占用约200字节堆空间。

运行与验证

  1. 编译下载:将上述代码编译后通过ST-Link烧录至开发板。
  2. 观察现象:板载LED应以约1Hz频率(每500ms翻转一次)持续闪烁。
  3. 验证调度器运行:在vLEDTask中设置断点,全速运行后暂停,若PC指针停在任务函数内且调用栈显示vPortSVCHandler或xPortPendSVHandler,说明任务已被调度执行。
  4. 通过条件:LED闪烁周期稳定在1秒(亮500ms灭500ms),且系统不进入HardFault。

常见问题排查

  • LED不闪烁:检查MX_GPIO_Init()是否正确配置PC13为输出;确认vTaskStartScheduler()未被优化器跳过。
  • 编译报错:确认FreeRTOS源码路径已添加,且FreeRTOSConfig.h位于头文件搜索路径中。
  • 任务未执行:检查configTOTAL_HEAP_SIZE是否足够(建议≥2048);确认xTaskCreate返回值是否为pdPASS(可在创建后添加断言)。
FreeRTOS任务状态迁移图 运行态 Running 就绪态 Ready 阻塞态 Blocked 调度器选择 时间片耗尽/高优先级抢占 vTaskDelay() 延时到期(tick中断) 当前占用CPU的任务 可被调度器选中的任务 等待事件/延时的任务

本案例中LED任务在运行态与阻塞态间循环切换:调用vTaskDelay()后任务进入阻塞态并让出CPU,延时到期后由tick中断将其移回就绪态,调度器再次选择该任务进入运行态。整个过程无需用户手动管理寄存器或中断,体现了RTOS任务调度的核心机制。