8086_i386试验箱综合设计实验

完成于 2018-3-27 22:22:03
tags:

  • 汇编
  • 计算机原理

文章目录

  • ​​8086_i386试验箱综合设计实验​​
  • ​​设计要求​​
  • ​​设计分析及系统方案设计​​
  • ​​程序结构​​
  • ​​算法描述​​
  • ​​开关控制步进电机的转速、转向​​
  • ​​8*8点阵显示​​
  • ​​六位数码管显示​​
  • ​​定时指示​​
  • ​​硬件电路图​​
  • ​​8255​​
  • ​​8253​​
  • ​​数码管​​
  • ​​程序流程图​​
  • ​​程序清单​​
  • ​​实验结果与分析​​
  • ​​系统运行环境​​
  • ​​调试遇到的问题​​
  • ​​对应的解决办法​​
  • ​​系统运行结果​​
  • ​​结论及设计体会​​

设计要求

  1. 利用实验台上的开关(K7-K4),实现步进电机的转速、转向控制。具体要求如下:
  • 利用8255的PC3-PC0做输出,输出步进电机的相序、驱动步进电机工作(同时使用四个LED监视步进电机的相序信号),相序之间的时间决定着步进电机的转速,而间隔时间由延时程序中的CX寄存器的初值决定。
  • 利用8255的PC7-PC4做输入,与K7-K4连接。其中K7做步进电机的转向控制,其余位做步进电机的转速控制。程序运行时通过K7-K4对步进电机实施动态控制。
  • 利用8253做秒脉冲发生器,产生约2秒的周期性方波信号。其中CNT0做分频器:将1MHZ信号分频为100HZ;CNT1做秒脉冲输出(0.5HZ)。
  • 利用386模块的主8259的MIR5做中断请求输入,将CNT1的OUT1秒信号方波作为中断请求信号,引发中断服务ISR。
  • 在中断服务程序中实现对步进电机的转速、转向实时记录存储。方法如下:在ISR中,对D8255的PC7-PC4口进行一次输入操作,并根据输入的数据:
  • 对D7(与K7对应)位的数据识别为步进电机的转向控制;
  • 对D6-D4(与K6-K4对应)位的数据识别为步进电机的转速控制。
  1. 利用实验台上的8*8点阵显示电路实现“小人跑步”的模拟。具体要求如下:
  • 利用8255的PA7-PA0与8*8LED点阵显示模块的“行线Ri”连接,控制着每一列的8个LED的显示数据。
  • 利用8255的PB7-PB0与8*8LED点阵显示模块的“列线Ri”连接,列线信号即为“位扫描”信号,决定着8255的PA数据显示在哪一列LED上。
  • 利用8*8LED点阵显示一个小人在运动的动态画面,即显示一个小人四肢在摆动。
  • 实现小人运动的速度与电机转速保持一致。即步进电机转速快,则小人运动快,转速慢,小人运动慢。
  1. 利用实验台上的六位数码管锁存器驱动动态显示具体档位。具体要求如下:
  • 利用六位数码管模块的CPU模式实现功能。
  • 利用锁存器(74LS73)电路实现一位共阴极数码管动态显示档位。
  • 实现六位数码管锁存器驱动动态显示具体档位,即由滑动开关确定档位后,即时的显示对应的档位数字。
  1. 利用试验台的8253完成一个定时提示器。具体要求如下:
  • 在CNT1的基础上,利用8253的CNT2做负脉冲发生器(0.1HZ)。
  • 指定计数初值,实现定时使逻辑笔闪烁,实现提示功能。

设计分析及系统方案设计

程序结构

主要程序组成如下结构图。

8086_i386试验箱综合设计实验_i386

算法描述

开关控制步进电机的转速、转向

步进电机驱动原理是通过对电机每相线圈施加某一顺序电压(电流)来使电机作步进式旋转。驱动电路由脉冲信号来控制,所以调节脉冲的频率便可改变步进电机的转速。由ULN2803八路达灵顿反向驱动器实现电机的电流驱动,只要在ULN2803的输入端(BJ_IN1~BJ_IN4)提供相序信号。经ULN2803反相后实现电机的驱动。ULN2803与步进电机的链接在实验台上已经完成。利用8255的低四位PC端口连接到ULN2803的输入端,输出脉冲序列,从而控制步进电机的旋转。

