第一篇文章,将自己的微机原理课程实验报告放在这里,给有需要的人参考
一、任务要求
1. 设有8bits符号数X存于外部RAM单元,按以下方式计算后的结果Y也存于外部RAM单元,请按要求编写程序。
2. 利用51系列单片机设计一个24小时制电子时钟,电子时钟的时、分、秒数值分别通过P0、P1、P2端口输出(以压缩BCD码的形式)。P3.0为低电平时开始计时,为高电平时停止计时。设计1s延时子程序(延时误差小于10us,晶振频率12MHz)。
提高部分(选做):
a. 实现4位十进制加、减1计数,千位、百位由P1口输出;十位、个位由P2口输出。利用P3.7状态选择加、减计数方式。
b. 利用P3口低四位状态控制开始和停止计数,控制方式自定。
二、设计思路
1.用不断比较大小的方式实现三分支结构。首先通过MOVX指令将片外RAM数据传送到累加器A中,并将结果备份到寄存器R0中。根据A的最高位是正还是负,进行第一次跳转,若A为负,则直接执行取反的程序,若A为正,再通过用A减去40后,若Cy位为零,则执行第一个分支程序,若Cy位为1,则跳转到SWL程序。在SML程序中,再判断X与20的大小,从而执行相应的处理程序。通过几次比较,便得到了A属于哪个范围区间并执行相应程序。最后再将存储在A、B中的程序转移到片外RAM中。
2.程序的编写分为主程序和子程序,主程序用于实现正确的时分秒的输出,子程序用于实现一秒延时。首先通过对P3.0高低电平的判断进行计时或停止,当进行计时,先调用一秒延时子程序,再进入输出时分秒的程序,然后回到对P3.0高低电平判断的程序。在一秒延时子程序中,利用三重循环结构,采用了软件方法来进行延时。在时分秒输出的部分中,延时一秒后对秒数进行加一,然后进行BCD码转换,之后再判断是否满60秒,若满60秒,则对秒数清零,然后分钟数加一,再判断是否满60分钟;若不满60秒,则返回主程序之初。从分钟数到时钟数的进位以此类推。
3.(提高题)程序采用P3端口的低四位和第七位作为控制信号,当P3端口低四位全为零时开始计数,否则等待暂停。首先通过循环等待程序判断P3端口低四位是否全为零,若全为零,则再根据P3.7的高低电平来进入不同的程序。P3.7为1则加法计数,为0则减法计数。对于加一计数,先对个位进行加一,BCD码转换后,通过CY将十位的进位加到百位中;对于减一计数,首先判断P2是否为零(个十位是否为零),若不为零则加99H再BCD码来实现减一运算,若P2为零,则直接赋值99H;然后通过P1对百千位进行判断,若不为零则通过和前面同样方法置为零,若为零则直接加99H。
三、资源分配
1.需要用到DPTR来做寄存器间接寻址,用A,B存放中间运算结果,用Cy位来判断,用R0备份X。片外RAM的0000H单元中存放X,片外0001H和0002H单元存放运算结果。
2.用R0存放小时数,用R1存放分钟数,用R2存放秒钟数。用P0端口输出小时数,用P1端口输出分钟数,用P2端口输出秒数,用P3端口的零号位来控制计时或停止的操作。用R5、R6、R7来实现三重循环结构从而实现延时一秒的要求。
3.需要用到P3端口的低四位和第七位来作为控制信号,需要用到P2来输出个十位,用P1来输出百千位。
四、流程图
1.8位符号数分段计算
2.时分秒时钟程序
3.P1、P2端口计数程序
五、源代码 (含文件头说明、语句行注释)
1. ;signed.asm
;有符号数散转程序,实现分段函数运算
MAIN: MOV DPTR, #0000H
MOVX A, @DPTR
MOV R0, A ;备份数到R0寄存器中
JB ACC.7, QFN ;A为负数则跳到QFN
CLR C
SUBB A, #28H ;A与40相减,通过CY判断大小
JC SML ;Cy为负,说明A小于40,则跳到AML
CLR C
MOV A, R0
MOV B, A
MUL AB ;第一种情况求X平方,积的高八位在A,低八位在B
JIAOHUAN:MOV R1, A
MOV A, B
MOV B, R1 ;交换A,B中的数,使得最终积的高八位在DPTR的低地址,低八位在高地址
LJMP CUN
QFN: CPL A ;取反后跳到CUN程序
LJMP CUN
SML: CLR C ;对X小于40时的处理
MOV A, #14H
SUBB A, R0
MOV A, R0
JNC QFN ;X小于20,跳到QFN程序取反
CLR C
MOV B, #2H
DIV AB ;X在20到40之间,进入第二分支程序
CUN: INC DPTR ;把算出来的值存储到外部RAM中的程序
MOVX @DPTR, A
INC DPTR
MOV A, B
MOVX @DPTR, A
SJMP $
END
2. ;CLK.asm
;用汇编实现时钟计数
ORG 0000H
LJMP MAIN
ORG 2000H
MAIN: MOV SP, #30H ;初始化
MOV R0, #00H ;存时寄存器
MOV R1, #00H ;存分寄存器
MOV R2, #00H ;存秒寄存器
MOV P0, #00H
MOV P1, #00H
MOV P2, #00H
WAIT: JB P3.0, BREAK ;若P3.0为高电平,则一直停留在此处
BEGIN: LCALL DELIS ;若为低电平进入一秒延时子程序
LJMP LOOP
BREAK: SJMP WAIT
LOOP: MOV A, R2 ;秒数计时
ADD A, #01H
DA A
MOV P2, A
MOV R2, A
LOOP1: CJNE R2,#60H,LOOP4 ;计满60秒向分钟数进一
MOV R2, #00H
MOV A, R1
ADD A, #01H
DA A
MOV P1,A
MOV R1,A
LOOP2: CJNE R1, #60H, LOOP4 ;计满60分向时钟数进一
MOV R1, #00H
MOV A, R0
ADD A, #01H
DA A
MOV P0, A
MOV R0, A
LOOP3: CJNE R0,#24H,LOOP4 ;计满24时
MOV R0, #00H
LOOP4: LJMP WAIT ;判断完是否时分秒数是否有进位后回到等待程序
DELIS: MOV R7, #46 ;延时子程序
DE:MOV R6, #88 ;1
DE1:MOV R5, #122 ;1
DE2:DJNZ R5, DE2 ;2
DJNZ R6, DE1 ;2
DJNZ R7, DE ;2
RET ;3+
END
3. ;test.asm
;四位十进制数加减计数
ORG 0000H
LJMP MAIN
ORG 2000H
MAIN: MOV A, P3
ANL A, #0FH
CJNE A, #0, MAIN ;判断P3的低四位是否为零
JNB P3.7, SUBER ;P3.7为零则执行减法,否则执行加法
ADDER: CLR C
MOV A, P2
ADD A, #1 ;个位加一
DA A ;将二进制调整为十进制
MOV P2, A
MOV A, P1
ADDC A, #0 ;十位有进位则百位加一
DA A
MOV P1, A
LJMP MAIN
SUBER: CLR C
MOV A, P2
CJNE A, #0, SUBER1 ;个十位为零则执行下去,否则跳到SUBER1
MOV P2, #99H ;个十位为零,直接置99H,同时向百位借一
MOV A, P1
CJNE A, #0, SUBER2 ;判断百千位是否为零
MOV P1, #99H ;百千位为零,直接置99H
LJMP MAIN
SUBER1: ADD A, #99H ;个位减一
DA A ;二进制转为十进制
MOV P2, A
LJMP MAIN
SUBER2: ADD A, #99H ;百位减一
DA A ;二进制转为十进制
MOV P1, A
LJMP MAIN
END
六、程序测试方法与结果
1.对程序编译后进入调试界面,通过Register窗口观察DPTR、A、B及R0的变化。在Memory 1窗口中输入x:0000H,通过鼠标直接点击内存单元0000H对应的值,即可修改片外RAM的值,分别输入不同区间的值进行运算,运算结果符合预期要求。
2.对程序编译并将晶振频率修改为12MHz后,进入调试界面,打开Peripherals窗口的四个Port窗口,此时发现四个端口均输出高电平。将程序全速运行,可以看到P0-P2窗口均清零了,暂停程序,此时程序停留在WAIT中。继续将程序全速运行,点击P3.0将其置零,P2开始变化,并且计满60后P1变为1,说明程序设计思路正确。将P3.0置高电平,P2端口暂停变化,再暂停程序,此时程序停留在WAIT中,符合预期。最后在子程序的RET处设置断点,将P3.0置低电平,全速运行程序,当程序运行到第一个时间断点后将时间t1复位,
,再将程序全速运行,此时t1为
,误差为12微秒,比要求的10微秒以内多了至少2微秒。这是因为用软件延时1s并不能做到很准确,且由于三重循环结构,使得循环次数的计算也麻烦了起来,只能大致地通过估算和调试确定最接近的数值。
3. 对程序编译后进入调试界面,通过Peripherals窗口将P1-P3的状态显示出来,控制P3端口的低四位和第七位作为控制信号。先将程序全速运行,此时P1与P2无变化,将P3的低四位置零,可以发现P1与P2开始快速变化;改变P3的第七位,转换计数模式,P1与P2仍在快速变化,通过逻辑分析仪来分析P2端口是递增计数还是递减计数。
将P3.7置一为加一计数模式,由波形窗口可以看到P2的波形如下:
将P3.7置零为减一计数模式,P2波形如下:
由测试结果可知,本实验结果符合预期要求
七、实验问题分析与对策
实验问题分析:在第二个实验的延时一秒子程序中采用了软件延时,但精度不高,存在12us的误差。
对策:采用硬件延时,使用单片机里的定时器外设,能更大地提高定时的精度。
思考题
1.实现多分支结构程序的主要方法有哪些?举例说明。
1.方法一:采用分支地址表:
MOV DPTR, #BRATAB ;取表首地址
MOV A, R3
ADD A, R3 ;A<-R3*2
JNC NADD
INC DPH
NADD: MOV R4,A ;暂存A
MOVC A, @A+DPTR ;取分支地址高8位
XCH A, R4
INC A
MOVC A, @A+DPTR ;取分支地址低8位
MOV DPL, A ;分支地址低8位送DPL
MOV DPH, R4 ;分支地址高8位送DPH
CLR A
JMP @A+DPTR ;转相应分支程序
BRATAB:DW SUBR0 ;分支地址表
DW SUBR1
……
DW SUBR7
方法二:采用转移指令表
MOV DPTR, #JMPTAB ;取表首地址
MOV A,R3
MOV A,R3
JNC NADD
INC DPH ;有进位加到DPH
NADD: JMP @A+DPTR ;转相应分支程序
JMPTAB:AJMP SUBR0 ;转移指令表
AJMP SUBR1
……
AJMP SUBR7
方法三:采用地址偏移量表:
MOV DPTR, #DISTAB ;取表首地址
MOV A,R3 :表的序号数送A
MOVC A, @A+DPTR ;查表
JMP @A+DPTR ;转相应分支程序
DISATAB:DB SUBR0-DISTAB ;地址偏移表
DB SUBR1-DISTAB
……
DB SUBR79-DISTAB
……
SUBR0: ……
SUBR1: ……
2.在编程上,十进制加1计数器与十六进制加1计数器的区别是什么?怎样用十进制加法指令实现减1计数?
答:由于数据在单片机中以二进制的形式保存,所以数据在单片机中只能完成十六进制加1计数器,用INC指令即可实现。而十进制加1计数器,则需低位满10进1,在编程上,需要用ADD A, #01H和DA A两条指令来实现。若要用十进制加法指令实现减一计数,可用ADD A, #99H和DA A指令进行进制调整实现。