2019-06-25 09:34:39 weixin_42625444 阅读数 354
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3408 人正在学习 去看看 朱有鹏

 

关注【电子开发圈】微信公众号,一起学习吧!

电子DIY、Arduino、51单片机、STM32单片机、FPGA……
电子百科、开发技术、职业经验、趣味知识、科技头条、设备拆机……

点击链接,免费下载100G+电子设计学习资料!

http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==&hid=7&sn=ad5d5d0f15df84f4a92ebf72f88d4ee8&scene=18#wechat_redirect

 

 

并行I/O口扩展实例

//《51单片机原理及应用(第二版)——基于Keil C与Proteus》第四章例4.4

I/O口不能完全用于输入/输出操作,当需要扩展外部存储器时,P0、P2口用作地址总线和数据总线,此时能用的I/O口就只有P1和P3口,如果再使用串行通信,I/O口就不够使用了,需要扩展I/O口

两种方式:

① 采用普通锁存器、三态门等芯片来进行简单的扩展(如74LS373或74LS244等)

② 采用可编程的I/O芯片来扩展(如8255或8155等)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

要求:用4个开关控制4个LED的亮灭状态,其中采用74LS244控制开关的输入,采用74LS373控制LED输出

代码:

电路图:

过程分析:

过程://右键点击图片"在新标签页中打开图片"即可放大

  1. #18 P0=0xff; 通过拉高P0口置为1111 1111,即关闭LED灯;
  2. #19 com=0; 对CD4071 U2.A的1和CD4071 U2.B的5输入一个0;
  3. #20 rd = 0; 对CD4071 U2.A的2输入一个0;
  4. 由于CD4071的功能是执行逻辑或运算,1和2分别输入0,则3输出0给74LS244的OE口;
  5. 74LS244缓冲驱动器的OE为使能端,低电平是有效的,高电平时输出为三态,此时通过CD4071传给OE低电平,使开关的电平可以通过74LS244传入P0;
  6. 开关状态传入P0;
  7. #21 temp = P0; 通过定义的unsigned char temp来存放P0的状态
  8. #22 rd = 1; 对CD4071 U2.A的2输入一个1;
  9. 1=0;2=1;此时或门输出为1;
  10. 74LS244的OE端高电平时输出为三态,关闭74LS244
  11. #23 wr = 1;对CD4071 U2.B的6输入一个1;
  12. 此时5=0;6=1;CD4071 U2.B的7输出一个1;
  13. 74LS343的OE为低电平时,用作地址锁存器;LE为高电平时,输出Q0-Q7状态与D0-D7状态相同;当LE发生负跳变(1->0)时,输入端的D0-D7锁入Q0-Q7
  14. #24 P0 = temp; temp中的数据存入P0,传送到74LS343的D端;
  15. #25 wr = 0; 对CD4071 U2.B的6输入一个0;
  16. 此时5=0;6=0;CD4071 U2.B的7输出一个0;
  17. LE的状态从1->0,发生负跳变,输出Q0-Q7
  18. LED等接受到开关的情况,发生亮灭。

       

实验结果

 

https://www.cnblogs.com/hughdong/p/6734899.html

2015-02-26 22:35:12 u013030441 阅读数 3439
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3408 人正在学习 去看看 朱有鹏

       由于单片机的I/O口和有限则免不了需要做一些外部的I/O扩展,下面对其进行一些总结。对I/O口扩展的方法有很多,现从74HC59574HC165两种芯片来做一些说明。

74HC595是一个串入转并出的芯片,数据通过一个I/O串行输入后经过74HC595后将数据8位并行输出。下图是它的各引脚原理图。


       74HC595的第14引脚接单片机的P3.4引脚,用来做数据的串行输入口,数据通过该引脚传入到74HC595中;其第11引脚接单片机的P3.6引脚,做串行时钟线,在该引脚的一个上升沿上可以做数据的锁存,将8位数据依次锁存到74HC595中;其第12引脚接单片机的P3.5引脚,用来做数据的并行输出控制,在引脚的上升沿时将8位数据一同并行输出。参考代码如下:

<span style="font-size:18px;">#include <reg51.h> 
#include <intrins.h>
#define  NOP() _nop_()  /* 定义空指令 */

//SPI IO
sbit MOSIO =P3^4;  //串行数据线
sbit R_CLK =P3^5;  //数据并行输出控制
sbit S_CLK =P3^6;  //串行时钟线


void delay(unsigned int i);      //函数声名
void HC595SendData(unsigned char SendVal);  //函数声名
 
