ARM Cortex-M底层技术(2)—启动代码详解

杂谈

  工作了一天,脑袋比较乱。一直想把底层的知识写成一个系列,希望可以坚持下去。为什么要写底层的东西呢?首先,工作用到了这部分内容,最近和内部Flash打交道比较多,自然而然会接触到一些底层的东西;第二,近些年来Cortex-M阵营各厂商(ST、Nordic、ATMEL……)对新产品的迭代速度越来越快,以及微控制器应用普及程度的加深,越来越多的开发者把更多精力投注在应用层开发上,花在对底层技术上的时间越来越少,更深层次的原因是走嵌入式底层没有做互联网上层赚钱。希望自己可以把嵌入式ARM Cortex-M(M0/M0+/M3/M4/M7/M23/M33)底层技术写下去,加油!(长文,慎入,谢谢)

一. STM32的启动代码分析
  当前,STM32因其丰富的学习资料,已经成为了80%嵌入式工作者入门学习的首选,当然我也不例外,主要是因为在学生时代,没钱买更好的开发板。工作之后,你会发现老板更抠门,产品的核心芯片一代比一代便宜。废话不多说,直接上知识点。

1.1 启动代码的概念

 问题1. 什么是启动代码

  启动代码是系统上电或者复位后运行的第一段代码,是进入C 语言的main 函数之前需要执行的那段汇编代码。或者说用户程序运行之前对系统硬件及软件环境进行必要的初始化并在最后使程序跳转到用户程序。

 问题2. 启动代码主要干了什么

  启动代码直接面对ARM 处理器内核及硬件控制器进行编程,所执行的操作与具体的目标系统紧密相关。C语言程序的运行需要具备一定的条件,如分配好外部数据空间、设置初始堆栈指针、配置时钟、设置中断向量入口、设置初始程序计数器(指向main())等。对于 Cortex-M系列的芯片而言,启动代码大同小异,故我挑选其中一个进行分析。ARM Cortex-M系列MCU的启动代码的主要做3件事:

  • 初始化并正确放置异常/中断向量表;
  • 分散加载;
  • 初始化C语言运行环境(初始化堆栈以及C Library、浮点等)

1.2 启动代码详解

汇编基础1:
  1. 伪指令: EQU
  语法格式:名称  EQU  表达式{,类型}
  EQU 伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于 C 语言的#define,所以这下能理解了吧。
  

  2. 伪指令: AREA
  语法格式: AREA 段名{, 属性 1}{, 属性 2}……
  AREA 命令指示汇编程序汇编一个新的代码段或数据段。理解:段是独立的、指定的、不可见的代码或数据块,它们由链接程序处理。
  段名: 可以为段选择任何段名。但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。例如, |1_DataArea|。
  有些名称是习惯性的名称。例如: |.text|用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段。
  属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。 常用的属性如下:
——CODE 属性:用于定义代码段,默认为 READONLY。
——DATA 属性:用于定义数据段,默认为 READWRITE。
——READONLY 属性:指定本段为只读,代码段默认为 READONLY。
——READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE。
——ALIGN 属性:使用方式为 ALIGN 表达式。在默认时, ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为 0~31,相应的对齐方式为 2 表达式次方。

     如:ALIGN=3表示8字节对齐。

——NOINIT 属性: 表示数据段是未初始化的或初始化为零。

一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。

  3. 伪指令: SPACE 用于分配一片连续的存储单元

第一部分 定义栈段,不初始化 

