基于stm32f103的MDK汇编介绍
一、ARM汇编语法简介
汇编语言是一种底层的语言,需要依赖硬件进行编写,基本换个硬件程序就需要重新编写,其实在stm32的C语言在机器执行时也要被转换为汇编语言再转换为机器代码执行。汇编语言与其他高级语言不同的地方在于,对数据或寄存器的操作是通过各种指令完成的,主要有跳转指令
、数据处理指令
、程序状态寄存器(PSR)处理指令
、加载/存储指令
、协处理器指令
、异常产生指令
6大指令,常用指令集的介绍与使用可参考以下文章:
ARM汇编指令集
ARM汇编指令集汇总
二、MDK创建汇编工程
1、汇编工程的建立与C语言工程的建立有所不同,我们这里新建一个汇编工程,完成对一个汇编文件的编写及调试,keil也可执行多文件的汇编编译调试,具体方法参考Keil 汇编asm/A51多个文件。
2、keil创建工程
(1)选择保存路径时尽量不要太深,名字不包含中文,切记不要直接丢桌面,放在文件夹里。
(2)选择芯片类型
(3)配置运行环境(如果自己的程序中写入了启动文件的内容则不需要添加startup
)
(4)添加源文件
工程创建完成。
三、编译测试及输出解释
1、测试代码
代码实现将寄存器r5、r6、r7、r8的值设置为0x00000005``0x00000006``0x00000007``0x00000008
AREA MYDATA, DATA
AREA MYCODE, CODE
ENTRY
EXPORT __main
__main
MOV R0, #10
MOV R1, #11
MOV R2, #12
MOV R3, #13
;LDR R0, =func01
BL func01
;LDR R1, =func02
BL func02
BL func03
LDR LR, =func01
LDR PC, =func03
B .
func01
MOV R5, #05
BX LR
func02
MOV R6, #06
BX LR
func03
MOV R7, #07
MOV R8, #08
BX LR
2、编译Debug调试查看寄存器地址
(1)编译结果无误则工程配置正确
(2)调试查看
(3)先运行程序,然后停止。
(4)在func01执行处设置断点
(5)运行代码后,r5的值没有改变。
(6)取消断点后,运行程序,寄存器的值发生改变,且与程序设定一样。
3、输出hex文件解释
(1)设置工程输出hex文件
(2)hex文件在工程目录中,以文本打开。
第一行的数据即为,0x02、0x00、0x00、0x04、0x08、0x00、0xf2
第一个字节 0x02表示本行数据的长度;
第二、三字节 0x00 0x00表示本行数据的起始地址;
第四字节 0x04表示数据类型,数据类型有:0x00
、0x01
、0x02
、0x03
、0x04
、0x05
。
00
Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录
01
End of File Record: 用来标识文件结束,放在文件的最后,标识HEX文件的结尾
02
Extended Segment Address Record: 用来标识扩展段地址的记录
03
Start Segment Address Record:开始段地址记录
04
Extended Linear Address Record: 用来标识扩展线性地址的记录
05
Start Linear Address Record:开始线性地址记录
第五、六字节是数据,0x08 0x00
5.最后一个字节 0xf2为校验和。
校验和的算法为:计算 0xf2前所有16进制码的累加和(不计进位),检验和 = 0x100 - 累加和
‘04’ ‘05’,都是用来提供地址信息的。每次碰到这2个记录的时候,都可以根据记录计算出一个“基”地址。对于后面的数据记录,计算地址的时候,都是以这些“基”地址为基础的。
HEX文件都是由记录(RECORD)组成的。在HEX文件里面,每一行代表一个记录。记录的基本格式为:
:020000040800F2
:1000000048B6002085010008B1450008252B0108ED
:10001000B9450008C1450008C945000800000000B6
:00000001FF
四、简单的汇编点灯程序
1、程序中包含了启动文件内容,新建工程不添加启动文件
2、测试代码
LED0 EQU 0x42218194 ;LED0 (PB5)的bit-bond地址
RCC_APB2ENR EQU 0x40021018
GPIOB_CRL EQU 0x40010C00
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
BL LED_Init
MainLoop BL LED_ON
BL Delay
BL LED_OFF
BL Delay
B MainLoop
LED_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x08 ;使能GPIOB
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOB_CRL
BIC R0,R0,#0XFF0FFFFF ;配置为模拟输入
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_CRL
ORR R0,R0,#0X00300000 ;推挽输出
LDR R1,=GPIOB_CRL
STR R0,[R1]
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_ON
PUSH {R0,R1, LR}
MOV R0,#0
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF
PUSH {R0,R1, LR}
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,#0
MOVS R1,#0
MOVS R2,#0
DelayLoop0
ADDS R0,R0,#1
CMP R0,#330
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#330
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
END
添加新的汇编文件,放入以上代码,编译下载。
3、代码简介
(1)预定义
每个需要用到的寄存器地址定义一个名字,类似于C语言的#define。bit-bond地址可查询标准库的sys.h文件中的计算方法,RCC和GPIO的地址是固定的,可从STM32的手册查询,或者根据ST官方的库文件查找计算。
(2)分配栈空间
(3)分配向量表
(4)开始代码段
通知汇编器,开始代码段。
这段的意思是,汇编器支持THUMB指令,代码段按8字节对齐。
ENTRY命令:声明整个程式的入口点,入口点有且仅有一个。
(5)程序运行
1、BL:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器。
2、B:无条件跳转。
3、PUSH和POP:可以看到,所有的子程序都是由PUSH和POP包起来的。PUSH {R0,R1, LR}的意思即把R0,R1,LR中的值放入堆栈中。由于主程式中使用BL跳转指令,所以LR中的值实际上就是当前PC的值。而POP {R0,R1,PC}的意思即是将栈中之前存的R0,R1,LR的值返还给R0,R1,PC。这样就完成了子程序的返回。
4、LDR和STR:寄存器的装载和存储指令。
LDR是把地址装载到寄存器中(比如R0)。
STR是把值存储到寄存器所指的地址中。
5、ORR和BIC:
ORR 按位或操作。ORR R0,R0,#0x04意思即将R0中的数或上0x04,再将结果送往R0中。实际意思就是将R0的第二位置1,其他位不变。
BIC 先把立即数取反,再按位与。
6、CMP和BCC:CMP是比较两个数,相等或大于则将标志位C置位,否则将C清零。BCC是个组合指令,实际为B+CC,意思是如果C=0则跳转。
CMP R2,#15; 计算R2-15的值,若是R2<15,则C=0;若是R2>=15,则C=1。
BCC DelayLoop0;若是C=0,则跳到DelayLoop0,若是c=1,则不跳转。