花了3个晚上,把这个章节看完,受益匪浅。

  1. 最有用的应该是与中断相关的错误,优先排查中断优先级设置。
  2. 堆栈溢出检查,可能用到,一般先把堆栈设置的足够大,只要没有溢出就是好事,溢出了,掌握了栈溢出钩子函数排错很方便。
  3. printf()相关的问题应该尽量不会出现,毕竟只要需要打印调试信息的情况下才使用,而且嵌入式系统一般都是用串口重定向的。

    讲真,嵌入式中printf()真的挺烦的,严重影响性能,我的开发案例中发现,串口打印会影响板子的 power save性能,这是实测到的。

此章节涉及新手最常遇见的3种问题:

  1. 错误的中断优先级设置
  2. 栈溢出
  3. 不恰当的使用printf()

使用configASSERT()能够显著地提高生产效率,它能够捕获、识别多种类型的错误。强烈建议在开发或者调试中开启宏configASSERT()

中断优先级

注意:这是头号需要技术支持的问题,在大多数的移植版本中通过定义configASSERT()就能够立刻捕获这个错误。

如果FreeRTOS移植版本支持中断嵌套,并且中断服务程序使用了FreeRTOS API,那么必须把中断优先级设置为configMAX_SYSCALL_INTERRUPT_PRIORITY或者低一点。没有这么设置将会导致临界区失效,反过来就会导致间歇性的错误。

当FreeRTOS运行在以下处理器上需要特别注意:

  • 中断优先级使用可能的最高优先级,这就是ARM Cortex 处理器上的情形,还有一些其他的。在这些处理器上,调用FreeRTOS API 的中断的优先级不能留置未初始化。
  • 优先级数值越高表示逻辑上优先级越低,这可能与直觉相反,因此可能导致混淆。同样这可能在某些ARM Cortex处理器上,可能还有其他的。
  • 例如,在某个处理器上一个中断的优先级为5,正在运行,但是能够被一个优先级为4的中断打断。因此,如果configMAX_SYSCALL_INTERRUPT_PRIORITY设置为5,那么任何其他的使用FreeRTOS API的中断的优先级必须设置为5甚至更高。在这种情形下优先级为5或者6的是有效的,但是优先级为3的中断是无效的。
  • 不同的库实现期待中断优先级用不同的方式指定。此外尤其是针对ARM Cortex处理器相关的库,它们的中断优先级在写入硬件寄存器之前是经过位移的。某些库可能自己进行位移操作,然而其他的库期待中断优先级在传给库函数之前已经进行了位移操作。
  • 同样架构上的不同的实现,实现的是中断优先级的比特位不同。例如同样的Cortex-M处理器某一个厂商可能实现了3个优先级比特位,但是另一个厂商实现了4个优先级比特位。
  • 定义一个中断优先级的比特位被分成两个部分,一部分定义抢占的级别,另外的比特位定义子优先级。确保所有的比特位都是指定抢占的优先级,而子优先级不使用。

在某些移植版本中configMAX_SYSCALL_INTERRUPT_PRIORITY有一个别名configMAX_API_CALL_INTERRUPT_PRIORITY

栈溢出

栈溢出是第二个经常寻求技术支持的问题。FreeRTOS提供了几个特性来辅助捕获和调试和栈相关的问题。

API函数uxTaskGetStackHighWaterMark()

每个任务都在维护自己的栈,栈的总大小在创建任务的时候就指定了。函数uxTaskGetStackHighWaterMark()就是用来查询分配给这个任务的栈接近栈溢出的程度。返回值称为栈的高水位线

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

任务使用栈的多少,随着任务的运行和中断的处理时而增加时而减少。uxTaskGetStackHighWaterMark()返回自任务开始运行以来剩余可用的栈空间的最小值。它返回的是栈未使用的空间占最大值的比值。高水位越接近于0,那么这个任务的栈就越快要溢出。

运行时栈检查

FreeRTOS提供了两种在运行时检查栈的机制。都是由文件FreeRTOSConfig.h中的configCHECK_FOR_STACK_OVERFLOW来在编译时进行控制的。两种方法都会增加上下文切换的时间。

栈溢出钩子函数(又称为栈溢出回调函数)是一个由内核检查到栈溢出时调用的函数。要使用栈溢出钩子函数要满足以下条件:

  1. 在文件FreeRTOSConfig.h中把configCHECK_FOR_STACK_OVERFLOW设置为1或者2
  2. 实现以下钩子函数,使用完全一样的函数名字和原型:
