接口芯片课程设计记录
更新2013-09-27
//////////////////////////////////////////////////////////////////////
对很多人来说,这次课程设计是16位x86汇编+8255a芯片+8254芯片编程
实践,设备陈旧,内容也不新,但对新手来说,这是一次离硬件很近的
一次编程,作个记录。
1)第一次机箱编程
第一天接触计算机硬件编程,是对机箱编程,却无从下手。怎么连接硬件?
怎么将软件在指定的硬件上跑?幸好有同学在以前自学过单板机编程。使用
的是xp的window系统,调试软件是Tddebug,16位微机接口试验平台。由PIC
总线连接计算机,PIC扩展卡将PIC总线转换为微机系统总线。要做的就是
在软件环境中对指定端口地址进行读写编程,将机箱的微机系统总线接口和
芯片接口连接,最后运行程序实现功能。
第一步,了解在工具软件环境中,程序操作的硬件接口地址,也就
是x86汇编out指令和in指令所操作的端口地址。芯片对外和对cpu的交换信
息也就存放在这些端口中。这相当简单也很抽象,无法知道在out和in的操
作背后,硬件需要做什么操作。
第二步,硬件的接线。在计算机课本学的计算机硬件是实在简单,因为在
学习的时候,那些由cpu控制而不是由程序控制的数据线对我们来说,基本
透明,而在课本上根本无法感性认识到计算机在完成对一个硬件操作的过程中
要做进行什么操作,在这一步学习时,对一些连线,自己一开始总觉含糊。
8255a是我第一个接线学习的芯片。机箱上该芯片可视的接口引脚有:
8bit的数据线DX0~DX7,2bit的读控制RD,2bit的写控制WR,
2bit的片选控制CS,2bit的芯片端口地址接口A0A1,
8255a芯片对外设的3*8bit的数据口接PA0~PA7,PB0~PB7,PC0~PC7。
汇编程序中端口的操作也就是out指令和in指令,但一个芯片的引脚却有那么
多,如果你将芯片的每个接口的作用和端口操作指令联系弄清就好。
从哪个接口开始学呢?从我觉得最简单的接口开始说,WR接口和RD接口。
学过汇编或微机原理应该了解到,计算机要对一单元进行操作时,要先对单元
进行定位,然后发出读写命令,最后是单元的数据的读取或写入。RD接口就是
对芯片发出读信号,WR接口就是对芯片发出写信号。那单元的定位是怎么
做到的?看看A0A1接口和CS接口。对一个地址发出out指令或in指令后,如果
地址在芯片的端口地址范围内,那么cs接口就会接收到一个片选信号,芯片
开始和系统总线沟通,再根据A0A1接口的输入,就会知道对哪个单元进行定
位,a8255有三个可编程接口,A0A1的值00~11分别对应芯片的A端口,B端口,
C端口和控制字。这也是地址只需两位A0A1就好的原因。
下面说与芯片接口相连的机箱提供的微机总线接口。
机箱中相关的接口有:
数据接口XD0~XD7,地址接口XA0~XA15,内存读接口XMR,
内存写接口XMW,外设读接口XIOR,外设写接口XIOW,
片选信号接口四个:IOY0,IOY2,IOY3,IOY4。
在软件环境中,提供4组片选地址,每组选片地址对应一选片信号接口。在每组
地址中,根据输出端口地址在本组地址的偏移量(最低地址为0偏移),XA0~XA15
就会输出出的地址偏移。我的机子中,程序中端口地址值每偏移1字节(在32位
中为4),xA0 ~xA15值就会增加1。8255a端口和控制字的地址A0A1是连
续的,但可以根据程序实际和XA连接。按照上面的说法,我们要连数据线接口
XD0~xD7,端口地址接口XA两个,选片接口一个IOY?,外设备读写接口XIOR,
XIOW。PA0~PC1根据需要连接外设。在机箱中,还可以看到XMRD,XMWR,
这次编程只设计总线对芯片读写,不涉及芯片对内存读写,可以看到对内存和对芯片
的对称的读写控制设计。
这次设计中用到的芯片的连接原理基本和上面的相似。
2)编程的开始
我和拍档选的题目是做秒表计时器。
这是硬软结合的编程,程序真的就像你在计算机中用c语言编一个hello world
那么友好的跑起来吗?我的第一个程序,是令数四个7段码管一起显示0~9,我
以后的程序基本是基于这个程序来修改和扩展的,外加一些注释。
;用于存放栈段
sstack segment
dw 0ffh dup(0h)
sstack ends
;用于存放数据段
sdata segment
stackinit dw 0ffh-2h ;栈顶指针
port1mode dw 10010000b ;控制字
port1input dw 1460h ;键盘输入口地址
port1abl dw 1461h ;数码管选通输出地址
port1num dw 1462h ;数码管显示字形码输出地址
port1ctl dw 1463h ;控制字输出地址
number db 3fh, 06h, 5bh, 4fh, 66h, 6dh, 7dh, 07h, 7fh, 6fh, 00h ;数
码管字符编码
sdata ends
;个人习惯给功能的过程分了个段
sprocess segment
assume ss:sstack, ds:sdata, cs:sfunc ;没有assume时在调用
;显示0~9和灭灯
showloop proc far
push dx ;保存寄存器的值
push bx
push cx
mov dx, [port1abl]
mov al, 0000b
out dx, al
mov dx, [port1num]
mov bx, 0h
mov cx, 11d
showloop1:
mov al, [number][bx]
inc bx
out dx, al
call far ptr delay1s
loop showloop1
pop cx ;恢复寄存器的值
pop bx
pop dx
retf ;如果是用ret,前面的proc far应该会对其解释为弹出cs:ip
showloop endp
;两个空循环模拟一秒的延时
delay1s proc far
push cx
mov cx, 05fffh
delay1:
push cx
mov cx, 0ffffh
delay2:
loop delay2
pop cx
loop delay1
pop cx
retf
delay endp
sprocess ends
;主流程段
scode segment
assume ss:sstack, ds:sdata, cs:scode
start:
;初始化栈段
mov ax, sstack
mov ss, ax
mov ax, [stackinit]
mov sp, ax
;初始化数据段
mov ax, sdata
mov ds, ax
;初始化8255a
mov dx, [port1ctl]
mov al, 10000000b
out dx, al
displayshow:
call far ptr showloop
;结束程序
mov ah, 4ch
int 21h
scode ends
end start
到底是硬件还是硬件出问题?程序就是跑不起来,就像我的刚开始写的c语言
程序,令人百思不得其解的跑不动。
对了,想起自己没有进行电线的检测,于是一口气将所有的连线都拔了,以后
每使用一根线都练到led灯进行有效检查。
程序还是跑不动。
继续检查一遍自己的程序,好吧,端口号地址我将它当成10进制的地址值了,
还有,有没有在所有的数据后都显式表示其进制,2进制有没有泄露中间位,
你想要控制的端口是0有效还是1有效。
程序还是跑不动。
不停的瞧程序,不停的检查插孔间的高低位是否对应,有没有哪个指针引脚漏
插了。
程序还是跑不动。
用手摇一摇所有的插孔,咦?一闪而过。叫别人来帮忙看,等待,折腾了大
半天,好了,最后结论:换机箱。
因为实验用的机子较老,有时候程序是运行调试一次,重启一次。
上面说的错误都是我遇到过的。还有,程序的结构规范化能有效提高软调试效
率。个人觉得计算机的编程,一定要注重细节。
3)编程
c语言高级编程中,函数的封装可以实现程序结构化的方法。那汇编程序应该以
什么机构来编写?汇编中有函数什么的吗?从前就没有用过汇编写过什么程序
,于是很想在汇编中找一些自己学过的编程语言的影子。
流程控制
汇编语言是机器语言,但其也是过程语言,这和c语言有共同点。很显然,要学
会用汇编实现控制:顺序,分支,循环。汇编中,是通过状态寄存器psw和条件
转跳指令来实现程序控制的。
过程调用
c语言中的函数能提高代码的利用率和程序的结构化,汇编也提供一些类似的机
制。再看下去,转跳也是函数(汇编中好像叫过程合适)调用实现的方法。汇
编中没有严格的函数定义,就是转跳。汇编中为了方便调用过程的实现,提供
了一些关于cs和ip的进出栈操作指令。说到这里出现了一个很重要的东西,
栈。汇编中有与栈相关的操作,但不提供栈空间。栈是一个很重要的数据结构
。汇编就是通过修改cs和ip进行转跳到调用过程,在过程完毕后再修改cs和ip
返回调用处。只有转跳和函数的功能还是不同的,函数可以有参数和返回值,
那过程的怎么实现参数和返回值?通常用两种方法:简单的参数和返回值可以
用寄存器来存放,复杂的参数和返回值可以用栈来存放。这里也用到栈。
上面的代码中用"过程名 proc far 过程体 过程名 endp"伪指令来定义一个过程。
栈结构
栈结构是一个容器,后进先出。ss寄存器一般用来存放栈空间的低地址,sp是
用来存放栈顶地址。要注意,数据的入栈sp会减一字长,数据的出栈sp会加一
字长。栈在高级语言编程中,不是每次都会用到这中数据结构,但在汇编中,
几乎不可 少。我觉得主要原因是cpu中可操作的寄存器数量少。栈在这时充当
一个记忆的功能。在进入一段局部代码后,代码的操作会用到某些寄存器,但
用到的寄存器有可能会影响到外部代码。这时,先将要用到的寄存器的值进栈
,到退出局部代码前,再将寄存器的值退栈恢复寄存器,这样栈可以实现很好
的局部封装。如果将寄存器作为返回值的话,那一般就不进栈了。栈在写调用
过程时,还可以实现复杂的数据结构做参数或返回值。
关于编程语言,不多说。
4)硬件的脾气
硬件不按程序办事。
写惯了高级程序的人会这样想:如果要往屏幕输出“hello world”,用c语言可
以用这样一语句:printf("hello world\n");如果我要往芯片或外设输出数据
是不是这样一句话:out al, dx就能实现往芯片输出了吗。不一定是的。如果
在一定时间内只是向这个端口输出一次,是可以的,但一当连续多次输出时,
会有问题了。数据在硬件中的传输是有时差的,如果两个部件之间的时差较大
,当较快的那个部件向较慢的部件连续输出时,会发生什么呢?之前的寄存器
的数据还没被取走时就已经被后来的数据覆盖了。部件之间的速度差也是中断
机制产生的原因之一吧。
硬件也会小聪明。
我们的题目是秒表寄存器,共使用4个七段数码管。在画机箱的连接图时,被要
求将每个数码管的引脚都画出来,查到了数码管的接法有共阳极接法和共阴极
接法。机箱的数码管的引脚是封装好的只有一个8bit编码引脚和一个4bit的选
通引脚。也查到了这个封装好的数码管是共阴极的。那选通引脚刚好对应4个数
码管。要想那么想要控制不同的数码管变化只要改变选通信号就好了。想实现
在不同的数码管显示不同的数字来实现进位计数,一开始以为,当要改变那个
数码管时,只要将它选通就好了。于是在一次的计数更新中,先将4个数码管依
次更新,然后延时显示。但发现数码管只显示一位。什么问题呢?在后来自己
想到延时问题后,可能是其它数码观的显示输出被覆盖了。又有新的问题了:
每位数码管要显示不同的数字,那每次只能显示一位数码管,因为只要数码管
被使能,就会被编码改变,只要该数码管不被使能,它就会灭。这样怎么能同
时显示四个独立改变的数码管?硬件耍小聪明了,利用人视觉的滞后性,通过
循环,一次循环中每个数码管依次更新一次,那么看起来就是多个独立显示的
数码管在同时显示。
实践出来的答案。
较适合的输出延时做到不占太多的cpu空转,怎么调整输出延时和显示循环而令
数码管闪烁频率肉眼不可识别等等,这些问题,也许你可以在纸上计算出来,
但我觉得有一种方法不精确但有效,就是实践。修改参数,观察。
5)电路的焊接
电路的焊接前,必须严格按照自己已有的芯片和组件画出实物图,因为器件在
硬件中被焊接错误,改动相当麻烦。电路设计要清晰条理。电路板的安排要想
到器件的安装,电路板对外连接(例如电池,总线)。在焊接时,先固定,再
焊连线。因为硬件几乎是我的拍档焊的,所以不多说了。
本博客版权声明点击打开链接