问题现象
在一个有两个任务且能正常运行的FreeRTOS工程中,再次添加一个新任务会出现新的任务无法运行(进入)的现象。如下图所示,
- 旧任务(可正常运行):OLED任务、LED任务
- 新添加任务(无法运行):串口测试任务
一、问题验证
初步怀疑是在创建任务时出现了问题,因此可以利用任务创建函数的返回值,来监控任务创建的情况。返回值xReturn有以下三种类型:
/* FreeRTOS error definitions. */
#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 )
#define errQUEUE_BLOCKED ( -4 )
#define errQUEUE_YIELD ( -5 )
除上述情况还有pdPASS,代表创建任务成功,实际上任务创建函数只会返回以下两种情况
- pdPASS(成功)
- errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(内存不足)
- 其他错误码(如errQUEUE_BLOCKED、errQUEUE_YIELD)属于队列操作函数的返回值,与任务创建无关
于是创建一个变量来读取返回值,并在Debug中对该变量进行监控。
volatile BaseType_t UARTtask_Create_status = 0;
UARTtask_Create_status = xTaskCreate((TaskFunction_t )UART_task,
(const char* )"UART_task",
(uint16_t )UART_STK_SIZE,
(void* )NULL,
(UBaseType_t )UART_TASK_PRIO,
(TaskHandle_t* )&UARTtask_Handler);
Debug结果如下图:
可以看到返回值为-1,创建该任务内存不足,接着打开FreeRTOSConfig.h文件。
修改其中的configTOTAL_HEAP_SIZE宏定义。
#define configTOTAL_HEAP_SIZE ((size_t)3072)
||
\/
#define configTOTAL_HEAP_SIZE ((size_t)2*3072)
根据自己的情况来修改,我这个工程只是个Demo,所以我索性翻倍了。实际上需要根据任务、队列等资源需求合理配置,否则会导致内存耗尽。
修改后的结果如下:
二、导致内存不足的原因
该问题主要是因为任务创建的时,任务栈空间设置与FreeRTOS堆空间分配冲突导致的。所有任务及内核变量加在一起所占的大小已经超出了分配的堆空间。
-
任务栈(Stack)
FreeRTOS中 xTaskCreate() 函数的栈大小参数以字(word)为单位,而非字节(byte)。
对于32位MCU(如STM32):1字 = 4字节
计算示例
若栈大小设置为150(单位:字),在不同架构下的内存占用为:
32位系统: 150字 x 4字节 = 600字节
16位系统: 150字 x 2字节 = 300字节 -
任务控制块(TCB)
TCB内存占用:
每个任务的控制块(TCB)是FreeRTOS管理任务的核心数据结构,其大小与具体配置和版本有关。
典型大小:
在32位系统中,TCB通常占用约 80~120字节(具体取决于FreeRTOSConfig.h中的配置,如是否启用调试信息)。 -
总内存占用计算
假设一个任务创建时栈大小参数所填150(单位:字),那么这个任务在创建时所占堆空间为:
总内存占用 = 栈空间 + TCB
总内存 ≈ 600字节(栈)+100字节(TCB)=700字节
我的任务设置如下:分别包括三个应用任务,栈空间各为150,初始任务200,软件定时任务。需要注意的是初始任务的空间是在创建完所有任务之后才会释放。
那么总内存大小约为:700*3+900 = 3000字节,而除此之外还有软件定时及其他FreeRTOS内核变量。最开始我配置的堆空间是3072,显然是不够用的,所以就导致了最后一个任务创建失败。
三、其他可能的原因
-
1、任务优先级冲突: 新任务的优先级设置不当,导致无法被调度。
高优先级任务阻塞:若新任务优先级与已有高优先级任务相同或更低,且高优先级任务未主动释放CPU(如未调用vTaskDelay或等待信号量),则低优先级任务无法运行。
空闲任务被占用:若所有任务优先级均高于空闲任务(优先级0),且没有任务主动释放CPU,会导致系统卡死。 -
2、任务函数实现问题: 任务函数逻辑错误(如死循环未释放CPU、未调用任何阻塞API)。
-
3、中断优先级冲突: 若新任务涉及中断服务,中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY,会导致任务切换异常。