基于stm32f103c8t6芯片的启动文件进行分析。

启动文件在嵌入式芯片开发是必不可少的,其文件后缀是.s,通常需要加入工程参与编译。它的的用包括一下几点:

  1. 开辟栈、堆的空间。
  2. 初始化中断向量表。
  3. 调用外部SystemInit函数,初始化时钟。
  4. 调用C库函数__main初始化用户栈,调用main函数转到C世界。

启动文件使用汇编语言编写,如果熟悉汇编语言,那么很容易理解它;如果不熟悉汇编语言,针对启动文件里用到的汇编指令,下面会一一介绍。

1.启动文件使用到的汇编指令

指令名称

作用

EQU

给数据常量起一个符号名,相当于C语言的#define

AREA

汇编一个新的代码段或者数据段

SPACE

分配内存空间

PRESERVE8

告诉编译器,指定当前文件的堆按照8字节对齐

THUMB

表示后面指令兼容THUMB指令。

EXPORT

声明一个标号具有外部属性,可在外部文件使用

DCD

以字节为单位,按照4字节对齐,并要求初始化这些内存

PROC

定义子程序,与ENDP成对使用,表示子程序结束。

WEAK

编译器指令(不同的编译器有差异),弱定义一个标号,如果外部文件声明了该标号,则优先使用外部文件的定义。避免出现重定义错误。

IMPORT

声明标号来自外部文件,相当于C语言的extern

B

跳转到一个标号

ALIGN

编译器指令(不同的编译器有差异),编译器对指令或数据的存放地址进行对齐,一般跟一个立即数,默认表示按照4字节对齐。

END

文件结束标志

IF,ELSE,ENDIF

汇编条件分支语句,相当于C语言的#if #else #endif

;

行注释起始符号,相当于C语言的“//“

2.启动文件代码分析

Stack_Size      EQU     0x00000400AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

上面代码定义了栈的大小为0x00000400(1KB),名称为STACKNOINIT表示不初始化,REAWRITE表示可读可下,ALIGN = 3表示按照8(即2^3)字节对齐。还定义了标号__initial_sp(栈的结束地址)。利用SPACE关键字来申请空间,大小为Stack_Size,记作Stack_Mem。栈是由高向低生长的。

栈的空间用于局部变量、函数调用、函数形参等开销,栈的大小不能超过芯片内部SRAM大小;如果编写的程序局部变量比较多,占用内存比较大,会造成栈溢出,导致进入硬故障,这时候需要考虑增加栈空间大小。

Heap_Size       EQU     0x00000200AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_baseHeap_Mem        SPACE   Heap_Size
__heap_limit

上面的代码定义了堆大小为0x00000200(512字节),名称为HEAPNOINIT表示不初始化,READWRITE表示可读可写,ALIGN = 3表示按照8(即2^3)字节对齐。还定义了标号:__heap_base(堆的起始地址)、__heap_limit(堆的结束地址)。利用SPACE关键字来申请空间,大小为Heap_Size,记作Heap_Mem。堆是由低向高生长的,与栈的生长方向相反。

堆的主要用于动态内存的分配,像malloc()函数申请的内存就在堆中。这个在STM32里面用得比较少。

                PRESERVE8THUMB

上面两个在前面的指令表格里已经说明。

; Vector Table Mapped to Address 0 at ResetAREA    RESET, DATA, READONLYEXPORT  __VectorsEXPORT  __Vectors_EndEXPORT  __Vectors_Size

上面代码定义了一个数据段,名称为RESET,仅可读,还声明了3个外部文件可以使用的标号:__Vectors(向量表起始地址)、__Vectors_End(向量表结束地址)、__Vectors_Size(向量表大小)。

向量表其实就是一个WORD(32bit整数)数组,每一个下标对应一种异常,该下标元素的值则是该ESR的入口地址。向量表在地址空间的位置是可以被设置的,同过NVIC中的一个重定位寄存器来指定向量表的地址。复位之后,该寄存器的值为0。因此,在地址0(即flash地址0)处必须包含一张向量表,用于初始化时的异常分配。

接下来的代码就是分配向量表的内存

