mcu 执行代码流程
2021-09-23 本文已影响0人
wjundong
-
为了弄清楚mcu的代码执行流程,我们应当从复位后执行的第一条代码开始解析。这里以STM32F103VE为例。无论是哪款芯片,最应该先了解的是内存分布,去官网查看可以得到STM32F103VE具有512 KB Flash,64 KB SRAM。然后查看其这些存储器被映射到地址空间的什么地方。在目录中很容易找到 Memory mapping 一章,知道了 512KB 的Flash被映射到 0x0800 0000-0x0807 FFFF的地址空间, 64KB SRAM 的地址空间为0x2000 0000-0x2000 FFFF。
-
Flash 是用来保存用户代码的,SRAM是用来存储运行的变量和开始堆栈的,CPU复位后必然会去Flash 加载用户的代码,那么复位后是从哪个地方开始加载呢?根据MDK自动生成的分散加载文件:
LR_IROM1 0x08000000 0x00080000 { ; load region size_region
ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
}
LR_IROM1 == Load region Internal ROM 1,表示第一个内部 ROM 加载域。
ER_IROM1 == Execution region Internal ROM 1, 表示第一个内部 ROM 运行域。
可以看到, (RESET, +First) 指定了Flash 加载顺序将以 RESET 区域开始
最简单的程序:
AREA RESET, DATA, READONLY
DCD 0x1234
DCD Reset_Handler
AREA |.text|, CODE, READONLY
Reset_Handler PROC
MOV R0,#0x666
B .
ENDP
END
该程序首先声明一个只读的 DATA 区,DATA 区定义两个32位数据, 然后声明一个代码区, 里面定义一个子函数Reset_Handler;
因为复位后CPU会从 Flash 起始地址的第二个32位取数据, 因此将通过 DCD Reset_Handler 将 Reset_Handler 地址写到该区域让cpu复位后立刻转跳到 Reset_Handler 以执行复位代码。
下面是官方提供的启动文件:
;*******************************************************************************
; 申明堆区
;*******************************************************************************
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
;*******************************************************************************
; 申明栈区
;*******************************************************************************
Stack_Size EQU 0x00000500
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
PRESERVE8
THUMB
;*******************************************************************************
; 向量表区
;*******************************************************************************
AREA RESET, DATA, READONLY
DCD __initial_sp
DCD Reset_Handler
; ... 此处省略许多中断入口
;*******************************************************************************
; 代码区
;*******************************************************************************
AREA |.text|, CODE, READONLY
Reset_Handler PROC
MOV R0,#0x666
PUSH {R0}
POP {R1}
B .
ENDP
;*******************************************************************************
; 用户堆栈初始化
;*******************************************************************************
IF :DEF:__MICROLIB ; 如果使用 MicroLib 则只需导出堆栈的关键变量
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE ; 否则需要导出 __user_initial_stackheap 子函数用于初始化堆栈
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
END