STM32 堆栈空间分布

参考

  1. 运行时访问__initial_sp__heap_base

无RTOS时的情况

在这里插入图片描述
在以上配置的情况下,生成工程。在工程的startup.s文件中,由如下代码:

Stack_Size		EQU     0x400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
__Stack_top		; 自己添加
Stack_Mem       SPACE   Stack_Size
__initial_sp

IF      :DEF:__MICROLIB           
	EXPORT  __initial_sp
	EXPORT  __heap_base
	EXPORT  __heap_limit
	EXPORT  __Stack_top	; 自己添加
ELSE
	IMPORT  __use_two_region_memory
	EXPORT  __user_initial_stackheap
__user_initial_stackheap
	LDR     R0, =  Heap_Mem
	LDR     R1, =(Stack_Mem + Stack_Size)
	LDR     R2, = (Heap_Mem +  Heap_Size)
	LDR     R3, = Stack_Mem
	BX      LR
	ALIGN
ENDIF

通过以上代码可以看出,需要使能MicroLib才会默认导出__initial_sp,__Stack_top,__heap_base,__heap_limit这几个变量。(如果不使能MicroLib,则需要在上面代码的ELSE下面也添加EXPORT语句将这几个变量导出)。
然后在main.c中添加如下代码,查看以上这些变量的值:

  extern uint32_t __heap_base, __heap_limit;
  extern uint32_t __Stack_top, __initial_sp;
  extern uint32_t __Vectors, __Vectors_End, __Vectors_Size;
  while (1)
  {
  	// 0x200013A8 ~ 0x200015A8  size:0x200  RAM
    printf("heapS[0x%08x], heapE[0x%08x]\r\n", (uint32_t)&__heap_base, (uint32_t)&__heap_limit);
      
    // 0x200019A8 ~ 0x200015A8  size:0x400  RAM
    printf("stackS[0x%08x], stackE[0x%08x]\r\n", (uint32_t)&__initial_sp, (uint32_t)&__Stack_top); 

	// 0x08000000 ~ 0x080000EC  ROM
	printf("VectS[0x%08x], VectE[0x%08x], VectSize[0x%x]\r\n\r\n", (uint32_t)&__Vectors, (uint32_t)&__Vectors_End, (uint32_t)&__Vectors_Size);
  }

从上可以看到,__Stack_top__heap_limit是一样的,说明这里分配的堆和栈是紧邻的。而且地址刚好也和我们在cubeMx中定义的一致。堆和栈是在RAM中,而中断向量表是在Flash中。

添加FreeRTOS

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从以上三张图中,可以发现,我们给FreeRTOS总共分配了3072(0xC00) Bytes HEAP空间。而我们定义了一个defaultTask并分配了256 * 4 = 1024 Bytes,但是在最后的FreeRTOS Heap Usage页面看到,defaultTask实际使用了1144 Bytes,剩余3072 - 1144 = 1928 Bytes。(除了defaultTask多使用的1144 - 1024 = 120 Bytes(用于任务控制块TCB)外,其余都是可以对上的。)
生成工程后,我们还是将之前的那些堆栈指针地址打印出来,看一下MCU是如何进行地址分配的。只是这里还需要添加FreeRTOS中的堆栈信息了。首先通过追踪configTOTAL_HEAP_SIZE可以发现,堆空间定义为了一个数组:

	static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
    uint8_t* osHeapPoint = ucHeap;		// 由于以上变量是 static 类型,所有这里再定义一个指针指向这个 Heap 地址,以便在外部访问它;

