1.问题提出
在编写新软件时,由于某功能需要使用RAM约24KB,而新模块的总RAM是96KB,该某功能一旦开启则需要占用四分之一的RAM空间。为了保证在开启某功能后,新模块拥有足够的RAM空间,故需要对模块的软件进行优化。其中分配给任务函数的堆栈空间就是一个优化的部分。
2.问题分析
新软件使用的操作系统是FreeRTOS,该操作系统提供了任务堆栈的查看API。我们可以在串口打印API的返回值,查看到各个任务堆栈使用情况。如下图所示。
观察串口打印的信息,我们发现分配给任务的堆栈有大部分是没有使用到的。如第一个任务的usrTimer 中分配了 512B的空间,而当前没有使用到的栈空间是400B,有大量的栈空间是空闲的,大大的减少了空间的使用效率。
但是我们并不能根据一时的栈空间使用情况而判断该任务函数就是存在空闲的栈空间,我们是需要的是确认任务函数的最大栈空间,并根据这个最大的栈空间来确定任务函数的栈空间分配。
3.环境搭建
Keil5编译软件
ICA400-06模块
J-Link调试器
4.解决方案
步骤一:添加编译选项
在Keil5编译工具中的Options中选择Linker框,在Misc controls中添加编译选项 –info stack。
步骤二:重新编译工程,在生成的map文件中找到 Image Stack Usage Information.
Image Stack Usage Information中就可以查看得到 每个函数的最大堆栈使用情况。如下图所示
步骤三:分析任务函数的最大栈空间,调整在创建任务时分配给该任务函数的最大栈空间。
查看 modbusrtu_sample_thread_handle 函数的最大栈空间是0x718=1816,故该分配给该任务函数的栈空间大于1816B即可
步骤四:如果任务函数的最大栈空间过大,不满足预期,则可通过函数调用链优化函数栈空间。
通过Call chain查看最大栈空间的函数调用链。
如下图所示,modbusrtu_sample_thread_handle任务函数使用到最大栈空间的函数调用链是modbusrtu_sample_thread_handle => handle_remote_rtu_read_mode => mbmaster_read_input_registers => mbmaster_main_type1_process => mb_mutex_unlock => xQueueGenericSend => xTaskResumeAll => xTaskIncrementTick
我们可以逐一查看该调用链中的每个函数的使用情况,把函数中非动态申请的大数组或其他大内存的数据结构改成动态申请空间的方式,减少函数栈的使用。如下图所示。
步骤五:优化并确认任务函数的最大栈空间
在根据函数调用链优化函数后,再次重复步骤一、二、三确定任务函数的最大栈空间,从而确定任务函数的最大栈空间,减少内存空间的浪费。
如下图所示,modbusrtu_sample_thread_handle的堆栈空间在未优化之前是0x718=1816,在优化后,该任务函数的堆栈空间是0x1a0=416,所以通过优化,我们大大的减少了任务栈空间的浪费。
下图是优化全部任务函数后的一个系统堆栈使用情况。
5.经验总结
在使用keil5编译工具时,可以通过添加编译选项—info stack,并根据生成的.map文件分析函数的堆栈使用情况,根据实际的使用情况给对应的任务函数分配最大堆栈,减少RAM内存空间的浪费,提高RAM内存空间的使用效率。
6.疑问
注意:若存在函数指针,该方法貌似不能够 追踪到 函数指针指向的 函数堆栈