FreeRTOS 任务调试问题
생성일: 2025년 1월 8일
생성일: 2025년 1월 8일
我的stm32f103c8t6为什么同时运行2的任务就开始不成功了?是不是内存爆了?但是我看教学视频他是可以运行的,我是debug没有设置号码?我学习的是stm32freertos的教学,你可以帮助我解决问题嘛?我的代码:/* USER CODE BEGIN Header / #include "driver_led.h" #include "driver_lcd.h" #include "driver_mpu6050.h" #include "driver_timer.h" #include "driver_ds18b20.h" #include "driver_dht11.h" #include "driver_active_buzzer.h" #include "driver_passive_buzzer.h" #include "driver_color_led.h" #include "driver_ir_receiver.h" #include "driver_ir_sender.h" #include "driver_light_sensor.h" #include "driver_ir_obstacle.h" #include "driver_ultrasonic_sr04.h" #include "driver_spiflash_w25q64.h" #include "driver_rotary_encoder.h" #include "driver_motor.h" #include "driver_key.h" #include "driver_uart.h" /* ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** / / USER CODE END Header / / Includes ------------------------------------------------------------------/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" / Private includes ----------------------------------------------------------/ / USER CODE BEGIN Includes / / USER CODE END Includes / / Private typedef -----------------------------------------------------------/ / USER CODE BEGIN PTD / / USER CODE END PTD / / Private define ------------------------------------------------------------/ / USER CODE BEGIN PD / / USER CODE END PD / / Private macro -------------------------------------------------------------/ / USER CODE BEGIN PM / / USER CODE END PM / / Private variables ---------------------------------------------------------/ / USER CODE BEGIN Variables / static StackType_t g_pucStackOfLightTask[128]; static StaticTask_t g_TCBofLightTask; static TaskHandle_t xLightTaskHandle; static StackType_t g_pucStackOfColorTask[128]; static StaticTask_t g_TCBofColorTask; static TaskHandle_t xColorTaskHandle; / USER CODE END Variables / / Definitions for defaultTask / osThreadId_t defaultTaskHandle; const osThreadAttr_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityNormal, }; / Private function prototypes -----------------------------------------------/ / USER CODE BEGIN FunctionPrototypes */ struct TaskPrintInfo { uint8_t x; uint8_t y; char name[16]; }; static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"}; static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"}; static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"}; static int g_LCDCanUse = 1; void LcdPrintTask(void params) { struct TaskPrintInfo pInfo = params; uint32_t cnt = 0; int len; while (1) { / ղӡхϢ / if (g_LCDCanUse) { g_LCDCanUse = 0; len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name); len += LCD_PrintString(len, pInfo->y, ":"); LCD_PrintSignedVal(len, pInfo->y, cnt++); g_LCDCanUse = 1; } mdelay(500); } } / USER CODE END FunctionPrototypes / void StartDefaultTask(void argument); void MX_FREERTOS_Init(void); / (MISRA C 2004 rule 8.1) / / * @brief FreeRTOS initialization * @param None * @retval None / void MX_FREERTOS_Init(void) { / USER CODE BEGIN Init / TaskHandle_t xSoundTaskHandle; BaseType_t ret; LCD_Init(); LCD_Clear(); / USER CODE END Init / / USER CODE BEGIN RTOS_MUTEX / / add mutexes, ... / / USER CODE END RTOS_MUTEX / / USER CODE BEGIN RTOS_SEMAPHORES / / add semaphores, ... / / USER CODE END RTOS_SEMAPHORES / / USER CODE BEGIN RTOS_TIMERS / / start timers, add new ones, ... / / USER CODE END RTOS_TIMERS / / USER CODE BEGIN RTOS_QUEUES / / add queues, ... / / USER CODE END RTOS_QUEUES / / Create the thread(s) / / creation of defaultTask / //defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); / USER CODE BEGIN RTOS_THREADS / / add threads, ... / / ԴݨɎϱ: ʹ / extern void PlayMusic(void params); //ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal, &xSoundTaskHandle); / ԴݨɎϱ: ڢ / //xLightTaskHandle = xTaskCreateStatic(Led_Test, "LightTask", 128, NULL, osPriorityNormal, g_pucStackOfLightTask, &g_TCBofLightTask); / ԴݨɎϱ: ɫ / //xColorTaskHandle = xTaskCreateStatic(ColorLED_Test, "ColorTask", 128, NULL, osPriorityNormal, g_pucStackOfColorTask, &g_TCBofColorTask); / ʹԃͬһٶگ˽ԴݨһͬքɎϱ / xTaskCreate(LcdPrintTask, "task1", 128, &g_Task1Info, osPriorityNormal, NULL); xTaskCreate(LcdPrintTask, "task2", 128, &g_Task2Info, osPriorityNormal, NULL); xTaskCreate(LcdPrintTask, "task3", 128, &g_Task3Info, osPriorityNormal, NULL); / USER CODE END RTOS_THREADS / / USER CODE BEGIN RTOS_EVENTS / / add events, ... / / USER CODE END RTOS_EVENTS / } / USER CODE BEGIN Header_StartDefaultTask / / * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None / / USER CODE END Header_StartDefaultTask / void StartDefaultTask(void argument) { / USER CODE BEGIN StartDefaultTask / / Infinite loop / LCD_Init(); LCD_Clear(); for(;;) { //Led_Test(); //LCD_Test(); //MPU6050_Test(); //DS18B20_Test(); //DHT11_Test(); //ActiveBuzzer_Test(); //PassiveBuzzer_Test(); //ColorLED_Test(); IRReceiver_Test(); / Ӱ / //IRSender_Test(); //LightSensor_Test(); //IRObstacle_Test(); //SR04_Test(); //W25Q64_Test(); //RotaryEncoder_Test(); //Motor_Test(); //Key_Test(); //UART_Test(); } / USER CODE END StartDefaultTask / } / Private application code --------------------------------------------------/ / USER CODE BEGIN Application / / USER CODE END Application */
在 STM32F103C8T6 上使用 FreeRTOS 同时跑多个任务时,如果出现“跑不起来”或者“看似内存爆了”的情况,常见的排查思路主要集中在以下几方面:
FreeRTOSConfig.h
中的 configTOTAL_HEAP_SIZE
大小xTaskCreate
)的方式,需要保证 configTOTAL_HEAP_SIZE
足够大。CubeMX 默认生成的 FreeRTOSConfig.h
,configTOTAL_HEAP_SIZE
往往设置得比较小(比如 768、1024 等),需要根据自己创建任务的数量和任务堆栈来适当加大。xTaskCreateStatic
),则需要看静态数组(如你代码里的 g_pucStackOfLightTask[128]
)是不是过小或过大,也要留意系统中剩余的可用 RAM。128
堆栈。要注意这里的 128
通常指 以 32 位字(word)为单位(具体要看 FreeRTOS 的移植配置),也就是 128 * 4 = 512 字节。如果一个任务在运行过程中会调用比较多的函数或者操作 LCD,需要更多局部变量,那么 512 字节可能就不够。128
调大一点,比如改成 256
(即 1KB)或 512
(即 2KB),再测试是否能正常运行。当然也要留意总的内存占用别超了。main()
里先调用 MX_FREERTOS_Init()
创建任务,然后调用 vTaskStartScheduler()
开始调度。如果你把某些默认任务注释掉了,或者改了 main()
结构,要确保最后仍然有 vTaskStartScheduler()
被调用,否则不会真正进入多任务调度。你提到“我看教学视频可以跑多个任务,但我 debug 没有设置号码”,这可能指的是 硬件断点 或 观察点(watchpoint) 数量的问题。
HAL_Delay
),在某些 FreeRTOS 移植环境下可能会导致冲突。建议使用 RTOS 提供的 vTaskDelay
或者 osDelay
。LcdPrintTask
里如果不小心访问了非法地址,也会引发硬故障 (HardFault),导致程序停止。g_LCDCanUse
是个全局变量,三个任务都在用它保护 LCD。如果你在其它地方也直接操作 LCD,可能会产生冲突,导致某些任务意外卡住。(启用 heap_4.c
/ heap_5.c
) 在 FreeRTOSConfig 中指定合适的堆分配算法,如果用的是 heap_4.c
,可以在任务创建失败时方便地断点调试。
(启用内存使用监测) 在 FreeRTOSConfig.h
中启用 configUSE_MALLOC_FAILED_HOOK
、configCHECK_FOR_STACK_OVERFLOW
等宏,并实现对应的 Hook 函数,例如:
cvoid vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { // stack overflow 时会进这里 for(;;); } void vApplicationMallocFailedHook(void) { // malloc fail 时会进这里 for(;;); }
(查看 .map 文件或使用 FreeRTOS API)
.map
文件,看看静态分配的数组是否已经把内存占满了。xPortGetFreeHeapSize()
、xPortGetMinimumEverFreeHeapSize()
(或者 uxTaskGetStackHighWaterMark()
) 来观测实时剩余堆大小及任务栈剩余情况。恢复最简工程验证
检查 FreeRTOS 调度是否真正启动
main()
里一定要有 MX_FREERTOS_Init();
和 vTaskStartScheduler();
。defaultTask
创建,也要确保还有别的 RTOS 任务能把系统“带起来”。减小不必要的外设/全局变量占用
driver_xxx.h
,看看是否占用了过多 RAM(比如某些驱动里有大数组)。启用 Debug Hook
FreeRTOSConfig.h
里启用 configUSE_MALLOC_FAILED_HOOK
和 configCHECK_FOR_STACK_OVERFLOW
,实现相应的 Hook 函数,看看程序是否真的因为堆分配失败或栈溢出而崩溃。调大或减小任务堆栈
清理硬件断点
configTOTAL_HEAP_SIZE
和任务栈都检查好,再试试两三个任务是否能正常运行。这样一步步排查下来,就能确定到底是哪里“爆了”,或者哪里出了资源冲突。祝调试顺利!
再增加第二个任务 “task2”,看是否还能正常工作,若不行就调大栈、调大堆,或者去掉一些外设驱动再试。:“我在第二个就不能同时运行,怎么解决的提供代码给我
下面给你一个最小可行示例的参考代码,用于在 STM32F103C8T6 上使用 FreeRTOS 同时跑两个任务。你可以把它整合到自己的工程里,对比一下和你现有工程的差异,逐步改到能跑起来为止。
在你工程的 FreeRTOSConfig.h
里,至少要有如下设置(可根据需要调整数值):
c#define configTOTAL_HEAP_SIZE ( ( size_t ) (4 * 1024) ) // 先设置成 4KB,看能否正常运行。如果还有内存不足问题,可以继续调大, // 但是要注意 STM32F103C8T6 只有 20KB RAM,别超过可用范围。 #define configMINIMAL_STACK_SIZE (128) // 最小堆栈大小,这里仅做参考,后续根据需要可调。 // 如果要检查栈溢出,需要开启下面两个配置 #define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_MALLOC_FAILED_HOOK 1
另外,如果你的工程使用了 静态创建(xTaskCreateStatic
+ 数组),就不使用 configTOTAL_HEAP_SIZE
中的堆,而是用你分配的全局数组。但还是要留意全局数组也占RAM,总量不能超过芯片容量。
下面是一段示例代码,你可以直接放到类似 freertos.c
文件中(或新建一个 .c
文件),然后在 main()
中调用 vTaskStartScheduler()
(或者用 CubeMX 的方式调用),就可以看到两个任务同时运行。一个任务每隔 500ms 闪一次 LED,另一个任务每隔 1000ms 在 LCD 上打印一句话。
假设你有一个简单的 LED 驱动(
LED_Init()
、LED_Toggle()
),以及一个简单的 LCD 驱动(LCD_Init()
、LCD_PrintString()
)。具体函数名请根据你自己的驱动更改。
c#include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" // 假设你有这两个驱动头文件 #include "driver_led.h" #include "driver_lcd.h" /* 用来演示的两个任务 */ // 任务1:LED 闪烁 void Task1_LED(void *pvParameters) { (void)pvParameters; // 初始化 LED LED_Init(); for (;;) { LED_Toggle(); // 翻转 LED 状态 vTaskDelay(pdMS_TO_TICKS(500)); // 延时 500ms } } // 任务2:LCD 显示 void Task2_LCD(void *pvParameters) { (void)pvParameters; // 初始化 LCD LCD_Init(); LCD_Clear(); for (;;) { LCD_PrintString(0, 0, "Hello Task2"); vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000ms } } /** * @brief FreeRTOS 初始化函数 * 在这里创建任务,然后等待调度器启动 */ void MX_FREERTOS_Init(void) { // 可以先不创建 CubeMX 默认的 defaultTask,把它注释了也行 // defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); // 创建任务1(LED) xTaskCreate( Task1_LED, // 任务入口函数 "Task1_LED", // 任务名称 128, // 任务堆栈大小(单位:word,如果是 32bit MCU,128 = 512字节) NULL, // 传给任务的参数 osPriorityNormal, // 优先级 NULL // 任务句柄(不需要时传 NULL) ); // 创建任务2(LCD) xTaskCreate( Task2_LCD, "Task2_LCD", 128, // 任务堆栈大小 NULL, osPriorityNormal, NULL ); // 如果你是手动写 main(),记得最后要调用 vTaskStartScheduler(); // 如果是用 CubeMX 自动生成 main(),看它的 main() 里通常已经有这句了。 }
然后,在你的 main()
函数中,通常会有这样的一段:
cint main(void) { // 1. 硬件初始化 (HAL_Init、SystemClock_Config、引脚配置等) HAL_Init(); SystemClock_Config(); // 2. FreeRTOS 任务创建和调度 MX_FREERTOS_Init(); // 创建任务 vTaskStartScheduler(); // 启动调度 // 如果一切正常,程序不会运行到这里 while (1) { } }
只要保证上述流程正确、堆大小够用、每个任务的栈足够,两个任务就能并发执行。
FreeRTOSConfig.h
中启用:
c#define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_MALLOC_FAILED_HOOK 1
freertos.c
)实现 Hook 函数:
cvoid vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { // 如果发生栈溢出,会进这里,可以打断点或者用 LED 提示 for(;;); } void vApplicationMallocFailedHook(void) { // 如果申请内存失败,会进这里 for(;;); }
这样,如果发生栈溢出或创建任务失败,程序会在这些 Hook 里卡住,方便你用调试器查看问题。
没有启动调度器
main()
最后调用 vTaskStartScheduler()
,否则任务不会被真正调度。硬件断点过多
中断优先级或配置错误
LCD 或其他外设冲突
configTOTAL_HEAP_SIZE
到 8KB 再试。这样做,你就可以轻松地同时运行 2 个任务,甚至 3、4 个任务都没有问题。祝你调试顺利!