RT-Thread 堆区大小设置
一、利用栈区的空间作为堆区
看过我之前的笔记的小伙伴都知道,以前我是通过申请栈区的空间使用的,感兴趣的小伙伴可以看我之前的笔记,RT-Thread移植到stm32。
在board.c文件文件中的代码如下所示:
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) #define RT_HEAP_SIZE 1024 static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4) RT_WEAK void *rt_heap_begin_get(void) { return rt_heap; } RT_WEAK void *rt_heap_end_get(void) { return rt_heap + RT_HEAP_SIZE; } #endif
从上面的代码中可以看出,申请了一个栈空间rt_heap数组,大小为4K。但是这样在使用RT-Thread堆内存的时候就不太友好了。缺点如下:
-
堆内存大小是固定的,使得RT-Thread系统没有最大化的利用STM32的内存空间。
-
在RT-Thread的堆内存中使用了相应的内存算法,容易造成内存碎片化。,所以应将栈剩余的空间全部用于堆内存使用,有关算法可以看RT-Thread内存管理。
-
发生堆栈溢出的时候,不利于问题的查找。
所以我们使用的时候,应该获取堆区与栈区的内存分界地址,以便于给RT-Thread的堆内存使用。
二、STM32内存知识
在获取堆内存与栈内存之前,我们需要简单的理解STM32的内存知识,如果想了解更详细的内容,可以看我之前的笔记,STM32内存知识。
- code:代码存储区,存放函数体的二进制代码
- Ro-data:只读数据存储区,存放字常量数据类型(如const类型)程序结束后有系统自动释放
- RW-data:初始化可读写变量的大小,程序结束后由系统自动释放。
- ZI-data:没有初始化的可读写变量大小,程序结束后由系统自动释放。
- heap:堆区,一般由程序员分配释放,若程序员不释放,程序结束时可能由OS释放。
- stack:栈区,由编译器自动分配释放,存放函数的参数值,局部变量的值等。
- RO Size:包含Code及RO Data,表示只读数据占用Flash空间的大小。
- RW Size:包含RW Data及ZI Data,表示运行时占用的RAM的大小。
- ROM Size包含Code,RO Data及RW Data,表示烧写程序所占用的Flash的大小。
-
STM32程序运行的流程。如下图所示:
-
栈向下生长,内存地址由高至低;堆向上,内存地址由低至高,堆栈之间没有固定的界限,如下图所示:
从上面两点可以看出来,RAM是包含堆区和栈区,而堆区与栈区没有明确的界限,所以内存中没有使用的栈空间都可以申请为堆内存。我们只需要找出栈空间接结束地址,也是ZI段和结束地址即可。
三、获取栈空间的结束地址
因为不同编译器的内存标识不一样,所以需要注意一下不同环境下的获取方式。
-
MDK环境下,栈结束地址的获取,如下所示:
extern unsigned char Image$$ER_IROM1$$Limit; // 获取RW段在FLASH中的加载地址 extern unsigned char Image$$RW_IRAM1$$Base; // 获取RW段在RAM中的运行地址 extern unsigned char Image$$RW_IRAM1$$RW$$Limit; // 获取RW段在RAM中的结束地址 extern unsigned char Image$$RW_IRAM1$$ZI$$Limit; // 获取ZI段在RAM中的结束地址 所以在MDK中只需要使用Image$$RW_IRAM1$$ZI$$Limit链接便可获得栈空间的结束地址,如下所示:
extern int Image$$RW_IRAM1$$ZI$$Limit; #define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit) -
IAR环境下,栈结束地址的获取,如下所示:
#pragma section="CSTACK" #define HEAP_BEGIN (__segment_end("CSTACK")) -
GCC环境下,栈结束地址的获取,如下所示:
extern int __bss_end; #define HEAP_BEGIN ((void *)&__bss_end)
四、RT-Thread程序更改
只需要在 board.h 文件中定义相关宏即可,然后修改 board.c 文件中rt_system_heap_init函数 的内存获取地址。
board.h 文件
/* * Change Logs: * Date Author * 2022-06-29 jiaozhu */ #ifndef __BOARD_H__ #define __BOARD_H__ #include "stm32f10x.h" #include "drv_gpio.h" #include "drv_usart.h" #ifdef __cplusplus extern "C" { #endif /*-------------------------- ROM/RAM CONFIG BEGIN --------------------------*/ #define ROM_START ((uint32_t)0x08000000) #define ROM_SIZE (64 * 1024) #define ROM_END ((uint32_t)(ROM_START + ROM_SIZE)) #define RAM_START (0x20000000) #define RAM_SIZE (20 * 1024) #define RAM_END (RAM_START + RAM_SIZE) #define STM32_SRAM1_END RAM_END /*-------------------------- GET HEAP SIZE --------------------------*/ #if defined(__CC_ARM) || defined(__CLANG_ARM) extern int Image$$RW_IRAM1$$ZI$$Limit; #define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit) #elif __ICCARM__ #pragma section="CSTACK" #define HEAP_BEGIN (__segment_end("CSTACK")) #else extern int __bss_end; #define HEAP_BEGIN ((void *)&__bss_end) #endif #define HEAP_END STM32_SRAM1_END #ifdef __cplusplus } #endif #endif /* __BOARD_H__ */
board.C 文件
/* * Copyright (c) 2006-2019, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2017-07-24 Tanek the first version * 2018-11-12 Ernest Chen modify copyright */ #include <rtthread.h> #include <board.h> #define _SCB_BASE (0xE000E010UL) #define _SYSTICK_CTRL (*(rt_uint32_t *)(_SCB_BASE + 0x0)) #define _SYSTICK_LOAD (*(rt_uint32_t *)(_SCB_BASE + 0x4)) #define _SYSTICK_VAL (*(rt_uint32_t *)(_SCB_BASE + 0x8)) #define _SYSTICK_CALIB (*(rt_uint32_t *)(_SCB_BASE + 0xC)) #define _SYSTICK_PRI (*(rt_uint8_t *)(0xE000ED23UL)) // Updates the variable SystemCoreClock and must be called // whenever the core clock is changed during program execution. extern void SystemCoreClockUpdate(void); // Holds the system core clock, which is the system clock // frequency supplied to the SysTick timer and the processor // core clock. extern uint32_t SystemCoreClock; void STM32F10x_peripheral_init(void) { // Initialize serial port USART1_Config(); // Initialize LED LED_GPIO_Config(); } static uint32_t _SysTick_Config(rt_uint32_t ticks) { if ((ticks - 1) > 0xFFFFFF) { return 1; } _SYSTICK_LOAD = ticks - 1; _SYSTICK_PRI = 0xFF; _SYSTICK_VAL = 0; _SYSTICK_CTRL = 0x07; return 0; } /** * This function will initial your board. */ void rt_hw_board_init() { STM32F10x_peripheral_init(); /* System Clock Update */ SystemCoreClockUpdate(); /* System Tick Configuration */ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND); #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END); #endif /* Set the shell console output device */ #if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE) rt_console_set_device(RT_CONSOLE_DEVICE_NAME); #endif /* Board underlying hardware initialization */ #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif } void SysTick_Handler(void) { /* enter interrupt */ rt_interrupt_enter(); rt_tick_increase(); /* leave interrupt */ rt_interrupt_leave(); }
五、堆区和栈区的空间使用查看
-
栈空间的使用情况查看
因为栈空间的管理直接是由编译管理的,所以编译完成后直接可以看到编译器查看(这里以MDK环境为例),如下图所示:
从图中可知,栈空间的使用等于208+3448(字节)。
-
堆空间的使用情况查看
在RT-Thread 有相应内存管理算法,也提供了堆内存是查看方式,所以只需要在 FinSH 中使用 free 指令查看即可,如下图所示,对于 FinSH 不了解的小伙伴,可以查看STM32 移植 RT-Thread 标准版的 FinSH 组件
参考文献
STM32链接脚本详解:https://blog.csdn.net/qq_27575841/article/details/104373417
本文来自博客园,作者:浇筑菜鸟,转载请注明原文链接:https://www.cnblogs.com/jzcn/p/16427067.html
如本博客的内容侵犯了你的权益,请与以下地址联系,本人获知后,马上删除。同时本人深表歉意,并致以崇高的谢意! cn_jiaozhu@qq.com
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律