// 此表为 LED 的字模 
unsigned char code LED7Code[] = {~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F,~0x77,~0x7C,~0x39,~0x5E,~0x79,~0x71};
 
main()
{  unsigned char  HC595SendVal;
   unsigned int LedNumVal;
   
  while(1)
  {	 
	 	LedNumVal++;
		HC595SendVal = LED7Code[LedNumVal%16];     //LED7;显示0-F  LedNumVal%10 显示0-9
	    HC595SendData(HC595SendVal); //调用595驱动函数	
  		delay(200);
	 
  }   
}	
/*******************延时函数************/
void delay(unsigned int i)
{
    unsigned int j;
    for(i; i > 0; i--)	 //CPU循环执行i*300次
        for(j = 300; j > 0; j--);
}


/*********************************************************************************************************
** 函数名称: HC595SendData
** 功能描述: 向SPI总线发送数据
*********************************************************************************************************/
void HC595SendData(unsigned char SendVal)
{  
  unsigned char i;
		
  for(i=0;i<8;i++) 
   {
	if((SendVal<<i)&0x80) MOSIO=1; //set dataline high  0X80  最高位与SendVal左移的最高位 进行逻辑运算
	else MOSIO=0;				   // 如果为真 MOSIO = 1  
 
	S_CLK=0;  
	NOP();	 //短暂延时产生一定宽度的脉冲信号
	NOP();	 //短暂延时
	S_CLK=1;
		
   }

 
  R_CLK=0; //set dataline low
  NOP();  //短暂延时
  NOP();  //短暂延时
  R_CLK=1; 	//
	

}</span>

    74HC165是一个并入转串出的芯片,它可以将采集的8位数据通过一个I/O接口串行的输出,它的引脚原理图如下;


    74HC165的第9引脚接单片机的P1.7,通过该引脚将数据送入到单片机中处理;其第2引脚和第15引脚通过一个或非门连接后连接74HC165,所以第2和第15引脚功能可以互换一起连接到单片机的P3.6引脚做时钟控制;它的第1引脚接单片机的P1.6引脚,用来控制将8位数据锁存到74HC165中。参考代码如下:

<span style="font-size:18px;"> #include <reg52.H>
 #include <intrins.h> 

 #define  NOP()   _nop_()   /* 定义空指令 */

//SPI 接口
sbit    CLK    = P3^6;	   //串行时钟
sbit    IN_PL  = P1^6;    //把数据加载到锁存器中
sbit    IN_Dat = P1^7;    //数据通过P1.7脚移进单片机内处理

sbit    RELAY  = P1^4;
sbit    BEEP   = P1^5;

unsigned char bdata Key;
sbit    K0=Key^0;	 //位定义
sbit    K1=Key^1;	 
sbit    K2=Key^2;	 
sbit    K3=Key^3;	 
sbit    K4=Key^4;
sbit    K5=Key^5;
sbit    K6=Key^6;
sbit    K7=Key^7;

bit  M0 ,K0J;  //位定义
unsigned long ReHC74165(void);	 //函数声名 
void beep();    

 /********************************************************
 *	主函数												 *
 *														 *
 ********************************************************/
 main()
  {	  

  while(1)
  { 
    <span style="white-space:pre">	</span> unsigned long  Input=ReHC74165();//调用165驱动程序
	 Key=Input&0xff;  //将数据传给位变量
	 RELAY = 1;
     <span style="white-space:pre">	</span> P2 = 0xff;	  //清除

	  //实现脉冲输入
    <span style="white-space:pre">	</span>if(K0&K0J)M0=~M0;      
	K0J=~K0;  
  
	if(M0) {RELAY = 0; P2 = 0x7f;	}//实现脉冲输入
	if(K1) {beep();    P2 = 0xbf;   }//K1 为1时开启蜂鸣器和2个灯
	if(K2) {beep();    P2 = 0xdf;   }
    <span style="white-space:pre">	</span>if(K3) {beep();    P2 = 0xef;   }
	if(K4) {beep();    P2 = 0xf7;   }
	if(K5) {beep();    P2 = 0xfb;   }
	if(K6) {beep();    P2 = 0xfd;   }
	if(K7) {beep();    P2 = 0xfe;   }
   }    
   
 }

 /**********************************************************
 *														   *
 *	蜂鸣器 (让蜂鸣器发出动听声音)						   *
 ************************************************************/
void beep()
{
  unsigned char i , j;
  for (i=0;i<2;i++)
   {
  
   for (j = 0; j<255; j++) {_nop_();}
   BEEP=!BEEP;                 //BEEP取反
   } 
   BEEP=1;                     //关闭蜂鸣器
}
 
 /*************************此部分为74HC165的驱动程序使用SPI总线连接*************************************/


