zoukankan      html  css  js  c++  java
  • STM32的例程GPIO的汇编指令初探

    任务一:寻找main函数的汇编指令集

    任务二:寻找main函数中的SystemClock_Config函数的汇编指令集

    寻找main函数的汇编指令集

    运行例程中GPIO工程时,总会加载startup_stm32f103xb.s文件.如此文件注释所说

    ;******************** (C) COPYRIGHT 2016 STMicroelectronics ********************
    ;* File Name          : startup_stm32f103xb.s
    ;* Author             : MCD Application Team
    ;* Version            : V1.4.0
    ;* Date               : 29-April-2016
    ;* Description        : STM32F103xB Devices vector table for MDK-ARM toolchain. 
    ;*                      This module performs:
    ;*                      - Set the initial SP
    ;*                      - Set the initial PC == Reset_Handler
    ;*                      - Set the vector table entries with the exceptions ISR address
    ;*                      - Configure the clock system
    ;*                      - Branches to __main in the C library (which eventually
    ;*                        calls main()).
    ;*                      After Reset the Cortex-M3 processor is in Thread mode,
    ;*                      priority is Privileged, and the Stack is set to Main.
    ;********************************************************************************

    此文件实现了 -Set the initial SP //设置初始sp指针

          - Set the initial PC == Reset_Handler //设置PC等于初始句柄

    以往总是认为程序一定是从main函数入口, 但是通过调试GPIO这个例程知道startup_stm32f103xb.s是比main函数还要早执行的文件. 文件是由汇编指令组成, 其中有几条语句看似简单, 其实是整个main的函数的生命开始.

    ; Reset handler
    Reset_Handler    PROC
                     EXPORT  Reset_Handler             [WEAK]
         IMPORT  __main
         IMPORT  SystemInit
                     LDR     R0, =SystemInit
                     BLX     R0
                     LDR     R0, =__main // 将main指令集的首地址传给R0
                     BX      R0       // pc指针指向R0存储的地址
    ENDP

     main函数的汇编指令全集, 如下

    ; main函数的起始
    ; 由startup_stm32f103xb.s文件跳转过来,
    ; 所以将LR(先前PC指针的值)和R3的值入栈
    0x08000CCC B508      PUSH     {r3,lr} 
    
    /* 
        HAL_Init();
    */
    ; 跳转到HAL_Init函数的首地址, 并且会把当前pc值保存在LR寄存器
    0x08000CCE F7FFFB4B  BL.W     HAL_Init (0x08000368)
    
    /*
        SystemClock_Config();
    */
    ; 跳转到SystemClock_Config函数的首地址
    ; 并且会把当前pc值保存在LR寄存器 
    0x08000CD2 F7FFFF97  BL.W     SystemClock_Config (0x08000C04)
       
    /*
        LED2_GPIO_CLK_ENABLE();
    */
    ; 本应该同上跳转到函数LED2_GPIO_CLK_ENABLE()首地址,  但是LED2_GPIO_CLK_ENABLE()实质是宏替换, 所以会变成main函数里面的实际的语句
    0x08000CD6 480F      LDR      r0,[pc,#60]  ; @0x08000D14
    0x08000CD8 6981      LDR      r1,[r0,#0x18]
    0x08000CDA F0410104  ORR      r1,r1,#0x04
    0x08000CDE 6181      STR      r1,[r0,#0x18]
    0x08000CE0 6980      LDR      r0,[r0,#0x18]
    
    /*
      GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
      GPIO_InitStruct.Pull  = GPIO_PULLUP;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    
      GPIO_InitStruct.Pin = LED2_PIN;
      HAL_GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStruct);
    */
    0x08000CE2 2101      MOVS     r1,#0x01
    0x08000CE4 F0000004  AND      r0,r0,#0x04
    0x08000CE8 9000      STR      r0,[sp,#0x00]
    0x08000CEA 480B      LDR      r0,[pc,#44]  ; @0x08000D18
    0x08000CEC 4C0B      LDR      r4,[pc,#44]  ; @0x08000D1C
    0x08000CEE 6041      STR      r1,[r0,#0x04]
    0x08000CF0 6081      STR      r1,[r0,#0x08]
    0x08000CF2 2103      MOVS     r1,#0x03
    0x08000CF4 60C1      STR      r1,[r0,#0x0C]
    0x08000CF6 2120      MOVS     r1,#0x20 
    0x08000CF8 6001      STR      r1,[r0,#0x00]
    0x08000CFA 4601      MOV      r1,r0
    0x08000CFC 4620      MOV      r0,r4
    0x08000CFE F7FFFA43  BL.W     HAL_GPIO_Init (0x08000188)
     
    
       
    /*
      while (1)
      {
        HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN);
        HAL_Delay(100);
      }
    */
    0x08000D02 2120      MOVS     r1,#0x20
    0x08000D04 4620      MOV      r0,r4
    0x08000D06 F7FFFB1D  BL.W     HAL_GPIO_TogglePin (0x08000344)
    0x08000D0A 2064      MOVS     r0,#0x64
    0x08000D0C F7FFFA30  BL.W     HAL_Delay (0x08000170)
      
    ; main函数的结尾
    ; 本应是B LR, 即跳回调用main函数前的PC指令的值, 这里编译器考虑到while(1)永远不会停止,于是优化了一下,所以没有B LR只一句
    0x08000D10 E7F7      B        0x08000D02

    对应C源文件如下

    int main(void)
    {
      /* This sample code shows how to use GPIO HAL API to toggle LED2 IO
        in an infinite loop. */
    
      /* STM32F103xB HAL library initialization:
           - Configure the Flash prefetch
           - Systick timer is configured by default as source of time base, but user 
             can eventually implement his proper time base source (a general purpose 
             timer for example or other time source), keeping in mind that Time base 
             duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and 
             handled in milliseconds basis.
           - Set NVIC Group Priority to 4
           - Low Level Initialization
         */
      HAL_Init();
    
      /* Configure the system clock to 64 MHz */
      SystemClock_Config();
      
      /* -1- Enable GPIO Clock (to be able to program the configuration registers) */
        /* ¼¤»îGPIO Clock */
      LED2_GPIO_CLK_ENABLE();
    
      /* -2- Configure IO in output push-pull mode to drive external LEDs */
      GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
      GPIO_InitStruct.Pull  = GPIO_PULLUP;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    
      GPIO_InitStruct.Pin = LED2_PIN;
      HAL_GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStruct);
    
      /* -3- Toggle IO in an infinite loop */
      while (1)
      {
        HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN);
        /* Insert delay 100 ms */
        HAL_Delay(100);
      }
        
    }

    寻找main函数中的SystemClock_Config函数的汇编指令集

    SystemClock_Config函数指令集全集如下

    SystemClock_Config()
    
    0x08000C04 B530      PUSH     {r4-r5,lr}                     //保存R4-R5,LR寄存器
    0x08000C06 B08F      SUB      sp,sp,#0x3C                    //SP的值偏移0x3C
    
    0x08000C08 2114      MOVS     r1,#0x14                       //将值0x14传送到R1
    0x08000C0A A80A      ADD      r0,sp,#0x28                    //SP偏移0x28后传送到R0
    0x08000C0C F7FFFA91  BL.W     __aeabi_memclr (0x08000132)    //跳转到地址0x08000132
    
    0x08000C10 2128      MOVS     r1,#0x28                       //将值0x28传送给R1
    0x08000C12 4668      MOV      r0,sp                          //将SP传送给R0
    0x08000C14 F7FFFA8D  BL.W     __aeabi_memclr (0x08000132)    //跳转到地址0x08000132
    
    0x08000C18 2502      MOVS     r5,#0x02                       //将值0x02传送给R5
                                                                 //选择振荡器类型为内部高速晶振
    
    0x08000C1A 2400      MOVS     r4,#0x00                       //将值0x02传送R4,关闭外部高速/低速晶振
    0x08000C1C E9CD5400  STRD     r5,r4,[sp,#0]                  //
    
    0x08000C20 2001      MOVS     r0,#0x01                       //将值0x01传送给R0,打开内部高速晶振
    0x08000C22 E9CD4003  STRD     r4,r0,[sp,#0x0C]               //
    
    0x08000C26 2010      MOVS     r0,#0x10                       //将值0x10传送给R0
                                                                 //将内部高速晶振设为默认值,外部高速晶振预分频,打开锁相环
                                                                 //设置PLL输入时钟源
    
    0x08000C28 9005      STR      r0,[sp,#0x14]                  //将SP偏移0x14后存储到R
    0x08000C2A F44F1060  MOV      r0,#0x380000                   //将值0x380000传送到R0
    0x08000C2E E9CD4008  STRD     r4,r0,[sp,#0x20]               //
    0x08000C32 9507      STR      r5,[sp,#0x1C]                  //将SP偏移0x1C后存储到R5
    0x08000C34 9402      STR      r4,[sp,#0x08]                  //将SP偏移0x08后存储到R4
                                                                 //设置PLL的倍频系数为16倍
    
    0x08000C36 4668      MOV      r0,sp                          //将SP传送给R0
    0x08000C38 F7FFFCFA  BL.W     HAL_RCC_OscConfig (0x08000630) //跳转到振荡器配置地址0x08000630
    0x08000C3C B100      CBZ      r0,0x08000C40                  //
                                                                 //检验振荡器初始化是否完成
    
    0x08000C3E E7FE      B        0x08000C3E                     //循环
    
    0x08000C40 200F      MOVS     r0,#0x0F                       //将0x0F传送给R0
    0x08000C42 900A      STR      r0,[sp,#0x28]                  //将SP偏移0x28后存储到R0
    0x08000C44 E9CD540B  STRD     r5,r4,[sp,#0x2C]               //
                                                                 //选择PLL作为系统时钟源,并配置时钟
    
    0x08000C48 F44F6080  MOV      r0,#0x400                      //将值0x400传送给R0
                                                                 //设置APB1外设时钟=HCLK/2
    
    0x08000C4C E9CD040D  STRD     r0,r4,[sp,#0x34]               //
    0x08000C50 2102      MOVS     r1,#0x02                       //将值0x02传送给R1
    0x08000C52 A80A      ADD      r0,sp,#0x28                    //SP加0x28得值放入R0
    0x08000C54 F7FFFBDE  BL.W     HAL_RCC_ClockConfig (0x08000414) //跳转到时钟配置地址0x08000414
    0x08000C58 2800      CMP      r0,#0x00                       //将R0和0x00进行比较,进行一次减法但不保存结果
    0x08000C5A D000      BEQ      0x08000C5E                     //根据比较结果跳转到地址0x08000C5E
                                                                 //验证时钟配置是否完成
    
    0x08000C5C E7FE      B        0x08000C5C                     //循环
    
    0x08000C5E B00F      ADD      sp,sp,#0x3C                    //SP加0x3C的值保存在SP中
    0x08000C60 BD30      POP      {r4-r5,pc}                     //恢复R4-R5,PC寄存器
    0x08000C62 0000      MOVS     r0,r0                          //nop指令
    
                                                               

     对应C源文件如下

    void SystemClock_Config(void)
    {
      RCC_ClkInitTypeDef clkinitstruct = {0};
      RCC_OscInitTypeDef oscinitstruct = {0};
      
      /* Configure PLL ------------------------------------------------------*/
      /* PLL configuration: PLLCLK = (HSI / 2) * PLLMUL = (8 / 2) * 16 = 64 MHz */
      /* PREDIV1 configuration: PREDIV1CLK = PLLCLK / HSEPredivValue = 64 / 1 = 64 MHz */
      /* Enable HSI and activate PLL with HSi_DIV2 as source */
      oscinitstruct.OscillatorType  = RCC_OSCILLATORTYPE_HSI;
      oscinitstruct.HSEState        = RCC_HSE_OFF;
      oscinitstruct.LSEState        = RCC_LSE_OFF;
      oscinitstruct.HSIState        = RCC_HSI_ON;
      oscinitstruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
      oscinitstruct.HSEPredivValue    = RCC_HSE_PREDIV_DIV1;
      oscinitstruct.PLL.PLLState    = RCC_PLL_ON;
      oscinitstruct.PLL.PLLSource   = RCC_PLLSOURCE_HSI_DIV2;
      oscinitstruct.PLL.PLLMUL      = RCC_PLL_MUL16;
      if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
      {
        /* Initialization Error */
        while(1); 
      }
    
      /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 
         clocks dividers */
      clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
      clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
      clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
      clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
      clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;  
      if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
      {
        /* Initialization Error */
        while(1); 
      }
    }

                                             by: arri.ouyang@cssiot.com.cnsundy.li@cssiot.com.cn  

                                                              

  • 相关阅读:
    spring AOP的实现原理
    spring IOC的实现原理
    springboot开发环境搭建
    JEECG入门
    maven项目搭建步骤
    Centos 7上搭建sftp服务(适用Centos6)
    订制rpm包到Centos7镜像中
    Centos7上搭建redis主从
    windows server 几大实时同步软件比较
    windows server 2008 R2 Enterprise 间实时同步之FreeFileSync 部署过程
  • 原文地址:https://www.cnblogs.com/sundy-lee/p/6066185.html
Copyright © 2011-2022 走看看