文章目录
-
- 一、移植前准备
-
- 1.1 硬件与软件环境
- 1.2 核心文件获取
- 二、移植整体流程
- 三、详细移植步骤
-
- 3.1 步骤1:搭建STM32F103基础工程
- 3.2 步骤2:复制FreeRTOS源码到工程目录
- 3.3 步骤3:添加源码文件到Keil工程
- 3.4 步骤4:配置头文件包含路径
- 3.5 步骤5:创建并配置FreeRTOSConfig.h
- 3.6 步骤6:修改STM32中断服务函数
- 3.7 步骤7:适配系统时钟与任务调度
- 3.8 步骤8:编写测试任务验证移植效果
- 3.9 步骤9:编译与下载验证
- 四、常见问题与解决方法
-
- 4.1 编译错误:undefined reference to `vPortSVCHandler`
- 4.2 任务调度器启动失败
- 4.3 LED不闪烁/串口无输出
- 五、移植优化建议
-
- 总结
一、移植前准备
1.1 硬件与软件环境
- 硬件平台:STM32F103C8T6最小系统板(核心板)
- 软件工具:
- MDK-ARM (Keil uVision5) V5.38及以上版本
- STM32CubeMX V6.9.0及以上版本
- FreeRTOS官方源码(V10.4.3,适配Cortex-M3内核)
- STM32F103标准库(V3.5.0)或HAL库(V1.8.5)
- 依赖文件:STM32F103芯片对应的启动文件(startup_stm32f103xb.s)
1.2 核心文件获取
FreeRTOS源码可从官方网站下载(https://www.freertos.org/),解压后核心目录结构如下:
FreeRTOSv10.4.3/
├── FreeRTOS/
│ ├── Source/ # FreeRTOS核心源码
│ │ ├── croutine.c
│ │ ├── event_groups.c
│ │ ├── list.c
│ │ ├── queue.c
│ │ ├── stream_buffer.c
│ │ ├── tasks.c
│ │ ├── timers.c
│ │ ├── portable/ # 移植相关文件
│ │ │ ├── GCC/ # GCC编译器适配
│ │ │ ├── Keil/ # Keil编译器适配
│ │ │ ├── MemMang/ # 内存管理方案
│ │ └── include/ # 核心头文件
│ └── Demo/ # 官方示例(可参考)
二、移植整体流程
以下是FreeRTOS移植到STM32F103的完整流程,通过流程图清晰展示各步骤的依赖关系:
#mermaid-svg-1X3MS1I6wEzIcAU7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1X3MS1I6wEzIcAU7 .error-icon{fill:#552222;}#mermaid-svg-1X3MS1I6wEzIcAU7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1X3MS1I6wEzIcAU7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .marker.cross{stroke:#333333;}#mermaid-svg-1X3MS1I6wEzIcAU7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1X3MS1I6wEzIcAU7 p{margin:0;}#mermaid-svg-1X3MS1I6wEzIcAU7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster-label text{fill:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster-label span{color:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster-label span p{background-color:transparent;}#mermaid-svg-1X3MS1I6wEzIcAU7 .label text,#mermaid-svg-1X3MS1I6wEzIcAU7 span{fill:#333;color:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .node rect,#mermaid-svg-1X3MS1I6wEzIcAU7 .node circle,#mermaid-svg-1X3MS1I6wEzIcAU7 .node ellipse,#mermaid-svg-1X3MS1I6wEzIcAU7 .node polygon,#mermaid-svg-1X3MS1I6wEzIcAU7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .rough-node .label text,#mermaid-svg-1X3MS1I6wEzIcAU7 .node .label text,#mermaid-svg-1X3MS1I6wEzIcAU7 .image-shape .label,#mermaid-svg-1X3MS1I6wEzIcAU7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-1X3MS1I6wEzIcAU7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .rough-node .label,#mermaid-svg-1X3MS1I6wEzIcAU7 .node .label,#mermaid-svg-1X3MS1I6wEzIcAU7 .image-shape .label,#mermaid-svg-1X3MS1I6wEzIcAU7 .icon-shape .label{text-align:center;}#mermaid-svg-1X3MS1I6wEzIcAU7 .node.clickable{cursor:pointer;}#mermaid-svg-1X3MS1I6wEzIcAU7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .arrowheadPath{fill:#333333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1X3MS1I6wEzIcAU7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1X3MS1I6wEzIcAU7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1X3MS1I6wEzIcAU7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster text{fill:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 .cluster span{color:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1X3MS1I6wEzIcAU7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1X3MS1I6wEzIcAU7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-1X3MS1I6wEzIcAU7 .icon-shape,#mermaid-svg-1X3MS1I6wEzIcAU7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1X3MS1I6wEzIcAU7 .icon-shape p,#mermaid-svg-1X3MS1I6wEzIcAU7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1X3MS1I6wEzIcAU7 .icon-shape rect,#mermaid-svg-1X3MS1I6wEzIcAU7 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1X3MS1I6wEzIcAU7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1X3MS1I6wEzIcAU7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1X3MS1I6wEzIcAU7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
搭建STM32基础工程
获取FreeRTOS核心源码
复制FreeRTOS源码到工程目录
添加源码文件到Keil工程
配置头文件包含路径
修改FreeRTOS配置文件FreeRTOSConfig.h
修改STM32中断服务函数
适配系统时钟与任务调度
编写测试任务验证移植效果
编译下载验证功能
三、详细移植步骤
3.1 步骤1:搭建STM32F103基础工程
首先需要一个可正常编译、下载的STM32F103基础工程(以HAL库为例),包含以下核心文件:
- 启动文件:startup_stm32f103xb.s
- HAL库核心文件:stm32f1xx_hal.c、stm32f1xx_hal_gpio.c等
- 系统时钟配置:stm32f1xx_hal_rcc.c
- 中断服务函数:stm32f1xx_it.c
- 主函数文件:main.c
- 工程配置文件:stm32f1xx_hal_conf.h
基础工程需实现最基本的时钟配置(72MHz)、GPIO初始化,确保无编译错误,可正常下载到开发板。
3.2 步骤2:复制FreeRTOS源码到工程目录
在基础工程根目录下创建FreeRTOS文件夹,按以下目录结构复制源码:
STM32F103_FreeRTOS/
├── Core/
│ ├── Inc/
│ │ ├── FreeRTOS/ # 存放FreeRTOS头文件
│ │ └── …
│ ├── Src/
│ │ ├── FreeRTOS/ # 存放FreeRTOS源码文件
│ │ │ ├── portable/ # 仅保留Keil和MemMang子目录
│ │ │ ├── croutine.c
│ │ │ ├── event_groups.c
│ │ │ ├── list.c
│ │ │ ├── queue.c
│ │ │ ├── stream_buffer.c
│ │ │ ├── tasks.c
│ │ │ └── timers.c
│ │ └── …
└── FreeRTOSConfig.h # FreeRTOS配置文件(手动创建)
复制规则:
- 从FreeRTOS源码Source/include目录复制所有.h文件到工程Core/Inc/FreeRTOS
- 从FreeRTOS源码Source目录复制所有.c文件到工程Core/Src/FreeRTOS
- 从FreeRTOS源码Source/portable/Keil/ARM_CM3复制port.c和portmacro.h到工程Core/Src/FreeRTOS/portable
- 从FreeRTOS源码Source/portable/MemMang选择heap_4.c(推荐)复制到工程Core/Src/FreeRTOS/portable/MemMang
3.3 步骤3:添加源码文件到Keil工程
在Keil中创建以下分组,并添加对应文件:
| FreeRTOS_Core | croutine.c、event_groups.c、list.c、queue.c、stream_buffer.c、tasks.c、timers.c |
| FreeRTOS_Portable | port.c(来自ARM_CM3) |
| FreeRTOS_MemMang | heap_4.c(来自MemMang) |
操作方式:
3.4 步骤4:配置头文件包含路径
在Keil中配置头文件路径,确保编译器能找到FreeRTOS的头文件:
- ./Core/Inc/FreeRTOS
- ./Core/Src/FreeRTOS/portable/Keil/ARM_CM3
- ./Core/Src/FreeRTOS/portable/MemMang
3.5 步骤5:创建并配置FreeRTOSConfig.h
文件名:FreeRTOSConfig.h 存放路径:Core/Inc/FreeRTOS
该文件是FreeRTOS的核心配置文件,以下是适配STM32F103的完整配置代码:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* ————————– 内核配置 ————————– */
#define configUSE_PREEMPTION 1U // 启用抢占式调度
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0U // 禁用端口优化的任务选择(通用CM3)
#define configUSE_TICKLESS_IDLE 0U // 禁用低功耗空闲模式
#define configCPU_CLOCK_HZ (72000000U) // CPU主频72MHz
#define configTICK_RATE_HZ (1000U) // 系统滴答频率1kHz
#define configMAX_PRIORITIES (5U) // 最大任务优先级
#define configMINIMAL_STACK_SIZE (128U) // 最小任务栈大小
#define configTOTAL_HEAP_SIZE (1024U * 5) // 堆大小5KB
#define configMAX_TASK_NAME_LEN (16U) // 任务名最大长度
#define configUSE_16_BIT_TICKS 0U // 使用32位tick计数
#define configIDLE_SHOULD_YIELD 1U // 空闲任务让出CPU
#define configUSE_TASK_NOTIFICATIONS 1U // 启用任务通知
#define configUSE_MUTEXES 1U // 启用互斥锁
#define configUSE_RECURSIVE_MUTEXES 1U // 启用递归互斥锁
#define configUSE_COUNTING_SEMAPHORES 1U // 启用计数信号量
#define configQUEUE_REGISTRY_SIZE 8U // 队列注册表大小
#define configUSE_QUEUE_SETS 0U // 禁用队列集
#define configUSE_TIME_SLICING 1U // 启用时间片调度
#define configUSE_NEWLIB_REENTRANT 0U // 禁用Newlib重入
#define configENABLE_BACKWARD_COMPATIBILITY 0U // 禁用向后兼容
/* ————————– 钩子函数配置 ————————– */
#define configUSE_IDLE_HOOK 0U // 禁用空闲钩子函数
#define configUSE_TICK_HOOK 0U // 禁用滴答钩子函数
#define configCHECK_FOR_STACK_OVERFLOW 0U // 禁用栈溢出检测
#define configUSE_MALLOC_FAILED_HOOK 0U // 禁用内存分配失败钩子函数
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0U // 禁用守护任务启动钩子函数
/* ————————– 定时器配置 ————————– */
#define configUSE_TIMERS 1U // 启用软件定时器
#define configTIMER_TASK_PRIORITY (3U) // 定时器任务优先级
#define configTIMER_QUEUE_LENGTH (10U) // 定时器队列长度
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) // 定时器任务栈大小
/* ————————– 中断配置 ————————– */
#define configPRIO_BITS 4U // STM32F103使用4位优先级(0-15)
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15U // 库函数最低中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5U // 系统调用最高中断优先级
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 – configPRIO_BITS) )
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 – configPRIO_BITS) )
/* ————————– 函数重定义 ————————– */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
/* ————————– 包含头文件 ————————– */
#include "stm32f1xx_hal.h"
#endif /* FREERTOS_CONFIG_H */
3.6 步骤6:修改STM32中断服务函数
需要修改stm32f1xx_it.c文件,注释或替换与FreeRTOS冲突的中断服务函数:
文件名:stm32f1xx_it.c
#include "stm32f1xx_it.h"
#include "FreeRTOS.h"
#include "task.h"
// 注释原有的SVC_Handler、PendSV_Handler、SysTick_Handler函数
/*
void SVC_Handler(void)
{
}
void PendSV_Handler(void)
{
}
void SysTick_Handler(void)
{
HAL_IncTick();
}
*/
// 保留其他中断函数(如EXTI、USART等)
void NMI_Handler(void)
{
}
void HardFault_Handler(void)
{
while (1)
{
}
}
void MemManage_Handler(void)
{
while (1)
{
}
}
void BusFault_Handler(void)
{
while (1)
{
}
}
void UsageFault_Handler(void)
{
while (1)
{
}
}
// 其他中断函数保持不变…
3.7 步骤7:适配系统时钟与任务调度
修改stm32f1xx_hal.c文件中的HAL_InitTick函数,避免与FreeRTOS的SysTick冲突:
文件名:stm32f1xx_hal.c
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
// 注释原有的SysTick初始化代码,由FreeRTOS接管SysTick
/*
if (SysTick_Config(SystemCoreClock / (1000U / uwTickFreq)))
{
return HAL_ERROR;
}
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
*/
return HAL_OK;
}
// 添加获取系统tick的函数(可选)
uint32_t HAL_GetTick(void)
{
// 使用FreeRTOS的tick计数替代HAL的uwTick
return (uint32_t)xTaskGetTickCount();
}
3.8 步骤8:编写测试任务验证移植效果
修改main.c文件,编写两个测试任务(LED闪烁+串口打印),验证FreeRTOS是否正常运行:
文件名:main.c
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* 硬件初始化函数声明 */
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* 任务句柄 */
TaskHandle_t xTaskLEDHandle = NULL;
TaskHandle_t xTaskUARTHandle = NULL;
/* 任务函数声明 */
void vTaskLED(void *pvParameters);
void vTaskUART(void *pvParameters);
int main(void)
{
/* 1. 初始化HAL库 */
HAL_Init();
/* 2. 配置系统时钟为72MHz */
SystemClock_Config();
/* 3. 初始化硬件(LED、串口) */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* 4. 创建FreeRTOS任务 */
xTaskCreate(vTaskLED, // 任务函数
"TaskLED", // 任务名
128, // 任务栈大小
NULL, // 任务参数
1, // 任务优先级
&xTaskLEDHandle); // 任务句柄
xTaskCreate(vTaskUART, // 任务函数
"TaskUART", // 任务名
128, // 任务栈大小
NULL, // 任务参数
2, // 任务优先级
&xTaskUARTHandle); // 任务句柄
/* 5. 启动任务调度器 */
vTaskStartScheduler();
/* 如果程序运行到这里,说明任务调度器启动失败(如堆内存不足) */
while (1)
{
}
}
/**
* @brief LED闪烁任务
* @param pvParameters 任务参数
* @retval None
*/
void vTaskLED(void *pvParameters)
{
UNUSED(pvParameters);
for (;;)
{
/* 翻转LED引脚电平(假设LED接PA0) */
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
/* 延时500ms(FreeRTOS延时函数,不会占用CPU) */
vTaskDelay(pdMS_TO_TICKS(500));
}
}
/**
* @brief 串口打印任务
* @param pvParameters 任务参数
* @retval None
*/
void vTaskUART(void *pvParameters)
{
UNUSED(pvParameters);
uint32_t uartCount = 0;
for (;;)
{
/* 串口打印信息 */
HAL_UART_Transmit(&huart1, (uint8_t*)"FreeRTOS移植成功!计数:", 22, 100);
/* 转换数字为字符串并打印 */
char countBuf[10] = {0};
sprintf(countBuf, "%d\\r\\n", uartCount++);
HAL_UART_Transmit(&huart1, (uint8_t*)countBuf, strlen(countBuf), 100);
/* 延时1000ms */
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/**
* @brief GPIO初始化函数(LED接PA0)
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 启用GPIOA时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 配置PA0为推挽输出 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 初始电平为低 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
}
/**
* @brief USART1初始化函数(115200 8N1)
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 系统时钟配置函数(72MHz)
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 配置HSE振荡器 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 配置系统时钟 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 错误处理函数
* @param None
* @retval None
*/
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
3.9 步骤9:编译与下载验证
- LED灯以500ms为周期闪烁
- 串口助手(115200 8N1)每隔1秒收到一行打印信息:FreeRTOS移植成功!计数:x
四、常见问题与解决方法
4.1 编译错误:undefined reference to vPortSVCHandler
- 原因:FreeRTOSConfig.h中定义的中断函数名与STM32中断向量表不匹配
- 解决:确认vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler的定义与中断函数名一致
4.2 任务调度器启动失败
- 原因:configTOTAL_HEAP_SIZE配置过小,不足以创建任务
- 解决:增大configTOTAL_HEAP_SIZE(如改为1024*10=10KB)
4.3 LED不闪烁/串口无输出
- 原因1:系统时钟配置错误(非72MHz),导致延时函数计时不准
- 解决1:检查SystemClock_Config函数,确保HSE和PLL配置正确
- 原因2:中断优先级配置错误,FreeRTOS中断优先级需低于系统调用阈值
- 解决2:确认configMAX_SYSCALL_INTERRUPT_PRIORITY配置正确


