kei创建第一个STM32的汇编程序

前言:  

      最近从学校实验室拿到了WarShip STM32F1的开发板,想着趁着疫情被封在宿舍里系统得学习下STM32的Cortex-M3。众所周知,学单片机如果不去学汇编等于没学。在我看《Cortex-M3权威指南》这本书的时候,完全被32位机器指令系统的复杂程度给震撼到了。

        我原来只学过51单片机,所以在学Cortex-M3架构时感到非常吃力,于是就想像51单片机一样在电脑上测试Cortex-M3(以下简称为CM3)的指令。我在网上找了好多教程,但都是没有说出完整的流程。我在仔细研究了各位先驱的方法和官方给的start-up文件后,找到了下面这个方法。

        本文内容完全属于个人学习总结以及个人的理解和看法,在学习过程中借鉴了很多来自于网络的资料。我自己的水平有限,里面难免回出现错误,如遇到还请您不吝赐教。

        我默认您对CM3有一定的了解,并且具备一定的基于CM3的C语言开发经验。文中所使用的芯片为STM32F103ZE,所使用的环境为keil5。

1.创建第一个汇编工程

        创建工程的步骤与平常创建工程的步骤并没有太大的区别。对于工程的目录,大家都有不同的习惯,我这里就不再赘述。

 工程名称随便写一个就行。

 接下来选择对应的芯片。

 注意在接下来这个配置环境窗口,要按照以下勾选,否则在编译时会报错。勾选上之后点下方的ok即可。

 接下来我们要对可以了的debug模式进行设置,以免在编译完成后无法正常的进入的debug模式。

首先点击这个魔法棒的标志.

在新打开的窗口中找到debug标签,然后将Use Simulator 选项给勾选上,下方的Run to main()也勾选上。下方的Dialog DLL:中的内容换成DARMSTM.DLL,将Parameter:下的内容换成-pSTM32F103ZE。如下图所示

2.编写汇编程序

接下来我们就能在上面写代码了。在这里我想先放上一个乍一看正确但是确实错误的写法。了解这个写法为什么是错误的对于我们理解CM3芯片的启动过程有着不可或缺的作用。

AREA REST,CODE,READONLY
		ENTRY
		
START	PROC
		EXPORT START
		MOV R0,#1
		MOV R1,#2
		MOV R2,#3
		
		ENDP
		
		END

 

在这里解释下上面这短短的几行汇编代码。

1.第一行AREA REST,CODE,READONLY 

这一行在汇编中其实应该被称为伪指令,这几行代码的意思是定义一个段【section】,这个段是汇编语言组织代码的基本单位(应该只是在STM32上才有的,写51汇编时完全没有这一说),这个段有点类似于C语言中的函数。AREA为伪指令,表示声明一个段,这个段的名称为RESET。而RESET段是默认的入口,所以在汇编程序中有且只有一个RESET段。CODE表示这个段的属性,表示当前的段是代码段。相应的,表示段的属性的还有DATA、STACK、HEAP等,它们分别表示数据、栈和堆。READONLY表示这个代码段的访问属性为只读,当然还有表示允许读写的READWRITE。

2.第二行ENTRY

该表示程序的入口,与C语言的main相同,但是汇编允许程序有多个入口,这个以后再说。

3.第三行START PROC

这一行的START PROC 与第十行的ENDP成对出现,表示定义的一个子程序,也可以说是子函数。START为子程序的标号,你可以叫任何名字,但是标号一定要顶格写,编译器会将任何顶格写的当做子函数的入口。这里简单介绍下ARM的汇编规定,ARM汇编规定:标号一定要顶格写,而指令、伪指令、伪操作等指令码的前面一定要有前导空格。在书写是我们为了方便还有美观,一般有一个或是多个TAB代替。

4.第五行EXPORT START

这一行是一个链接属性的声明的语句,表示START这个标号和其对应的子函数在其他的汇编语言文件中也能够被调用。调用的时需要对这个标号进行导入,导入的方法为IMPORT START。EXPORT与IMPORT一般也是成对出现的,只有用EXPORT声明过后的标号才能使用IMPORT调用。

5.第十二行END

END也是个伪指令,用处是告诉编译器,END之后的所有都不在编译。一般用END表示该汇编文件的结束。

现在我们对我们刚才写好的汇编程序进行编译了。

这时会报出一个错误,编译器说我们没有指定主函数,这个简单我们再添加一个C语言代码,在代码中写上个空白的主函数。就像下面这样:

 

此时要注意,还记得我们在汇编程序中写的 ENTRY吗,ENTRY就是程序入口,现在在C语言中还有一个main,这就代表着我们定义了多个入口。这时候,我们将汇编文件中的ENTRY语句给删除。再进行编译。

这时候编译器就不再报错了,但我们是否真的写对了还需要验证。接下来打开debug仿真。 

然后我们就发现我们的程序跑飞了,我们想修改的寄存器的值都不正确。这时候就需要我们仔细研究下CM3的启动以及程序执行过程了。

3.CM3的启动过程 

对于51单片机,在启动是直接从0x0000H开始执行的,然后直接读到一个跳转指令,直接跳到主函数中进行执行。大部分芯片也是这样的,从0X00000000H进行执行,进入系统得RESET中断,进行一些初始化工作,然后跳转到主函数进行执行。但是,CM3却不是这样的。

 上图为CM3的中断向量表,我们可以看到,CM3在0X00H中存放的不是RESET中断的入口,而是其他的一些东西。里面存放的为MSP即为系统的主堆栈指针。在复位后CPU会首先将0X00000000H的值给主堆栈指针MSP。我们都知道这个CM3有两个堆栈MSP和PSP,这两个堆栈指针的寄存器的名字都为R13,只是在处理器切换模式时自动切换堆栈。那么在我们按下复位后CPU为什么要这样做。这是因为,在CM3架构的CPU复位后系统进入的为特权级别的Handler模式,Handler模式有些必须使用MSP。为了能够正常初始化,所以CPU先在0X00000000H处将MSP进行初始化。这也就是说我们的程序将PC指针赋予了错误的值,为了解决这个问题,我们需要看下start_up.sl里面的中断向量表。

 我们发现,在复位后,CPU执行了两个操作。一个是SystemInit另一个是完全不存在的__main程序(或者说是空白的)而不是去执行我们写的START函数。既然CPU在执行完SystemInit之后去执行__main,那么我们在__main中多怕用我们自己写的函数或者是说直接把我们的函数的标号改成__main就行了。这时候,我们的汇编文件中就要表明了主函数的入口,那么C语言写的空白的主函数就完全没有任何用处了,直接删掉就行了。在最后一行记得加上B .以防代码跑飞。

	AREA REST,CODE,READONLY
		ENTRY
	
__main	
		EXPORT __main
		MOV R1,#1
		MOV R2,#2
		MOV R3,#3
		B .
		ENDP
		
		
		END

结果如下,这时寄存器的值就是我们想要的正确值了。

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值