走马灯实验报告
1、实验目的
1、学会DP-51PRO实验仪监控程序下载、动态调试等联机调试功能的使用;
2、理解和学会单片机并口的作为通用I/O的使用;
3、理解和学会单片机外部中断的使用;
4、了解单片机定时器/计数器的应用。
2、实验设备
PC 机、ARM 仿真器、2440 实验箱、串口线。
3、实验内容
熟悉A RM 开发环境的建立。
使用A RM 汇编和C语言设置G PIO 口的相应寄存器。
编写跑马灯程序。
5、实验原理
走马灯实验是一个硬件实验,因此要求使用DP-51PRO 单片机综合仿真实验仪进行硬件仿真,首先要求先进行软件仿真,排除软件语法错误,保证关键程序段的正确。然后连接仿真仪,下载监控程序,进行主机与实验箱联机仿真。
为了使单独编译的C语言程序和汇编程序之间能够相互调用,必须为子程序间的调用规定一定的规则。ATPCS ,即ARM ,Thumb 过程调用标准(ARM/Thumb Procedure Call Standard),是A RM 程序和T humb 程序中子程序调用的基本规则,它规定了一些子程序间调用的基本规则,如子程序调用过程中的寄存器的使用规则,堆栈的使用规则,参数的传递规则等。
下面结合实际介绍几种A TPCS 规则,如果读者想了解更多的规则,可以查看相关的书籍。
1.基本A TPCS
基本A TPCS 规定了在子程序调用时的一些基本规则,包括下面3方
面的内容:
(1)各寄存器的使用规则及其相应的名称。
(2)数据栈的使用规则。
(3)参数传递的规则。
相对于其它类型的A TPCS,满足基本A TPCS 的程序的执行速度更快,所占用的内存更少。但是它不能提供以下的支持: ARM 程序和T humb 程序相互调用,数据以及代码的位置无关的支持,子程序的可重入性,数据栈检查的支持。而派生的其他几种特定的A TPCS 就是在基本A TPCS 的基础上再添加其他的规则而形成的。其目的就是提供上述的功能。
2.寄存器的使用规则
寄存器的使用必须满足下面的规则:
(1) 子程序间通过寄存器R0~R3 来传递参数。这时,寄存器R0~R3 可以记作A0~A3。被调用的子程序在返回前无需恢复寄存器R0~R3 的内容。
(2) 在子程序中,使用寄存器R4~Rll 来保存局部变量。这时,寄存器R4~R11 可以记作V1~V8。如果在子程序中使用到了寄存器V1~V8 中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在T humb 程序中,通常只能使用寄存器R4~R7 来保存局部变量。
(3) 寄存器R12 用作子程序间s cratch 寄存器,记作I P。在子程序间的连接代码段中常有这种使用规则。
(4) 寄存器R13 用作数据栈指针,记作S P。在子程序中寄存器R13 不能用作其他用途。寄存器S P 在进入子程序时的值和退出子程序时的值必须相等。
(5) 寄存器R14称为连接寄存器,记作LR。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。
(6) 寄存器R15 是程序计数器,记作P C。它不能用作其他用途。
3.参数传递规则根据参数个数是否固定可以将子程序分为参数个数固定的(nonvariadic)子程序和参数个数可变的(variadic)子程序。这两种子程序的参数传递规则是不同的。
(1)参数个数可变的子程序参数传递规则
对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~
R3 来传递参数;当参数超过4个时,还可以使用数据栈来传递参数。
在参数传递时,将所有参数看作是存放在连续的内存字单元中的字数据。然后,依次将各字数据传送到寄存器R0、R1、R2、R3 中,如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递。
(2)参数个数固定的子程序参数传递规则对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同。如果系统包含浮点运算
的硬件部件,浮点参数将按照下面的规则传递:
·各个浮点参数按顺序处理。
·为每个浮点参数分配F P 寄存器。
·分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP 寄存器。第一个整数参数,通过寄存器R0~R3 来传递。其他参数通过数据栈传递。
(3)子程序结果返回规则子程
序中结果返回的规则如下:
·结果为一个32 位的整数时,可以通过寄存器R0 返回。
·结果为一个64 位整数时,可以通过寄存器R0 和R l 返回,依次类推。
·结果为一个浮点数时,可以通过浮点运算部件的寄存器f0、d0 或者s0 来返回。
·结果为复合型的浮点数(如复数)时,可以通过寄存器f0~fN 或者d0~dN 来返回。
·对于位数更多的结果,需要通过内存来传递。
4.C 语言函数和A RM 汇编语言函数间相互调用高级语言函数与汇编语言函数的混合调用也要遵循A TPCS 规则,保证程序调用时参数的正确传递。在汇编程序中使用 EXPORT 伪指令声明本子程序,使其它程序可以调用此子程序,而在C语言程序中使用ex tern 关键字声明外部函数(声明要调用的汇编子程序),即可调用此汇编子程序。下面给出两个例子来介绍函数相互调用。
6、实验步骤
1.编写一段程序,用P1口作为控制端口,使D1区的LED 轮流点亮。原理图如下图。
图3-1 走马灯实验原理图
①仿照实验一的过程,编写程序、汇编、连接、软件仿真,基本达到功能要求。
ORG 0000H
LJMP MAIN
ORG 0100H
MAIN: MOV A,#0FEH ;准备点亮第一只发光管
NEXT: MOV P1,A
ACALL DELAY
RL A ; 准备点亮下一只发光管
SJMP NEXT
; 延时子程序
DELAY: MOV R2,#5
DELAY2: MOV R3,#100
DELAY3: MOV R4,#100
DJNZ R4,$
DJNZ R3,DELAY3
DJNZ R2,DELAY2
RET
END
调试成功以后,将程序中的ORG部分的偏移地址再加上8000H,重新编译。
②按照原理图,将A1区P1口的引线分别连接到D1区J52上。
③下载监控程序到实验仪上。短接A1区的JP15,连接实验仪串口和主机的串口,将MOD-SW1拨到LOAD一边,FLASH-E和SRAM-E断开,运行主机上DPFLASH,下载MON51.HEX到实验仪上。
④在Keil系统上,完成程序的动态调试。在主菜单DEBUG下的Options for Target ‘Target 1’,点击Debug,选择Use Keil Monitor-51 Driver,并且设置其他参数,将MOD-SW1拨到RUN一边,按复位键,再按DEBUG下的全速运行命令运行监控程序后,就可以进行动态调试了。
观察和记录各个寄存器内容的变化,端口P1的内容变化,以及对应的LED 的变化。全速运行时,观察LED的变化和闪烁速度。
7、实验相关寄存器
GPBCON――端口配置寄存器
GPBDAT――端口数据寄存器
GPBUP――端口上拉电阻使能寄存器
8、实验电路图
2.编写一段程序,用P1 口作为控制端口,使D1 区的LED 轮流点亮。用外部中断0控制走马灯的暂停/继续。(此实验也可脱机运行)
ORG 0000H
LJMP MAIN
ORG 0003
LJMP ZEX0
ORG 0100H
MAIN: MOV SP,#60H
SETB IT0
SETB EX0
SETB EA
MOV A,#0FEH ;准备点亮第一只发光管
MOV R5,#00H
NEXT: CJNE R5,#00H,NEXT ;00表示继续,FF表示暂停MOV P1,A
ACALL DELAY
RL A ;准备点亮下一只发光管
SJMP NEXT
; 延时子程序
DELAY: MOV R2,#5
DELAY2: MOV R3,#100
DELAY3: MOV R4,#100
DJNZ R4,$
DJNZ R3,DELAY3
DJNZ R2,DELAY2
RET
;外部中断0服务子程序
ZEX0: PUSH ACC
MOV A,R5
XRL A,#0FFH
MOV R5,A
POP ACC
RETI
END
将A1区P1口的引线分别连接到D1区J52上,将D1区的J53的KEY1连接到A1区的外部中断0上。
◆观察实验结果,解释如何实现走马灯的暂停和继续。
◆比较电平触发和边沿触发时走马灯控制的难易程度。
3.编写一段程序,用P1 口作为控制端口,使D1 区的LED 轮流点亮。用定时/计数器0控制走马灯的闪烁速度。(此实验也可脱机运行)
ORG 0000H
LJMP MAIN
ORG 000BH
LJMP ZIT0
ORG 0100H
MAIN: MOV SP,#60H
MOV TMOD,#01H
MOV TH0,#3CH
MOV TL0,#0B0H
MOV R6,#20 /循环次数
MOV R7,#0FEH ;准备点亮第一只发光管
SETB ET0
SETB EA
SETB TR0
SJMP $
;定时中断0服务子程序
ZIT0: DJNZ R6,ZRET /为零时,跳转!
MOV A,R7
MOV P1,A
RL A
MOV R7,A
MOV R6,#20 /循环次数
ZRET: MOV TH0,#3CH
MOV TL0,#0B0H
RETI
END
◆修改R6=0或1时,观察走马灯的速度快慢。
◆ZRET:处没有重置初值时的走马灯的速度与ZRET处有重置初值时的走马灯的速度的比较。
◆R6=50时,改变T0的初值,设为最大、最小时,比较走马灯的速度。
◆去掉R6的软件辅助延时,是否可以,速度怎样?
9、实验记录
第一个程序走马灯逐步地闪亮
第二个程序当按下D1区k1时, 走马灯暂停, 再按下时就又开始了!
第三个程序把R6值改大了,那间隔时间就增大! 走马灯越来越慢
!
10、实验结果分析
程序运行后,LED 灯D3、D4不断闪烁,跑马灯程序运行。
11、实训分析与总结:
1、简述LED灯点亮的原理
2、观察并描述实验现象,尝试修改参数,使得LED灯变化的更快或者更慢。
3、实验的体会。
实验现象:
1号灯亮→2号灯亮→1、2号灯亮→3号灯亮→1、2、3号灯亮→4号灯亮→4个灯全亮→4个灯全灭→4号灯亮→3号灯亮→2号灯亮→1号灯亮→1、2号灯亮→1、2、3号灯亮→4个灯全亮→4个灯全灭→4号灯亮→3、4号灯亮→2、3、4号灯亮→4个灯全亮→全灭→1、2号灯亮→1、2、3号灯亮→全亮→全灭。
12、实验程序:
/* 定义端口寄存器*/
#define rGPBCON (*(volatile unsigned *)0x56000010) //Port B control
#define rGPBDAT (*(volatile unsigned *)0x56000014) //Port B data
#define rGPBUP (*(volatile unsigned *)0x56000018) //Pull-up control B
/* 延迟程序*/
extern void delay(int times);
/* 主程序*/
void xmain(void)
{
rGPBCON= (rGPBCON & ~(0xff<<10)) |
(0x55<<10);//GPB5-8 set output
rGPBUP = (rGPBUP & ~(0xf<<5)) | (0xf<<5); //disable GPB pull up
while(1)
{ rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<5));//GPB7
output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<6));//GPB7 output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x3<<5);//1.2两个个灯亮
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<7));//GPB7 output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x7<<5);//1.2.3 亮
delay(0x2000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<8));//GPB8
output 0
delay(0x1000000);//调用汇编语言编写的延时程序0
rGPBDAT= rGPBDAT & ~(0xf<<5);//4灯全亮
delay(0x2000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT|(0xf<<5);//全灭
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<8));//GPB8 output 0
delay(0x1000000);//调用汇编语言编写的延时程序0
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<7));//GPB7 output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<6));//GPB7
output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<5));//GPB7 output 0
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x3<<5);//1.2两个个灯亮
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x7<<5);//1.2.3 亮
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0xf<<5);//4灯全亮
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT|(0xf<<5);//全灭
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT=(rGPBDAT|(0xf<<5))& (~(0x1<<8));//4 ok delay(0x1000000);//调用汇编语言编写的延时程序0
rGPBDAT= rGPBDAT & ~(0x3<<7);//3.4两个个灯亮delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x7<<6);//2.3.4三个灯亮delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0xf<<5);//4灯全亮
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT|(0xf<<5);//全灭
delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x3<<5);//1.2两个个灯亮delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0x7<<5);//1.2.3 亮delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT & ~(0xf<<5);//4灯全亮delay(0x1000000);//调用汇编语言编写的延时程序
rGPBDAT= rGPBDAT|(0xf<<5);//全灭
delay(0x1000000);//调用汇编语言编写的延时程序