当前位置: 首页 > news >正文

在STM32F103上进行FreeRTOS移植和配置(源码移植)

个人博客:blogs.wurp.top

1. 移植准备

硬件要求

  • STM32F103开发板(如Blue Pill)
  • ST-Link/V2调试器
  • USB转串口模块(用于调试输出)

软件资源

  1. FreeRTOS源码(v10.4.6或更高版本)
  2. STM32标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)
  3. 开发环境:Keil MDK-ARM或GCC工具链

创建以下工程目录结构:

/Project├── /CMSIS              # 核心支持文件├── /FreeRTOS           # FreeRTOS源码│   ├── /Source         # 内核源码│   ├── /Portable       # 移植层文件│   └── /MemMang        # 内存管理实现├── /Libraries          # ST标准外设库├── /User               # 用户代码└── /Drivers            # 板级驱动

2. 添加FreeRTOS源码

从FreeRTOS官网下载源码后,复制以下内容:

  • FreeRTOS/Source/*.c/FreeRTOS/Source
  • FreeRTOS/Source/include/FreeRTOS/include
  • FreeRTOS/Source/portable/[Compiler]/ARM_CM3/FreeRTOS/Portable/GCC (或对应编译器目录)
  • FreeRTOS/Source/portable/MemMang/heap_4.c/FreeRTOS/MemMang

3. 创建FreeRTOSConfig.h

  • /User目录下创建FreeRTOSConfig.h配置文件:

    #ifndef FREERTOS_CONFIG_H
    #define FREERTOS_CONFIG_H#include "stm32f10x.h"// 基本配置
    #define configUSE_PREEMPTION        1
    #define configUSE_IDLE_HOOK         0
    #define configUSE_TICK_HOOK         0
    #define configCPU_CLOCK_HZ          (SystemCoreClock)  // 72MHz
    #define configTICK_RATE_HZ          ((TickType_t)1000) // 1ms tick
    #define configMAX_PRIORITIES        (5)
    #define configMINIMAL_STACK_SIZE    ((uint16_t)128)    // 字为单位
    #define configTOTAL_HEAP_SIZE       ((size_t)(10 * 1024)) // 10KB堆
    #define configMAX_TASK_NAME_LEN     (16)// 功能配置
    #define configUSE_16_BIT_TICKS      0
    #define configUSE_MUTEXES           1
    #define configUSE_RECURSIVE_MUTEXES 1
    #define configUSE_COUNTING_SEMAPHORES 1
    #define configUSE_QUEUES            1
    #define configUSE_TIME_SLICING      1// 内存分配方案
    #define configSUPPORT_DYNAMIC_ALLOCATION 1
    #define configSUPPORT_STATIC_ALLOCATION  0// 钩子函数
    #define configUSE_IDLE_HOOK         0
    #define configUSE_TICK_HOOK         0// 中断优先级配置 (STM32使用4位优先级)
    #define configKERNEL_INTERRUPT_PRIORITY         255 // 等价于15 (0xF)
    #define configMAX_SYSCALL_INTERRUPT_PRIORITY    191 // 等价于11 (0xB)// 移植层重定义
    #define vPortSVCHandler    SVC_Handler
    #define xPortPendSVHandler PendSV_Handler
    #define xPortSysTickHandler SysTick_Handler// 断言配置
    #define configASSERT(x) if((x) == 0) { taskDISABLE_INTERRUPTS(); for(;;); }#endif /* FREERTOS_CONFIG_H */
    

4. 修改启动文件

  • 在STM32启动文件(如startup_stm32f10x_md.s)中,修改以下中断向量:

    ; 修改前
    ; DCD     SVC_Handler                ; SVCall Handler          
    ; DCD     PendSV_Handler             ; PendSV Handler          
    ; DCD     SysTick_Handler            ; SysTick Handler         ; 修改后
    DCD     vPortSVCHandler             ; FreeRTOS SVC Handler
    DCD     xPortPendSVHandler          ; FreeRTOS PendSV Handler
    DCD     xPortSysTickHandler         ; FreeRTOS SysTick Handler
    

5. 初始化FreeRTOS

  • main.c中添加FreeRTOS初始化和任务创建:

    #include "stm32f10x.h"
    #include "FreeRTOS.h"
    #include "task.h"// LED任务函数
    void vLEDTask(void *pvParameters) {const TickType_t xDelay = pdMS_TO_TICKS(500);for(;;) {GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)));vTaskDelay(xDelay);}
    }// 调试任务函数
    void vDebugTask(void *pvParameters) {// 初始化串口...for(;;) {// 发送调试信息vTaskDelay(pdMS_TO_TICKS(1000));}
    }int main(void) {// 系统时钟初始化(72MHz)SystemInit();// 外设初始化(GPIO、USART等)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);// 创建任务xTaskCreate(vLEDTask, "LED", configMINIMAL_STACK_SIZE, NULL, 2, NULL);xTaskCreate(vDebugTask, "Debug", configMINIMAL_STACK_SIZE+128, NULL, 1, NULL);// 启动调度器vTaskStartScheduler();// 不应执行到这里while(1);
    }// 空闲任务钩子函数(可选)
    void vApplicationIdleHook(void) {// 低功耗处理__WFI(); // 进入睡眠模式
    }
    

6. 内存管理配置

在工程中包含heap_4.c内存管理实现:

  • 这是最推荐的内存管理方案,支持内存碎片整理
  • FreeRTOSConfig.h中通过configTOTAL_HEAP_SIZE设置堆大小

7. 中断优先级配置要点

  • 在STM32F103中正确处理中断优先级:
    // 设置中断分组(在main初始化中调用)
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 外设中断优先级设置示例(USART1)
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10; // 必须高于configMAX_SYSCALL_INTERRUPT_PRIORITY
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    

