Chinaunix首页 | 论坛 | 博客
  • 博客访问: 804787
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-09-28 15:24:31

概览

本章主要是为刚接触FreeRTOS 的用户指出那些新手通常容易遇到的问题。这里把最主要的篇幅放在栈溢出以及栈溢出侦测上,因为栈相关的问题是过去几年遇到最多的问题。对其它一些比较常见的问题,本章简要的以FAQ(问答)的形式给出可能的原因和解决方法。

printf-stdarg.c
当调用标准C 库函数时,栈空间使用量可能会急剧上升,特别是IO 与字符串处理函数,比如sprintf()。在FreeRTOS 下载包中有一个名为printf-stdarg.c 的文件。这个文件实现了一个栈效率优化版的小型sprintf(),可以用来代替标准C 库函数版本。在大多数情况下,这样做可以使得调用sprintf()及相关函数的任务对栈空间的需求量小很多。
printf-stdarg.c 源代码开放,但是为第三方所有。所以此源代码的license 独立于FreeRTOS。具体的license 条款包含在该源文件的起始部分。

见 printf-stdarg.c 代码:

点击(此处)折叠或打开

  1. /*
  2.     Copyright 2001, 2002 Georges Menie (www.menie.org)
  3.     stdarg version contributed by Christian Ettinger

  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU Lesser General Public License as published by
  6.     the Free Software Foundation; either version 2 of the License, or
  7.     (at your option) any later version.

  8.     This program is distributed in the hope that it will be useful,
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11.     GNU Lesser General Public License for more details.

  12.     You should have received a copy of the GNU Lesser General Public License
  13.     along with this program; if not, write to the Free Software
  14.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */

  16. /*
  17.     putchar is the only external dependency for this file,
  18.     if you have a working putchar, leave it commented out.
  19.     If not, uncomment the define below and
  20.     replace outbyte(c) by your own function call.

  21. #define putchar(c) outbyte(c)
  22. */

  23. #include <stdarg.h>

  24. static void printchar(char **str, int c)
  25. {
  26.     extern int putchar(int c);
  27.     
  28.     if (str) {
  29.         **str = c;
  30.         ++(*str);
  31.     }
  32.     else (void)putchar(c);
  33. }

  34. #define PAD_RIGHT 1
  35. #define PAD_ZERO 2

  36. static int prints(char **out, const char *string, int width, int pad)
  37. {
  38.     register int pc = 0, padchar = ' ';

  39.     if (width > 0) {
  40.         register int len = 0;
  41.         register const char *ptr;
  42.         for (ptr = string; *ptr; ++ptr) ++len;
  43.         if (len >= width) width = 0;
  44.         else width -= len;
  45.         if (pad & PAD_ZERO) padchar = '0';
  46.     }
  47.     if (!(pad & PAD_RIGHT)) {
  48.         for ( ; width > 0; --width) {
  49.             printchar (out, padchar);
  50.             ++pc;
  51.         }
  52.     }
  53.     for ( ; *string ; ++string) {
  54.         printchar (out, *string);
  55.         ++pc;
  56.     }
  57.     for ( ; width > 0; --width) {
  58.         printchar (out, padchar);
  59.         ++pc;
  60.     }

  61.     return pc;
  62. }

  63. /* the following should be enough for 32 bit int */
  64. #define PRINT_BUF_LEN 12

  65. static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase)
  66. {
  67.     char print_buf[PRINT_BUF_LEN];
  68.     register char *s;
  69.     register int t, neg = 0, pc = 0;
  70.     register unsigned int u = i;

  71.     if (i == 0) {
  72.         print_buf[0] = '0';
  73.         print_buf[1] = '\0';
  74.         return prints (out, print_buf, width, pad);
  75.     }

  76.     if (sg && b == 10 && i < 0) {
  77.         neg = 1;
  78.         u = -i;
  79.     }

  80.     s = print_buf + PRINT_BUF_LEN-1;
  81.     *s = '\0';

  82.     while (u) {
  83.         t = u % b;
  84.         if( t >= 10 )
  85.             t += letbase - '0' - 10;
  86.         *--s = t + '0';
  87.         u /= b;
  88.     }

  89.     if (neg) {
  90.         if( width && (pad & PAD_ZERO) ) {
  91.             printchar (out, '-');
  92.             ++pc;
  93.             --width;
  94.         }
  95.         else {
  96.             *--s = '-';
  97.         }
  98.     }

  99.     return pc + prints (out, s, width, pad);
  100. }

  101. static int print( char **out, const char *format, va_list args )
  102. {
  103.     register int width, pad;
  104.     register int pc = 0;
  105.     char scr[2];

  106.     for (; *format != 0; ++format) {
  107.         if (*format == '%') {
  108.             ++format;
  109.             width = pad = 0;
  110.             if (*format == '\0') break;
  111.             if (*format == '%') goto out;
  112.             if (*format == '-') {
  113.                 ++format;
  114.                 pad = PAD_RIGHT;
  115.             }
  116.             while (*format == '0') {
  117.                 ++format;
  118.                 pad |= PAD_ZERO;
  119.             }
  120.             for ( ; *format >= '0' && *format <= '9'; ++format) {
  121.                 width *= 10;
  122.                 width += *format - '0';
  123.             }
  124.             if( *format == 's' ) {
  125.                 register char *s = (char *)va_arg( args, int );
  126.                 pc += prints (out, s?s:"(null)", width, pad);
  127.                 continue;
  128.             }
  129.             if( *format == 'd' ) {
  130.                 pc += printi (out, va_arg( args, int ), 10, 1, width, pad, 'a');
  131.                 continue;
  132.             }
  133.             if( *format == 'x' ) {
  134.                 pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'a');
  135.                 continue;
  136.             }
  137.             if( *format == 'X' ) {
  138.                 pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'A');
  139.                 continue;
  140.             }
  141.             if( *format == 'u' ) {
  142.                 pc += printi (out, va_arg( args, int ), 10, 0, width, pad, 'a');
  143.                 continue;
  144.             }
  145.             if( *format == 'c' ) {
  146.                 /* char are converted to int then pushed on the stack */
  147.                 scr[0] = (char)va_arg( args, int );
  148.                 scr[1] = '\0';
  149.                 pc += prints (out, scr, width, pad);
  150.                 continue;
  151.             }
  152.         }
  153.         else {
  154.         out:
  155.             printchar (out, *format);
  156.             ++pc;
  157.         }
  158.     }
  159.     if (out) **out = '\0';
  160.     va_end( args );
  161.     return pc;
  162. }

  163. int printf(const char *format, ...)
  164. {
  165.         va_list args;
  166.         
  167.         va_start( args, format );
  168.         return print( 0, format, args );
  169. }

  170. int sprintf(char *out, const char *format, ...)
  171. {
  172.         va_list args;
  173.         
  174.         va_start( args, format );
  175.         return print( &out, format, args );
  176. }


  177. int snprintf( char *buf, unsigned int count, const char *format, ... )
  178. {
  179.         va_list args;
  180.         
  181.         ( void ) count;
  182.         
  183.         va_start( args, format );
  184.         return print( &buf, format, args );
  185. }


  186. #ifdef TEST_PRINTF
  187. int main(void)
  188. {
  189.     char *ptr = "Hello world!";
  190.     char *np = 0;
  191.     int i = 5;
  192.     unsigned int bs = sizeof(int)*8;
  193.     int mi;
  194.     char buf[80];

  195.     mi = (1 << (bs-1)) + 1;
  196.     printf("%s\n", ptr);
  197.     printf("printf test\n");
  198.     printf("%s is null pointer\n", np);
  199.     printf("%d = 5\n", i);
  200.     printf("%d = - max int\n", mi);
  201.     printf("char %c = 'a'\n", 'a');
  202.     printf("hex %x = ff\n", 0xff);
  203.     printf("hex %02x = 00\n", 0);
  204.     printf("signed %d = unsigned %u = hex %x\n", -3, -3, -3);
  205.     printf("%d %s(s)%", 0, "message");
  206.     printf("\n");
  207.     printf("%d %s(s) with %%\n", 0, "message");
  208.     sprintf(buf, "justif: \"%-10s\"\n", "left"); printf("%s", buf);
  209.     sprintf(buf, "justif: \"%10s\"\n", "right"); printf("%s", buf);
  210.     sprintf(buf, " 3: %04d zero padded\n", 3); printf("%s", buf);
  211.     sprintf(buf, " 3: %-4d left justif.\n", 3); printf("%s", buf);
  212.     sprintf(buf, " 3: %4d right justif.\n", 3); printf("%s", buf);
  213.     sprintf(buf, "-3: %04d zero padded\n", -3); printf("%s", buf);
  214.     sprintf(buf, "-3: %-4d left justif.\n", -3); printf("%s", buf);
  215.     sprintf(buf, "-3: %4d right justif.\n", -3); printf("%s", buf);

  216.     return 0;
  217. }

  218. /*
  219.  * if you compile this file with
  220.  * gcc -Wall $(YOUR_C_OPTIONS) -DTEST_PRINTF -c printf.c
  221.  * you will get a normal warning:
  222.  * printf.c:214: warning: spurious trailing `%' in format
  223.  * this line is testing an invalid % at the end of the format string.
  224.  *
  225.  * this should display (on 32bit int machine) :
  226.  *
  227.  * Hello
  228.  * printf test
  229.  * (null) is null pointer
  230.  * 5 = 5
  231.  * -2147483647 = - max int
  232.  * char a = 'a'
  233.  * hex ff = ff
  234.  * hex 00 = 00
  235.  * signed -3 = unsigned 4294967293 = hex fffffffd
  236.  * 0 message(s)
  237.  * 0 message(s) with %
  238.  * justif: "left "
  239.  * justif: " right"
  240.  * 3: 0003 zero padded
  241.  * 3: 3 left justif.
  242.  * 3: 3 right justif.
  243.  * -3: -003 zero padded
  244.  * -3: -3 left justif.
  245.  * -3: -3 right justif.
  246.  */

  247. #endif