程序设计中,为了实现更为便捷的操作,使用“双四拍”相序信号。于是将初始相序存放在一个寄存器中(原始相序数据位33H),然后利用对该寄存器“移位”的方式产生下一个相序。这种方式下,输出脉冲的频率决定着步进电机的旋转速度,而对寄存器中的数据移位方向则决定着电机旋转方向。

所以在程序设计中,通过读取8255的PC口的高四位,以PC7作为方向,将其存放在方向数据变量direc,进而控制步进电机旋转方向。而PC6~PC4中数据作为速度参数,这里的速度参数存在程序中的速度数据变量speed中,而它又与步进电机相序输出的延时有关。以这样的方式,实现了对于步进电机的速度的调控。

在这里需要指定8255的工作方式,向8255的控制口输出10001000b,设定PA,PB口为输出,PC高位输入,低位输出。

实际中,还需要利用8253来实现定时引发自定义的中断。对于1MHZ时钟进行两次分频,最终获得周期为2s的输出信号,将这个信号接到MIR5上,定时引发中断。在中断程序中实现对于开关信号的读取和转存到对应的变量direc和speed内的功能。在编制与中断相关的程序时,需要设定中断屏蔽字、中断向量表以及开中断的操作。

在利用PC口进行读写操作时,这里需要注意,由于高四位为输入,低四位为输出,这导致读写数据时会修改了其他端口的数据,这对于程序的正常运行是不利的。所以,需要使用“读改写”的操作,在写数据的时候,先读入数据,再保留高四位的基础上,只写入低四位数据,再利用OUT指令输出。这样就保证了数据的稳定性和程序的健壮性。

8*8点阵显示

这里要实现对于8x8点阵的操作,在连接好8255的PA与行接口,PB与列接口后,通过对PA与PB的写数据,可以实现对具体LED的操作。为了实现小人跑步的模拟,需要先在数据段中准备好至少两份不同的小人动作的图形字符码,即程序中的LED1与LED2。通过利用speed来控制8x8点阵的扫描速度,来实现小人的运动速度的控制。这里利用speed来控制,也有利于点阵变化与步进电机变化的同步,更为合理的实现了“小人跑步”的概念设定。

8*8点阵图案的绘制,首先需要设定最高位扫描码,再通过对8255的PB口输出0,来关闭扫描信号,熄灭led,接下来通过向PA送列码,向PB送对应的列上的行的数据,不断地左移列码,显示行数据,这就是实现了图案从左向右扫描不断地显示。而多次调用,显示不同的图案,从表面上来看,就是实现了小人的运动状态的“模拟”。

六位数码管显示

程序使用的是CPU模式来操作一个七段数码管,来显示对应的数据的档位。对于数码管的操作,需要指定它的地址,这里使用220h,这里实际指定的是字型码锁存器,而扫描码锁存器地址为221h。实际的使用需要向220h中写入对应的字形码,指定显示的数字,而向221h中写入对应的扫描码,指定使用的数码管的位置。

程序设计中,先在数据段中指定0-3的字型码,对应四个档位。利用分支结构,实现了对应的字型码的选择。再对220h与221h中写数据,进而实现对于不同档位的显示。

定时指示

为了实现对于“运动者”定时的提示,利用8253的CNT2手动指定初值,以及工作于方式2,来实现对于CNT1输出的2s的信号定时引发负脉冲的功能,将输出接到逻辑笔上,进而实现了指示功能。

对8253的控制口输出控制字0b4h,指定cnt2的工作方式。实现负脉冲发生器。

硬件电路图

8255

用于配合CPU实现对8x8LED点阵,步进电机以及滑动开关的数据的相关读写操作。初始化命令字为10001000b,PA,PB口链接行列接口,控制8x8LED点阵,PC3~PC0连接BJ_IN4~BJ_IN1以及LED灯,用于控制步进电机和显示相序,PC7~PC4接K7~K0,用于读取开关数据。

