使用74LS164和74LS165实现简单I/O扩展

1、I/O扩展:

如题,本人尝试使用74LS164和74LS165实现简单I/O扩展,使用Keil C51编写程序,并使用Proteus搭建模型进行验证。

1)、74LS164:8位串入、并出移位寄存器

①、DSA和DSB:其中之一接高电平做使能端,另一个接输入数据;

②、CP:时钟,每个上升沿数据右移一位;

③、MR:主复位,高电平对输出无影响,低电平强制所有输出为0;

④、Q0~Q7:8位并行输出。

2)、74LS165:8位并入、串出移位寄存器

①、SH/LD:移位与置位控制端;SH/LD置0,并行口的8位数据将被置入其内部的8个触发器,在SH/LD为1时,并行输入被封锁,移位操作开始;

②、INH:低电平时充许时钟输入,高电平时禁止时钟输入;

③、CLK:时钟输入,上升沿有效;

①、D0~D7:并行输入端。

⑤、SI:串行输入,用于扩展多个74LS165 的首尾连接端;

⑥、SO(QH):串行输出;

QH(反):也是串行输出端,它与 SO(QH)是反相的关系;

2、Proteus模型:

使用4片74LS164级联,作I/O扩展输出,共扩展32位;使用4片74LS165级联,作作I/O扩展输入。

实现功能:①、74LS164扩展I/O输出点亮4个数码管;

②、74LS165读取扩展I/O引脚状态;

③、在LCD1602中同步显示数码管显示的数字。

3、Keil C51程序:

1)、工程文件结构:

2)、main.c

/**

***************************************************************************************

* 文  件:工程主函数源文件

* 芯  片:AT89S52

* 晶  振:12MHz

* 创建人:hug567

* 时  间:2018-4-27

***************************************************************************************

*/

#include <regx52.h>  //52系列单片机头文件

#include "delay.h"  //延时函数实现头文件

#include "lcd1602.h"  //LCD1602实现头文件

sbit LS164_DATA = P0^0;  //74LS164数据线

sbit LS164_CLK = P0^1;  //74LS164时钟线

sbit LS165_SH_LD = P0^2;  //74LS165移位控制线

sbit LS165_DATA = P0^3;  //74LS165数据线

sbit LS165_CLK = P0^4;  //74LS165时钟线

//7段共阳极数码管段码,0~f,不带小数点

unsigned char tube[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};

unsigned char LED_Num[4]={0,0,0,0};  //存储4个数码管显示的数字

/**

***************************************************************************************

* 函  数:74LS164写32位扩展引脚函数

* 参  数:state:要写入的32位数据

* 返回值:无

***************************************************************************************

*/

void LS164_Write(unsigned long state)  //74LS164写32位扩展引脚函数

{

unsigned char i;

for(i=0;i<32;i++)  //循环写32位扩展引脚状态

{

LS164_CLK = 0;

if(state & 0x80000000)

{

LS164_DATA = 1;

}

else

{

LS164_DATA = 0;

}

LS164_CLK = 1;

state = state<<1;

}

}

/**

***************************************************************************************

* 函  数:74LS165读32位扩展引脚函数

* 参  数:无

* 返回值:state:读取的32位数据

***************************************************************************************

*/

unsigned long LS165_Read(void)  //74LS165读32位扩展引脚函数

{

unsigned char i;  //计数变量

unsigned long state = 0;  //读取的32位数据

LS165_SH_LD = 0;  //并行口的8位数据被置入内部的8个触发器

LS165_SH_LD = 1;  //并行输入被封锁,移位操作开始

for(i=0;i<32;i++)

{

state = state<<1;  //数据左移1位

if(LS165_DATA)  //若引脚为高电平

{

state |= 0x01;  //最低位置1

}

LS165_CLK = 0;

LS165_CLK = 1;  //上升沿数据移位

}

return state;  //返回读取的数据

}

/**

***************************************************************************************

* 函  数:读取当前4个数码管显示的数字

* 参  数:无

* 返回值:无

***************************************************************************************

*/

void Tube_Read(void)  //读取当前4个数码管显示的数字

