请选择 进入手机版 | 继续访问电脑版

硬汉嵌入式论坛

 找回密码
 立即注册
查看: 8072|回复: 12
收起左侧

[FreeRTOS教程] 第11章 FreeRTOS任务栈大小确定及其溢出检测

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-8-19 10:09:39 | 显示全部楼层 |阅读模式



第11章     FreeRTOS任务栈大小确定及其溢出检测



    本章节为大家讲解FreeRTOS任务栈大小的确定方法以及栈溢出检测方法。给任务分配多大的栈空间,一直是初学者比较头疼的问题,本章就主要为大家讲解如何解决此问题。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
11.1 任务栈大小的确定
11.2 什么是栈溢出
11.3 FreeRTOS的栈溢出检测机制
11.4 实验例程说明(任务栈溢出检测方式一)
11.5 实验例程说明(任务栈溢出检测方式二)
11.6总结




11.1 任务栈大小的确定


    在基于RTOS的应用设计中,每个任务都需要自己的栈空间,应用不同,每个任务需要的栈大小也是不同的。将如下的几个选项简单的累加就可以得到一个粗略的栈大小:
u 函数的嵌套调用,针对每一级函数用到栈空间的有如下四项:
    l 函数局部变量。
    l 函数形参,一般情况下函数的形参是直接使用的CPU寄存器,不需要使用栈空间,但是这个函数中如果还嵌套了一个函数的话,这个存储了函数形参的CPU寄存器内容是要入栈的。所以建议大家也把这部分算在栈大小中。
    l 函数返回地址,针对M3和M4内核的MCU,一般函数的返回地址是专门保存到LR(Link Register)寄存器里面的,如果这个函数里面还调用了一个函数的话,这个存储了函数返回地址的LR寄存器内容是要入栈的。所以建议大家也把这部分算在栈大小中。
    l 函数内部的状态保存操作也需要额外的栈空间。


u 任务切换,任务切换时所有的寄存器都需要入栈,对于带FPU浮点处理单元的M4内核MCU来说,FPU寄存器也是需要入栈的。


u 针对M3内核和M4内核的MCU来说,在任务执行过程中,如果发生中断:
    l  M3内核的MCU有8个寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈。
    l  M4内核的MCU有8个通用寄存器和18个浮点寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈。


u  进入中断以后使用的局部变量以及可能发生的中断嵌套都是用的系统栈,这点要注意。
    实际应用中将这些都加起来是一件非常麻烦的工作,上面这些栈空间加起来的总和只是栈的最小需求,实际分配的栈大小可以在最小栈需求的基础上乘以一个安全系数,一般取1.5-2。上面的计算是我们用户可以确定的栈大小,项目应用中还存在无法确定的栈大小,比如调用printf函数就很难确定实际的栈消耗。又比如通过函数指针实现函数的间接调用,因为函数指针不是固定的指向一个函数进行调用,而是根据不同的程序设计可以指向不同的函数,使得栈大小的计算变得比较麻烦。
    另外还要注意一点,建议不要编写递归代码,因为我们不知道递归的层数,栈的大小也是不好确定的。
    一般来说,用户可以事先给任务分配一个大的栈空间,然后通过第8章介绍的调试方法打印任务栈的使用情况,运行一段时间就会有个大概的范围了。这种方法比较简单且实用些。
    l 函数栈大小确定
    函数的栈大小计算起来是比较麻烦的,那么有没有简单的办法来计算呢?有的,一般IDE开发环境都有这样的功能,比如MDK会生成一个htm文件,通过这个文件用户可以知道每个被调用函数的最大栈需求以及各个函数之间的调用关系。但是MDK无法确定通过函数指针实现函数调用时的栈需求。另外,发生中断或中断嵌套时的现场保护需要的栈空间也不会统计。
    关于MDK生成的map和htm文件的使用,我们安富莱电子有出过一期视频教程,可以在这里查看:http://www.armbbs.cn/forum.php?mod=viewthread&tid=15408

评分

参与人数 1金币 +10 收起 理由
Xaurora + 10 赞一个!

查看全部评分

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:14:00 | 显示全部楼层
11.2 什么是栈溢出


    前面为大家讲解了如何确定任务栈的大小,那什么又是栈溢出呢?简单的说就是用户分配的栈空间不够用了,溢出了。下面我们举一个简单的实例,栈生长方向从高地址向低地址生长(M4和M3是这种方式)。