8086_i386试验箱综合设计实验_8086_02

8253

用于实现对于基准时钟1MHZ的分频输出2s脉冲引发中断以及指定时间输出到逻辑笔。

8086_i386试验箱综合设计实验_i386_03

数码管

用于显示对应的档位数字。将地址接到230h即可

8086_i386试验箱综合设计实验_汇编_04

程序流程图

8086_i386试验箱综合设计实验_数据_05

8086_i386试验箱综合设计实验_汇编_06

程序清单

```asm
; 2018-3-27 22:57:16
; BY: Lart Pang

.model small
.386

;begin for variable of address
io8253_0 equ 200h
io8253_1 equ 201h
io8253_2 equ 202h
io8253_k equ 203h
p8255_a equ 210h
p8255_b equ 211h
p8255_c equ 212h
p8255_ctl equ 213h
SMG equ 220h
;end for variable of address

data segment
;begin for motor
buf db 0 ;相序存储
speed db 0 ;转速控制
direc db 0 ;转向控制
;end for motor

;begin for 7-seg led
ledcode db 3fh, 06h, 5bh, 4fh ;0-3的对应字形码
;end for 7-seg led

;begin for 8*8 led
led1 db 88h, 44h, 24h, 1fh, 1fh, 24h, 44h, 88h
led2 db 02h, 04h, 84h, 7fh, 7fh, 84h, 04h, 02h
count dw 00h ;查表计数器
bz dw ? ;定义一个位扫描码字的空间
;end for 8*8 led
data ends

ss_seg segment stack
dw 100 dup(0)
ss_seg ends

code segment
assume cs:code, ds:data
start:
cli ;关中断
mov ax, data ;指定数据段
mov ds, ax

;begin 8259 for interruption
in al, 21h ;读IMR寄存器
and al, 11011111b ;开放主片IR5中断
out 21h, al ;装载中断到IMR

push ds ;压栈保存数据
mov ax, 0 ;设置中断向量
mov ds, ax
lea ax, cs:int_proc ;AX指向中断程序入口地址
mov si, 35h ;中断类型码35H
add si, si ;35H*4H找到相应中断向量表中的位置
add si, si
mov ds:[si], ax ;装入自定义中断服务程序IP
push cs ;将CS压栈,用以获取CS地址存到AX中
pop ax
mov ds:[si+2], ax ;装入自定义中断服务程序CS:int_proc
pop ds ;恢复数据
;end 8259 for interruption

;begin 8255 for initialization
mov dx, p8255_ctl ;设定A口方式0输出,B口方式0输出
mov al, 10001000b ;高C口方式0输入,低C口方式0输出
out dx, al
;end 8255 for initialization

;begin for setting the initial phase sequence
mov buf, 33h ;初始相序33h = 0011 0011,这里是用"双四拍"
;相序驱动电机,故实际使用33h
;end for setting the initial phase sequence

;begin 8253 for frequency division
;第一次分频 1M-->1K
mov dx, io8253_k ;cnt0 双数 方式3 二进制
mov al, 36h ;00 11 011 0
out dx, al ;方波发生器,方式3

mov dx, io8253_0 ;cnt0产生1000Hz(1ms)的时钟
mov ax, 1000 ;分高低位写入1000
out dx, al
mov al, ah
out dx, al
;第二次分频 1K-->0.5
mov dx, io8253_k ;cnt1 双数 方式3 二进制
mov al, 76h ;01 11 011 0
out dx, al

mov dx, io8253_1 ;cnt1产生0.5Hz的时钟
mov ax, 2000 ;分高低位写入2000
out dx, al
mov al, ah
out dx, al

;计时 2s->10s
mov dx, io8253_k ;cnt2 双数 方式2 二进制
mov al, 0b4h ;10 11 010 0
out dx, al

mov dx, io8253_2 ;cnt2产生指定时间的负脉冲
mov ax, 5 ;分高低位写入指定数据
out dx, al ;这里指定为5
mov al, ah
out dx, al
;end 8253 for frequency division

;begin main program
mov dx, p8255_c ;读取8255C口内容
in al, dx ;由于C口既有输入又有输出,所以为了不会干扰
and al, 11110000b ;在写数据的时候,要保证不会改动输入的口的数据
mov bl, buf
and bl, 00001111b ;只写buf的低四位到al里
add al, bl ;这里需要在低四位写数据,所以保留高四位。
out dx, al ;将数据传送

loop_main:
cmp direc, 0 ;比较direc,决定相序正向输出还是反向输出
je direc_0 ;这里的方向控制实际上就是通过改变相序的循环方向
;进而改变的
mov al, buf ;载入相序
ror al, 1 ;循环右移一位
jmp direc_1
direc_0:
mov al, buf
rol al, 1 ;循环左移一位
direc_1: ;移动完成后,进行后续操作
mov buf, al

mov dx, p8255_c ;读取8255C口内容
in al, dx ;由于C口既有输入又有输出,所以为了不会干扰
and al, 11110000b ;在写数据的时候,要保证不会改动输入的口的数据
mov bl, buf
and bl, 00001111b ;只写buf的低四位到al里
add al, bl ;这里需要在低四位写数据,所以保留高四位。
out dx, al ;将数据传送

sti ;开中断

;begin 8*8 led & 7-seg digital tube
mov di, offset led1 ;设di为字型码缓冲区(指向首地址)
call display
mov di, offset led2 ;设di为字型码缓冲区(指向首地址)
call display
call disp_shumaguan
;end 8*8 led & 7-seg digital tube

jmp loop_main
;end main program

;begin for interrupt service program
int_proc proc far
push ax ;保护现场
push cx
push dx

mov dx, p8255_c ;读8255C口,修改方向
in al, dx ;读取端口信息
and al, 80h ;1000 0000保留方向位
mov direc, al ;将方向存入方向存储单元
mov dx, p8255_c ;再读8255C口,修改速度
in al, dx ;读取端口信息
and al, 01110000b ;0111 0000保留速度档位
mov speed, al ;将速度档位信息存入存储单元

mov al, 20h ;发EOI结束命令
out 20h, al

pop dx ;恢复现场
pop cx
pop ax
sti ;开中断
iret
int_proc endp
;end for interrupt service program

;begin for 8*8 led display
display proc
push bx ;保护现场
push ax
push dx

mov count, 0000h ;计数器初始值0
mov bh, 01h ;产生最高位扫描码
mov al, 0 ;扫描码消失(熄灭)
mov dx, p8255_b ;指向b口
out dx, al ;输出扫描码熄灭数码管
again:
mov byte ptr bz, bh ;将位扫描码字节送存储空间bz
push di ;保存变量指针
add di, count ;修改变量数据指针(基地址+偏移量)
mov bl, byte ptr [di] ;查表得到变量数据送bl
pop di ;恢复变量指针
mov al, bl
mov dx, p8255_a ;指向a口
out dx, al ;输出扫描码
mov al, byte ptr bz ;使相应的数码管亮
mov dx, p8255_b
out dx, al ;显示字型
inc count
push cx ;保护CX
mov dh, speed ;设定延时
dh_0_disp:
mov cx, 30h ;设定循环延时次数
loop_disp:
loop loop_disp
dec dh
jnz dh_0_disp
pop cx ;恢复CX

mov al, 0 ;扫描码消失(熄灭)
mov dx, p8255_b ;指向b口
out dx, al ;输出扫描码熄灭数码管
mov bh, byte ptr bz ;取出扫描码
shl bh, 1 ;将扫描码右移一位
jnz again ;如果不等于0就跳转again

pop dx ;恢复现场
pop ax
pop bx
ret
display endp
;end for 8*8 led display

;begin for 7-seg digital tube
disp_shumaguan proc
push bx ;保护现场
push ax
push dx

mov al, speed ;读取已经存储好的速度信息
cmp al, 01000000b ;第一档
jz al_1
cmp al, 00100000b ;第二档
jz al_2
cmp al, 00010000b ;第三档
jz al_3
;al=0
mov si, 0 ;第0档:最慢,因为其间隔时间最长
jmp next_0
al_1:
mov si, 1
jmp next_0
al_2:
mov si, 2
jmp next_0
al_3:
mov si, 3
next_0:
lea bx, ledcode ;将数码管的字形码地址赋给BX里
mov bl, [bx+si] ;将对应的字形码存放到BL里
mov al, bl
mov dx, SMG ;将数码管的地址存到DX里
out dx, al

mov al, 01h ;设置数码管的位码,这里使用第一个数码管
inc dx ;数码管的位码,在CPU模式下
;要存在数码管地址的下一地址
out dx, al

pop dx ;恢复现场
pop ax
pop bx
ret
disp_shumaguan endp
;end for 7-seg digital tube

code ends
end start ;程序结束
```

