freeRTOS手册 第十二章 . 解决问题

本文介绍了FreeRTOS用户可能遇到的问题,包括中断优先级配置错误可能导致的间歇性错误,如何使用uxTaskGetStackHighWaterMark()检测堆溢出,以及不恰当使用printf()的潜在影响。建议在开发和调试时定义configASSERT()以提高错误检测能力,并提醒用户避免在中断服务程序中使用非中断安全的FreeRTOS函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果我对本翻译内容享有所有权。允许任何人复制使用本文章,不会收取任何费用。如有平台向你收取费用与本人无任何关系

第十二章 . 解决问题

章节介绍和范围

本章重点关注FreeRTOS新用户可能遇到的共同问题。首先过去的一年请求源码功能,频率最高的3个问题已经解决;纠正中断优先级作业,栈溢出,不恰当的使用printf()。然后简略的用FAQ(常见问题)的格式指明了一些常见问题的可能原因和解决方案。
用configASSERT()可以提高立即捕获和识别许多源代码错误的能力。强烈建议在开发和调试的时候定义好configASSERT(),11.2章节有介绍configASSERT()。

中断优先级

注意这是引起的支持请求首要原因,在大部分版本FreeRTOS中,configASSERT()会立即捕获错误。
如果当前FreeRTOS支持中断嵌套,中断服务程序中有调用FreeRTOS函数,章节6.8中有说明,中断优先级需要小于configMAX_SYSCALL_INTERRUPT_PRIORITY。如果不这样做,就会影响关键代码,继而出现间歇性错误。
特别关注FreeRTOS运行在哪个处理器上:

  • 中断优先级默认有一个最高可能优先级,比如ARM Cortex或其他处理器上。在这些处理器上,使用FreeRTOS函数的中断优先级必须初始化。
  • 高优先级的数字化优先级任务会阻止低优先级的逻辑化任务,这看起来反直觉,且认人困惑。在ARM Cortex和一些其它处理器中就是这样。
  • 例如,一个中断优先级为5的任务可以被一个中断优先级4打断的处理器中。因此,如果设置configMAX_SYSCALL_INTERRUPT_PRIORITY为5,那么使用FreeRTOS函数的中断优先级就只能比5高。所以中断优先级5和6就是可用的,但中断优先级3就是不可用的。
  • 不同库的实现,特别是中断优先级的实现方法不同。然而,与ARM Cortex关联的库,中断优先级都是通过位移动后写入硬件寄存器。有些库会自动处理位移操作,有一些在写入硬件寄存器之前需要使用者自己进行移位操作。
  • 相同架构的不同库实现有不同的中断优先级值。比如Cortex-M处理器的一个制造商可能实现3个优先级位,另外一个制造商实现4位优先级位。
  • 中断的优先级位可以分为抢占优先级位和子优先级位。确保所有的位都指定为抢占优先级位,而不是子优先级位。
    在有的FreeRTOS版本,configMAX_SYSCALL_INTERRUPT_PRIORITY有另外一个名字configMAX_API_CALL_INTERRUPT_PRIORITY

堆溢出

堆溢出是第二个最常见的支持请求。FreeRTOS提供了多个功能帮助追踪和调试堆相关问题。

uxTaskGetStackHighWaterMark()函数

每个任务都有一个自己的堆,它的大小是在创建任务的时候指定的。uxTaskGetStackHighWaterMark()用于查询怎样关闭一个任务,其中分配给这个任务的堆已经溢出的情况。这个溢出后的值叫堆的高水位标志。

// uxTaskGetStackHighWaterMark()函数原型。列表173
UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask);

/* 参数
 * xTask: 将要被查询堆高水位值的任务句柄,可以查看xTaskCreate()函数的pxCreatedTask参数了解更多任务句柄相关信息。也可以传递一个NULL查询本任务的堆高水位标志。
 * 返回值: 任务执行过程中的堆扩张和收缩,以及处理中断过程中使用的堆数量。uxTaskGetStackHighWaterMark()返回自开始执行以来,堆保持的可以使用的最小数量。这个堆数量就是堆中因为任务使用最多的堆,而余下的数量。高水位标志越接近0,任务就越接近堆溢出。
 */ 

