1. 创建第一个RTOS任务

任务目标

在STM32裸机工程基础上移植FreeRTOS,创建两个任务分别控制板载LED以不同频率交替闪烁,通过实际运行观察任务调度与延时效果。

硬件与工程准备

  • 硬件:STM32F103C8T6最小系统板(板载LED接PC13),ST-Link调试器
  • 软件:STM32CubeIDE(或Keil MDK)+ STM32CubeMX
  • CubeMX配置:开启PC13为GPIO输出;RCC选择外部晶振;SYS选择Serial Wire调试;在Middleware中选择FreeRTOS(CMSIS_V1)

核心代码实现

在CubeMX生成的main.c中,找到MX_FREERTOS_Init函数,将默认生成的defaultTask替换为以下两个任务:

/* main.c 中 MX_FREERTOS_Init 函数内 */
void MX_FREERTOS_Init(void) {
    /* 创建任务1:LED快速闪烁(200ms周期) */
    osThreadDef(taskLED1, TaskLED1, osPriorityNormal, 0, 128);
    task1Handle = osThreadCreate(osThread(taskLED1), NULL);

    /* 创建任务2:LED慢速闪烁(800ms周期) */
    osThreadDef(taskLED2, TaskLED2, osPriorityNormal, 0, 128);
    task2Handle = osThreadCreate(osThread(taskLED2), NULL);
}

/* 任务1回调函数:每200ms翻转一次LED */
void TaskLED1(void const *argument) {
    for (;;) {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        osDelay(100);  // 100ms延时,翻转间隔200ms
    }
}

/* 任务2回调函数:每800ms翻转一次LED */
void TaskLED2(void const *argument) {
    for (;;) {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        osDelay(400);  // 400ms延时,翻转间隔800ms
    }
}

在main.h中添加任务函数声明与任务句柄全局变量:

/* main.h */
extern osThreadId task1Handle;
extern osThreadId task2Handle;
void TaskLED1(void const *argument);
void TaskLED2(void const *argument);

在main.c文件顶部(用户代码区)定义全局句柄:

/* 用户代码区1 */
osThreadId task1Handle;
osThreadId task2Handle;

编译与烧录

  1. 在CubeIDE中点击Build按钮编译工程,确认无错误
  2. 连接ST-Link,点击Run烧录并启动调试
  3. 程序自动开始运行,无需额外操作

验证方法

观察板载LED闪烁节奏:

  • 通过标准:LED呈现两种闪烁频率叠加的效果——快速闪烁(约5Hz)与慢速闪烁(约1.25Hz)同时存在,视觉上表现为不规则的亮灭节奏
  • 若LED仅以单一频率闪烁,说明两个任务未正确交替运行,检查任务优先级或延时配置
  • 若LED完全不亮,检查GPIO配置或任务是否进入死循环
FreeRTOS 双任务调度时序示意 t (ms) 0 200 400 600 800 Task1 翻转间隔200ms Task2 翻转间隔800ms LED亮 LED灭

关键说明

  • 两个任务优先级相同(osPriorityNormal),FreeRTOS使用时间片轮转调度,两个任务交替获得CPU执行权
  • osDelay使当前任务进入阻塞态,释放CPU给另一个任务,这是RTOS多任务能够“同时”运行的基础
  • 任务栈大小设为128字(512字节),足够本次简单任务使用;若任务中增加局部数组或调用较深函数,需相应增大
  • LED引脚同时被两个任务操作,最终亮灭状态由最近一次执行的翻转决定,因此视觉上呈现两种频率叠加效果