实验结果与分析

系统运行环境

  • 硬件:Windows XP 32位、386EX试验箱
  • 软件:HQFC集成开发环境
  • 设计语言:8086汇编

调试遇到的问题

  • 由于使用了8*8点阵,导致只能将8255的接口PC分高低位用于不同的工作方式。在按此,高四位接好滑动开关,低四位接好步进电机后,运行程序,发现显示结果不正常。
  • 在最后,准备利用8253分频配合逻辑笔,实现定时提示功能,代码写好,接口连接好后,发现逻辑笔始终显示低电平。

对应的解决办法

  • 因为在程序中的读写PC口的时候,没有考虑到每次的写操作,是会影响到另外的四个口的数据的。所以在原来向PC口写数据的基础上,进行了进一步的处理,即“读改写”操作,先读回来原来的数据,只修改低四位,改为想要发送的数据,再写回去。这样就避免了互相干扰的问题,最终恢复正常了。
  • 仔细检查代码后,发现,原来是CNT2相应的命令字写错了。对于2号计数器的指定出了错误,应该是0b4h,结果写成了0e4h。这里主要是因为对于16进制数字不熟悉的结果,导致信息错乱,没有输到想要的位置上。

系统运行结果

由下图可见,最终实现了档位指示,小人跑步模拟,步进电机的转动以及定时提示。

