STM32学习笔记——六、启动文件详解

1启动文件简介

  • startup_stm32f10x_hd.s:简单介绍,后续《启动文件详解》章节详细介绍
    • 芯片上电后,首先执行这个汇编程序,建立起C语言的运行环境
    • 使用汇编语言写好了基本程序(Cortex-M3内核支持的指令)
    • 由官方提供(可在官方基础上修改)(ST固件库里面找到,复制添加到工程项目中即可)(不同型号芯片、编译环境使用的汇编文件是不一样的)

功能
- 初始化堆栈指针SP = _initial_sp;
- 初始化程序计数器指针PC = Reset_Handler;
- 初始化中断向量表;
- 调用SystemIni()函数配置STM32的系统时钟
- 设置C库的分支入口“_main”(最终用来调用main函数)
- 设置堆、栈的大小;
- 配置外部SRAM作为数据存储器(用户配置,可没有外部SRAM)

2 如何查找ARM汇编指令

在讲解启动代码的时候,会涉及到ARM 的汇编指令和Cortex 内核的指令

  • 有关Cortex 内核的指令,参考《CM3 权威指南CnR2》第四章:指令集。
  • ARM 的汇编指令,参考MDK->Help->Uvision Help (下面列出了启动文件中使用到的ARM 汇编指令,该列表的指令全部从ARM Development Tools 这个帮助文档里面检索而来)
    在这里插入图片描述
  • 编译器相关的指令WEAK 和ALIGN 为了方便也放在同一个表格了
    在这里插入图片描述

2 启动文件代码讲解

文件:startup_stm32f10x_hd.s

2.1 Stack——栈

栈(stack):由编译器自动分配和释放,存放局部变量,函数调用,函数形参等,其操作方式类似于数据结构中的栈。

  • 向低地址扩展(向下增长)
  • 存放临时变量,退出该作用域就会自动释放
  • stack的空间由操作系统自动分配/释放(程序员不做干涉)
  • stack的空间有限(不能超过SRAM的大小)
    • 编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。
    • 程序出现莫名错误,进入硬fault,可能栈不够大,出现溢出了

在这里插入图片描述
名字:STACK
STM32分区:开辟栈的大小为0X00000400(1KB)

  • EQU:宏定义的伪指令,相当于等于,类似与C 中的define。
  • AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK 表示段名,这个可以任意命名;
  • NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照2^3 对齐,即8 字节对齐。
  • READWRITE:可读可写
  • SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于Stack_Size。
  • 标号__initial_sp 紧挨着SPACE 语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。

2.2 Heap——堆

堆(heap):一般由程序员使用malloc或new来进行分配,在适当的时候用free或delete来进行释放。若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表。

  • 向高地址扩展(向上增长)
  • malloc变量,通过free函数释放
  • heap是很大的自由存储区(heap虽然有很大的存储区,但是这个存储区并不是无限大的,在stm32中,heap区的最大值由SRAM区决定)。
  • 用来动态内存的分配,像malloc() 函数申请的内存就在堆上面。这个在STM32 里面用的
    比较少。

在这里插入图片描述名字:HEAP
STM32分区:开辟栈的大小为0X00000200(512字节)

  • EQU:宏定义的伪指令,相当于等于,类似与C 中的define。
  • AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK 表示段名,这个可以任意命名;
  • NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照2^3 对齐,即8 字节对齐。
  • READWRITE:可读可写
  • SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于Stack_Size。
  • __heap_base 表示堆的起始地址
  • __heap_limit 表示堆的结束地址。

在这里插入图片描述

  • PRESERVE8:指定当前文件的堆栈按照8 字节对齐。
  • THUMB:表示后面指令兼容THUMB 指令。THUBM 是ARM 以前的指令集,16bit,现在Cortex-M系列的都使用THUMB-2 指令集,THUMB-2 是32 位的,兼容16 位和32 位的指令,是THUMB的超集。

2.3 向量表

