Air32F103学习笔记-4.看看SDK中startup_air32f10x.s, .sct, .map

SDK中有很多内容,前面没有讲,一笔带过了,我们来逐个看看它们的作用。

以下部分内容整理自《野火STM32库开发指南》。

程序的启动,加载,存储像一团迷雾,挡在了我们和底层配置之间。不了解起始也不妨碍我们使用固件库编程,但是这部分内容太精彩了,越往下看,坑越多,这里推荐一本好书俞甲子老师的《程序员的自我修养--链接、装载与库》,揭开了C程序底层的奥秘,尤其是关于链接的部分,会带到一个新的高度。

同时王利涛老师的《嵌入式C语言自我修养》也是按照这部分讲的。还是建议俞甲子的那本。

先来启动文件startup_air32f10x.s

启动文件是必不可少的,这个文件用汇编编写,规定了程序上电后,MCU执行的第一个操作。

第一个操作是啥呢?

OK,我们需要看下《CM3权威指南》:

3.8 复位序列

在离开复位状态后,CM3做的第一件就是读取下列32个整数的值:

  • 从地址 0x0000 0000处取出MSP的初始值 - 即这个地址上放的数据是多少,栈指针指向的地址;我们这里放的0x2000 0400。因为栈在SRAM里,SRAM的基地址是0x2000 0000, 栈大小是0x0400,启动文件里设置好的。栈又是满减栈,所以初始地址就是0x2000 0400。data段和bss段大小为0。
  • 从地址 0x0000 0004处取出PC 的初始值(Program Counter程序计数器,其实就是当前程序的位置 )。这里很有讲究,PC的初始值一定是指向复位向量的(因为复位向量指向了1个子程序,帮助系统初始化时钟,堆栈,搬运代码等基本操作,后续我们才能使用我们自己写的main函数)

当我们将Air32F103设置为从内部FLASH启动后,FLASH存储器将被映射到启动空间(0x0000 0000)

0x0800_0000处取出栈顶地址存放于MSP寄存器,   栈顶地址为0x0000400

0x0800_0004处取出复位中断服务入口地址放入PC寄存器,复位中断函数Reset_Handler地址为0x800023D

在这之前先看看用到的汇编指令的含义:

指令                作用
EQU给数字常量起个符号名字,相当于define
AREA汇编1个新的代码段或者数据段
SPACE           分配内存空间
EXPORT        全局声明,可被外部文件调用,类似extern
DCD以字节为单位分配内存,要求4字节对齐,并初始化这些内存
PROC定义子程序,与ENDP成对使用
ENDP子程序结束
IMPORT        声明标号来自外部
B跳转到标号
BL跳转到寄存器/标号给出的地址,并把跳转前下一条指令的地址存入LR(链接寄存器)中。
BLX跳转到寄存器给出的地址,并把跳转前下一条指令的地址存入LR(链接寄存器)中。
BX跳转到寄存器/标号给出的地址,不返回
END到达文件结尾,文件结束
WEAK编译器指令,弱定义

 Cortex M3存储映射表

这个表比较泛泛,具体怎么存储的,还得看map文件分析

1.初始化栈,栈的作用是用于局部变量函数调用函数形参

一般CM3内核优先使用R0,R1,R2,R3这4个内核通用寄存器,当参数超过4时才会使用栈。- 《CM3权威指南CnR2 - 宋岩》

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

这段代码定义了一个段,名字叫STACK,大小是0x0000 0400,1024 = 1KByte。注意:此时也没有给其划分地址,只是给这个段起了个名字,他的地址要在分散加载时才知道。

(后续我们看分散加载时就明白了,因为栈放在SRAM中,并且没有全局变量和局部变量,所以栈(内部SRAM)的起始地址是0x2000 0000,那么结束地址就是0x2000 0400)

正常来说     栈顶地址=SRAM起始地址+data段大小+bss段大小+栈大小。因为我们的例程中没有全局变量,所以data段和和bss段大小为0,栈顶地址就等于SRAM起始地址+栈大小

EQU:宏定义的伪指令,相当于等于,类似与C中的define。Stack_Siz=0x00000400

AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK表示段名,这个可以任意命名;NOINIT表示不初始化; READWRITE表示可读可写,ALIGN=3,表示按照2^3对齐,即8字节对齐。

SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于Stack_Size,即分配1个大小为0x00000400的空间。

标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。地址是多少要看栈放在哪里,放在第几位。

__initial_sp 的地址是0x2000 0400,栈的空间是0x2000 0000 ~ 0x2000 03FF。

因为MSP主堆栈指针的地址=堆栈内存末地址+1

