STM32裸机开发(7) — 复制data段和清除BSS段(ZI段)

本文介绍了STM32裸机开发中数据段(data段)和BSS段(ZI段)的概念,解释了为何需要复制data段以及清除BSS段的原因。在Keil环境下,通过修改start.s文件,实现了数据段的复制及BSS段的清零操作。通过这种方法,确保了全局变量的正确初始化。文章还提到,小于8字节的变量会被放在data段,而大于8字节的变量则会放到BSS段,并提供了验证方法。
摘要由CSDN通过智能技术生成

STM32裸机开发(7) — 复制data段和清除BSS段(ZI段)

一、什么是BSS段(ZI段)

bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
bss是英文Block Started by Symbol的简称。
bss段属于静态内存分配。

而在Keil里,BSS段被称为ZI段

二、为什么要复制data段

这是因为对于在STM32F103这类资源紧缺的单片机芯片中,数据段只是暂时先保存在Flash上,在使用前被复制到内存里,而复制到内存这个过程是需要我们自己实现的。实际上在keil中,*(InRoot$$Sections)这一段就是keil帮我们做好的一段代码,当我们调用main函数时,keil会帮我们自动添加。但是为了学习,我们来自己实现一下,那么就需要知道数据段的加载地址(在Flash上的位置)和链接地址(需要放在内存中的位置),在keil中获取这些地址的方法如下:

数据段的加载地址:	Load$$RW_IRAM1$$Base
数据段的链接地址:	Image$$RW_IRAM1$$Base
数据段的长度: 		Image$$RW_IRAM1$$Length
ZI段的链接地址:		Image$$RW_IRAM1$$ZI$$Base
ZI段的长度: 		Image$$RW_IRAM1$$ZI$$Length

三、编写代码实现

start.s修改为如下所示

Stack_Size      EQU     0x00000500  				;定义堆栈大小为1024byte
				AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为08字节对齐
Stack_Mem		SPACE	Stack_Size    				;保留Stack_Size大小的栈空间
__initial_sp  										;标号,代表堆栈顶部地址,后面有用

                PRESERVE8							;指示编译器8字节对齐
                THUMB								;指示编译器以后的指令为THUMB指令								


; Vector Table Mapped to Address 0 at Reset
				AREA    RESET, CODE, READONLY		;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
				EXPORT  __Vectors					;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
					
__Vectors       DCD     __initial_sp				;当前地址写入一个字(32bit)数据,值应该为栈顶地址
                DCD     Reset_Handler              	;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址

				AREA    |.text|, CODE, READONLY		;定义代码段,标记为.text

; Reset handler	;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler   PROC								;过程的开始 
				EXPORT  Reset_Handler	[WEAK]		;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
					
				IMPORT |Image$$RW_IRAM1$$Base|		;从别处导入data段的链接地址
				IMPORT |Image$$RW_IRAM1$$Length|	;从别处导入data段的长度
				IMPORT |Load$$RW_IRAM1$$Base|		;从别处导入data段的加载地址
				IMPORT |Image$$RW_IRAM1$$ZI$$Base|	;从别处导入ZI段的链接地址
				IMPORT |Image$$RW_IRAM1$$ZI$$Length|;从别处导入ZI段的长度

; 复制数据段
				LDR R0, = |Load$$RW_IRAM1$$Base|   	;将data段的加载地址存入R0寄存器
				LDR R1, = |Image$$RW_IRAM1$$Base|   ;将data段的链接地址存入R1寄存器
				LDR R2, = |Image$$RW_IRAM1$$Length| ;将data段的长度存入R2寄存器
CopyData		
				SUB R2, R2, #4						;每次复制4个字节的data段数据
				LDR R3, [R0, R2]					;把加载地址处的值取出到R3寄存器
				STR R3, [R1, R2]					;把取出的值从R3寄存器存入到链接地址					
				CMP R2, #0							;将计数和0相比较
				BNE CopyData						;如果不相等,跳转到CopyData标签处,相等则往下执行

; 清除BSS段
				LDR R0, = |Image$$RW_IRAM1$$ZI$$Base|   ;将bss段的链接地址存入R1寄存器
				LDR R1, = |Image$$RW_IRAM1$$ZI$$Length| ;将bss段的长度存入R2寄存器
CleanBss	
				SUB R1, R1, #4						;每次清除4个字节的bss段数据
				MOV R3, #0							;0存入r3寄存器
				STR R3, [R0, R1]					;把R3寄存器存入到链接地址					
				CMP R1, #0							;将计数和0相比较
				BNE CleanBss						;如果不相等,跳转到CleanBss标签处,相等则往下执行
				
				
				IMPORT  mymain						;通知编译器要使用的标号在其他文件
				BL		mymain 						;跳转去执行main函数
				B		.							;原地跳转,即处于循环状态
				ENDP

                ALIGN 								;填充字节使地址对齐
                END									;整个汇编文件结束

四、验证

其他文件不做修改,编译烧录运行,可以看到所有变量的值都正确了
在这里插入图片描述

五、注意

我们之前看过编译出来的hex文件,发现myzeromy变量也存在,这就意味着这两个本该在bss段的变量也会在Flash中,这就很奇怪了,而事实是:

对于keil来说,一个本该放到BSS段的变量,如果它所占据的空间小于等于8字节自己,keil仍然会把它放在data段里。只有当它所占据的空间大于8字节时,才会放到BSS段。

那么我们将myzero修改为一个数组
在这里插入图片描述
这样查看hex文件,可以看到,只有my变量存在了
在这里插入图片描述

六、附录

上一篇:STM32裸机开发(6) — Keil-MDK下散列文件的分析
下一篇:STM32裸机开发(8) — 在gcc环境下编写uart串口打印程序
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/keil-mdk/03_links

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值