{

unsigned char i,j;  //计数变量

unsigned long state;  //32位扩展引脚状态

state = LS165_Read();  //读取所有扩展引脚状态

LED_Num[0] = (unsigned char)(state&0xFF);  //扩展引脚EPA0~8状态

LED_Num[1] = (unsigned char)((state>>8)&0xFF);  //扩展引脚EPB0~8状态

LED_Num[2] = (unsigned char)((state>>16)&0xFF);  //扩展引脚EPC0~8状态

LED_Num[3] = (unsigned char)((state>>24)&0xFF);  //扩展引脚EPD0~8状态

for(i=0;i<4;i++)

{

for(j=0;j<16;j++)

{

if(LED_Num[i]==tube[j])  //根据引脚状态判断显示的数字

{

LED_Num[i] = j;  //数码管i+1显示的数字为j

break;

}

}

}

}

/**

***************************************************************************************

* 函  数:指定数码管显示指定数字函数

* 参  数:serial:数码管序号,1~4

* 参  数:num:指定显示的数字,0~9

* 返回值:无

***************************************************************************************

*/

void Tube_Write(unsigned char serial, unsigned char num)  //指定数码管serial显示指定数字num函数

{

unsigned long state;  //LS164需写的32位扩展引脚状态

if((serial>=1)&&(serial<=4)&&(num>=0)&&(num<=9))

{

state = LS165_Read();  //LS165读取当前所有扩展引脚状态

switch(serial)  //指定数码管serial

{

case 1:{  //指定第1个数码管显示数字num

state &= 0xFFFFFF00;

state |= tube[num];

break;

}

case 2:{  //指定第2个数码管显示数字num

state &= 0xFFFF00FF;

state |= (((unsigned long)tube[num])<<8);

break;

}

case 3:{  //指定第3个数码管显示数字num

state &= 0xFF00FFFF;

state |= (((unsigned long)tube[num])<<16);

break;

}

case 4:{  //指定第4个数码管显示数字num

state &= 0x00FFFFFF;

state |= (((unsigned long)tube[num])<<24);

break;

}

}

LS164_Write(state);  //输出所有32位扩展引脚状态

}

}

/**

***************************************************************************************

* 函  数:工程主函数

* 参  数:无

* 返回值:无

***************************************************************************************

*/

void main(void)  //工程主函数

{

unsigned char i,j;  //计数变量

LS164_Write(0xFFFFFFFF);  //所有数码管灭

LCD1602_Init();  //LCD1602模块初始化

while(1)

{

for(i=0;i<10;i++)  //4个数码管同时循环显示0~9

{

for(j=0;j<4;j++)  //分别指定4个数码管显示同一数字

{

Tube_Write(j+1,i);  //指定数码管显示数字

}

Tube_Read();  //读取数码管显示的数字

for(j=0;j<4;j++)  //在LCD1602中显示

{

LCD1602_DispChar(1,j+1,LED_Num[j]+48);  //LCD1602指定行列显示字符

}

delay_ms(2000);  //延时

}

}

}

3)、lcd1602.h

/**

***************************************************************************************

* 文  件:LCD1602模块实现头文件

* 创建人:hug567

* 时  间:2018-4-27

***************************************************************************************

*/

sbit LCD1602_RS = P2^0;  //LCD1602线

sbit LCD1602_RW = P2^1;  //LCD1602线

sbit LCD1602_E  = P2^2;  //LCD1602线

#define LCD1602_DATA P3  //LCD1602模块8位数据线

void LCD1602_isBusy    (void);  //LCD1602判断忙函数

void LCD1602_WriteCmd  (unsigned char);  //LCD1602写指令、写显示地址函数

void LCD1602_WriteData (unsigned char);  //LCD1602写数据函数

void LCD1602_DispChar  (unsigned char,unsigned char,unsigned char);  //LCD1602指定行列位置显示字符函数

void LCD1602_Init      (void);  //LCD1602初始化函数

4)、lcd1602.c

/**

***************************************************************************************

* 文  件:LCD1602模块实现源文件

* 创建人:hug567

* 时  间:2018-4-27

***************************************************************************************

*/

#include "reg52.h"  //52系列单片机头文件

#include "delay.h"  //延时函数实现头文件

#include "lcd1602.h"  //LCD1602实现头文件

/**

***************************************************************************************

* 函  数:LCD1602判断忙函数,忙则循环等待

* 参  数:无

* 返回值:无

***************************************************************************************

*/

void LCD1602_isBusy(void)  //LCD1602判断忙函数