11.1.png

(1)   上图标识1的位置是RTOS的某个任务调用了函数test()前的SP栈指针位置。
  1. void  test (void);
  2. {
  3.      int i;
  4.      int array[10];
  5.      :
  6.      :
  7.      // Code
  8. }
复制代码
(2)   上图标识2的位置是调用了函数test需要保存返回地址到栈空间。这一步不是必须的,对于M3和M4内核是先将其保存到LR寄存器中,如果LR寄存器中有保存上一级函数的返回地址,需要将LR寄存器中的内容先入栈。
(3)   上图标识3的位置是局部变量int i和int array[10]占用的栈空间,但申请了栈空间后已经越界了。这个就是所谓的栈溢出了。如果用户在函数test中通过数组array修改了这部分越界区的数据且这部分越界的栈空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
(4)   上图标识4的位置是局部变量申请了栈空间后,栈指针向下偏移(返回地址+变量i+10个数组元素)*4 =48个字节。
(5)   上图标识5的位置可能是其它任务的栈空间,也可能是全局变量或者其它用途的存储区,如果test函数在使用中还有用到栈的地方就会从这里申请,这部分越界的空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:16:34 | 显示全部楼层
11.3 FreeRTOS的栈溢出检测机制


    FreeRTOS提供了两种栈溢出检测机制,这两种检测都是在任务切换时才会进行:
u  方法一
    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
       void vApplicationStackOverflowHook( TaskHandle_t xTask,
                                                 signed char *pcTaskName );
    用户可以在钩子函数里面做一些处理。这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中出现过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法一需要用户在FreeRTOSConfig.h文件中配置如下宏定义:
#define  configCHECK_FOR_STACK_OVERFLOW   1
u  方法二
    任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区数据被修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
    使用方法二需要用户在FreeRTOSConfig.h文件中配置如下宏定义:
      #define  configCHECK_FOR_STACK_OVERFLOW   2
l 栈溢出检测方法
    除了FreeRTOS提供的这两种栈溢出检测机制,还有其它的栈溢出检测机制,大家可以在Mircrium官方发布的如下这个博文中学习:https://www.micrium.com/detecting-stack-overflows-part-2-of-2/
l 钩子函数
    钩子函数的主要作用就是对原有函数的功能进行扩展,用户可以根据自己的需要往里面添加相关的测试代码,大家可以在FreeRTOS工程中检索这个钩子函数vApplicationStackOverflowHook所在的位置。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:26:42 | 显示全部楼层
11.4   实验例程说明(任务栈溢出检测方式一)


11.4.1 STM32F103开发板实验


配套例子:
    V4-305_FreeRTOS实验_任务栈溢出检测方法一(模拟栈溢出)
实验目的:
    1.     学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。
    2.     FreeRTOS的任务栈溢出检测方法一说明:
        a.     FreeRTOSConfig.h文件中配置宏定义:
            #define  configCHECK_FOR_STACK_OVERFLOW    1
        b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
        voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                                                 signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
    c.     这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
    d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
      FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    1
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
      系统主频72MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
      定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   1
      任务栈检测采用方式一。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
    定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
11.2.png

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
11.3.png

程序设计:
u  任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
         #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
u  系统栈大小分配:
11.4.png

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:34:15 | 显示全部楼层
11.4.2 STM32F407开发板实验


配套例子:
    V5-305_FreeRTOS实验_任务栈溢出检测方法一(模拟栈溢出)
实验目的:
    1.     学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。
    2.     FreeRTOS的任务栈溢出检测方法一说明:
       a.     FreeRTOSConfig.h文件中配置宏定义:
          #define  configCHECK_FOR_STACK_OVERFLOW    1
       b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
          voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                                                          signed char *pcTaskName );
          用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
       c.     这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
       d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    1
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long )168000000 )   
      系统主频168MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) (30 * 1024 ) )
      定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   1
      任务栈检测采用方式一。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
      定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
11.5.png

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
11.6.png

程序设计:
u  任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
u  系统栈大小分配:
11.7.png

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:43:36 | 显示全部楼层
11.4.3  STM32F429开发板实验


