请教LCD1602四线制的驱动重新上电复位不稳定问题
我最近用ATMEGA8515驱动LCD1602,采用四线制,程序下载完后可以正常显示,但要是重新上电的话,就不能正常工作,基本上不显示了,
按照论坛上说的一些经验方法,在初始化程序里,插入2秒的延时,也多次设置了4线的格式。
但还是不能解决问题~~~~~~~~~ ./emotion/em015.gif
还请大家再一起研究下,程序如下:
/*---------------------------------------------
项目: AVR实验板测试程序:LED & LCD & SOUND 测试
编译器:Winavr20050214
单片机:ATMega8515L-8PI
时钟: 6MHz外部时钟
建立时间:2005年11月18号
修改日期:
LCD驱动采用4线驱动方法,数据的双向传输由PA口的PA7-PA6-PA5-PA4
LCD_write函数功能:当command=0时,向LCD写入数据,否则向LCD写入命令
LCD第一行显示寄存器地址:0X80-0X8F
LCD第二行显示寄存器地址:0XC0-0XCF
------------------------------------------------------*/
#include <avr/io.h>
#include <avr/delay.h>
//注: 内部函数_delay_ms() 最高延时262.144mS@1MHz 即 32.768ms@8MHz
// 该函数可以实现较精确的定时for()/while()指令很难计算延时时间
// 为了使 _delay_ms()函数的延时正确,须在makefile中设定F_CPU为实际的系统时钟频
// 本范例为8MHz外部晶振振荡器 即 F_CPU=8000000
//定义MCU与LCD的接口
#define LCD_RS_PORT PORTC
#define LCD_EN_PORT PORTC
#define LCD_RW_PORT PORTC
#define LCD_DATA_PORTPORTA
#define LCD_DATA_DDR DDRA
#define LCD_DATA_PIN PINA
#define LCD_RS (1<<5) //PORTC5 out
#define LCD_EN (1<<7) //PORTC7 out
#define LCD_RW (1<<6) //PORTC6 out/in
#define LCD_DATA 0xf0 //PORTA4/5/6/7 out
/*-------函数声明----------------------------------------------------------------*/
void LCD_init (void);
void LCD_en_write (void);
void LCD_write_char (unsigned command,unsigned data);
void LCD_wait_Ready (void);
void LCD_set_xy (unsigned char x, unsigned char y);
void LCD_write_string (unsigned char X,unsigned char Y,unsigned char *s);
/*-------------------------------------------------------------------------------*/
/*----------------------------------------------------
光标命令
LCD_write_char(0x0e,0); //光标开
LCD_write_char(0x0d,0); //光标所在字符闪烁
LCD_write_char(0x0c,0); //光标关
-------------------------------------------------------*/
//内部函数_delay_us() 最高延时768 us@1MHz 即 96 us@8MHz
void delay_us(unsigned int us);//----us级延时
void delay_ms(unsigned int ms);
//--------------------------------------------------------------------------
/**********************************************************/
int main(void)
{
delay_ms(2000);
DDRA = 0XFF;
//DDRA |= LCD_DATA ;//
DDRC = 0XFF;
//DDRC |= LCD_RS| LCD_EN | LCD_RW ; //
LCD_init();//初始化
delay_ms(100);
LCD_write_char(0x01,0); //显示清屏
delay_ms(100);
LCD_write_char(0x01,0); //显示清屏
delay_ms(100);
LCD_write_string(1,0,"love avr???");
delay_ms(500);
LCD_write_string(1,1,"come with me!");
//delay_ms(500);
while(1);//到此停止
/*
while(1)
{
for(k=0;k<240;k++)
{
LCD_set_xy(12,1);
LCD_write_char(0,16+k);
for(j=0;j<20;j++) delay_nms(20);
}
}
*/
}
//----------------ms级延时---------------
void delay_ms(unsigned int ms)
{
unsigned int i;
for(i=0;i<ms;i++) _delay_ms(1); //延时 i*ms= 毫秒,可自行调节
}
//----------------us级延时---------------
void delay_us(unsigned int us)
{
unsigned int i;
for(i=0;i<us;i++) _delay_us(1); //延时 i*us= 毫秒,可自行调节
}
/*---------------LCD初始化----------------*/
void LCD_init(void)
{
delay_ms(3000);
LCD_write_char(0x28,0); //4bit Display
delay_ms(30);
LCD_write_char(0x28,0); //4bit Display
delay_ms(30);
LCD_write_char(0x28,0); //4bit Display
delay_ms(30);
LCD_write_char(0x28,0); //4bit Display
delay_ms(30);
LCD_write_char(0x0c,0); //显示开
delay_ms(30);
LCD_write_char(0x0c,0); //显示开
delay_ms(30);
LCD_write_char(0x01,0); //显示清屏
delay_ms(30);
LCD_write_char(0x01,0); //显示清屏
delay_ms(30);
LCD_write_char(0x06,0); //显示光标移动设置
delay_ms(30);
}
/*---------------写入使能---------------*/
void LCD_en_write(void) //EN端产生一个高电平脉冲,写LCD
{
LCD_EN_PORT |= LCD_EN;
delay_us(20);
LCD_EN_PORT &= ~LCD_EN;
delay_us(10);
}
/*-----------------------------------------------------------------------
LCD_write_char : 英文字符串显示函数
输入参数:*s :英文字符串指针;
X、Y : 显示字符串的位置,X:0-15,Y:0-1
LCD第一行显示寄存器地址:0X80-0X8F
LCD第一行显示寄存器地址:0XC0-0XCF
编写日期 :2005-10-7
最后修改日期 :
-----------------------------------------------------------------------*/
void LCD_write_char(unsigned command,unsigned data)
{
unsigned command_temp,data_temp;
command_temp = command;
data_temp = data;
LCD_wait_Ready();
LCD_RW_PORT &= ~LCD_RW; //RW=0
LCD_DATA_PORT &= 0X0f;
if (command == 0)
{
LCD_RS_PORT |= LCD_RS; //RS=1
LCD_DATA_PORT |= data_temp&0xf0; //send high 4bit
}
else
{
LCD_RS_PORT &= ~LCD_RS; //RS=0
LCD_DATA_PORT |= command_temp&0xf0;//send high 4bit
}
LCD_en_write();
LCD_DATA_PORT &= 0X0f;
command_temp=command_temp << 4; //send low 4bit
data_temp=data_temp << 4;
if (command==0)
LCD_DATA_PORT |= data_temp&0xf0;
else
LCD_DATA_PORT |= command_temp&0xf0;
LCD_en_write();
LCD_RW_PORT |= LCD_RW;
LCD_RS_PORT ^= LCD_RS;
}
void LCD_wait_Ready(void) //等待LCD空闲
{
LCD_DATA_DDR &= ~0x80; //PD7 I/O口方向设置为输入
LCD_RW_PORT |= LCD_RW; //RW=1
LCD_RS_PORT &= ~LCD_RS; //RS=0
LCD_EN_PORT |= LCD_EN; //EN=1
while (!( LCD_DATA_PIN&0x80 ) == 0); //RW=1,读PD7,为0表示空闲;
LCD_EN_PORT &= ~LCD_EN; //EN=0
LCD_DATA_DDR |= 0xf0;
}
/*-----------------------------------------------------------------------
LCD_set_xy : 设置LCD显示的起始位置
输入参数:x、y : 显示字符串的位置,X:0-15,Y:0-1
LCD第一行显示寄存器地址:0X80-0X8F
LCD第一行显示寄存器地址:0XC0-0XCF
编写日期 :2005-10-7
最后修改日期 :
-----------------------------------------------------------------------*/
void LCD_set_xy( unsigned char x, unsigned char y )
{
unsigned char address;
if (y == 0) address = 0x80 + x;
else
address = 0xc0 + x;
LCD_write_char( address, 0 );
}
/*-----------------------------------------------------------------------
LCD_write_string: 英文字符串显示函数
输入参数:*s :英文字符串指针;
X、Y : 显示字符串的位置
编写日期 :2005-10-7
最后修改日期 :
-----------------------------------------------------------------------*/
void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s)
{
LCD_set_xy( X, Y );
while (*s)
{
LCD_write_char( 0, *s );
s ++;
}
}
-----此内容被benladn911于2005-11-25,11:22:40编辑过
以下红色文字由版主:dengyh 于:2005-11-27,15:46:24 加入。请更改成能说明帖子大意的标题
本论坛规定:一定要起一个能说明帖子大意的标题。不允许“关于 AVR 的 ADC 使用!”这种笼统标题。作为标题,必须说明清楚:
①你是在请教问题,还是在介绍有关的知识?
②ADC的问题有许多,你是想说那方面的内容?
注意以上两点,标题应该改成如:“请教:ADC 可以不使用中断吗?”。
起一个能说明帖子大意的标题,除了减轻我们的帖子分类整理工作,还方便其它人阅读,节省大家的时间。
谢谢你的支持。
注:你可以使用编辑功能修改标题,并将这段红色文字删除。谢谢你的支持!
-----<font color=blue>此内容被benladn911于2005-12-01,21:25:15编辑过 看前两天的帖子,我也用1602,mega8l,4线;相同的问题。
我得解决方法是:1、把32个字符显示一遍,2、把光标复位清零,就没有问题了。
我分析很多人都遇到了,说明是1602的问题,无论你怎么延长时间,怎么延时复位,都不顶用,你要是直接加一个复位信号(0V),有可能显示正确,但不能保证每次成功。
哪位大侠有妙着能告诉我们吗?
-----此内容被bg9fu于2005-11-25,11:56:19编辑过 我也碰到相同问题,一开始还以为程序跑飞了。 bg9fu 想请教你:2、把光标复位清零,就没有问题了。 -----是怎么一回事? 參考那個M8做的數字電源,有相關的程序,我試過,比較正常。 benladn911,
Don't use LCD_write_char() before LCD initialized, write 2 (4-bits) directly to LCD, because each LCD_wirte_char() will send 2 and 8 (two 4-bits) instead of 2 (one 4-bits). Refer to datasheet or to my posts before.
Here is correct code:
void LCD_init(void) {
// send three 2 (4-bits) to LCD, don't use LCD_write_char here
// delay between each nibble
// you can use LCD_write_char() here to send any comment now;
LCD_write_char(0x28,0); //4bit Display
delay_ms(2);
LCD_write_char(0x0c,0); //显示开
delay_ms(2);
...
} Epegic,thanks for your help! thank you ! 把光标复位清零就是:送命令字02、01,现在好了吗? 还没搞定,我再试试,等调可以了,一定和大家分享其中的成果,呵呵,刚学GCC 楼上各位,你们有没有搞妥这个问题啊!!!!
惨啊!!这事终于发生在我身上了,
增加以上程序,开机有时还是没有显示!!按复位可以显示了。
void LCD_init(void)
{
delay_nms(1000);
unsigned int i=0;
for (i=0;i<20;i++)
{
LCD_write_char(0,0x28); //4bit test
delay_nms(15);
}
LCD_write_char(0,0x0c); //显示开
delay_nms(5);
.............................
"
线路板做好!!!还赶明天交客户!!搞了一个晚上了!!! 问题还是没有解决~~~~~~`
继续努力!!!!!!! 换一个电源或者检查一下电路布线是否合理,或换一片单片机试一下!!! 终于搞掂了原来是初始化的代码不正确试一试这个!!
void Lcdinit( void ) {
DelayMs(50); /*************
LCD_write_char(0x30,0);
DelayMs(6);
LCD_write_char(0x30,0); 这段加上去,不用阿莫介绍延时的那种
DelayMs(1);
LCD_write_char(0x30,0);
DelayMs(1);
LCD_write_char(0x02,0); *******************/
DelayMs(1);
LCD_write_char(0x28,0); //4bit test 显示模式设置(不检测忙信号)
DelayMs(1);
LCD_write_char( 0x08, 0); // 显示关闭
DelayMs(1);
LCD_write_char( 0x01, 0); // 显示清屏
DelayMs(1);
LCD_write_char( 0x06, 0); // 显示光标移动设置
DelayMs(1);
LCD_write_char( 0x0C, 0); // 显示开及光标设置
DelayMs(10);
} 我暂时还没有遇到这个问题哈。
我遇到的问题跟你们不太一样。
我的情况是:
必须上电LCD才能复位……好在,这期间,LCD仍旧能够正常地接受任何指令。 哈哈,总算解决了,呵呵,谢谢大家了。 怎么解决的? ./emotion/em021.gif
按照fangmcu 方谭 兄的方法解决了,真的要好好感谢fangmcu 方谭 兄了,呵呵。
void Lcdinit( void ) {
DelayMs(50); /*************
LCD_write_char(0x30,0);
DelayMs(6);
LCD_write_char(0x30,0); 这段加上去,不用阿莫介绍延时的那种
DelayMs(1);
LCD_write_char(0x30,0);
DelayMs(1);
LCD_write_char(0x02,0); *******************/
DelayMs(1);
LCD_write_char(0x28,0); //4bit test 显示模式设置(不检测忙信号)
DelayMs(1);
LCD_write_char( 0x08, 0); // 显示关闭
DelayMs(1);
LCD_write_char( 0x01, 0); // 显示清屏
DelayMs(1);
LCD_write_char( 0x06, 0); // 显示光标移动设置
DelayMs(1);
LCD_write_char( 0x0C, 0); // 显示开及光标设置
DelayMs(10);
} 不用谢!!大家以后多交流就是了!! 好
-----此内容被xcm_gz于2005-12-29,03:58:10编辑过 各位大虾你们好,我是个初学者,请问一下:内部函数_delay_ms() 最高延时262.144mS@1MHz 即 32.768ms@8MHz和内部函数_delay_us() 最高延时768 us@1MHz 即 96 us@8MHz是怎么做出来的??请赐教!!!!谢谢 自己写个ASM汇编程序就明白了。
8位减法dec bren
_delay_us() /_delay_loop_1(uint8_t __count)
16位减法sbiw,1bren
_delay_ms() /_delay_loop_2(uint16_t __count) 就是用汇编程序写,然后在C里面调用吗?? 这是GCCAVR自动生成的,用户不需理会。
原理是这样,实际运行代码也是这样。 非常感谢你的回答,还有点问题,你的意思是直接把上面你给的那几句放在C里面吗??我用的是ICCAVR!!! ICCAVR???一般用for(;;)来做,时间自己算
嵌入汇编是可以的,不过我也不清楚ICCAVR如何嵌入汇编。
上面的不是完整的ASM指令,示意而已。
整个延时时间要考虑调用,赋值,循环,返回的时间才能精确,GCCAVR是自动算出而且以inline嵌入的,没有调用返回时间,人工算还是比较麻烦。 请问一下,我也用了上面的程序,但不知道为什么第一行显示总是不正确,主要现象为前面要显示的几个字不见了,而第二行显示正常 是用了benladn911的程序 c 我碰到这样的问题:
如果加电冷启动显示正常的话,用看门狗复位就会出现乱屏现象,在程序启动阶段WHILE(1)前初始化LCD两次后看门狗复位正常了,可加电冷启动又有问题了。 各位大虾你们好,我是个初学者,请问一下:内部函数_delay_ms() 最高延时262.144mS@1MHz 即 32.768ms@8MHz和内部函数_delay_us() 最高延时768 us@1MHz 即 96 us@8MHz是怎么做出来的??请赐教!!!!谢谢 请问 void LCD_write_char(unsigned command,unsigned data) 函数中最后两行程序 LCD_RW_PORT |= LCD_RW;
LCD_RS_PORT ^= LCD_RS; 起什么作用? 谢谢. 坛里有成熟的4线,很好用,也很好移植。好像是iccavr 对这问题,我也有深刻的认识,遇到过这样的问题,开始还以为程序出现了问题,后来经过调试达到了每次开机都能正常工作了,我的做法就是加了这一段:
for(p=0;p<5;p++)
lcd_init();
void lcd_init(void)
{
delay_nms(15);
lcd_write_char(0x28,0); //4bit test
lcd_write_char(0x0C,0); //显示开
//lcd_write_char(0x01,0); //显示清屏
lcd_write_char(0x06,0); //显示光标移动设置
}
LCD初始化时最好清屏指令不用,正常复位OK之后再写一条清屏指令,复位很可靠。 可怜,以前发的那个ICCAVR 4位16XX液晶的程序被忽略了。 DelayMs(50); /*************
LCD_write_char(0x30,0);
DelayMs(6);
LCD_write_char(0x30,0); 这段加上去,不用阿莫介绍延时的那种
DelayMs(1);
LCD_write_char(0x30,0);
DelayMs(1);
实际上这才是重要的吧!资料上也是叫这么做,不知道为什么必须送三次? 35楼说的对,我试过。真的要这样才能稳定。 还是按照数据手册来稳妥 好像见过这个问题。
你们看ST706第22版的datasheet, 里面有对这个问题的说明。
这还是我在前一家公司发现的。
休眠以后必须确保LCD上全部IO脚都为低电平, LCD电源也必须关闭,否则重新上电时LCD不会产生复位信号。重新上电时, 必须等电压稳定后,过约10ms才可以把IO口拉高。 记号 挖出来 http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=889846&bbs_page_no=1&search_mode=1&search_text=1602&bbs_id=1000
void LCD_init(void)
{
LCD_DDR=0xff; //设为输出
CLRBIT(LCD_PORT,RW);//设为永远W状态
_delay_ms(100);
write_command(0x33);
Delay_10ms(2);
write_command(0x32);
Delay_10ms(2);
write_command(0x28);
write_command(0x0c);//显示开--对应开关显示控制指令
write_command(0x01);//清屏--对应清屏指令
Delay_10ms(2); //delay
}
能解决问题 mark http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=889846&bbs_page_no=1&search_mode=1&search_text=1602&bbs_id=1000
void LCD_init(void)
{
LCD_DDR=0xff; //设为输出
CLRBIT(LCD_PORT,RW);//设为永远W状态
_delay_ms(100);
write_command(0x33);
Delay_10ms(2);
write_command(0x32);
Delay_10ms(2);
write_command(0x28);
write_command(0x0c);//显示开--对应开关显示控制指令
write_command(0x01);//清屏--对应清屏指令
Delay_10ms(2); //delay
}
能解决问题
不错,在我遇到的情况中,这个是唯一正常的方法。非常感谢!! mark 谢谢了我也搞定这个问题了 CVAVR里面库函数里有LCD四线驱动程序的。。。 记号 up 今天,用四线制驱动1602,遇到了楼主的问题,看了这些贴子终于搞定了,真的感谢fangmcu 方谭 同样问题,加上后解决问题 MARK mark mark mark mark! Lcdwriteinitcommand(3); //第一次
delay_ms(5);
Lcdwriteinitcommand(3); //第二次
Lcdwriteinitcommand(3); //第三次
Lcdwriteinitcommand(2); //变成4bit
我四线在低位,这四次只送1Byte。
很多人还是不知道为什么要送三次0x3,当工程师不要不明就里。
假如刚开始是8bit那设定第一次将会变为8bit。
但假如是4bit,且是第一个Data则会在二次之后确定变成8bit。
那如果你现在LCD是在4bit且在第二个Data则要在第三次才会确定为8bit。
结束上面三次确定一定是8bit,在送成4bit就绝对不会顺序乱掉。 另外我在KS0066U的规格书没看到有写这个东西,也是后来除错才发现的。 奇怪为啥这么复杂,我用C51来初始化LCD1602四线模式一般只要0x32再加个0x28,只要写入命令延时是对的基本没问题(如果查忙的代码正确连延时都省了),哪有这么复杂的(0x32就是先写入高位3为8Bit单行,再写入低位2为4Bit单行模式,相当于8Bit到4Bit的切换,后面0x28就是完整的4Bit命令,意思是设为双行模式4Bit)。 benladn911 发表于 2005-12-2 14:02 static/image/common/back.gif
按照fangmcu 方谭 兄的方法解决了,真的要好好感谢fangmcu 方谭 兄了,呵呵。
void Lcdinit( void )...
真的解决了,太好了。 我也遇到同样问题,我的1602是3.3V的。同时还有一个怪现象,时而上电能复位,时而没有显示。如果在没有显示状态,等1-2分钟,显示的内容就正常了,不知道如何解决这个问题。 找到原因了,是调节对比度的那个脚电压在变化,每次没有显示时,调节一下对比度就可以了,估计是液晶的品质不好。 CoolBird007 发表于 2010-4-8 14:50
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=889846&bbs_page_no=1&search_mode=1&search_text=1602& ...
_delay_ms(100);
write_command(0x33);
Delay_10ms(2);
write_command(0x32);
Delay_10ms(2);
加上以上几句开机就能显示正常,非常感谢
抄好程序,解决问题,谢谢
页:
[1]