世界杯预选赛亚洲区_高达世界杯 - fzxzyy.com

STM32F1系列之复制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来说,即初始化为0,8字节对齐

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 ;整个汇编文件结束

复制代码

四、验证

其他文件不做修改,编译烧录运行,可以看到所有变量的值都正确了

20210124143838385.png (19.29 KB, 下载次数: 6)

下载附件

保存到相册

2022-8-27 13:30 上传

五、注意

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

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

那么我们将myzero修改为一个数组

20210124144421174.png (29.95 KB, 下载次数: 6)

下载附件

保存到相册

2022-8-27 13:30 上传

这样查看hex文件,可以看到,只有my变量存在了

20210124144510149.png (158.91 KB, 下载次数: 6)

下载附件

保存到相册

2022-8-27 13:30 上传

口袋新世代如何快速获得体力值 口袋新世代体力获取攻略
这些常见标点符号用英语怎么说?