02_STM32启动文件详解

本文详细介绍了嵌入式系统中的内存管理技术,包括栈和堆的分配与初始化、中断向量表的设置、复位及中断服务函数的作用。通过汇编语言深入探讨了这些组件如何在硬件层面进行操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

介绍

一、Stack-栈(使用的是RAM的内存)

二、Heap-推(使用的是RAM的内存)

三、中断向量表(使用的是flash)

四、复位函数

五、中断服务函数地址


六、堆和栈的初始化


介绍

启动文件是以.s后缀的汇编编写的,启动文件的作用:

1.初始化推栈指针SP  

2.初初始化PC指针,指向复位程序

3.初始化中断向量表

4.配置系统时钟

5.调用C库函数_main,最终进入c语言编写的代码

一、Stack-栈(使用的是RAM的内存)

作用:用于局部变量、函数调用、函数形参的开销

汇编指令讲解:

EQU:宏定义的伪指令,相当于等于,类似于C语言中的宏定义define

AREA: 告诉汇编器汇编一个新的代码段或者数据段

SPACE:用于分配一定大小的内存空间,单位字节

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

Stack_Size      EQU     0x00000400

Stack_Size 等于 0x00000400(1kb)的空间

AREA    STACK, NOINIT, READWRITE, ALIGN=3

定义栈空间  命名为STACK,NOINIT表示不初始化,READWRITE表示可读可写,ALIGN=3表示2的3次方(8个字节)对齐开辟。

Stack_Mem       SPACE   Stack_Size

分配内存为Stack_Size大小的空间单位字节

__initial_sp

初始化栈指针就是指向栈顶地址(栈使用的时候栈顶先出依次到栈底,先进后出)

标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。

二、Heap-推(使用的是RAM的内存)

作用:用于动态内存的分配,malloc函数

EQU:宏定义的伪指令,相当于等于,类似于C语言中的宏定义define

AREA: 告诉汇编器汇编一个新的代码段或者数据段

SPACE:用于分配一定大小的内存空间,单位字节

Heap_Size       EQU     0x00000400

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

Heap_Size       EQU     0x00000400

Heap_Size 等于 0x00000400(1kb)的空间

AREA    STACK, NOINIT, READWRITE, ALIGN=3

定义栈空间  命名为HEAP,NOINIT表示不初始化,READWRITE表示可读可写,ALIGN=3表示2的3次方(8个字节)对齐开辟。

__heap_base

表示堆的起始地址

Heap_Mem        SPACE   Heap_Size

分配内存为Heap_Size大小的空间单位字节

__heap_limit

初始化推指针

标号__initial_sp紧挨着SPACE语句放置,表示推的结束地址,堆是由低向高生长的(于栈相反)。

                PRESERVE8
                THUMB

PRESERVE8表示指定当前文件的推栈按照8字节对齐

THUMB表示后面指令位THUMB指令

三、中断向量表(使用的是flash)

AREA: 告诉汇编器汇编一个新的代码段或者数据段

EXPORT:声明一个标号具有全局属性,类似于C中的extem功能

DCD:分配一个或者多个以字节为单位的内存,以四字节对齐,并要求初始化这些内存。在向量表中,DCD分配了一推内存,并且以ESR的入口地址初始化它们。

AREA: 告诉汇编器汇编一个新的代码段或者数据段

1.向量表实际上是一个32位的整形数组,一个元素对应一个异常(ESR:中断程序服务地址),数组元素存的就是ESR的入口地址。

2.向量表在复位后从FLASH的0地址开始,具体的初始化值查询参考手册的中断章节。

                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

AREA    RESET, DATA, READONLY

定义一个数据段 命名为RESET,DATA表示数据, READONLY表示可读

EXPORT  __Vectors

表示向量表的开始地址(全局属性)

EXPORT  __Vectors_End

表示向量表的结束地址(全局属性)

EXPORT  __Vectors_Size