8086_i386试验箱综合设计实验_i386_07

结论及设计体会

最终,完成了自己的设计,实现了“跑步机”的模拟。

最终完成的功能有:8*8LED点阵动态显示小人运动,一位七段数码管显示对应的速度档位,而利用滑动开关,实现跑步机(步进电机)的正反转和档位的控制,同时,利用分频实现定时功能,可以在指定时间输出负脉冲,将逻辑笔的指示灯切换,最终实现了定时提示的功能。

这个实验持续了两三周的时间,之前由于有考试,主要的精力没有放在这个上面,只是先做好了步进电机的基本功能,还未拓展点阵和数码管的功能。不过还好,最后还是赶上来了。

这个思路很好,在一个小的,课本的例子的基础上,不断扩展,不断增加更多的功能,这样也让我在不断地学习新的模块的知识,同时不同模块之间的配合,也在考验着我的能力,因为并不是把模块拿来接好就可以用的,总是会遇到各种各样的问题,需要我们不断地取寻找,解决。

这时,渐近式的编程就显得尤为重要。保留好源代码,先修改一部分,测试,再修改,再测试,不断地反复,直到效果满足预期要求,否则就不能拿来用,只能使用原来的未修改的源代码。这里也提示了我们,及时存档的重要性。

总的看来,磕磕绊绊,但还是走到了终点。

本次试验最遗憾的事情就是12864没用上。为了它我花费了半天时间,查资料,咨询老师,研究了半天,但最终也还是也没有用上12864。不过还是感谢老师的指导。

这次实验学到了很多,包括各种模块的使用,接口操作,中断操作,这都是在程序设计中使用到的,以及对于各种问题的调试解决方法,甚至对于编程也有了进一步的认识。汇编这门语言也因此而熟练了许多。感觉获益匪浅。