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 編譯程序產生的代碼段,或用於以某種方式與 庫關聯的代碼段。
  屬性字段表示該代碼段(或數據段)的相關屬性,多個屬性用逗號分隔。 常用的屬性如下:
——CODE 屬性:用於定義代碼段,默認為 READONLY
——DATA 屬性:用於定義數據段,默認為 READWRITE
——READONLY 屬性:指定本段為只讀,代碼段默認為 READONLY
——READWRITE 屬性:指定本段為可讀可寫,數據段的默認屬性為 READWRITE
——ALIGN 屬性:使用方式為 ALIGN 表達式。在默認時, ELF(可執行連接文件)的代碼段和數據段是按字對齊的,表達式的取值范圍為 0~31,相應的對齊方式為 表達式次方。

     如: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),只分配空間不做初始化或者初始化為 0NOINIT,可讀可寫READWRITE;8 字節對齊: ALIGN3;棧頂地址: __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
只分配空間不做初始化或者初始化為 0NOINIT
可讀可寫: READWRITE
8 字節對齊: ALIGN3
堆起始地址: __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]選項表示弱定義,如果編譯器發現在別處定義了同名的函數,則在鏈接時用別處的地址進行鏈接,如果其它地方沒有定義,編譯器也不報錯,以此處地址進行鏈接。

例如:對NMI_Handler的定義有兩處,如下圖,首先使用的是C語言的定義的,而不是匯編定義的。

1,匯編定義,后面加【weak】

 

 2, C語言定義 在stm32f10x_it.c中

 

 

 

 

  

  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_Handler\
11                 PROC
12                 EXPORT  MemManage_Handler          [WEAK]
13                 B       .
14                 ENDP
15 BusFault_Handler\
16                 PROC
17                 EXPORT  BusFault_Handler           [WEAK]
18                 B       .
19                 ENDP
20 UsageFault_Handler\
21                 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_Handler\
30                 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寄存器來進行重映射,修改。

 

  后面,手把手寫一下啟動文件,並進行驗證一下;寫完啟動文件之后寫分散加載文件。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM