汇编实现LED灯闪
1. 本文目的
基于汇编语言实现最简单的LED灯闪烁。
汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。汇编的作用有很多,这里我们更偏终于对高级语言的理解,高级语言C语言、C++语言有很多概念,如果我们懂汇编,看懂每一行代码编译器生成的汇编代码,我们就能知道这行代码计算机在做什么,从本质上理解高级语言。同时,启动代码使用的也是汇编语言,汇编是高手的必经之路。
2. 硬件平台
- 开发板CPU: STM32F103RBT6
- 开发板板载:
- W25Q32 : SPI1(PA4 PA5 PA6) CS(PA2)
- 24C02 (PA4 PA5 PA6)CS(PA3)
- SD卡: SPI1
- LED0: PA8 LED1: PD2 LED2: PC12
- KEY_WKUP:PA0 KEY0:PA13 KEY:PA15
- DS18B20: PA0
- HS0038: PA1
3. 软件开发环境
MDK uVision V5.24.2.0
4. 实现步骤
- 新建文件夹 test ,在此处新建 mdk 工程
- 新建工程 工程位置
- 选择CPU
- 添加cortex的接口标准
- 复制代码添加后编译下载运行
;led.s
LED0 EQU 0x422101a0
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;AREA命令:堆栈段 不初始化 可读写 8字节对齐
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 ;汇编器支持THUMB指令
REQUIRE8 ;该文件标识为REQ8属性
PRESERVE8;汇编文件是8字节对齐
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}
END
END
5. 代码分析
1)首先分配一个STACK段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。
2)DCD指令:用于分配一片连续的字存储单元(32bit),并将表达式的值初始化给该字存储单元,类似于C中定义数组并初始化。
3) 开始代码段
AREA |.text|, CODE, READONLY THUMB REQUIRE8 PRESERVE8 这段的意思是,汇编器支持THUMB指令,代码段按8字节对齐。
ENTRY命令:声明整个程式的入口点,入口点有且仅有一个。不管哪种语言,编译器都得有个入口点。
LR 是连接寄存器(Link Register, LR),在ARM体系结构中LR的是用来保存子程序返地址
6. 汇编的基本语法
- BL:带链接的跳转指令。
- B:无条件跳转。
- PUSH和POP:压栈和出栈。
- LDR是把地址装载到寄存器中(比如R0)。
- STR是把值存储到寄存器所指的地址中。
- ORR:按位或操作。
- BIC: 先把立即数取反,再按位与。
ORR R0,R0,#0x04 ;即将R0中的数或上0x04,再将结果送往R0中。实际意思就是将R0的第二位置1,其他位不变。
- 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,则不跳转。 例如 将PA8置1。
MOV R0,#1 ;将立即数1送入R0. LDR R1,=LED0;将PA8 bit-bond的地址送入R1. STR R0,[R1];将R0的值,也就是1,送给R1中的值所指向的地址中,也就是PA8的地址。 以上代码段相关指令的介绍结束 。
7. 出现的问题及解决方法
编译后警告:
.Objectsproject.sct(8): warning: L6314W: No section matches pattern *(InRoot$$Sections). 以下转载自 keil mini2440 分散加载文件scatter中(InRoot$$Sections)的理解
分析:
(InRoot Sections)实现对映像的加载,这一段代码就是∗(InRoot" role=“presentation”>Sections)实现对映像的加载,而这一段代码就是∗(InRoot Sections)是__main()的一部分。如果只用汇编代码,没有 C 代码,则这一段加载应该去除。
从启动代码说起
启动代码
1。异常/中断跳转的地址表。
2。堆栈初始化
3。分散加载镜像文件
IMAGE(映像文件)
1个RO,
1个RW,
1个ZI组成。
并且RO的load region和execution region相同,这个里面放置
*(InRoot$$Sections)
主要作用COPY RW区到RAM,然后再RW区后面创建ZI区。
库函数__main函数中有这个段。
,则这一段加载应该去除。
从启动代码说起
启动代码
1。异常/中断跳转的地址表。
2。堆栈初始化
3。分散加载镜像文件
IMAGE(映像文件)
1个RO,
1个RW,
1个ZI组成。
并且RO的load region和execution region相同,这个里面放置
*(InRoot$$Sections)
主要作用COPY RW区到RAM,然后再RW区后面创建ZI区。
库函数__main函数中有这个段。
注释掉"…InRoot…",如图示,则编译不再出现警告。
汇编实现LED灯闪
1. 本文目的
基于汇编语言实现最简单的LED灯闪烁。
汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。汇编的作用有很多,这里我们更偏终于对高级语言的理解,高级语言C语言、C++语言有很多概念,如果我们懂汇编,看懂每一行代码编译器生成的汇编代码,我们就能知道这行代码计算机在做什么,从本质上理解高级语言。同时,启动代码使用的也是汇编语言,汇编是高手的必经之路。
2. 硬件平台
- 开发板CPU: STM32F103RBT6
- 开发板板载:
- W25Q32 : SPI1(PA4 PA5 PA6) CS(PA2)
- 24C02 (PA4 PA5 PA6)CS(PA3)
- SD卡: SPI1
- LED0: PA8 LED1: PD2 LED2: PC12
- KEY_WKUP:PA0 KEY0:PA13 KEY:PA15
- DS18B20: PA0
- HS0038: PA1
3. 软件开发环境
MDK uVision V5.24.2.0
4. 实现步骤
- 新建文件夹 test ,在此处新建 mdk 工程
- 新建工程 工程位置
- 选择CPU
- 添加cortex的接口标准
- 复制代码添加后编译下载运行
;led.s
LED0 EQU 0x422101a0
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;AREA命令:堆栈段 不初始化 可读写 8字节对齐
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 ;汇编器支持THUMB指令
REQUIRE8 ;该文件标识为REQ8属性
PRESERVE8;汇编文件是8字节对齐
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}
END
END
5. 代码分析
1)首先分配一个STACK段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。
2)DCD指令:用于分配一片连续的字存储单元(32bit),并将表达式的值初始化给该字存储单元,类似于C中定义数组并初始化。
3) 开始代码段
AREA |.text|, CODE, READONLY THUMB REQUIRE8 PRESERVE8 这段的意思是,汇编器支持THUMB指令,代码段按8字节对齐。
ENTRY命令:声明整个程式的入口点,入口点有且仅有一个。不管哪种语言,编译器都得有个入口点。
LR 是连接寄存器(Link Register, LR),在ARM体系结构中LR的是用来保存子程序返地址
6. 汇编的基本语法
- BL:带链接的跳转指令。
- B:无条件跳转。
- PUSH和POP:压栈和出栈。
- LDR是把地址装载到寄存器中(比如R0)。
- STR是把值存储到寄存器所指的地址中。
- ORR:按位或操作。
- BIC: 先把立即数取反,再按位与。
ORR R0,R0,#0x04 ;即将R0中的数或上0x04,再将结果送往R0中。实际意思就是将R0的第二位置1,其他位不变。
- 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,则不跳转。 例如 将PA8置1。
MOV R0,#1 ;将立即数1送入R0. LDR R1,=LED0;将PA8 bit-bond的地址送入R1. STR R0,[R1];将R0的值,也就是1,送给R1中的值所指向的地址中,也就是PA8的地址。 以上代码段相关指令的介绍结束 。
7. 出现的问题及解决方法
编译后警告:
.Objectsproject.sct(8): warning: L6314W: No section matches pattern *(InRoot$$Sections). 以下转载自 keil mini2440 分散加载文件scatter中(InRoot$$Sections)的理解
分析:
(InRoot Sections)实现对映像的加载,这一段代码就是∗(InRoot" role=“presentation”>Sections)实现对映像的加载,而这一段代码就是∗(InRoot Sections)是__main()的一部分。如果只用汇编代码,没有 C 代码,则这一段加载应该去除。
从启动代码说起
启动代码
1。异常/中断跳转的地址表。
2。堆栈初始化
3。分散加载镜像文件
IMAGE(映像文件)
1个RO,
1个RW,
1个ZI组成。
并且RO的load region和execution region相同,这个里面放置
*(InRoot$$Sections)
主要作用COPY RW区到RAM,然后再RW区后面创建ZI区。
库函数__main函数中有这个段。
,则这一段加载应该去除。
从启动代码说起
启动代码
1。异常/中断跳转的地址表。
2。堆栈初始化
3。分散加载镜像文件
IMAGE(映像文件)
1个RO,
1个RW,
1个ZI组成。
并且RO的load region和execution region相同,这个里面放置
*(InRoot$$Sections)
主要作用COPY RW区到RAM,然后再RW区后面创建ZI区。
库函数__main函数中有这个段。
注释掉"…InRoot…",如图示,则编译不再出现警告。
举报