表示向量表的大小(全局属性)

__Vectors       DCD     __initial_sp
               DCD     Reset_Handler 
等等很多
               DCD     SysTick_Handler 
               DCD     WWDGT_IRQHandler  
等等很多
               DCD     USBFS_IRQHandler
__Vectors_End

__Vectors_Size  EQU     __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

__Vectors       DCD     __initial_sp                      

中断向量表的第一个字节存放的是__initial_sp栈顶的指针

               DCD     Reset_Handler

中断向量表的第二个字节存放的是Reset_Handler函数入口地址

              DCD     SysTick_Handler

SysTick_Handler以上是内核使用的

DCD     WWDGT_IRQHandler

从 WWDGT_IRQHandler到USBFS_IRQHandler,都是用户使用的。

__Vectors_End

表示向量表结束

__Vectors_Size  EQU     __Vectors_End - __Vectors

表示量表的大小   __Vectors_Size=Vectors结束地址-Vectors开始地址

AREA    |.text|, CODE, READONLY

定义一个代码段   命名text, CODE表示代码段,READONLY只读

四、复位函数

PROC:表示子程序的开始

EXPORT:声明一个标号具有全局属性,类似于C中的extem功能

WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以有程序用户在其他文件重新实现,这里并不是唯一的。

IMPORT:表示该标号来自外部文件,跟C语言中的extern关键字类似。

LDR:从存储器中加载字到一个寄存器中

BLX:跳转到有寄存器给出的地址,并根据寄存器的LES确定处理器的状态,还要吧跳转钱的下一条指令地址保存到LR

BX:跳转到有寄存器/标号给出的地址,不用返回

ENDP:表示子程序的结束

Reset_Handler   PROC
                EXPORT  Reset_Handler                     [WEAK]
                IMPORT  SystemInit
                IMPORT  __main
                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__main
                BX      R0
                ENDP

Reset_Handler   PROC

表示复位子程序开始

               EXPORT  Reset_Handler                     [WEAK]

表示用户可在其他地方定义全局函数

                IMPORT  SystemInit

外部的一个函数SystemInit:主要初始化系统时钟

                IMPORT  __main

外部的一个函数

LDR     R0, =SystemInit

把SystemInit函数入口地址加载到R0寄存器中

BLX     R0

跳转到R0寄存器去执行里面的函数,执行完返回。

                LDR     R0, =__main

把__main函数入口地址加载到R0寄存器中。

(注意这里的_main不是我们写程序的main,这里的_main是外部导入进来的,是C库里面的,执行这个_main后最后会道我们写程序的main里面)

_main的作用:1.初始化程序里面的局部变量全局变量由C库完成2.调用我们写程序的mai函数

                BX      R0

执行完不用返回

                ENDP

子程序结束

五、中断服务函数地址

PROC:表示子程序的开始

EXPORT:声明一个标号具有全局属性,类似于C中的extem功能

WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以有程序用户在其他文件重新实现,这里并不是唯一的。

B .:表示无限循环

用汇编全写好了,在三章中断向量表初化函数__initial_sp用到

NMI_Handler     PROC
                EXPORT  NMI_Handler                       [WEAK]
                B       .
                ENDP

NMI_Handler     PROC

表示子程序的开始

                EXPORT  NMI_Handler                       [WEAK]

表示用户可在其他地方定义全局函数

                B       .

表示无限循环

                ENDP

子程序结束

六、堆和栈的初始化

上面我们只是开辟了空间没有初始化

EXPORT:声明一个标号具有全局属性,类似于C中的extem功能

IMPORT:表示该标号来自外部文件,跟C语言中的extern关键字类似。

IF、ELSE:和C语言的if、else一样


                IF      :DEF:__MICROLIB

                EXPORT  __initial_sp
                EXPORT  __heap_base
                EXPORT  __heap_limit

                ELSE

                IMPORT  __use_two_region_memory
                EXPORT  __user_initial_stackheap

