前言
MDK并没有将所有的启动文件的所有配置开源,我们我们只能配置一部分启动文件的参数;本文我们了解一下这一部分可以配置的参数;
上电之后,CPU首先根据boot引脚选择存储器重映射区域,将该区域的地址重映射为地址偏移量为0;
CPU从地址偏移量为0的地址处开始执行;一开始执行的烧录代码是xx.s启动文件,使用汇编语言编写;
1 启动方式
1.1 对于STM32的F0和F4开发板而言,一共有3种启动方式;以下的存储空间大小和地址是以大容量F1芯片作为参考的;不同芯片有一些差别;
注意下面存储空间大小的单位都是byte,这是因为内存的数据线是8bit的;内存的数据线和单片机的数据线是不同的总线;
boot[0:1] | 启动地址 | 存储空间 | 启动方式 | 事项 | |
[0:x] |
0x0800 0000- 0x0807 FFFF |
512K bytes |
Flash启动 |
使用JTAG或SWD下载 较为常用; |
|
[1:0] |
0x1FFF F000- 0x1FFF F7FF |
2K bytes |
系统存储器启动 |
使用串口下载,代码是通过bootloader搬运到flash中; 该区域内存储了厂家烧录的bootloader程序(即ISP程序),需要配合st提供的下载软件; |
|
[1:1] |
0x2000 0000- 0x2001 0000 |
64K bytes |
内嵌SRAM启动 |
使用JTAG或SWD下载,仅支持调试模式;较为少用; 没有掉电存储程序的功能,需要添加宏定义VECT_TAB_SRAM,配合脚本文件使用; |
1.2 对于 STM32H7x3 而言,只有一个boot引脚,配合BOOT_ADD0/BOOT_ADD1 的组合配置,可以让系统从两个不同的区域启动;
boot引脚 | 启动地址 | 默认值 | 默认值启动方式 | 事项 |
0 | BOOT_ADD0[15:0] | 0x0800 | Flash启动 |
启动地址高16位由BOOT_ADD0寄存器决定,低16位为0x0000; BOOT_ADD0寄存器可以修改,修改后掉电不丢失; 0x0000 0000 到 0x3FFF 0000 的存储器地址都可以设置; |
1 | BOOT_ADD1[15:0] | 0x1FF0 | 系统存储器启动 |
2 堆栈
2.1 堆栈的汇编程序
1 ;********************** 目的是分配数据段用来做"栈"********************** 2 ; 表示即将声明数据段STACK用来做"栈",不用填入初始数据,可读写,首地址按照2^3对齐(8字节对齐); 3 Stack_Size EQU 0x00001000 4 AREA STACK, NOINIT, READWRITE, ALIGN=3 5 Stack_Mem SPACE Stack_Size 6 __initial_sp 7 8 ; **********************目的是分配数据段用来做"堆"********************** 9 Heap_Size EQU 0x0000800 10 AREA HEAP, NOINIT, READWRITE, ALIGN=3 11 __heap_base 12 Heap_Mem SPACE Heap_Size 13 __heap_limit
2.2 那么什么是堆栈呢?总结了一下,大概概括成了下面的表格形式;
STACK 栈 |
主要用来存储局部变量,变量的内存块;栈空间是从顶层开始向下生长的; 对于多级调用的函数入口地址,需要使用栈空间配合连接寄存器来存储主调函数的地址; |
|
Stack_Mem | 表示栈的首地址,应该是给microLIB库使用的; | |
__initial_sp | 栈标号,表示栈顶地址;也就是栈的内存最大地址; | |
HEAP 堆 |
动态分配时使用的内存块; 如果程序中没有使用到动态内存分配的话,编译器是不会编译出"堆"空间的; |
|
__heap_base | 堆标号,表示"堆"的起始地址 | |
Heap_Mem | 表示堆的首地址,应该是给microLIB库使用的; | |
__heap_limit | 堆标号,表示"堆"的结束地址 |
2.3 至于堆栈标号Stack_Mem和Stack_Size大概是标识堆栈地址给microLIB库使用的把,应该没什么用;如下所示
;MDK针对嵌入式推出了microLIB小型库,在功能上是用来替代C标准库的; ;下面代码的功能主要是决定要不要启用microLIB库;以下代码比较不重要; IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF
3 启动文件
;**************中断向量表***************************************************************** AREA RESET, DATA, READONLY ; 即将声明一个内存区域RESET,是数据段,只读; EXPORT __Vectors ; 声明"标识__Vectors"可以被外部文件调用 EXPORT __Vectors_End ; 声明"标识__Vectors_End"可以被外部文件调用 EXPORT __Vectors_Size ; 声明"标识__Vectors_Size"可以被外部文件调用 __Vectors DCD __initial_sp ; 分配4字节内存,初始化为栈顶地址,该内存开始的地址标识为"__Vectors" DCD Reset_Handler ; 分配4字节内存,放入复位中断服务函数的"地址标识Reset Handler" DCD NMI_Handler ; ;..... DCD 0 ; DCD WAKEUP_PIN_IRQHandler ; __Vectors_End ; 内存结束的地址标识为"__Vectors_End" __Vectors_Size EQU __Vectors_End - __Vectors ; 声明标识"__Vectors_Size"用来表示中断向量表的大小 ;**************汇编代码段:上电复位中断服务程序举例*************************************************************** AREA |.text|, CODE, READONLY ; 即将声明一个内存区域.text,是代码段,只读; Reset_Handler PROC ;伪指令PROC和ENDP用来声明一个程序,当前程序段标识为"Reset_Handler" EXPORT Reset_Handler [WEAK] ;声明当前程序可被外部函数调用,为弱函数 IMPORT SystemInit ;引入文件外部声明的函数SystemInit() IMPORT __main ;引入文件外部声明的函数__main() LDR R0, =SystemInit ;将32bit函数入口地址放入R0寄存器中? BLX R0 ;跳转到R0寄存器内的地址去执行? LDR R0, =__main ; BX R0 ; ENDP ; ;**************汇编代码段:NMI中断服务程序举例*************************************************************** NMI_Handler PROC ; EXPORT NMI_Handler [WEAK] ;这里导入了中断服务程序,如果外部文件写了,那此处的弱函数就不使用了 B . ;B. 表示在这里跳转当前程序? 就是跳转到前面导入的程序 ENDP ;
EQU | 声明常数,汇编的常数单位不是bit,而是byte |
AREA | 表示即将分配一个数据段或代码段; 需要跟分配内存的SPACE指令一起使用; |
EXPORT | 声明为可被外部文件引用,主要提供给链接器用于连接库文件; |
IMPORT | 引入外部文件声明 |
WEAK | 弱声明,如果外部文件有相同的声明,则编译外部文件的声明,不编译弱声明 |
ALIGN | 声明编译器需要对指令或数据的存放地址进行对齐;默认缺省值为32bit对齐 |
PRESERVE8 | 当前文件的堆栈需按照8字节对齐,也就是64bit数据线对齐; |
THUMB | 表示接下来的指令都是thumb指令集,cm3,cm7采用的是thumb-2指令集, |
3.2 汇编指令
SPACE | 分配一段内存空间,并初始化这些内存空间为0; 需要跟声明内存属性的AREA伪指令一起使用; |
DCD | Define Constant Double-words;分配4字节的内存空间用来存储一个32bit的数据;并初始化这些内存空间; |
LDR | Load word;加载指令,将32bit数据存入目的寄存器中; |
B | Branch,跳转到目标地址执行,执行指令集为ARM指令集 |
BX | Branch with Exchange;跳转到目的地址执行,并且指令集从ARM指令集切换到thumb指令集; |
BLX |
Branch with Link and Exchange;thumb-2兼容许多thumb指令,但是cortex-m3不支持当前指令; |
零散1 | [地址]:地址加了[],用来表示取出地址内的数据; |
零散2 | 汇编指令中的常数都是地址,自然数的格式为#常数; |
原文:https://www.cnblogs.com/caesura-k/p/13639279.html