2. 移植FreeRTOS到STM32

任务目标

在STM32F103裸机工程中集成FreeRTOS内核,完成SysTick与PendSV中断配置,创建两个优先级相同的任务,通过串口输出验证任务调度器正常交替运行。

环境与依赖

  • 硬件:STM32F103C8T6最小系统板,USART1(PA9/PA10)连接USB转串口模块
  • 软件:STM32CubeIDE 或 Keil MDK-ARM(本文以STM32CubeIDE为例)
  • FreeRTOS版本:V10.4.6(或更高版本,接口一致)
  • 基础工程:已配置好系统时钟(72MHz)和USART1驱动,能通过printf输出调试信息

实现步骤

将FreeRTOS源码添加到工程:复制FreeRTOS/Source目录下的tasks.c、list.c、queue.c、timers.c、event_groups.c及portable/MemMang/heap_4.c到工程,并添加portable/RVDS/ARM_CM3(Keil)或portable/GCC/ARM_CM3(CubeIDE)下的port.c和portmacro.h。在工程头文件搜索路径中添加FreeRTOS源码根目录和portable对应目录。

创建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              ( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES            ( 5 )
#define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 8 * 1024 ) )
#define configMAX_TASK_NAME_LEN         ( 16 )
#define configUSE_16_BIT_TICKS          0
#define configIDLE_SHOULD_YIELD         1
#define configUSE_MUTEXES               0
#define configUSE_RECURSIVE_MUTEXES     0
#define configUSE_COUNTING_SEMAPHORES   0
#define configUSE_TIMERS                0
#define configQUEUE_REGISTRY_SIZE       0

#define configPRIO_BITS                 4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY     15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY     ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

#endif /* FREERTOS_CONFIG_H */

关键完整代码

/* main.c - FreeRTOS双任务交替运行示例 */
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"   /* 包含串口初始化函数 MX_USART1_UART_Init() 和 printf 重定向 */

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

/* 任务句柄(本示例未使用,但保留以演示创建方式) */
TaskHandle_t xTask1Handle = NULL;
TaskHandle_t xTask2Handle = NULL;

int main(void)
{
    /* 初始化系统时钟(CubeIDE生成的SystemClock_Config()) */
    SystemClock_Config();
    /* 初始化USART1 */
    MX_USART1_UART_Init();

    /* 创建两个相同优先级的任务 */
    xTaskCreate(vTask1, "Task1", 128, NULL, 2, &xTask1Handle);
    xTaskCreate(vTask2, "Task2", 128, NULL, 2, &xTask2Handle);

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

    /* 正常情况下不会执行到这里 */
    for( ;; );
}

void vTask1(void *pvParameters)
{
    (void)pvParameters;
    for( ;; )
    {
        printf("[Task1] Running\r\n");
        vTaskDelay(pdMS_TO_TICKS(500));  /* 延时500ms */
    }
}

void vTask2(void *pvParameters)
{
    (void)pvParameters;
    for( ;; )
    {
        printf("[Task2] Running\r\n");
        vTaskDelay(pdMS_TO_TICKS(500));  /* 延时500ms */
    }
}

/* 可选:空闲任务钩子未启用,无需实现 */

中断优先级配置说明

FreeRTOS要求PendSV和SysTick中断优先级设为最低。在STM32CubeIDE中,通过HAL_NVIC_SetPriority()设置:

/* 在main函数中、创建任务前添加 */
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);

若使用Keil标准库,则在NVIC_Configuration()中设置:

NVIC_SetPriority(PendSV_IRQn, 15);
NVIC_SetPriority(SysTick_IRQn, 15);

注意:configKERNEL_INTERRUPT_PRIORITY必须与上述设置一致(均为15),否则调度异常。

编译与运行

  1. 将上述main.c内容替换工程中生成的main.c(保留SystemClock_Config和MX_USART1_UART_Init函数)。
  2. 确认FreeRTOS源码已添加至工程且头文件路径正确。
  3. 编译工程,烧录至STM32F103C8T6。
  4. 打开串口助手,波特率115200,观察输出。

验证方法

串口助手应持续交替收到以下输出(预期输出,非实测结果):

[Task1] Running
[Task2] Running
[Task1] Running
[Task2] Running
...

判定通过条件:

  • 两个任务均能周期执行,输出间隔约500ms;
  • Task1和Task2交替出现,无任务长期独占CPU;
  • 系统不进入HardFault,无卡死现象。

常见问题

  • 编译报错“undefined symbol vApplicationStackOverflowHook”:在任意.c文件中添加空实现:void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { (void)xTask; (void)pcTaskName; for(;;); }
  • 任务不切换:检查PendSV和SysTick优先级是否已设为最低(数值15),且configKERNEL_INTERRUPT_PRIORITY与之匹配。
  • 串口无输出:确认USART1已初始化且printf重定向正确(使用MicroLIB或fputc重定向)。