void vApplicationStackOverflowHook( TaskHandle_t *pxTask, signed char *pcTaskName);

栈溢出钩子函数会让捕获和调试栈错误更加的简单,但是发生栈溢出错误时是没有办法恢复的。此函数把发生栈溢出的任务的句柄和名字传递进去。

栈溢出钩子函数会在一个中断的上下文中进行调用。

某些微控制器在检测到一个错误的内存访问时会产生一个错误异常,这个错误异常的触发会使得内核根本就没有机会调用栈溢出钩子函数。

运行时栈检查--方法1

当进行如下设置是会选择方法1.

#define configCHECK_FOR_STACK_OVERFLOW 1

每当一个任务被切换出去时它的整个的执行上下文都会被保存到它自己的栈中。很有可能这就是栈使用率达到最大值的时候。当使用方法1是,当任务的上下文被保存之后内核回去检查栈指针是否在栈可用空间内。如果发现栈指针已经超出了可用的范围那么就会调用栈溢出钩子函数。

方法1执行速度快,但是在发生上下文切换时有可能会错过栈溢出。

运行时栈检查--方法2

进行如下设置后才会选择方法2.

#define configCHECK_FOR_STACK_OVERFLOW 2

除了方法1中的行为,方法2还会执行其他的检查。

创建任务时它的栈会被一个已知的样本填充。任务2检查栈空间的最后20个字节,验证这个已知的样本是否已经被覆盖。如果这20个字节的值与预期值不一样那么就会调用栈溢出钩子函数。

方法2不如方法1快,当时相对来讲还是快,毕竟只是测试20个字节。很有可能方法2会捕获到所有的栈溢出,但是有可能(几乎不可能)某些栈溢出还是漏掉了。

不恰当地使用printf()sprintf()

不恰当地只用printf()是一种常见的错误源,并且没有意识到这种错误,通常应用开发者会增加更多的printf()来辅助调试,结果就是加重了问题。

许多交叉编译器厂商会提供一种适合在小型嵌入式系统中使用的printf()的实现。即便在这种情形下,printf()的实现可能并不是线程安全的,几乎可以肯定不适合在中断服务程序中使用,并且取决于输出被重定向到哪里,会占用相当长的一段执行时间。。

如果小型嵌入式系统的printf()的实现不可用,并且使用了通用的printf()的实现,那么就需要特别注意了:

  • 仅仅增加了一个对printf()或者sprintf()的调用就会急剧的增加应用执行文件的体积;
  • 如果使用了heap_3以外的存储空间方案,printf()sprintf()调用了malloc(),这个是无效的。
  • printf()sprintf()可能会申请一个几倍于通常情况的栈空间。

Printf-stdarg.c

许多的FreeRTOS示例工程了使用了一个printf-stdarg.c的文件,它提供了一个极小的、栈使用率非常高效的能够取代标准库函数版本的sprintf()实现。在大多数情形下,使得任务每次调用sprintf()或者相关的函数却分配更少的栈空间。

printf-stdarg.c提供了一种机制把printf()输出重定向,一个字节一个字节的输出,虽然慢,但是却极大地减少了栈空间的占用。

注意:并不是所有的FreeRTOS下载副本中文件printf-stdarg.c都实现了snprintf()函数。没有实现snprintf()的副本直接忽略缓冲区大小参数,它们是直接映射到sprintf()函数。

printf-stdarg.c是开源的,但是是第三方拥有的,因此它的授权和FreeRTOS是分开的。它的授权条款在文件的首部。

其他的常见错误

症状:添加一个简单任务到例程中却导致了例程挂掉

创建任务需要从堆中分配内存。许多示例工程的栈空间仅仅能够容纳例程任务,因此在创建了例程任务后,没有足够的堆空间留给其他更多的任务,队列,事件组,信号量。

空闲任务,又或许是FreeRTOS的守护进程,在调用vTaskStartScheduler()时是自动创建的。只有当堆空间不足以创建这些任务时vTaskStartScheduler()才会返回。在调用vTaskStartScheduler()之后添加一个for(;;);会让这个问题更容易排错。

要想添加更多的任务,要么扩大堆空间,要么减少已经存在的例子任务。

症状:中断中使用API导致应用挂掉

