FreeRTOS 任务调试问题

Creato il: 8 gennaio 2025

Risposto usando O1 da Chat01

Domanda

我的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 */

Pensiero

Identifying potential issues

I’m considering possible causes for unsuccessful coexistence of tasks in FreeRTOS, like inadequate memory, missing heap configuration, or lack of vTaskStartScheduler().

Ensuring proper configuration

I’m noting the need to verify task names, confirm HAL library initialization, and check configTOTAL_HEAP_SIZE for adequate dynamic memory in FreeRTOS.

Uncovering debugging clues

The user might be facing memory overflow due to limited stack size or improper debugger settings. Verifying heap and stack configurations or increasing the debugger breakpoints could help.

Addressing potential issues

The STM32F103C8T6 might lack sufficient memory, so reducing stack size, task count, or tweaking FreeRTOS configurations could help. Ensuring task and heap configurations are also crucial.

Assessing resource allocation

I'm gathering info on confirming configTOTAL_HEAP_SIZE in FreeRTOSConfig.h, reducing default task stack, avoiding infinite loops, and ensuring proper debugger configuration and task management.

Verifying task initiation

I'm digging into ensuring vTaskStartScheduler is called in main, checking error messages, and assessing default FreeRTOS config total heap size for task management.

Maximizing resources

Consider increasing heap size, optimizing stack usage, and streamlining tasks. Using HAL_Delay or excessive local variables might hinder progress.

Thought about STM32 memory and task issues,持续 35 秒

Risposta

在 STM32F103C8T6 上使用 FreeRTOS 同时跑多个任务时,如果出现“跑不起来”或者“看似内存爆了”的情况,常见的排查思路主要集中在以下几方面:


1. 任务和堆栈配置是否合理

1.1 检查 FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZE 大小

  • STM32F103C8T6 的 RAM 只有 20KB(有的芯片是 10KB,看具体型号),如果你的 FreeRTOS 配置和其他外设使用的全局变量占用过大,很可能会导致堆内存不足,从而任务创建失败或者运行中崩溃。
  • 如果是使用 动态创建xTaskCreate)的方式,需要保证 configTOTAL_HEAP_SIZE 足够大。CubeMX 默认生成的 FreeRTOSConfig.hconfigTOTAL_HEAP_SIZE 往往设置得比较小(比如 768、1024 等),需要根据自己创建任务的数量和任务堆栈来适当加大。
  • 如果是 静态创建xTaskCreateStatic),则需要看静态数组(如你代码里的 g_pucStackOfLightTask[128])是不是过小或过大,也要留意系统中剩余的可用 RAM。

1.2 任务堆栈大小(stack size)是否足够

  • 你在代码中给每个任务分配了 128 堆栈。要注意这里的 128 通常指 以 32 位字(word)为单位(具体要看 FreeRTOS 的移植配置),也就是 128 * 4 = 512 字节。如果一个任务在运行过程中会调用比较多的函数或者操作 LCD,需要更多局部变量,那么 512 字节可能就不够。
  • 可以尝试把 128 调大一点,比如改成 256(即 1KB)或 512(即 2KB),再测试是否能正常运行。当然也要留意总的内存占用别超了。

1.3 检查是否真的启动了调度器

  • 在使用 CubeMX 或者其他自动生成代码的工程里,通常会在 main() 里先调用 MX_FREERTOS_Init() 创建任务,然后调用 vTaskStartScheduler() 开始调度。如果你把某些默认任务注释掉了,或者改了 main() 结构,要确保最后仍然有 vTaskStartScheduler() 被调用,否则不会真正进入多任务调度。

2. Debug 配置及硬件断点数量限制

你提到“我看教学视频可以跑多个任务,但我 debug 没有设置号码”,这可能指的是 硬件断点观察点(watchpoint) 数量的问题。

  • STM32F1 系列的硬件断点通常只有 4~6 个左右(具体看内核和工具链)。如果你的工程里下了太多断点,会导致调试出现各种奇怪问题,比如断点失效、程序跑飞等等。
  • 可以试着在调试前把断点清空,或者尽量只留一个主断点,看能不能正常跑起来。

3. 任务是否因其他原因卡死或崩溃

3.1 死循环 / 看门狗 / 异常中断等

  • 如果某个任务里使用了阻塞式延时(如 HAL_Delay),在某些 FreeRTOS 移植环境下可能会导致冲突。建议使用 RTOS 提供的 vTaskDelay 或者 osDelay
  • 如果使用了硬件看门狗却没有喂狗,也会导致系统复位,从而看起来“任务跑不起来”。