栈溢出
FreeRTOS 提供了多种特性来辅助跟踪调试栈相关的问题。

uxTaskGetStackHighWaterMark() API 函数
每个任务都独立维护自己的栈空间,栈空间总量在任务创建时进行设定。uxTaskGetStackHighWaterMark()主要用来查询指定任务的运行历史中,其栈空间还差多少就要溢出。这个值被称为栈空间的”高水线(High Water Mark)”。
函数原型:
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask );

xTask
被查询任务的句柄——欲知如何获得任务句柄,详情请参见API 函数xTaskCreate()的参数pxCreatedTask。如果传入NULL 句柄,则任务查询的是自身栈空间的高水线。

返回值
任务栈空间的实际使用量会随着任务执行和中断处理过程上下浮动。
uxTaskGetStackHighWaterMark()返回从任务启动执行开始的运行历史中,栈空间具有的最小剩余量。这个值即是栈空间使用达到最深时的剩下的未使用的栈空间。这个值越是接近0,则这个任务就越是离栈溢出不远了。

运行时栈侦测 —— 概述
FreeRTOS 包含两种运行时栈侦测机制,由FreeRTOSConfig.h 中的配置常量configCHECK_FOR_STACK_OVERFLOW 进行控制。这两种方式都会增加上下切换开销。
栈溢出钩子函数(或称回调函数)由内核在侦测到栈溢出时调用。要使用栈溢出钩子函数,需要进行以下配置:
(1)在FreeRTOSConfig.h 中把configCHECK_FOR_STACK_OVERFLOW 设为1 或2。
(2)提供钩子函数的具体实现