__user_initial_stackheap PROC
                LDR     R0, =  Heap_Mem
                LDR     R1, =(Stack_Mem + Stack_Size)
                LDR     R2, = (Heap_Mem +  Heap_Size)
                LDR     R3, = Stack_Mem
                BX      LR
                ENDP

                ALIGN

                ENDIF

                END

如果DEF:__MICROLIB成立就把__heap_limit、__heap_base、__initial_sp声明为全局文件

如果没定义会执行__user_initial_stackheap PROC这段代码来初始化(我们都是会定义DEF:__MICROLIB这个的所以不会跑去执行下面这段代码区执行)

查看文章 STM32 keil mdk启动代码发分析_转2010年01月29日 星期五 13:50 ;// Stack Configuration ;// Stack Size (in Bytes) ;// Stack_Size EQU 0x00000200 ;//定义堆栈大小 AREA STACK, NOINIT, READWRITE, ALIGN=3 ;//定义一个数据段 按8字节对齐 ;AREA 伪指令用于定义一个代码段或数据段 NOINIT:指定此数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各个内存单元值初始化为0 Stack_Mem SPACE Stack_Size ;//保留Stack_Size大小的堆栈空间 分 配连续 Stack_Size 字节的存储单元并初始化为 0 __initial_sp ;//标号,代表堆栈顶部地址,后面有用 ;// Heap Configuration ;// Heap Size (in Bytes) ;// Heap_Size EQU 0x00000020 ;//定义堆空间大小 AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;//定义一个数据段,8字节对齐 __heap_base Heap_Mem SPACE Heap_Size ;//保留Heap_Size的堆空间 __heap_limit ;//标号,代表堆末尾地址,后面有用 PRESERVE8 ;//指示编译器8字节对齐 THUMB ;//指示编译器为THUMB指令 ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY ;//定义只读数据段,其实放在CODE区,位于0地址 EXTERN NMIException EXTERN HardFaultException EXTERN MemManageException EXTERN BusFaultException EXTERN UsageFaultException EXTERN SVCHandler EXTERN DebugMonitor EXTERN PendSVC EXTERN SysTickHandler ;//声明这些符号在外部定义,同C ;//在××it.c中实现这些函数 ,中断就能自动调用了 EXPORT __Vectors EXPORT __initial_sp ;EXPORT:在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用;IMPORT:伪指令用于通知编译器要使用的标号在其他的源文件中定义, ;但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中 __Vectors DCD __initial_sp ; Top of Stack //Cotex-M 要求此处为堆栈顶部地址 DCD Reset_Handler ; Reset Handler DCD NMIException ; NMI Handler DCD HardFaultException ; Hard Fault Handler DCD MemManageException ; MPU Fault Handler DCD BusFaultException ; Bus Fault Handler DCD UsageFaultException ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVCHandler ; SVCall Handler DCD DebugMonitor ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSVC ; PendSV Handler DCD SysTickHandler ; SysTick Handler //一大堆的异常处理函数地址 ; External Interrupts EXTERN WWDG_IRQHandler EXTERN PVD_IRQHandler EXTERN TAMPER_IRQHandler EXTERN RTC_IRQHandler EXTERN FLASH_IRQHandler EXTERN RCC_IRQHandler EXTERN EXTI0_IRQHandler EXTERN EXTI1_IRQHandler EXTERN EXTI2_IRQHandler EXTERN EXTI3_IRQHandler EXTERN EXTI4_IRQHandler EXTERN DMAChannel1_IRQHandler EXTERN DMAChannel2_IRQHandler EXTERN DMAChannel3_IRQHandler EXTERN DMAChannel4_IRQHandler EXTERN DMAChannel5_IRQHandler EXTERN DMAChannel6_IRQHandler EXTERN DMAChannel7_IRQHandler EXTERN ADC_IRQHandler EXTERN USB_HP_CAN_TX_IRQHandler EXTERN USB_LP_CAN_RX0_IRQHandler EXTERN CAN_RX1_IRQHandler EXTERN CAN_SCE_IRQHandler EXTERN EXTI9_5_IRQHandler EXTERN TIM1_BRK_IRQHandler EXTERN TIM1_UP_IRQHandler EXTERN TIM1_TRG_COM_IRQHandler EXTERN TIM1_CC_IRQHandler EXTERN TIM2_IRQHandler EXTERN TIM3_IRQHandler EXTERN TIM4_IRQHandler EXTERN I2C1_EV_IRQHandler EXTERN I2C1_ER_IRQHandler EXTERN I2C2_EV_IRQHandler EXTERN I2C2_ER_IRQHandler EXTERN SPI1_IRQHandler EXTERN SPI2_IRQHandler EXTERN USART1_IRQHandler EXTERN USART2_IRQHandler EXTERN USART3_IRQHandler EXTERN EXTI15_10_IRQHandler EXTERN RTCAlarm_IRQHandler EXTERN USBWakeUp_IRQHandler ;//同上, 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 DMAChannel1_IRQHandler ; DMA Channel 1 DCD DMAChannel2_IRQHandler ; DMA Channel 2 DCD DMAChannel3_IRQHandler ; DMA Channel 3 DCD DMAChannel4_IRQHandler ; DMA Channel 4 DCD DMAChannel5_IRQHandler ; DMA Channel 5 DCD DMAChannel6_IRQHandler ; DMA Channel 6 DCD DMAChannel7_IRQHandler ; DMA Channel 7 DCD ADC_IRQHandler ; ADC DCD USB_HP_CAN_TX_IRQHandler ; USB High Priority or CAN TX DCD USB_LP_CAN_RX0_IRQHandler ; USB Low Priority or CAN RX0 DCD CAN_RX1_IRQHandler ; CAN RX1 DCD CAN_SCE_IRQHandler ; CAN 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 ;//同上 AREA |.text|, CODE, READONLY ;//定义代码段 ; Reset Handler Reset_Handler PROC ;过程的开始 ;//Rset_Handler的实现 利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰 EXPORT Reset_Handler [WEAK] ;//在外部没有定义该符号时导出该符号,见HELP中[WEAK] IMPORT __main ;//导入符号,__main为 运行时库提供的函数;完成堆栈,堆的初始话 LDR R0, =__main ;//等工作,会调用下面定义的__user_initial_stackheap; BX R0 ;//跳到__main,进入C的世界 ENDP ;过程的结束 ALIGN ; User Initial Stack & Heap IF :DEF:__MICROLIB ;//如果使用micro lib,micro lib 描述见armlib.chm EXPORT __heap_base EXPORT __heap_limit ;//只导出几个定义 ELSE ;//如果使用默认C运行时库 IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap ;//则进行堆栈和堆的赋值,在__main函数执行过程中调用。 LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF END ;//OK ,完了 http://blog.youkuaiyun.com/chehlcy/archive/2010/01/09/5164472.aspx http://files.ourdev.cn/bbs_upload134190/files_11/ourdev_495775.txt ====================================================================== Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main LDR R0, =__main BX R0 ENDP 这段代码什么意思。 有2个地方不理解 一:PROC ENDP 二: [WEAK] 什么意思 ------------------------------------------------------------------------------- 一:PROC为子程序开始,ENDP为子程序结束 二:[weak]的意思就是弱。 怎么弱呢?如果你在其他地方写一个同名函数,比如Reset_handler, 你写的这个函数就可以取代它这个函数了。 语法格式: EXPORT 标号 {[WEAK]} EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。 EXPORT可用 GLOBAL 代替。标号在程序中区分大小写, [WEAK] 选项声明其他的同名标号优先于该标号被引用。 使用示例: AREA Init , CODE , READONLY EXPORT Stest ;声明一个可全局引用的标号Stest…… END
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值