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

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

一、什么是BSS段(ZI段)

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

二、为什么要复制data段

这是因为对于在STM32F103这类资源紧缺的单片机芯片中,数据段只是暂时先保存在Flash上,在使用前被复制到内存里,而复制到内存这个过程是需要我们自己实现的。

三、未处理的结果

修改main函数如下所示:

#include "uart.h"
#include "led.h"

int mydata = 0x32315;
const int myconst = 0x22315;
int myzero = 0;
int my;

int main(void)
{
    uart_init();
    led_init();
    putstring("stm32f103zet6\r\n");
	putstring("mydata\t:");
    puthex((unsigned int)&mydata);
    puthex((unsigned int)mydata);
    putstring("\r\nmyconst\t:");
    puthex((unsigned int)&myconst);
    puthex((unsigned int)myconst);
	putstring("\r\nmyzero\t:");
    puthex((unsigned int)&myzero);
    puthex((unsigned int)myzero);
    putstring("\r\nmy\t:");
    puthex((unsigned int)&my);
    puthex((unsigned int)my);
    putstring("\r\n");

    while(1)
    {
		putstring("led on\r\n");
        led_on();
        delay(1000000);

		putstring("led off\r\n");
        led_off();
        delay(1000000);
    }
}

编译烧录运行,可以看到,除了存在Flash的只读全局变量的值正确,其他几个变量的值都是错误的
在这里插入图片描述

四、修改链接文件

既然要复制data段和清除BSS段,那么就需要知道如下几个数据

1、数据段的加载地址,即数据段存放在Flash的哪个位置
2、数据段的链接地址,即数据段应该复制到在RAM的哪个位置
3、数据段的长度,应该复制多长的数据从Flash到RAM
4、BSS段的链接地址,即BSS段应该从何处开始清除
5、ZI段的长度,应该清除多长的BSS段

我们将链接文件修改为如下所示,得到data段的起始加载地址、起始链接地址和结束链接地址,以及bss段的起始链接地址和结束链接地址。其中>RAM AT > FLASH 表示存放时使用RAM地址,存放时使用FLASH地址。

/* 指定内存区域 */
MEMORY
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
    RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 64K
}

SECTIONS {

    .text      :
    {
        . = ALIGN(4);
        *start.o (.text)
        *main.o (.text)
        *(.text)
    } > FLASH

    .rodata :
    {
        . = ALIGN(4);
        *(.rodata)         /* .rodata sections (constants, strings, etc.) */
        *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
        . = ALIGN(4);
    } >FLASH

    _sidata = LOADADDR(.data);  /* 获取data段的起始加载地址 */
    .data : 
    {
        . = ALIGN(4);
        _sdata = .;        /* 获取data段的起始链接地址 */
        *(.data)           /* .data sections */
        *(.data*)          /* .data* sections */
        . = ALIGN(4);
        _edata = .;        /* 获取data段的结束链接地址 */
    } >RAM AT > FLASH

    . = ALIGN(4);
    .bss :
    {
        _sbss = .;         /* 获取bss段的起始链接地址 */ 
        *(.bss)
        *(.bss*)
        *(COMMON)

        . = ALIGN(4);
        _ebss = .;         /* 获取bss段的结束链接地址 */
    } >RAM
}

五、修改汇编文件

在start.s中加入如下代码复制data段和清除bss段的代码,其中data段的起始加载地址_sidata、起始链接地址_sdata和结束链接地址_edata,以及bss段的起始链接地址_sbss和结束链接地址_ebss,都是从链接脚本中获取的。


  .syntax unified                   /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */
  .cpu cortex-m3                    /* 指明cpu核为cortex-m3 */
  .fpu softvfp                      /* 软浮点 */
  .thumb                            /* thumb指令 */

.global  _start                     /* .global表示Reset_Handler是一个全局符号 */

.word 0x00000000                    /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
.word _start+1                      /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/

_start:                             /* 标签_start,汇编程序的默认入口是_start */
    /* 1、设置栈 */
    LDR SP, =(0x20000000+0x400)

    /* 2、复制data段 */
	movs r1, #0						/* r1寄存器用来计数 */
    b LoopCopyDataInit

CopyDataInit:
    ldr r3, =_sidata				/* 将data段的起始加载地址存入r3寄存器 */
    ldr r3, [r3, r1]				/* 从Flash取出data段的值存入r3寄存器 */
    str r3, [r0, r1]				/* 将r3寄存器的值存入RAM中 */
    adds r1, r1, #4					/* 每次复制4个字节的data段数据 */

LoopCopyDataInit:
    ldr r0, =_sdata					/* 将data段的起始链接地址存入r0寄存器 */
    ldr r3, =_edata					/* 将data段的结束链接地址存入r0寄存器 */
    adds r2, r0, r1					/* 起始链接地址加上计数,即当前复制地址存入r2寄存器 */
    cmp r2, r3						/* 当前复制地址和结束链接地址相比较 */
    bcc CopyDataInit				/* 如果r2小于等于r3跳转到CopyDataInit标签处,如果大于则往下执行 */

    /* 3、清除bss段 */
	ldr r2, =_sbss					/* 将bss段的起始链接地址存入r2寄存器 */
	b LoopFillZerobss

FillZerobss:
	movs r3, #0						/* 将0存入r3寄存器 */
	str r3, [r2], #4				/* 将r3中的值存到r2中的值所指向的地址中, 同时r2中的值加4 */

LoopFillZerobss:
	ldr r3, = _ebss					/* 将bss段的结束链接地址存入r3寄存器 */
	cmp r2, r3						/* 比较r2和r3内的值,即当前清除地址和结束链接地址相比较 */
  	bcc FillZerobss					/* 如果r2小于等于r3跳转到FillZerobss标签处,如果大于则往下执行 */

    /* 4、跳转到led函数 */
    BL main
    /* 5、原地循环 */
    B .

然后编译运行烧录,这样我们就可以看到变量的值正常了
在这里插入图片描述

六、附录

上一篇:STM32裸机开发(9) — 使用链接脚本链接代码
下一篇:
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/make-gcc/03_links

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值