本节书摘来异步社区《51单片机应用开发范例大全(第3版)》一书中的第2章,第2.2节,作者:张杰 , 宋戈 , 黄鹤松 , 员玉良,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.2 扩展芯片实现端口扩展
用串口扩展I/O口非常实用,但是串口是按位读取的,输入的数据必须重组后才能使用,速度受到限制,同时还需要严格的时钟配合。
在有些场合,利用串口扩展I/O口不是很理想,比如BCD码的输入及多组速率较高的并行数据的输入。所以,用并行数据端口扩展I/O口也是很有必要的。
2.2.1 【实例24】用8243扩展I/O端口
BCD码由四位二进制数组成,有些设备直接以BCD码的形式收发数据。如果这类接口的设备比较多,就需要扩展4位并行接口。本设计利用单片机4个I/O接口,扩展成4个4位的并行I/O端口,用于4位并行数据的输入/输出。
1.8243简介
8243共有4个4位的并行I/O端口,即P4、P5、P6、P7口,这4个端口均可独立地设置为输入口或输出口。由于各端口均为4位。因此,十分适宜用于BCD码的输入/输出。图2-7所示为8243引脚图,具体引脚说明如下。
![5d12c983a39d334ab45505c8938b3985be996635](https://i-blog.csdnimg.cn/blog_migrate/14d2ae9bf8bb42d012e196cc4b3afb5d.png)
PROG:地址/数据传送选通信号线。
P2.0~P2.3:数据/地址和控制信号输入端,由PROG信号控制选择P4、P5、P6、P7 4个4位双向I/O口。
overline{rm{CS}}:片选信号。
PROG信号用于选择P2口的功能。在进行输入/输出时,先通过P2口传送选择端口及端口操作方式的控制命令,该命令由PROG的下跳沿所存至8243内部的指令寄存器和地址译码器,而进行的数据传送,由PROG的上跳沿将数据通过指定的端口输入/输出。P2传送命令时,由P2.1和P2.0指定端口地址,由P2.3和P2.2规定端口的工作方式,各位具体的定义如表2-1所示。
![a81741ba4f1c110d088451d342bc78bffdab2f37](https://i-blog.csdnimg.cn/blog_migrate/89cdc0e534b0ab662d02d70d74fb3004.png)
表中的“或”、“与”方式是指分别把输出的数据与被寻址端口的内容进行“逻辑或”以及“逻辑与”运算后再写入该端口。
2.8243与单片机的接口设计
8243的P2口负责传送控制命令、输入/输出数据,所以需要将P2口与单片机的I/O口直接相连,PROG信号另外用1个单片机的I/O接口产生,overline{rm{CS}}片选直接接地,保证8243始终选通。应用中,可以用一个I/O接口产生片选信号。具体接口电路如图2-8所示。
![993065bb8cfb67e764c1d4c3ca3f940c6999d390](https://i-blog.csdnimg.cn/blog_migrate/13dfc069491fc924483f0e8021ffe52b.png)
以下是单片机串口驱动8243的程序代码:
#include<reg52.h>
sbit ContrBit0=P1^0;
sbit ContrBit1=P1^1;
sbit ContrBit2=P1^2;
sbit ContrBit3=P1^3;
sbit PROG=P1^4;
sbit CS=P1^5;
char driver8243(char sele_P,char sele_M,char out_data)
{
char in_data=0;
char data_buf;
PROG=1;
//置PROG为高电平
//----------------------------开始写控制字------------------------------
if((sele_P&0x01)==0) //将控制字最低位送到8243的p2.0
ContrBit0=0;
else
ContrBit0=1;
if((sele_P&0x02)==0) //将控制字第二位送到8243的p2.1
ContrBit1=0;
else
ContrBit1=1;
//以上两位共同指定端口地址
//-------------------------写端口工作模式控制字--------------------------
if((sele_M&0x01)==0) //将端口工作模式控制字低位送到8243的p2.2
ContrBit2=0;
else
ContrBit2=1;
if((sele_M&0x01)==0) //将端口工作模式控制字高位送到8243的p2.3
ContrBit3=0;
else
ContrBit3=1;
//完成写控制字
PROG=0;
//在PROG上产生下降沿
switch(sele_M&0x03) //判断工作模式
{
case 0: break;
//sele_M=B00为输入,不处理,等待上升沿
case 1: data_buf=out_data;
break;
//sele_M=B01为输出,直接送数据
case 2: data_buf=out_data;
break;
//sele_M=B10为逻辑或,直接送数据
case 3: data_buf=out_data;
break;
//sele_M=B11为逻辑与,直接送数据
}
PROG=1;
//产生上升沿
if((sele_M&0x03)==0) //sele_M=B00为输入,接收数据
in_data=(data_buf&0x0F);
return(in_data);
//sele_M=B00,返回接收到的数据
}
//sele_M!=B00,返回0
void main( void)
{
char receive_data;
receive_data=driver8243(1,0,5);
}```
####2.2.2 【实例25】用8255A扩展I/O口
可编程并行I/O接口芯片8255A是Intel公司生产的标准外围接口电路。它采用NMOS工艺制造,用单一+5V电源供电,具有40条引脚,采用双列直插式封装。它有A、B、C共3个端口共24条I/O线,可以通过编程的方法来设定端口的各种I/O功能。由于它功能强,又能方便地与各种微机系统相连,而且在连接外部设备时,通常不需要再附加外部电路,所以得到了广泛的应用。
1.8255A的引脚介绍
8255A是一种有40个引脚的双列直插式标准芯片,其引脚如图2-9所示。除电源(+5V)和地址外,其他信号可以分为两组。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/42d2c0872d7d9f64c3c76ec864e1aa0ccd1c0c5a.png" width="" height="">
</div>
与外设相连接的如下。
PA7~PA0:端口A数据线。
PB7~PB0:端口B数据线。
PC7~PC0:端口C数据线。
与CPU相连接的如下。
D7~D0:8255A的数据线,和系统数据总线相连RESET。
复位信号,高电平有效。当RESET有效时,所有内部寄存器都被清除,同时,3个数据端口被自动设为输入方式。
\overline{\rm{CS}}:片选信号,低电平有效。只有当\overline{\rm{CS}}有效时,芯片才被选中,允许8255A与CPU交换信息。
\overline{\rm{RD}}:读信号,低电平有效。当\overline{\rm{RD}}有效时,CPU可以从8255A读取输入数据。
\overline{\rm{WR}}:写信号,低电平有效。当\overline{\rm{WR}}有效时,CPU可以往8255A中写控制字或数据A1、A0:端口选择信号。8255A内部有3个数据端口和1个控制口,当A1A0=00时选中端口A;A1A0=01时选中端口B,A1A0=10时选中端口C,A1A0=11时选中控制口A1、A0和\overline{\rm{RD}}、\overline{\rm{WR}}、\overline{\rm{CS}}组合所实现的各种功能如表2-2所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/173f6ef822a2b53b51be79b5ec30bc902969f83a.png" width="" height="">
</div>
2.8255A的工作方式
8255A共有3种工作方式,即工作方式0、工作方式1和工作方式2,这些工作方式可以用软件编程来指定。
(1)工作方式0。
工作方式0也叫基本输入/输出方式。这种工作方式不需任何选通信号,端口A、端口B及端口C的高4位和低4位都可以设定为输入或输出。作为输出端口时,输出的数据均被锁存;作为输入端口时,端口A的数据能锁存,端口B与端口C的数据不能锁存。
(2)工作方式1。
工作方式1也叫选通输入/输出方式。在这种工作方式下,端口A可由编程指定为输入口或输出口,端口C的高4位用来作为输入/输出操作的控制联络信号;端口B同样可由编程指定为输入口或输出口,端口C的低4位用来作为输入/输出操作的控制联络信号。在方式1下,端口A和端口B的输入数据或输出数据均能被锁存。
(3)工作方式2。
8255A的工作方式2仅适合于端口A,这种工作方式下,端口A可作为8位的双向数据传输端口,即可发送数据,也可接收数据。端口C的PC7~PC3用来作为输入/输出的同步控制信号。此时,端口B和PC2~PC0只能编程为方式0或方式1工作,而端口C剩下的3条线可作为输入或输出线使用或用作端口B方式1下的控制线。
3.8255A的控制字及初始化
8255A为可编程接口芯片,以控制字形式对其工作方式和端口C各位的状态进行设置,它有两种控制字:工作方式控制字和端口C置位/复位控制字。
工作方式控制字用于确定各端口的工作方式及数据传送方向,其格式如表2-3所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/8c34c9f4b5541d4caab4ba2889193896af95910f.png" width="" height="">
</div>
对工作方式控制字作如下说明。
端口A有3种工作方式,而端口B只有2种工作方式。
A组包括端口A与端口C的高4位,B组包括端口B与端口C的低4位。
在方式1或方式2下,对端口C的定义(输入或输出)不影响作为联络使用的端口C各位的功能。
最高位(D7)为标志位,D7为方式控制字。
利用端口C置位/复位控制字可以很方便地使端口C8位任一位清0或置1,控制字的格式如表2-4所示。D7位为控制字的标志位,D7 = 0为端口C置位/复位控制字。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/1733f30eddd3f96225bb08d73e15d8fa247e1172.png" width="" height="">
</div>
在使用中,控制字每次只能对端口C的一位进行置位或复位。
应注意的是,作为联络信号使用的端口C各位是不能采用置位/复位操作来使其置位或复位的。其数值应视现场的具体情况而定。
8255A初始化的内容就是向控制寄存器写入工作方式控制字或端口C置位/复位控制字。
这两个控制字可按同一地址写入且不受先后顺序限制,因为两个控制字标志位的状态不同,因此8255A能加以区分。
例如,对8255A各口作如下设置:端口A方式0输入,端口B方式0输出,端口C高位部分为输出、低位部分为输入。假设控制寄存器的地址为03FFH,则其工作方式控制字可设置如下。
D0 = 1:端口C低半部分输入。
D1 = 0:端口B输出。
D2 = 0:端口B方式0。
D3 = 0:端口C高半部分输出。
D4 = 0:端口A输入。
D6D5 = 00:端口A方式0。
D7 = 1:工作方式字标志。
因此工作方式控制字为10010001B,即91H。
4.8255A与单片机的接口电路
图2-10所示为8255A与单片机的接口电路。8255A中D7~D0用作地址端口,A0~A1用作地址端口,\overline{\rm{WR}}、\overline{\rm{RD}}、\overline{\rm{CS}}、RESET用作控制端口。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/0c9e677e692e1a935ffb6de4578f98b947ff7a53.png" width="" height="">
</div>
因为8255A所有的寄存器、I/O端口都对应有读写地址,所以可以对8255A的各I/O口和控制字寄存器进行编址。令A15~A8为01111111,A6~A2为11111时,8255A才会工作。
PA地址:7F7CH。
PB地址:7F7DH。
PC地址:7F7EH。
控制字地址:7F7FH。
5.8255A驱动程序设计
8255A的驱动程序主要是涉及对端口A、B、C以及控制字的设置,8255A具体的驱动程序主要包括以下代码及函数。
(1)管脚定义及函数声明。
管脚定义是指端口A、端口B、端口C和控制字的地址说明以及状态标志位的定义;函数的声明包括端口A、端口B、端口C的读写函数和控制字以及C口配置函数,具体代码如下:
//------------------------函数声明,管脚定义---------------------------------
include
include
define a8255_PA XBYTE[0x7F7C] //PA地址
define a8255_PB XBYTE[0x7F7D] //PB地址
define a8255_PC XBYTE[0x7F7E] //PC地址
define a8255_CON XBYTE[0x7F7F] //控制字地址
unsigned char bdata IO_flags;
//用于表示PA、PB、PC的当前输入输出状态
//内容不能被其他程序改写
sbit IO_flagsA=IO_flags^0;
//PA的当前输入输出状态
sbit IO_flagsB=IO_flags^1;
//PB的当前输入输出状态
sbit IO_flagsC=IO_flags^2;
//PC的当前输入输出状态
unsigned char const cfg_table[8]=
{
0x80, //10000000b, c=out b=out a=out
0x90, //10010000b, c=out b=out a=in
0x82, //10000010b, c=out b=in a=out
0x92, //10010010b, c=out b=in a=in
0x89, //10001001b, c=in b=out a=out
0x99, //10011001b, c=in b=out a=in
0x8B, //10001011b, c=in b=in a=out
0x9B, //10011011b, c=in b=in a=in
}
unsigned char rd_PA(void);
//读PA
unsigned char rd_PB(void);
//读PB
unsigned char rd_PC(void);
//读PC
void wr_PA(unsigned char PA_data);
//写PA
void wr_PB(unsigned char PB_data);
//写PB
void wr_PC(unsigned char PC_data);
//写PC
void set_PC(unsigned char PC_num);
//PC位操作,置位,PC_num为端口号0~7
void clr_PC(unsigned char PC_num);
//PC位操作,复位,PC_num为端口号0~7
void PABC_config(void);
//写8255A控制字`
(2)端口A、B、C读写函数。
端口A、B、C读写函数完成8255A端口A、B、C的数据读写,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:rd_PA
// 输入函数:无
// 输出参数:PA_data,PA输入的数据
// 功能说明:驱动PA实现输入功能,读入PA的并行数据
//-----------------------------------------------------------------------
unsigned char rd_PA(void) //读PA
{
unsigned char PA_data;
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
do
{
IO_flagsA=1;
//置PA状态标志位为高--输入
IO_flags=ACC;
PABC_config();
//调用配置子程序,完成对8255的设置
ACC=IO_flags;
}
while(IO_flagsA==0);
//判断状态标志位是否为高
//控制字设置完成
PA_data=a8255_PA;
//把PA的数据读到PA_data
return(PA_data);
//返回PA_data
}
//-----------------------------------------------------------------------
// 函数名称:rd_PB
// 输入函数:无
// 输出参数:PB_data,PB输入的数据
// 功能说明:驱动PB实现输入功能,读入PB的并行数据
//-----------------------------------------------------------------------
unsigned char rd_PB(void) //读PB
{
unsigned char PB_data;
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
do
{
IO_flagsB=1;
//置PB状态标志位为高--输入
IO_flags=ACC;
PABC_config();
//调用配置子程序,完成对8255的设置
}
while(IO_flagsB==0);
//判断状态标志位是否为高
//控制字设置完成
PB_data=a8255_PB;
//把PB的数据读到PB_data
return(PB_data);
//返回PB_data
}
//-----------------------------------------------------------------------
// 函数名称:rd_PC
// 输入函数:无
// 输出参数:PC_data,PC输入的数据
// 功能说明:驱动PC实现输入功能,读入PC的并行数据
//-----------------------------------------------------------------------
unsigned char rd_PC(void) //读PC
{
unsigned char PC_data;
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
do
{
IO_flagsC=1;
//置PC状态标志位为高--输入
IO_flags=ACC;
PABC_config();
//调用配置子程序,完成对8255的设置
//ACC=IO_flags;
}
while(IO_flagsC==0);
//判断状态标志位是否为高
//控制字设置完成
PC_data=a8255_PC;
//把PC的数据读到PC_data
return(PC_data);
//返回PC_data
}
//-----------------------------------------------------------------------
// 函数名称:wr_PA
// 输入函数:PA_data,送PA输出的数据
// 输出参数:无
// 功能说明:驱动PA实现输出功能,输出数据到PA
//-----------------------------------------------------------------------
void wr_PA(unsigned char PA_data) //写PA
{
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
{
IO_flagsA=0;
//置PA状态标志位为低--输出
IO_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
PABC_config();
//调用配置子程序,完成对8255的设置
ACC=IO_flags;
}
while(IO_flagsA==1);
//判断状态标志位是否为高,
//为高,设置未完成,需从新设置
a8255_PA=PA_data;
//将PA_data的内容送到PA
}
//-----------------------------------------------------------------------
// 函数名称:wr_PB
// 输入函数:PB_data,送PB输出的数据
// 输出参数:无
// 功能说明:驱动PB实现输出功能,输出数据到PA
//-----------------------------------------------------------------------
void wr_PB(unsigned char PB_data) //写PB
{
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
{
IO_flagsB=0;
//置PB状态标志位为低--输出
IO_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
PABC_config();
//调用配置子程序,完成对8255的设置
ACC=IO_flags;
}
while(IO_flagsB==1);
//判断状态标志位是否为高,为高,设置未完成,
//需从新设置
a8255_PB=PB_data;
//将PB_data的内容送到PB
}
//-----------------------------------------------------------------------
// 函数名称:wr_PC
// 输入函数:PC_data,送PC输出的数据
// 输出参数:无
// 功能说明:驱动PC实现输出功能,输出数据到PC
//-----------------------------------------------------------------------
void wr_PC(unsigned char PC_data) //写PC
{
ACC=IO_flags;
//把状态标志字读到ACC便于进行位操作
{
IO_flagsC=0;
//置PC状态标志位为低--输出
IO_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
PABC_config();
//调用配置子程序,完成对8255的设置
ACC=IO_flags;
}
while(IO_flagsC==1);
//判断状态标志位是否为高,
//为高,设置未完成,需从新设置
a8255_PC=PC_data;
//将PC_data的内容送到PC
}```
(3)端口C配置函数。
端口C配置函数可实现PC口具体某一位的输入/输出设置,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:set_PC
// 输入函数:PC_num,范围0~7
// 输出参数:无
// 功能说明:对PC进行位操作,置PC(PC_num)为高
//-----------------------------------------------------------------------
void set_PC(unsigned char PC_num)
{
ACC=IO_flags;
IO_flagsC=0;
ACC=IO_flags;
PC_num=PC_num<<1;
PC_num=(PC_num|0x01);
a8255_CON=PC_num;
}
//-----------------------------------------------------------------------
// 函数名称:clr_PC
// 输入函数:PC_num,范围0~7
// 输出参数:无
// 功能说明:对PC进行位操作,清PC(PC_num)为低
//-----------------------------------------------------------------------
void clr_PC(unsigned char PC_num)
{
ACC=IO_flags;
IO_flagsC=1;
ACC=IO_flags;
PC_num=PC_num<<1;
PC_num=(PC_num&0xFE);
a8255_CON=PC_num;
}`
(4)写控制字函数。
写控制字函数完成对控制字的写,从而实现对端口A、B、C口输入/输出的配置,程序
代码如下:
//-----------------------------------------------------------------------
// 函数名称:PABC_config
// 功能说明:写8255A的控制字寄存器
//-----------------------------------------------------------------------
void PABC_config(void)
{
a8255_CON=cfg_table[IO_flags];
}```
####2.2.3 【实例26】用8155扩展I/O口
8155是Intel公司生产的可编程多功能接口芯片。它的内部有两个可编程的8位并行I/O口、一个6位并行I/O口、一个14位定时/计数器以及256字节的RAM存储器。8155可以直接和MCS-51系列单片机连接,而不需增加硬件电路,它是单片机应用系统中最常用的芯片之一。8155的结构及引脚如图2-11所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/2409a91ad9d87c23e877d21ee56b1fe3e470241c.png" width="" height="">
</div>
8155有3个可编程并行I/O端口:端口A、端口B、端口C。其中,端口A和端口B是8位,端口C是6位;1个14位可编程定时/计数器和256B的静态RAM,能方便地进行I/O端口扩展和RAM扩展。
8155共有40个引脚,按其功能特点分类说明如下。
地址数据线:AD0~AD7是低8位地址和数据共用输入口,当ALE=1时,输入的是地址信息,否则是数据信息。所以,AD0~AD7应与MCS-51的P0口相连。
端口线:PA0~PA7、PB0~PB7用于8155与外设之间传送数据,PC0~PC5既可用于8155与外设之间传送数据,也可作为端口A和端口B的控制信号线。
地址锁存线:在ALE的下降沿将单片机P0口输出的低8位地址信息及\overline{\rm{CE}}、\rm{IO}\overline{\rm{M}}的状态都锁存到8155内部寄存器,因此,单片机P0口输出的低8位地址信号不需要外接锁存器。
RAM或I/O端口选择线:当 \rm{IO}\overline{\rm{M}}= 0时,选中8155的片内RAM,AD0~AD7为RAM地址(00H~FFH);若 \rm{IO}\overline{\rm{M}}= 1时,选中8155的3个I/O端口及命令/状态寄存器和定时/计数器。AD0~AD7为I/O端口地址,其分配如表2-5所示。
片选线:\overline{\rm{CE}}为低电平时选中8155。
读、写线:\overline{\rm{RD}}、\overline{\rm{WR}}控制对8155的读/写操作。
定时/计数器的脉冲输入、输出线:TIMER IN 是外界向8155输入计数脉冲的输入端,\overline{\rm{TIMEROUT}}是8155向外界输出脉冲或方波的输出端。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/f090e018b9c6485baca1362d231b62d0dd63f3be.png" width="" height="">
</div>
1.8155的工作方式及基本操作
8155可作为通用I/O端口,也可作为片外256B RAM及定时器使用,在各种不同类型下使用时的基本操作如下。
作为片外256B RAM使用时,将\rm{IO}\overline{\rm{M}}引脚置低电平,这时8155只能做片外RAM使用,其寻址范围由片选线\overline{\rm{CE}}(高位地址译码)和AD0~AD7决定,与应用系统中其他数据存储器统一编址。使用片外RAM的读/写操作指令“MOVX”。
作为扩展I/O端口使用时,\rm{IO}\overline{\rm{M}}引脚必须为高电平,这时PA、PB、PC的口地址低8位分别为01H、02H、03H(设地址无关位为0时)。
8155的I/O端口各种方式选择是通过对8155内部命令寄存器命令字来实现的。命令寄存器由8位锁存器组成,只能写入不能读出。命令字各位定义如表2-6所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/2a968b68a30dc2bf539c2af91a2400d5f910418f.png" width="" height="">
</div>
8155的工作状态由状态寄存器指出,与命令字寄存器用同一个地址,只能读出不能写入。状态字的格式如表2-7所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/b58800fe54ff0204efcf0c537f683e1d02323a87.png" width="" height="">
</div>
端口操作如下。
端口A寄存器和端口B寄存器有完全相同的功能,可工作于基本I/O方式或选通I/O方式。
端口C可工作于基本I/O方式,也可作为端口A、端口B选通方式工作时的状态控制信号线。
当8155设定为方式1和方式2时,端口A、端口B、端口C均工作于基本输入/输出方式,由“MOVX”类指令进行输入/输出操作。
设定为方式3时,端口A定义为选通输入/输出,由端口C低3位作为端口A联络线,端口C其余位作为I/O端口线。
设定为方式4,端口A、端口B均定义为选通输入/输出方式,由端口C作为端口A、端口B的联络线。
逻辑组态如表2-8所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/22c93dd11e52b41570682789dfd9501c338b734f.png" width="" height="">
</div>
INTR为中断请求输出线,作为CPU的中断源,高电平有效。当8155的端口A或端口B缓冲器接收到设备输入的数据或设备从缓冲器中取走数据时,中断请求线INTR升高(仅当命令字寄存器相应中断允许位为1),向CPU请求中断,CPU对8155的相应I/O端口进行一次读/写操作后,INTR自动变为低电平。
BF为I/O端口缓冲器标志输出线,缓冲器存有数据时,BF为高电平,否则为低电平。为设备选通信号输入线,低电平有效。
在I/O端口设定为输出口时,仍可用对应的端口地址执行读操作,读取输出端口的内容;设定为输入端口时,输出锁存器被清除,无法将数据写入输出锁存器。所以每次通道由输入方式转为输出方式时,输出端总是低电平。8155复位时,清除所有输出寄存器,3个端口都为输入方式。
2.8155与单片机的接口电路
图2-12所示为8155与单片机接口电路。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/38724c27c4a109cbd1dfa6177f659d5ec7d4cca0.png" width="" height="">
</div>
根据图中连线图,假设A14~A9均为高电平,8155才工作,所以会有下述信息。
存储器地址:0x7F00~0x7FFF。
命令/状态寄存器地址:0x7E00(共有32个地址对应,选期中一个有效地址)。
端口A地址:0x7E01(共有32个地址对应,选期中一个有效地址)。
端口B地址:0x7E02(共有32个地址对应,选期中一个有效地址)。
端口C地址:0x7E03(共有32个地址对应,选期中一个有效地址)。
定时器寄存器A地址:0x7E04(共有32个地址对应,选期中一个有效地址)。
定时器寄存器B地址:0x7E05(共有32个地址对应,选期中一个有效地址)。
3.8155驱动程序设计
8155驱动程序主要是涉及对端口PA、PB、PC、控制字以及定时器的设置,主要包括以下代码及函数。
(1)相关函数声明及管脚定义。
管脚定义主要是指端口PA、端口PB、端口PC、控制字以及定时器A、B和存储器首地
址和相关标志位的定义;函数的声明涉及存储器及端口PA、端口PB、端口PC的读写函数、中断的开关函数和定时器相关函数,具体代码如下。
//------------------------函数声明,管脚定义------------------------------*/
include
include
define a8155_PA XBYTE[0x7E00] //控制字地址
define a8155_PB XBYTE[0x7E01] //PA地址
define a8155_PC XBYTE[0x7E02] //PB地址
define a8155_CON XBYTE[0x7E03] // PC地址
define Timer_A XBYTE[0x7E04] // 定时器寄存器A
define Timer_B XBYTE[0x7E05] // 定时器寄存器B
define mem_head XBYTE[0x7F00] // 存储器首地址
unsigned char bdata IO_flags;
//用于表示PA、PB、PC的当前输入输出状态
//内容不能被其他程序改写
sbit IO_flagA=IO_flags^0;
//PA的当前输入输出状态
sbit IO_flagB=IO_flags^1;
//PB的当前输入输出状态
sbit IO_flagC=IO_flags^2;
//PC的当前输入输出状态
sbit IO_flagC1=IO_flags^3;
//PC的当前输入输出状态
sbit Int_flagA=state_flags^4;
//PA的当前输入输出状态
sbit Int_flagB=state_flags^5;
//PB的当前输入输出状态
sbit Timer_flag1=state_flags^6;
sbit Timer_flag2=state_flags^7;
//Timer 的状态置位表示计数中
unsigned char rd_mem(unsigned char mem_ad);
//读存储器
void wr_mem(unsigned char mem_ad,unsigned char mem_data);
//写存储器
char rd_PA(void);
//读PA
char rd_PB(void);
//读PB
char rd_PC(void);
//读PC
void wr_PA(unsigned char PA_data);
//写PA
void wr_PB(unsigned char PB_data);
//写PB
void wr_PC(unsigned char PC_data);
//写PC
void Dint_PA(void);
//关端口A中断
void Eint_PA(void);
//开端口A中断
void Dint_PB(void);
//关端口B中断
void Eint_PB(void);
//开端口B中断
void setting_PC0int(void);
void setting_PC4int(void);
void start_timer(void);
//开始计数器计数
void stop_timer(void);
//停止计数器计数
void setting_zero_stop(void);
//设定计数到零停止计数
int rd_timer(void);
//读计数值
void setting_timerout_mode(unsigned char mode);
//设定输出模式
(2)读写外RAM函数。
读写外RAM函数对外部存储器指定单元数据进行读写,程序代码如下。
//-----------------------------------------------------------------------
// 函数名称:rd_mem
// 输入函数:mem_ad,范围0~255
// 输出参数:mem_data,存储对应数据
// 功能说明:读外部RAM,输入相对地址,返回数据
//-----------------------------------------------------------------------
unsigned char rd_mem(unsigned char mem_ad) //读存储器
{
unsigned char mem_data;
unsigned int AD_mem;
AD_mem=&mem_head;
AD_mem=AD_mem+mem_ad;
mem_data=XBYTE[AD_mem];
return(mem_data);
}
//-----------------------------------------------------------------------
// 函数名称:wr_mem
// 输入函数:mem_ad,mem_data 相对地址和数据
// 输出参数:无
// 功能说明:写数据到外部RAM,把数据写到相应的地址
//-----------------------------------------------------------------------
void wr_mem(unsigned char mem_ad, unsigned char mem_data) //写存储器
{
unsigned int AD_mem;
AD_mem=&mem_head;
AD_mem=AD_mem+mem_ad;
XBYTE[AD_mem]=mem_data;
}`
(3)端口PA、端口PB以及端口PC的读写设置函数。
端口PA、端口PB以及端口PC的读写设置函数主要完成对8155端口的输入输出设置及
数据读写,程序代码如下。
//-----------------------------------------------------------------------
// 函数名称:rd_PA
// 输入函数:无
// 输出参数:PA_data
// 功能说明:返回PA数据
//-----------------------------------------------------------------------
char rd_PA(void) //读PA
{
unsigned char PA_data;
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
do
{
IO_flagA=0;
//置PA状态标志位为低--输入
state_flags=ACC;
a8155_CON=state_flags;
//重写控制字,完成对8155的设置
}
while(IO_flagA==1);
//判断状态标志位是否为高
//控制字设置完成
PA_data=a8155_PA;
//把PA的数据读到PA_data
return(PA_data);
//返回PA_data
}
//-----------------------------------------------------------------------
// 读PB、PC的函数:rd_PB和rd_PC程序代码与rd_PA类似,不再赘述
//-----------------------------------------------------------------------
// 函数名称:wr_PA
// 输入函数:PA_data
// 输出参数:无
// 功能说明:把PA_data送到PA输出
//-----------------------------------------------------------------------
void wr_PA(unsigned char PA_data) //写PA
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
{
IO_flagA=1;
//置PA状态标志位为高--输出
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
while(IO_flagA==0);
//判断状态标志位是否为低
//为低,设置未完成,需从新设置
a8155_PA=PA_data;
//将PA_data的内容送到PA
}
//-----------------------------------------------------------------------
// 写PB、PC的函数:wr_PB和wr_PC程序代码与wr_PA类似,不再赘述
//-----------------------------------------------------------------------
(4)端口PA、端口PB以及端口PC的中断设置函数。
① 端口PA、端口PB以及端口PC的中断设置函数完成各个端口的中断开启和关断,程序代码如下。
// 函数名称:Eint_PA
// 输入函数:无
// 输出参数:无
// 功能说明:PA中断允许
//-----------------------------------------------------------------------
void Eint_PA(void) //开端口A中断
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Int_flagA=1;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 函数名称:Dint_PA
// 输入函数:无
// 输出参数:无
// 功能说明:PA中断禁止
//-----------------------------------------------------------------------
void Dint_PA(void) //关端口A中断
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Int_flagA=0;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 开关PB中断的函数Eint_PB、Dint_PB和Eint_PA、Dint_PA程序代码类似,不再赘述
//-----------------------------------------------------------------------
② 端口PC上下半口配置函数可实现端口PC上半口配置为PA状态输出和PC下半口配置为PB状态输出。程序代码如下。
//-----------------------------------------------------------------------
// 函数名称:PC0_PAint
// 输入函数:无
// 输出参数:无
// 功能说明:设置PC上半口为PA状态输出,PC0=INTRa,PC1=BFa,PC2=/STBb
//-----------------------------------------------------------------------
void PC0_PAint(void) //PC上半口为PA状态输出
{
//PC0=INTRa,PC1=BFa,PC2=/STBa
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Int_flagA=1;
IO_flagC1=1;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 函数名称:PC4_PBint
// 输入函数:无
// 输出参数:无
// 功能说明:设置PC下半口为PB状态输出,PC3=INTRb,PC4=BFb,PC5=/STBb
//-----------------------------------------------------------------------
void PC4_PBint(void) //PC下半口为PB状态输出
{
//PC0=INTRa,PC1=BFa,PC2=/STBa
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Int_flagA=1;
IO_flagC1=1;
IO_flagC=1;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
③ 计数器设置函数完成计数器的起停和读写和输出模式设置,具体程序代码如下。
//----------------------------------------------------------------------
// 函数名称:start_timer
// 输入函数:无
// 输出参数:无
// 功能说明:开始计数器计数
//-----------------------------------------------------------------------
void start_timer(void) //开始计数器计数
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Timer_flag1=1;
Timer_flag2=1;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 函数名称:stop_timer
// 输入函数:无
// 输出参数:无
// 功能说明:停止计数器计数
//-----------------------------------------------------------------------
void stop_timer(void) //停止计数器计数
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Timer_flag1=1;
Timer_flag2=0;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 函数名称:size_zero_stop
// 输入函数:无
// 输出参数:无
// 功能说明:设定计数到零停止计数
//-----------------------------------------------------------------------
void stop_timer(void) //停止计数器计数
{
ACC=state_flags;
//把状态标志字读到ACC便于进行位操作
Timer_flag1=1;
Timer_flag2=0;
state_flags=ACC;
//位操作完成,把ACC的内容写回状态标志字
a8155_CON=state_flags;
//写控制字,完成对8155的设置
}
//-----------------------------------------------------------------------
// 函数名称:rd_timer
// 输入函数:无
// 输出参数:time
// 功能说明:读计数值
//-----------------------------------------------------------------------
int rd_timer(void) //读计数值
{
int time;
char timea;
time=Timer_B;
timea=Timer_A;
time=time<<8;
time=((time&timea)&0x3F);
return(time);
}
//-----------------------------------------------------------------------
// 函数名称:setting_timerout_mode
// 输入函数:mode,范围0~3
// 输出参数:无
// 功能说明:设定 输出模式
//-----------------------------------------------------------------------
void setting_timerout_mode(unsigned char mode) //设定输出模式
{
Timer_B=(mode&0x03);
//写控制字