/*********************************************************************************************************
** 函数名称:  74HC165
** 功能描述: 
** 管脚描述:请参考相关的芯片资料(学习光盘中以配)
*********************************************************************************************************/
unsigned long ReHC74165(void)
{  
  unsigned char i;
  unsigned int indata;
		
   IN_PL=0;
   NOP();    //短暂延时 产生一定宽度的脉冲
   IN_PL=1;	 //将外部信号全部读入锁存器中
   NOP(); 
              
   indata=0;   //保存数据的变量清0  
   for(i=0; i<8; i++)
    { 
	indata=indata<<1;	 //左移一位
      <span style="white-space:pre">	</span>if(IN_Dat==1)indata=indata+1;	//如果IN_Dat检测到高电平 保存数据的变量加1 
 	CLK=0;   //时钟置0
      <span style="white-space:pre">	</span>NOP();
      <span style="white-space:pre">	</span>CLK=1;   //时钟置1	  
    } 
   
   return(~indata);	 //将保存数据的变量取反后返回
}</span>





2018-04-27 08:42:50 qtyt567 阅读数 3138
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3408 人正在学习 去看看 朱有鹏

使用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


2010-03-25 22:21:00 yt198808 阅读数 1275
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3408 人正在学习 去看看 朱有鹏

     在单片机的学习和开发中,I/O往往是大家接触最多的,故把它们比作心灵的窗口一点都不为过,日常应用中发现很多人对其不甚了解,有些很重要的知识含混不清,这对我们的后续的开发和更高级的处理器学习显然是不利的,现在就让我们以MCS51为例一起揭开I/O口的神秘面纱。

    MCS51单片机有四个八位I/O口,分别为P0到P3,为方便说明,我分2个部分来描述。

     一:结构(由于系统原因,若图片不能加载请到我的相册中查看。)

      p0口结构图

      上图是p0口一位的结构图,为三态双向口,从图上我们可以看到,它包括1个输出锁存器、2个三态缓冲器、1个输出驱动电路和1个输出控制端。输出驱动电路由一对场效应管T1、T2组成,其工作状态由输出控制端决定。

      ~~当做地址/数据复用总线用时,控制端置高,与门开锁,内部总线上的地址或数据信号分别通过与门和非门去驱动T1管和T2管,构成推挽结构,可以把它想象成生活中的跷跷板, 高电平时,T1打开,T2截止,输出为高;低电平时,T1截止,T2打开,输出接地输出为低。这种情况下不用外接上拉电阻.而且,当T1打开,T2截止,输出高电平时,内部电源直接通过T1输出到p0口线上,因此驱动能力较大,可驱动8个LSTTL门电路。(关于驱动能力下文中会单独介绍)

      ~~当做通用I/O口使用时(当然这里是对有内部ROM的单片机而言),控制端置低,与门输出为低使输出级T1管截止,由于此时输出级为漏极开路电路,所以课本上往往会说当驱动NMOS电路时应接上拉电阻;做输入口时应先将锁存器写1,使T2管也截止,这时呈现高阻抗,单片机通过三态输入缓冲器读取引脚信号,我们可以理解为主窗口关上了,开了一扇副窗口,原来单片机也会开小灶o(∩_∩)o 哈哈。

      ~~读--修改--写。锁存器Q端的数据是通过上面的三态缓冲器读取的。这里要特别提醒大家,向各端口的写数据均写入到对应端口的锁存器中, 但对各端口的读操作却有两个方式:读锁存器和读引脚。在MCS-51指令系统中有些指令读锁存器的值, 有些指令则读引脚上的值。读锁存器指令是从锁存器中读取一个值并进行处理, 把处理后的值(原值或已修改后的值)重新写入锁存器中。这类指令称为读-修改-写指令http://www.neoic.cn/html/embed/mcu/200811/24-2790.html点击这个链接会有详细的此类指令介绍及准确读取锁存器值的方法,很好很重要,这里就不啰嗦了,嘿嘿。

     p1口结构图

     P1口作通用I/O口使用时与P0口有所不同,由上图我们也可以看到它在内部有一个上拉电阻。做输入时也是必须应先将锁存器写1,以使场效应管截止,该口线既可由内部上拉电阻提拉成高电平又同时可被外部输入源拉低,显然现在是外部输入说了算,它高就高它低就低,当上老大各了,哈哈,当然由于有内部上拉电阻,自然不用外家上拉电阻就有一定的驱动能力,可驱动4个LSTTL门电路,注意了,由于内部上拉电阻较大,电流太小,有时因为电流不够,也会再并一个上拉电阻以提高驱动能力。

     P2口结构图

     P2口是P1口的孪生兄弟,只是比它哥多了个“痣”---一个输出控制部分,通用I/O功能这里不再累赘(细心的朋友会发现结构稍有不同,留给大家自己分析)。当系统接外部存储器扩展时,P2口用于输出高八位地址,当然接外部数据存储器和外部程序存储器情况是有所不同的,若外接数据存储器容量为256B,则有效访问地址为8位,则可使用MOVX@Ri类指令由P0口送出8位地址,P2口上引脚信号自然在整个访问外部存储器期间不会改变,所以这种情况P2口还可做通用I/O口用;若外部数据存储器大于256B,需用MOVX@DPTR类指令由P0口和P2口送出16位地址。在读写周期内P2口引脚上会保持地址信息,但由结构可知输出地址时锁存器并不锁存1,其内容也不会改变,故访问结束后,其锁存器内容会重新出现在引脚上。

      p3口结构图

     还剩最后一个了,P3口是一个双功能口,通用I/O和第二功能口,这里不再啰嗦。

 

 

    二:负载能力和接口要求

      这里我整理了一下,P0做地址数据复用总线时每位口可驱动8个LSTTL门电路,当作通用I/O口时输出级漏极开路需外接上拉电阻。

       P1~P3口有内部上拉电阻,每位口线可驱动4个LSTTL门电路,输入端都可被任何TTL和MOS电路所驱动,可直接被集电极和漏极开路所驱动。

       CHMOS端口提供几毫安输出电流,故当作输出驱动一个普通晶体管的基极时,应在端口与晶体管基极之间串个电阻(要基极电流在uA级阻值大约多少?自己算一下哈)以限制高电平输出时的电流。

 

 

 

     有不妥之处请多指教

 

 

    

   