运行过程中的堆检查 Overview

FreeRTOS中运行过程中堆检查包含2个选项。它们由FreeRTOSConfig.h中的configCHECK_FOR_STACK_OVERFLOW常量控制,这个常量会在编译时确定。它们都会增加上下文切换的时间。
堆溢出勾子(堆溢出回调)是内核检测到堆溢出时的一个函数。要使用堆溢出勾子函数需要:

  1. 设置FreeRTOSConfig.h中的configCHECK_FOR_STACK_OVERFLOW为1或者2,具体下面会介绍。
  2. 提供勾子函数的实现,用列174中指定的函数名和原型。
// 堆勾子函数原型。列表174
void vApplicationStackOverflowHook(TaskHandle_t *pxTask, signed char *pcTaskName);

堆溢出勾子函数用于简单的追踪和调试堆错误,但当发生堆溢出时没有实际的方法恢复堆空间。这个函数的2个参数就是发生堆溢出的任务句柄和名字。
堆溢出勾子函数会在中断上下文中调用。
一些微处理器会在检测到内存访问错误时生成一个错误,可能会出现调用堆溢出勾子函数之前就触发了这个错误。

运行时堆检测 - 方法1

第一个方法是设置configCHECK_FOR_STACK_OVERFLOW为1。
每次任务切出会保存它的执行上下文到堆中。很可能这时堆的使用就会达到顶峰。当configCHECK_FOR_STACK_OVERFLOW设置为1时,内核就会在上下文被保存时检测堆指针是否在可用的范围。如果发现推指针不在可用范围就会运行堆溢出勾子函数。
第一个方法执行非常快,但会丢失上下文切换之前的堆溢出。

运行时堆检测 - 方法2

第二个方法除了会进行方法1中已经说明的检测,还会附加更多的检测。需要设置configCHECK_FOR_STACK_OVERFLOW为2。
当创建一个任务,它的堆中存放的是实际的数据。方法2就是通过检测堆中最后的20字节堆空间数据是不是被覆盖来判断是否溢出。如果最后的20个字节的数据都被其它数据替代就会调用堆溢出勾子函数。
第二个方法比第一个方法慢,但还是比较快的,因为只测试最后20个字节数据。大多数时候它能捕获所有堆溢出,但还是有可能会丢失一些堆溢出。

不适当的使用printf()和sprintf()

不适当的使用printf()是一个常见的错误,程序开发者未意识到的情况下,增加更多的printf()的调用,为了增加调试信息,加剧问题。
很多交叉编译工具有提供可以用于嵌入式printf()的实现,尽管这是一个小事。printf()的实现可能不是线程安全的,也可能不适用于中断服务程序中,而且依赖于具体输出到那里,可能也会花很多的时间执行。
必须特别注意,专门为小型嵌入系统实现的printf()是不可用的,而是需要自己重新实现一个printf()作为替代,因为:

  • 只是简单的调用一个printf()和sprintf()函数会严重增加应用程序执行的大小
  • printf()和sprintf()可能会调用malloc(),除非使用heap_3方案分配堆,不然内存的分配计划可能就不可用。可以查看2.2章节(内存分配计划)了解更多信息
  • printf()和sprintf()可以需要一个堆栈多次相比于其它的需求

Printf-stdarg.c

很多FreeRTOS的实例程序用了一个叫做printf-stdarg.c的文件,它提供了一个最小且栈有效的snprintf(),可以用于替代标准库中的snprintf()版本。大多数情况,它会占用一个非常小的栈用于spnrintf()相关的函数。
printf-stdarg.c提供了一个立即将printf()要输出的字符打印出来的机制,它很慢,但用到的栈空间会减少很多。
注意FreeRTOS上下载的printf-stdarg.c不是都包含有snprintf()实现。复制那些没有实现snprintf()的只是简单的忽略缓存大小参数,就像它只是sprintf()的映像一样。
printf-stdarg.c是开源的,但是第三方所有,因此只是FreeRTOS许可证的部分。源代码目录顶部中包含了许可证的详细信息。

其他常见错误

