zl程序教程

您现在的位置是:首页 >  其他

当前栏目

STM32 CM3/CM4 ------ startup.s 启动文件分析 ------ GCC RT-Thread Studio 版本

文件Studio 分析 版本 启动 STM32 thread gcc
2023-09-11 14:19:53 时间

startup.s 功能

* This module performs:
* - Set the initial SP        // 初始化 SP 寄存器,进入C程序需要先设置栈地址,因为是通过函数调用进入C程序,需要用到栈空间
* - Set the initial PC == Reset_Handler,      // 设置PC初始值应该是链接脚本的 “ ENTRY(Reset_Handler) ”,即代码从Reset_Handler处开始运行
* - Set the vector table entries with the exceptions ISR address    // 定义了一个中断向量表(即各个中断的中断服务函数指针),进入C程序前把中断向量表的起始地址赋值到某个寄存器,以便中断产生时能找到对应的服务函数
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M4 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.

 

函数 Reset_Handler 内调用函数 entry(),entry() 函数添加一些我们要在 main() 函数之前执行的代码,entry() 函数的最后调用 main()。

 

向量表

在 startup.s 内,定义了一个向量表,由链接脚本决定向量表存储在哪里,对于STM32,就是 flash 的起始地址 0x08000000,向量指向的位置就是对应的中断服务函数

MEMORY
{
    ROM (rx) : ORIGIN = 0x08000000, LENGTH =  1024k /* 1024K flash */
    RAM (rw) : ORIGIN = 0x20000000, LENGTH =  128k /* 128K sram */
}

SECTIONS
{
    .text :
    {
        . = ALIGN(4);
        _stext = .;
        KEEP(*(.isr_vector))            /* Startup code */

        . = ALIGN(4);
        *(.text)                        /* remaining code */
        *(.text.*)                      /* remaining code */

        _etext = .;
    } > ROM = 0

......
}

由上可知,变量 _stext 为空,不占用地址,所以首地址存的是中断向量表 isr_vector 

 

向量表的内容按地址增长顺序如下:
  主堆栈指针(MSP)的初始值
  复位中断函数
  NMI中断函数
  硬 fault 服务函数
  ......
后两者(NMI 和硬 fault )也是必需的,因为有可能在引导过程中发生这两种异常。

链接脚本把向量表放在内存首地址处,所以 0x20000D84 就是主堆栈指针的初始值,0x08059545-1就是复位向量,指向中断服务函数 Reset_Handler 

 可以看 rtthread.map 得到验证

 

 

startup.s 文件内容

 以下是 GCC RT-Thread 版本的 startup.s

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

/* Copy the .data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
    /* bl __libc_init_array */
/* Call the application's entry point.*/
  bl  entry
  bx  lr    
.size  Reset_Handler, .-Reset_Handler

/**
 * @brief  This is the code that gets called when the processor receives an 
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None     
 * @retval None       
*/
    .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
* 
*******************************************************************************/
   .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors
    
    
g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler
  
  /* External Interrupts */
  .word     WWDG_IRQHandler                   /* Window WatchDog              */                                        
  .word     PVD_IRQHandler                    /* PVD through EXTI Line detection */                        
  .word     TAMP_STAMP_IRQHandler             /* Tamper and TimeStamps through the EXTI line */            
  .word     RTC_WKUP_IRQHandler               /* RTC Wakeup through the EXTI line */                      
  .word     FLASH_IRQHandler                  /* FLASH                        */                                          
  .word     RCC_IRQHandler                    /* RCC                          */                                            
  .word     EXTI0_IRQHandler                  /* EXTI Line0                   */                        
  .word     EXTI1_IRQHandler                  /* EXTI Line1                   */                          
  .word     EXTI2_IRQHandler                  /* EXTI Line2                   */                          
  .word     EXTI3_IRQHandler                  /* EXTI Line3                   */                          
......
                         
                         
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler. 
* As they are weak aliases, any function with the same name will override 
* this definition.
* 
*******************************************************************************/
   .weak      NMI_Handler
   .thumb_set NMI_Handler,Default_Handler
  
   .weak      HardFault_Handler
   .thumb_set HardFault_Handler,Default_Handler
  
   .weak      MemManage_Handler
   .thumb_set MemManage_Handler,Default_Handler
  
   .weak      BusFault_Handler
   .thumb_set BusFault_Handler,Default_Handler

   .weak      UsageFault_Handler
   .thumb_set UsageFault_Handler,Default_Handler

......

 

下面两张图来自链接脚本,由 ENTRY(Reset_Handler) 可知程序从 Reset_Handler 开始运行,第一条语句就是对 SP 进行初始化,初始化值是 _estack

 

 

 

不同于 IAR 版本,这个版本除了中断服务函数 Reset_Handler 外,其他中断服务函数都是调用函数 Default_Handler,Default_Handler是一个死循环。功能上和 IAR 版本一样都是死循环。