本节书摘来自异步社区《51单片机应用开发范例大全(第3版)》一书中的第2章,第2.1节,作者 张杰,宋戈,黄鹤松,员玉良,更多章节内容可以访问云栖社区“异步社区”公众号查看。
第2章 单片机接口的扩展
单片机输入/输出(I/O)接口是单片机和外部设备之间信息交换和控制的桥梁。它可以实现和不同外部设备的速度匹配,可以改变数据传送的方式,也可以改变信号的性质和电平等,可以根据不同的外设需要对输入/输出(I/O)接口进行扩展。本章主要结合具体的实例进行讲解,主要包括以下内容:
- 基本器件实现端口扩展;
- 扩展芯片实现端口扩展;
- cpld实现端口扩展。
2.1 基本器件实现端口扩展实例
目前,比较常用的串行口转换并行口的专用芯片有74LS165、CD4014等,并行口转换串行口的专用芯片有74LS164、CD4094等。
2.1.1 【实例20】用74LS165实现串口扩展并行输入口
一些低速的并行设备,如果直接和单片机连接,则浪费了宝贵的端口资源;如果先经过并行转换,然后以串行方式送入数据,则可以节省I/O端口。本设计就是通过74LS165,利用单片机串口,实现8位并行数据的输入。
1.74LS165与单片机接口电路设计
74LS165有多种封装,它们在功能上并没有什么差别,可以根据实际需要选择合适的封装。图2-1所示是74LS165的引脚图。
图2-2所示是8051单片机与74LS165的接口电路。8051单片机的串口工作于模式0,为同步移位寄存器输入/输出方式,收/发的数据为8位,低位在前,无起始位、奇偶校验位和停止位。串行数据从RXD(P3.0)输入,移位时钟由TXD(P3.1)输出。端口线P1.7用于控制74LS165的工作状态。当P1.7输出低电平时,74LS165将并行数据置入寄存器中;当P1.7输出高电平时,74LS165工作在时钟控制下的串行移位状态,数据通过RXD(P3.0)移入8051单片机。
注意
由于单片机的串口资源有限,在串口被占用的情况下,也可以利用I/O口来模拟时序,实现移位寄存,这是一种简单可行的方法。串行数据从P1.5输入,移位时钟脉冲可由P1.6输出,还是用P1.7来控制74LS165的工作状态。
2.用串口驱动74LS165
利用单片机串口实现输入移位寄存器,只需用软件置REN = 1(同时RI =0),即开始接收。数据字节在移位时钟脉冲的配合下,从低位至高位一位一位地接收下来并装入SBUF中,在启动接收过程(即写SCON,清RI位)开始后的第8个机器周期RI被置位。这一数据帧接收完毕,可进行下一帧的接收。
在模式0下,数据传输速率为fosc/12,fosc是时钟频率。时钟频率为12MHz时串行数据传输速率为1Mbit/s,速度较快,故程序中对接收过程采取查询等待方式。如果有必要,应该用中断控制方式以提高程序速率。
需要特别注意,在工作模式0下,必须将 SCON的SM2位清零。
单片机串口驱动74LS165的程序主要包括函数声明管脚定义部分、串口初始化函数以及数据接收函数。
(1)函数声明管脚定义。
函数声明管脚定义部分主要完成程序所涉及的库函数的声明及有关引脚的定义,一般置于程序的开头部分,代码如下:
//------------------库函数声明,管脚定义--------------------------------------
#include<reg52.h>
sbit LOAD=P1^7;
//用P1^7控制SH/ 管脚
(2)串口初始化函数UART_init()。
串口初始化函数UART_init()实现串口的初始化,包括工作方式选择和中断的开禁等功能,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:UART_init()
// 功能说明:串口初始化,设定串口工作在方式0
//-----------------------------------------------------------------------
void UART_init(void)
{SCON=0x10;//设串行口方式0,允许接收,启动接收过程ES=0;//禁止串口中断
}
(3)数据接收函数PA()。
数据接收函数PA()能够完成8位串行数据的接收,代码如下:
//-----------------------------------------------------------------------
// 函数名称:PA()
// 输入参数:无
// 输出参数:返回由并口输入的数据
// 功能说明:接收8位串行数据
//-----------------------------------------------------------------------
unsigned char PA(void)
{unsigned char PA_data;LOAD=0;//当P1.7输出低电平,74LS165将并行数据装入寄存器当中LOAD=1;//当P1.7输出高电平,74LS165在时钟信号下进行移位UART_init();//74LS165工作在时钟控制下的串行移位状态while(RI==0);//循环等待RI=0;PA_data=SBUF;return PA_data;//返回并行输入的数据
}
3.用I/O端口驱动74LS165
单片机的串口工作在模式0,只是作为同步移位寄存器。如果能够直接用I/O模拟移位寄存器的时序,同样能驱动74LS165,实现并行数据的输入。如图2-2所示,P1.5被用于串行数据输入,P1.6用于移位时钟输出,P1.7用来控制74LS165的工作状态。
单片机I/O端口驱动74LS165主要包括函数声明管脚定义部分、数据输入函数以及数据输出函数。
(1)函数声明管脚定义。
函数声明管脚定义部分主要完成程序所涉及的库函数的声明及有关引脚的定义,一般置于程序的开头部分,代码如下:
//----------------------------库函数声明,管脚定义---------------------------
#include<reg52.h>
sbit a7=ACC^7;
sbit simuseri_CLK=P1^6;
//用P1^6模拟串口时钟
sbit simuseri_DATA=P1^5;
//用P1^5模拟串口数据
sbit drive74165_LD=P1^7;
//用P1^7控制SH/ 管脚
(2)数据输入函数in_simuseri()。
数据输入函数in_simuseri()能够实现8位数据的从低位到高位的串行输入,程序代码如下所示:
//-----------------------------------------------------------------------
// 函数名称:in_simuseri()
// 输入参数:无
// 输出参数:data_buf
// 功能说明:8位同位移位寄存器,将simuseri_DATA串行输入的数据按从低位到
// 高位
// 保存到data_buf
//-----------------------------------------------------------------------
unsigned char in_simuseri(void)
{unsigned char i;unsigned char data_buf;i=8;do{ACC=ACC>>1;for(;simuseri_CLK==0;);a7= simuseri_DATA;for(;simuseri_CLK==1;);}while(--i!=0);simuseri_CLK=0;data_buf=ACC;return(data_buf);
}
(3)数据输出函数PAs()。
数据输出函数PAs()能够实现数据的并行输出,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:PAs()
// 输入参数:无
// 输出参数:PAs _buf,返回并行输入74LS165的数据
// 功能说明:直接调用,即可读取并行输入74LS165的数据,不需要考虑74LS165的
// 工作原理
//-----------------------------------------------------------------------
unsigned char PAs(void)
{unsigned char PAs_buf;drive74165_LD=0;drive74165_LD=1;PAs_buf= in_simuseri();return(PAs_buf);
}
2.1.2 【实例21】用74LS164实现串口扩展并行输出口
在单片机应用系统中,并行输入的接口设备很多,如果设备数据传输速率不是很高,可以在单片机输出后端预处理。如果利用串并转换接口,单片机只需要串行输出,就可以满足接口设备并行输入的需要。本设计采用74LS164,通过单片机的串口实现串口转换为并口输出。
1.74LS164与单片机接口电路设计
图2-3所示是74LS164的引脚图。具体各引脚说明如下。
- VCC:电源。
- GND:接地端。
- QA~QH:并行输出口。
- B:串行输入口。
- CLK:时钟输入。
- CLR:清零位,当为低电平时,并行输出口上均为低电平“0”。
74LS164与8051单片机接口电路如图2-4所示。当51系列单片机的串行口工作在方式0的发送状态下,串行数据由P3.0(RXD)送出,移位时钟由P3.1(TXD)送出。在移位时钟的作用下,串行口发送缓冲器的数据一位一位地移入74LS164。
如果串行口被其他设备占用,可以用普通I/O口模拟移位寄存器的时序向74LS164发送数据。实践证明,这是一种方便、经济、可行的方法。P1.5用于输出串行数据,P1.6用于移位时钟输出。
在实际应用中,还应注意,74LS164没有并行输出控制端,在串行输入数据时,并行输出口会不断变化。如果需要,可在74LS164的输出端加接输出三态门控制,以便保证串行输入结束后再输出。
2.用串口驱动74LS164
单片机的串口工作在模式0,只是作为同步移位寄存器。RXD(P3.0)用于串行数据输出,TXD(P3.1)用于移位时钟输出,P1.7用来控制74LS164的工作状态。
单片机串口驱动74LS164的程序主要包括函数声明管脚定义部分、串口初始化函数以及数据发送函数。
(1)函数声明管脚定义。
函数声明管脚定义部分主要完成程序所涉及的库函数的声明及有关引脚的定义,一般置于程序的开头部分,代码如下:
//-------------------------------------库函数声明,管脚定义
#include <reg52.h>
sbit CLR=P1^7;
//用P1^7控制CLR
(2)串口初始化函数UART_init()。
串口初始化函数UART_init()能够实现串口的初始化,包括工作方式选择和中断的开禁等功能,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:UART_init()
// 功能说明:串口初始化,设定串口工作在方式0
//-----------------------------------------------------------------------void UART_init(void)
{SCON =0x00;//没串行口方式0,允许发送,启动发送过程ES=0;// 禁止串口中断
}
(3)数据发送函数PA_out()。
数据发送函数PA_out()能够完成8位数据由串口串行发出,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:PA_out()
// 输入参数:PA_data,需要从74LS164并行口输出的数据
// 输出参数:无
// 功能说明:发送8位串行数据至并口
//-----------------------------------------------------------------------void PA_out(unsigned char PA_data)
{CLR=0;//并口输出清零CLR=1;//开始串行移位UART_init();//74LS165工作在时钟控制下的串行移位状态while(TI==0);//循环等待TI=0;SBUF=PA_data;
}
3.用I/O端口驱动74LS164
74LS164工作时,在移位时钟CLK的作用下,串行口送入的数据一位一位地移入。用单片机的P1.6口输出移位脉冲,用P1.5口输出串行数据,同样可以驱动74LS164工作。如图2-4所示,74LS164的清零端CLR由单片机P1.7控制。
单片机I/O端口驱动74LS164主要包括函数声明管脚定义部分、数据输入函数以及数据输出函数。
(1)函数声明管脚定义。
函数声明管脚定义部分主要完成程序所涉及的库函数的声明及有关引脚的定义,一般置于程序的开头部分,代码如下:
//-------------------------库函数声明,管脚定义-----------------------------
#include <reg52.h>
sbit simuseri_CLK=P1^6;
//用P1^6模拟串口时钟
sbit simuseri_DATA=P1^5;
//用P1^5模拟串口数据
sbit drive74164_CLR=P1^7;
//用P1^7控制CLR
sbit a0=ACC^0;
(2)数据输入函数out_simuseri ()。
数据输入函数out_simuseri ()将8位数据的从低位到高位的逐位输入simuseri_DATA当中,程序代码如下所示:
//-----------------------------------------------------------------------
// 函数名称:out_simuseri
// 输入参数:data_buf
// 输出参数:无
// 功能说明:8位同步移位寄存器,将data_buf的数据逐位输出到simuseri_DATA
//-----------------------------------------------------------------------void out_simuseri(char data_buf)
{char i;i=8;ACC=data_buf;do{simuseri_CLK=0;simuseri_DATA=a0;simuseri_CLK=1;ACC=ACC>>1;}while(--i!=0);simuseri_CLK=0;
}
(3)数据输出函数PA_out ()。
数据输出函数PA_out ()能够实现数据的并行输出,程序代码如下:
//-----------------------------------------------------------------------
// 函数名称:PA_out
// 输入参数:Pseri_out,需要输出的8位数据
// 输出参数:无
// 功能说明:将Pseri_out中的数据送到74165并行口A-G输出
//-----------------------------------------------------------------------void PA_out (char Pseri_out )
{drive74164_CLR =0;//并口输出清零drive74164_CLR =1;//开始串行移位out_simuseri(Pseri_out);
}
2.1.3 【实例22】P0 I/O扩展并行输入口
CPU操作指令为:
#define 244_addr xbyte[0XFEFF]
unsigned char I/O_DATA;
I/O_DATA=244_addr;
2.1.4 【实例23】P0 I/O扩展并行输出口
I/O端口对应的地址为:
1111 1101 1111 1111 B=FDFFH
CPU操作指令为:
#define 273_addr xbyte[0XFDFF]
unsigned char I/O_DATA;
244_addr =I/O_DATA;