本节书摘来异步社区《51单片机应用开发范例大全(第3版)》一书中的第2章,第2.2节,作者:张杰 , 宋戈 , 黄鹤松 , 员玉良,更多章节内容可以访问云栖社区“异步社区”公众号查看
用串口扩展I/O口非常实用,但是串口是按位读取的,输入的数据必须重组后才能使用,速度受到限制,同时还需要严格的时钟配合。
在有些场合,利用串口扩展I/O口不是很理想,比如BCD码的输入及多组速率较高的并行数据的输入。所以,用并行数据端口扩展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口配置函数,具体代码如下://------------------------函数声明,管脚定义---------------------------------
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);//读PAunsigned char rd_PB(void);//读PBunsigned char rd_PC(void);//读PCvoid wr_PA(unsigned char PA_data);//写PAvoid wr_PB(unsigned char PB_data);//写PBvoid wr_PC(unsigned char PC_data);//写PCvoid set_PC(unsigned char PC_num);//PC位操作,置位,PC_num为端口号0~7void clr_PC(unsigned char PC_num);//PC位操作,复位,PC_num为端口号0~7void 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的读写函数、中断的开关函数和定时器相关函数,具体代码如下。//------------------------函数声明,管脚定义------------------------------*/
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);//读PAchar rd_PB(void);//读PBchar rd_PC(void);//读PCvoid wr_PA(unsigned char PA_data);//写PAvoid wr_PB(unsigned char PB_data);//写PBvoid wr_PC(unsigned char PC_data);//写PCvoid 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); //写控制字