配套例子:
    V6-305_FreeRTOS实验_任务栈溢出检测方法一(模拟栈溢出)
实验目的:
     1.     学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。
     2.     FreeRTOS的任务栈溢出检测方法一说明:
       a.     FreeRTOSConfig.h文件中配置宏定义:
          #define  configCHECK_FOR_STACK_OVERFLOW    1
       b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
         voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                                                   signed char *pcTaskName );
          用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
       c.     这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出,任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
       d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    1
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )  
      系统主频168MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
       定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   1
      任务栈检测采用方式一。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
      定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
     l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
     l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
     l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):

程序设计:
u  任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30* 1024 ) )
u  系统栈大小分配:

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 10:55:26 | 显示全部楼层
11.5   实验例程说明(任务栈溢出检测方式二)



11.5.1 STM32F103开发板实验


配套例子:
    V4-306_FreeRTOS实验_任务栈溢出检测方法二(模拟栈溢出)
实验目的:
    1.     学习FreeRTOS的任务栈溢出检测方法二(模拟栈溢出)。
    2.     FreeRTOS的任务栈溢出检测方法二说明:
      a.     FreeRTOSConfig.h文件中配置宏定义:
        #define  configCHECK_FOR_STACK_OVERFLOW    2
      b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
        voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                                             signed char *pcTaskName );
        用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
      c.     任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
      d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    2
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
      系统主频72MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
      定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   2
      任务栈检测采用方式二。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
      定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
     l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
     l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
     l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里 面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
11.11.png

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
11.12.png

程序设计:
u  任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
     任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
u  系统栈大小分配:
11.13.png

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 11:01:27 | 显示全部楼层
11.5.2   STM32F407开发板实验


配套例子:
     V5-306_FreeRTOS实验_任务栈溢出检测方法二(模拟栈溢出)
实验目的:
    1.     学习FreeRTOS的任务栈溢出检测方法二(模拟栈溢出)。
    2.     FreeRTOS的任务栈溢出检测方法二说明:
        a.     FreeRTOSConfig.h文件中配置宏定义:
          #define  configCHECK_FOR_STACK_OVERFLOW    2
        b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
          voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                                         signed char *pcTaskName );
          用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
        c.     任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常。这种情况下,栈溢出检测功能也是检测不到的。
        d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    2
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )  
      系统主频168MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
      定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   2
      任务栈检测采用方式二。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
      定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
     l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
     l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
     l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
11.14.png

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
11.15.png

程序设计:
u  任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
u  系统栈大小分配:
11.16.png

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 11:14:12 | 显示全部楼层
11.5.3   STM32F429开发板实验


配套例子:
    V6-306_FreeRTOS实验_任务栈溢出检测方法二(模拟栈溢出)
实验目的:
    1.     学习FreeRTOS的任务栈溢出检测方法二(模拟栈溢出)。
    2.     FreeRTOS的任务栈溢出检测方法二说明:
        a.     FreeRTOSConfig.h文件中配置宏定义:
          #define  configCHECK_FOR_STACK_OVERFLOW    2
        b.    在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
          voidvApplicationStackOverflowHook( TaskHandle_t xTask,
                                 signed char *pcTaskName );
          用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
         c.     任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常。这种情况下,栈溢出检测功能也是检测不到的。
        d.    本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,模拟栈溢出。
    3.     各个任务实现的功能如下:
               vTaskUserIF任务   :按键消息处理。
               vTaskLED任务     :LED闪烁。
               vTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
               vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. #define configCHECK_FOR_STACK_OVERFLOW    2
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
u #define configUSE_PREEMPTION        1
      使能抢占式调度器
u #define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )  
      系统主频168MHz。
u #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
      系统时钟节拍1KHz,即1ms。
u #define configMAX_PRIORITIES          ( 5 )
      定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
      定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u #define configCHECK_FOR_STACK_OVERFLOW   2
      任务栈检测采样方式二。
u #define INCLUDE_vTaskSuspend               1
      使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
u configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
      定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    l 使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    l 对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    l 这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
11.17.png

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
11.18.png

程序设计:
u  任务栈大小分配:
        vTaskUserIF任务   :2048字节
        vTaskLED任务     :2048字节
        vTaskMsgPro任务 :2048字节
        vTaskStart任务    :2048字节
        任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
        #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
