《51单片机应用开发范例大全(第3版)》——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引脚图,具体引脚说明如下。

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所示。

表中的“或”、“与”方式是指分别把输出的数据与被寻址端口的内容进行“逻辑或”以及“逻辑与”运算后再写入该端口。

2.8243与单片机的接口设计
8243的P2口负责传送控制命令、输入/输出数据,所以需要将P2口与单片机的I/O口直接相连,PROG信号另外用1个单片机的I/O接口产生,\overline{\rm{CS}}片选直接接地,保证8243始终选通。应用中,可以用一个I/O接口产生片选信号。具体接口电路如图2-8所示。

以下是单片机串口驱动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);
    //写控制字
}```
时间: 2024-09-22 18:31:44

《51单片机应用开发范例大全(第3版)》——2.2 扩展芯片实现端口扩展的相关文章

《51单片机应用开发范例大全(第3版)》——2.3 CPLD实现端口扩展

2.3 CPLD实现端口扩展 单片机与大规模CPLD有很强的互补性.单片机具有性价比高.功能灵活.易于实现人机对话和良好的数据处理能力等优点:CPLD/FPGA则具有高速度.高可靠性以及开发便捷.灵活等优点.以此两类器件相结合的电路结构在许多高性能仪器仪表和电子产品中已经被广泛应用. 单片机与CPLD/FPGA的接口方式一般有两种,即总线方式与独立方式. 1.总线方式 单片机以总线方式与CPLD/FPGA进行数据与控制信息通信有如下优点. (1)速度快.其通信工作时序是纯硬件行为,对于MCS-5

《51单片机应用开发范例大全(第3版)》——第2章 单片机接口的扩展

第2章 单片机接口的扩展 51单片机应用开发范例大全(第3版) 单片机输入/输出(I/O)接口是单片机和外部设备之间信息交换和控制的桥梁.它可以实现和不同外部设备的速度匹配,可以改变数据传送的方式,也可以改变信号的性质和电平等,可以根据不同的外设需要对输入/输出(I/O)接口进行扩展.本章主要结合具体的实例进行讲解,主要包括以下内容: 基本器件实现端口扩展: 扩展芯片实现端口扩展: cpld实现端口扩展.

《51单片机应用开发范例大全(第3版)》——导读

前 言 本书延续了之前两版的风格,书中内容安排基本一致,只是替换了某些应用实例和综合实例.本书内容仍然注重51单片机的技术实际应用,以提高读者的工程实践和开发能力为宗旨. 本书通过18个单片机C语言基础实例.79个单片机技术应用实例和3个综合实例,总计100个实例来讲解单片机的C语言基础知识和单片机开发应用技术.读者可以通过本书的实例快速掌握单片机的开发技术以及开发技巧. 本书分为14章,每章内容安排如下. 第1章介绍了51单片机的基础知识,主要讲解单片机的基本概念.硬件结构特点及应用,单片机的

《51单片机应用开发范例大全(第3版)》——第1章 单片机C语言开发基础 1.1 MCS-51单片机硬件基础

第1章 单片机C语言开发基础 单片微型计算机(Single Chip Micro Computer)现已正名为微控制器(MCU,Micro Controller Unit),单片机的称谓只是其习惯称呼.它把组成微型计算机的各功能部件(包括中央处理单元CPU.随机存储器RAM.只读存储器ROM.I/O接口电路.定时器/计数器以及串行口等)集成在一块电路芯片上.由于单片机的硬件结构与指令系统的功能都是按工业控制要求而设计的,因此常用在工业检测.控制装置中. 1.1 MCS-51单片机硬件基础 MCS

《51单片机应用开发范例大全(第3版)》——1.2 Keil Vision2

1.2 Keil Vision2 MCS-51单片机的开发除了需要硬件的支持以外,同样离不开软件.CPU真正可执行的是机器码,用汇编语言或C等高级语言编写的源程序必须转换为机器码才能运行,转换的方法有手工汇编和机器汇编两种,前者目前已极少使用.机器汇编是指通过汇编软件将源程序变为机器码的编译方法.这种汇编软件称为编译器.本节将向大家介绍目前十分流行的Keil Vision2. 1.2.1 Keil Vision2集成开发环境介绍 Keil Vision2是一个集成开发环境(Intergrated

《51单片机应用开发范例大全(第3版)》——1.1 MCS-51单片机硬件基础

1.1 MCS-51单片机硬件基础 MCS-51是指美国Intel公司生产的一系列单片机的总称.这一系列单片机包括很多种,如8031.8051.8751.8032.8052.8752等.其中8051是最早.最典型的产品,该系列其他单片机都是以8051为核心发展起来的,都具有8051的基本结构和软件特征.8051单片机内部包含了作为微型计算机所必需的基本功能部件,各部件相互独立地集成在同一块芯片上.其基本功能特性如下: 8位CPU: 32条双向可独立寻址的I/O线: 4KB程序存储器(ROM),外

《51单片机应用开发范例大全(第3版)》——2.1 基本器件实现端口扩展实例

2.1 基本器件实现端口扩展实例 目前,比较常用的串行口转换并行口的专用芯片有74LS165.CD4014等,并行口转换串行口的专用芯片有74LS164.CD4094等. 2.1.1 [实例20]用74LS165实现串口扩展并行输入口 一些低速的并行设备,如果直接和单片机连接,则浪费了宝贵的端口资源:如果先经过并行转换,然后以串行方式送入数据,则可以节省I/O端口.本设计就是通过74LS165,利用单片机串口,实现8位并行数据的输入. 1.74LS165与单片机接口电路设计74LS165有多种封

《51单片机应用开发范例大全(第3版)》——1.3 C51基础知识

1.3 C51基础知识 1.3.1 C51控制语句 C51语言中,有相关的控制语句,用以实现选择结构与循环结构. 选择控制语句:if语句和switch-case语句. 循环控制语句:for语句.while语句和do-while语句. 转移控制语句:break语句.continue语句和goto语句. 1.选择控制语句 在C51语言中选择结构主要是利用if语句和switch-case语句来实现的. (1)if语句的3种常用形式. C51语言中分支结构主要是应用if语句来实现的,if语句是对给定条件

《51单片机应用开发范例大全(第3版)》——1.4 【实例19】P1口控制直流电动机实例

1.4 [实例19]P1口控制直流电动机实例 利用P1口,编制程序输出一串脉冲,经放大后驱动小电动机,改变输出脉冲的电平及持续时间,达到使电动机正转.反转.加速.减速.停转之目的. 1.实例概述 可以通过74HC244输入开关量数据来控制小直流电动机的转动,实现正转4种转速,反转4种转速及停转.电路及连线如图1-31所示. 图1-31中P1.0连接74HC244的2A2.两个输出通过两个74HC32连接直流电动机电源.小直流电动机原理是:转动方向是由电压来控制的,电压为正则正转,电压为负则负转.