1 Stack_Size      EQU     0x00000400
2
3                 AREA    STACK, NOINIT, READWRITE, ALIGN=3
4 Stack_Mem       SPACE   Stack_Size
5 __initial_sp

  上面的程序这样理解,定义了一个栈,栈名为STACK (AREA  STACK),大小为Stack_Size(EQU理解为#define),只分配空间不做初始化或者初始化为 0;NOINIT,可读可写READWRITE;按 8 字节对齐: ALIGN=3;栈顶地址: __initial_sp ,SPACE表示分配一块连续的区域。

第二部分 定义堆段,不初始化 

1 Heap_Size       EQU     0x00000200
2
3                 AREA    HEAP, NOINIT, READWRITE, ALIGN=3
4 __heap_base
5 Heap_Mem        SPACE   Heap_Size
6 __heap_limit

堆名: HEAP
大小: Heap_Size
只分配空间不做初始化或者初始化为 0: NOINIT
可读可写: READWRITE:
按 8 字节对齐: ALIGN=3
堆起始地址: __heap_base
堆终止地址: __heap_limit

1 PRESERVE8 ;指示编译器 8 字节对齐(keil 编译器时需要加上)
2 THUMB ;指示编译器为 THUMB 指令

汇编基础2:

4. 伪指令: EXPORT

语法格式: EXPORT 标号{[WEAK]}
EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。
EXPORT 可用 GLOBAL 代替。标号在程序中区分大小写, [WEAK]选项声明其他的同名标号优先于该标号被引用。

5. 伪指令: DCD
语法格式: DCD  表达式
DCD(或 DCDU) 伪指令用于分配一个或多个连续的字(32bit)存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。用 DCD 分配的字存储单元是字对齐的。(一片是指多少?我并没有查到相关资料,但是我看了公司大神们写的启动文件,备注的地址只占了4个字节,所以我理解成分配一个字的存储单元)

 第三部分  定义复位段(中断向量表),并初始化

 1                 AREA    RESET, DATA, READONLY
 2                 EXPORT  __Vectors
 3                 EXPORT  __Vectors_End
 4                 EXPORT  __Vectors_Size
 5
 6 __Vectors       DCD     __initial_sp               ; Top of Stack
 7                 DCD     Reset_Handler              ; Reset Handler
 8                 DCD     NMI_Handler                ; NMI Handler
 9                 DCD     HardFault_Handler          ; Hard Fault Handler
10                 DCD     MemManage_Handler          ; MPU Fault Handler
11                 DCD     BusFault_Handler           ; Bus Fault Handler
12                 DCD     UsageFault_Handler         ; Usage Fault Handler
13                 DCD     0                          ; Reserved
14                 DCD     0                          ; Reserved
15                 DCD     0                          ; Reserved
16                 DCD     0                          ; Reserved
17                 DCD     SVC_Handler                ; SVCall Handler
18                 DCD     DebugMon_Handler           ; Debug Monitor Handler
19                 DCD     0                          ; Reserved
20                 DCD     PendSV_Handler             ; PendSV Handler
21                 DCD     SysTick_Handler            ; SysTick Handler
22
23                 ; External Interrupts
24                 DCD     WWDG_IRQHandler            ; Window Watchdog
25                 DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
26                 DCD     TAMPER_IRQHandler          ; Tamper
27                 DCD     RTC_IRQHandler             ; RTC
28                 DCD     FLASH_IRQHandler           ; Flash
29                 DCD     RCC_IRQHandler             ; RCC
30                 DCD     EXTI0_IRQHandler           ; EXTI Line 0
31                 DCD     EXTI1_IRQHandler           ; EXTI Line 1
32                 DCD     EXTI2_IRQHandler           ; EXTI Line 2
33                 DCD     EXTI3_IRQHandler           ; EXTI Line 3
34                 DCD     EXTI4_IRQHandler           ; EXTI Line 4
35                 DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
36                 DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
37                 DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
38                 DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
39                 DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
40                 DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
41                 DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
42                 DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
43                 DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
44                 DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
45                 DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
46                 DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
47                 DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
48                 DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
49                 DCD     TIM1_UP_IRQHandler         ; TIM1 Update
50                 DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
51                 DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
52                 DCD     TIM2_IRQHandler            ; TIM2
53                 DCD     TIM3_IRQHandler            ; TIM3
54                 DCD     TIM4_IRQHandler            ; TIM4
55                 DCD     I2C1_EV_IRQHandler         ; I2C1 Event
56                 DCD     I2C1_ER_IRQHandler         ; I2C1 Error
57                 DCD     I2C2_EV_IRQHandler         ; I2C2 Event
58                 DCD     I2C2_ER_IRQHandler         ; I2C2 Error
59                 DCD     SPI1_IRQHandler            ; SPI1
60                 DCD     SPI2_IRQHandler            ; SPI2
61                 DCD     USART1_IRQHandler          ; USART1
62                 DCD     USART2_IRQHandler          ; USART2
63                 DCD     USART3_IRQHandler          ; USART3
64                 DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
65                 DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
66                 DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
67                 DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
68                 DCD     TIM8_UP_IRQHandler         ; TIM8 Update
69                 DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
70                 DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
71                 DCD     ADC3_IRQHandler            ; ADC3
72                 DCD     FSMC_IRQHandler            ; FSMC
73                 DCD     SDIO_IRQHandler            ; SDIO
74                 DCD     TIM5_IRQHandler            ; TIM5
75                 DCD     SPI3_IRQHandler            ; SPI3
76                 DCD     UART4_IRQHandler           ; UART4
77                 DCD     UART5_IRQHandler           ; UART5
78                 DCD     TIM6_IRQHandler            ; TIM6
79                 DCD     TIM7_IRQHandler            ; TIM7
80                 DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
81                 DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
82                 DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
83                 DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
84 __Vectors_End
85
86 __Vectors_Size  EQU  __Vectors_End - __Vectors

段名: RESET(根据前面的套路,发现AREA的第一个属性表示段名)
大小: __Vectors_Size(大小肯定要表示出来,其实堆栈是直接给出的,RESET段是先分配,后计算得到的)
数据段: DATA
只读: READONLY
按字节对齐: 默认 ALIGN
向量表起始地址: __Vectors(标号)
向量表终止地址: __Vectors_End

注意:SPACE 和 DCD有什么区别?

1. SPACE和DCD的功能类似,SPACE申请一片内存空间,DCD申请一个或多个字(32bit)的内存空间。
2. SPACE和DCD的区别在于,SPACE申请空间但不赋初值,DCD申请一个字的空间,并赋初值。

参考资料:https://blog.csdn.net/inurlcn/article/details/20691233#reply

汇编基础3:

  6. 过程定义伪指令: PROC、 ENDP
  语法格式: <过程名> PROC [类型]
        ……
        RET
        <过程名> ENDP
  过程就是子程序,即定义一个子程序。一个过程可以被其它程序所调用(用 CALL 指令),过程的最后一条指令一般是返回指令(RET)。

  7. 伪指令: IMPORT
  语法格式: IMPORT 标号 {[WEAK]}
  IMPORT 伪指令用于通知编译器要使用的标号在其他源文件中定义。

   [WEAK]选项表示弱定义,如果编译器发现在别处定义了同名的函数,则在链接时用别处的地址进行链接,如果其它地方没有定义,编译器也不报错,以此处地址进行链接。
  

  8. 伪指令: LDR
  语法格式: LDR{执行条件,如 EQ、 NE 等} register,=expr/label_expr

  大范围的地址读取伪指令 LDR 用于加载 32 位的立即数或一个地址值到指定寄存器,在汇编编译源程序时, LDR 伪指令被编译器替换成一条合适的指令。

  9. Thumb 跳转指令: B、 BL、 BX
  语法格式: B{执行条件,如 EQ、 NE 等} label
  带链接 BL{执行条件,如 EQ、 NE 等} label
  带状态切换 BX{执行条件,如 EQ、 NE 等} label

 1                 AREA    |.text|, CODE, READONLY
 2
 3 ; Reset handler
 4 Reset_Handler   PROC
 5                 EXPORT  Reset_Handler             [WEAK]
 6                 IMPORT  __main
 7                 IMPORT  SystemInit
 8                 LDR     R0, =SystemInit
 9                 BLX     R0
10                 LDR     R0, =__main
11                 BX      R0
12                 ENDP

段名: .text
代码段: CODE
只读: READONLY
按字节对齐: 默认 ALIGN
代码段起始地址: Reset_Handler

更详细的来说一下这段代码,

  这部分可以称作Reset_Handler实体,是芯片上电经过厂商BOOTROM后,用户最开始可控的地方。

  • 第一行,申请一个名为.text的代码段,该代码段的属性是只读的,其他没写,认为认为是默认的;
  • 第三行,注释
  • 第四行,Reset_Handler是标号,定义同一个名为Reset_Handler的子程序(代码段)
  • 第五行,声明一下Reset_Handler程序可以在外部使用,[WEAK]表示没有找到其他地方的定义时,然后连接器使用此处定义的Reset_Handler程序
  • 第六行和第七行,在Reset_Handler函数中导入SystemInit 和__main ,这两个标号在其他文件,在链接的时候需要到其他文件去寻找
  • 第八行,把SystemInit 的地址加载到寄存器R0
  • 第九行,程序跳转到R0 中的地址执行程序,如果在SystemInit中配置了时钟,之后系统的时钟就被设置成我们配置的了。
  • 第十行把_main 的地址加载到寄存器R0。
  • 第十一行程序跳转到R0 中的地址执行程序,执行完毕之后就去到我们熟知的C 世界。
  • 第十二行表示子程序的结束。

   因为默认的标准的启动代码主要工作是在Reset_Handler里面完成的,调用函数一般也会再这里。我们可以发现,在启动代码的汇编语言里调用C语言函数都可以使用以上两步:

  1. 导入函数标号
  2. 调用这个函数

  例如:

1 IMPORT  SystemInit        ;导入函数标号
2 LDR     R0, =SystemInit     ;2行和3行合起来,是调用函数的功能
3 BLX     R0          

当然这里有几点注意事项,这里不是所有函数都可以在汇编语言中调用的,因为此时__main还没有运行,C语言运行环境还没有被完整搭建起来,堆栈也没有初始化完成,所以要注意:

(1)调用的C函数参数不能超过4个,不用可以,但用的话不能超过4个参数,原因是在Cortex-M体系MCU中,函数的1-4个形参会压进R0-R3这4个通用寄存器(Cortex-M系列MCU,M0也好、M3也好、M4也好都只有16个通用寄存器,内部寄存器结构去参照ARM官方的白皮书)如果有第五个参数,这个参数会被压栈,但因为此时__main还没有运行,堆栈没有被初始化所以此时如果函数有超过4个以上的参数,会导致程序跑飞;

(2)不要把需要调用的函数写到__main之后,因为没有意义,程序不会跑到那里;

参考网址:https://blog.csdn.net/weixin_39118482/article/details/79632734

汇编基础4:

  10. 内置变量: {PC} 或“.” 当前指令地址

  11. 汇编语句格式规范:
  ARM 汇编中,所有标号必须在一行的顶格书写,其后面不要添加“:”,但所有指令均不能顶格书写
  ARM 汇编器对标识符大小写敏感,书写标号及指令时字母大小写要一致, 在 ARM 汇编程序中,一个 ARM 指令、伪指令、寄存器名可以全部为大写字母,也可以全部为小写字母,但不要大小写混合使用。

 异常处理函数 1

 1 NMI_Handler     PROC        ;定义一个名为NMI_Handler的子程序
 2                 EXPORT  NMI_Handler                [WEAK]    ;外部声明
 3                 B       .     ;跳转到子程序的地址(这个函数里面通常写的是死循环,所以当出现异常时,就会卡死)
 4                 ENDP      ;结束
 5 HardFault_Handler 6                 PROC
 7                 EXPORT  HardFault_Handler          [WEAK]
 8                 B       .
 9                 ENDP
10 MemManage_Handler11                 PROC
12                 EXPORT  MemManage_Handler          [WEAK]
13                 B       .
14                 ENDP
15 BusFault_Handler16                 PROC
17                 EXPORT  BusFault_Handler           [WEAK]
18                 B       .
19                 ENDP
20 UsageFault_Handler21                 PROC
22                 EXPORT  UsageFault_Handler         [WEAK]
23                 B       .
24                 ENDP
25 SVC_Handler     PROC
26                 EXPORT  SVC_Handler                [WEAK]
27                 B       .
28                 ENDP
29 DebugMon_Handler30                 PROC
31                 EXPORT  DebugMon_Handler           [WEAK]
32                 B       .
33                 ENDP
34 PendSV_Handler  PROC
35                 EXPORT  PendSV_Handler             [WEAK]
36                 B       .
37                 ENDP
38 SysTick_Handler PROC
39                 EXPORT  SysTick_Handler            [WEAK]
40                 B       .
41                 ENDP

异常处理函数2

这个默认的异常处理函数处理所有外部中断 。

  1 Default_Handler PROC
  2
  3                 EXPORT  WWDG_IRQHandler            [WEAK]
  4                 EXPORT  PVD_IRQHandler             [WEAK]
  5                 EXPORT  TAMPER_IRQHandler          [WEAK]
  6                 EXPORT  RTC_IRQHandler             [WEAK]
  7                 EXPORT  FLASH_IRQHandler           [WEAK]
  8                 EXPORT  RCC_IRQHandler             [WEAK]
  9                 EXPORT  EXTI0_IRQHandler           [WEAK]
 10                 EXPORT  EXTI1_IRQHandler           [WEAK]
 11                 EXPORT  EXTI2_IRQHandler           [WEAK]
 12                 EXPORT  EXTI3_IRQHandler           [WEAK]
 13                 EXPORT  EXTI4_IRQHandler           [WEAK]
 14                 EXPORT  DMA1_Channel1_IRQHandler   [WEAK]
 15                 EXPORT  DMA1_Channel2_IRQHandler   [WEAK]
 16                 EXPORT  DMA1_Channel3_IRQHandler   [WEAK]
 17                 EXPORT  DMA1_Channel4_IRQHandler   [WEAK]
 18                 EXPORT  DMA1_Channel5_IRQHandler   [WEAK]
 19                 EXPORT  DMA1_Channel6_IRQHandler   [WEAK]
 20                 EXPORT  DMA1_Channel7_IRQHandler   [WEAK]
 21                 EXPORT  ADC1_2_IRQHandler          [WEAK]
 22                 EXPORT  USB_HP_CAN1_TX_IRQHandler  [WEAK]
 23                 EXPORT  USB_LP_CAN1_RX0_IRQHandler [WEAK]
 24                 EXPORT  CAN1_RX1_IRQHandler        [WEAK]
 25                 EXPORT  CAN1_SCE_IRQHandler        [WEAK]
 26                 EXPORT  EXTI9_5_IRQHandler         [WEAK]
 27                 EXPORT  TIM1_BRK_IRQHandler        [WEAK]
 28                 EXPORT  TIM1_UP_IRQHandler         [WEAK]
 29                 EXPORT  TIM1_TRG_COM_IRQHandler    [WEAK]
 30                 EXPORT  TIM1_CC_IRQHandler         [WEAK]
 31                 EXPORT  TIM2_IRQHandler            [WEAK]
 32                 EXPORT  TIM3_IRQHandler            [WEAK]
 33                 EXPORT  TIM4_IRQHandler            [WEAK]
 34                 EXPORT  I2C1_EV_IRQHandler         [WEAK]
 35                 EXPORT  I2C1_ER_IRQHandler         [WEAK]
 36                 EXPORT  I2C2_EV_IRQHandler         [WEAK]
 37                 EXPORT  I2C2_ER_IRQHandler         [WEAK]
 38                 EXPORT  SPI1_IRQHandler            [WEAK]
 39                 EXPORT  SPI2_IRQHandler            [WEAK]
 40                 EXPORT  USART1_IRQHandler          [WEAK]
 41                 EXPORT  USART2_IRQHandler          [WEAK]
 42                 EXPORT  USART3_IRQHandler          [WEAK]
 43                 EXPORT  EXTI15_10_IRQHandler       [WEAK]
 44                 EXPORT  RTCAlarm_IRQHandler        [WEAK]
 45                 EXPORT  USBWakeUp_IRQHandler       [WEAK]
 46                 EXPORT  TIM8_BRK_IRQHandler        [WEAK]
 47                 EXPORT  TIM8_UP_IRQHandler         [WEAK]
 48                 EXPORT  TIM8_TRG_COM_IRQHandler    [WEAK]
 49                 EXPORT  TIM8_CC_IRQHandler         [WEAK]
 50                 EXPORT  ADC3_IRQHandler            [WEAK]
 51                 EXPORT  FSMC_IRQHandler            [WEAK]
 52                 EXPORT  SDIO_IRQHandler            [WEAK]
 53                 EXPORT  TIM5_IRQHandler            [WEAK]
 54                 EXPORT  SPI3_IRQHandler            [WEAK]
 55                 EXPORT  UART4_IRQHandler           [WEAK]
 56                 EXPORT  UART5_IRQHandler           [WEAK]
 57                 EXPORT  TIM6_IRQHandler            [WEAK]
 58                 EXPORT  TIM7_IRQHandler            [WEAK]
 59                 EXPORT  DMA2_Channel1_IRQHandler   [WEAK]
 60                 EXPORT  DMA2_Channel2_IRQHandler   [WEAK]
 61                 EXPORT  DMA2_Channel3_IRQHandler   [WEAK]
 62                 EXPORT  DMA2_Channel4_5_IRQHandler [WEAK]
 63 ;下面的全部异常处理函数标号都对应同一个地址, 这个地址也是 Default_Handler 的地址 64 WWDG_IRQHandler
 65 PVD_IRQHandler
 66 TAMPER_IRQHandler
 67 RTC_IRQHandler
 68 FLASH_IRQHandler
 69 RCC_IRQHandler
 70 EXTI0_IRQHandler
 71 EXTI1_IRQHandler
 72 EXTI2_IRQHandler
 73 EXTI3_IRQHandler
 74 EXTI4_IRQHandler
 75 DMA1_Channel1_IRQHandler
 76 DMA1_Channel2_IRQHandler
 77 DMA1_Channel3_IRQHandler
 78 DMA1_Channel4_IRQHandler
 79 DMA1_Channel5_IRQHandler
 80 DMA1_Channel6_IRQHandler
 81 DMA1_Channel7_IRQHandler
 82 ADC1_2_IRQHandler
 83 USB_HP_CAN1_TX_IRQHandler
 84 USB_LP_CAN1_RX0_IRQHandler
 85 CAN1_RX1_IRQHandler
 86 CAN1_SCE_IRQHandler
 87 EXTI9_5_IRQHandler
 88 TIM1_BRK_IRQHandler
 89 TIM1_UP_IRQHandler
 90 TIM1_TRG_COM_IRQHandler
 91 TIM1_CC_IRQHandler
 92 TIM2_IRQHandler
 93 TIM3_IRQHandler
 94 TIM4_IRQHandler
 95 I2C1_EV_IRQHandler
 96 I2C1_ER_IRQHandler
 97 I2C2_EV_IRQHandler
 98 I2C2_ER_IRQHandler
 99 SPI1_IRQHandler
100 SPI2_IRQHandler
101 USART1_IRQHandler
102 USART2_IRQHandler
103 USART3_IRQHandler
104 EXTI15_10_IRQHandler
105 RTCAlarm_IRQHandler
106 USBWakeUp_IRQHandler
107 TIM8_BRK_IRQHandler
108 TIM8_UP_IRQHandler
109 TIM8_TRG_COM_IRQHandler
110 TIM8_CC_IRQHandler
111 ADC3_IRQHandler
112 FSMC_IRQHandler
113 SDIO_IRQHandler
114 TIM5_IRQHandler
115 SPI3_IRQHandler
116 UART4_IRQHandler
117 UART5_IRQHandler
118 TIM6_IRQHandler
119 TIM7_IRQHandler
120 DMA2_Channel1_IRQHandler
121 DMA2_Channel2_IRQHandler
122 DMA2_Channel3_IRQHandler
123 DMA2_Channel4_5_IRQHandler
124                 B       .
125
126                 ENDP

先定义,当外部中断触发时,B . 表示跳进去执行。

汇编基础5:

  由于前面只是定义了堆栈段并没有初始化,这里对堆栈段进行初始化。 就像定义了: int a; 初始化 a = 1;也可以像代码段一样定义的同时就初始化: int b = 2;

  在_main中,会调用一下的程序,进行堆栈的初始化,进而为进入到C语言中的main函数做好准备。

  下面代码中有个__MICROLIB,对应后面 MDK 截图的 Use MicroLIB,如果选了勾选了 Use MicroLIB, IF 就为真,否则为假

初始化堆栈段

 1                  IF      :DEF:__MICROLIB
 2
 3                  EXPORT  __initial_sp
 4                  EXPORT  __heap_base
 5                  EXPORT  __heap_limit
 6
 7                  ELSE
 8
 9                  IMPORT  __use_two_region_memory
10                  EXPORT  __user_initial_stackheap
11
12 __user_initial_stackheap
13
14                  LDR     R0, =  Heap_Mem
15                  LDR     R1, =(Stack_Mem + Stack_Size)
16                  LDR     R2, = (Heap_Mem +  Heap_Size)
17                  LDR     R3, = Stack_Mem
18                  BX      LR
19
20                  ALIGN
21
22                  ENDIF
23
24                  END

  microlib是缺省C库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。这些应用程序不在操作系统中运行。microlib进行了高度优化以使代码变得很小。它的功能比缺省C库少,并且根本不具备某些ISOC特性。某些库函数的运行速度也比较慢,例如,memcpy()。不管使用与否,都可以,但是启动时稍微有点区别。

  • 启动流程1(使用标准库,不使用Microlib)如下图:

  • 启动流程2(使用Microlib)如下图:

  假设STM32被设置为从内部FLASH启动(这也是最常见的一种情况),中断向量表起始地位为0x8000000,则栈顶地址存放于0x8000000处(大部分是这个地址),而复位中断服务入口地址存放于0x8000004处(复位地址在栈顶地址4字节后)。当STM32遇到复位信号后,则从0x80000004处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转__main函数,最后进入mian函数,来到C的世界。

  

  解释一下一个小细节,绝大部分ARM-M协议的芯片,复位之后先进入厂商boot,此时所有的用户均无法接入处理器;厂商boot主要负责芯片最初级的初始化,加密及

对MCU进行一些差异性设置等,BOOT完成后,会把主动权交给用户,也就是启动代码;启动代码(执行汇编语言不需要此启动代码),在启动文件中,会设置MSP(主堆栈指针)和PC(程序计数器)的值,MSP的地址默认是0x00000000,PC的地址默认是0x00000004,这两个地址可以通过CORTEX-M中的VTOR寄存器来进行重映射,修改。

  下一节,手把手写一下启动文件,并进行验证一下;写完启动文件之后写分散加载文件。

原文地址:https://www.cnblogs.com/39950436-myqq/p/11574799.html

时间: 2024-09-29 05:54:13

ARM Cortex-M底层技术(2)—启动代码详解的相关文章

第14章 启动文件详解—零死角玩转STM32-F429系列

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料<STM32F4xx 中文参考手册>第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表:MDK中的帮助手册—ARM Development Tools:用来查询ARM的汇编指令和编译器相关的指令. 14.1 启动文件简介 启动文件由汇编编写,是

第14章 启动文件详解

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料<STM32F4xx 中文参考手册>第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表:MDK中的帮助手册-ARM Development Tools:用来查询ARM的汇编指令和编译器相关的指令. 14.1 启动文件简介 启动文件由汇编编写,是

php缓存技术——memcache常用函数详解

php缓存技术——memcache常用函数详解 2016-04-07 aileen PHP编程 Memcache函数库是在PECL(PHP Extension Community Library)中,主要作用是搭建大容量的内存数据的临时存放区域,在分布式的时候作用体现的非常明显,否则不建议使用. memcache 函数所有的方法列表如下: 参考http://www.php.net/manual/zh/function.Memcache-add.php Memcache::add - 添加一个值,

计算机启动过程详解

计算机启动过程详解打开电源启动机器几乎是电脑爱好者每天必做的事情,面对屏幕上出现的一幅幅启动画面,我们一点儿也不会感到陌生,但是,计算机在显示这些启动画面时都做了些什么工作呢?相信有的朋友还不是很清楚,本文就来介绍一下从打开电源到出现Windows的蓝天白云时,计算机到底都干了些什么事情.  首先让我们来了解一些基本概念.第一个是大家非常熟悉的BIOS(基本输入输出系统),BIOS是直接与硬件打交道的底层代码,它为操作系统提供了控制硬件设备的基本功能.BIOS包括有系统BIOS(即常说的主板BI

高可用集群技术之corosync应用详解(一)

Corosync概述: Corosync是集群管理套件的一部分,它在传递信息的时候可以通过一个简单的配置文件来定义信息传递的方式和协议等.它是一个新兴的软件,2008年推出,但其实它并不是一个真正意义上的新软件,在2002年的时候有一个项目Openais , 它由于过大,分裂为两个子项目,其中可以实现HA心跳信息传输的功能就是Corosync ,它的代码60%左右来源于Openais. Corosync可以提供一个完整的HA功能,但是要实现更多,更复杂的功能,那就需要使用Openais了.Cor

20155326《网络对抗》免考项目—— 深入恶意代码之恶意代码详解

20155326<网络对抗>免考项目--深入恶意代码之恶意代码详解 什么是恶意代码 恶意代码是一种程序,它通过把代码在不被察觉的情况下镶嵌到另一段程序中,从而达到破坏被感染电脑数据.运行具有入侵性或破坏性的程序.破坏被感染电脑数据的安全性和完整性的目的. 恶意代码生命周期 攻击目标: 个人计算机 服务器 移动智能终端 手机.平板等 智能设备 特斯拉汽车.智能家居.智能手表等 通信设备 路由器.交换机等 安全设备等 防火墙.IDS, IPS. VDS 攻击目标范围: 定点攻击 邮件.IP.域名.

s5pv210的启动方式详解(二)

s5pv210的启动流程参考s5pv210_iROM_ApplicationNote_Preliminary_20091126.pdf这篇文档. s5pv210支持Moveinand/iNand.SD/MMC.NandFlash.eSSD.UART.USB等多种启动方式. s5pv210启动流程详解: 1.在cpu上电后,首先执行iROM(类似于NorFlash,可以直接读数据)中的代码,iROM中的代码被称为BL0,BL0会初始化一些SoC内部的硬件资源. 2.BL0会根据OM pin上的电平

Linux开机启动程序详解

我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Linux开机引导的步骤. 加载内核LILO启动之后,如果你选择了Linux作为准备引导的操作系统,第一个被加载的东西就是内核.请记住此时的计算机内存中还不存在任何操作系统,PC(因为它们天然的设计缺陷)也还没有办法存取机器上全部的内存.因此,内核就必须完整地加载到可用RAM的第一个兆字节之内.为了实现这个目的,内核是被压缩了的.这个文件的头部包含着

Linux如何实现开机启动程序详解

Linux开机启动程序详解我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Linux开机引导的步骤. 加载内核LILO启动之后,如果你选择了Linux作为准备引导的操作系统,第一个被加载的东西就是内核.请记住此时的计算机内存中还不存在任何操作系统,PC(因为它们天然的设计缺陷)也还没有办法存取机器上全部的内存.因此,内核就必须完整地加载到可用RAM的第一个兆字节之内.为了实现这个目的,内核是被压缩