久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    第14章 Cortex-M啟動文件詳解

     waston 2018-05-02
    本章參考資料《STM32F4xx 中文參考手冊》第十章-中斷和事件:表 46. STM32F42xxx STM32F43xxx 的向量表;MDK中的幫助手冊—ARM Development Tools:用來查詢ARM的匯編指令和編譯器相關的指令。

    14.1 啟動文件簡介

    啟動文件由匯編編寫,是系統上電復位后第一個執行的程序。主要做了以下工作:

    1、初始化堆棧指針SP=_initial_sp

    2、初始化PC指針=Reset_Handler

    3、初始化中斷向量表

    4、配置系統時鐘

    5、調用C庫函數_main初始化用戶堆棧,從而最終調用main函數去到C的世界

    14.2 查找ARM匯編指令

    在講解啟動代碼的時候,會涉及到ARM的匯編指令和Cortex內核的指令,有關Cortex內核的指令我們可以參考《CM3權威指南CnR2》第四章:指令集。剩下的ARM的匯編指令我們可以在MDK->Help->Uvision Help中搜索到,以EQU為例,檢索如下:

    141 ARM 匯編指令索引

    檢索出來的結果會有很多,我們只需要看Assembler User Guide 這部分即可。下面列出了啟動文件中使用到的ARM匯編指令,該列表的指令全部從ARM Development Tools這個幫助文檔里面檢索而來。其中編譯器相關的指令WEAKALIGN為了方便也放在同一個表格了。

    表格 10 啟動文件使用的ARM匯編指令匯總

    指令名稱

    作用

    EQU

    給數字常量取一個符號名,相當于C語言中的define

    AREA

    匯編一個新的代碼段或者數據段

    SPACE

    分配內存空間

    PRESERVE8

    當前文件堆棧需按照8字節對齊

    EXPORT

    聲明一個標號具有全局屬性,可被外部的文件使用

    DCD

    以字為單位分配內存,要求4字節對齊,并要求初始化這些內存

    PROC

    定義子程序,與ENDP成對使用,表示子程序結束

    WEAK

    弱定義,如果外部文件聲明了一個標號,則優先使用外部文件定義的標號,如果外部文件沒有定義也不出錯。要注意的是:這個不是ARM的指令,是編譯器的,這里放在一起只是為了方便。

    IMPORT

    聲明標號來自外部文件,跟C語言中的EXTERN關鍵字類似

    B

    跳轉到一個標號

    ALIGN

    編譯器對指令或者數據的存放地址進行對齊,一般需要跟一個立即數,缺省表示4字節對齊。要注意的是:這個不是ARM的指令,是編譯器的,這里放在一起只是為了方便。

    END

    到達文件的末尾,文件結束

    IF,ELSE,ENDIF

    匯編條件分支語句,跟C語言的if else類似

    14.3 啟動文件代碼講解

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

    開辟棧的大小為0X000004001KB),名字為STACKNOINIT即不初始化,可讀可寫,82^3)字節對齊。

    棧的作用是用于局部變量,函數調用,函數形參等的開銷,棧的大小不能超過內部SRAM的大小。如果編寫的程序比較大,定義的局部變量很多,那么就需要修改棧的大小。如果某一天,你寫的程序出現了莫名奇怪的錯誤,并進入了硬fault的時候,這時你就要考慮下是不是棧不夠大,溢出了。

    EQU:宏定義的偽指令,相當于等于,類似與C中的define

    AREA:告訴匯編器匯編一個新的代碼段或者數據段。STACK表示段名,這個可以任意命名;NOINIT表示不初始化;READWRITE表示可讀可寫,ALIGN=3,表示按照2^3對齊,即8字節對齊。

    SPACE:用于分配一定大小的內存空間,單位為字節。這里指定大小等于Stack_Size

    標號__initial_sp緊挨著SPACE語句放置,表示棧的結束地址,即棧頂地址,棧是由高向低生長的。

    2.    Heap堆
     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
    				
    			

    開辟堆的大小為0X00000200512字節),名字為HEAPNOINIT即不初始化,可讀可寫,82^3)字節對齊。__heap_base表示對的起始地址,__heap_limit表示堆的結束地址。堆是由低向高生長的,跟棧的生長方向相反。

    堆主要用來動態內存的分配,像malloc()函數申請的內存就在堆上面。這個在STM32里面用的比較少。

     1 PRESERVE8
    				
     2 THUMB
    				
    			

    PRESERVE8指定當前文件的堆棧按照8字節對齊。

    THUMB表示后面指令兼容THUMB指令。THUBMARM以前的指令集,16bit,現在Cortex-M系列的都使用THUMB-2指令集,THUMB-232位的,兼容16位和32位的指令,是THUMB的超級。

    3.    向量表
     1 AREA    RESET, DATA, READONLY
    				
     2 EXPORT  __Vectors
    				
     3 EXPORT  __Vectors_End
    				
     4 EXPORT  __Vectors_Size
    				
    			

    定義一個數據段,名字為RESET,可讀。并聲明__Vectors__Vectors_End__Vectors_Size這三個標號具有全局屬性,可供外部的文件調用。

    EXPORT聲明一個標號可被外部的文件使用,使標號具有全局屬性。如果是IAR編譯器,則使用的是GLOBAL這個指令。

    當內核響應了一個發生的異常后,對應的異常服務例程(ESR)就會執行。為了決定ESR 的入口地址,內核使用了"向量表查表機制"。這里使用一張向量表。向量表其實是一個WORD32 位整數)數組,每個下標對應一種異常,該下標元素的值則是該ESR 的入口地址。向量表在地址空間中的位置是可以設置的,通過NVIC 中的一個重定位寄存器來指出向量表的地址。在復位后,該寄存器的值為0。因此,在地址0 (即FLASH 地址0)處必須包含一張向量表,用于初始時的異常分配。要注意的是這里有個另類:0 號類型并不是什么入口地址,而是給出了復位后MSP 的初值。

    表格 11 F429向量表

    編號

    優先級

    優先級類型

    名稱

    說明

    地址

     

    -

    -

    -

    保留(實際存的是MSP地址)

    0X0000 0000

     

    -3

    固定

    Reset

    復位

    0X0000 0004

     

    -2

    固定

    NMI

    不可屏蔽中斷。 RCC 時鐘安全系統(CSS) 連接到 NMI 向量

    0X0000 0008

     

    -1

    固定

    HardFault

    所有類型的錯誤

    0X0000 000C

     

    0

    可編程

    MemManage

    存儲器管理

    0X0000 0010

     

    1

    可編程

    BusFault

    預取指失敗,存儲器訪問失敗

    0X0000 0014

     

    2

    可編程

    UsageFault

    未定義的指令或非法狀態

    0X0000 0018

     

    -

    -

    -

    保留

    0X0000 001C-

    0X0000 002B

     

    3

    可編程

    SVCall

    通過 SWI 指令調用的系統服務

    0X0000 002C

     

    4

    可編程

    Debug Monitor

    調試監控器

    0X0000 0030

     

    -

    -

    -

    保留

    0X0000 0034

     

    5

    可編程

    PendSV

    可掛起的系統服務

    0X0000 0038

     

    6

    可編程

    SysTick

    系統嘀嗒定時器

    0X0000 003C

    0

    7

    可編程

    -

    窗口看門狗中斷

    0X0000 0040

    1

    8

    可編程

    PVD

    連接EXTI 線的可編程電壓檢測中斷

    0X0000 0044

    2

    9

    可編程

    TAMP_STAMP

    連接EXTI 線的入侵和時間戳中斷

    0X0000 0048

    中間部分省略,詳情請參考STM32F4xx 中文參考手冊》第十章-中斷和事件-向量表部分

    84

    91

    可編程

    SPI4

    SPI4全局中斷

    0X0000 0190

    85

    92

    可編程

    SPI5

    SPI5全局中斷

    0X0000 0194

    86

    93

    可編程

    SPI6

    SPI6全局中斷

    0X0000 0198

    87

    94

    可編程

    SAI1

    SAI1全局中斷

    0X0000 019C

    88

    95

    可編程

    LTDC

    LTDC全局中斷

    0X0000 01A0

    89

    96

    可編程

    LTDC_ER

    LTDC_ER全局中斷

    0X0000 01A4

    90

    97

    可編程

    DMA2D

    DMA2D全局中斷

    0X0000 01A8

    代碼 12 向量表

     1 __Vectors  DCD   __initial_sp        ;棧頂地址
    				
     2            DCD   Reset_Handler       ;復位程序地址
    			
     3            DCD   NMI_Handler
    				
     4            DCD   HardFault_Handler
    				
     5            DCD   MemManage_Handler
    				
     6            DCD   BusFault_Handler
    				
     7            DCD   UsageFault_Handler
    				
     8            DCD
    								0                    ; 0 表示保留
    			
     9            DCD
    								0
    				
    10            DCD
    								0
    				
    11            DCD
    								0
    				
    12            DCD   SVC_Handler
    				
    13            DCD   DebugMon_Handler
    				
    14            DCD
    								0
    				
    15            DCD   PendSV_Handler
    				
    16            DCD   SysTick_Handler
    				
    17 
    			
    18 
    			
    19 ;外部中斷開始
    					
    20            DCD   WWDG_IRQHandler
    				
    21            DCD   PVD_IRQHandler
    				
    22            DCD   TAMP_STAMP_IRQHandler
    				
    23 
    			
    24 ;限于篇幅,中間代碼省略
    25            DCD   LTDC_IRQHandler
    				
    26            DCD   LTDC_ER_IRQHandler
    				
    27            DCD   DMA2D_IRQHandler
    				
    28 __Vectors_End
    				
    			

    1 __Vectors_Size EQU __Vectors_End - __Vectors

    __Vectors為向量表起始地址,__Vectors_End 為向量表結束地址,兩個相減即可算出向量表大小。

    向量表從FLASH的0地址開始放置,以4個字節為一個單位,地址0存放的是棧頂地址,0X04存放的是復位程序的地址,以此類推。從代碼上看,向量表中存放的都是中斷服務函數的函數名,可我們知道C語言中的函數名就是一個地址。

    DCD:分配一個或者多個以字為單位的內存,以四字節對齊,并要求初始化這些內存。在向量表中,DCD分配了一堆內存,并且以ESR的入口地址初始化它們。

    4.    復位程序
     1 AREA    |.text|, CODE, READONLY
    				
    			
    定義一個名稱為.text的代碼段,可讀。
    
     1 Reset_Handler PROC
    				
     2               EXPORT  Reset_Handler    [WEAK]
    				
     3               IMPORT  SystemInit
    				
     4               IMPORT  __main
    				
     5 
    			
     6               LDR
    								R0, =SystemInit
    				
     7 
    							BLX
    									R0
    				
     8 
    							LDR
    									R0, =__main
    				
     9 
    							BX
    									R0
    				
    10 
    							ENDP
    				
    			

    復位子程序是系統上電后第一個執行的程序,調用SystemInit函數初始化系統時鐘,然后調用C庫函數_mian,最終調用main函數去到C的世界。

    WEAK:表示弱定義,如果外部文件優先定義了該標號則首先引用該標號,如果外部文件沒有聲明也不會出錯。這里表示復位子程序可以由用戶在其他文件重新實現,這里并不是唯一的。

    IMPORT:表示該標號來自外部文件,跟C語言中的EXTERN關鍵字類似。這里表示SystemInit__main這兩個函數均來自外部的文件。

    SystemInit()是一個標準的庫函數,在system_stm32f4xx.c這個庫文件總定義。主要作用是配置系統時鐘,這里調用這個函數之后,F429的系統時鐘配被配置為180M

    __main是一個標準的C庫函數,主要作用是初始化用戶堆棧,最終調用main函數去到C的世界。這就是為什么我們寫的程序都有一個main函數的原因。如果我們在這里不調用__main,那么程序最終就不會調用我們C文件里面的main,如果是調皮的用戶就可以修改主函數的名稱,然后在這里面IMPORT你寫的主函數名稱即可。

    1 Reset_Handler PROC
    				
     2               EXPORT  Reset_Handler    [WEAK]
    				
     3               IMPORT  SystemInit
    				
     4               IMPORT  user_main
    				
     5 
    			
     6               LDR
    								R0, =SystemInit
    				
     7 
    							BLX
    									R0
    				
     8 
    							LDR
    									R0, =user_main
    				
     9 
    						BX
    								R0
    
    10 
    						ENDP
    

    這個時候你在C文件里面寫的主函數名稱就不是main了,而是user_main了。

    LDRBLXBXCM4內核的指令,可在《CM3權威指南CnR2》第四章-指令集里面查詢到,具體作用見下表:

    指令名稱

    作用

    LDR

    從存儲器中加載字到一個寄存器中

    BL

    跳轉到由寄存器/標號給出的地址,并把跳轉前的下條指令地址保存到LR

    BLX

    跳轉到由寄存器給出的地址,并根據寄存器的LSE確定處理器的狀態,還要把跳轉前的下條指令地址保存到LR

    BX

    跳轉到由寄存器/標號給出的地址,不用返回

    5.    中斷服務程序

    在啟動文件里面已經幫我們寫好所有中斷的中斷服務函數,跟我們平時寫的中斷服務函數不一樣的就是這些函數都是空的,真正的中斷復服務程序需要我們在外部的C文件里面重新實現,這里只是提前占了一個位置而已。

    如果我們在使用某個外設的時候,開啟了某個中斷,但是又忘記編寫配套的中斷服務程序或者函數名寫錯,那當中斷來臨的時,程序就會跳轉到啟動文件預先寫好的空的中斷服務程序中,并且在這個空函數中無線循環,即程序就死在這里。

     1 NMI_Handler     PROC    ;系統異常
    			
     2                 EXPORT  NMI_Handler           [WEAK]
    				
     3                 B       .
    				
     4                 ENDP
    				
     5 
    			
     6 ;限于篇幅,中間代碼省略
    			
     7 SysTick_Handler PROC
    				
     8                 EXPORT  SysTick_Handler       [WEAK]
    				
     9                 B       .
    				
    10                 ENDP
    				
    11 
    			
    12 Default_Handler PROC    ;外部中斷
    			
    13                 EXPORT  WWDG_IRQHandler       [WEAK]
    				
    14                 EXPORT  PVD_IRQHandler        [WEAK]
    				
    15                 EXPORT  TAMP_STAMP_IRQHandler [WEAK]
    				
    16 
    			
    17 ;限于篇幅,中間代碼省略
    			
    18 LTDC_IRQHandler
    				
    19 LTDC_ER_IRQHandler
    				
    20 DMA2D_IRQHandler
    				
    21                 B       .
    				
    22                 ENDP
    				
    			

    B:跳轉到一個標號。這里跳轉到一個'.',即表示無線循環。

    6.    用戶堆棧初始化
     1 ALIGN
    				
    			

    ALIGN:對指令或者數據存放的地址進行對齊,后面會跟一個立即數。缺省表示4字節對齊。

    1 ;用戶棧和堆初始化

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

    判斷是否定義了__MICROLIB ,如果定義了則賦予標號__initial_sp(棧頂地址)、__heap_base(堆起始地址)、__heap_limit(堆結束地址)全局屬性,可供外部文件調用。如果沒有定義(實際的情況就是我們沒定義__MICROLIB)則使用默認的C庫,然后初始化用戶堆棧大小,這部分有C庫函數__main來完成,當初始化完堆棧之后,就調用main函數去到C的世界。

    IF,ELSE,ENDIF:匯編的條件分支語句,跟C語言的if ,else類似

    END:文件結束

    14.4 系統啟動流程

    下面這段話引用自《CM3權威指南CnR2》3.8—復位序列,CM4的復位序列跟CM3一樣。—秉火注。

    在離開復位狀態后, CM3 做的第一件事就是讀取下列兩個 32 位整數的值:

    1、從地址 0x0000,0000 處取出 MSP 的初始值。

    2、從地址 0x0000,0004 處取出 PC 的初始值——這個值是復位向量, LSB 必須是 1。 然后從這個值所對應的地址處取指。

    142 復位序列

    請注意,這與傳統的 ARM 架構不同——其實也和絕大多數的其它單片機不同。傳統的 ARM 架構總是從 0 地址開始執行第一條指令。它們的 0 地址處總是一條跳轉指令。 CM3 中,在 0 地址處提供 MSP 的初始值,然后緊跟著就是向量表。向量表中的數值是 32 位的地址,而不是跳轉指令。向量表的第一個條目指向復位后應執行的第一條指令,就是我們剛剛分析的Reset_Handler這個函數。

    143 初始化MSPPC的一個范例

    因為 CM3 使用的是向下生長的滿棧,所以 MSP 的初始值必須是堆棧內存的末地址加 1。舉例來說,如果我們的堆棧區域在 0x20007C00-0x20007FFF 之間,那么 MSP 的初始值就必須是 0x20008000

    向量表跟隨在 MSP 的初始值之后——也就是第 2 個表目。要注意因為 CM3 是在 Thumb 態下執行,所以向量表中的每個數值都必須把 LSB 1(也就是奇數)。正是因為這個原因,圖 143中使用0x101 來表達地址 0x100。當 0x100 處的指令得到執行后,就正式開始了程序的執行(即去到C的世界)。在此之前初始化 MSP 是必需的,因為可能第 1 條指令還沒來得及執行,就發生了 NMI 或是其它 fault MSP 初始化好后就已經為它們的服務例程準備好了堆棧。

    現在,程序就進入了我們熟悉的C世界,現在我們也應該明白main并不是系統執行的第一個程序了。

    14.5 每課一問

    1、啟動文件的主要作用是什么?

    2、FLASH地址0存放的是什么?

    3、熟悉啟動文件里面的ARM匯編指令

      本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
      轉藏 分享 獻花(0

      0條評論

      發表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 色爱综合另类图片av| 无码中文人妻视频2019| 亚洲精品无码久久久久SM| 国产一区二区三区在线观看免费| 免费午夜无码片在线观看影院| 无码人妻一区二区三区免费N鬼沢 午夜三级A三级三点在线观看 | 少妇人妻AV无码专区| 亚洲精品在线二区三区| 国产在线高清视频无码| 国产乱啊有帅gv小太正| 欧美国产日产一区二区| 99riav国产精品视频| 精品国偷自产在线视频99| 无码囯产精品一区二区免费| 国产乱理伦片在线观看夜| 老师扒下内裤让我爽了一夜| 精品国际久久久久999波多野| gogogo高清在线观看视频中文| 久久婷婷五月综合色国产免费观看| 亚洲avav天堂av在线网爱情| 无码人妻精品一区二区三区下载| 亚洲国产成人资源在线| 亚洲熟妇无码AV在线播放| 99久久国产成人免费网站| 51精品免费视频国产专区| 亚洲 欧洲 无码 在线观看| 在线播放深夜精品三级| 婷婷成人丁香五月综合激情 | 精品国产亚洲一区二区三区 | 好紧好湿好黄的视频| 办公室强奷漂亮少妇视频| 国产精品无码久久久久成人影院| 黄又色又污又爽又高潮| 国产高清不卡一区二区| 日韩AV无码精品一二三区| 亚洲欧美日韩在线码| 亚洲综合色婷婷六月丁香宅男大增 | 亚洲一区成人av在线| 曰韩精品无码一区二区三区视频| 性欧美老人牲交XXXXX视频| 午夜DY888国产精品影院|