打印
[开发工具]

STM32学习笔记(6): 启动代码(Startup Code)

[复制链接]
4029|32
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本文将对ST官网提供的关于STM32F429的启动代码(在startup_stm32f429xx.s文件中)作出详细的解释,希望能够起到抛砖引玉的作用,帮助大家理解ST其他型号MCU的启动代码。

由于启动代码是用汇编语言写的,并且启动代码中有大量的伪指令(Directives),所以,在正式介绍启动代码前,先来介绍下相关的伪指令。

使用特权

评论回复
沙发
过期的塔头|  楼主 | 2021-9-29 23:34 | 只看该作者
一、伪指令(Directives)

由于MDK中的汇编器(汇编编译器)用的是ARM的汇编器,所以可以从ARM官网下载汇编器的用户指南寻找关于伪指令的详细介绍,或者从MDK的“Help”中的汇编器用户指南中查找。

在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,通常称这些特殊指令助记符为伪指令,他们所完成的操作称为伪操作。

伪指令(directives)也称汇编伪指令,是ARM汇编器提供的一种仅仅在汇编程序被汇编过程中指导汇编器如何进行汇编的且不同于指令的特殊“指令”,即为完成汇编程序作各种准备工作的。伪指令既不会控制机器的操作,也不会被汇编器生成机器码,只要汇编过程完成它的使命就结束了。

使用特权

评论回复
板凳
过期的塔头|  楼主 | 2021-9-29 23:35 | 只看该作者

使用特权

评论回复
地板
过期的塔头|  楼主 | 2021-9-29 23:35 | 只看该作者
如图2所示,ARM汇编器提供了多种多样的伪指令,根据功能的不同可以分别:符号定义伪指令、数据定义伪指令、汇编控制伪指令、宏伪指令及其它伪指令。

使用特权

评论回复
5
过期的塔头|  楼主 | 2021-9-29 23:36 | 只看该作者
下面将详细介绍启动代码中用到伪指令:

注意:在汇编语言中,标号、内存变量名、子程序名和宏名等都是标识符。
在汇编语言中,标号的使用方式有两种:
(1)标号后带“:”,这时标号只表示内存地址;
(2)标号后不带“:”,这时标号表示内存地址和单元长度。

使用特权

评论回复
6
过期的塔头|  楼主 | 2021-9-29 23:38 | 只看该作者
● EQU
伪指令EQU用来为一个数字常量、或一个和内核寄存器相关的数值或一个和程序计数器相关的数值定义的一个符号名称。类似于C语言中的”#define “。
语法格式:name EQU expr{ , type}
注意:语法格式中的{ }不属于语法格式的部分,并且{ }中的内容是可选的。
name:数值(expr)的符号名称。
expr:一个与内核寄存器相关的地址,或一个绝对地址,或一个与PC相关的地址,或一个32位整型常量。
type:可选项。它可以是ARM、THUMB、CODE16、CODE32或DATA中的任何一个。
举个例子:fiq EQU 0x1C,CODE32

使用特权

评论回复
7
过期的塔头|  楼主 | 2021-9-29 23:39 | 只看该作者
AREA
伪指令AREA用来定义一个代码段或数据段(data section),到底是数据段还是代码段可以从属性名词分辨出。
语法格式:AREA sectionname{,attr}…{,attr}…
注意:语法格式中的{ }不属于语法格式的部分,并且{ }中的内容是可选的。
sectionname:代码段或数据段的名称。惯用|.text|,它被用于由C编译器产生的代码段或和C库相关的代码段。
它是一块独立的不可分割的数据段或代码段,可以为任何名称。不过,非字符起始的必须加下划线,如1_dataarea。
attr:由一个或多个被逗号隔开的节(或段)属性组成。
段属性有:ALIGN=expression,表示这个数据或代码段按2^expression个字节对齐;
NOINIT表示不零初始化;
READWRITE表示可读可写;
DATA表示只对数据段进行操作,默认可读可写。
注意:伪指令DATA已经被编译器忽略了,不过它可以作为属性使用。
此外还有其他的属性,这里就不过多介绍了,详见《汇编器用户指南》。

使用特权

评论回复
8
过期的塔头|  楼主 | 2021-9-29 23:40 | 只看该作者
● SPACE
伪指令SPACE用于在存储器中开辟一段连续的存储空间,并初始化为零。
语法格式:{label} SPACE expr
注意:语法格式中的{ }不属于语法格式的部分,并且{ }中的内容是可选的。
label:它是可选的。它可以是任何不与编译器冲突的字符名称,可以被用来说明开辟的内存空间的名称或作用。
expr:开辟的零初始化存储空间的大小,即字节数。也可是某一个有确定数值的字符。
举个例子:若Stack_Size=0x40,那么语句 Stack_Mem SPACE Stack_Size是正确的。

使用特权

评论回复
9
过期的塔头|  楼主 | 2021-9-29 23:42 | 只看该作者
● PRESERVE8
伪指令PRESERVE8指定当前的文件中,堆栈区的对齐方式为8字节对齐。
语法格式:PRESERVE8 {bool}
注意:语法格式中{ }不属于语法格式的部分,并且{ }中的内容是可选的。
bool:它是可选的。它不是 true 就是 false,默认为true。

使用特权

评论回复
10
过期的塔头|  楼主 | 2021-9-29 23:42 | 只看该作者
● THUMB
伪指令THUMB命令汇编器以UAL语法将THUMB后面的指令翻译成T32指令。
语法格式:THUMB

使用特权

