基于MDK下的STM32F103纯汇编语言练习

一、汇编语言简介

汇编语言, 即第二代计算机语言,用一些容易理解和记忆的字母,单词来代替一个特定的指令,比如:用“ADD”代表数字逻辑上的加减,“MOV”代表数据传递等等,通过这种方法,人们很容易去阅读已经完成的程序或者理解程序正在执行的功能,对现有程序的bug修复以及运营维护都变得更加简单方便。但计算机的硬件不认识字母符号,这时候就需要一个专门的程序把这些字符变成计算机能够识别的二进制数。因为汇编语言只是将机器语言做了简单编译,所以并没有根本上解决机器语言的特定性,所以汇编语言和机器自身的编程环境息息相关,推广和移植很难,但是还是保持了机器语言优秀的执行效率,因为他的可阅读性和简便性,汇编语言到现在依然是常用的编程语言之一。 汇编语言不像其他大多数的程序设计语言一样被广泛用于程序设计。在今天的实际应用中,它通常被应用在底层,硬件操作和高要求的程序优化的场合。驱动程序、嵌入式操作系统和实时运行程序都需要汇编语言。

二、在Keil下完成一个汇编程序的编写

  • 1、新建工程,点击上方的的Project->New μVision Project… 选择工程存放位置及工程名。
  • 选择芯片

在这里插入图片描述

  • 添加启动文件
    在这里插入图片描述

  • 在左侧Source Group 1文件上右键,选择Add new item…,选择Asm File,然后点击Add
    在这里插入图片描述

  • 添加代码

 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
	END
  • 为了能在MDK中进行仿真,需要进行一些设置,点击魔法棒,选择Debug,Use Simulator,将下方的Dialog.DLL修改成DARMSTM.DLL,Parameter修改成-pSTM32F103C8(根据你选的芯片来写),如果这里不进行修改,仿真调试的时候就会一直循环在在这里插入图片描述
    这是由于我们进行的仿真,没有外部晶振进行起振。
    在这里插入图片描述
  • 开始仿真,点击上方的Start/Stop Debug(或者直接ctrl+F5),然后点击②处的RUN,再点击Stop

在这里插入图片描述

  • 运行结果
    在这里插入图片描述
  • 最终生成 hex文件的各段的大小
    在这里插入图片描述

Code:指程序中代码的字节数
RO-data:指程序中定义的常量字节数
RW-data :程序中已初始化的变量字节数
ZI-Data:程序中未初始化的变量字节数
可计算出flash和RAM的占用情况:
flash = Code + RO-data + RW-data
ram = RW-data + ZI-dat

  • 打开hex文件
    在这里插入图片描述

如图,其中第一行020000040800F2中,可以看做是0x02 0x00 0x00 0x04 0x08 0x00 0xf2,其前四个字节和最后一个字节有特殊含义。中间为数据

第一个0×02表示该行数据中有两个数据

第二个,第三个0x00 0x00表示本行数据的起始地址位

第四个字节有0x00 0x01 0x02 0x03 0x04 0x05,分别有以下含义

'00’Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录

'01’文件结束记录:用来标识文件结束,放在文件的最后,标识HEX文件的结尾**

'02’扩展段地址记录:用来标识扩展段地址的记录

'03’开始段地址记录:开始段地址记录

'04’扩展线性地址记录:用来标识扩展线性地址的记录

'05’开始线性地址记录:开始线性地址记录

最后一个字节0xf2为校验和。
校验和的算法为:
计算0xf2前所有16进制码的累加和(不计进位),检验和 = 0x100 - 累加和

三、 用汇编程序完成每间隔1秒钟闪烁一次LED的程序。

  • 按上面的步骤再建立一个新工程,不用加启动文件,加启动文件的步骤可省略。
  • 主要代码
LED0 EQU 0x422201b4
RCC_APB2ENR EQU 0x40021018
GPIOC_CRH EQU 0x40011004                           ;预定义


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指令,代码段按8字节对齐
                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}			;将R0,R1,LR入栈
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                LDR R0,=GPIOC_CRH
                BIC R0,R0,#0XFF0FFFFF   ;配置为模拟输入模式
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_CRH
                ORR R0,R0,#0X00300000   ;配置为通用推挽输出模式,最大速度为50MHz
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                MOV R0,#1			;将立即数1送入R0.
                LDR R1,=LED0		;将PC13 bit-bond的地址送入R1.
                STR R0,[R1]			;将R0的值,也就是1,送给R1中的值所指向的地址中
             
                POP {R0,R1,PC}      ;将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         ;加法,R0=R0+1
                CMP R0,#330           ;计算R0-330的值,R0<330,则C=0;否则C=1。
                BCC DelayLoop0        ;若是C=0,则跳到DelayLoop0,若是c=1,则不跳转
        
                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 0x422201b4           //PC13的Bit-Bond地址
RCC_APB2ENR EQU 0x40021018    //时钟总线APB2地址
GPIOC_CRH EQU 0x40011004	  //GPIO端口C高位寄存器CRH起始地址

为方便操作,给每个需要用到的寄存器地址定义一个名字。博主的stm32F103核心板的灯在PC13,计算公式:
((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
经过查询stm32中文手册可知,端口C地址为0x4001100C,bitnum=D(16进制),计算过程:
在这里插入图片描述
最终算出PC13端口地址为0x422201b4,后两个地址是固定的,查询stm32手册即可。

2,分配栈空间

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

这一段摘自启动文件。
AREA命令: AREA 命令指示汇编器汇编一个新的代码段或数据段。段是独立的、指定的、不可见的代码或数据块,它们由链接器处理。
格式如下:
AREA 段名,段属性1,段属性2,段属性3。。。

SPACE命令: SPACE 命令保留一个用零填充的存储器块。

所以整段的意思为:分配一个STACK段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。

3,其他命令
ENTRY命令: 声明整个程式的入口点,入口点有且仅有一个。

LDR和STR: 寄存器的装载和存储指令。
LDR是把地址装载到寄存器中(比如R0)。
STR是把值存储到寄存器所指的地址中。

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,则不跳转。

4、PC13端口

 LDR R0,=GPIOC_CRH
BIC R0,R0,#0XFF0FFFFF   ;配置为模拟输入模式
LDR R1,=GPIOC_CRH
STR R0,[R1]
                
LDR R0,=GPIOC_CRH
ORR R0,R0,#0X00300000   ;配置为通用推挽输出模式,最大速率为50MHz

在这里插入图片描述

  • 将代码下载到核心板上,效果展示:
    在这里插入图片描述

四、参考资料

1、HEX文件格式详解:https://blog.csdn.net/weixin_43836778/article/details/108598294
2、简单的STM32 汇编程序—闪烁LED:https://www.cnblogs.com/WeyneChen/p/4860764.html
3、ARM汇编基础之基于MDK创建纯汇编语言的STM32工程:https://blog.csdn.net/u010632165/article/details/106481146
4、STM32中文参考手册_V10

  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值