单片机的I/O数量有限,很多时候在实际应用中你要考虑共用端口(比如小尺寸控制器,不方便用管脚多的单片机,另外体积大)。
大家看了下面的电路图后就会发现:LCD的数据信号线占用了P1,而P1同时又是三个按键的输入端口。这样可能有的朋友会说,用LCD的同时也想使用P1当其他控制信号输入端,岂不是冲突,不!可以的。因为您的思想停留在一个概念:I/O要么只能当输入、要么当只能输出。单片机不是那么没用的,要不然不会很多人好奇地去学,关键是你怎么去控制,在此我提示一下:一个端口在当输入的时候也能实现输出,关键的因素是要找个时间差来错过冲突。
(电路接线方法示意图,共用端口也无妨,功能照样去做)
首先,LCD和LED数码管不同,只要您保持了电源,即使断开了信号它也能保持当前显示状态。好的,第一个疑问解除了。在您送了显示的更新数据后,立刻把P1全部设为1,为P1做其他输入信号创造条件,然后紧接着检测输入状态(比如按键),检测到后执行你想做的其他事情,如果是显示,就给出更新显示命令,完成一个事情后继续把P1设为1,接下来再做检测、就是以此类推的意思。单片机的执行速度非常快,看似有些步骤,其实反应就是瞬间的事。
(按键计数1602显示截图)
//====================================================================
// 程序名称:LCD1602显示计数( 0~9999计数范围 ) 程序
// 本程序不包含EEPROM测试,XY-30B另有EEPROM应用范例
// 实验对象:XY-30B单片机工控板(点击查看产品), 核心:STC12C5A16S2
// 功 能:
// 每按一次开关KEY1(接X0)键,LCD1602便更新显示加1后的数据;
// 每按一次开关KEY2(接X1)键是减1操作,按KEY3(接X2)键是清零操作。
// 编 写:kenny Wan http://www.mcusy.cn
//===================================================================*/
#include <stc_new_8051.H> //新一代1T型51单片机头文件
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
//-------------------------------------------------------------------
#define DataPort P1 // LCD接口
sbit RS = P0^5; // LCD读写控制
sbit RW = P0^6;
sbit EN = P0^7;
sbit KEY1 = P1^0; // 加1按键(也可接传感器数字计数信号)
sbit KEY2 = P1^1; // 减1按键
sbit KEY3 = P1^2; // 清0按键
uchar code tab0[] = {" MODEL : XY-30B "}; // 第一行
uchar code tab1[] = {" Q =00000 "}; // 第二行
uint count;
//====================== 延时程序 ===========================//
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=925;y>0;y--); //12M晶体/约1MS为初
}
//=================函数名:LCD检测信号======================//
void WaitEnable(void)
{
DataPort = 0xff;
RS = 0; RW = 1; _nop_();
EN=1; _nop_(); _nop_();
while(DataPort&0x80);
EN=0;
}
//------------------- 函数名:写命令到LCD -------------------//
void WriteCommand(uchar Order,uchar Attribc)
{
if(Attribc)WaitEnable();
RS = 0; RW = 0; _nop_();
DataPort=Order; _nop_();
EN = 1;
_nop_(); _nop_();
EN = 0;
}
//------------------- 函数名: LCD写数据 --------------------//
void WriteData(uchar dataW)
{
WaitEnable();
RS = 1; RW = 0; _nop_();
DataPort=dataW; _nop_();
EN = 1;
_nop_(); _nop_();
EN = 0;
}
//------------ 函数名:显示指定坐标的一个字符 -------------//
void DisplayOne(uchar X,uchar Y,uchar DData)
{
Y&=1; X&=15;
if(Y)X|= 0x40; X|= 0x80;
WriteCommand(X,0);
WriteData(DData);
}
//------------- 函数名:显示指定坐标的一串字符 --------------//
void DisplayList(uchar X,uchar Y,uchar code *DData)
{
uchar ListLength=0;
Y&= 0x1; X&= 0xF;
while(X<=15)
{
DisplayOne(X,Y,DData[ListLength]);
ListLength++; X++;
}
}
//----------------- 函数名: LCD初始化 --------------------//
void InitLcd()
{
char n;
WriteCommand(0x38,1);
WriteCommand(0x08,1);
WriteCommand(0x01,1);
WriteCommand(0x06,1);
WriteCommand(0x0c,1);
for(n=15;n>=0;n--) //显示第一行
{DisplayList(n,0,tab0); delay(100);}
for(n=15;n>=0;n--) //显示第二行
{DisplayList(n,1,tab1); delay(100);}
}
//============= 函数名:LCD显示函数 ==============//
void display(uint count)
{
uchar w,q,b,s,g; //分离的变量
w=count/10000+0x30; //万位数
q=count/1000%10+0x30; //千位数
b=count/100%10+0x30; //百位数
s=count/10%10+0x30; //十位数
g=count%10+0x30; //个位数
//以下为显示位置
DisplayOne(4,1,w);_nop_();
DisplayOne(5,1,q);_nop_();
DisplayOne(6,1,b);_nop_();
DisplayOne(7,1,s);_nop_();
DisplayOne(8,1,g);_nop_();
}
//==================== 函数名:主函数 ========================//
void main(void)
{
P0M1=0x00;
P0M0=0xff; //P0设成强推挽输出(AT89S5x和STC89C5x系列等均无此功能)
P0=0x00; //P0初始化为低电平
InitLcd(); //LCD初始化
delay(100);
display(count);
while(1)
{
P1=0XFF; //先把P1全部设全1
if(KEY1==0) //加1键(可接入传感器计数信号)
{
delay(50); while(KEY1==0); //防抖延时(视情况可修改);是否放开了按
count++; display(count); //count的值加1并刷新显示
if(count==10000){count=0;} //若加满了10000个便清零
}
P1=0XFF;
if(KEY2==0) //减1键
{
delay(50); while(KEY2==0);
count--; display(count); //count的值减1并刷新显示
if(count==0){count=10000;} //若减到了0个,便赋值10000
}
P1=0XFF;
if(KEY3==0) //清零键
{
delay(50); while(KEY3==0);
count=0;display(count); //count的值减1并刷新显示
}
P1=0XFF;
}
}