FreeRTOS 移植到 STM32F407VETX 记录(三)

FreeRTOS 移植后参数配置指南

基于 STM32F407VETX + FreeRTOS V11.1.0 手动移植实战总结

编译器:arm-none-eabi-gcc,-mfpu=fpv4-sp-d16 -mfloat-abi=hard


一、必要参数 ------ 配错一个都跑不起来

1.1 configCPU_CLOCK_HZ

c 复制代码
#define configCPU_CLOCK_HZ  160000000

含义:CPU 主频(Hz)。FreeRTOS 用它计算 SysTick 重装载值和软件定时器时间。

配错后果 :tick 周期不对,vTaskDelay(1000) 不是 1 秒。配成 16MHz 但实际跑 160MHz → 所有延时 ×10。

怎么填 :等于 SystemCoreClock,和 SystemClock_Config() 里 PLL 算出来的一致。


1.2 configTICK_RATE_HZ

c 复制代码
#define configTICK_RATE_HZ  ((TickType_t)1000)

含义:FreeRTOS 心跳频率。1000 表示 1ms 一次 tick。

配错后果:

  • 太小(100Hz=10ms)→ 任务调度延迟大,vTaskDelay(10) 实际延迟 ≥10ms
  • 太大(10000Hz=100µs)→ CPU 全花在 tick 中断上,实际跑不了应用

推荐值:

应用 推荐值
通用控制 1000Hz(1ms)
高实时伺服 2000-10000Hz
低功耗 IoT 100Hz

1.3 configMAX_PRIORITIES

c 复制代码
#define configMAX_PRIORITIES  8

含义 :优先级数量。有效优先级范围:0(最低,idle 任务)到 configMAX_PRIORITIES - 1(最高)。

配错后果 :设 5,但 xTaskCreate(..., 6, ...) → configASSERT 触发,系统锁死。

注意事项:

  • 每增加一级,RAM 多占 ~4 bytes × 任务数(就绪链表开销)
  • 不需要无限大,5-10 级对 BMS 够用
  • 高优先级数值低在硬件中断里是反的,在任务里是正的

1.4 configMINIMAL_STACK_SIZE

c 复制代码
#define configMINIMAL_STACK_SIZE  ((unsigned short)130)

含义:FreeRTOS 自带的 idle 任务和 timer 任务的栈大小(单位:words)。

注意 :这个值只影响 idle 和 timer 任务 ,你的应用任务栈在 xTaskCreate 第 3 个参数单独指定。

推荐值:

  • 无 FPU:70-100 words
  • 有 FPU(CM4F):130 words 起步

1.5 configTOTAL_HEAP_SIZE

c 复制代码
#define configTOTAL_HEAP_SIZE  ((size_t)(75 * 1024))

含义 :FreeRTOS 动态内存池总大小(字节)。所有的任务栈、队列、信号量、互斥锁都从这里分配。

怎么算:

复制代码
总和 = 所有任务栈 + TCB + 队列/信号量/互斥锁 + 系统开销

任务栈 = xTaskCreate 第 3 参数 × 4(words → bytes)
TCB    ≈ 100 bytes × 任务数

例子(5 个 BMS 任务):
  SENSORS:     1024 × 4 = 4096
  SAFETY:      2048 × 4 = 8192
  ALGORITHM:   8192 × 4 = 32768
  PROTOCOLS:   2048 × 4 = 8192
  HOUSEKEEP:   1024 × 4 = 4096
  idle:         130 × 4 = 520
  timer:        260 × 4 = 1040
  TCB × 7:                ~700
  信号量/队列:            ~200
  ─────────────────────────
  总计:                   ~59804 ≈ 58KB

设 75KB → 富余 ~17KB,安全

配错后果:

  • 太小 → pvPortMalloc 返回 NULL → vApplicationMallocFailedHook 触发 → 系统死
  • 太大 → 超出芯片 RAM → 链接报错,或运行到一半和 main stack 相撞