2017-04-19 18:29:00 weixin_30797027 阅读数 65
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3408 人正在学习 去看看 朱有鹏

并行I/O口扩展实例

//51单片机原理及应用(第二版)——基于Keil C与Proteus》第四章例4.4

I/O口不能完全用于输入/输出操作,当需要扩展外部存储器时,P0、P2口用作地址总线和数据总线,此时能用的I/O口就只有P1和P3口,如果再使用串行通信,I/O口就不够使用了,需要扩展I/O口

两种方式:

① 采用普通锁存器、三态门等芯片来进行简单的扩展(如74LS373或74LS244等)

采用可编程的I/O芯片来扩展(如8255或8155等)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

要求:用4个开关控制4个LED的亮灭状态,其中采用74LS244控制开关的输入,采用74LS373控制LED输出

代码:

电路图:

过程分析:

过程://右键点击图片"在新标签页中打开图片"即可放大

  1. #18 P0=0xff; 通过拉高P0口置为1111 1111,即关闭LED灯;
  2. #19 com=0; CD4071 U2.A1CD4071 U2.B5输入一个0
  3. #20 rd = 0; CD4071 U2.A2输入一个0
  4. 由于CD4071的功能是执行逻辑或运算,12分别输入0,则3输出074LS244OE口;
  5. 74LS244缓冲驱动器的OE为使能端,低电平是有效的,高电平时输出为三态,此时通过CD4071传给OE低电平,使开关的电平可以通过74LS244传入P0
  6. 开关状态传入P0
  7. #21 temp = P0; 通过定义的unsigned char temp来存放P0的状态
  8. #22 rd = 1; CD4071 U2.A2输入一个1
  9. 1=02=1;此时或门输出为1
  10. 74LS244OE端高电平时输出为三态,关闭74LS244
  11. #23 wr = 1;CD4071 U2.B6输入一个1
  12. 此时5=06=1CD4071 U2.B7输出一个1
  13. 74LS343OE为低电平时,用作地址锁存器;LE为高电平时,输出Q0-Q7状态与D0-D7状态相同;当LE发生负跳变(1->0)时,输入端的D0-D7锁入Q0-Q7
  14. #24 P0 = temp; temp中的数据存入P0,传送到74LS343D端;
  15. #25 wr = 0; CD4071 U2.B6输入一个0
  16. 此时5=06=0CD4071 U2.B7输出一个0
  17. LE的状态从1->0,发生负跳变,输出Q0-Q7
  18. LED等接受到开关的情况,发生亮灭。

       

实验结果

转载于:https://www.cnblogs.com/hughdong/p/6734899.html

单片机I/O口使用

阅读数 1273

没有更多推荐了,返回首页