在中断服务程序中不要使用API,除非API名字是以FromISR()结尾。特别地,在中断中不要创建一个临界区,除非使用中断安全的宏。

在支持中断嵌套的FreeRTOS移植版本中,如果中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY就不要在其中使用 API 函数。

症状:有时应用程序在中断服务函数中挂掉

首先要检查中断是否产生了栈溢出。有的移植版本中只检查任务的栈溢出,并没有检查中断是否栈溢出。

中断的定义和使用方式随着移植版本和编译器的不同而不同。因此,其次要检查语法,宏定义,调用规则在中断服务程序中的使用是否与移植文档中的完全一致,是否与例程中的完全一致。

如果应用运行在数值越低的优先级表示逻辑上越高的优先级的处理器上,那么需要确保分配给中断的优先级要考虑到这种情况,因为它看起来是违反直觉的。

如果应用运行在把中断优先级默认设置为最大可能的优先级的处理器上,需要确保每个中断的优先级没有留置为默认值。

症状:调度器尝试启动第一个任务时挂掉

确保设置了FreeRTOS的中断句柄。参考FreeRTOS移植文档,还有示例程序。

某些处理器必须在调度器启动之前处于特权模式。最简单的实现方法是在C语言main()之前的启动代码中就把处理器置于特权模式。

症状: 中断被异常地禁止了,又或者临界区没有正确地嵌套

如果在调度器启动之前就调用了FreeRTOS API函数那么中断就会被蓄意地禁止,直到第一个任务启动之后才会重新使能。这样做是为了保护系统不挂掉,原因在于初始化过程中中断尝试调用FreeRTOS API函数,然而调度器还没有启动,它可能处于一个不一致的状态。

除了调用taskENTER_CRITICAL()taskEXIT_CRITICAL()函数之外不要使用任何其它的方法来更改微处理器的中断使能位和优先级标志。这两个宏会统计中断嵌套深度确保当中断嵌套深度为0时中断又使能。需要知悉某些库函数可能在内部使能和禁止中断。

症状:远在调度器启动之前应用就挂掉了

有可能发生上下文切换的中断是禁止在调度器启动之前就开始执行的。同样的规则适用于尝试发送或者接收FreeRTOS对象(例如队列和信号量)的任何中断服务程序。上下文切换必须在调度器启动之后才能发生。

很多API函数必须在调度器启动之后才能调用。最好是在调用vTaskStartScheduler()之后将API的使用限制在创建诸如任务,队列和信号量上,而不是使用这些对象。

症状: 在调度器挂起时又或者是临界区内部调用API函数会导致应用程序挂掉

调用函数vTaskSuspendAll()会挂起调度器,调用函数xTaskResumeAll()会恢复调度器。

调用函数taskENTER_CRITICAL()会进入临界区,调用函数taskEXIT_CRITICAL()会退出临界区。

在调度器挂起时或者临界区内永远不要调用API函数。

声明

欢迎转载,请注明出处和作者,同时保留声明。

作者:LinTeX9527

出处:http://www.cnblogs.com/LinTeX9527/p/8031565.html

本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