void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName );

栈溢出钩子函数只是为了使跟踪调试栈空间错误更容易,而无法在栈溢出时对其进行恢复。函数的入口参数传入了任务句柄和任务名,但任务名很可能在溢出时已经遭到破坏。
栈溢出钩子函数还可以在中断的上下文中进行调用。某些微控制器在检测到内存访问错误时会产生错误异常,很可能在内核调用栈溢出钩子函数之前就触发了错误异常中断。

运行时栈侦测 —— 方法1
当configCHECK_FOR_STACK_OVERFLOW 设置为1 时选用方法1。
任务被交换出去的时候,该任务的整个上下文被保存到它自己的栈空间中。这时任务栈的使用应当达到了一个峰值。当configCHECK_FOR_STACK_OVERFLOW 设为
1 时,内核会在任务上下文保存后检查栈指针是否还指向有效栈空间。一旦检测到栈指针的指向已经超出任务栈的有效范围,栈溢出钩子函数就会被调用。

方法1 具有较快的执行速度,但栈溢出有可能发生在两次上下文保存之间,这种情况不会被侦测到。

运行时栈侦测 —— 方法2
将configCHECK_FOR_STACK_OVERFLOW 设为2 就可以选用方法2。方法2在方法1 的基础上进行了一些补充。
当创建任务时,任务栈空间中就预置了一个标记。方法2 会检查任务栈的最后20个字节,查看预置在这里的标记数据是否被覆盖。如果最后20 个字节的标记数据与预
设值不同,则栈溢出钩子函数就会被调用。
方法2 没有方法1 的执行速度快,但测试仅仅20 个字节相对来说也是很快的。这种方法应该可以侦测到任何时候发生的栈溢出,虽然理论上还是有可能漏掉一些情况,但这些情况几乎是不可能发生的。

