微处理器与嵌入式设备——ARM基础编程实验

该实验通过Keil开发工具,让学生掌握ARM汇编指令,包括数据处理、内存访问、堆栈指针、程序计数器和程序状态寄存器等,并通过实现累加、字符串拷贝、求和及冒泡排序等函数,理解C与汇编混合编程,强调了函数调用、参数传递和工作模式切换的过程。
摘要由CSDN通过智能技术生成

实验目的

  1. 熟悉并掌握常用ARM汇编指令

  2. 熟悉并掌握"C+汇编"混合编程技术

  3. 熟练使用ARM软件开发调试工具Keil

实验内容

  1. 学习使用Keil开发工具;

  2. 实现累加运算功能;

  3. 实现字符串拷贝功能;

  4. 实现求和运算功能;

  5. 实现冒泡排序算法。

实验步骤

学习使用Keil开发工具

运行Keil,建立工程文件,单步运行调试演示示例程序,深刻理解每一条指令,观察寄存器,内存空间的变化。并且了解软件的使用和调试方法。

  1. 点击"Keil uVision5"打开软件主窗口,点击Project->New uVision
    Project建立新工程,为新工程命名。

  2. 为工程选择目标器件:三星S3C2440A。

  3. 选择是否添加S3C2440.s启动文件:C源程序工程选"是",汇编源程序工程选"否"。

  4. 在工程Source
    Group上点右键选择添加新工程文件,选择源文件类型,录入源代码并保存。

  5. 对工程进行build或rebuild。

  6. 点击debug按钮进入/退出调试,忽略代码大小限制。使用单步调试,仔细观察过程中关键寄存器值的变化。

实现累加运算功能

将实验1.2文件夹中的sum.s文件添加至工程中,根据代码参考流程图补全程序中的内容,然后debug调试,观察结果。

image1

实现字符串拷贝功能

  1. 将实验1.3文件夹中的testfile.s和main.c文件添加至工程中。

  2. 在汇编文件testfile.s中添加两行汇编代码,分别实现:

    a. 拷贝源字符串的一个字节到R2中;

    b. 将拷贝的字节复制到目标空间。

  3. 运行Debug进行调试。

实现求和运算功能

  1. 将实验1.4文件夹中的sum.c和testfile.s文件添加至工程中。

  2. 在汇编文件testfile.s中相应位置添加汇编代码,通过调用c函数g()实现a+b+c+d+e,结果存在R8中。

  3. 运行Debug进行调试,观察实验现象。

实现冒泡排序算法

  1. 将实验1.5文件夹中的maopao.s文件添加至工程中。

  2. 在汇编文件maopao.s中相应位置添加汇编代码,实现冒泡排序。

  3. 运行Debug进行调试。

  4. 在debug界面,点击Debug →Memory Map,修改地址分段属性。

  5. 观察实验现象

实验结果

学习使用Keil开发工具

ARM数据处理指令寻址方式

在ARM中数据处理指令的寻址方式可以大致的分为三类,分别是立即数寻址,寄存器寻址和寄存器移位寻址,在这段示例代码中用到了上述的三种方式,每条代码的作用已经在下图中给出:

我们生成解决方案,然后进行单步调试,最终运行的结果如下所示。

ARM内存访问指令寻址方式

同样的,每段程序的作用已经在注释中标明:

我们生成解决方案,然后进行单步调试,对于出现的无法读取或者写入,可以在memory
map中用如下的方式进行改变,得到部分的运行结果如下所示

指令 LDR R6,[R2],由此也可以看到ARM采用的是小段存储的方式。

指令 STR R3,[R7] 将一个值写入内存

其他的结果都是类似的,我们不再赘述。

ARM堆栈指针SP的变化

同样的,每段程序的作用已经在注释中标明:

我们具体的看两个指令的效果:

指令 STMFA R13!,{R2-R5}

指令 STMEA R13!,{R1-R5}

从这里就可以看出满递增和空递增的区别,其他的不再赘述。

ARM程序计数器PC的变化

关键代码的作用如注释所示,由于前边的都是数据的存取指令,我们这部分探究的是PC的变化,所以前边的指令我们不再分析,直接从LOOP部分开始,关键部分代码的作用已经在注释中标明。

ARM程序状态寄存器PSR的变化

CPSR中的各个字段的含义如下所示:

  1. N:设为指令结果的第31位,如果结果当作二进制补码表示的有符号整型,N=1表示结果是负的;N=0表示结果是非负的。

  2. Z:指令结果是0的情况下设置为1,反之设为0。

  3. C:如果加法运算产生无符号溢出,C设为1,反之设为0;如果减法运算产生一个借位,C就设置为1,反之设为0;对于包含移位操作的非加减运算,C设为移位器移出的最后一位。

  4. V:如果发生符号溢出,设置为1。

  5. A:abort中断禁止位

  6. I:被置位后禁止IRQ中断

  7. F:被置位后禁止FIQ中断

  8. M的五位是工作模式,共有七种

    代码的含义已经在注释中给出,如下所示:

