今天我心情不错,一方面是昨天我的LCD1602调出来了,另外一方面是今天外加信号控制触发任意函数发生器AFG3102产生指定信号有了不错的方法。哈哈,高兴喔!
LCD1602是我遇到的又一个难题,算算调试1602花了我四天的时间。昨天终于调成功了,对我这样的菜鸟来说,真的不容易呀。
1602的调试过程介绍如下:
1、1602的原理、接口设计
这个网上一大把,就找个可信的方法,按照自己的要求设计好电路接口(具体的自己找找吧)。从中,我得到的一些启发是:1602的原理、单片机与1602的接口设计等资料,最好多参照几份。
我的接口是按照下图来接的。
2、万用板的焊制
这个得多动手,多练,注意合理布局,焊完最好用万用表测试一下,焊线是否导通。对初学者来说,这也非常容易出错喔。有时候,就是焊线有问题就能让你郁闷两三天啊。
3、驱动程序的编写与调试
1602作为一个成熟的产品,其接口已经非常简单,但程序的编写与调试却最容易出问题。
一开始,我把程序看了个大概,就开始烧写到单片机上。但调了将近两天也没结果。LCD只能显示LCD1602第一行全黑的效果(如图2)。
我查资料发现,这个问题的原因是接线错误或1602初始化程序未运行。
当时我很高兴,就重新焊了一块板子。结果新板子效果仍旧,因为当时我认为程序应该没问题,就一直没找到问题的解决方法(很郁闷)。
正当我要放弃调1602时,伟大的转机出现啦。我找了个同学给看看,他结合时序图看过程序后说,程序是有点小问题呀。我们调了一个多小时,但问题仍未解决。这时,我感到一定要从细节上读懂程序才行。于是,我稍加了初始化程序的延时,就出来了一个让人兴奋的结果。哈哈哈哈哈……这次我的1602出现了两行全黑块(如图3)。
因为我的1602第3脚VL是接地了。所以,我马上把3脚用10K电位器接地,从全黑调至刚刚变白即可。这时就1602就能正常显示了(我在程序里让LCD1602第一行显示89S52,第二行则是HELLO,图暂缺)。
调试的几点小经验:
1、连线,焊线一定要小心,细心。
2、设计很重要,但调试也非常重要,而且非常费时间和精力。这时,我原来没有想到的。以前我就认为设计一定费更多时间很精力,其实不然,调试才最费时间和精力,因此调试很重要很重要。
3、调试中,很容易头脑麻木,得注意劳逸结合,多和他人交流学习。
4、硬件的时序图很重要,对驱动程序的编写很有帮助,我一开始就没注意它,结果找不到错误。所以以后,我一定硬下头皮来也要学会读时序图。
作者:飞燕
2011.2.11
//先定义接口
# include <AT89x51.h>
/*****************************************
P1------DB0~DB7 P2.0------RS
P2.1------RW
P2.2------E
*****************************************/
# define LCD_DB P1
sbit LCD_RS=P2^0; //P2^0是p2.0的意思;LCD_RS与P2.0等效起来,对LCD_RS读写,就是对P2.0读写 好处在于LCD_RS含义直接明了,写程序多了就会知道有必要de
sbit LCD_RW=P2^1; //P2^1是p2.1的意思
sbit LCD_E=P2^2; //P2^2是p2.2的意思
/******定义函数****************/
# define uchar unsigned char
# define uint unsigned int
void LCD_init(void);//初始化函数
void LCD_write_command(uchar command);//写指令函数
void LCD_write_data(uchar dat);//写数据函数
void LCD_disp_char(uchar x,uchar y,uchar dat);//在某个屏幕位置上显示一个字符,X(0-16),y(1-2)
//void LCD_check_busy(void);//检查忙函数。我没用到此函数,因为通过率极低。
void delay_n40us(uint n);//延时函数
//********************************
//*******初始化函数***************
void LCD_init(void)
{
delay_n40us(10);
LCD_write_command(0x38);//设置8位格式,2行,5x7
delay_n40us(10);
LCD_write_command(0x0c);//整体显示,关光标,不闪烁
delay_n40us(10);
LCD_write_command(0x06);//设定输入方式,增量不移位
delay_n40us(10);
LCD_write_command(0x01);//清除屏幕显示
delay_n40us(100);//实践证明,我的LCD1602上,用for循环200次就能可靠完成清屏指令。
}
//********************************
//********写指令函数************
void LCD_write_command(uchar dat)
{
delay_n40us(10);
LCD_RS=0;//指令
LCD_RW=0;//写入
LCD_E=1;//允许
LCD_DB=dat;
delay_n40us(10);//实践证明,我的LCD1602上,用for循环1次就能完成普通写指令。
LCD_E=0;
delay_n40us(10);//实践证明,我的LCD1602上,用for循环1次就能完成普通写指令。
}
//*******************************
//********写数据函数*************
void LCD_write_data(uchar dat)
{
delay_n40us(10);
LCD_RS=1;//数据
LCD_RW=0;//写入
LCD_E=1;//允许
LCD_DB=dat;
delay_n40us(10);
LCD_E=0;
delay_n40us(10);
}
//********************************
//*******显示一个字符函数*********
void LCD_disp_char(uchar x,uchar y,uchar dat)
{
uchar address;
if(y==1)
address=0x80+x;
else
address=0xc0+x;
LCD_write_command(address);
LCD_write_data(dat);
}
//********************************
/*******检查忙函数*************
void LCD_check_busy() //实践证明,在我的LCD1602上,检查忙指令通过率极低,以
{ //至于不能正常使用LCD。因此我没有再用检查忙函数。而使
do //用了延时的方法,延时还是非常好用的。我试了一下,用
{ LCD_E=0; //for循环作延时,普通指令只要1次循就可完成。清屏指令
LCD_RS=0; //要用200次循环便能完成。
LCD_RW=1;
LCD_DB=0xff;
LCD_E=1;
}while(LCD_DB^7==1);
}
******************************/
//********延时函数***************
void delay_n40us(uint n)
{ uint i;
uchar j;
for(i=n;i>0;i--)
for(j=0;j<2;j++); //在这个延时循环函数中我只做了2次循环,
} //实践证明我的LCD1602上普通的指令只需1次循环就能可靠完成。
//*******************************
//*********主函数*****************
void main(void)
{
LCD_init();
LCD_disp_char(0,1,0x38);//89S52
LCD_disp_char(1,1,0x39);
LCD_disp_char(2,1,0x53);
LCD_disp_char(3,1,0x35);
LCD_disp_char(4,1,0x32);
LCD_disp_char(0,2,0x48);//HELLO
LCD_disp_char(1,2,0x45);
LCD_disp_char(2,2,0x4C);
LCD_disp_char(3,2,0x4C);
LCD_disp_char(4,2,0x4F);
while(1);
}