STM32手动移植FreeRTOS

📦 准备工作

  1. 获取FreeRTOS源码:

    • 访问 FreeRTOS官网 或其 GitHub仓库 下载最新版内核源码。

    • 你也可以使用Git克隆(注意要包含子模块):git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules

  2. 准备STM32基础工程:

    • 使用 STM32CubeMX 生成一个针对你芯片型号的裸机工程(例如一个简单的LED闪烁工程),配置好时钟树、调试接口(如SYS)和你需要的外设(如GPIO、USART等)。

    • 确保这个基础工程在你的开发板上能够编译并通过(例如,LED能闪烁)。

📁 文件组织与添加

在你的STM32工程目录下(通常与CoreDrivers等文件夹同级),创建一个用于存放FreeRTOS源码的文件夹,例如Middlewares/FreeRTOS。然后按照下表的指引将必要的文件复制到相应位置:

所需文件 源路径 (基于FreeRTOS源码根目录) 目标路径 (你的工程目录) 作用描述
核心源文件(.c) FreeRTOS/Source/*.c (如 tasks.c, queue.c, list.c等) Middlewares/FreeRTOS/Source FreeRTOS内核的核心功能实现
核心头文件(.h) FreeRTOS/Source/include/*.h Middlewares/FreeRTOS/Source/include FreeRTOS内核的头文件,提供API和数据类型定义
移植层文件 FreeRTOS/Source/portable/[Compiler]/[Architecture]/* Middlewares/FreeRTOS/Source/portable 与编译器及CPU架构相关的移植代码(关键选择,见下文说明
内存管理实现 FreeRTOS/Source/portable/MemMang/heap_x.c (选一个) Middlewares/FreeRTOS/Source/portable/MemMang FreeRTOS的动态内存管理方案(五选一,通常推荐heap_4.c
配置文件 FreeRTOS/Demo/[Demo项目]/FreeRTOSConfig.h 通常放在工程Inc目录或Middlewares/FreeRTOS FreeRTOS内核的配置文件(需根据你的芯片和需求修改

🔧 关键选择说明:

  • 移植层文件 ([Compiler]和[Architecture]):

    • [Compiler]: 根据你使用的开发环境选择。

      • Keil MDK : 选择 RVDS 目录。

      • IAR : 选择 IAR 目录。

      • GCC (如STM32CubeIDE, CLion) : 选择 GCC 目录。

    • [Architecture] : 根据你STM32芯片的Cortex内核型号选择。

      • Cortex-M0 : ARM_CM0

      • Cortex-M3 : ARM_CM3

      • Cortex-M4 (无FPU): ARM_CM4F

      • Cortex-M7 (有FPU): ARM_CM7

      • *例如,STM32F103是Cortex-M3,STM32F407是Cortex-M4。*

  • 内存管理实现 (heap_x.c) :

    FreeRTOS提供了5种内存管理方案,通常选择 heap_4.c支持内存分配与释放,并能有效减少碎片 )。对于极其简单或从不释放内存的应用,也可考虑 heap_1.c

📝 操作步骤:

  1. 在你的工程目录下(例如Middlewares/FreeRTOS)创建相应的子文件夹:Source, Source/include, Source/portable

  2. 根据上表和你的芯片、编译器情况,将FreeRTOS源码包中对应的文件复制到刚刚创建的相应文件夹中。

  3. 从FreeRTOS源码包的Demo文件夹里,找一个与你芯片型号相近的Demo工程,将其中的FreeRTOSConfig.h文件复制到你的工程目录下(通常放在Inc目录下便于包含)。

⚙️ 工程配置与修改

1. 添加文件到IDE工程

  • 打开你的Keil MDK(或其他IDE)工程。

  • 在IDE中创建新的分组(Group),例如 "FreeRTOS_CORE", "FreeRTOS_PORTABLE"。

  • 将刚才复制到Middlewares/FreeRTOS/Source下的.c文件(如tasks.c, queue.c等)添加到 "FreeRTOS_CORE" 分组。

  • 将你选择的内存管理文件 (如heap_4.c)和移植层文件 (如port.c)添加到 "FreeRTOS_PORTABLE" 分组。

  • 不要添加其他未选择的内存管理文件和移植层文件。

2. 添加头文件路径

在IDE的工程设置("Options for Target" -> "C/C++" -> "Include Paths")中,添加以下头文件路径35:

  • ../Middlewares/FreeRTOS/Source/include

  • ../Middlewares/FreeRTOS/Source/portable/[Compiler]/[Architecture] (例如 ../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3)

  • 确保也包含了存放FreeRTOSConfig.h文件的路径(如../Inc)。

3. 修改FreeRTOSConfig.h

FreeRTOSConfig.h是FreeRTOS的核心配置文件,你需要根据你的芯片和项目需求进行修改。以下是一些最关键的配置项34:

配置宏 说明与典型设置
configCPU_CLOCK_HZ 设置为你STM32芯片的主时钟频率(Hz) ,例如STM32F103为72000000,STM32F407为168000000。可直接使用 SystemCoreClock
configTICK_RATE_HZ 系统节拍频率。通常设置为1000Hz,表示1ms一个时钟节拍。
configTOTAL_HEAP_SIZE FreeRTOS动态内存堆的总大小。根据你计划创建的任务、队列等数量估算。如果不够,任务创建会失败。例如可先设置为(10 * 1024)(10KB),后续再调整。
configMAX_PRIORITIES 系统支持的最大任务优先级数。设置一个够用的值即可,如5-8,不是越大越好。
configKERNEL_INTERRUPT_PRIORITY``configMAX_SYSCALL_INTERRUPT_PRIORITY 中断优先级配置非常重要 !需要根据你芯片的NVIC优先级位数(如STM32F1/F4是4位,即0-15)和你的应用来设置。设置错误可能导致系统不稳定或无法运行。务必仔细查阅FreeRTOS手册和芯片数据手册
configUSE_PREEMPTION 设置为1启用抢占式调度器,这是最常用的模式。
configUSE_TIMERS``configTIMER_TASK_PRIORITY``configTIMER_QUEUE_LENGTH``configTIMER_TASK_STACK_DEPTH 如果你要使用软件定时器,需要将这些配置使能并设置相关参数。

其他常用配置 :你还可以根据需求使能或禁用互斥量(configUSE_MUTEXES)、递归互斥量(configUSE_RECURSIVE_MUTEXES)、事件组(configUSE_EVENT_GROUPS)、栈溢出检查(configCHECK_FOR_STACK_OVERFLOW)等功能。

4. 处理中断服务程序(ISR)

FreeRTOS需要接管SVCPendSVSysTick这三个中断246。

  • 打开你的工程中stm32fxxx_it.c文件(例如stm32f1xx_it.cstm32f4xx_it.c)。

  • 找到并注释掉或删除以下三个函数的具体实现:

    • SVC_Handler(void)

    • PendSV_Handler(void)

    • SysTick_Handler(void)

  • 原因 :这些中断的服务程序已经在你之前添加的移植层文件(如port.c)中实现了。如果不注释掉,会导致函数重复定义。

注意SysTick的特殊情况 :如果你的HAL库仍然使用SysTick作为时基源(HAL_InitTick()),你可能需要修改SysTick_Handler而不是简单地删除它。一种常见的做法是28:

c

复制代码
#include "FreeRTOS.h"
#include "task.h"

void SysTick_Handler(void)
{
  HAL_IncTick(); // 维持HAL库的时基
  if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
    xPortSysTickHandler(); // 调用FreeRTOS的SysTick Handler
  }
}

确保在FreeRTOSConfig.h中启用了INCLUDE_xTaskGetSchedulerState宏。

5. 修改HAL库的时基源(强烈推荐)

STM32的HAL库默认使用SysTick作为其时基源(用于HAL_Delay(), HAL_GetTick()等)。而FreeRTOS也使用SysTick作为其任务调度的时钟节拍。虽然通过一些技巧可以让两者共享SysTick,但更推荐的做法是将HAL库的时基源切换到另一个硬件定时器(如TIM1, TIM6等),以避免潜在冲突9。

  • 你可以在STM32CubeMX中重新配置 :在SYS选项下,将Timebase SourceSysTick改为其他的硬件定时器(如TIM1)。

  • 或者直接修改代码 :在main.cHAL_Init()调用之后,重新初始化一个定时器作为HAL库的时基源。

🧪 编写测试代码

完成以上步骤后,就可以编写简单的FreeRTOS任务来测试移植是否成功了。

  1. 包含头文件 :在main.c中包含FreeRTOS头文件。

    复制代码
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h" // 如果需要使用队列等功能
  2. 创建任务函数:定义至少一个简单的任务函数。

    复制代码
    void vTaskLED(void *pvParameters) {
      for (;;) {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 假设LED连接在PC13
        vTaskDelay(500); // 延迟500个时钟节拍,即500ms (假设configTICK_RATE_HZ=1000)
      }
    }
  3. 创建任务并启动调度器 :在main()函数的初始化代码之后(while (1)之前)创建任务并启动FreeRTOS调度器。

    复制代码
    int main(void) {
      HAL_Init();
      SystemClock_Config();
      // ... 其他外设初始化代码
    
      // 创建任务
      xTaskCreate(vTaskLED, "LED_Task", 128, NULL, 2, NULL);
    
      // 启动FreeRTOS调度器,永远不会返回
      vTaskStartScheduler();
    
      for (;;) {} // 调度器启动后,不会执行到这里
    }

🔬 编译、下载与调试

  1. 编译工程:解决所有编译错误。常见的错误包括头文件路径不正确、函数未定义(可能是移植层文件没添加或路径错误)、重复定义(中断服务函数没注释掉)等。

  2. 下载到开发板并运行。

  3. 观察现象:如果一切正常,LED应该会以你设置的周期闪烁。

  4. 使用调试器:如果程序运行不正常,使用调试器进行单步调试,检查系统是否能成功创建任务、是否成功启动调度器、是否进入正确的硬件中断等。

⚠️ 常见问题排查

  • 编译错误 undefined reference to ... : 检查FreeRTOS的.c文件是否都已添加到工程组中,头文件路径是否设置正确。

  • 编译错误 redefinition of ... : 检查stm32fxxx_it.c中的SVC、PendSV、SysTick中断处理函数是否已注释掉。

  • 程序在启动调度器后卡死或进入HardFault:

    • 检查FreeRTOSConfig.h中的configCPU_CLOCK_HZ是否设置正确。

    • 检查FreeRTOSConfig.h中的中断优先级配置(configKERNEL_INTERRUPT_PRIORITYconfigMAX_SYSCALL_INTERRUPT_PRIORITY)是否正确。这是非常常见的错误来源

    • 检查堆大小configTOTAL_HEAP_SIZE是否足够创建初始任务。

    • 使用调试器检查是否成功进入SVC_Handler(用于启动第一个任务)。

  • SysTick中断冲突 : 确保HAL库的时基源已切换至非SysTick的定时器,或者按照前述方法修改了SysTick_Handler函数。

相关推荐
HHONGQI1235 小时前
STM32 之GP2Y1014AU0F的应用--基于RTOS的环境
stm32·单片机·嵌入式硬件
贾亚超19 小时前
【STM32外设】ADC
stm32·单片机·嵌入式硬件
2006yu19 小时前
从零开始学习单片机18
单片机·嵌入式硬件·学习
意法半导体STM3221 小时前
STM32 USBx Device MSC standalone 移植示例 LAT1488
单片机·嵌入式硬件·device·msc·standalone·usbx
清风6666661 天前
基于STM32单片机的OneNet物联网云平台农业土壤湿度控制系统
stm32·单片机·物联网·毕业设计·课程设计
学不动CV了1 天前
嵌入式硬件电路分析---AD采集电路
arm开发·stm32·单片机·嵌入式硬件·51单片机
leo__5201 天前
STM32的RTC模块及其应用场景
stm32·嵌入式硬件·实时音视频
GoodG_study1 天前
Matlab函数转C语言供Keil使用
stm32·matlab·c
hateregiste2 天前
国产数据库FlashDB移植到国产MCU GD32F103RC上的几点经验总结
单片机·嵌入式硬件