当前位置:网站首页>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
边栏推荐
- Face to face summary 2
- Canvas learning Chapter 1
- The whole house intelligence bet by the giant is driving the "self revolution" of Hisense, Huawei and Xiaomi
- My heart's broken! A woman's circle of friends envied others for paying wages on time and was fired. Even her colleagues who liked her were fired together
- 岛屿的个数
- DataBinding的使用五
- 【Appium】测试时遇到手机内嵌H5页面的切换问题
- LeetCode簡單題之計算字符串的數字和
- 数据安全问题已成隐患,看vivo如何让“用户数据”重新披甲
- JS converts tree structure data into one-dimensional array data
猜你喜欢
There are some problems when using numeric type to query string type fields in MySQL
Discussion on ES6 tail tune optimization
mysql查询字符串类型的字段使用数字类型查询时问题
My heart's broken! A woman's circle of friends envied others for paying wages on time and was fired. Even her colleagues who liked her were fired together
Rearranging log files for leetcode simple question
[appium] encountered the problem of switching the H5 page embedded in the mobile phone during the test
智能名片小程序名片详情页功能实现关键代码
总线结构概述
Qt读写XML文件
如何在SQL Server中导入excel数据,2019版
随机推荐
渗透测试面试合集---HVV---
Qt编译QtXlsx库
岛屿的个数
Common regular expressions
Depth of binary tree
Qt利用QtXlsx操作excel文件
js常用数组方法
[effective go Chinese translation] function
Multi vision slam
英语课小记(四)
数据安全问题已成隐患,看vivo如何让“用户数据”重新披甲
An article understands variable lifting
Manipulator motion planning in 3C assembly
LeetCode15. Sum of three
Mobile web (Font Icon, plane conversion, color gradient)
396. Rotate Function
Kubernetes in browser and IDE | interactive learning platform killercoda
扎心了!一女子发朋友圈羡慕别人按时发工资被开除,连点赞的同事也一同被开除了...
php生成短链接:将数字转成字母,将字母转成数字
干货!以点为形:可微分的泊松求解器