一、什么是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 .
复制代码
然后编译运行烧录,这样我们就可以看到变量的值正常了
————————————————
转载:Willliam_william
|