部分指令运行产生的变化如下所示:

指令 SUBS R3,R2,R1

即指令的运算结果为0,并且产生了一个借位

指令 ADDS R7,R5,R2,LSL #0x4

即没有产生任何的异常

指令 CMP R1,R2

  1. 对于加法,包括比较指令CMN,如果加法运算产生无符号溢出,C设为1,反之设为0。

  2. 对于减法,包括比较指令CMP,如果减法运算产生一个借位,C就设置为1,反之设为0。

ARM工作模式的切换

ARM共有如下的其中工作模式,是由CPSR中的5位M字段来指定的,对应的值和模式对应的功能如下所示:

在这里插入图片描述

进入每个模式的方式都是类似的,每种模式能访问的寄存器也已经在表中给出,我们只说明其中的一种模式:

指令 MSR CPSR_cxsf,R0 进入system模式 M变为0x1F 如下所示

实现累加运算功能

在原先的程序上,我们补全一部分内容,每段指令的作用已在注释中给出。

最终运行结果如下所示,0x13BA对应十进制的5050,结果正确:

实现字符串拷贝功能

将给出的c代码和汇编代码导入到程序中,然后补全代码如下所示,关键语句的作用已在注释中标明。

下边给出的是汇编代码,同样的关键语句的作用已在注释中标明。

然后我们在监视中添加dststr和srcstr,观察运行前后值的变化,下两图分别是运行前和运行后dststr的变化。

实现求和运算功能

将给出的c代码和汇编代码导入到程序中,然后更正示例程序中的代码。

下边给出的是汇编代码,同样的关键语句的作用已在注释中标明。

然后我们调试程序,同样的设置监视在c代码中观察传入的参数。

随后调用结束,返回汇编程序,寄存器中的值如下所示,返回值存在R0中,其值为0x1E,即对应十进制的30,计算结果正确。

实现冒泡排序算法

冒泡排序是一种简单的排序方法,每次循环能将最大的元素"下沉"到数组的尾部,就避免了对后边元素的再次排列,其程序框图可用下图表示。

D:\QQ接收的文件\MobileFile\IMG_20210416_091516.png

在这样的思想基础上,我们补充汇编程序如下所示,同样的关键代码的作用已经在注释中标明。

随后我们点击Debug调试程序,在地址为0x40000000的地址中手动填入数据,如下所示。

随后单步调试,观察程序运行中的变化,如下所示是程序在结束第一轮外循环后,数组的变化,可以看到,最大的0x14已经被放在了数组的末尾。

最终程序运行的结果如下图所示:

实验总结

在实验1中,我们探究了ARM的基本工作方式,了解了他们的数据处理指令寻址方式,内存访问指令寻址方式,堆栈指针SP的使用,程序计数器PC的工作方式和子程序的返回和调用,程序状态寄存器PSR的使用和工作模式的切换。

在实验2中,我们利用纯汇编的方式完成了累加的操作,掌握了如何控制循环的方式。

在实验3中,我们用C调用汇编,要在在C中声明函数原型,并加extern关键字;在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识。其中EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。在汇编的最后用MOV
PC,LR返回主程序。

在实验4中,我们在汇编中调用C的函数,需要在汇编中
IMPORT对应的C函数名。在调用该C函数之前还需要通过汇编语言传递该函数的参数。C和汇编之间的参数传递是通过ATPCS规定行的。如果函数有不多于四个参数,对应的用ARM寄存器RO-R3来进行传递,多于4个时借助栈。函数的返回值通过R0来返回。

在实验5中,我们实现了基于汇编的冒泡排序,设计了相比之前更加复杂的算法,对汇编语言的设计程序有了更好的理解和掌握。

实验思考题

  1. ADD替换成ADDS ,SUB替换成SUBS有什么影响?

运算结果不影响CPSR中相应标志位的值,跳转指令因为上一步的CPSR
的值没有改变而无法正确执行。

  1. MOV替换成MOVNE有什么影响?

只有在上一步计算结果为不相等时才执行。

  1. STMIA换成STMIB ,STMIA换成STMDA有什么区别?

第一个是将每次传送后地址加4改每次传送前地址加4,第二个是将每次传送后地址加4改为每次传送后地址减4

  1. 思考用ARM汇编实现1+3+5+…+(2n+1)或者2+4+6+…+2n。

  1. 实验3中如果去除汇编代码中的"EXPORT
    strcopy"会有什么现象,为什么?

C语言无法调用用strcopy函数。因为EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。

  1. 实验4中如果去除汇编代码中的"IMPORT …" 会有什么现象,为什么?

无法调用c语言的main函数。因为IMPORT伪指令用于通知编译器要使用的标号或变量在其他的源文件中定义。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值