然后我们就可以在defaultTask任务里面添加输出以上变量地址的代码了:

  extern uint32_t __heap_base, __heap_limit;
  extern uint32_t __Stack_top, __initial_sp;
  extern uint32_t __Vectors, __Vectors_End, __Vectors_Size;
  extern uint8_t* osHeapPoint;
  extern uint32_t local_addr1, local_addr2;
  void* os_mal_buf = malloc(50);
  uint32_t tast_local_val = 1;
  
  for(;;) {
  	// 0x20002D10 ~ 0x20002F10  size:0x200  RAM
  	printf("HeapS[0x%08x], HeapE[0x%08x]\r\n", (uint32_t)&__heap_base, (uint32_t)&__heap_limit);
	
	// 0x20003310 ~ 0x20002F10  size:0x400  RAM
	printf("StackS[0x%08x], StackE[0x%08x]\r\n", (uint32_t)&__initial_sp, (uint32_t)&__Stack_top);	// 
	
	// 0x08000000 ~ 0x080000EC  ROM
	printf("VectS[0x%08x], VectE[0x%08x], VectSize[0x%x]\r\n", (uint32_t)&__Vectors, (uint32_t)&__Vectors_End, (uint32_t)&__Vectors_Size);		// 中断向量表

	// 0x20002110   size: 3072 Bytes(0xC00)
	printf("OsHeapPoint[0x%08x]\r\n", (uint32_t)osHeapPoint);	// freeRTOS中定义的HEAP数组
	
	// 0x20002D18
	printf("Os_mal_buf[0x%08x]\r\n", (uint32_t)os_mal_buf);

	// 0x20002504
	printf("&Os_mal_buf[0x%08x]\r\n", (uint32_t)&os_mal_buf);

	// 0x20002508
	printf((char*)buf, "&tast_local_val[0x%08x]\r\n\r\n", (uint32_t)&tast_local_val);	// 在freeRTOS任务中定义的局部变量
	
	// 0x20000008, 0x2000000C
	printf("&global_var1[0x%08x], &global_var2[0x%08x]\r\n", (uint32_t)&global_var1, (uint32_t)&global_var2);	// 函数外部定义的全局变量

	// 0x20003304, 0x20003308
	printf("local_addr1[0x%08x], local_addr2[0x%08x]\r\n\r\n", local_addr1, local_addr2);	// 这两个全局变量保存了在freeRTOS初始化前,在main()函数中定义的两个局部变量的地址
  }

根据以上输出总结如下:
在这里插入图片描述

  1. local_addr的地址(靠近__initial_sp)可以看出,栈空间的地址值是向下增长的,即栈顶(__initial_sp = 0x20003310)在高地址;
  2. 从全局变量的地址可以看出,全局变量是默认分配在RAM的起始地址;
  3. freeRTOS任务中定义的局部变量(&tast_local_val = 0x20002508)存储在定义freeRTOS时定义的HEAP里(0x20002110~0x20002D10);
  4. 再看一下&tast_local_val = 0x20002508这个数据,发现0x20002508大概等于0x20002110 + 256*4 = 0x20002510。这就再次说明,freeRTOS定义任务时,是从配置好的HEAP地址初始空间的开始部分给新任务分配Task stack的,而在任务中使用这个task stack时,还是符合栈的使用规范(即从高地址开始分配);
  5. s_mal_buf = 0x20002D18可以知道,即使在freeRTOS任务中动态分配,也是在任务之外的堆中分配空间。

疑问

  1. 通过以上总结,发现在freeRTOS任务中定义局部变量和在freeRTOS任务之外定义局部变量,他们分配的地址空间是不一样的。那么,如果有以下函数:
void addr_test(void)
{
	uint32_t loc_var = 1;
	printf("&loc_var = 0x%08x\r\n", (uint32_t)&loc_var);
}

我分别在freeRTOS初始化之前调用,以及在freeRTOS的任务中调用,其输出会是什么呢?
经过验证,在freeRTOS初始化之前调用,输出的地址范围在0x20002F10 - 0x20003310之间;而在freeRTOS的任务中调用,输出的地址范围在0x20002110 - 0x20002D10之间。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]中的信息,对于FreeRTOS中的任务,静态内存是全局变量,不需要对keil编译器中的堆栈大小进行修改。而动态内存的管理方式有多种,其中除了heap_3.c使用了标准的malloc()和free()函数外,其他方案都是全局静态变量,也不需要对堆栈大小进行修改。因此,如果需要扩大堆栈,可以考虑以下几种情况: 1. 当局部变量较多时,可以适当调整栈的空间。 2. 当使用malloc()和free()函数时,需要根据实际需求调整堆的空间。 3. 如果调整堆栈空间过大,可能会导致编译后占用的SRAM超过单片机的内存大小,虽然可以正常编译,但是程序无法运行,可能会进入硬件异常中断。 因此,在扩大堆栈时需要根据具体情况进行调整,确保不会超出单片机的内存限制。 #### 引用[.reference_title] - *1* *3* [stm32 堆栈的作用及设置 FreeRTOS内存策略 如何修改stm32堆栈](https://blog.csdn.net/QAQ18866/article/details/123194887)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [stm32中断堆栈_STM32启动文件](https://blog.csdn.net/weixin_39627408/article/details/110303953)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值