完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
代码见CSDN链接为: 编译环境为Keil5, 烧写的bin文件,烧写的区域为STM32F103ZET6内部的ROM 内部的ROM地址为0x08000000 LED.bin Code 代表执行的代码,程序中所有的函数都位于此处。 RO-data 代表只读数据,程序中所定义的全局常量数据和字符串都位于此处 RW-data 代表已初始化的读写数据,程序中定义并且初始化的全局变量和静态变量位于此处。 ZI-data 代表未初始化的读写数据,程序中定义了但没有初始化的全局变量和静态变量位于此处。 ZI英语是zero initial,就是程序中用到的变量并且被系统初始化为0的变量的字节数, keil编译器默认是把你没有初始化的变量都赋值一个0,这些变量在程序运行时是保存在RAM中的。 程序的大小为0x288字节,648字节 以上可以了解到,LED.bin文件是下载到STM32ZET6的0x08000000~0x08000280处, map文件使用微库的映射 内存映射的图像 这里使用了Mircro LIB CPU从Reset_Handler=0x0800017f处执行,MSP=0x2000400. 启动文件分析 startup_stm32f10x_hd.s 1、Stack-栈 Stack_Size EQU 0x00000800 ;EQU 宏定义的未定义,相当于C中的define AREA STACK, NOINIT, READWRITE, ALIGN=3 ;AREA 告诉汇编器编译一个新的代码段或者数据段,STACK 表明段名,这个可以任意命名; ;NOINIT 表示不初始化,READWRITE 表示可读可写,ALIGN=3,表示8字节对齐 Stack_Mem SPACE Stack_Size ;SPACE 用于分配一定的内存空间,单位字节,指定大小为Stack_Size __initial_sp ;__initial_sp 紧挨着SPACE语句放置,表示栈的结束地址,栈顶地址,栈由高向低生长, __initial_sp=0x20000800 __initial_spTop EQU 0x20000400 ; stack used for SystemInit_ExtMemCtl ; always internal RAM used ;在0x20000400处的位置为__initial_spTop ;此段为名词为STACK段,大小为0x800 ; Heap Configuration ; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; 2、Heap-堆 Heap_Size EQU 0x00000000 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ;__heap_base 表示堆的起始地址,, Heap_Mem SPACE Heap_Size __heap_limit ;__heap_limit 表示堆的结束地址, ;堆主要用来动态内存分配,像malloc()函数申请的内存就在堆上, ;以上的代码段名词为HEAP,大小为0 PRESERVE8 THUMB ;PERSERVE8 指定当前文件的堆栈按照8字节对齐 ; THUMB表示后面指令兼容THUMB指令, 3、 向量表 ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size ;定义一个数据区(实则为startup_stm32f10x_hd.o 数据区名称为RESET(在内存映射的图像)), ;RESET可读,声明__Vectors,__Vectors_End和__Vectors_Size三个标号,三个标号具有全局属性,可以供外部文件调用, EXPORT:声明一个标号可被外部文件使用,标号具有全局属性,如果是IAR编译器,则使用GLOBAL 当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址, 内核使用了―向量表查表机制‖。这里使用一张向量表。向量表其实是一个WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0) 处必须包含一张向量表,用于初始时的异常分配。要注意的是这里有个另类: 0 号类型并不是什么入口地址,而是给出了复位后 MSP 的初值。 F103向量表 __Vectors DCD __initial_spTop ; Top of Stack ;栈顶地址0x200004000 ;__Vectors 向量的其实地址,在STM32ZET6,__Vectors=0x08000000 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 & ADC2 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 DCD TIM8_BRK_IRQHandler ; TIM8 Break DCD TIM8_UP_IRQHandler ; TIM8 Update DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare DCD ADC3_IRQHandler ; ADC3 DCD FSMC_IRQHandler ; FSMC DCD SDIO_IRQHandler ; SDIO DCD TIM5_IRQHandler ; TIM5 DCD SPI3_IRQHandler ; SPI3 DCD UART4_IRQHandler ; UART4 DCD UART5_IRQHandler ; UART5 DCD TIM6_IRQHandler ; TIM6 DCD TIM7_IRQHandler ; TIM7 DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 ;DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。在向量表中, DCD 分配了一堆内存; __Vectors_End ;__Vectors_End=0x0800012c __Vectors_Size EQU __Vectors_End - __Vectors ;__Vectors_Size =0x0800012c-0x08000000=0x12c; ;以上的代码段名词为RESET,范围为0x08000000~0x0800012c,大小为0x00000130 AREA |.text|, CODE, READONLY ;同理上文可知text代码段 可读 ; Dummy SystemInit_ExtMemCtl function SystemInit_ExtMemCtl PROC EXPORT SystemInit_ExtMemCtl [WEAK] BX LR ENDP SystemInit_ExtMemCtl区域SystemInit_ExtMemCtl=0x0800017c 标记__weak 或 [weak]的函数 就是用在本文件占位的,如果别的文件重写的这个函数就用别文件的,否则使用本文件的。加上了 [WEAK] 修饰. 用户可以根据自己的需要重新编写自己的处理函数, 而且只要命名一样就 OK 了. ( WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。) ;利用PROC、ENDP这一对伪指令把程序段分为若干个部分 ; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。这里__main 这两个函数均来自外部的文件。 LDR R0, = SystemInit_ExtMemCtl ; initialize external memory controller BLX R0 ;BLX挑战到寄存器值得地址,并把跳转前的下一个指令的地址保存到lr寄存器中, LDR R1, = __initial_sp ; restore original stack pointer ldr;将存储器中加载字到一个寄存器中, MSR MSP, R1 LDR R0, =__main ; 这里的__main和c文件的main是不同的地址,__main来自于编译器中 这里的__main=0x08000130 BX R0 ;跳到寄存器值得地址, ENDP ;这里存储的区域是编译器的__main,因为__main会条用外部的main, __main区域 __main=0x08000130 …(因为__main会调用其他代码用…) main.o区域 因为main条用了led.o led.o区域 led=0x08000144 Stm32_Clock_Init.o区域 Stm32_Clock_Init=0x080001d0 ; Dummy Exception Handlers (infinite loops which can be modified) NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP HardFault_Handler PROC EXPORT HardFault_Handler [WEAK] B . ENDP MemManage_Handler PROC EXPORT MemManage_Handler [WEAK] B . ENDP BusFault_Handler PROC EXPORT BusFault_Handler [WEAK] B . ENDP UsageFault_Handler PROC EXPORT UsageFault_Handler [WEAK] B . ENDP SVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDP DebugMon_Handler PROC EXPORT DebugMon_Handler [WEAK] B . ENDP PendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDP SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP Default_Handler PROC EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] EXPORT TAMPER_IRQHandler [WEAK] EXPORT RTC_IRQHandler [WEAK] EXPORT FLASH_IRQHandler [WEAK] EXPORT RCC_IRQHandler [WEAK] EXPORT EXTI0_IRQHandler [WEAK] EXPORT EXTI1_IRQHandler [WEAK] EXPORT EXTI2_IRQHandler [WEAK] EXPORT EXTI3_IRQHandler [WEAK] EXPORT EXTI4_IRQHandler [WEAK] EXPORT DMA1_Channel1_IRQHandler [WEAK] EXPORT DMA1_Channel2_IRQHandler [WEAK] EXPORT DMA1_Channel3_IRQHandler [WEAK] EXPORT DMA1_Channel4_IRQHandler [WEAK] EXPORT DMA1_Channel5_IRQHandler [WEAK] EXPORT DMA1_Channel6_IRQHandler [WEAK] EXPORT DMA1_Channel7_IRQHandler [WEAK] EXPORT ADC1_2_IRQHandler [WEAK] EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] EXPORT CAN1_RX1_IRQHandler [WEAK] EXPORT CAN1_SCE_IRQHandler [WEAK] EXPORT EXTI9_5_IRQHandler [WEAK] EXPORT TIM1_BRK_IRQHandler [WEAK] EXPORT TIM1_UP_IRQHandler [WEAK] EXPORT TIM1_TRG_COM_IRQHandler [WEAK] EXPORT TIM1_CC_IRQHandler [WEAK] EXPORT TIM2_IRQHandler [WEAK] EXPORT TIM3_IRQHandler [WEAK] EXPORT TIM4_IRQHandler [WEAK] EXPORT I2C1_EV_IRQHandler [WEAK] EXPORT I2C1_ER_IRQHandler [WEAK] EXPORT I2C2_EV_IRQHandler [WEAK] EXPORT I2C2_ER_IRQHandler [WEAK] EXPORT SPI1_IRQHandler [WEAK] EXPORT SPI2_IRQHandler [WEAK] EXPORT USART1_IRQHandler [WEAK] EXPORT USART2_IRQHandler [WEAK] EXPORT USART3_IRQHandler [WEAK] EXPORT EXTI15_10_IRQHandler [WEAK] EXPORT RTCAlarm_IRQHandler [WEAK] EXPORT USBWakeUp_IRQHandler [WEAK] EXPORT TIM8_BRK_IRQHandler [WEAK] EXPORT TIM8_UP_IRQHandler [WEAK] EXPORT TIM8_TRG_COM_IRQHandler [WEAK] EXPORT TIM8_CC_IRQHandler [WEAK] EXPORT ADC3_IRQHandler [WEAK] EXPORT FSMC_IRQHandler [WEAK] EXPORT SDIO_IRQHandler [WEAK] EXPORT TIM5_IRQHandler [WEAK] EXPORT SPI3_IRQHandler [WEAK] EXPORT UART4_IRQHandler [WEAK] EXPORT UART5_IRQHandler [WEAK] EXPORT TIM6_IRQHandler [WEAK] EXPORT TIM7_IRQHandler [WEAK] EXPORT DMA2_Channel1_IRQHandler [WEAK] EXPORT DMA2_Channel2_IRQHandler [WEAK] EXPORT DMA2_Channel3_IRQHandler [WEAK] EXPORT DMA2_Channel4_5_IRQHandler [WEAK] WWDG_IRQHandler=0x0800019e WWDG_IRQHandler PVD_IRQHandler TAMPER_IRQHandler RTC_IRQHandler FLASH_IRQHandler RCC_IRQHandler EXTI0_IRQHandler EXTI1_IRQHandler EXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler DMA1_Channel1_IRQHandler DMA1_Channel2_IRQHandler DMA1_Channel3_IRQHandler DMA1_Channel4_IRQHandler DMA1_Channel5_IRQHandler DMA1_Channel6_IRQHandler DMA1_Channel7_IRQHandler ADC1_2_IRQHandler USB_HP_CAN1_TX_IRQHandler USB_LP_CAN1_RX0_IRQHandler CAN1_RX1_IRQHandler CAN1_SCE_IRQHandler EXTI9_5_IRQHandler TIM1_BRK_IRQHandler TIM1_UP_IRQHandler TIM1_TRG_COM_IRQHandler TIM1_CC_IRQHandler TIM2_IRQHandler TIM3_IRQHandler TIM4_IRQHandler I2C1_EV_IRQHandler I2C1_ER_IRQHandler I2C2_EV_IRQHandler I2C2_ER_IRQHandler SPI1_IRQHandler SPI2_IRQHandler USART1_IRQHandler USART2_IRQHandler USART3_IRQHandler EXTI15_10_IRQHandler RTCAlarm_IRQHandler USBWakeUp_IRQHandler TIM8_BRK_IRQHandler TIM8_UP_IRQHandler TIM8_TRG_COM_IRQHandler TIM8_CC_IRQHandler ADC3_IRQHandler FSMC_IRQHandler SDIO_IRQHandler TIM5_IRQHandler SPI3_IRQHandler UART4_IRQHandler UART5_IRQHandler TIM6_IRQHandler TIM7_IRQHandler DMA2_Channel1_IRQHandler DMA2_Channel2_IRQHandler DMA2_Channel3_IRQHandler DMA2_Channel4_5_IRQHandler B . ENDP ALIGN ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示 4 字节对齐。 ;******************************************************************************* ; User Stack and Heap initialization ;******************************************************************************* ;用户栈和堆初始化,由 C 库函数_main 来完成 IF :DEF:__MICROLIB ;是否启用微库, EXPORT __initial_sp ;=0x08000800 EXPORT __heap_base;=0x20000000 EXPORT __heap_limit;=0x20000000 ELSE ;如果没有使用微库 IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR 如 果没 有 定 义 __MICROLIB , 则 才 用 双 段 存 储 器 模 式 , 且 声 明 标 号__user_initial_stackheap 具有全局属性,让用户自己来初始化堆栈。 ALIGN IF,ELSE,ENDIF:汇编的条件分支语句,跟 C 语言的 if ,else 类似 END:文件结束 ENDIF END 内存映射的图像 这张图很重要,在后续都会用到, main函数前的启动文件分析(一下的分析都要结合内存映射图像分析) 程序的编译会生成axf文件,和map的文件,如果希望生成汇编文件和bin文件需要使用命令行生成, fromelf --text -c -o .ListingsLED.asm .ObjectsLED.axf fromelf --bin -o .ListingsLED.bin .ObjectsLED.axf |
|
|
|
1、 LDR R0, = SystemInit_ExtMemCtl ; initialize external memory controller SystemInit_ExtMemCtl =0x0800017c,由于SystemInit_ExtMemCtl 并没有任何代码,直接返回就行,有人问为什么要加这一段,其实可以不加,该段为了初始化外部存储器控制器,只预留了一个接口而已,不必太在意,
BLX R0 2、 LDR R1, = __initial_sp ; restore original stack pointer __initial_sp =0x20000800 MSR MSP, R1 将0x20000800值赋给MSP(主堆栈指针),主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程);堆栈指针的最低两位永远是 0,这意味着堆栈总是 4 字节对齐的 ;这汇编就是将0x20000800赋值给MSP寄存器 这里打断一下介绍一下一些寄存器 这里就详细介绍,我们还是接着分析我们的启动代码 3、 LDR R0, =__main =0x08000130 BX R0 程序跳到了__main处 sp保存0x20000800, BL跳转到__scatterload处,0x80001ac,带返回值的挑战,lr保存的是0x08000138 LDR r4,[pc,#24] ; [0x80001c8] = 0x8000278 LDR r5,[pc,#28] ; [0x80001cc] = 0x8000288 分析这段汇编前,先不急的去分析他的逻辑,先分析0x8000278 和0x8000288是什么,在内存映射图像中的区域为Region$$Table 分析汇编这段的汇编肯定更内存映射区域的内容有关 这段的汇编功能是: r4=0x8000278 r5=0x8000288,比较r4和r5, 如果不相等,r0=[0x8000278 +0xc]=[0x8000284 ]=0x08000268,r3=0x08000269,r0=0x08000288; r1=0x20000000;r2=0x00000800,然后跳转到r3=0x08000269位置上执行, 这里汇编代码,在分析这段代码前,我们可以知道代码的返回为lr=0x080001be; 这段代码的功能是; r0=0x0,比较r2=0x800不等于0;如果不等,将r0的内容存储在r1=0x20000000处,并且r1=r1+0x4; r2=0x800-0x02=0x7FC,然后比较r2=0x7FC是否等于零,这就形成了一个循环,这里直接跳到最好一次执行循环,r1=0x20008000,r2=0x0,比较r2=0x0等于0,执行BX 0x080001BE, (分析可知,这段的循环功能是把0x20000000到0x20000800内存清空,) 以上分析程序跳到了0x080001BE处, 这段汇编的功能,r4=0x8000278+0x10=0x8000288 ,r5=0x8000288 ,BL=0x8000138; 然后跳转到0x8000169处, 这就跳到了main函数的位置, 经过仔细的分析,终于运行到了我们熟悉的main位置,该工程使用keil5,采用硬件仿真器,开发板为STM32F103ZET6板, 这里总结启动文件, 1、启动文件调用__main文件 2、_main调用__scatterload __scatterload其中调用了__scatterload_zeroinit 来清空内存区域0x20000000到0x20000800 3、__scatterload调用了_main_init 4、_main_init调用了main 到此main函数前的启动分析以及结束, 汇编代码点亮LED 本文的目的不仅仅是为了分析启动代码,而是为了点灯,当然点灯是一件很容易的事情,为了巩固一下汇编能力,点灯采用汇编文件的代码形式。纯粹为了多了解一下汇编,没有任何的其他想法,后续的驱动分析,能使用汇编,尽量用汇编来编写代码。 点亮LED0 属于PB5引脚,基地址为0x40010C00 寄存器的配置 根据以上的寄存器很快的就可以写出一点灯的汇编代码了 这里采用的c文件的嵌套汇编的写法, 另外还需要配置系统的时钟系统,由于时钟系统采用汇编写,我估计会把我搞晕,我相信也会把大家搞晕,这里就使用C来写了,我们可以通过汇编来调试整个程序。 上图的汇编程序,先初始化时钟,里面使用到的寄存器 这里不懂的按照STM32中文参考手册进行配置,具体的功能就不进行介绍了, 程序最后运行都了这里 这里的灯也点亮了, 到这里本文的已经结束了。欢迎指正本文的错误, |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1801 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1684 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
745浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
580浏览 3评论
602浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 16:00 , Processed in 1.253350 second(s), Total 80, Slave 63 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号