STM32
直播中

张波

7年用户 1408经验值
私信 关注
[问答]

startup.s启动文件的启动代码最重要的工作是什么

startup.s启动文件的启动代码最重要的工作是什么?

回帖(1)

龚羿峰

2021-11-29 15:12:01
       从大学时期,使用的第一块ARM单片机——飞思卡尔K60起,到后面参加工作 STM32,I.MX RT系列,Infineon 的 TLE984等等。从当初啥外设都不知,只会在main函数里写个 printf,到现在对ARM架构略知一二。接下来笔者来重点说明下,我们常常见到的startup.s文件。也就是常说的启动文件
         若连一个单片机是怎么启动都不知道,只知道在while(1)里写代码,这就常说的只会敲代码的码农,还不算一个完整的嵌入式工程师吧。这里笔者就拿最近在用的 i.MX RT1052来讲,cortex M7 一款性价比贼高的跨界处理器。下面的描述都是笔者通过相关书籍或网络整理后的笔记内容:
  ;---------------------------------------------------------------------------------------------------------------
; 第一部分:
;
;  1.启动代码最重要的工作是把异常&中断向量表放到正确的Flash地址上(也有可能是RAM上)
;  2.把向量表定义为只读数据段,并导出向量表符号(Symbol),让链接器识别此符号并根据分散加载文件正确的放置向量表(链接部分就不讲了)
;  3.__Vector***标号以及|Image$$ARM_LIB_STACK$$ZI$$Limit|标号需要与分散加载文件合起来看,才会明白其真正的功能
;
;---------------------------------------------------------------------------------------------------------------


  
                PRESERVE8                                                           ; 8 字节对齐
                THUMB                                                                    ; THUMB指令集
  
; Vector Table Mapped to Address 0 at Reset
                  AREA    RESET, DATA, READONLY                               ; 声明权限为"READONLY" 名称为"RESET"的数据段
                EXPORT  __Vectors                                                        ; 导出"__Vectors"标号,理解为向量表收地址,分散加载描述文件中会用到
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size
                IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|         ; 导入"|Image$$ARM_LIB_STACK$$ZI$$Limit|"
  ;---------------------------------------------------------------------------------------------------------------
; 第二部分:
;
; |Image$$ARM_LIB_STACK$$ZI$$Limit| :
; 1.栈顶指针地址,此语法跟MDK编译器的底层相关,是ARMCC编译器才能识别的语法,GCC与IAR的底层编译器ICCARM编译器
;   不能识别  
; 2.|Image$$ARM_LIB_STACK$$ZI$$Limit| 是一个链接器 Image Symbol
; 3.此处|Image$$ARM_LIB_STACK$$ZI$$Limit|相当于栈顶地址,或者理解为直接将栈顶地址写到此处也行
;
; Reset_Handler :
; 1.Reset_Handler函数地址,此处相当于把Reset_Handler函数地址赋值给PC,即调用Reset_Handler函数
;
; 重要关键节点:
; 1.绝大多数Cortex-M微控制器(M0 M3 M4 M7都是这样)复位后先进入厂商BOOTROM,此时所有用户行为均无法介入处理器
; 2.厂商BOOTROM(有些厂商会有其他称呼)主要负责处理一些芯片最基本的初始化、加密(RT1052就是这样)以及一些对MCU
;   的差异化设置等工作
; 3.BOOTROM顺利完成后,MCU控制权会交给用户,即启动代码
; 4.启动代码(运行汇编语言则不需要此启动代码),最重要的工作在于设置MSP(主堆栈指针)以及PC(程序计数器)的值
; 5.Cortex-M微控制器会默认把0x00000000地址里面的值设置为MSP的值,0x00000004地址里面的值设置为PC的值
;
; 关于External(异常)与 Interrupts(中断)的区别说明
; 1.External(异常)与 Interrupts(中断)是不同的,是两个不同的概念
; 2.External(异常)是向量表的前16个向量,其优先级为负数,高于所有中断,而且不可调整优先级也不可以关闭,可以
;   打断正常程序与Interrupts(中断)的运行
; 3.从第16个向量以后才是Interrupts(中断),可以设置优先级,不用时可以关闭,但优先级一般低于External(异常)
;
;
;---------------------------------------------------------------------------------------------------------------


  
__Vectors       DCD     |Image$$ARM_LIB_STACK$$ZI$$Limit|       ; 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                    ;操作系统会用到的异常向量
                DCD     SysTick_Handler                     ;操作系统会用到的滴答定时器异常向量(没有操作系统时可以用作普通定时器中断,这里强烈建议这么用)
                                                              ;External Interrupts