__Vectors       DCD     __initial_sp               ; Top of Stack(栈顶地址)DCD     Reset_Handler              ; Reset Handler(复位程序地址)DCD     NMI_Handler                ; NMI HandlerDCD     HardFault_Handler          ; Hard Fault HandlerDCD     MemManage_Handler          ; MPU Fault HandlerDCD     BusFault_Handler           ; Bus Fault HandlerDCD     UsageFault_Handler         ; Usage Fault HandlerDCD     0                          ; Reserved(保留)DCD     0                          ; ReservedDCD     0                          ; ReservedDCD     0                          ; ReservedDCD     SVC_Handler                ; SVCall HandlerDCD     DebugMon_Handler           ; Debug Monitor HandlerDCD     0                          ; ReservedDCD     PendSV_Handler             ; PendSV HandlerDCD     SysTick_Handler            ; SysTick Handler; External InterruptsDCD     WWDG_IRQHandler            ; Window WatchdogDCD     PVD_IRQHandler             ; PVD through EXTI Line detectDCD     TAMPER_IRQHandler          ; Tamper;(中间代码省略)DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI LineDCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
__Vectors_End__Vectors_Size  EQU  __Vectors_End - __Vectors

上面代码利用DCD关键字以4字节对齐,分配了一堆内存,类似之前SPACE关键字的作用。利用__Vectors_End(堆结束地址)减去__Vectors(堆的起始地址)得到堆的大小__Vectors_Size

向量表从flash的0地址开始放置,以4字节为一个单位,地址0存放的时栈顶地址,接着0x04存放的时复位程序的地址,以此类推。向量表中存放的都是中断函数的函数名,可我们知道C语言中函数名就是一个地址。

        AREA    |.text|, CODE, READONLY

上面代码表示是定义一个名称为.text的代码段,仅可读。

       ; Reset handler
Reset_Handler    PROCEXPORT  Reset_Handler             [WEAK]IMPORT  __mainIMPORT  SystemInitLDR     R0, =SystemInitBLX     R0LDR     R0, =__mainBX      R0ENDP

复位子程序是系统上电第一个执行的程序,调用SystemInit函数(sysyem_stm32f4xx.c文件里定义的)初始化系统时钟等,然后调用C库函数_main(编译器自带的),在_main函数里最终会调用main函数转到C的世界。

LDR、BLX、BX是CM4内核的指令:

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

BL:跳转到由寄存器/标号给出的地址,并把跳转前的下一条指令保存到LR中。

BLX:跳转到由寄存器/标号给出的地址,并根据寄存器的LSE确定处理器的状态,并把跳转前的下一条指令保存到LR中。

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

接下来的代码就是中断服务程序的实现,一般来说,我们会在外部的c文件实现中断函数,这里定义了只是备用,以防我们把某个中断使能了,但忘了实现它的中断函数或者函数名写错,那么系统就会执行下面的程序,并且下面的中断函数会进入无限循环,程序也就死在这里。

NMI_Handler     PROC    ;不可屏蔽的系统异常中断EXPORT  NMI_Handler                [WEAK]B       .ENDPHardFault_Handler\             ;所有类型错误的中断PROCEXPORT  HardFault_Handler          [WEAK]B       .ENDP(省略一部分代码)SysTick_Handler PROC        ;系统滴答定时器中断EXPORT  SysTick_Handler            [WEAK]B       .ENDPDefault_Handler PROC        ;外设中断EXPORT  WWDG_IRQHandler            [WEAK]EXPORT  PVD_IRQHandler             [WEAK]EXPORT  TAMPER_IRQHandler          [WEAK]
(省略一部分代码)RTCAlarm_IRQHandlerUSBWakeUp_IRQHandlerB       .ENDPALIGN

B:跳转到一个标号,这里跳转到一个‘.’,表示无限循环。

;*******************************************************************************; User Stack and Heap initialization;*******************************************************************************IF      :DEF:__MICROLIB  ;这个宏在KEIL里面开启                     EXPORT  __initial_spEXPORT  __heap_baseEXPORT  __heap_limit             ELSE               IMPORT  __use_two_region_memory     ;这个函数由用户自己实现EXPORT  __user_initial_stackheap
__user_initial_stackheapLDR     R0, =  Heap_MemLDR     R1, =(Stack_Mem + Stack_Size)LDR     R2, = (Heap_Mem +  Heap_Size)LDR     R3, = Stack_MemBX      LRALIGNENDIFEND

如果勾选【Options for Target】->【Target】->【Use MicroLIB】,那么就会定义宏__MICROLOB。

如果定义了__MICROLOB,那么把__initial_sp(栈起始地址)、__heap_base(堆开始地址)、__heap_limit(堆结束地址)标号赋予全局属性,那么在外部文件也可以使用,然后堆和栈的初始化就由C库函数__main来完成。

如果没有定义__MICROLOB,采用双段存储器模式,且声明标号__user_initial_stackheap具有全局性,让用户自己来初始化堆和栈。KEIL C库函数会调用__user_initial_stackheap,通过R0~R3将堆栈以参数形式传递给KEIL C库。

