在STM32上使用FreeRTOS

目录

一、环境准备

[二、通过 STM32CubeMX 配置 FreeRTOS](#二、通过 STM32CubeMX 配置 FreeRTOS)

[1. 新建工程并选择芯片](#1. 新建工程并选择芯片)

[2. 配置基础外设](#2. 配置基础外设)

[3. 启用并配置 FreeRTOS](#3. 启用并配置 FreeRTOS)

[4. 生成工程代码](#4. 生成工程代码)

三、代码开发:创建任务与功能实现

[1. 基本任务示例(LED 闪烁 + UART 打印)](#1. 基本任务示例(LED 闪烁 + UART 打印))

[步骤 1:在 CubeMX 中添加任务(或手动创建)](#步骤 1:在 CubeMX 中添加任务(或手动创建))

[步骤 2:实现任务函数](#步骤 2:实现任务函数)

[步骤 3:启动 FreeRTOS 调度器](#步骤 3:启动 FreeRTOS 调度器)

[2. 任务间通信示例(队列 / 信号量)](#2. 任务间通信示例(队列 / 信号量))

示例:队列传递数据

四、编译与调试

五、关键注意事项

六、常见问题排查


在 STM32 上使用 FreeRTOS 主要分为环境搭建移植 / 配置代码开发调试几个步骤。以下是详细的实操指南,以 STM32CubeMX + Keil MDK 为例(也适用于 STM32CubeIDE):

一、环境准备

  1. 硬件:任意 STM32 开发板(如 STM32F103、STM32F407、STM32L431 等)。
  2. 软件
    • STM32CubeMX(用于配置芯片和 FreeRTOS);
    • Keil MDK/STM32CubeIDE(用于代码编写和编译);
    • FreeRTOS 源码(CubeMX 可自动集成,无需手动下载)。

二、通过 STM32CubeMX 配置 FreeRTOS

STM32CubeMX 已内置 FreeRTOS 适配层,可一键配置,无需手动移植,大幅简化流程。

1. 新建工程并选择芯片
  • 打开 STM32CubeMX,选择 "New Project",搜索并选择目标 STM32 型号(如 STM32F407ZG)。
  • 选择启动模式(默认即可),进入配置界面。
2. 配置基础外设
  • 启用时钟:配置 HSE(外部晶振)或 HSI(内部晶振),设置系统时钟(如 F407 设为 168MHz)。
  • 启用调试接口:配置 SWD/JTAG(如 "Serial Wire"),用于后续调试。
  • 可选:启用 GPIO、UART 等外设(如 UART1 用于打印日志)。
3. 启用并配置 FreeRTOS
  • 在 "Middleware" 中选择FreeRTOS ,点击 "Enabled" 启用。
    • 注意:FreeRTOS 有两个版本可选:FreeRTOSv10(标准版)和FreeRTOS-Kernel(最新版),按需选择。
  • 配置 FreeRTOS 核心参数:
    • Task and Queue Settings :可直接创建任务(如StartDefaultTask),设置任务名称、优先级、堆栈大小、入口函数。
    • Config Parameters :关键配置(建议保持默认,或按需修改):
      • configUSE_PREEMPTION:启用抢占式调度(必选,设为1);
      • configMAX_PRIORITIES:最大优先级数(如设为5);
      • configTICK_RATE_HZ:系统节拍频率(默认 1000Hz,即 1ms/tick);
      • configMINIMAL_STACK_SIZE:空闲任务堆栈大小(默认即可);
      • configUSE_IDLE_HOOK/configUSE_TICK_HOOK:是否启用空闲 / 节拍钩子函数(按需开启)。
4. 生成工程代码
  • 点击 "Project Manager",设置工程名称、路径、编译器(如MDK-ARM)。
  • 点击 "Generate Code",CubeMX 会自动生成包含 FreeRTOS 的工程框架(已完成底层移植,无需手动修改启动文件或中断向量)。

三、代码开发:创建任务与功能实现

打开生成的工程(如 Keil MDK),核心开发围绕任务创建任务通信外设操作展开。

1. 基本任务示例(LED 闪烁 + UART 打印)

假设已配置 LED(PA5)和 UART1(用于日志输出),创建两个任务:

  • LED_Task:控制 LED 周期性闪烁;
  • UART_Task:周期性打印日志。
步骤 1:在 CubeMX 中添加任务(或手动创建)
  • 回到 CubeMX 的 FreeRTOS 配置界面,添加新任务:
    • 任务 1:名称LED_Task,优先级2,堆栈大小128,入口函数LED_Task_Func
    • 任务 2:名称UART_Task,优先级1,堆栈大小256,入口函数UART_Task_Func
  • 重新生成代码,CubeMX 会自动在freertos.c中生成任务声明。
步骤 2:实现任务函数

freertos.c中找到任务入口函数,编写业务逻辑:

c

运行

复制代码
/* LED任务:每隔500ms翻转LED */
void LED_Task_Func(void *argument)
{
  for(;;)
  {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转LED引脚
    osDelay(500); // FreeRTOS延时函数(单位:ms)
  }
}

/* UART任务:每隔1s打印日志 */
void UART_Task_Func(void *argument)
{
  uint32_t count = 0;
  for(;;)
  {
    HAL_UART_Transmit(&huart1, (uint8_t*)("UART Task: Count = %d\r\n", count), strlen("UART Task: Count = %d\r\n"), 100);
    count++;
    osDelay(1000);
  }
}
步骤 3:启动 FreeRTOS 调度器

CubeMX 已自动在main.c中生成启动代码,无需手动修改:

c

运行

复制代码
int main(void)
{
  /* 初始化硬件:HAL库初始化、时钟、外设等 */
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  /* 创建任务并启动调度器 */
  osKernelInitialize(); // 初始化FreeRTOS内核
  osThreadNew(LED_Task_Func, NULL, &LED_Task_attributes); // 创建LED任务
  osThreadNew(UART_Task_Func, NULL, &UART_Task_attributes); // 创建UART任务
  osKernelStart(); // 启动调度器(从此开始任务调度)
  
  /* 调度器启动后,main函数不会执行到这里 */
  while (1)
  {
  }
}
2. 任务间通信示例(队列 / 信号量)

如果需要任务间交互(如 UART 接收数据后通知 LED 任务),可使用 FreeRTOS 的队列或信号量。

示例:队列传递数据
  • freertos.c中定义队列句柄:

    c

    运行

    复制代码
    osMessageQueueId_t dataQueueHandle; // 队列句柄
  • 在 CubeMX 中配置队列(或手动创建): c

    运行

    复制代码
    // 在osKernelInitialize()后创建队列(可存储5个uint32_t数据)
    dataQueueHandle = osMessageQueueNew(5, sizeof(uint32_t), NULL);
  • UART 任务发送数据到队列,LED 任务接收数据: c

    运行

    复制代码
    // UART任务发送数据
    void UART_Task_Func(void *argument)
    {
      uint32_t sendData = 123;
      for(;;)
      {
        osMessageQueuePut(dataQueueHandle, &sendData, 0, osWaitForever); // 发送数据
        sendData++;
        osDelay(1000);
      }
    }
    
    // LED任务接收数据
    void LED_Task_Func(void *argument)
    {
      uint32_t recvData;
      for(;;)
      {
        if(osMessageQueueGet(dataQueueHandle, &recvData, NULL, osWaitForever) == osOK)
        {
          HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
          // 打印接收的数据(需实现UART打印)
          printf("Received data: %d\r\n", recvData);
        }
      }
    }

四、编译与调试

  1. 编译工程:在 Keil MDK 中点击 "Build",确保无错误(若提示堆栈不足,可增大任务堆栈或 Heap 大小)。
  2. 下载程序:连接开发板,点击 "Download" 将程序烧录到 STM32。
  3. 调试
    • 启用调试模式(Keil 中点击 "Debug"),可查看任务状态、堆栈使用、变量值等;
    • 通过 UART 助手查看日志输出,或观察 LED 闪烁是否符合预期。

五、关键注意事项

  1. 中断与 FreeRTOS 兼容

    • 若使用 HAL 库中断(如 UART 接收中断),需在中断服务函数中使用 FreeRTOS 的中断安全 API(如xQueueSendFromISRxSemaphoreGiveFromISR)。
    • 配置中断优先级:确保 FreeRTOS 的 SysTick 中断优先级低于其他外设中断(通过NVIC_PriorityGroupConfig设置,如分组 4,抢占优先级 0~15)。
  2. 内存管理

    • CubeMX 默认使用heap_4内存分配策略(适合大多数场景),可在FreeRTOSConfig.h中修改configTOTAL_HEAP_SIZE调整堆大小。
  3. 低功耗优化

    • 若需低功耗,启用configUSE_TICKLESS_IDLE(在 FreeRTOSConfig.h 中设为 1),结合 STM32 的低功耗模式(如 STOP 模式)。

六、常见问题排查

  • 任务无法启动 :检查osKernelStart()是否调用,任务优先级是否合理(空闲任务优先级最低)。
  • 串口打印乱码:检查 UART 波特率配置,确保时钟树正确。
  • 堆栈溢出 :通过vTaskList()vTaskGetRunTimeStats()查看堆栈使用,增大堆栈大小。

通过以上步骤,即可在 STM32 上快速实现 FreeRTOS 的基础应用。如需更复杂的功能(如软件定时器、事件组、互斥锁),可参考 FreeRTOS 官方文档或 STM32CubeMX 的配置选项。

相关推荐
云山工作室3 小时前
基于单片机的智能电表
单片机·毕业设计·毕设
不怕犯错,就怕不做4 小时前
RK3562 +RK817的dts布尔属性解析(uboot基础知识)
linux·驱动开发·嵌入式硬件
逐梦之程5 小时前
STM32的串口通讯--DMA接收和CPU接收不定长数据帧对比
stm32·单片机·嵌入式硬件
文弱书生6565 小时前
2-electronbot主控免驱工程结构
linux·单片机·嵌入式硬件
求知喻5 小时前
LCD真值表
单片机·嵌入式硬件
csg11076 小时前
高效驱动,灵活控制:深度解析RZ7899大电流DC双向马达驱动芯片及其创新应用
单片机·嵌入式硬件·物联网
心疼你的一切6 小时前
三菱FX5U PLC与C#通信开发指南
开发语言·单片机·c#
JSMSEMI118 小时前
JSM9N20C 200V N 沟道 MOSFET
单片机·嵌入式硬件
梁下轻语的秋缘8 小时前
I2S与I2C
运维·stm32·单片机·51单片机
chipsense8 小时前
机器人用霍尔电流传感器,能提升操作安全性么?
单片机·嵌入式硬件·人形机器人·霍尔电流传感器