{

unsigned char busy;  //忙标志

LCD1602_RS = 0;  //RS = 0

LCD1602_RW = 1;  //RW = 1

LCD1602_E = 0;  //E = 0

do{

LCD1602_E = 1;  //E = 1

delay_ms(5);  //延时5毫秒

busy = LCD1602_DATA & 0x80;  //读忙标志

LCD1602_E = 0;  //E = 0

}while(busy);  //忙则循环等待

}

/**

***************************************************************************************

* 函  数:LCD1602写指令、写显示地址函数

* 参  数:cmd:要写入的指令或显示地址

* 返回值:无

***************************************************************************************

*/

void LCD1602_WriteCmd(unsigned char cmd)  //LCD1602写指令、写显示地址函数

{

LCD1602_isBusy();  //判断忙,忙则循环等待

LCD1602_RS = 0;  //RS = 0

LCD1602_RW = 0;  //RW = 0

LCD1602_E = 0;  //E = 0

LCD1602_DATA = cmd;  //写入指令或显示地址

LCD1602_E = 1;  //E = 1

delay_ms(5);  //延时5毫秒

LCD1602_E = 0;  //E = 0

}

/**

***************************************************************************************

* 函  数:LCD1602写数据函数

* 参  数:num:要写入的8位数据

* 返回值:无

***************************************************************************************

*/

void LCD1602_WriteData(unsigned char num)  //LCD1602写数据函数

{

LCD1602_isBusy();  //判断忙,忙则循环等待

LCD1602_RS = 1;  //RS = 1

LCD1602_RW = 0;  //RW = 0

LCD1602_E = 0;  //E = 0

LCD1602_DATA = num;  //写入数据

LCD1602_E = 1;  //E = 1

delay_ms(5);  //延时5毫秒

LCD1602_E = 0;  //E = 0

}

/**

***************************************************************************************

* 函  数:LCD1602指定行列位置显示字符函数

* 参  数:x:行,1~2

* 参  数:y:列,1~16

* 参  数:chr:显示的字符,ASCII字符

* 返回值:无

***************************************************************************************

*/

void LCD1602_DispChar (unsigned char x,unsigned char y,unsigned char chr)  //LCD1602指定行x列y位置显示字符chr函数

{

LCD1602_WriteCmd(0x80+0x40*(x-1)+(y-1));  //显示字符位置:第x行第y列

LCD1602_WriteData(chr);  //显示指定字符chr

}

/**

***************************************************************************************

* 函  数:LCD1602初始化函数

* 参  数:无

* 返回值:无

***************************************************************************************

*/

void LCD1602_Init(void)  //LCD1602初始化函数

{

LCD1602_WriteCmd(0x38);  //显示模式:8位数据接口,两行显示,5*7点阵字符

LCD1602_WriteCmd(0x08);  //显示关,光标关,闪烁关

LCD1602_WriteCmd(0x01);  //清除DDRAM,清除AC(清屏)

LCD1602_WriteCmd(0x06);  //读数据后AC自动加1,读数据后画面不动

LCD1602_WriteCmd(0x0c);  //显示开,光标关,闪烁关

}

5)、delay.h

/**

***************************************************************************************

* 文  件:延时函数实现头文件

* 芯  片:AT89S52

* 晶  振:12MHz

* 创建人:hug567

* 时  间:2018-4-27

***************************************************************************************

*/

void delay_ms(unsigned long t);  //延时ms函数

6)、delay.c

/**

***************************************************************************************

* 文  件:延时函数实现源文件

* 芯  片:AT89S52

* 晶  振:12MHz

* 创建人:hug567

* 时  间:2018-4-27

***************************************************************************************

*/

#include "delay.h"  //延时函数实现头文件

/**

***************************************************************************************

* 函  数:延时ms函数

* 参  数:t:延时ms值,0~4294967295

* 返回值:无

***************************************************************************************

*/

void delay_ms(unsigned long t)  //延时ms函数

{

unsigned char i;  //计数变量

for(;t>0;t--)

for(i=200;i>0;i--);  //不精确,此处循环值需测试

}

4、程序下载地址:

1)、74LS164、74LS165实现I/O扩展的C程序:https://download.csdn.net/download/qtyt567/10377537

2)、74LS164、74LS165实现I/O扩展的Proteus程序:https://download.csdn.net/download/qtyt567/10377540

(0)

相关推荐