stm32系列启动文件解读(KEIL编译环境)相关推荐

  1. STM32单片机启动文件startup_stm32f10x_hd.s详解

    启动文件源码如下: ; Amount of memory (in bytes) allocated for Stack ; Tailor this value to your application ...

  2. 【STM32】STM32 startup启动文件选择

    STM32库里面的启动文件,需要根据实际使用的芯片的容量大小进行选择使用. startup_stm32f10x_cl.s 互联型的器件,STM32F105xx,STM32F107xx startup_ ...

  3. STM32之启动文件详细解析(V3.5.0)

    启动文件么,就是进入main函数(其实可以在本文件中改成别的,即替换"_main"为你想改的"_XXXX"即可)之前干的一些事--- 来看看ARM在进入的&qu ...

  4. 使用ipad编译c文件 设置C编译环境 编写C语言代码

    使用ipad编译.c/.cpp文件 目标机是ipad air3 第一种方式 使用带有ssh服务的IDE.我个人比较喜欢textastic AppStore 下载textastic.(有钱的直接付款,囊 ...

  5. 熊大UWB系列教程二:KEIL软件环境搭建以及程序下载

    有超宽带定位项目需求请加微信沟通  微信号: cc12131789 工具准备: 硬件:JLink下载器,超宽带mini板,micro usb线. 软件:超宽带程序. 第一步.将micro usb线插到 ...

  6. 高通9xxx系列4G模块modem linux编译环境安装及配置详细说明

    1.安装包: Perl, DS500-PA-00003-r5p0-05rel0(RVDS),Hexagon,Python,Llvm, Zlib 2,1:以上安装包除Zlib需默认安装外,其余可以指定安 ...

  7. STM32的启动过程 — startup_xxxx.s文件解析(MDK和GCC双环境)

    目录 一.什么是启动文件 二.STM32的启动方式 三.启动文件分析(MDK环境) 3.1 开辟栈空间和堆空间 3.2 中断向量表部分 3.3 Reset_Handler 系统启动 3.4 中断服务程 ...

  8. STM32高级开发(8)-链接器与启动文件

    最近休息了一下,中间断断续续在虚拟机上靠着记忆恢复了原来崩溃的虚拟机上80%的工作成果,还算过得去吧,完全丢失的也就是些不大重要的资料.今天新买的机械键盘也到货了,不得不说顺丰的工作人员好评,给过年假 ...

  9. STM32安装Keil5、芯片支持包、startup启动文件(启动过程分析)、建立工程、烧写

    参考:stm32入门之keil5的安装以及第一个工程的建立 作者:SKY丶丿平才 发布时间: 2020-12-06 17:08:30 网址:https://blog.csdn.net/weixin_4 ...

最新文章

  1. pku 1077 Eight
  2. 目录:SpringBoot学习目录
  3. Adreno GPU Profiler
  4. android 使用Binder通信
  5. xgboost分类_XGBoost(Extreme Gradient Boosting)
  6. MySQL入门之select、from、join、where子句及基本匹配符
  7. day21 java的八大包装类
  8. 25 岁的老 Delphi,还值得程序员入手吗?
  9. caffe+CPU︱虚拟机+Ubuntu16.04+CPU+caffe安装笔记
  10. 【bzoj1433】[ZJOI2009]假期的宿舍
  11. SQL语句——查询语句
  12. 外螺纹对照表_最新英制螺纹对照表(2016年完整版).
  13. 关于嵌入式软件系统测试策略和方案设计详解
  14. 华为笔记本已经设置睡眠时间但无法自动进入睡眠状态解决方案(亲测有效)
  15. IE10访问Apache2.4卡死的问题
  16. mysql根据字长查询_MYSQL常用查命令
  17. Android平台App进程优先级
  18. 用 Python 实现哈希算法检测重复图片
  19. 副屏幕全屏_电脑 双屏(双屏电脑主屏副屏设置)
  20. 理解杀毒软件的意义(完整---另加补充和总结)

热门文章

  1. 实现接口时@Override注解问题
  2. Spring Security 入门(3-11)Spring Security 的使用-自定义登录验证和回调地址
  3. 【面试必备】Swiftamp;nbsp;面试题及其答案
  4. 利用介质创建额外域控制器
  5. Enterprise Library学习所得(一):总体概述
  6. 鸿蒙系统 新闻,鸿蒙系统升级名单曝光,为什么你还没有收到推送?3个原因望周知...
  7. oracle触发器监听数据变化,【Trigger】利用Oracle的触发器,自动记录数据的变动履历...
  8. [IDEA 配置MYSQL数据库连接]
  9. 【Python】 1055 集体照 (25 分)
  10. 第七次的服务端课程:hibernate,jpa,springdata