当前位置:网站首页>stm32以及freertos 堆栈解析
stm32以及freertos 堆栈解析
2022-04-23 07:17:00 【NULL_1969】
以前在mcu编程的时候没有太注意堆栈的情况,只知道需要将堆栈设置的大一点。现在逐步使用freertos,在freertos中也有关于堆栈的设置,freertos的堆栈和启动文件中的堆栈关系是什么?为了以后使用的无误,本次一次性把这些弄清楚。
1、定义
堆栈是一个特定的存储区或者寄存器。一般在内存总开辟一块区域作为堆栈,叫做软件堆栈;用寄存器构成的堆栈,叫硬件堆栈。大多数情况下,我们使用的都是软件堆栈。

在stm或者gd32的启动文件中的堆栈就是软件堆栈。
堆栈中数据的存储,都要遵循先进后出的原则,可以类比为木桶,先放进去的数据在桶的底部,后放进去的数据在桶的顶部。取数据的时候是先在桶的顶部取数据,在从桶的底部取数据。
单片机应用中,堆栈是个特殊存储区,堆栈属于RAM空间的一部分,堆栈用于函数调用、中断切换时保存和恢复现场数据。堆栈中的物体具有一个特性:第一个放入堆栈中的物体总是被最后拿出来, 这个特性通常称为先进后出 (FILO—First-In/Last-Out)。 堆栈中定义了一些操作, 两个最重要的是PUSH和POP。 PUSH(入栈)操作:堆栈指针(SP)加1,然后在堆栈的顶部加入一 个元素。POP(出栈)操作相反,出栈则先将SP所指示的内部ram单元中内容送入直接地址寻址的单元中(目的位置),然后再将堆栈指针(SP)减1。这两种操作实现了数据项的插入和删除。
关于堆栈的理论知识就简述这么多,部分来自于百度百科。在mcu中我们只要知道上图设置的堆栈实际是位于mcu的ram区就可以了。在mcu编程中,貌似对ram的关注都不多,更多的是关注flash的容量,falsh大小能不能存储的了我的固件之类的。对ram就没有知道有这个东西,但真没有细致关注。
2、堆栈的空间分配
在mcu中,heap和stack的使用者是不同的。
stack(栈):由系统自动分配释放,存放的函数的参数值,局部变量的值。这个空间用户操作不了的。
heap(堆):由用户分配及释放,也就是调用malloc 和free,时操作的空间就是堆空间。
站原子论坛上,建议,如果没有调用系统的malloc和free函数,heap空间可以设置为0.
从空间分配上,最好理解的就是heap,完全由用户操作。比较难理解的是stack,那么系统到底是怎么使用的栈空间的呢。
3、stm32栈空间及编译的固件大小
用keil编译工程完成后,output窗口会显示如下信息:

上图的中的code ro-data rw-data zi-data是什么意思,哪些是站ram空间,哪些是站flash空间呢?在上文中我们说到stack是有系统自动分配的,那么我把启动文件中的stack_size改变一下,再看下编译输出结果。

将stack_size 由0x2000改为0x1000,有8k字节改为4k字节。编译后的结果如下图。

与上一次的编译对比发现,只有zi-data段变化了由10508变成了6412.二者的差值刚好是4096,就是stack_size的减少值。
在将heap_size设置为0x1000,发现编译出来的没有任何变化。
3.1 存储数据段
Code是代码占用的空间;
RO-data是 Read Only 只读常量的大小,如const型;
RW-data是(Read Write) 初始化了的可读写变量的大小;
ZI-data是(Zero Initialize) 没有初始化的可读写变量的大小。
最终,烧写时flash被占有的空间为:
falsh = Code + RO-Data + RW-Data
程序运行时ram被占有的空间为
** ram = RW-Data + ZI-Data**
计算一下falsh占有空间 = 12568+ 368+ 180 = 13116字节

看下最终生成的bin文件刚好是13116字节大小。
找到了flash占用的空间,我们再来看看ram占用的空间。ram占用空间在生成的.map文件中。

map文件中显示ram占用的size为0x19c0,转换为10进制为6592,而生成RW-Data + ZI-Data=180+6412=6592.这个也得到了验证。
根据上面的图还可以得出:
** RW-Data= .data** data的空间累加即为RW-Data
** ZI-Data= .bss+STACK** .bss的空间累加+STACK即为ZI-Data
上图中也可以看出STACK=0x1000刚好是在启动文件中设定的值。也可以看出当前芯片的ram总空间为0x18000=96kB。
到这里我们基本梳理出来的ram和falsh空间的构成。接下来看看freertos的堆栈是啥。
3.2freertos堆栈
我使用的是freertos是v10版本,内存分配采用的heap4.c
在freertos的FreeRTOSConfig.h中,需要设置堆的大小
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 1 * 1024 ) )
这里做测试我就只设置为1024字节。全局搜索下configTOTAL_HEAP_SIZE ,发现是在heap_4.c中使用了。