8. 关键移植文件说明

  1. port.c (位于/FreeRTOS/Portable/GCC/ARM_CM3)

    • 实现任务上下文切换
    • 提供SysTick和PendSV中断处理
    • 定义临界区进入/退出宏
  2. portmacro.h

    • 定义处理器特定数据类型
    • 声明硬件相关宏和函数
  3. heap_4.c

    • 内存分配实现
    • 提供pvPortMalloc()vPortFree()函数

9. 调试与优化技巧

栈溢出检测

  • FreeRTOSConfig.h中启用栈溢出检测:

    #define configCHECK_FOR_STACK_OVERFLOW 2
    
  • 实现栈溢出钩子函数:

    void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {// 处理栈溢出错误while(1);
    }
    

堆使用监控

  • 在任务中监控堆使用情况:
    void vMonitorTask(void *pvParameters) {for(;;) {size_t freeHeap = xPortGetFreeHeapSize();// 通过串口输出堆使用情况vTaskDelay(pdMS_TO_TICKS(5000));}
    }
    

任务状态监控

  • 启用统计功能:

    #define configUSE_TRACE_FACILITY 1
    #define configUSE_STATS_FORMATTING_FUNCTIONS 1
    
  • 获取任务状态:

    void vTaskStats(char *pcWriteBuffer) {TaskStatus_t *pxTaskStatusArray;volatile UBaseType_t uxArraySize, x;uxArraySize = uxTaskGetNumberOfTasks();pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));if(pxTaskStatusArray != NULL) {uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL);for(x = 0; x < uxArraySize; x++) {// 格式化输出任务信息}vPortFree(pxTaskStatusArray);}
    }
    

10. 常见问题解决

  1. 任务无法调度

    • 检查vTaskStartScheduler()是否被调用
    • 验证SysTick中断是否正常工作
    • 确保PendSV和SVC中断优先级设置为最低
  2. HardFault异常

    • 检查栈大小是否足够(使用栈高水位线检测)
    • 确认内存访问权限(特别是MPU配置)
    • 检查中断处理函数是否调用了FreeRTOS API
  3. 堆分配失败

    • 增加configTOTAL_HEAP_SIZE
    • 使用xPortGetFreeHeapSize()监控内存使用
    • 考虑静态分配任务内存
  4. 时间相关函数不准确

    • 确认configCPU_CLOCK_HZconfigTICK_RATE_HZ设置正确
    • 检查系统时钟配置是否为72MHz
    • 验证SysTick重载值计算

11. 高级配置选项

低功耗模式

void vApplicationIdleHook(void) {// 进入STOP模式PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);// 唤醒后重新配置时钟SystemInit();
}

使用静态内存分配

// 定义任务控制块和栈
static StaticTask_t xTaskTCB;
static StackType_t xTaskStack[configMINIMAL_STACK_SIZE];// 创建任务
xTaskCreateStatic(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, xTaskStack, &xTaskTCB);

软件定时器

FreeRTOSConfig.h中启用:

#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
#define configTIMER_QUEUE_LENGTH 5
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)

创建定时器:

TimerHandle_t xTimer = xTimerCreate("Timer", pdMS_TO_TICKS(1000), pdTRUE, NULL, vTimerCallback);
xTimerStart(xTimer, 0);

12. 总结

通过以上步骤,你已经完成了:

  1. 手动移植FreeRTOS到STM32F103
  2. 正确配置内核参数和中断优先级
  3. 创建并管理多个任务
  4. 实现基本调试和监控功能
  5. 应用高级功能如低功耗模式和静态分配

源码移植方式虽然步骤较多,但提供了更深层次的理解和更大的灵活性。关键点在于:

  • 正确配置FreeRTOSConfig.h参数
  • 确保中断优先级设置合理
  • 仔细管理内存资源
  • 使用调试工具监控系统状态

这种移植方式特别适合对实时性要求高、需要深度优化的应用场景。

http://www.dtcms.com/a/334030.html

相关文章:

  • 【总结型】c语言中的位运算
  • 给AI装上“稳压器”:批归一化如何加速深度学习
  • [Linux] Cockpit管理服务器 软件包管理
  • VScode 使用遇到的问题
  • linux docker neo4j 导出 (windows 导入)
  • Winsows系统去除右键文件显示的快捷列表
  • 微服务架构实战指南:从单体应用到云原生的蜕变之路
  • 头文件包含和前置声明
  • python---包
  • libcurl 中 curl_multi 的演进:从双路并进到 epoll 革命
  • 8.16打卡 DAY43 复习日
  • 0301-solidity进阶-区块链-web3
  • 布隆过滤器的原理及使用
  • Kotlin-基础语法练习一
  • Maven私服配置模版
  • Qt 关于QString和std::string数据截断的问题- 遇到\0或者0x00如何处理?
  • 小白学投资理财 第一天
  • 算力与显存、显存带宽的关系
  • 【php反序列化介绍与常见触发方法】
  • Houdini Vop学习笔记
  • 测试工程师的AI转型指南:从工具使用到测试策略重构
  • TDengine IDMP 高级功能(4. 元素引用)
  • OpenAI TTS API + Web 前端 AudioContext 实战方案
  • 【Web后端】Django、flask及其场景——以构建系统原型为例
  • 《深度解构:构建浏览器端Redis控制台的WebSocket协议核心技术》
  • Protues使用说明及Protues与Keil联合仿真实现点亮小灯和流水灯
  • 【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
  • C#WPF实战出真汁08--【消费开单】--餐桌面板展示
  • C语言基础:(十五)深入理解指针(5)
  • 数据清洗:数据处理的基石