1.6 configPRIO_BITS

c 复制代码
#define configPRIO_BITS  4   /* STM32F4 用 4 bits 表示优先级 */

含义:Cortex-M 芯片用几位来表示中断优先级。STM32F4 系列是 4 bits(16 级)。

配错后果:中断优先级位移计算全错,ISR 和 PendSV/SysTick 的嵌套关系乱套,随机的 HardFault。

正确写法:

c 复制代码
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS  __NVIC_PRIO_BITS   /* CMSIS 提供的,优先用 */
#else
    #define configPRIO_BITS  4
#endif

1.7 configLIBRARY_LOWEST_INTERRUPT_PRIORITY & configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

c 复制代码
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY          0xf   /* 最低中断优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY     5     /* 可以调 FreeRTOS API 的最高优先级 */

含义:这两者共同定义了"哪些中断可以安全调用 FreeRTOS API"。

复制代码
STM32F4 中断优先级(数值越大优先级越低):
  0x00(最高)............ 0x50(分界线)............ 0xF0(最低)
                    ↑
          configMAX_SYSCALL_INTERRUPT_PRIORITY

  优先级数值 ≥ 0x50 的中断 → 可以调用 FreeRTOS API(如 xQueueSendFromISR)
  优先级数值 < 0x50 的中断 → 绝对不能调 FreeRTOS API

配错后果:

  • 在优先级过高(数值太小)的 ISR 里调 FreeRTOS API → 破坏内核状态 → HardFault
  • 把关键 ISR(急停按钮)设得太低 → 被 FreeRTOS 临界区屏蔽 → 延迟响应

BMS 推荐分配:

中断 优先级 原因
急停按钮 EXTI 0x00 立即响应
CAN RX 0x40 高于 syscall 阈值,不能调 API
UART DMA 0x60 可以调 FreeRTOS API
SysTick 0xF0 最低,由 FreeRTOS 管理
PendSV 0xF0 最低,由 FreeRTOS 管理

二、中断向量映射 ------ 不配对就 HardFault

SVC 和 PendSV

c 复制代码
#define vPortSVCHandler     SVC_Handler
#define xPortPendSVHandler  PendSV_Handler

作用 :把 FreeRTOS 在 port.c 中定义的函数名映射到 CMSIS 启动文件约定的中断向量名。

FreeRTOS 内部函数 宏展开后 启动文件中的向量名
vPortSVCHandler SVC_Handler .word SVC_Handler
xPortPendSVHandler PendSV_Handler .word PendSV_Handler

同时必须 :stm32f4xx_it.c 中的 SVC_Handler 和 PendSV_Handler 要被注释掉,否则链接冲突。

SysTick

c 复制代码
// 不通过宏重命名,改为在 stm32f4xx_it.c 中手动分发:
void SysTick_Handler(void)
{
    HAL_IncTick();
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
        xPortSysTickHandler();
    }
}

三、强烈推荐配置 ------ 不开也能跑,开了更安全

3.1 configCHECK_FOR_STACK_OVERFLOW

c 复制代码
#define configCHECK_FOR_STACK_OVERFLOW  2    /* 推荐设为 2 */
值 含义
0 不检查
1 上下文切换时检查当前任务的栈(快,但可能漏检)
2 创建时用 0xA5 填充栈,切换时检查(更可靠,推荐)

检测到溢出 → vApplicationStackOverflowHook 被调用,配合调试器看 pcTaskName 快速定位。


3.2 configUSE_MALLOC_FAILED_HOOK

c 复制代码
#define configUSE_MALLOC_FAILED_HOOK  1

作用 :堆内存分配失败(OOM)时调用 vApplicationMallocFailedHook()。

BMS 里建议在里面记录到 EEPROM 并复位,而不是静默死循环------你不会想在客户现场拆机调试。