现象: 项目中增加一个任务就引起崩溃
创建一个任务需要用到堆中的内存。很多实例程序的任务分配得特别大。这样这个任务创建后,就没有足够的空间给更多的任务,队列,事件组或信号量使用了。
空闲任务甚至RTOS守护任务都是调用vTaskStartScheduler()的时候自动创建。vTaskStartScheduler()只有在没有足够的空间分配给创建的任务的时候返回。可以在vTaskStartScheduler()后面包含一个空循环方便调试。
为了可以增加更多的任务,可以增加物理的堆大小,或者及时删除不用的任务。查看2.2章节,内存分配调度的更多信息。

现象: 中断服务程序中使用一个函数引起程序崩溃
不要在中断服务程序中使用不带FromISR后缀的)函数。特别不要在中断中创建一个关键代码,除非使用中断安全的宏。查看6.2章节,在中断服务程序中使用FreeRTOS函数了解很多细节。
在支持中断嵌套的FreeRTOS版本,不要在中断中使用任何中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的FreeRTOS函数。查看章节6.8,中断嵌套了解更多信息。

现象: 程序有时候在中断服务程序中崩溃
首先确定中断没有引起栈溢出。有些版本的FreeRTOS只检测任务栈是否溢出,不会检查中断。
不同版本和不同编译器使用不同的方式定义中断。因此,第二件事是检查中断服务程序中的语法,宏,调用协议是否精确,是否和这个版本中手册中说明一致,或者这个版本提供的实例程序一致。
如果处理器上的程序,以低优先级的数字化任务阻止高优先级逻辑化任务,请确保每个中断的优先级都考虑到这一点,因为这种设计太反人类了。如果处理器上运行的程序,每个中断默认优先级时最大可能优先级,请确保每个中断优先级不会都是默认优先级。查看章节6.8,中断嵌套,和章节12.2,中断优先级查看很多详情。

现象: 试图创建第一个任务的时候,调度器就崩溃
确保FreeRTOS的中断句柄已经安装。可以查看当前版本的FreeRTOS手册或实例程序获取更多信息。
有些处理器开启调度器之前需要进入专用模式。最简单进入这种专有模式的方式,就是在C启动中,在调用main()之前。

现象: 中断突然被禁用,关键部分没有正确的嵌套
如果调度器开启之前调用FreeRTOS函数,那么中断就会被禁用,不要在第一个任务开始执行之前再次使能中断。这样做可以避免调度器开启之前,调度器处于未明确状态时,系统初始化因为调用FreeRTOS函数引起系统崩溃。
除非调用taskENTER_CRITICAL()taskEXIT_CRITICAL(),不然不要用任何方法改变处理器中断的优先级标识位和使能位。这些宏会保存一个调用嵌套深度的值,确保中断只有在调用嵌套被完全释放为0的时候才使能。小心一些库函数自身会使能或失能中断。

现象: 开启调度器之前程序就崩溃
中断服务程序会潜在的引起上下文切换,所以不能在开启调度器之前允许中断的执行。这同样适用于试图发送或接收数据给FreeRTOS对象的中断。这里的中断对象可以是队列,信号量。开启调度器之前不允许上下文切换。
开启调度器之前很多函数不能使用。最好限制只用于创建一些对象,比如任务,队列和信号量。至于使用这些对象,只能在vTaskStartScheduler()调用后。

