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,#] ; @0x08000D14
0x08000CD8 LDR r1,[r0,#0x18]
0x08000CDA F0410104 ORR r1,r1,#0x04
0x08000CDE STR r1,[r0,#0x18]
0x08000CE0 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 MOVS r1,#0x01
0x08000CE4 F0000004 AND r0,r0,#0x04
0x08000CE8 STR r0,[sp,#0x00]
0x08000CEA 480B LDR r0,[pc,#] ; @0x08000D18
0x08000CEC 4C0B LDR r4,[pc,#] ; @0x08000D1C
0x08000CEE STR r1,[r0,#0x04]
0x08000CF0 STR r1,[r0,#0x08]
0x08000CF2 MOVS r1,#0x03
0x08000CF4 60C1 STR r1,[r0,#0x0C]
0x08000CF6 MOVS r1,#0x20
0x08000CF8 STR r1,[r0,#0x00]
0x08000CFA MOV r1,r0
0x08000CFC MOV r0,r4
0x08000CFE F7FFFA43 BL.W HAL_GPIO_Init (0x08000188) /*
while ()
{
HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN);
HAL_Delay();
}
*/
0x08000D02 MOVS r1,#0x20
0x08000D04 MOV r0,r4
0x08000D06 F7FFFB1D BL.W HAL_GPIO_TogglePin (0x08000344)
0x08000D0A 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 ()
{
HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN);
/* Insert delay 100 ms */
HAL_Delay();
} }
寻找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 MOVS r1,#0x14 //将值0x14传送到R1
0x08000C0A A80A ADD r0,sp,#0x28 //SP偏移0x28后传送到R0
0x08000C0C F7FFFA91 BL.W __aeabi_memclr (0x08000132) //跳转到地址0x08000132 0x08000C10 MOVS r1,#0x28 //将值0x28传送给R1
0x08000C12 MOV r0,sp //将SP传送给R0
0x08000C14 F7FFFA8D BL.W __aeabi_memclr (0x08000132) //跳转到地址0x08000132 0x08000C18 MOVS r5,#0x02 //将值0x02传送给R5
//选择振荡器类型为内部高速晶振 0x08000C1A MOVS r4,#0x00 //将值0x02传送R4,关闭外部高速/低速晶振
0x08000C1C E9CD5400 STRD r5,r4,[sp,#] // 0x08000C20 MOVS r0,#0x01 //将值0x01传送给R0,打开内部高速晶振
0x08000C22 E9CD4003 STRD r4,r0,[sp,#0x0C] // 0x08000C26 MOVS r0,#0x10 //将值0x10传送给R0
//将内部高速晶振设为默认值,外部高速晶振预分频,打开锁相环
//设置PLL输入时钟源 0x08000C28 STR r0,[sp,#0x14] //将SP偏移0x14后存储到R
0x08000C2A F44F1060 MOV r0,#0x380000 //将值0x380000传送到R0
0x08000C2E E9CD4008 STRD r4,r0,[sp,#0x20] //
0x08000C32 STR r5,[sp,#0x1C] //将SP偏移0x1C后存储到R5
0x08000C34 STR r4,[sp,#0x08] //将SP偏移0x08后存储到R4
//设置PLL的倍频系数为16倍 0x08000C36 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/ 0x08000C4C E9CD040D STRD r0,r4,[sp,#0x34] //
0x08000C50 MOVS r1,#0x02 //将值0x02传送给R1
0x08000C52 A80A ADD r0,sp,#0x28 //SP加0x28得值放入R0
0x08000C54 F7FFFBDE BL.W HAL_RCC_ClockConfig (0x08000414) //跳转到时钟配置地址0x08000414
0x08000C58 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 MOVS r0,r0 //nop指令
对应C源文件如下
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef clkinitstruct = {};
RCC_OscInitTypeDef oscinitstruct = {}; /* 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();
} /* 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();
}
}
by: arri.ouyang@cssiot.com.cn & sundy.li@cssiot.com.cn
STM32的例程GPIO的汇编指令初探的更多相关文章
- ARM汇编指令的一些总结-转
ARM汇编指令的一些总结ARM汇编指令很多,但是真正常用的不是很多,而且需要认真琢磨的又更少了.比较有用的是MOV B BL LDR STR还是通过具体汇编代码来学习吧.@ disable watch ...
- ARM汇编指令调试方法
学习ARM汇编时,少不了对ARM汇编指令的调试.作为支持多语言的调试器,gdb自然是较好的选择.调试器工作时,一般通过修改代码段的内容构造trap软中断指令,实现程序的暂停和程序执行状态的监控.为了在 ...
- 学习linux内核时常碰到的汇编指令(1)
转载:http://blog.sina.com.cn/s/blog_4be6adec01007xvg.html 80X86 汇编指令符号大全 +.-.*./∶算术运算符. &∶宏处理操作符. ...
- Windows内核 基本汇编指令
1)用VS2010新建Win32 Console Application,工程名为ACECore,工程建立完成后得到打开文件ACECore.cpp,代码如下: #include "stdaf ...
- 常用ARM汇编指令
常用ARM汇编指令 [日期:2012-07-14] 来源:Linux社区 作者:xuyuanfan77 [字体:大 中 小] 在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时初 ...
- Objdump-查看汇编指令
作用 Objdump可以用来看汇编指令 查看汇编指令 测试文件 编译指令 gcc -g -o objtest 1.8.c objdump -S objtest |more /main 查看结果
- ARM汇编指令(未完待续)
ARM指令自己在看的时候,看完之后就忘了,根本记不住,而且有些ARM汇编指令在平常的时候可能根本就用不到,所以也没必要把所有的ARM指令都去记忆,所以自己就想着不去一遍一遍的复习ARM指令,而是在平常 ...
- 【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137 转载请著名出处 本博客相关文档下载 : -- AR ...
- STM32标准库GPIO操作
STM32标准库GPIO操作 STM32任何外围设备的使用都分为两部分:初始化和使用.体现在代码上就是:(1)有一个初始化函数(2)main函数中的使用 1.初始化GPIO 初始化GPIO函数代码: ...
随机推荐
- hdu 1587
Problem Description As you know, Gardon trid hard for his love-letter, and now he's spending too muc ...
- JavaScript当中的eval函数
eval函数 eval函数接收一个由JavaScript语句组成的字符串,并且返回字符串中最后一条语句的返回值,如果最后一条语句没有返回值,那么eval函数返回undefined.如果传递给eval函 ...
- WordPress主题制作全过程
前言: 我想大多数使用WordPress的朋友都喜欢去尝试新的主题,但是换来换去,总是找不到那么一款适合自己的,让人很郁闷.于是很多人萌生了修改现有主题或自己动手从头制作一个主题的想法,但是问题又来了 ...
- python随机产生4个互不相等的随机数
从0-9中随机产生4个互不相等的数, 方法一: import random s=[] while(len(s)<4): x=random.randint(0,9) if x not in s: ...
- Win7/Win8 系统下安装Oracle 10g 提示“程序异常终止,发生未知错误”的解决方法
我的Oracle 10g版本是10.2.0.1.0,(10.1同理)选择高级安装,提示“程序异常终止,发生未知错误”. 1.修改Oracle 10G\database\stage\prereq\db\ ...
- JS+html实现简单的飞机大战
摘要:通过原生的js+html实现简单的飞机大战小游戏效果,如图所示: 实现代码如下: 1.自己的飞机实现 飞机html: <!DOCTYPE html> <html lang=&q ...
- Js函数加括号、不加括号(转)
函数只要是要调用它进行执行的,都必须加括号.此时,函数()实际上等于函数的返回值.当然,有些没有返回值,但已经执行了函数体内的行为,这个是根本,就是说,只要加括号的,就代表将会执行函数体代码. 不加括 ...
- Blogger建立Blog部落格 - Blog透视镜
Google谷歌提供Blogger免费建立Blog部落格服务,高自由度的设计,模版全面开放,允许你加入HTML/Script,最重要的是可以自定义网域,辛苦经营的Blog部落格,不希望有朝一日,倘若搬 ...
- Asp.net MVC Web.config配置技巧
一.视图引入命名空间的设置 之前经常写这样的代码,而且每个页面都要写: @model IEnumerable<MvcStart.Models.People_Model> 其实有一种很方便的 ...
- 关于DLL模块导出函数
当然以前我知道有一个.def文件的,里面写的都是需要导出的函数,以为与__declspec(dllexport)作用是一样的.但是今天看公司项目源码的时候才知道,它们两个导出方法是有一定的区别的,编译 ...