3.3 configASSERT

c 复制代码
#define configASSERT(x)  if ((x) == 0) { taskDISABLE_INTERRUPTS(); for (;;); }

作用:参数校验失败时停机。开发阶段保留,量产时可以定义成空的节省 Flash。

c 复制代码
// 量产时:
#define configASSERT(x)  ((void)0)

3.4 configASSERT_DEFINED

c 复制代码
#define configASSERT_DEFINED  1

作用 :使能 port.c 里的中断优先级合法性检查。调度器启动时验证 SVC/PendSV 是否被正确安装、NVIC 优先级是否合法。

强烈推荐开发阶段打开------会在调度器启动时就报错,而不是跑了几分钟后随机 HardFault。


3.5 configUSE_TASK_NOTIFICATIONS

c 复制代码
#define configUSE_TASK_NOTIFICATIONS  1    /* 默认就是 1 */

作用:每个任务自带一个 32 位"通知"通道,可作为轻量级二值信号量。比信号量省 RAM(0 bytes vs ~60 bytes),比队列快(~40 cycles)。

BMS 典型场景 :ADS1115 RDY 引脚 EXTI 中断里 xTaskNotifyFromISR 通知 SENSORS 任务"数据好了"。


3.6 INCLUDE 函数裁剪

c 复制代码
#define INCLUDE_uxTaskGetStackHighWaterMark  1   // 栈水位测量
#define INCLUDE_xTaskGetSchedulerState       1   // 调度器状态查询
#define INCLUDE_vTaskDelayUntil              1   // 精确周期延时
#define INCLUDE_vTaskDelay                   1   // 相对延时
#define INCLUDE_vTaskSuspend                 1   // 挂起/恢复

原则:用到哪个开哪个。设为 0 的函数不会被编译,省 Flash。


四、可选性能参数

4.1 configUSE_PREEMPTION

c 复制代码
#define configUSE_PREEMPTION  1    /* 1=抢占式, 0=合作式 */

抢占式(1) :高优先级任务就绪时立即抢占低优先级任务。BMS 必须用抢占式------SAFETY 不能等 PROTOCOLS 主动让出 CPU。

4.2 configUSE_TICKLESS_IDLE

c 复制代码
#define configUSE_TICKLESS_IDLE  0    /* 0=关闭 */

作用:所有任务空闲时停掉 SysTick 省电。BMS 通常不建议------SAFETY 需要 tick 做定时保护判断。

4.3 configUSE_IDLE_HOOK / configUSE_TICK_HOOK

c 复制代码
#define configUSE_IDLE_HOOK   1    /* idle hook: 喂 IWDG */
#define configUSE_TICK_HOOK   0    /* tick hook: HAL_IncTick 已经在 SysTick_Handler 里做了 */

4.4 configGENERATE_RUN_TIME_STATS

c 复制代码
#define configGENERATE_RUN_TIME_STATS        0  /* 开发阶段改 1 */
#define configUSE_STATS_FORMATTING_FUNCTIONS  0  /* 配合 vTaskList() 输出统计数据 */
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()  /* STM32 用 DWT_CYCCNT 做时基 */
#define portGET_RUN_TIME_COUNTER_VALUE()

开发时打开 ,通过 vTaskList() 输出每个任务的 CPU 占用和栈余量,定位瓶颈。量产后关掉。

4.5 configUSE_16_BIT_TICKS

c 复制代码
#define configUSE_16_BIT_TICKS  0    /* 0=32位(49天不溢出), 1=16位(655s溢出) */

必须为 0------BMS 要求长时间运行。


五、不需要动的参数

以下几项在 BMS 场景下保持默认即可:

参数 推荐值 原因
configIDLE_SHOULD_YIELD 1 同优先级任务间更快切换
configQUEUE_REGISTRY_SIZE 8 调试用队列命名上限
configUSE_CO_ROUTINES 0 Co-routine 已废弃
configUSE_TRACE_FACILITY 0(量产)/ 1(调试) trace 宏接口
configUSE_APPLICATION_TASK_TAG 0 调试用

