Keil5中STM32程序起动过程分析


1.起动过程分析配置

keil5新建stm32工程创建stm32工程(芯片选择STM32F103C8)后,按keil5使用simulator调试配置完成调试配置。然后点击魔术棒图标,在Options for Target 'Target 1’对话框的Debug标签页,取消勾选Run to Main(),点击OK,关闭对话框。
取消RUN-TO-MAIN
重新编译后,进入调试,程序跳入起动文件。如此,可以调试从程序开始执行到跳入我们自己定义的main函数的起动过程。
程序跳入起动文件

2.起动过程第一步

编译后可执行程序储存在ROM中,可执行程序存储位置的初始地址可在Options for Target 'Target 1’对话框的Target标签页中设置,默认为0x08000000。程序执行时,首先从这个初始地址取出第一条数据(32位)存入sp(堆栈寄存器),接着取出第二条数据(32位)存入pc(指令计数器)。由于此时pc为可执行程序的第二条数据(32位),从而程序跳转到该第二条数据对应的地址。
设置rom初始地址
.map文件中记录了程序代码中各段经编译器编译后在生成的可执行程序中的存储位置,从而,可以查看.map文件读出可执行程序中各存储位置对应的内容。在主界面的左侧试图中切换到Project选项卡,双击Target 1节点,可在右侧文件视图中打开stm32_demo3.map的.map文件。
map文件
在map文件中找到编译后可执行程序的起始地址0x08000000,其存储内容为236字节的RESET段,该段属于startup_stm32f10x_md.o对象,我们查看对应的源文件startup_stm32f10x_md.s找到对应的RESET段:

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; 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             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1_2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

该段调用了59次DCD指令,每次分配一个字32位,即4个字节,一共236个字节,与.map文件一致,其第一条数据为 __initial_sp,是栈顶指针地址,其第二条数据为 Reset_Handler,是复位中断函数地址。因此,程序执行时,pc的第一个数据就是复位中断函数地址,从而程序执行的第一步就是跳入复位中断函数。

3. 执行复位中断函数

复位中断函数如下所示,其依次调用了外部函数SystemInit和__main,注意这里的__main函数不是我们写的那个main函数。

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

SystemInit在system_stm32f10x.c文件中,如下所示,在该函数中,进行时钟设置和中断向量表偏移设置。

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemCoreClock variable.
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

__main函数为编译器库函数,其地址可在.map文件查到为0x080000ec,总共8字节大小。

!!!main                                  0x080000ec   Section        8  __main.o(!!!main)

查看汇编窗口,对应跳转__main函数的编译后的代码如下,其中pc=0x0800018C条指令对应跳转指令。由于指令预取,读取PC时,会返回当前指令地址+4的值,所以读取的pc=0x08000190,所以[pc,#36]对应0x08000190+36=0x080001B4地址对应的内容。
汇编窗口
由于stm32为小端模式,0x080001B4-0x080001B6的4字节地址对应的内容为0x080000ED,其对其的地址为:0x080000EC,和.map文件一样,最后一位1标志指令类型。
地址
跳转到0x080000EC,首先为寄存器r10、r11赋值,r10中的地址与r11中的地址相差32字节,共8个字,其中4个字为一组,第一组四个字分别为Rom中的非零数据存储基址,Ram中的加载基址,数据长度和加载函数地址,第二组为Rom中初始为零的数据的存储基址,Ram中的加载基址,数据长度和加载函数地址。程序比较r10和r11中地址是否相等,不相等就从r10位置取出一组数据执行从Rom将数据加载到ram,同时r10中地址增加4个字,共16字节,相等就跳转__rt_entry函数。根据r10、r11数据可知,第一次比较r10和r11,两者不相等,执行非零数据从Rom加载到Ram程序,第二次比较r10和r11,两者不相等,执行初始为零的数据从Rom加载到Ram程序,这样就完成了将数据从Rom加载到Ram,然后r10就和r11相等了,跳转到__rt_entry函数。
_main函数执行
跳入__rt_entry函数后,调用__user_setup_stackheap,__user_setup_stackheap中调用了__user_initial_stackheap函数,该函数在startup_stm32f10x_md.s文件中定义。然后调用__rt_lib_init函数之后跳入__rt_entry_main函数,在该函数中调用我们自己写的main函数。
跳转rt_entry
个人水平有限,如有错误,请不吝指出!!!

  • 33
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Keil5是一款嵌入式开发工具,而STM32是一系列的32位ARM Cortex-M微控制器。在使用Keil5进行STM32开发时,需要进行以下步骤: 1. 安装Keil5:首先需要下载并安装Keil5开发工具。可以从Keil官网上下载安装程序,并按照提示进行安装。 2. 创建STM32工程:在Keil5,可以通过选择“Project”菜单的“New Project”选项来创建一个新的STM32工程。在创建工程时,需要选择适合的STM32系列和型号,并设置工程的存储路径。 3. 配置工程:在创建工程后,需要进行一些配置。其,一个重要的配置文件是configuration.h,用于配置库函数头文件的包含关系和断相关内容。可以根据具体需求进行配置。 4. 编写代码:在Keil5,可以使用C语言或汇编语言编写STM32的应用程序代码。可以在工程创建源文件,并在其编写代码。 5. 编译和调试:在编写完代码后,可以使用Keil5进行编译和调试。可以通过选择“Build”菜单的“Build”选项来编译工程,并通过选择“Debug”菜单的“Start/Stop Debug Session”选项来启动调试会话。 6. 烧录程序:在调试完成后,可以使用Keil5将编译好的程序烧录到STM32微控制器。可以通过选择“Flash”菜单的“Download”选项来进行烧录操作。 以上是使用Keil5进行STM32开发的基本步骤。具体的操作和配置可能会因具体的STM32系列和型号而有所差异。在实际开发,还可以使用Keil5提供的其他功能和工具来进行更加高级的开发和调试操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值