;---------------------------------------------------------------------------------------------------------------
; 第三部分:
;
;  1.这里开始是中断向量表
;  2.各个向量的顺序是芯片设计的时候就定义好的,不能更改
;
;---------------------------------------------------------------------------------------------------------------  


                                                                                                                                                                       
                DCD     DMA0_DMA16_IRQHandler               ;DMA channel 0/16 transfer complete
                DCD     DMA1_DMA17_IRQHandler               ;DMA channel 1/17 transfer complete
                DCD     DMA2_DMA18_IRQHandler               ;DMA channel 2/18 transfer complete
                DCD     DMA3_DMA19_IRQHandler               ;DMA channel 3/19 transfer complete
                DCD     DMA4_DMA20_IRQHandler               ;DMA channel 4/20 transfer complete
                DCD     DMA5_DMA21_IRQHandler               ;DMA channel 5/21 transfer complete
                ......
                DCD     DefaultISR                          ;251
                DCD     DefaultISR                          ;252
                DCD     DefaultISR                          ;253
                DCD     DefaultISR                          ;254
                DCD     0xFFFFFFFF                          ; Reserved for user TRIM value
__Vectors_End
  __Vectors_Size  EQU     __Vectors_End - __Vectors
                  
  ;---------------------------------------------------------------------------------------------------------------
; 第四部分:
;
;  这部分开始可以称作Reset Handler实体,芯片上电后,经过BOOTROM后进入的用户可控最开始处的地方
;  1.如果想让MCU正常使用C语言,务必在此处调用__main函数
;  2.__main()不是main()两者有着本质的区别
;  3.__main()是C Library中的函数,keil开发环境中自带的C Library
;  4.main()是被__main()调用的,__main()工作完成最后一步就是调用main()
;  5.__main()被调用之前,可以根据需要插入一个或多个其他功能函数
;
;---------------------------------------------------------------------------------------------------------------                                                            
   


                  AREA    RES_HANDLER, CODE, READONLY
; Reset Handler
  Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  SystemInit
                IMPORT  __main
                  CPSID   I               ; Mask interrupts
                LDR     R0, =0xE000ED08 ;
                LDR     R1, =__Vectors
                STR     R1, [R0]
                LDR     R2, [R1]
                MSR     MSP, R2
               
;Config OCRAM,DTCM,ITCM
                LDR     R0,=0x400AC038
                LDR     R1,=0x00AA0000
                STR     R1,[R0]
                LDR     R0,=0x400AC040
                LDR     R1,=0x00200007
                STR     R1,[R0]
                LDR     R0,=0x400AC044
                LDR     R1,=0xFFFFAAA9        ;OCRAM = 32KB, DTCM = 224KB ,ITCM = 256KB 这里定义RAM分配,不是每个MCU都是这样的
               
                STR     R1,[R0]               
                           
                LDR     R0, =SystemInit
                BLX     R0
                CPSIE   i               ; Unmask interrupts
                LDR     R0, =__main                                     ;__main 不是我们所理解的 main函数
                BX      R0
                ENDP
                  AREA  OTHER_HANDLER, CODE, READONLY
   
  ;---------------------------------------------------------------------------------------------------------------
; 第五部分:
;
;  1.[WEAK]指定了这个函数为“若函数”  (具体怎么用,问问度娘就知道了)
;  2.这些中断服务函数定义成弱函数的意义是,当中断出现时,需要有一个中断服务函数予以响应,但真实的用户程序往往只
;    会使用一部分中断,甚至不使用中断,所以以下这些函数给出了异常/中断服务函数的默认实现(死循环,汇编中的'B.'
;    语句,相当于while(1)),因为不知道用户是否会用到多少中断,但这些服务函数又很重要,所以就把这些函数都实现并
;    声明为若函数
;  3.在C语言中声明弱函数是在函数后加"__attribute((weak))"  
;  
;---------------------------------------------------------------------------------------------------------------   


  
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler
                PROC
                EXPORT  NMI_Handler         [WEAK]
                B       .                  ;这里实现死循环 类似 while(1)
                ENDP
HardFault_Handler
                PROC
                EXPORT  HardFault_Handler         [WEAK]
                B       .
                ENDP
                  ......
  ENET_1588_Timer_IRQHandler
                PROC
                EXPORT  ENET_1588_Timer_IRQHandler         [WEAK]
                LDR     R0, =ENET_1588_Timer_DriverIRQHandler
                BX      R0
                ENDP
  Default_Handler
                PROC
                EXPORT  DMA0_DMA16_DriverIRQHandler         [WEAK]
                EXPORT  DMA1_DMA17_DriverIRQHandler         [WEAK]
                EXPORT  DMA2_DMA18_DriverIRQHandler         [WEAK]
                   ......
  PWM4_1_IRQHandler
PWM4_2_IRQHandler
PWM4_3_IRQHandler
PWM4_FAULT_IRQHandler
Reserved168_IRQHandler
Reserved169_IRQHandler
Reserved170_IRQHandler
Reserved171_IRQHandler
Reserved172_IRQHandler
Reserved173_IRQHandler
SJC_ARM_DEBUG_IRQHandler
NMI_WAKEUP_IRQHandler
DefaultISR
                LDR    R0, =DefaultISR
                BX     R0
                ENDP
                  ALIGN
  
                END
   
举报

更多回帖

发帖
×
20
完善资料,
赚取积分