stm32f407启动代码分析

官方资料

有关Cortex 内核的指令我们可以参考《CM3 权威指南 CnR2》第四章:指令集。剩下的 ARM 的汇编指令我们可以在 MDK->Help->Uvision Help 中搜索到,检索出来的结果会有很多,我们只需要看 Assembler User Guide 这部分即可。

指令名称作用
EQU给数字常量取一个符号名,相当于 C 语言中的 define
AREA汇编一个新的代码段或者数据段
SPACE分配内存空间
PRESERVE8当前文件堆栈需按照 8 字节对齐
EXPORT声明一个标号具有全局属性,可被外部的文件使用
DCD以字为单位分配内存,要求 4 字节对齐,并要求初始化这些内存
PROC定义子程序,与 ENDP 成对使用,表示子程序结束
WEAK弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号
IMPORT声明标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似
B跳转到一个标号
ALIGN编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示 4 字节对齐。
END到达文件的末尾,文件结束
IF,ELSE,ENDIF汇编条件分支语句,跟 C 语言的 if else 类似

Cortex-M3内核怎么开始执行一个程序

程序要执行得有栈用于局部变量,函数调用,函数形参等的开销,得有一个指针指向要执行的程序,所以Cortex-M3内核复位后做以下三件事:
1、从0x00000000读出栈顶(MSP)
2、从0x00000004读出程序计数器(PC)
3、跳转执行PC指向的代码

启动文件做什么

我们知道了Cortex-M3内核要读出栈顶和程序计数器,那么栈顶在哪,栈有多大,PC指针指向哪里,中断怎么处理,怎么开始执行C函数?这些就是启动文件里通过汇编语句要处理的事情

1、初始化堆栈大小

设置栈大小

Stack_Size		EQU     0x400

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

EQU相当于#define
AREA用于定义一个新的数据段或者代码段,后面参数表示这个段名字为 STACK,NOINIT 即不初始化,可读可写,8(2^3)字节对齐。
SPACE指令用于分配一段连续的内存空间,Stack_Mem 是标签也是首地址,__initial_sp是末地址(即栈顶)
设置堆大小

Heap_Size      EQU     0x200

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

开辟堆的大小为 0X00000200(512 字节),名字为 HEAP,NOINIT 即不初始化,可读可写,8(2^3)字节对齐。__heap_base 表示对的起始地址,__heap_limit 表示堆的结束地址。堆是由低向高生长的,跟栈的生长方向相反。堆主要用来动态内存的分配,像 malloc()函数申请的内存就在堆上面。

2、初始化中断向量表

Cortex-M3内核拥有11个系统异常和最多240个外部中断,当有中断或异常发生,会返回一个编号给内核,内核通过编号查找中断向量表找到对应中断服务函数的地址去执行,所以中断向量表存放的就是中断服务函数的地址

3、初始化堆栈指针 SP=_initial_sp、PC 指针=Reset_Handler

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__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
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts(外部中断开始)
                DCD     WWDG_IRQHandler                   ; Window WatchDog                                        
                DCD     PVD_IRQHandler                    ; PVD through EXTI Line detection                        
                DCD     TAMP_STAMP_IRQHandler             ; Tamper and TimeStamps through 
                ;为减小篇幅中间部分省略
                                             
__Vectors_End
__Vectors_Size  EQU  __Vectors_End - __Vectors

DCD指令表示在存储器上分配一片连续的字存储单元,并把 DCD 后面跟的值赋值到刚分配的存储单元内。因为是第一次使用DCD所以分配的地址从0开始,每次分配4字节所以0x00000000放__initial_sp,0x00000004放Reset_Handler,实现了初始化MSP地址和PC指向的地址

4、配置系统时钟

Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

WEAK表示此声明是弱声明
EXPORT 是声明外部全局标签
IMPORT 引入外部全局标签
①“LDR R0, =SystemInit” 是将函数 SystemInit 的地址放到寄存器 R0中保存。
② “BLX R0” 是跳转到 R0 寄存器存储的地址处运行。B是直接跳转指令,B后加L表示保存PC的值到寄存器 R14,可以在执行完跳转全部指令后返回跳转前的位置。B后加X是表示根据跳转的地址改变当前的状态,地址的最低位 add[0] 若是1,则将存储器状态更改为 Thumb 状态,反之地址的最低位 add[0] 若是0,则将存储器状态更改为 ARM 状态。( 注意Cortex-M3内核不允许进入ARM 状态,否则将会产生一个硬件异常中断 HardFault_Handler )。

在SystemInit中完成时钟配置等的系统配置,__main() 函数执行C语言环境初始化的操作,包括堆栈、寄存器等的设置,初始化完成后,将会跳转到main()

5、初始化用户堆栈,从而最终调用 main 函数

                 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

                 END

首先判断是否定义了__MICROLIB (在 KEIL 里面配置),如果定义了这个宏则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用,然后堆栈的初始化就由 C 库函数_main 来完成如果没有定 义 __MICROLIB ,需要编写一个初始化堆栈的函数,标签为 __user_initial_stackheap ,并将其声明为外部全局标签,以供 __main() 函数在初始化C语言环境时使用。
IF,ELSE,ENDIF:汇编的条件分支语句,跟 C 语言的 if ,else 类似
END:文件结束

参考博客: https://blog.csdn.net/qq_36300069/article/details/124319349

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值