按照当前的设置可以看到,freertos是直接定义了一个静态数组 hcHeap大小为configTOTAL_HEAP_SIZE。
configAPPLICATION_ALLOCATED_HEAP这个宏定义一般是将堆设置为外部sram才会用到。这里暂时不多讲。
从上图可以得出这样的结论,默认情况下,freertos的堆就是自定义的一个大型数组,与启动文件中设置heap_size没有任何关系。所以只要只要没有调用系统的malloc函数,启动文件heap_size还真可以设置为0.
接下来,将configTOTAL_HEAP_SIZE改成20Kb进行编译。结果如下

zi-data由6142变成了25868,二者差值刚好是19kB。编译后的map文件内容如下:

与上一次的对比,.bss段的heap_4.o占用的空间由0x400(1kB)变成了0x5000(20kB),刚好是configTOTAL_HEAP_SIZE宏定义设定的大小。
4、结论
经过了上面的实验分析可以得出如下结论:
- 当freertos采用heap_4内存分配方案时,stm32启动文件中的stack_size 和heap_size与freertos中设置的堆大小没有任何关系。
- 只要代码中没有使用系统malloc函数,启动文件heap_size可以设置为0
- mcu运行时的ram空间= RW-Data+ZI-Data+启动文件中的heap_size,所以可以根据这个公式来设置freertos堆的大小。freertos的堆尽量要设置的大一点。
5、freertos堆常用函数heap_4

上还是那个图来自freertos的官方文档,可以看出freertos的栈实际实际上也是放到了堆空间。任务控制块TCB、queue 、pvPortMalloc等使用都是堆空间,对空间大小有configTOTAL_HEAP_SIZE决定。
5.1常用heap相关函数
获取剩余heap空间大小。
size_t xPortGetFreeHeapSize( void );
获取最小未分配的空间大小
size_t xPortGetMinimumEverFreeHeapSize( void );
动态内存分配及释放函数
void * pvPortMalloc( size_t xWantedSize )
void vPortFree( void * pv )
5.2任务栈空间占用的大小。
freertos中每个任务新建的时候都要设置栈空间。栈空间可以在FreeRTOSConfig.h中设定一个最小值
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) //设置最小栈空间为256字节
记住这里设定的和任务新建时设定的大小单位并不是字节,而是字,字占4个字节,所以上述设定的最小栈空间为256字节。由上文可知,freertos的栈空间实际是在堆里面。
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )
#define pvPortMallocStack pvPortMalloc
在新建任务函数中,可以看到,实际上使用pvPortMalloc分配的一段空间作为栈空间,大小为usStackDepth *sizeof( StackType_t ) 。而StackType_t 大小实际上是uint32_t 为4字节。

版权声明
本文为[NULL_1969]所创,转载请带上原文链接,感谢
https://blog.csdn.net/sinat_36568888/article/details/124320985
边栏推荐
- Listed on the Shenzhen Stock Exchange: the market value is 5.2 billion yuan. Lu is the East and his daughter is American
- LeetCode简单题之三除数
- Convert object to URL
- Face to face summary 2
- 单点登录 SSO
- 使用JWT生成与解析Token
- Situational leaders - Chapter 7, solving performance problems
- Planification du mouvement du manipulateur dans l'assemblage 3c
- LeetCode15. Sum of three
- Qt读取路径下所有文件或指定类型文件(含递归、判断是否为空、创建路径)
猜你喜欢

LeetCode简单题之三除数

关于ORB——SLAM运行中关键帧位置越来越近的异常说明

青苹果影视系统源码 影视聚合 影视导航 影视点播网站源码

Data security has become a hidden danger. Let's see how vivo can make "user data" armor again

Mobile terminal layout (3D conversion, animation)

浏览器中的 Kubernetes 和 IDE | 交互式学习平台Killercoda

Brief description of CPU

Briefly describe the hierarchical strategy of memory

智能名片小程序名片详情页功能实现关键代码

岛屿的个数
随机推荐
Implementation principle of instanceof
Talk about the basic but not simple stock data
Canvas learning Chapter 1
高精度焊接机械臂定位
一键清理项目下pycharm和Jupyter缓存文件
3C裝配中的機械臂運動規劃
利用Js实现一个千分位
Fibula dynamic programming
php高精度计算
DataBinding的使用五
Flatten arrays
Interesting JS code
分布式服务治理Nacos
vslam PPT
JS common array methods
C outputs a two-dimensional array with the following characteristics.
青苹果影视系统源码 影视聚合 影视导航 影视点播网站源码
怎么读书读论文
以下程序实现从字符串str中删除第i个字符开始的连续n个字
LeetCode简单题之计算字符串的数字和