u  系统栈大小分配:
11.19.png

u  FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24. }
复制代码
u  FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
u  四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下 删除任务vTaskLED */
  35.                    case KEY_DOWN_K2:         
  36.                        printf("K2键按下,模拟任务栈溢出检测\\r\\n");
  37.                        StackOverflowTest();
  38.                        break;
  39.                   
  40.                    /* 其他的键值不处理 */
  41.                    default:                    
  42.                        break;
  43.               }
  44.          }
  45.         
  46.          vTaskDelay(20);
  47.      }
  48. }
  49. /*
  50. *********************************************************************************************************
  51. *    函 数 名: vTaskLED
  52. *    功能说明: LED闪烁
  53. *    形    参: pvParameters 是在创建该任务时传递的形参
  54. *    返 回 值: 无
  55. *   优 先 级: 2
  56. *********************************************************************************************************
  57. */
  58. static void vTaskLED(void *pvParameters)
  59. {
  60.     while(1)
  61.     {
  62.          bsp_LedToggle(2);
  63.         vTaskDelay(200);
  64.     }
  65. }
  66. /*
  67. *********************************************************************************************************
  68. *    函 数 名: vTaskMsgPro
  69. *    功能说明: 消息处理,这里是用作LED闪烁   
  70. *    形    参: pvParameters 是在创建该任务时传递的形参
  71. *    返 回 值: 无
  72. *   优 先 级: 3
  73. *********************************************************************************************************
  74. */
  75. static void vTaskMsgPro(void *pvParameters)
  76. {
  77.     while(1)
  78.     {
  79.          bsp_LedToggle(3);
  80.         vTaskDelay(300);
  81.     }
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: vTaskStart
  86. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  87. *    形    参: pvParameters 是在创建该任务时传递的形参
  88. *    返 回 值: 无
  89. *   优 先 级: 4
  90. *********************************************************************************************************
  91. */
  92. static void vTaskStart(void *pvParameters)
  93. {
  94.     while(1)
  95.     {
  96.          /* 按键扫描 */
  97.          bsp_KeyScan();
  98.         vTaskDelay(10);
  99.     }
  100. }
复制代码
u  栈溢出测试函数:
    栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. /*
  5. *********************************************************************************************************
  6. *    函 数 名: StackOverflowTest
  7. *    功能说明: 任务栈溢出测试
  8. *    形    参: 无
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. static void StackOverflowTest(void)
  13. {
  14.      int16_t i;
  15.      uint8_t buf[2048];
  16.    
  17.      (void)buf; /* 防止警告 */
  18.    
  19.      /*
  20.        1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。
  21.           因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址
  22.           是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。
  23.              因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致
  24.           进入到硬件异常。
  25.        2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接
  26.           进入硬件异常。
  27.        3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小
  28.           ------uint8_t ucKeyCode;
  29.           ------uint8_t pcWriteBuffer[500];
  30.           这里再申请如下这么大的栈空间
  31.           -------int16_t i;
  32.           -------uint8_t buf[2048];
  33.           必定溢出。
  34.      */
  35.      for(i = 2047; i >= 0; i--)
  36.      {
  37.          buf[i] = 0x55;
  38.          vTaskDelay(1);
  39.      }
  40. }
复制代码
u  栈溢出钩子函数:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vApplicationStackOverflowHook
  4. *    功能说明: 栈溢出的钩子函数
  5. *    形    参: xTask        任务句柄
  6. *             pcTaskName   任务名
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
  11. {
  12.      printf("任务:%s 发现栈溢出\\r\\n", pcTaskName);
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-19 11:14:50 | 显示全部楼层
11.6 总结

    本章节主要为大家讲解了任务栈大小的确定以及栈溢出检测的两种方法,建议实际操作下本章节配套的例子,对栈溢出有一个感性的认识,随着以后的学习再深入理解并运用。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

3

主题

12

回帖

21

积分

新手上路

积分
21
发表于 2019-1-10 09:38:44 | 显示全部楼层
挺好的,给你个赞
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
发表于 2023-5-23 12:57:24 | 显示全部楼层
大神!这篇帖子太精辟了!
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 2023-9-28 10:33:56 | 显示全部楼层
感谢楼主,终于明白了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-12-13 20:59 , Processed in 0.336667 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表