评论回复
11
过期的塔头|  楼主 | 2021-9-29 23:44 | 只看该作者
● EXPORT
伪指令EXPORT用于在程序中声明一个全局的标号,该标号可在其他的文件中被引用。
语法格式:EXPORT的语法格式共有5种,下面主要介绍下启动代码中用到的3种。
(1)EXPORT { [WEAK]}
(2)EXPORT symbol { [SIZE=n]}
(3)EXPORT symbol [ WEAK {,attr}{type{,set}}{,SIZE=n}]
注意:语法格式中的{ }不属于语法格式的部分,并且{ }中的内容是可选的。
[WEAK]:表示其他的同名标号优先于该标号被引用。如果省略symbol,那么所有的标号都是“WEAK”。
从启动代码中可以发现,中断服务函数是弱声明的(由[WEAK]关键字标注)。所谓弱声明,即:如果用户定义了相同的函数则启动代码中的该函数失效而使用用户定义的中断服务函数。这样是为了防止用户使能了中断而没有中断服务函数,从而造成程序崩溃。假设使能了中断,而用户又没有定义这个中断服务函数则会进入默认中断,默认中断为死循环(注意:死循环与程序崩溃不是一个概念)。
symbol:它是全局属性标号,区分大小写。如果省略symbol,那么所有标号都是全局的。

使用特权

评论回复
12
过期的塔头|  楼主 | 2021-9-29 23:45 | 只看该作者
● IMPORT
IMPORT伪指令用于通知编译器要使用的标号在其他的源文件中被定义(即在外部文件中被定义,相当于C语言中的extern),但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的标号表中。该标号在程序中区分大小写。
语法格式:
(1)IMPORT symbol { [type]}
(2)IMPORT symbol { [SIZE=n]}
(3)IMPORT symbol [WEAK{,attr}…{,type}…{,SIZE=n}]
[WEAK]:[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0。若该标号被B或BL指令引用,则将B或BL指令置为NOP操作。
symbol:它分别在汇编源文件、目标文件或库文件中。区分大小写。

使用特权

评论回复
13
过期的塔头|  楼主 | 2021-9-29 23:46 | 只看该作者
● DCD
伪指令DCD用于分配一片连续的字存储单元并用指定的数据初始化。用DCD分配的字存储单元是字对齐的。
语法格式:{label} DCD {U} expr {,expr}
expr:它是程序表达式或数字表达式

使用特权

评论回复
14
过期的塔头|  楼主 | 2021-9-29 23:47 | 只看该作者
● IF ELSE ENDIF
伪指令IF,ELSE,ENDIF用来允许有条件的汇编指令或伪指令。
语法格式:
IF logic-expression
指令序列1
ELSE
指令序列2
ENDIF
说明:IF,ELSE,ENDIF伪指令能根据条件的成立与否决定是否执行某个指令序列。当IF后面的逻辑表达式为真,则执行指令序列1,否则执行指令序列2。其中,ELSE及指令序列2可以没有,此时,当IF后面的逻辑表达式为真,则执行指令序列1,否则继续执行后面的指令。
此外,伪指令IF,ELSE,ENDIF可以嵌套使用。
逻辑表达式logic-expression也可以为单目运算(Unary Operator,详细的可参考《汇编器用户指南》)。如单目操作数 :DEF: ,:DEF:A则表示如果A被定义,则为真,否则为假。

使用特权

评论回复
15
过期的塔头|  楼主 | 2021-9-29 23:47 | 只看该作者
● PROC
伪指令PROC标志着程序的开始。它容易理解,这里不多做介绍。

● ENDP
伪指令ENDP标志着程序(调用)的结束。这里不多做介绍。

● END
伪指令END告诉汇编器已经到源程序文件的末尾。这里不多做介绍。

使用特权

评论回复
16
过期的塔头|  楼主 | 2021-9-29 23:48 | 只看该作者
二、汇编指令
● B
指令B是跳转指令。
语法格式:B label
在启动代码中,会发现label是一个点“.”,它表示跳转到当前的指令地址处(即当前的PC值),也就是进入到当前的死循环中了。

使用特权

评论回复
17
过期的塔头|  楼主 | 2021-9-29 23:49 | 只看该作者
● BX
指令BX是跳转指令。
语法格式:BX Rm
其中,Rm是一个内核寄存器,它的值是一个地址值。上述指令表示程序跳转到Rm所指向的指令处

使用特权

评论回复
18
过期的塔头|  楼主 | 2021-9-29 23:50 | 只看该作者
LDR
LDR既可以作为加载指令使用,也可以作为伪指令。
作为伪指令时的语法格式:LDR Rt,=expr 。其作用是将expr的值(expr为立即数)或expr的地址(expr是一个标号)加载到Rt中。
作为加载指令时的语法格式:LDR {type}{cond} Rt, [Rn {, #offset}]。其作用是将Rn(Rn的值是一个地址值)中的数值加载给Rt。

使用特权

评论回复
19
过期的塔头|  楼主 | 2021-9-29 23:50 | 只看该作者
ORR
指令ORR是逻辑或操作指令。
语法格式:ORR {S} {cond} Rd, Rn, operand2
其中,Rn是第一操作数,operand2是第二操作数。上述指令表示将Rn和operand2进行逻辑或操作,其结果保存到目标操作数Rd中。

使用特权

评论回复
20
过期的塔头|  楼主 | 2021-9-29 23:52 | 只看该作者
STR
STR是一个典型的存储指令。
语法格式:STR {type}{cond} Rt, [Rn {, #offset}]。该指令表示将寄存器Rt中的字数据存放到以Rn{+offset}为地址的寄存器中。
注意:语法格式中的{ }不属于语法格式的部分,并且{ }中的内容是可选的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

67

主题

846

帖子

0

粉丝