设计要求
利用实验台上的开关(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对应)位的数据识别为步进电机的转速控制。
利用实验台上的8*8点阵显示电路实现“小人跑步”的模拟。具体要求如下:
- 利用8255的PA7-PA0与8*8LED点阵显示模块的“行线Ri”连接,控制着每一列的8个LED的显示数据。
- 利用8255的PB7-PB0与8*8LED点阵显示模块的“列线Ri”连接,列线信号即为“位扫描”信号,决定着8255的PA数据显示在哪一列LED上。
- 利用8*8LED点阵显示一个小人在运动的动态画面,即显示一个小人四肢在摆动。
- 实现小人运动的速度与电机转速保持一致。即步进电机转速快,则小人运动快,转速慢,小人运动慢。
利用实验台上的六位数码管锁存器驱动动态显示具体档位。具体要求如下:
- 利用六位数码管模块的CPU模式实现功能。
- 利用锁存器(74LS73)电路实现一位共阴极数码管动态显示档位。
- 实现六位数码管锁存器驱动动态显示具体档位,即由滑动开关确定档位后,即时的显示对应的档位数字。
利用试验台的8253完成一个定时提示器。具体要求如下:
- 在CNT1的基础上,利用8253的CNT2做负脉冲发生器(0.1HZ)。
- 指定计数初值,实现定时使逻辑笔闪烁,实现提示功能。
设计分析及系统方案设计
程序结构
主要程序组成如下结构图。算法描述
开关控制步进电机的转速、转向
步进电机驱动原理是通过对电机每相线圈施加某一顺序电压(电流)来使电机作步进式旋转。驱动电路由脉冲信号来控制,所以调节脉冲的频率便可改变步进电机的转速。由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点阵显示
这里要实现对于8*8点阵的操作,在连接好8255的PA与行接口,PB与列接口后,通过对PA与PB的写数据,可以实现对具体LED的操作。为了实现小人跑步的模拟,需要先在数据段中准备好至少两份不同的小人动作的图形字符码,即程序中的LED1与LED2。通过利用speed来控制8*8点阵的扫描速度,来实现小人的运动速度的控制。这里利用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实现对8*8LED点阵,步进电机以及滑动开关的数据的相关读写操作。初始化命令字为10001000b,PA,PB口链接行列接口,控制8*8LED点阵,PC3~PC0连接BJ_IN4~BJ_IN1以及LED灯,用于控制步进电机和显示相序,PC7~PC4接K7~K0,用于读取开关数据。
8253:用于实现对于基准时钟1MHZ的分频输出2s脉冲引发中断以及指定时间输出到逻辑笔。
数码管:用于显示对应的档位数字。将地址接到230h即可
程序流程图
程序清单
; 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集成开发环境
- 设计语言:汇编
- 调试遇到的问题
- 由于使用了8*8点阵,导致只能将8255的接口PC分高低位用于不同的工作方式。在按此,高四位接好滑动开关,低四位接好步进电机后,运行程序,发现显示结果不正常。
- 在最后,准备利用8253分频配合逻辑笔,实现定时提示功能,代码写好,接口连接好后,发现逻辑笔始终显示低电平。
- 对应的解决办法
- 因为在程序中的读写PC口的时候,没有考虑到每次的写操作,是会影响到另外的四个口的数据的。所以在原来向PC口写数据的基础上,进行了进一步的处理,即“读改写”操作,先读回来原来的数据,只修改低四位,改为想要发送的数据,再写回去。这样就避免了互相干扰的问题,最终恢复正常了。
- 仔细检查代码后,发现,原来是CNT2相应的命令字写错了。对于2号计数器的指定出了错误,应该是0b4h,结果写成了0e4h。这里主要是因为对于16进制数字不熟悉的结果,导致信息错乱,没有输到想要的位置上。
系统运行结果
- 由下图可见,最终实现了档位指示,小人跑步模拟,步进电机的转动以及定时提示。
- 系统运行环境:
结论及设计体会
最终,完成了自己的设计,实现了“跑步机”的模拟。
最终完成的功能有:8*8LED点阵动态显示小人运动,一位七段数码管显示对应的速度档位,而利用滑动开关,实现跑步机(步进电机)的正反转和档位的控制,同时,利用分频实现定时功能,可以在指定时间输出负脉冲,将逻辑笔的指示灯切换,最终实现了定时提示的功能。
这个实验持续了两三周的时间,之前由于有考试,主要的精力没有放在这个上面,只是先做好了步进电机的基本功能,还未拓展点阵和数码管的功能。不过还好,最后还是赶上来了。
这个思路很好,在一个小的,课本的例子的基础上,不断扩展,不断增加更多的功能,这样也让我在不断地学习新的模块的知识,同时不同模块之间的配合,也在考验着我的能力,因为并不是把模块拿来接好就可以用的,总是会遇到各种各样的问题,需要我们不断地取寻找,解决。
这时,渐近式的编程就显得尤为重要。保留好源代码,先修改一部分,测试,再修改,再测试,不断地反复,直到效果满足预期要求,否则就不能拿来用,只能使用原来的未修改的源代码。这里也提示了我们,及时存档的重要性。
总的看来,磕磕绊绊,但还是走到了终点。
本次试验最遗憾的事情就是12864没用上。为了它我花费了半天时间,查资料,咨询老师,研究了半天,但最终也还是也没有用上12864。不过还是感谢老师的指导。
这次实验学到了很多,包括各种模块的使用,接口操作,中断操作,这都是在程序设计中使用到的,以及对于各种问题的调试解决方法,甚至对于编程也有了进一步的认识。汇编这门语言也因此而熟练了许多。感觉获益匪浅。