一、项目描述
基于89C52单片机,在protues环境中开发了一个含有按键计数功能的小项目,计数最大值是999。输入是接在P32口和P3^3口的两个按键,输出是在1602显示屏上显示结果。程序在keil C51软件中编写调试。
二、硬件设计
主体是单片机一个,外围电路有晶振电路、复位电路、1602液晶显示屏、两个按钮开关按键。1602的VSS接地,VDD接电源,VEE接滑动变阻器来调节电位,以此调节屏幕对比度。RS接P30,来选择数据或者命令;E接P31,控制1602使能;根据工作手册,RW可以一直为低电平,因此RW始终接地。D0~D7接到P2口作为传输总线。两个按键接在P32口和P3^3口,分别对应外部中断0和外部中断1,且低电平触发中断。晶振电路可以提供11.0592MHZ的工作频率,复位电路可以通过按钮开关按键复位。
三、软件代码
/********************************************************
* 文 件: 1602显示屏按键计数
* 作 者:
* 外部晶振: 11.0592MHZ
* 编译环境: Keil μVisio4
* 功 能: 数码管静态显示
* 注意事项:
********************************************************/
#include<reg52.h>
unsigned char code table1[]="COUNT:"; //命名数组用来显示字符“COUNT:”
int count = 0; //全局变量初始化
sbit RS = P3^0; //数据/命令选择接口
sbit EN = P3^1; //使能接口
sbit button1 = P3^2; //按键1接在P3^2接口,对应外部中断0
sbit button2 = P3^3; //按键2接在P3^3接口,对应外部中断1
/********************************************************
函数名:毫秒级CPU延时函数
调 用:delay_ms(?);
参 数:1~65535(参数不可为0)
返回值:无
结 果:占用CPU方式延时与参数数值相同的毫秒时间
备 注:应用于1T单片机时i<600,应用于12T单片机时i<125
/********************************************************/
void delay(unsigned int a)
{
unsigned int i;
while( --a != 0){
for(i = 0; i < 125; i++);
}
}
/********************************************************
函数名:写指令函数
调 用:writeinstruction(x);
参 数:无符号十六进制数0x??
返回值:无
结 果:将指令发送给1602控制器
/********************************************************/
void writeinstruction(unsigned char x)
{
RS = 0;
EN = 0;
P2 = x;
delay(5);
EN = 1;
delay(5);
EN = 0;
}
/*********************************************************
函数名:写数据函数
调 用:writedata(y);
参 数:无符号十六进制数0x??
返回值:无
结 果:将数据发送至1602内部的显示内存中
/*********************************************************/
void writedata(unsigned char y)
{
RS = 1;
EN = 0;
P2 = y;
delay(5);
EN = 1;
delay(5);
EN = 0;
}
/*********************************************************
函数名:写变量函数
调 用:writechar(z);
参 数:无符号十六进制数0x??
返回值:无
结 果:将数据发送至1602内部的显示内存中
备 注:不能将整数型变量count直接作为writedata的参数,需要将个、十、百位分别+48(0x30)转化成ascii码。
/*********************************************************/
void writechar(unsigned char z)
{
unsigned char A;
unsigned char B;
unsigned char C;
RS = 1;
EN = 0;
A = z/1%10; //取个位
B = z/10%10; //取十位
C = z/100%10; //取百位
writedata(C+48);
delay(5);
writedata(B+48); //分离出变量的每一位数,再将每一位加上0x30,
delay(5);
writedata(A+48); //这样就变成了ASCII码了,再送给LCD才能显示出来变量的每一位的值。
delay(5);
}
/*********************************************************
函数名:1602设置初始化函数
调 用:init1602();
参 数:无
返回值:无
结 果:初始化。具体指令在手册中查询。
/*********************************************************/
void init1602()
{
writeinstruction(0x38); //设置显示模式 指令码 00111000 => 0x38
delay(5);
writeinstruction(0x0C); //[00001DCB]:D为显示开关,B为光标开关,C为光标闪烁开关。0x0C:开显示 不显示光标 不闪烁
delay(5);
writeinstruction(0x06); //写一个字符后地址指针加一
delay(5);
writeinstruction(0x01); //清屏
delay(5);
}
/**********************************************************
函数名:主函数main
/**********************************************************/
void main()
{
char i;
init1602();
writeinstruction(0x80); //设置数据地址指针从第一个开始
delay(5);
/* 初始化中断 */
EA = 1;
EX0 = 1;
EX1 = 1;
/* 写初始字符:COUNT:0 */
for (i = 0; i < 6; ++i) //逐个写入第一行数据
{
writedata(table1[i]);
delay(20);
}
writeinstruction(0x02); //第一行光标返回
writeinstruction(0x80 + 0x40); //数据地址指针指向第二行第一个
writechar(count);
/*砸瓦鲁多 */
while(1); //程序停在这里,等待中断到来
}
/*********************************************************
函数名:中断显示计数值函数
调 用:按下button1
参 数:无
返回值:无
结 果:显示当前计数值。具体指令在手册中查询。
/*********************************************************/
void Display()interrupt 0 using 1
{
int i;
if(button1==0)
{
delay(10);
if(button1==0)
{
count++; //按键松开后,程序会运行到这里,计数值加1
while(!button1); //如果按键没有松开,程序会停在这里
}
writeinstruction(0x01); //清屏
for (i = 0; i < 6; ++i) //逐个写入第一行数据
{
writedata(table1[i]);
delay(20);
}
writeinstruction(0x02); //回车
writeinstruction(0x80 + 0x40); //换行
writechar(count);
}
/*********************************************************
函数名:中断清零函数
调 用:按下button2;
参 数:无
返回值:无
结 果:计数值清零。具体指令在手册中查询。
/*********************************************************/
}
void Clear() interrupt 2 using 1
{
int i;
writeinstruction(0x01);
count=0; //count计数清零
for (i = 0; i < 6; ++i) //逐个写入第一行数据
{
writedata(table1[i]);
delay(20);
}
writeinstruction(0x02); //回车
writeinstruction(0x80 + 0x40); //换行
writechar(count);
}
四、结果展示
五、经验总结
在硬件连接方面,最初是用P0口作为数据传输总线,但1602每次都出现乱码,经排查后发现原因是未接上拉电阻。在接了上拉电阻之后依然不能正常显示,于是转为P2口作为数据传输总线,同时发现了RW口可以一直接地,省去了一个接口。
在软件调试方面,在向1602发送数据时不能用写数据函数writedata()直接将变量写入,因此我又编写了一个writechar()函数,通过将变量的个、十、百位分别转为char型,再加上+48(+0x30)转成ascii码,便可以用writedata()函数将十六进制数写入。1602可以将ascii码直接转为对应的字符再屏幕上显示。
4.4 更新:按键后的刷新很慢是因为延迟函数时间过长了,之前调试的时候没注意到这里用的是毫秒级延时,所以延时设置短一点就可以变得丝滑顺畅了。RST复位也有问题,把复位电阻改成1k就可以复位了,10k太大了。