在FreeRTOS新创建的任务无法进入


问题现象

在一个有两个任务且能正常运行的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堆空间分配冲突导致的。所有任务及内核变量加在一起所占的大小已经超出了分配的堆空间。

  1. 任务栈(Stack)

    FreeRTOS中 xTaskCreate() 函数的栈大小参数以字(word)为单位,而非字节(byte)。
    对于32位MCU(如STM32):1字 = 4字节
    计算示例
    若栈大小设置为150(单位:字),在不同架构下的内存占用为:
    32位系统: 150字 x 4字节 = 600字节
    16位系统: 150字 x 2字节 = 300字节

  2. 任务控制块(TCB)

    TCB内存占用:
    每个任务的控制块(TCB)是FreeRTOS管理任务的核心数据结构,其大小与具体配置和版本有关。
    典型大小:
    在32位系统中,TCB通常占用约 80~120字节(具体取决于FreeRTOSConfig.h中的配置,如是否启用调试信息)。

  3. 总内存占用计算
    假设一个任务创建时栈大小参数所填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,会导致任务切换异常。

### 可能的原因及解决方法 在使用 STM32 HAL 库和 FreeRTOS 时,如果创建任务无法进入,可能由以下几个原因引起。以下是详细的分析和解决方法: #### 1. **任务优先级设置错误** FreeRTOS任务调度依赖于任务的优先级设置。如果新创建任务优先级低于默认任务(或其他正在运行的任务),并且系统中没有其他更高优先级的任务需要调度,则可能导致新任务无法被调度执行。 - 确保新任务的优先级设置合理。例如,在 STM32CubeMX 中,可以通过任务创建窗口设置优先级[^1]。 - 如果手动编写代码创建任务,确保 `osPriority` 参数设置正确。例如: ```c osThreadAttr_t attr; attr.priority = osPriorityNormal; // 设置合理的优先级 attr.stack_size = 128 * 4; // 设置堆栈大小 attr.name = "Task1"; osThreadNew((osThreadFunc_t)Task1, NULL, &attr); ``` #### 2. **堆栈大小不足** 如果任务的堆栈大小设置过小,可能导致任务运行时发生堆栈溢出,从而无法正常进入任务。 - 在 STM32CubeMX 中,检查任务创建时的堆栈大小设置是否足够[^1]。 - 如果手动编写代码创建任务,确保为任务分配足够的堆栈空间。例如: ```c attr.stack_size = 512 * 4; // 增加堆栈大小 ``` #### 3. **未启用 FreeRTOS 或时钟配置错误** 如果在 STM32CubeMX 中未正确启用 FreeRTOS,或者系统的时钟配置不正确,可能导致任务调度失败。 - 确保在 STM32CubeMX 中正确启用了 FreeRTOS,并生成了相关代码[^1]。 - 检查系统时钟配置是否正确,特别是与 FreeRTOS 相关的定时器中断(如 systick)是否正常工作。 #### 4. **任务体代码问题** 如果任务体中的代码存在死循环或阻塞操作,可能导致任务无法正常运行。 - 确保任务体代码中包含 `for(;;)` 循环,并且在每次循环中调用 `osDelay()` 函数以释放 CPU 时间给其他任务。例如: ```c void Task1(void const * argument) { for(;;) { HAL_UART_Transmit(&huart4, (uint8_t *)"Task1 Running", 13, 0xffff); osDelay(1000); // 延迟 1 秒 } } ``` - 如果任务体中存在阻塞操作(如等待某个事件或信号量),确保相关资源已正确初始化。 #### 5. **中断优先级设置过高** 如果系统中某些中断的优先级设置过高,可能会导致 FreeRTOS 的调度功能失效。 - 确保所有中断的优先级低于 FreeRTOS 的最低优先级限制。例如,在 STM32CubeMX 中,可以通过 NVIC 配置调整中断优先级[^1]。 - 使用以下代码检查中断优先级: ```c __NVIC_SetPriority(SysTick_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); ``` #### 6. **调试工具干扰** 某些调试工具(如 JTAG 或 SWD)可能会干扰 FreeRTOS 的正常运行。 - 尝试关闭调试工具后重新运行程序,观察任务是否能够正常进入。 - 如果需要调试,确保调试工具的配置不会影响 FreeRTOS 的调度。 --- ### 示例代码:手动创建任务 以下是一个完整的示例代码,展示如何手动创建并运行 FreeRTOS 任务: ```c #include "main.h" #include "cmsis_os.h" void Task1(void const * argument) { for(;;) { HAL_UART_Transmit(&huart4, (uint8_t *)"Task1 Running", 13, 0xffff); osDelay(1000); // 延迟 1 秒 } } void Task2(void const * argument) { for(;;) { HAL_UART_Transmit(&huart4, (uint8_t *)"Task2 Running", 13, 0xffff); osDelay(5000); // 延迟 5 秒 } } int main(void) { HAL_Init(); SystemClock_Config(); osThreadAttr_t attr; // 创建 Task1 attr.priority = osPriorityNormal; attr.stack_size = 128 * 4; attr.name = "Task1"; osThreadNew((osThreadFunc_t)Task1, NULL, &attr); // 创建 Task2 attr.priority = osPriorityBelowNormal; attr.stack_size = 128 * 4; attr.name = "Task2"; osThreadNew((osThreadFunc_t)Task2, NULL, &attr); while (1) { osDelay(1000); // 主循环中延迟 } } ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值