在这里插入图片描述
名字:RESET,定义一个数据字段,可读
EXPORT:声明一个标号可被外部的文件使用,使标号具有全局属性。如果是IAR 编译器,则使用的是GLOBAL 这个指令。

  • 声明三个标号具有全局属性(可供外部的文件调用):
    • __Vectors
    • __Vectors_End
    • __Vectors_Size

在这里插入图片描述

  1. 当内核响应了一个发生的异常后,对应的异常服务例程(ESR) 就会执行。
  2. 为了决定ESR 的入口地址,内核使用了“向量表查表机制”。这里使用一张向量表。
  3. 向量表其实是一个WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该ESR 的入口地址。
  4. 向量表在地址空间中的位置是可以设置的,通过NVIC 中的一个重定位寄存器来指出向量表的地址。
  5. 在复位后,该寄存器的值为0。
  6. 因此,在地址0 (即FLASH 地址0)处必须包含一张向量表,用于初始时的异常分配。
  7. 要注意的是这里有个另类:0 号类型并不是什么入口地址,而是给出了复位后MSP 的初值。

在这里插入图片描述在这里插入图片描述在这里插入图片描述向量表大小:两个相减

  • __Vectors 为向量表起始地址
  • __Vectors_End 为向量表结束地址

向量表存放方式

  • 从FLASH 的0 地址开始放置,以4 个字节为一个单位,地址0 存放的是栈顶地址,0X04存放的是复位程序的地址,以此类推。
  • 从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C 语言中的函数名就是一个地址。

DCD

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

2.4 复位程序

定义一个名为.text的代码段,可读
在这里插入图片描述

  1. 复位子程序是系统上电后第一个执行的程序。
  2. 调用SystemInit 函数初始化系统时钟
  3. 然后调用C库函数_mian,最终调用main 函数去到C 的世界。
  • WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。
  • IMPORT:表示该标号来自外部文件,跟C 语言中的EXTERN 关键字类似。这里表示SystemInit和__main 这两个函数均来自外部的文件。
  • SystemInit() 是一个标准的库函数,在system_stm32f10x.c 这个库文件总定义。主要作用是配置系统时钟,这里调用这个函数之后,单片机的系统时钟配被配置为72M。
  • __main 是一个标准的C 库函数,主要作用是初始化用户堆栈,并在函数的最后调用main 函数去到C 的世界。这就是为什么我们写的程序都有一个main 函数的原因。

LDR、BLX、BX 是CM4 内核的指令,可在《CM3 权威指南CnR2》第四章-指令集里面查询到,具体作用见下表:
在这里插入图片描述

2.5 终端服务程序

启动文件已写好所有中断的中断服务函数

  • 与平时中断服务函数不一样:函数都是空的
  • 真正的中断复服务程序:需要在外部的C 文件里面重新实现,只是提前占一个位置。
    • 使用某个外设的时候,开启某个中断,忘记编写配套的中断服务程序或者函数名写错
    • 中断时,程序会跳转到启动文件预先写好的空的中断服务程序中
    • 在空函数中无线循环,程序就死在这里

在这里插入图片描述在这里插入图片描述在这里插入图片描述限于篇幅,中间部分代码省略(157行到329行均为相关内容)
B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环。

2.6 用户堆栈初始化

1 ALIGN
ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示4 字节对齐。
在这里插入图片描述

  1. 首先判断是否定义了__MICROLIB

  2. 如果定义了这个宏则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。

    • 有关这个宏我们在KEIL 里面配置,具体见图使用微库。
    • 然后堆栈的初始化就由C 库函数_main 来完成。
      在这里插入图片描述
  3. 如果没有定义__MICROLIB,则插入标号__use_two_region_memory

    • 这个函数需要用户自己实现,具体要实现成什么样,可在KEIL 的帮助文档里面查询到,具体见图use_two_region_memory函数。
    • 然后声明标号__user_initial_stackheap 具有全局属性,可供外部文件调用,并实现这个标号的内容。在这里插入图片描述
      IF,ELSE,ENDIF:汇编的条件分支语句,跟C 语言的if ,else 类似
      END:文件结束
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值