FreeRTOS--疑难杂症的更多相关文章

  1. FREERTOS 手册阅读笔记

    郑重声明,版权所有! 转载需说明. FREERTOS堆栈大小的单位是word,不是byte. 根据处理器架构优化系统的任务优先级不能超过32,If the architecture optimized ...

  2. Carousel 旋转画廊特效的疑难杂症

    疑难杂症 该画廊特效的特点就是前后元素有层级关系. 我想很多人应该看过或者用过这个插件carousel.js,网上也有相关的教程.不知道这个插件的原型是哪个,有知道的朋友可以告诉我. 该插件相对完美, ...

  3. FreeRTOS 中断优先级嵌套错误引发HardFault异常解决(转)

      最近在使用FreeRTOS的时候,突然发现程序在运行了几分钟之后所有的任务都不再调用了,只有几个中断能正常使用,看来是系统挂掉了,连续测试了几次想找出问题,可是这个真的有点不知所措.      我 ...

  4. Keil环境中建立带FreeRTOS的STM32L项目

    本文是网上转载,版权所有. Keil环境中建立带FreeRTOS的STM32L项目 1.先把source文件夹复制至project目录,然后在keil中添加RTOS文件,如图: 其中heap_2.c按 ...

  5. FreeRTOS学习及移植笔记之二:在IAR和STM32F103VET上移植FreeRTOS

    上一次,我们简单的测试了FreeRTOS的基于IAR EWARM v6.4和STM32F103VET6平台的Demo,对其有了一个基本认识.接下来我们开始自己移植FreeRTOS的过程. 1.创建一个 ...

  6. FreeRTOS学习及移植笔记之一:开始FreeRTOS之旅

    1.必要的准备工作 工欲善其事,必先利其器,在开始学习和移植之前,相应的准备工作必不可少.所以在开始我们写要准备如下: 测试环境:我准备在STM32F103平台上移植和测试FreeRTOS系统 准备F ...

  7. STM32与FreeRTOS实现低功耗

    在工作过程中,遇到这样一个产品,它基于 Cortex-M7 内核的 STM32F769 芯片,同时使用了 FreeRTOS 实时操作系统. 由于该产品使用电池供电,因此有着低功耗的需求. 接下来,我将 ...

  8. 了解FreeRTOS源文件目录结构

    参考文献: Understanding the FreeRTOS directory structure. 从官网下载下来的FreeRTOS源文件包,对于每一个已经移植完成的处理器,都有一个与之对应的 ...

  9. 用STM32CubeMX创建FreeRTOS项目

    1. 目标, PG13,PG14双线程双闪灯. 2. 测试平台 stm32f429i-disco keil v5.13.0.0 CubeMx 4.8.0 3. 步骤 3.1 打开cube, PG13, ...

  10. STM32F429i-DISCO FreeRTOS keil STM32CubeMX

    目标: 在STM32F429 Disco开发板上用FreeRTOS双线程点亮双闪led. 准备: 0. STM32F429i-DISCO 1. keil ARMMDK 5.13 2. STM32Cub ...

随机推荐

  1. poj 1061 青蛙约会(扩展欧几里德)

    题目链接: http://poj.org/problem?id=1061 题目大意: 中文题目,题意一目了然,就是数据范围大的出奇. 解题思路: 假设两只青蛙都跳了T次,可以列出来不定方程:p*l + ...

  2. 文件输入输出C++操作

    基于C++的文件操作 在C++中,有一个stream这个类,所有的I/O都以这个"流"类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符: 1.插入器(& ...

  3. 字符串处理 Codeforces Round #285 (Div. 2) B. Misha and Changing Handles

    题目传送门 /* 题意:给出一系列名字变化,问最后初始的名字变成了什么 字符串处理:每一次输入到之前的找相印的名字,若没有,则是初始的,pos[m] 数组记录初始位置 在每一次更新时都把初始pos加上 ...

  4. 配置Ubuntu16.04第02步:更改镜像源,更新系统

    Ubuntu 16.04下载软件速度有点慢,因为默认的是从国外下载软件,那就更换到国内比较好的快速更新源(就是这些软件所在的服务器),一般直接百度Ubuntu更新源就能出来一大堆,这时候最好是找和自己 ...

  5. iOS 集成银联支付(绕过文档的坑,快速集成)-转

    本文是投稿文章,作者:南栀倾寒当初集成支付宝的时候,觉得见了这么丑的代码,加上这么难找的下载地址,在配上几乎为零的文档,寒哥就要吐血了. 下午去集成银联,才知道血吐的早了. 下载地址:https:// ...

  6. Objective-c单例模式的正确写法--用dispatch 线程安全

    单例模式在iOS开发中可能算是最常用的模式之一了,但是由于oc本身的语言特性,想要写一个正确的单例模式相对来说比较麻烦,这里我就抛砖引玉来聊一聊iOS中单例模式的设计思路.关于单例模式更多的介绍请参考 ...

  7. 447 Number of Boomerangs 回旋镖的数量

    给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序).找到所有回旋镖的数量.你可以假设 n ...

  8. Objective - c Chapter 1 -2 Hello world

    Objective - c   Chapter 1  Hello world 1.1 1.2.On the Welcome screen, click "Create a new Xcode ...

  9. Xcode 9 打印信息解决

    Xcode 9 打印信息解决 打印信息 1 nw_proxy_resolver_create_parsed_array PAC evaluation error: kCFErrorDomainCFNe ...

  10. Winform webbrowser 隐藏 html 元素

    目的:用webbrowser打开网页,并隐藏网页上某个html元素 1.如果已知元素ID,比较好办 直接使用webbrowser1.Document.getElementById("id&q ...