方法1:进入Keil 调试模式,复位后,查看SP和PC寄存器的值

方法2:把Listings文件夹下的1.map文件用keil打开(直接拖进去),就能看到了

2. 初始化堆

堆主要用来动态内存的分配,像malloc()函数申请的内存就在堆上面。这个在STM32里面用的比较少。

Heap_Size       EQU     0x00001000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

EQU:宏定义的伪指令,相当于等于,类似与C中的define。Heap_Size =0x00001000

堆的使用要结合malloc函数,是动态分配的,所以这里并没有划分出对应的空间。

3.向量表

AREA    RESET, DATA, READONLY
EXPORT  __Vectors
EXPORT  __Vectors_End
EXPORT  __Vectors_Size

定义一个段,名字叫RESET, 类型是DATA,只读。注意:此时也没有给其划分地址,只是给这个段起了个名字,他的地址要在分散加载时才知道。(只读数据即ROM,放在FLASH里,起始地址0x0800 0000)

AREA:告诉汇编器汇编一个新的数据段。RESET表示段名; 可读

EXPORT:声明3个可被外部使用的标号,使标号具有全局属性。如果是IAR编译器,则使用的是GLOBAL这个指令。

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
忽略以下代码。。。。。

__Vectors 从向量表起始开始

DCD 为 __initial_sp(即栈顶地址)以及后续的符号分配4字节大小的地址。这块区域有多大,要看实际使用了多少。

				DCD		0
				DCD		0
				;DCD		0X20005000
                ;DCD     BOOT_RAM
					
					
__Vectors_End

__Vectors_End  标号,向量表结束


__Vectors_Size  EQU  __Vectors_End - __Vectors

向量表大小用 结束地址-起始地址即可。


  AREA    |.text|, CODE, READONLY

定义代码段(.text),类型是CODE,只读 


BOOT_RAM  		PROC
                EXPORT  BOOT_RAM             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP	

子程序 BOOT RAM,


复位中断子程序

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
				;unlock
				LDR  R0,=0x400210F0
                MOV R1,#0x00000001
                STR R1,[R0]
                LDR R2,=0x40016C00
                LDR R3,=0xa7d93a86
                STR R3,[R2]
                LDR R3,=0xab12dfcd
                STR R3,[R2]
                LDR R3,=0xcded3526
                STR R3,[R2]
                LDR R3,=0x200183FF
                STR R3,[R2,#0x18]
                LDR R4,=0x4002228c
                LDR R5,=0xa5a5a5a5
                STR R5,[R4]
				;lock
				LDR R2,=0x400210F0
                LDR R3,=0x00000000
                STR R3,[R2]
                LDR R2,=0x40016C00
                LDR R3,=0x5826c579
                STR R3,[R2]
                LDR R3,=0x54ed2032
                STR R3,[R2]
                LDR R3,=0x3212cad9
                STR R3,[R2]
                LDR R2,=0x4002228c
                LDR R3,=0x5a5a5a5a
                STR R3,[R2]
                MOV R1,#0x00000000	
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

EXPORT,定义一个全局标号 Reset_Handler , 弱定义

LDR  R0,=0x400210F0
MOV R1,#0x00000001
STR R1,[R0]

4.分散加载

分散加载文件位于Objects文件夹下,文件以sct结尾,例如1.sct

分散加载的作用:将Code与Data放在指定的区域。


LR_IROM1 0x08000000 0x00040000  {    ; Code链接区域 Code链接起始地址 Code链接区域大小
  ER_IROM1 0x08000000 0x00040000  {  ; Code = execution address
   *.o (RESET, +First)                    //放置所有.o文件,但是RESET放在最前面
   *(InRoot$$Sections)
   .ANY (+RO)                         //最后放置只读数据,例如 const修饰的变量
  }
  RW_IRAM1 0x20000000 0x00018000  {  ; RW data     //可读写的Data段起始地址,以及大小
   .ANY (+RW +ZI)                                  //先放RW段,再放ZI段
  }
}

其实在分散加载之前,所有的段都没有地址。分散加载给他们分配对应的地址。

这里有几个段名解释下:

RO段:只读数据段,一般放在ROM里,即FLASH里

RW段:可读可写的数据段,例如有初始值的全局变量

ZI段:可读可写的数据段,但没有初始化,或者初始化值为0。即BSS段,因为这部分变量没有初值或初值为0,所以编译后,不占用ImageSIze(bin文件大小)。可能是很久以前,资源宝贵吧,所以早期的程序员将变量分的很细,减少浪费

关于分散加载的详细内容请看:

ARM学习(4) 分散加载启动_arm分散加载-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值