任務一:尋找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.cn & sundy.li@cssiot.com.cn