六、BMS 项目推荐配置速查

c 复制代码
/* ── 必要(时钟 + 优先级 + 内存)── */
#define configCPU_CLOCK_HZ                          160000000
#define configTICK_RATE_HZ                          1000
#define configMAX_PRIORITIES                        8
#define configMINIMAL_STACK_SIZE                    130
#define configTOTAL_HEAP_SIZE                       (75 * 1024)

/* ── 中断 ── */
#define configPRIO_BITS                             4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY     0xf
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

/* ── 中断向量映射 ── */
#define vPortSVCHandler      SVC_Handler
#define xPortPendSVHandler   PendSV_Handler
// SysTick 由 stm32f4xx_it.c 手动分发,不在此处映射

/* ── 安全 ── */
#define configCHECK_FOR_STACK_OVERFLOW              2
#define configUSE_MALLOC_FAILED_HOOK                1
#define configASSERT_DEFINED                        1
#define configASSERT(x)  if ((x) == 0) { taskDISABLE_INTERRUPTS(); for (;;); }

/* ── 功能 ── */
#define configUSE_PREEMPTION                        1
#define configUSE_16_BIT_TICKS                      0
#define configUSE_IDLE_HOOK                         1
#define configUSE_TICK_HOOK                         0
#define configUSE_MUTEXES                           1
#define configUSE_COUNTING_SEMAPHORES               1
#define configUSE_RECURSIVE_MUTEXES                 1
#define configUSE_TIMERS                            1
#define configUSE_TASK_NOTIFICATIONS                1

/* ── API 裁剪 ── */
#define INCLUDE_uxTaskGetStackHighWaterMark         1
#define INCLUDE_xTaskGetSchedulerState              1
#define INCLUDE_vTaskDelayUntil                     1
#define INCLUDE_vTaskDelay                          1
#define INCLUDE_vTaskSuspend                        1

七、记住三个"必须一致"

检查项 正确值 位置
编译器 FPU 标志 -mfpu=fpv4-sp-d16 -mfloat-abi=hard Makefile / .cproject
startup.s FPU 指令集 .fpu fpv4-sp-d16 startup_stm32f407vetx.s:29
运行时 FPU 使能 `SCB->CPACR = (3<<20)

三个值必须一致------硬件浮点 vs 软件浮点的错配会导致栈帧大小不匹配 → FreeRTOS 上下文切换恢复错寄存器 → HardFault。

相关推荐
fly_sunnn9 小时前
STM32-GPIO
stm32·单片机·嵌入式硬件
宵时待雨12 小时前
STM32基础1:嵌入式历史与生态
stm32·单片机·嵌入式硬件
zhangzhangkeji12 小时前
stm32 14-1 WDG 看门狗:理论部分
stm32
不会代码的小猴13 小时前
启动文件
stm32·嵌入式硬件
深圳老胡13 小时前
STM32CubeMX 生成 CMake 工程,用 VSCode 编译与调试的完整流程
笔记·stm32·单片机·代码规范
甜甜的大香瓜13 小时前
【MT32F006】MT32F006之Timer定时
单片机
甜甜的大香瓜13 小时前
【MT32F006】MT32F006之读写内部EEPROM
单片机
2401_8628808214 小时前
iMX6ULL 驱动工程搭建 + LED 灯驱动
c语言·驱动开发·嵌入式硬件
hahaha601614 小时前
FPGA+ARM实现AWB的全流程
人工智能·嵌入式硬件·算法·计算机视觉·fpga开发
沐欣工作室_lvyiyi14 小时前
超市智能货架系统设计与实现(论文+源码)
c语言·单片机·课程设计·电子信息工程毕业设计·黑龙江工商学院·广州工商学院·成都工业学院