简单的STM32汇编程序—闪烁LED
要移植操作系统,汇编是道不得不跨过去的坎。所以承接上篇的思路,我准备⽤汇编写⼀个简单的闪烁LED灯的程式。以此练习汇编,为操作系统做准备。
第⼀步,还是和上篇⼀样,建⽴⼀个空的⽂件夹。
第⼆步,因为是要⽤汇编来写程式,所以不需要启动代码,这⾥选择否。
第三步,建⽴⼀个.s⽂件,并把⽂件添加到⼯程中。
第四步,在LED.s⽂件中添加如下代码。
LED0 EQU 0x422101a0
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
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,#0x04
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRH
BIC R0,R0,#0x0F
LDR R1,=GPIOA_CRH
STR R0,[R1]
LDR R0,=GPIOA_CRH
ORR R0,R0,#0x03
LDR R1,=GPIOA_CRH
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}
;
NOP
END
///////////////////////////////////////////////////////
代码的简单讲解
1,预定义
LED0 EQU 0x422101a0 ;PA8的Bit-Bond地址。
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
为⽅便操作,给每个需要⽤到的寄存器地址定义⼀个名字,类似于C语⾔的#define。PA8的Bit-Bond地址的计算⽅法可按上篇⽂章中C语⾔的算法算出。后⾯的两个地址时固定的,可从STM32的⼿册查询,或者根据ST官⽅的库⽂件查计算。
2,分配栈空间
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
这⼀段摘⾃启动⽂件。要读懂这段代码,⾸先要了解两个命令。
AREA命令:AREA 命令指⽰汇编器汇编⼀个新的代码段或数据段。段是独⽴的、指定的、不可见的代码或数据块,它们由链接器处理。格式如下:
AREA 段名,段属性1,段属性2,段属性3。。。
AREA STACK, NOINIT, READWRITE, ALIGN=3
NOINIT: = NO Init,不初始化。
READWRITE : 可读,可写。
ALIGN =3 : 2^3 对齐,即8字节对齐。
SPACE命令:SPACE 命令保留⼀个⽤零填充的存储器块。
所以整段的意思为:分配⼀个STACK段,该段不初始化,可读写,按8字节对齐。分配⼀个⼤⼩为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。
3,分配向量表
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
这⾥的向量可参考我之前写的《》。
4,开始代码段
AREA |.text|, CODE, READONLY
通知汇编器,开始代码段。
THUMB
REQUIRE8
PRESERVE8
这段的意思是,汇编器⽀持THUMB指令,代码段按8字节对齐。
ENTRY命令:声明整个程式的⼊⼝点,⼊⼝点有且仅有⼀个。不管哪种语⾔,编译器都得有个⼊⼝点,这没什么好说的。
5,程序正式开始。
后⾯的代码,皆⽤标准的THUMB2汇编指令。⾸先了解下代码中⽤到的指令。
BL:带链接的跳转指令。当使⽤该指令跳转时,当前地址(PC)会⾃动送⼊LR寄存器。
B:⽆条件跳转。
PUSH和POP:可以看到,所有的⼦程序都是由PUSH和POP包起来的。借⽤⼀张图解释下这两个指令。
据上可知,PUSH {R0,R1, LR}的意思即把R0,R1,LR中的值放⼊堆栈中。由于主程式中使⽤BL跳转指
令,所以LR中的值实际上就是当前PC 的值。⽽POP {R0,R1,PC}的意思即是将栈中之前存的R0,R1,LR的值返还给R0,R1,PC。这样就完成了⼦程序的返回。
LDR和STR:寄存器的装载和存储指令。
LDR是把地址装载到寄存器中(⽐如R0)。
STR是把值存储到寄存器所指的地址中。
举个例⼦:
MOV R0,#1 ;将⽴即数1送⼊R0.
LDR R1,=LED0;将PA8 bit-bond的地址送⼊R1.
STR R0,[R1];将R0的值,也就是1,送给R1中的值所指向的地址中,也就是PA8的bit-bond地址。
上⾯三句话的意思即是将PA8置1。
ORR和BIC:
指示汇编程序如何汇编的指令ORR 按位或操作。ORR R0,R0,#0x04意思即将R0中的数或上0x04,再将结果送往R0中。实际意思就是将R0的第⼆位置1,其他位不变。BIC 先把⽴即数取反,再按位与。
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,则不跳转。
以上就是代码段相关指令的介绍,相信了解了这些指令的含义,要理解代码并不困难。
整个代码的结构和上篇⽤C语⾔写的基本是⼀样的。可参照理解
////////////////////////////////////////////////////
第五步,编译,下载。
编译后,会有⼀个警告 No section matches pattern……可不⽤管。下载后,LED灯正常闪烁。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论