现象: 调度器暂停后使用FreeRTOS函数,或者在关键部分使用FreeRTOS函数引起程序崩溃
通过调用vTaskSuspendAll()暂停调度器和调用vTaskResumeAll()恢复调度器。通过调用taskENTER_CRITICAL()进入关键区域,调用taskEXIT_CRITICAL()`退出关键部分。
不要在调度器暂停时和关键代码中调用FreeRTOS函数。

<think>好的,我需要帮助用户找到关于HAL库和FreeRTOS的学习资料或笔记。首先,用户提供的引用内容提到了FreeRTOS的移植步骤、源码结构以及中断处理机制,这些可能都是用户关心的重点。接下来,我应该整理这些引用中的信息,并结合常见的HAL库和FreeRTOS学习资源给出建议。 首先,用户提到的引用[2]和[3]都涉及FreeRTOSSTM32上的移植,特别是HAL库的集成。这说明用户可能在STM32平台上开发,需要具体的移植步骤和验证方法。引用[4]详细介绍了ARM Cortex-M的中断机制,这对于理解FreeRTOS的任务调度和中断处理非常重要。 接下来,我应该推荐用户查阅官方文档,比如AWS FreeRTOS的官方指南,这通常是最权威的资料。然后,考虑到用户可能喜欢实践教程,可以建议参考社区博客或GitHub上的项目,特别是那些结合HAL库和FreeRTOS的示例代码。此外,书籍推荐也是必要的,比如专门讲解FreeRTOSSTM32开发的书籍。 用户还可能对实际应用案例感兴趣,所以需要提及实际项目中的应用场景,如物联网设备,并结合引用[1]中提到的AWS FreeRTOS的应用。最后,确保回答结构清晰,分点列出学习路径、推荐资料、移植指南和深入理解部分,方便用户逐步学习。 需要注意的是,所有提到的参考资料要正确标注引用标识,并检查是否有遗漏的重要资源。同时,生成的相关问题要围绕用户可能的后续疑问,比如移植步骤、中断配置、学习资源推荐等,帮助用户进一步探索。</think>### HAL库与FreeRTOS学习资源指南 #### 一、学习路径建议 1. **HAL库基础** - 学习STM32CubeMX工具配置外设,生成HAL库初始化代码,理解HAL的模块化设计(如GPIO、UART、ADC等驱动接口)。 - 关键文档:ST官方《STM32CubeMX用户手册》和《HAL库API参考手册》[^2]。 2. **FreeRTOS核心概念** - 掌握任务管理(`xTaskCreate`)、队列(`xQueueCreate`)、信号量(`xSemaphoreCreateBinary`)和中断服务例程(ISR)的集成方法。 - 重点理解优先级反转问题及互斥量(Mutex)的解决方案[^1]。 3. **HAL与FreeRTOS结合** - 在HAL库的中断回调函数中调用FreeRTOS的API(如`xQueueSendFromISR`),需注意中断优先级与FreeRTOS内核配置的兼容性[^4]。 --- #### 二、推荐学习资料 1. **官方资源** - **AWS FreeRTOS开发者指南**:涵盖任务调度、内存管理及安全通信(如MQTT)。 - **STM32CubeIDE集成开发环境**:内置FreeRTOS模板项目,支持一键生成带RTOS的工程框架。 2. **实战教程** - **移植FreeRTOS到HAL库**:引用[3]中提到的源码移植步骤,需重点关注`FreeRTOSConfig.h`配置文件中的堆大小(`configTOTAL_HEAP_SIZE`)和时钟源(`configSYSTICK_CLOCK_HZ`)设置。 - 示例代码片段: ```c // FreeRTOS任务与HAL中断协同 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendFromISR(xUartQueue, &received_data, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } ``` 3. **书籍与社区** - 《Mastering STM32》第二版:第12章详解FreeRTOS与HAL库的集成案例。 - STM32中文社区论坛:搜索“HAL+FreeRTOS”关键词,可找到实际项目中的死锁调试记录。 --- #### 三、关键配置注意事项 | 配置项 | 说明 | |-------------------------|----------------------------------------------------------------------| | `configUSE_PREEMPTION` | 设置为1启用抢占式调度,确保高优先级任务及时响应 | | `configMAX_SYSCALL_INTERRUPT_PRIORITY` | 需与NVIC中断优先级分组匹配,避免内核API在中断中被阻塞[^4] | | `configTICK_RATE_HZ` | 通常设置为1000Hz,对应1ms时间片,影响任务延迟精度[^3] | --- #### 四、进阶学习方向 1. **动态内存分配优化** - 替换默认的`heap_4.c`方案,使用内存池(Memory Pool)减少碎片,适用于长期运行的嵌入式系统。 2. **低功耗模式集成** - 在FreeRTOS空闲任务中调用HAL库的`HAL_SuspendTick()`函数,实现STM32的STOP模式节能。 3. **调试工具链** - 使用SEGGER SystemView可视化分析任务调度时序,定位优先级冲突问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值