1. 创建第一个RTOS任务

任务目标

在STM32裸机工程基础上移植FreeRTOS,创建第一个任务实现LED以500ms周期闪烁。通过本案例掌握xTaskCreate创建任务、vTaskDelay延时以及调度器启动的核心流程。

硬件与工程准备

  • 硬件:STM32F103C8T6最小系统板,板载LED接PC13(低电平点亮)
  • 工程基础:已有可运行的裸机工程(含GPIO初始化),使用Keil MDK或STM32CubeIDE
  • FreeRTOS源码:从FreeRTOS官网获取V10.4.6+,将Source目录下*.c文件加入工程,include目录加入头文件路径
  • 配置文件:在工程中新建FreeRTOSConfig.h,按本案例末尾配置内容填写

核心代码实现

以下代码完整实现任务创建、LED闪烁逻辑与调度器启动。将代码放入main.c中,替换原有main函数。

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"

/* STM32标准库头文件(根据实际工程调整) */
#include "stm32f10x.h"

/* LED引脚定义:PC13 */
#define LED_GPIO_PORT     GPIOC
#define LED_GPIO_PIN      GPIO_Pin_13
#define LED_GPIO_CLK      RCC_APB2Periph_GPIOC

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

/* 硬件初始化:配置PC13为推挽输出 */
static void HardwareInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
    GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
    GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);  /* 初始熄灭 */
}

int main(void)
{
    HardwareInit();

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

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

    /* 正常情况下不会执行到这里 */
    while (1) {}
}

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

    for (;;)
    {
        GPIO_WriteBit(LED_GPIO_PORT, LED_GPIO_PIN,
                      (BitAction)(1 - GPIO_ReadOutputDataBit(LED_GPIO_PORT, LED_GPIO_PIN)));
        vTaskDelay(pdMS_TO_TICKS(500));  /* 延时500ms */
    }
}

FreeRTOSConfig.h 必要配置

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* 基础配置 */
#define configUSE_PREEMPTION            1
#define configUSE_IDLE_HOOK             0
#define configUSE_TICK_HOOK             0
#define configCPU_CLOCK_HZ              (SystemCoreClock)
#define configTICK_RATE_HZ              (1000)
#define configMAX_PRIORITIES            (5)
#define configMINIMAL_STACK_SIZE        (128)
#define configTOTAL_HEAP_SIZE           (8 * 1024)
#define configMAX_TASK_NAME_LEN         (16)
#define configUSE_16_BIT_TICKS          0
#define configIDLE_SHOULD_YIELD         1

/* 钩子函数与内存 */
#define configCHECK_FOR_STACK_OVERFLOW  0
#define configUSE_MALLOC_FAILED_HOOK    0

/* 可选API(本案例未使用,可关闭) */
#define configUSE_TIMERS                0
#define configUSE_COUNTING_SEMAPHORES   0
#define configUSE_MUTEXES               0
#define configUSE_RECURSIVE_MUTEXES     0
#define configUSE_QUEUE_SETS            0
#define configUSE_TASK_NOTIFICATIONS    0

/* 中断优先级(STM32使用) */
#define configPRIO_BITS                 4
#define configMAX_SYSCALL_INTERRUPT_PRIORITY   5
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY    15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5

/* 必须包含的宏 */
#define INCLUDE_vTaskDelay             1
#define INCLUDE_xTaskGetCurrentTaskHandle 0
#define INCLUDE_vTaskDelete            0

#endif /* FREERTOS_CONFIG_H */

编译与运行

  1. 将上述代码加入工程,确认FreeRTOS源码已编译(tasks.c、queue.c、list.c、heap_4.c必须包含)
  2. 在stm32f10x_it.c中实现SysTick_Handler,调用xPortSysTickHandler();若使用PendSV和SVC中断,确认启动文件已正确映射(标准库工程通常已处理)
  3. 编译下载至开发板,观察LED是否以约1Hz频率闪烁(亮500ms,灭500ms)

验证方法

  • 现象验证:LED持续闪烁,周期约1秒(亮灭各500ms),说明任务创建成功且调度器正常运行
  • 调试验证:在vLEDTask中设置断点,全速运行后应能命中断点,证明任务被调度执行
  • 异常排查:若LED不闪烁,检查configTOTAL_HEAP_SIZE是否足够(本案例8KB充足)、SysTick_Handler是否已正确调用FreeRTOS心跳处理函数

关键说明

  • xTaskCreate参数依次为:任务函数、任务名、栈大小(单位字)、参数、优先级、任务句柄。本案例未使用任务删除功能,因此vTaskDelete未调用
  • vTaskDelay使任务进入阻塞态,让出CPU给低优先级任务或空闲任务,这是RTOS任务调度的基本机制
  • 若需删除任务,可在任务内调用vTaskDelete(NULL),但需在FreeRTOSConfig.h中定义INCLUDE_vTaskDelete为1