其它常见错误
问题现象1:在一个Demo 应用程序中增加了一个简单的任务,导致应用程序崩溃任务
创建时需要在内存堆中分配空间。许多Demo 应用程序定义的堆空间大小只够用于创建Demo 任务——所以当任务创建完成后,就没有足够的剩余空间来增加其它的任务,队列或信号量。

空闲任务是在vTaskStartScheduler()调用中自动创建的。如果由于内存不足而无法创建空闲任务,vTaskStartScheduler()会直接返回。在调用vTaskStartScheduler()后加上一条空循环[for(;;)]可以使这种错误更加容易调试。

如果要添加更多的任务,可以增加内存堆空间大小,或是删掉一些已存在的Demo任务。

问题现象2:在中断中调用一个API 函数,导致应用程序崩溃
除了具有后缀为”FromISR”函数名的API 函数,千万不要在中断服务例程中调用其它API 函数。

问题现象3:有时候应用程序会在中断服务例程中崩溃
需要做的第一件事是检查中断是否导致了栈溢出。

在不同的移植平台和不同的编译器上,中断的定义和使用方法是不尽相同的——所以,需要做的第二件事是检查在中断服务例程中使用的语法,宏和调用约定是否符合Demo 程序的文档描述,以及是否和Demp 程序中提供的中断服务例程范例相同。

如果应用程序工作在Cotex M3 上,需要确定给中断指派优先级时,使用低优先级号数值表示逻辑上的高优先级中断,因为这种方式不太直观,所以很容易被忘记。一个比较常见的错误就是,在优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断中调用了FreeRTOS API 函数。

问题现象4:在启动第一个任务时,调度器就崩溃了
如果使用的是ARM7,那么请确定调用vTaskStartScheduler()时处理器处于管理模式(Supervisor mode)。最简单的方式就是在main()之前的C 启动态码中将处理器设置为管理模式。ARM7 的Demo 应用程序就是这么做的。如果处理器不在管理模式下,调度器是无法启动的。

问题现象5:临界区无法正确嵌套
除了taskENTER_CRITICA()和taskEXIT_CRITICAL(),千万不要在其它地方修改控制器的中断使能位或优先级标志。这两个宏维护了一个嵌套深度计数,所以只有当所有的嵌套调用都退出后计数值才会为0,也才会使能中断。

问题现象6:在调度器启动前应用程序就崩溃了
如果一个中断会产生上下文切换,则这个中断不能在调度器启动之前使能。这同样适用于那些需要读写队列或信号量的中断。在调度器启动之前,不能进行上下文切换。还有一些API 函数不能在调度器启动之前调用。在调用vTaskStartScheduler()之前,最好是限定只使用创建任务,队列和信号量的API 函数。

问题现象7:在调度器挂起时调用API 函数,导致应用程序崩溃
调用vTaskSuspendAll()使得调度器挂起,而唤醒调度器调用xTaskResumeAll()。千万不要在调度器挂起时调用其它API 函数。

问题现象8:函数原型pxPortInitialiseStack()导致编译失败
每种移植都需要定义一个对应的宏,以把正确的内核头文件加入到工程中。如果编译函数原型pxPortInitialiseStack()时出错,这种现象基本上可以确定是因为没有正确定义相应的宏。

可以基本相应平台的Demo 工程建立新的应用程序。这种方式就不用担心没有包含正确的文件,也不必担心没有正确地配置编译器选项。

阅读(4211) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~