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_HandlerPendSV_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。

相关推荐
普中科技12 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 45 章 FSMC-外扩 SRAM 实验
stm32·单片机·嵌入式硬件·fsmc·普中科技·外扩sram·is62wv51216
xiaoyuchidayuma14 小时前
永磁同步发电机的线电压和直流母线电压的关系
嵌入式硬件
潜创微科技14 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
rit843249914 小时前
基于C#的USB HID设备读取测试软件
嵌入式硬件
三佛科技-1873661339715 小时前
FT32F103C8AT7兼容GD32F103C8T632 位通用微控制器MCU,替代性分析
单片机·嵌入式硬件
iCxhust15 小时前
8086汇编 word ptr
汇编·单片机·嵌入式硬件·微机原理·8088单板机
嵌入式ZYXC15 小时前
第3篇:《面试题:I2C为什么要加上拉电阻?阻值怎么选?》
stm32·单片机·嵌入式硬件·面试·职场和发展
leo__52016 小时前
C# 虚拟键盘(软键盘)实现
单片机·c#·计算机外设
你疯了抱抱我16 小时前
【STM32】使用 STM32CubeMX 生成项目,LED测试;上位机:STM32F411CEU6
stm32·单片机·嵌入式硬件