在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 的配置选项。

相关推荐
richxu202510012 小时前
嵌入式学习之路>(二)单片机核心原理篇>1.GPIO
单片机·嵌入式硬件·学习
恒锐丰小吕2 小时前
无锡黑锋 HF6213 高PSRR、低噪声射频LDO稳压器技术解析
嵌入式硬件·硬件工程
做cv的小昊2 小时前
在NanoPC-T6开发板上通过USB串口通信实现光源控制功能
java·后端·嵌入式硬件·边缘计算·安卓·信息与通信·开发
三佛科技-134163842122 小时前
FT8433-LRT/FT8433-KRT低成本5V80MA非隔离电源方案 BUCK/BUCK-BOOST典型电路
单片机·嵌入式硬件·智能家居·pcb工艺
星一工作室2 小时前
STM32项目分享:基于单片机的智能宠物笼舍设计及实现
stm32·单片机·嵌入式硬件·物联网·智能家居·宠物
沐欣工作室_lvyiyi2 小时前
微小功率智能充电器的设计(论文+源码)
单片机·嵌入式硬件·毕业设计·充电器
日更嵌入式的打工仔2 小时前
MCUXpresso开启汇编调试
汇编·单片机·nxp·mcuxpresso
Darken032 小时前
基于库函数来实现点灯操作
单片机·嵌入式硬件·stm32f103
TangDuoduo00052 小时前
【Cortex-M4分支跳转指令、内存访问指令、ARM AAPCS规则、异常处理】
stm32