3.2 是否有访问越界(如数组越界)等潜在问题

  • 比如你的 LcdPrintTask 里如果不小心访问了非法地址,也会引发硬故障 (HardFault),导致程序停止。

3.3 共享资源使用不当

  • 你代码里的 g_LCDCanUse 是个全局变量,三个任务都在用它保护 LCD。如果你在其它地方也直接操作 LCD,可能会产生冲突,导致某些任务意外卡住。
  • 建议使用 FreeRTOS 提供的互斥量 (Mutex) 或信号量 (Semaphore) 来保护 LCD 资源,更加稳妥。

4. 如何快速验证“是否内存不够”

  1. (启用 heap_4.c / heap_5.c 在 FreeRTOSConfig 中指定合适的堆分配算法,如果用的是 heap_4.c,可以在任务创建失败时方便地断点调试。

  2. (启用内存使用监测)FreeRTOSConfig.h 中启用 configUSE_MALLOC_FAILED_HOOKconfigCHECK_FOR_STACK_OVERFLOW 等宏,并实现对应的 Hook 函数,例如:

    c
    void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { // stack overflow 时会进这里 for(;;); } void vApplicationMallocFailedHook(void) { // malloc fail 时会进这里 for(;;); }
  3. (查看 .map 文件或使用 FreeRTOS API)

    • 查看编译后的 .map 文件,看看静态分配的数组是否已经把内存占满了。
    • 也可以在任务里用 xPortGetFreeHeapSize()xPortGetMinimumEverFreeHeapSize() (或者 uxTaskGetStackHighWaterMark()) 来观测实时剩余堆大小及任务栈剩余情况。

5. 建议的排查步骤

  1. 恢复最简工程验证

    • 先只保留一个任务,比如 “task1”。确认单个任务可以正常驱动 LCD 打印。
    • 再增加第二个任务 “task2”,看是否还能正常工作,若不行就调大栈、调大堆,或者去掉一些外设驱动再试。
    • 如果加到第三个任务就出问题,说明确实是内存/栈不够或资源冲突的问题。
  2. 检查 FreeRTOS 调度是否真正启动

    • main() 里一定要有 MX_FREERTOS_Init();vTaskStartScheduler();
    • 若你注释掉了 defaultTask 创建,也要确保还有别的 RTOS 任务能把系统“带起来”。
  3. 减小不必要的外设/全局变量占用

    • 你包含了很多 driver_xxx.h,看看是否占用了过多 RAM(比如某些驱动里有大数组)。
    • 如果是学习阶段,可以逐个测试外设,不要一次性都加上。先确认 FreeRTOS 正常多任务调度没问题,再逐步加外设。
  4. 启用 Debug Hook

    • FreeRTOSConfig.h 里启用 configUSE_MALLOC_FAILED_HOOKconfigCHECK_FOR_STACK_OVERFLOW,实现相应的 Hook 函数,看看程序是否真的因为堆分配失败或栈溢出而崩溃。
  5. 调大或减小任务堆栈

    • 如果你确定需要更多栈,就调大一些(例如 256、512),但要注意总内存。
    • 如果任务里只是简单打印,可以先把堆栈降到 64 / 96,看看是否反而能够成功创建更多任务,也可间接说明是不是有其它地方占用太多。
  6. 清理硬件断点

    • 进入调试前,可以清空所有断点,或只留一个主断点看看情况。

小结

  • 大概率就是内存(堆栈或 FreeRTOS 堆)不够,或者 没有正确启动调度器
  • 先从简到繁,减少不必要的外设和任务,把 configTOTAL_HEAP_SIZE 和任务栈都检查好,再试试两三个任务是否能正常运行。
  • 如果确认是断点导致的问题,也可以在调试配置里把软件断点数量调大(某些工具链可用 SW Breakpoint 代替 HW Breakpoint)或者减少断点使用。

这样一步步排查下来,就能确定到底是哪里“爆了”,或者哪里出了资源冲突。祝调试顺利!

Domanda

再增加第二个任务 “task2”,看是否还能正常工作,若不行就调大栈、调大堆,或者去掉一些外设驱动再试。:“我在第二个就不能同时运行,怎么解决的提供代码给我

Pensiero

Crafting the example

I’m preparing a minimal FreeRTOS example for the user. This snippet will illustrate setting configTOTAL_HEAP_SIZE to 4 K, and creating two tasks—one to toggle an LED and another to print on an LCD.

Crafting tasks

I’m creating two FreeRTOS tasks: one to toggle an LED every 500 ms and another to print on an LCD every 1000 ms. The code will include "FreeRTOS.h" and "task.h", and a minimal example will be provided for the STM32CUBEMX environment.

Preparing the snippet

I’m drafting a minimal code snippet for creating tasks with xTaskCreate in STM32CUBEMX, ensuring vTaskStartScheduler is called, and setting configTOTAL_HEAP_SIZE to 4K or 8K.

Aligning configurations

I’m including directives like "USE_FULL_ASSERT" and "configCHECK_FOR_STACK_OVERFLOW", and showing how to define "vApplicationStackOverflowHook" and "vApplicationMallocFailedHook" in the FreeRTOS example.

Thought about task creation troubleshooting,持续 28 秒

Risposta

下面给你一个最小可行示例的参考代码,用于在 STM32F103C8T6 上使用 FreeRTOS 同时跑两个任务。你可以把它整合到自己的工程里,对比一下和你现有工程的差异,逐步改到能跑起来为止。


1. 修改 FreeRTOSConfig.h 以保证堆够用

在你工程的 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,总量不能超过芯片容量。


2. 一个简化的双任务示例

下面是一段示例代码,你可以直接放到类似 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() 函数中,通常会有这样的一段:

c
int main(void) { // 1. 硬件初始化 (HAL_Init、SystemClock_Config、引脚配置等) HAL_Init(); SystemClock_Config(); // 2. FreeRTOS 任务创建和调度 MX_FREERTOS_Init(); // 创建任务 vTaskStartScheduler(); // 启动调度 // 如果一切正常,程序不会运行到这里 while (1) { } }

只要保证上述流程正确、堆大小够用、每个任务的栈足够,两个任务就能并发执行。


2.1 若要检查是否栈溢出或内存不足

  1. FreeRTOSConfig.h 中启用:
    c
    #define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_MALLOC_FAILED_HOOK 1
  2. 在某个 C 文件里(比如 freertos.c)实现 Hook 函数:
    c
    void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { // 如果发生栈溢出,会进这里,可以打断点或者用 LED 提示 for(;;); } void vApplicationMallocFailedHook(void) { // 如果申请内存失败,会进这里 for(;;); }

这样,如果发生栈溢出或创建任务失败,程序会在这些 Hook 里卡住,方便你用调试器查看问题。


3. 常见问题排查

  1. 没有启动调度器

    • 一定要在 main() 最后调用 vTaskStartScheduler(),否则任务不会被真正调度。
  2. 硬件断点过多

    • STM32F1 系列硬件断点数量有限,若你在 Keil/IAR 等 IDE 下了过多断点,可能会导致莫名其妙的调试异常。把不必要的断点删掉再试。
  3. 中断优先级或配置错误

    • 某些外设中断占用了过高优先级,也可能干扰到 FreeRTOS 的 Systick 或 PendSV 中断。通常要让 FreeRTOS 的中断优先级设置为最低(比如 15),其他外设要高一点(数值更小),具体要注意 STM32 NVIC 的优先级分组。
  4. LCD 或其他外设冲突

    • 如果你还跑了 MPU6050、DHT11 等一堆外设,可能占用过多堆或全局变量,或者互相打架。建议先用最小工程验证 FreeRTOS + LED + LCD 的双任务能否正常跑,然后再一点点加其他外设,哪里加上就出问题,就重点排查哪里。

4. 小结

  • 以上示例代码可以保证两任务同时运行,如果你在加第二个任务时就“跑不起来”,多数是 堆栈大小或堆大小不够,或者 没正确启动调度
  • 先用最简示例来排除问题;如果在这个示例也跑不起来,就进一步缩减(不初始化 LCD、只闪 LED),或者调大 configTOTAL_HEAP_SIZE 到 8KB 再试。
  • 待这个“双任务示例”OK之后,再把你的原有外设驱动和逻辑一点点加回去,看看是哪个地方占用太多内存或导致冲突。这样就能一步步定位到具体原因。

这样做,你就可以轻松地同时运行 2 个任务,甚至 3、4 个任务都没有问题。祝你调试顺利!

Condividi questa Q&A