一、什么是BSS段(ZI段)
, o" s O" t. g$ V* _/ Abss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
3 W) {7 t6 Y. F0 Abss是英文Block Started by Symbol的简称。
~: L* }; p/ v& M W" N9 ibss段属于静态内存分配。- ~: W/ X+ C! N1 x1 D. x) t# n
- k) O5 Q0 _" V8 }二、为什么要复制data段& \& q+ [' q1 B
这是因为对于在STM32F103这类资源紧缺的单片机芯片中,数据段只是暂时先保存在Flash上,在使用前被复制到内存里,而复制到内存这个过程是需要我们自己实现的。
1 K3 q3 q: E; L; g% Z P
5 O1 p8 N9 c" Z2 p+ d三、未处理的结果5 Q6 N# k1 z9 Y
修改main函数如下所示:7 k* X! S# t- \7 B" A \
! g( V0 Z. T- B5 @" w5 H0 G0 [
- #include "uart.h"
9 i( R5 o: q: P6 n - #include "led.h"6 t, A' D, M# ^
( K+ e W' d) {) m* X- int mydata = 0x32315;
& A. q" x5 P# }0 M - const int myconst = 0x22315;
' D4 w+ z: _) W2 E( r. R - int myzero = 0;) s2 Y+ X# }" b8 \4 Y
- int my;3 @- F. z; ?* A% M
- 7 W0 B. l3 ] k: e% e/ T9 v2 Q
- int main(void)6 {, i9 g" y2 v9 ^- h X
- {
5 b, L7 |& M9 f& U8 v3 V4 P6 N - uart_init();3 q4 J1 c8 }# g5 p
- led_init();
- J7 H( z, F% |4 A - putstring("stm32f103zet6\r\n");# X) b' {% o* S, ]6 @
- putstring("mydata\t:");
0 O: b) ^! H( ` | - puthex((unsigned int)&mydata);
0 P6 ~9 {* `" M5 b* ~ - puthex((unsigned int)mydata);
* v b6 a e( T! Q7 @2 Z - putstring("\r\nmyconst\t:");
8 Y) P8 i3 A! |& B7 t* h - puthex((unsigned int)&myconst);! U0 E2 `( o( _5 q( z
- puthex((unsigned int)myconst);1 ?; |6 [2 e5 M0 s* |
- putstring("\r\nmyzero\t:");3 C F2 @& a' t- @& F8 Z5 Q
- puthex((unsigned int)&myzero);
" P! R2 h6 R* a; E0 A( x - puthex((unsigned int)myzero);
9 `; t* N: x3 w. g1 l - putstring("\r\nmy\t:");$ R8 Q6 t. \1 o% i# k L
- puthex((unsigned int)&my);% x9 ?' b1 w4 }6 F& J$ g% s0 @
- puthex((unsigned int)my);, C* {4 B8 c0 `; D! x
- putstring("\r\n");
z8 V/ q- c3 } c. D# ~
7 o0 ?1 x. d* Z5 `' p' x! a: V- while(1)- X$ u7 C k+ x' L0 f
- {& _( y. C2 h+ V* ^2 Q
- putstring("led on\r\n");5 R: m! Q6 ]9 m; F6 n2 A3 s
- led_on();
8 ^6 d% i/ o- h, V! E9 O - delay(1000000);
" k0 Q- ~+ I2 j. B9 V
6 k. i$ }* x( I; g% ]+ K- putstring("led off\r\n");; z% o+ B9 \; k6 J" ?
- led_off(); Y% T; C, e3 h: n l& m# c
- delay(1000000);
' Z1 d5 P) X% k* _4 O3 e: u - }
& @" u- r R& X% E2 [) ~ - }
! H0 P% I* @* r% ~. n; {; j
复制代码 ! e/ h! a2 L3 q% u$ s: K4 [& X( V
编译烧录运行,可以看到,除了存在Flash的只读全局变量的值正确,其他几个变量的值都是错误的9 L' m! A2 x9 I! N
. Y2 c( y2 x p
! U7 r$ d2 r: {' E$ D' _) w3 t
& ? c- P' B3 V: b0 z% m四、修改链接文件
5 S4 @9 Q2 ~ Q4 S' H既然要复制data段和清除BSS段,那么就需要知道如下几个数据
9 B% C) u4 R; C: _ W, t& r7 k; T" K/ C! ^+ _" M7 m7 B
- 1、数据段的加载地址,即数据段存放在Flash的哪个位置
. P7 L* B; H4 D1 ? T2 i - 2、数据段的链接地址,即数据段应该复制到在RAM的哪个位置1 l T" S' m- p' d) c- C) p, Q
- 3、数据段的长度,应该复制多长的数据从Flash到RAM' b# P3 H- \, s. o# P2 q( M8 s
- 4、BSS段的链接地址,即BSS段应该从何处开始清除
8 M& f' J3 H7 C' D; k- D8 h% M - 5、ZI段的长度,应该清除多长的BSS段
复制代码 4 `7 S3 x: s$ }% n
我们将链接文件修改为如下所示,得到data段的起始加载地址、起始链接地址和结束链接地址,以及bss段的起始链接地址和结束链接地址。其中>RAM AT > FLASH 表示存放时使用RAM地址,存放时使用FLASH地址。% {2 e) W, E! Z
" x p* M) P- M! o# \3 f- /* 指定内存区域 */, h2 u6 k- D; ?; b
- MEMORY l, O( R+ j8 d+ M$ @. l
- {& D8 ^) J) a& }, P
- FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
8 b, d+ ]4 I. Y- H. \; P! O - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K8 T/ {; B% @. l8 T9 W
- }+ c7 Q7 W4 z8 j: L7 V7 Q
- # R# N2 u( V( p. e6 C7 J
- SECTIONS {
T0 j) l5 d5 N' R - % b. A+ |4 o- I; w' O
- .text :
2 o! U4 C) ]/ X% d# V- X% s* B# \ - {
& M+ K8 j7 a6 m7 U3 [ - . = ALIGN(4);
5 w; P1 A7 a' H% s+ o$ v* k, Q% \ - *start.o (.text)
4 A$ w9 N4 M8 [/ \& t) m3 t1 o - *main.o (.text)
2 q! O) j' f2 _6 U7 F1 s* s! m) U - *(.text)
" X) G" T7 \7 o# U9 I5 e) f! ]% r# \ - } > FLASH
+ {. I& `$ A n. e, [, y - 1 R H$ F/ U& m) F% U
- .rodata :
0 Y- Y) p7 a1 h* E6 {/ P - {
3 r6 T# A. s- l* Y Q/ b* x/ M$ ] - . = ALIGN(4);# k% g) R, t1 f
- *(.rodata) /* .rodata sections (constants, strings, etc.) */
) x* }( g: h" z - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */
# T" e2 I1 t" L5 I - . = ALIGN(4);; K, [- {' v( w
- } >FLASH# e5 [4 e# k9 F# ~8 l- j
3 s8 S+ j- k L9 P# d9 U' F3 Q4 Q& H- _sidata = LOADADDR(.data); /* 获取data段的起始加载地址 */
6 G: T) Q( a, I% r& w/ v - .data :
7 Q: `* e/ u7 o - {- j' |, `9 C6 e' ^' Z4 R! L2 o
- . = ALIGN(4);
, k! L$ v3 P) T& x! i - _sdata = .; /* 获取data段的起始链接地址 */
( ?3 C6 I- w7 W - *(.data) /* .data sections */& R! ?$ r( q- i( K+ q! W9 {9 p; a/ f
- *(.data*) /* .data* sections */( r7 H$ U) d( d1 l, Q! i( f- H. i
- . = ALIGN(4);' h" {6 [- Q4 i2 V& w0 V+ S
- _edata = .; /* 获取data段的结束链接地址 */
% S2 {, c. d: x8 g Q2 v - } >RAM AT > FLASH- W$ p9 N7 @" U3 m' c' }2 d. r- t
6 Z& G% W7 t3 |; E- . = ALIGN(4);
) b' @+ d: |5 L# b& n - .bss :% D' X) O: L# ~+ m$ N; [% M6 f
- {
" o, y8 o. p' a# G$ C; H( ? - _sbss = .; /* 获取bss段的起始链接地址 */
5 i& r, u2 {1 y O - *(.bss)
' b; C: w5 _. i J: x - *(.bss*)
" L) `- E$ l0 F% b7 ^! n - *(COMMON)' [" u8 L0 D# |! n! N( ]
- 4 W2 P/ S2 w% E# h- z! ^
- . = ALIGN(4);
( V3 q6 X; ~- o* w7 H - _ebss = .; /* 获取bss段的结束链接地址 */
- f4 F3 u1 X- a7 d, K) w X1 Z - } >RAM+ r9 X: B3 Y& ^8 H! k- d% B7 C% }
- }
复制代码
0 V: h) [' i6 T7 E6 R/ }) p, V五、修改汇编文件. F5 Z! G6 ~+ |) D+ y
在start.s中加入如下代码复制data段和清除bss段的代码,其中data段的起始加载地址_sidata、起始链接地址_sdata和结束链接地址_edata,以及bss段的起始链接地址_sbss和结束链接地址_ebss,都是从链接脚本中获取的。* Z$ |* k2 O7 w0 A, X
4 y- F' ]) K! ^% {- .syntax unified /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */
, {+ g5 R) [' ^+ e5 G4 q/ d - .cpu cortex-m3 /* 指明cpu核为cortex-m3 */5 l7 U5 q+ d" y6 u# P
- .fpu softvfp /* 软浮点 */
5 q6 l! L- r( S- }) U7 k; u( ? - .thumb /* thumb指令 */
: M" m: O; e c8 d5 i. ]9 f
3 \% I, N7 {3 U1 C- .global _start /* .global表示Reset_Handler是一个全局符号 */* L G) E9 l6 D2 U* m) }- }' g
% b" A+ x: L) v2 i- .word 0x00000000 /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
3 D9 L, q- c6 h! g& C3 W9 D9 U - .word _start+1 /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/ g/ N8 @) \2 g8 d2 z- g4 t+ t
- * z' v7 X3 d3 _5 A8 K l/ g* o8 E
- _start: /* 标签_start,汇编程序的默认入口是_start */
/ K5 Y$ E: ^+ }2 l - /* 1、设置栈 */
& D: p0 t, e! |0 j1 k0 D - LDR SP, =(0x20000000+0x400)) u! S5 _) q7 | U
- . u2 Z r7 }3 Y7 ?9 b& ?- G( V( U% z% r
- /* 2、复制data段 */; v x' H+ B6 a M/ }8 e
- movs r1, #0 /* r1寄存器用来计数 */
, ?, a+ y1 T& y) K) T8 w2 i: U! ] - b LoopCopyDataInit
& D" R8 e8 x C4 j* P! _! o
7 h" j' g( A% x: `1 c9 n% {" }8 r1 A- CopyDataInit:) s6 ~. n6 m% ~1 T
- ldr r3, =_sidata /* 将data段的起始加载地址存入r3寄存器 */2 k( N6 A( L+ d o
- ldr r3, [r3, r1] /* 从Flash取出data段的值存入r3寄存器 */. [) R4 y" P! {
- str r3, [r0, r1] /* 将r3寄存器的值存入RAM中 */
1 m a0 H1 T4 V. b7 Z - adds r1, r1, #4 /* 每次复制4个字节的data段数据 */
. k- P H% ]6 ~+ C0 s5 s - ; o; @4 C% A# M" }* ^
- LoopCopyDataInit:
9 d# i% N. a" g) E f5 T3 i - ldr r0, =_sdata /* 将data段的起始链接地址存入r0寄存器 */1 ^3 ?" x# j4 H4 `+ E
- ldr r3, =_edata /* 将data段的结束链接地址存入r0寄存器 */
1 k7 F7 I' T% U# {( D+ e | - adds r2, r0, r1 /* 起始链接地址加上计数,即当前复制地址存入r2寄存器 */
! W. B, K% A/ O( l - cmp r2, r3 /* 当前复制地址和结束链接地址相比较 */
5 ~* H) Y1 \* j4 I# F - bcc CopyDataInit /* 如果r2小于等于r3跳转到CopyDataInit标签处,如果大于则往下执行 */
# q# n A, E; |$ {2 A. U. }, n - 9 y4 }" m. K! h4 o! S3 [# ]
- /* 3、清除bss段 */$ V( @# [3 X+ H/ C: k; ?3 h/ u
- ldr r2, =_sbss /* 将bss段的起始链接地址存入r2寄存器 */
) { U! I1 t P9 F - b LoopFillZerobss, z3 u; ^" W& K, h
- 2 L' b. |' U& g
- FillZerobss:! y$ f% B; N9 B9 @5 ^8 j1 u
- movs r3, #0 /* 将0存入r3寄存器 */1 G9 I; b' V/ N
- str r3, [r2], #4 /* 将r3中的值存到r2中的值所指向的地址中, 同时r2中的值加4 */
" f* o) ]8 E- W4 `) ^5 N - % k2 f* A* ^; u2 a& F1 K) R6 F
- LoopFillZerobss:
* T* @8 H/ @3 D6 [# d - ldr r3, = _ebss /* 将bss段的结束链接地址存入r3寄存器 */
* A( X% M* ?8 ? - cmp r2, r3 /* 比较r2和r3内的值,即当前清除地址和结束链接地址相比较 */8 Z; h- r- ?4 K8 ~
- bcc FillZerobss /* 如果r2小于等于r3跳转到FillZerobss标签处,如果大于则往下执行 */( j7 a, `1 m7 u, ]) l
- - s, C( K) v& m5 o+ i( D$ K
- /* 4、跳转到led函数 */
; p2 f9 J% P- q1 C - BL main) I) a3 S, J, I+ B6 J
- /* 5、原地循环 */
0 s6 C: S& k( n) T/ b - B .# t4 a% }2 ]. X( b- U! L
$ J! U" P4 \+ r1 J( O7 y% c
复制代码
$ j4 D5 Q6 @9 C3 O4 j' ?9 i1 _然后编译运行烧录,这样我们就可以看到变量的值正常了
& }8 `- |- ^6 i( s
- Q: N s* f+ t" t \9 o o/ V
( d; y' f8 C2 K% v* E% N6 w5 ^
" v) Z, \4 ~, c% s6 \, C, _ b3 q————————————————
# V1 U0 Q, {: X `6 q/ @转载:Willliam_william
$ w3 Q2 K8 o! d) B( A+ h0 b" q0 }' i0 x {3 t
|