stm32启动文件详解及SystemInit函数分析
STM32启动文件详解
![STM32启动文件详解](https://img.taocdn.com/s3/m/12e4234803d8ce2f006623b2.png)
STM32启动文件详解(2012-07-28 11:22:34)转载▼分类:STM32标签:stm32启动在<<STM32不完全手册里面>>,用的是STM32F103RBT6,所有的例程都采用了一个叫STM32F10x.s的启动文件,里面定义了STM32的堆栈大小以及各种中断的名字及入口函数名称,还有启动相关的汇编代码。
STM32F10x.s是MDK提供的启动代码,从其里面的内容看来,它只定义了3个串口,4个定时器。
实际上STM32的系列产品有5个串口的型号,也只有有2个串口的型号,定时器也是,做多的有8个定时器。
比如,如果你用的STM32F103ZET6,而启动文件用的是STM32F10x.s的话,你可以正常使用串口1~3的中断,而串口4和5的中断,则无**常使用。
又比如,你TIM1~4的中断可以正常使用,而5~8的,则无法使用。
而在固件库里出现3个文件startup_stm32f10x_ld.sstartup_stm32f10x_md.sstartup_stm32f10x_hd.s其中,ld.s适用于小容量产品;md.s适用于中等容量产品;hd适用于大容量产品;这里的容量是指FLASH的大小.判断方法如下:小容量:FLASH≤32K中容量:64K≤FLASH≤128K大容量:256K≤FLASH;******************** (C) COPYRIGHT 2011 STMicroelectronics ******************** ;* File Name : startup_stm32f10x_hd.s;* Author : MCD Application Team;* Version : V3.5.0;* Date : 11-March-2011;* Description : STM32F10x High Density 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 and also configure the external;* SRAM mounted on STM3210E-EVAL board to be used as data;* memory (optional, to be enabled by user);* - Branches to __main in the C library (which eventually;* calls main()).;* After Reset the CortexM3 processor is in Thread mode,;* priority is Privileged, and the Stack is set to Main.;* 说明: 此文件为STM32F10x高密度设备的MDK工具链的启动文件;* 该模块执行以下操作:;* -设置初始堆栈指针(SP);* -设置初始程序计数器(PC)为复位向量,并在执行main函数前初始化系统时钟;* -设置向量表入口为异常事件的入口地址;* -复位之后处理器为线程模式,优先级为特权级,堆栈设置为MSP主堆栈;* <<< Use Configuration Wizard in Context Menu >>>; 首先对栈和堆的大小进行定义,并在代码区的起始处建立中断向量表,其第一个表项是栈; 顶地址,第二个表项是复位中断服务入口地址。
stm32的启动分析
![stm32的启动分析](https://img.taocdn.com/s3/m/c3deb04c69eae009581becdb.png)
STM32的启动分析一、STM32的复位序列当STM32产生复位后,做的第一件事就是读取下列两个32位整数的值:1、从地址0x0000,0000处取出MSP(主堆栈指针)的初始值放入MSP寄存器中;2、从地址0x0000,0004处取出复位向量放入PC寄存器中,然后从PC中存取的地址出取指并开始执行。
图1:复位序列请注意,这与传统的ARM架构以及其他的单片机完全不同,他们复位后一般是从0x0000,0000地址处取出第一条指令并执行,而一般0x0000,0000都是一条跳转指令。
而在STM32中,在0地址处提供的是MSP的初始值,然后紧跟着就是向量表(上电复位时向量表是被默认放在0x04地址处,但是通过修改向量表偏移量寄存器(VTOR)可以将其定义在其他位置)。
另外,向量表中的数值是32位的地址,而不是跳转指令,系统会自动将该数值存入PC寄存器中后从该32为地址指向的地址出开始执行,这有点像指针的指针。
图2:初始化MSP及PC的初始化的一个范例因为SMT32使用的是向下生长的满栈,所以MSP初始值必须是堆栈内存的末地址加1。
举例来说,如果你的堆栈区域在0x20007C00-0x20007FFF之间,那么MSP的初始值就必须是0x20008000。
向量表跟随在MSP的初始化之后——也就是第2个标目。
要注意因为STM32是在Thumb 态下执行,所以向量表中每个数值必须把LSB(最低权重位)置1.正是因为这个原因,图2中就是用0x101来表示0x100.当0x100处的指令得到执行后,就正是开始了程序的执行。
在这之前MSP是必须的,因为可能第1条指令还没来得及执行,就发生了NMI(不可屏蔽中断)或者其他的Fault,MSP初始化好后就已经为他们的服务例程准备好了堆栈。
二、STM32的3种启动模式在STM32中,可以通过BOOT[1:0]引脚选择三种不同的启动模式,如表1:表1:STM32的三种启动模式根据选定的启动模式,主闪存存储器、系统存储器或SRAM可以按照以下方式访问:1、从主闪存存储器启动:主闪存存储器被映射到启动空间(0x0000,0000),但仍然能够在原有的地址(0x8000,0000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x0000,0000或者0x8000,0000.2、1、从系统存储器启动:系统存储器被映射到启动空间(0x0000,0000),但仍然能够在原有的地址(0x1FFF,F000)访问它。
STM32固件库V3.5.0的CMSIS文件解析
![STM32固件库V3.5.0的CMSIS文件解析](https://img.taocdn.com/s3/m/3111c53731126edb6f1a101b.png)
STM32固件库V3.5.0的CMSIS文件解析1.首先选择启动文件:根据自己所用芯片的型号,选择正确的启动文件。
例如选择STM32F107系列的处理器,我们就选择startup_stm32f10x_hd.s。
在这个文件里首先要定义自己堆栈的大小,根据自己的需要确定。
文件中已经定义好了中断向量的位置及堆栈的初始化操作。
/* Reset handler*/Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT __mainIMPORT SystemInitLDR R0, =SystemInitBLX R0LDR R0, =__mainBX R0ENDP通过上面的代码可以看到先执行SystemInit()函数在进入main()函数。
SystemInit()函数在文件system_stm32f10x.c中有定义。
2.stm32f10x.h:这个头文件包含了STM32的大部份定义:①定义了芯片的类型,例如:#define STM32F10X_CL②定义是否包含标准外设库,#defined USE_STDPERIPH_DRIVER③定义外部振荡器频率,#defined HSE_V ALUE上面的三个定义,最好在编译器选项中定义好,这样就不用修改stm32f10x.h文件,最好不要修改这个文件。
④采用枚举的方式定义中断号。
⑤包含core_cm3.h,system_stm32f10x.h。
#include "core_cm3.h"#include "system_stm32f10x.h"#include <stdint.h>⑥定义数据类型,例如:typedef uint32_t u32此外还定义了FlagStatus,ITStatus及ErrorStatus等。
⑦定义外设结构体,地址及用到的常量。
⑧包含stm32f10x_conf.h来配置外设。
函数SystemInit()
![函数SystemInit()](https://img.taocdn.com/s3/m/8febf903763231126edb1134.png)
函数调用顺序:startup_stm32f10x_cl.s(启动文件)→ SystemInit() →SetSysClock () → SetSys ClockTo72()第一课时钟频率STM32F103内部8M的内部震荡,经过倍频后最高可以达到72M。
目前TI的M3系列芯片最高频率可以达到80M。
在stm32固件库3.0中对时钟频率的选择进行了大大的简化,原先的一大堆操作都在后台进行。
系统给出的函数为SystemInit()。
但在调用前还需要进行一些宏定义的设置,具体的设置在system_stm32f10x.c文件中。
文件开头就有一个这样的定义://#define SYSCLK_FREQ_HSE HSE_Value//#define SYSCLK_FREQ_20MHz 20000000//#define SYSCLK_FREQ_36MHz 36000000//#define SYSCLK_FREQ_48MHz 48000000//#define SYSCLK_FREQ_56MHz 56000000#define SYSCLK_FREQ_72MHz 72000000ST 官方推荐的外接晶振是8M,所以库函数的设置都是假定你的硬件已经接了8M 晶振来运算的.以上东西就是默认晶振8M 的时候,推荐的CPU 频率选择.在这里选择了:#define SYSCLK_FREQ_72MHz 72000000也就是103系列能跑到的最大值72M然后这个C文件继续往下看#elif defined SYSCLK_FREQ_72MHzconst uint32_t SystemFrequency = SYSCLK_FREQ_72MHz;const uint32_t SystemFrequency_SysClk = SYSCLK_FREQ_72MHz;const uint32_t SystemFrequency_AHBClk = SYSCLK_FREQ_72MHz;const uint32_t SystemFrequency_APB1Clk = (SYSCLK_FREQ_72MHz/2);const uint32_t SystemFrequency_APB2Clk = SYSCLK_FREQ_72MHz;这就是在定义了CPU跑72M的时候,各个系统的速度了.他们分别是:硬件频率,系统时钟,AHB总线频率,APB1总线频率,APB2总线频率.再往下看,看到这个了:#elif defined SYSCLK_FREQ_72MHzstatic void SetSysClockTo72(void);这就是定义72M 的时候,设置时钟的函数.这个函数被SetSysClock ()函数调用,而SetSysClock ()函数则是被SystemInit()函数调用.最后SystemInit()函数,就是被你调用的了所以设置系统时钟的流程就是:首先用户程序调用SystemInit()函数,这是一个库函数,然后SystemInit()函数里面,进行了一些寄存器必要的初始化后,就调用SetSysClock()函数. SetSysClock()函数根据那个#define SYSCLK_FREQ_72MHz 72000000 的宏定义,知道了要调用SetSysClockTo72()这个函数,于是,就一堆麻烦而复杂的设置~!@#$%^然后,CPU跑起来了,而且速度是72M. 虽然说的有点累赘,但大家只需要知道,用户要设置频率,程序中就做的就两个事情:第一个: system_stm32f10x.c 中#define SYSCLK_FREQ_72MHz 72000000第二个:调用SystemInit()第一个函数SystemInit()SystemInit()这个函数出现在main()函数的第一行,可以看出它的重要性。
解析STM32的启动过程
![解析STM32的启动过程](https://img.taocdn.com/s3/m/c0dccaf8b8f67c1cfad6b84d.png)
解析STM32的启动过程当前的嵌入式应用程序开发过程里,并且C语言成为了绝大部分场合的最佳选择。
如此一来main函数似乎成为了理所当然的起点——因为C程序往往从main函数开始执行。
但一个经常会被忽略的问题是:微控制器(单片机)上电后,是如何寻找到并执行main函数的呢?很显然微控制器无法从硬件上定位main函数的入口地址,因为使用C语言作为开发语言后,变量/函数的地址便由编译器在编译时自行分配,这样一来main函数的入口地址在微控制器的内部存储空间中不再是绝对不变的。
相信读者都可以回答这个问题,答案也许大同小异,但肯定都有个关键词,叫“启动文件”,用英文单词来描述是“Bootloader”。
无论性能高下,结构简繁,价格贵贱,每一种微控制器(处理器)都必须有启动文件,启动文件的作用便是负责执行微控制器从“复位”到“开始执行main函数”中间这段时间(称为启动过程)所必须进行的工作。
最为常见的51,AVR或MSP430等微控制器当然也有对应启动文件,但开发环境往往自动完整地提供了这个启动文件,不需要开发人员再行干预启动过程,只需要从main函数开始进行应用程序的设计即可。
话题转到STM32微控制器,无论是keiluvision4还是IAR EWARM开发环境,ST公司都提供了现成的直接可用的启动文件,程序开发人员可以直接引用启动文件后直接进行C应用程序的开发。
这样能大大减小开发人员从其它微控制器平台跳转至STM32平台,也降低了适应STM32微控制器的难度(对于上一代ARM 的当家花旦ARM9,启动文件往往是第一道难啃却又无法逾越的坎)。
相对于ARM上一代的主流ARM7/ARM9内核架构,新一代Cortex内核架构的启动方式有了比较大的变化。
ARM7/ARM9内核的控制器在复位后,CPU会从存储空间的绝对地址0x000000取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x000000(PC = 0x000000)同时中断向量表的位置并不是固定的。
stm32启动文件和汇编指令简析
![stm32启动文件和汇编指令简析](https://img.taocdn.com/s3/m/ee51ac730a1c59eef8c75fbfc77da26925c596dc.png)
stm32启动⽂件和汇编指令简析
以stm32fxxxx为例
⼀、启动⽂件,startup_stm32f40_41xxx.S
1.定义:启动⽂件由汇编填写,是系统上电复位后第⼀个执⾏的程序。
2.特征
(1)初始化堆栈指针SP=_initial_sp
(2)初始化PC指针=Reset_Handler
(3)初始化中断向量表。
(4)配置系统时钟。
(5)调⽤c库函数_main初始化⽤户堆栈,从⽽最终调⽤main函数去到c的内容。
⼆、汇编指令
启动⽂件使⽤ARM汇编指令汇总
EQU 给数字常量取⼀个符号名,相当于c语⾔define。
AREA 汇编⼀个新的代码段或数据段。
SPACE 分配内存空间。
PRESERVE8 当前⽂件堆栈需按照8字节对齐。
EXPORT 声明⼀个标号具有全局性,可被外部⽂件使⽤。
DCD 以字为单位分配内存,要求4字节对齐,并要求初始化这些内存。
PROC 定义⼦程序,与ENDP成对使⽤,表⽰⼦程序结束。
WEAK 弱定义,如果外部⽂件声明⼀个标号,则优先使⽤外部⽂件定义的标号,如果外部⽂件没有定义也不会出错。
要注意这个不是ARM 指令,是编译器的。
IMPORT 声明标号来⾃外部⽂件,跟c语⾔EXTERN关键字类似。
B 跳转到⼀个标志号。
STM32之启动文件详解
![STM32之启动文件详解](https://img.taocdn.com/s3/m/25e446015a8102d276a22fb1.png)
STM32 之启动文件详解在嵌入式应用程序开发过程里,由于使用C 语言编程,基本很少涉及到机器底层寄存器的执行过程,一般都会直接在main 函数里开始写代码,似乎main 成为了理所当然的起点,尽管从C 程序的角度来看程序都是直接从main 函数开始执行。
然而,MCU 上电后,是如何寻找到并执行main 函数这一问题却很自然的被忽略了!事实上微控制器是无法从硬件上去定位main 函数的入口地址,因为使用C 语言作为开发语言后,变量/函数的地址便由编译器在编译时自行分配,因此main 函数的入口地址在编译后便不一定是一个绝对地址。
MCU 上电后又是如何寻找到这个入口地址呢?以前接触无论是PIC、AVR、MSP430 或是51 过程中都没涉及到启动文件的配置,仅仅只有熔丝位或配置字是需要根据实际使用配置来设置,其实并非没有,而是由于大部分的开发环境往往自动完整地提供了这个启动文件,不需要开发人员再行干预启动过程,只需要从main 函数开始进行应用程序的设计即可。
然而,但接触到嵌入内核比如Linux 系统移植过程bootloader 却是很重要也是必不可少的一个环节。
事实上,每一种微控制器,无论性能高下,结构简繁,价格贵贱都是必须有启动文件才能正常工作的,它的作用同bootloader 类似。
启动文件完成了微控制器从复位到开始执行main 函数中间这段时间的必要启动配置。
在STM32 中,如果是在MDK 下创建一个工程,一般都有提示是否加入Star up Code 文件,这个就是启动文件,这里有个误区,一般对于初学者来看,很容易误以为STM32F10x.s 这个启动文件是STM32 所有类型芯片的通用启动文件,因此也自然不会去理会它的作用,事实上,这个启动文件只是。
STM32启动过程分析
![STM32启动过程分析](https://img.taocdn.com/s3/m/ff1a3cc008a1284ac85043d0.png)
STM32启动ARM7和ARM9启动时从绝对地址0X00000000开始执行复位中断程序,即固定了复位后的起始地址,但中断向量表的位置是可变的。
而STM32则不同,M3内核规定起始地址必须存放栈顶地址,第二个地址必须是复位中断向量的入口地址,这样CPU复位后会自动从下一个32位地址取出复位中断向量的入口地址,PC就跳转到中断服务程序,所以M3是固定了中断向量表的位置而地址是可变的。
M3的中断向量表有三个位置,通过BOOT引脚进行启动设置。
BOOT1=x BOOT0=0 从用户闪存启动,这是正常的工作模式。
中断向量表定位于FLASH区,复位后PC=0x80000000.OOT1=0 BOOT0=1 从系统存储器启动(相当于厂家在存储器中固化了BOOTLOADER),这种模式启动的程序功能由厂家设置。
BOOT1=1 BOOT0=1 从内置SRAM启动,这种模式可以用于调试。
这种模式下,中断向量表位于SRAM区,起始地址为0x20000000.复位为PC=0x20000000.startup_stm32f10x_XX.s这个文件里面首先定义了复位中断(复位入口矢量被硬件固定在地址0x0000_0004)的处理函数:Reset_Handler,它的作用就是将保存于flash中的初始化数据复制到sram中,调用上面说到的SystemInit来初始化时钟,接着跳转到main执行。
接着定义了Default_Handler,这个是作为其他所有中断的默认处理函数,作用就是死循环,所以你假如开启了某个中断,请按照这里面的中断函数名给它写中断处理函数,例如串口中断处理函数名是USART1_IRQHandler,你开了串口中断,如果不重写USART1_IRQHandler,就默认执行Default_Handler,死循环了。
而如果你有重写,那么中断向量表中的处理函数的地址就会更新为你自己写的那个函数的地址了。
为什么会这样呢?因为此文件的末尾用了类似这样的语句:.weak USART1_IRQHandler.thumb_set USART1_IRQHandler,Default_Handler它给中断处理函数提供了弱(weak)别名(Default_Handler),如果不重写,中断了默认执行Default_Handler,如果重写了,因为是弱别名,所以会被你写的同名函数覆盖。
一文了解STM32启动过程
![一文了解STM32启动过程](https://img.taocdn.com/s3/m/86d2507a2f3f5727a5e9856a561252d381eb204b.png)
一文了解STM32启动过程1 概述说明每一款(芯片)的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。
通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。
(STM32)作为一款高端Cortex-M3系列(单片机),有必要了解它的启动文件。
打好基础,为以后优化程序,写出高质量的代码最准备。
本文以一个实际测试代码--START_(TE)ST为例进行阐述。
整体过程STM32整个启动过程是指从上电开始,一直到运行到main 函数之间的这段过程,步骤为(以使用微库为例):①上电后(硬件)设置SP、PC②设置系统(时钟)③软件设置SP④加载.data、.bss,并初始化栈区⑤跳转到C文件的main函数代码启动过程涉及的文件不仅包含startup_stm32f10x_hd.s,还涉及到了MDK自带的连接库文件entry.o、entry2.o、entry5.o、entry7.o 等(从生成的map文件可以看出来)。
2 程序在Flash上的存储结构在真正讲解启动过程之前,先要讲解程序下载到Flash上的结构和程序运行时(执行到main函数)时的S(RAM)数据结构。
程序在用户Flash上的结构如下图所示。
下图是通过阅读hex文件和在MDK下调试综合提炼出来的。
上图中:MSP初始值由编译器生成,是主堆栈的初始值。
初始化数据段是.data未初始化数据段是.bss.data和.bss是在__main里进行初始化的,对于(ARM)Com (pi)ler,__main主要执行以下函数:其中__scatterlo(ad)会对.data和.bss进行初始化。
加载数据段和初始化栈的参数加载数据段和初始化栈的参数分别有4个,这里只讲解加载数据段的参数,至于初始化栈的参数类似。
0x0800033c Flash上的数据段(初始化数据段和未初始化数据段)起始地址0x20000000加载到SRAM上的目的地址0x0000000c数据段的总大小0x080002f4调用函数_scatterload_copy需要说明的是初始化栈的函数-- 0x08000304与加载数据段的函数不一样,为_scatterload_zeroinit,它的目的就是将栈空间清零。
STM32启动模式详解
![STM32启动模式详解](https://img.taocdn.com/s3/m/867a5703a66e58fafab069dc5022aaea998f41ce.png)
STM32启动模式详解一、三种boot启动模式:一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。
用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式。
1、第一种方式(boot0 = 0):Flash memory启动方式。
启动地址:0x08000000 (stm32F207系列)是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。
基本上都是采用这种模式。
2、第二种方式(boot0 = 1;boot1 = 0:System memory启动方式。
启动地址:0x1FFF0000 (stm32F207系列)从系统存储器启动,这种模式启动的程序功能是由厂家设置的。
一般来说,这种启动方式用的比较少。
系统存储器是芯片内部一块特定的区域,STM32在出厂时,由ST在这个区域内部预置了一段BootLoader,也就是我们常说的ISP程序,这是一块ROM,出厂后无法修改。
一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的BootLoader 中,提供了串口下载程序的固件,可以通过这个BootLoader将程序下载到系统的Flash中。
但是这个下载方式需要以下步骤:Step1:将BOOT0设置为1,BOOT1设置为0,然后按下复位键,这样才能从系统存储器启动BootLoaderStep2:最后在BootLoader的帮助下,通过串口下载程序到Flash 中Step3:程序下载完成后,又有需要将BOOT0设置为GND,手动复位,这样,STM32才可以从Flash中启动可以看到,利用串口下载程序还是比较的麻烦,需要跳帽跳来跳去的,非常的不注重用户体验。
3、第三种方式(boot0 = 1;boot1 = 1):SRAM启动方式。
启动地址:0x20000000(stm32F207系列)内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。
STM32启动代码分析、简化、实战
![STM32启动代码分析、简化、实战](https://img.taocdn.com/s3/m/60902d37b90d6c85ec3ac68d.png)
本文通过对STM32的官方固件库STM32F10x_StdPeriph_Lib_V3.5.0里的MDK启动文件分析,简化部分不需要的代码,并从繁杂的固件库里,精炼出一个类似于“hello world”的入门实战小程序——点亮一个LED。
该工程仅仅包含一个启动文件和一个有main函数的C文件。
本文初衷:不用固件库建立自己的工程!实验软件:Keil uVision4实验硬件:神舟IV号开发板芯片型号:STM32F107VCSTM32启动代码分析、简化、实战汇编基础:1.伪指令:EQU语法格式:名称EQU表达式{,类型}EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言的#define。
其中EQU可以用“*”代替。
名称为EQU伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有一下三种类型:CODE16、CODE32和DA TA2.伪指令:AREA语法格式:AREA段名{,属性1}{,属性2}……AREA命令指示汇编程序汇编一个新的代码段或数据段。
段是独立的、指定的、不可见的代码或数据块,它们由链接程序处理。
段名:可以为段选择任何段名。
但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。
例如,|1_DataArea|。
有些名称是习惯性的名称。
例如:|.text|用于表示由C编译程序产生的代码段,或用于以某种方式与C库关联的代码段。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。
常用的属性如下:——CODE属性:用于定义代码段,默认为READONLY。
——DA TA属性:用于定义数据段,默认为READWRITE。
——READONLY属性:指定本段为只读,代码段默认为READONLY。
——READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE。
——ALIGN属性:使用方式为ALIGN表达式。
STM32的启动
![STM32的启动](https://img.taocdn.com/s3/m/76e1e6fff9c75fbfc77da26925c52cc58ad6905e.png)
STM32的启动1、启动⽂件简介 启动⽂件由汇编编写,是系统上电复位后第⼀个执⾏的程序。
主要做了以下⼯作: (1)初始化堆栈指针 MSP=_initial_sp (2)初始化 PC 指针=Reset_Handler (3)初始化中断向量表 (4)配置系统时钟 (5)调⽤ C 库函数_main 初始化⽤户堆栈,从⽽最终调⽤ main 函数去到 C 的世界2、STM32的启动流程 下⾯这段话引⽤⾃《CM3 权威指南 CnR2》—复位序列, CM4 的复位序列跟 CM3 ⼀样。
在离开复位状态后, CM3 做的第⼀件事就是读取下列两个 32 位整数的值: (1)从地址 0x0000,0000(FLASH 的地址 0x08000000,因为STM32设计的Flash起始地址是在0x0800 0000开始的)处取出 MSP 的初始值。
(2)从地址 0x0000,0004(FLASH 的地址 0x08000004,因为STM32设计的Flash起始地址是在0x0800 0000开始的)处取出 PC 的初始值——这个值是复位向量, LSB 必须是1,然后从这个值所对应的地址处取值。
请注意,这与传统的 ARM 架构不同——其实也和绝⼤多数的其它单⽚机不同。
传统的 ARM 架构总是从 0 地址开始执⾏第⼀条指令。
它们的 0 地址处总是⼀条跳转指令。
在CM3 中,在 0 地址处提供 MSP 的初始值,然后紧跟着就是向量表。
向量表中的数值是 32位的地址,⽽不是跳转指令。
向量表的第⼀个条⽬指向复位后应执⾏的第⼀条指令,就是我们刚刚分析的 Reset_Handler 这个函数。
初始化 MSP 和 PC 的⼀个范例 因为 CM3 使⽤的是向下⽣长的满栈,所以 MSP 的初始值必须是堆栈内存的末地址加1。
举例来说,如果我们的堆栈区域在 0x20007C00-0x20007FFF 之间,那么 MSP 的初始值就必须是 0x20008000。
STM32启动文件详解及SystemInit函数分析
![STM32启动文件详解及SystemInit函数分析](https://img.taocdn.com/s3/m/0a275fba69dc5022aaea00e2.png)
1/6/afeibfp/archive/2013/01/08/2850408.html <2013年1月>日一二三四五六303112345678910111213141516171819202122232425262728293031123456789昵称:afeibfp 园龄:2年5个月粉丝:0关注:0+加关注搜索找找看 谷歌搜索常用链接我的随笔我的评论我的参与最新评论我的标签更多链接我的标签51单片机(2)多字节除法(2)汇编(2)随笔分类(2)转发(2)随笔档案(16)2013年1月 (14)2011年9月 (2)最新评论1. Re:014:针对mdk 中STM32程序无法使用printf ,产生停留BEAB BKPT 0xAB 处问题的解决(转)不点那个MiclroLIB 就行了--blakeliu阅读排行榜1. 001:无符号双字节除以单字节(51单片机,汇编源码)(418)2. 004:STM32启动文件详解及SystemInit 函数分析(转)(389)3. 014:针对mdk 中STM32程序无法使用printf ,产生停留BE AB BKPT 0xAB 处问题的解决(转)(312)4. 010:请教STM32用JLINK V8 SWD 输出调试信息到ITM V iewer 的问题(转)(208)5. 009:semihost/ITM 机制浅析以及使用JLINK 通过ITM 调试stm32单片机(转)(190)评论排行榜1. 014:针对mdk 中STM32程序无法使用printf ,产生停留BE AB BKPT 0xAB 处问题的解决(转)(1)2. 013:ADS semihosting 与硬件重定向(转)(0)3. 012:Keil 调试技术(转)(0)4. 011:Nuvoton(新唐) Cort ex M0 使用semihost 输入输出办法(转)(0)5. 010:请教STM32用JLINK V8 SWD 输出调试信息到ITM V iewer 的问题(转)(0)推荐排行榜博客园首页新随笔联系管理订阅 随笔- 16 文章- 0 评论- 1afeibfp004:STM32启动文件详解及SystemInit 函数分析(转)1 ;先在R A M 中分配系统使用的栈,R A M 的起始地址为0x 2000_00002 ;然后在R A M 中分配变量使用的堆3 ;然后在C O D E 区(f l a s h )分配中断向量表,f l a s h 的起始地址为0x 0800_0000,该中断向量表就从这个起始地址开始分配4 ;分配完成后,再定义和实现相应的中断函数,5 ;所有的中断函数全部带有[w e a k ]特性,即弱定义,如果编译器发现在别处文件中定义了同名函数,在链接时用别处的地址进行链接。
stm32启动文件详解
![stm32启动文件详解](https://img.taocdn.com/s3/m/d0ca155df18583d04864594f.png)
STM32启动文件详解一、启动文件的作用1.初始化堆栈指针 SP;2.初始化程序计数器指针 PC;3.设置堆、栈的大小;4.设置异常向量表的入口地址;5.配置外部 SRAM作为数据存储器(这个由用户配置,一般的开发板可没有外部SRAM);6.设置 C库的分支入口__main(最终用来调用 main函数);7.在 3.5版的启动文件还调用了在 system_stm32f10x.c文件中的SystemIni()函数配置系统时钟。
二、汇编指令三、启动代码1.stack ----- 栈Stack_Size EQU 0x00000400 ; 栈的大小AREA STACK, NOINIT, READWRITE,ALIGN=3 Stack_Mem SPACE Stack_Size ; 分配栈空间__initial_sp ; 栈的结束地址(栈顶地址) 分配名为STACK,不初始化,可读可写,8(2^3)字节对齐的1KB空间。
栈:局部变量,函数形参等。
栈的大小不能超过内部SRAM大小。
AREA:汇编一个新的代码段或者数据段。
STACK段名,任意命名;NOINIT表示不初始化;READWRITE可读可写;ALIGN=3(2^3= 8字节对齐)。
__initial_sp紧挨了SPACE放置,表示栈的结束地址,栈是从高往低生长,结束地址就是栈顶地址。
2.heap ----- 堆Heap_Size EQU 0x00000200 ; 堆的大小(512Bytes)AREA HEAP, NOINIT, READWRITE,ALIGN=3__heap_base ; 堆的起始地址Heap_Mem SPACE Heap_Size ; 分配堆空间__heap_limit ; 堆的结束地址分配名为HEAP,不初始化,可读可写,8(2^3)字节对齐的512字节空间。
__heap_base堆的起始地址,__heap_limit堆的结束地址。
STM32启动代码分析
![STM32启动代码分析](https://img.taocdn.com/s3/m/b1fc1823bcd126fff7050b71.png)
STM32启动代码概述一般嵌入式开发流程就是先建立一个工程,再编写源文件,然后进行编译,把所有的*.s文件和*.c文件编译成一个*.o文件,再对目标文件进行链接和定位,编译成功后会生成一个*.hex文件和调试文件,接下来要进行调试,如果成功的话,就可以将它固化到flash 里面去。
启动代码是用来初始化电路以及用来为高级语言写的软件作好运行前准备的一小段汇编语言,是任何处理器上电复位时的程序运行入口点。
比如,刚上电的过程中,PC机会对系统的一个运行频率进行锁定在一个固定的值,这个设计频率的过程就是在汇编源代码中进行的,也就是在启动代码中进行的。
与此同时,设置完后,程序开始运行,注意,程序是在内存中运行的。
这个时候,就需要把一些源文件从flash里面copy到内存中,又要对它们进行初始化读写,这又有频率的设置。
这些都是初始化。
初始化完成后,我们又要设置一些堆栈,要跳到C语言的main函数里面运行。
这就需要堆栈。
对普通的ARM CPU有这样一个要求:在绝对地址为零的地方要放置一个异常向量表,但并不是所有的ARM CPU都留有这个一个空间,这就需要用到映射的功能。
我们可以将其它地方的一些空间映射到绝对地址里面。
当发生异常时,ARM核来读取异常中断表的时候,它会使用映射之后的那个表,这个就可以接着往下执行,否则在绝对地址零的地方找不到任何信息,程序就会死掉。
这些运行的环境全部建立好后,程序就会跳转到我们的main函数里面。
总之,启动代码,就是对最小系统的初始化。
包括晶振,CPU频率等。
启动代码的最小系统是:异常向量表的初始化–存储区分配–初始化堆栈–高级语言入口函数调用– main()函数。
程序的启动过程:以下面这个例子为例,编译完后,DEBUG后,我们可以看到,光标指向绝对地址为零的地方,这里存放的就是一个异常向量表。
它对应在startup.s里的源文件如下:单步运行后,马上跳转到初始化CPU的频率。
即初始化锁相环,将其锁在一个固定的频率。
STM32启动代码详细分析
![STM32启动代码详细分析](https://img.taocdn.com/s3/m/f98dbbfc4793daef5ef7ba0d4a7302768e996f67.png)
STM32启动代码详细分析最近需要学习iap的功能,因此离不开stm32的启动代码的分析,以前看了很多遍,都看不懂,读书百遍,其义⾃见,因此我有看了⼀遍,下⾯的⽂章,挺好的,因此转载:在上电复位后,我们都知道会先运⾏启动代码,但是启动代码到底使⼲什么⽤的呢?下⾯⼩弟给⼤家⼀⼀列出来。
1、初始化堆栈指针2、初始化 PC 指针3、初始化中断向量表4、配置系统时钟5、调⽤ C 库函数_main 初始化⽤户堆栈我们根据这以上的⼏个步骤⼀⼀进⾏详细的解析:1、栈的内存分配这段代码的意思是,开辟了⼀个栈,这个栈的⼤⼩是0x00000400也就是1KB的⼤⼩,名字为STACK,不初始化,可读可写,2^3=8字节对齐。
那么问题来了,那这个栈到底使⼲什么的呢?⼩弟相信⼤家在学习C语⾔的时候应该也是知道了,栈区保存的是局部变量,只是当时并没有深⼊研究它的⼤⼩问题。
那在这⾥⼩弟给⼤家详细讲解⼀下栈的作⽤:1、局部变量2、函数调⽤3、函数形参以上的这三种情况的开销都是使⽤我们的栈区的资源的。
所以啊!这⾥⼩弟给个位提个醒,千万不要把栈区当成⽆⽌境⼤⼩的哦!STM32可不⽐我们的电脑,没有那么多的空间可以给⼤伙挥霍,如果我们定义的局部变量过⼤可是会莫名其妙报错的。
温馨提⽰:请不要在写程序时,过度使⽤局部变量,会造成栈的益处,从⽽导致编译报错,如果在特殊情况下真的需要很⼤的栈区空间,只需来这⾥进⾏栈区⼤⼩的修改即可。
那么⼩弟再来给这个程序段⾥的汇编指令做⼀个详细的介绍Stack_Size EQU 0x00000400EQU:宏定义的伪指令,相当于等于,类似与 C 中的 define。
这句话的意思是,定义⼀个宏名Stack_Size这个宏代表0x00000400的意思,⽤我们C语⾔来表⽰就是#define Stack_Size 0x00000400AREA STACK, NOINIT, READWRITE, ALIGN=3AREA:告诉汇编器汇编⼀个新的代码段或者数据段。
STM32F4 第7讲 SystemInit时钟系统初始化函数剖析-M4
![STM32F4 第7讲 SystemInit时钟系统初始化函数剖析-M4](https://img.taocdn.com/s3/m/53f22e22b14e852459fb5713.png)
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC ⑤、PLL为锁相环倍频输出。STM32F4有两个PLL:
主PLL(PLL)由HSE或者HSI提供时钟信号,并具有两个不同的输出时钟。 ✓ 第一个输出PLLP用于生成高速的系统时钟(最高168MHz) ✓ 第二个输出PLLQ用于生成USB OTG FS的时钟(48MHz),随机数发生器 的时钟和SDIO时钟。
__IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
__IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
Address offset: 0x24 */
uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0xB1ENR; /*!< RCC AHB1 peripheral clock register,
Address offset: 0x30 */
__IO uint32_t PLLCFGR;
__IO uint32_t CFGR;
/*!< RCC clock configuration register,
Address offset: 0x08 */
__IO uint32_t CIR;
STM32时钟初始化函数SystemInit()详解
![STM32时钟初始化函数SystemInit()详解](https://img.taocdn.com/s3/m/1355a085cd22bcd126fff705cc17552707225e98.png)
STM32时钟初始化函数SystemInit()详解花了⼀天的时间,总算是了解了SystemInit()函数实现了哪些功能,初学STM32,,现记录如下(有理解错误的地⽅还请⼤侠指出):使⽤的是3.5的库,⽤的是STM32F107VC,开发环境RVMDK4.23我已经定义了STM32F10X_CL,SYSCLK_FREQ_72MHz函数调⽤顺序:startup_stm32f10x_cl.s(启动⽂件)→ SystemInit() → SetSysClock () → SetSysClockTo72()初始化时钟⽤到的RCC寄存器复位值:RCC_CR = 0x0000 xx83; RCC_CFGR = 0x0000 0000;RCC_CIR = 0x0000 0000; RCC_CF GR2 = 0x0000 0000;SystemInit()在调⽤ SetSysClock()之前RCC寄存器的值如下(都是⼀些与运算,或运算,在此就不赘述了):RCC->CR = 0x0000 0083;内、外部⾼速时钟的选择使能、就绪标志RCC->CIR = 0x00FF0000; LSI、LSE、HIS、HSE、PLL就绪中断标志RCC->CFGR2 = 0x00000000;系统时钟源切换及状态⾄于这些寄存器都代表着什么意思,详见芯⽚资料RCC寄存器,该⽂重点不在此处;SetSysClock()函数如下:static void SetSysClock(void){#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();#elif defined SYSCLK_FREQ_36MHzSetSysClockTo36();#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();#elif defined SYSCLK_FREQ_72MHz//我的定义的是SYSCLK_FREQ_72MHz,所以调⽤SetSysClockTo72()SetSysClockTo72();#endif}SetSysClockTo72()函数如下:static void SetSysClockTo72(void){__IO uint32_t StartUpCounter = 0, HSEStatus = 0;/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*//* Enable HSE */RCC->CR |= ((uint32_t)RCC_CR_HSEON);1、使能外部晶振/* Wait till HSE is ready and if Time out is reached exit */do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++;} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));if ((RCC->CR & RCC_CR_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;}if (HSEStatus == (uint32_t)0x01){/* Enable Prefetch Buffer */FLASH->ACR |= FLASH_ACR_PRFTBE;/* Flash 2 wait state */FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;⼀、先设AHB APB1 AHB2分频寄存器RCC->CFGR/* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;2、系统时钟不分频作为AHB频率所以AHB等于SYSCLK频率0xxx: system clock not divided Bits 7:4 HPRE: AHB prescaler:Set and cleared by software t o control AHB clock division factor./* PCLK2 = HCLK/2 */RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;3、AHB时钟2分频作为⾼速APB2时钟故APB2 =(AHB=SYSCLK)/2PPRE2: APB high-speed prescaler (APB2)Set and cleared by software to control APB high-speed clock division factor.0xx: AHB clock not divided/* PCLK1 = HCLK */RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;4、AHB时钟4分频作为低速APB1时钟故APB1 =(APB2=AHB=SYSCLK)/4,所以定时器2~7时钟为2倍APB1,为60M--参加⽂章:STM32中定时器的时钟源PPRE1: APB Low speed prescaler (APB1)Set and cleared by software to control APB low-speed clock division factor.100: AHB clock divided by 2#ifdef STM32F10X_CL⼆、配置PLL2时钟频率/* Configure PLLs ------------------------------------------------------*//* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz *//* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz *//* Configure the main PLL */RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);5、所以PLL时钟=8M/8*240/2=1 20M SYSCLK=AHB=120M,PLL时钟=8M/8*240/2=120M,APB2=60M,APB1=30M(upt项⽬时钟)RCC_PLLCFGR_PLLSRC_HSE:Bit 22 PLLSRC: Main PLL(PLL) and audio PLL (PLLI2S) entry clock source1: HSE oscillator clock selected as PLL and PLLI2S clock entryBits 17:16 PLLP: Main PLL (PLL) division factor for main system clockPLL output clock frequency = VCO frequency / PLLP with PLLP = 2, 4, 6, or 800: PLLP = 2f(PLL general clock output) = f(VCO clock) / PLLP,///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define PLL_M 8#define PLL_N 240#define PLL_P 2/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */#define PLL_Q 5///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Enable the main PLL */RCC->CR |= RCC_CR_PLLON; 6、使能PLL时钟Bit 24 PLLON: Main PLL (PLL) enableSet and cleared by software to enable PLL./* Wait till the main PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* Configure Flash prefetch, Instruction cache, Data cache and wait state */FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;/* Select the main PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= RCC_CFGR_SW_PLL;7、将PLL作为系统时钟Bits 1:0 SW: System clock switchSet and cleared by software to select the system clock source.Set by hardware to force the HSI selection when leaving the Stop or Standby mode or incase of failure of the HSE oscillator used directly or indirectly as the system clock.00: HSI oscillator selected as system clock01: HSE oscillator selected as system clock10: PLL selected as system clock11: not allowed/* Wait till the main PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);{}1:AHB, APB1,APB2时钟确定//HCLK = SYSCLK ,从下⾯的分析可以得出SYSCLK是使⽤PLLCLK时钟的,也就是72MHZ(⾄于72MHZ如何得来,请看下⾯分析)//那么就是HCLK(AHB总线时钟)=PLLCLK = 72MHZ//AHB总线时钟等于系统时钟SYSCLK,也就是 AHB时钟 = HCLK = SYSCLK = 72MHZ/* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;//PLCK2等于HCLK⼀分频,所以PCLK2 = HCLK,HCLK = 72MHZ, 那么PLCK2(APB2总线时钟) = 72MHZ//APB2总线时钟等于HCLK的⼀分频,也就是不分频;APB2 时钟 = HCLK = SYSCLK = 72M HZ/* PCLK2 = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;//PCLK1 = HCLK / 2;PCLK1 等于HCLK时钟的⼆分频,那么PCLK1(APB1) = 72MHZ / 2 = 36MHZ//APB1总线时钟等于HCLK的⼆分频,也就是APB1时钟= HCLK / 2 = 36MHZ/* PCLK1 = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;2:如何得出SYSCLK(系统时钟)为72MHZ(外部晶振25MHZ)//记得参考英⽂芯⽚资料的时钟树P115页和RCC时钟寄存器进⾏理解RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);RCC_CFGR2_PREDIV2_DIV5: PREDIV2 = 5; 5分频也就是PREDIV2对输⼊的外部时钟 5分频,那么PLL2和PLL3没有倍频前是25 /5 = 5MHZ RCC_CFGR2_PLL2MUL8 : PLL2MUL = 8; 8倍频8倍频后,PLL2时钟 = 5 * 8 = 40MHZ; 因此 PLL2CLK = 40MHZRCC_CFGR2_PREDIV1SRC_PLL2 : RCC_CFGR2的第16位为1,选择PLL2CLK 作为PRED IV1的时钟源RCC_CFGR2_PREDIV1_DIV5:PREDIV1 = 5;PREDIV1对输⼊时钟5分频PREDIV1CLK = PLL2CLK / 5 = 8MHZ以上是对RCC_CFGR2进⾏的配置--------------------------------------------------------------------------------------RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PR EDIV1 |RCC_CFGR_PLLMULL9);RCC_CFGR_PLLXTPRE_PREDIV1 :操作的是RCC_CFGR的第17位PLLXTPRE,操作这⼀位和操作RCC_CFGR2寄存器的位[3:0]中的最低位是相同的效果RCC_CFGR_PLLSRC_PREDIV1 :选择PREDIV1输出作为PLL输⼊时钟;PREDIV1CLK = 8MHZ,所以输⼊给PLL倍频的时钟源是8MHZRCC_CFGR_PLLMULL9 :PLLMUL = 9;PLL倍频系数为9,也就是对 PLLCLK = PREDIV1C LK * 8 = 72MHZ以上是对RCC_CFGR进⾏的配置---------------------------------------------------------------------------------------------------RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; //选择PLLCLK作为系统时钟源--------------------------------------------------------------------------------------------------⾄此基本配置已经完成,配置的时钟如下所述:SYSCLK(系统时钟) = 72MHZAHB总线时钟 = 72MHZAPB1总线时钟 = 36MHZAPB2总线时钟 = 72MHZPLL时钟 = 72MHZPLL2时钟 = 40MHZ。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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
;__main函数由编译器生成,负责初始化栈、堆等,并在最后跳转到用户自定义的main()函数,来到C的世界。
Stack_Size EQU 0x00000400 ;//定义堆栈大小
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;//定义一个数据段 按8字节对齐 ;AREA 伪指令用于定义一个代码段或数据段 NOINIT:指定此数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各个内存单元值初始化为0
;链接器检查要求堆栈八字节对齐的任何代码是否仅由保持堆栈八字节对齐的代码直接或间接地调用。
PRESERVE8 ;//指示编译器8字节对齐
Байду номын сангаас THUMB ;//指示编译器以后的指令为THUMB指令
;中断向量表定义
; Vector Table Mapped to Address 0 at Reset
;中断函数仅仅实现了Reset_Handler,其他要么是死循环,要么仅仅定义了函数名称
;STM32被设置为从内部FLASH启动时(这也是最常见的一种情况),当STM32遇到复位信号后,
;从0x0800_0000处取出栈顶地址存放于MSP寄存器,从0x0800_0004处取出复位中断服务入口地址放入PC寄存器,
;继而执行复位中断服务程序Reset_Handler,
;Reset_Handler仅仅执行了两个函数调用,一个是SystemInit,另一个__main,
;SystemInit定义在system_stm32f10x.c中,主要初始化了STM的时钟系统:HSI,HSE,LSI,LSE,PLL,SYSCLK,USBCLK,APECLK等等.
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved ; 这种形式就是保留地址,不给任何标号分配
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
Stack_Mem SPACE Stack_Size ;//保留Stack_Size大小的堆栈空间 分 配连续 Stack_Size 字节的存储单元并初始化为 0
__initial_sp ;//标号,代表堆栈顶部地址,后面有用
AREA RESET, DATA, READONLY ;//定义只读数据段,其实放在CODE区,位于0地址
EXPORT __Vectors ;EXPORT:在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
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 EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
__heap_base ;//标号,代表堆末底部地址,后面有用
Heap_Mem SPACE Heap_Size ;//保留Heap_Size的堆空间
__heap_limit ;//标号,代表堆界限地址,后面有用
;PRESERVE8 指令指定当前文件保持堆栈八字节对齐。 它设置 PRES8 编译属性以通知链接器。
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
;先在RAM中分配系统使用的栈,RAM的起始地址为0x2000_0000
;然后在RAM中分配变量使用的堆
;然后在CODE区(flash)分配中断向量表,flash的起始地址为0x0800_0000,该中断向量表就从这个起始地址开始分配
;分配完成后,再定义和实现相应的中断函数,
;所有的中断函数全部带有[weak]特性,即弱定义,如果编译器发现在别处文件中定义了同名函数,在链接时用别处的地址进行链接。
DCD Reset_Handler ; Reset Handler ; 给标号Reset Handler分配地址为0x00000004
DCD NMI_Handler ; NMI Handler ; 给标号NMI Handler分配地址0x00000008
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000200 ;//定义堆空间大小
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;//定义一个数据段,8字节对齐
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack ;给__initial_sp分配4字节32位的地址0x0
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3