原创
昨天在ouravr看到的关于1602四位数据线时初始化不稳定问题的正确解决方法的文章
几天前搞了个LCM1602来玩,网上下载了别人的程序改巴改巴就写到M48里面,就遇到了初始化很不稳定的问题.
上网查看,发现很多人都反映如此,也有人提供了解决方法但并不完美.继续找资料,查了很多pdf资料,最后在
微雪电子公布的技术文档里找到了答案.
原来1602初始化成4位数据线之前必需先初始化成8位(此时命令发送方式是8位格式,但数据线只需接4位),然后再改到4位线宽,
这样才能稳定的进入4位模式.
正确的初始化过程是这样的:
1)上电
2)等待15ms以上
3)8位模式写命令0b0011xxxx (后面4位线不用接,所以是无效的)
4)等待4.1ms以上
5)同3)
6)等待100us以上
7)同3)
以上步骤中不可查询BF状态,只能用延时控制
从一下步骤开始可以查询BF状态确定模块是否忙
8)8位模式写命令0b0010xxxx 进入4位模式
以下操作都必需用4位模式格式进行
9)4位模式写命令0b0010NFxx NF为行数和字符高度设置位,之后行数和字符高不可重设
10)4位模式写命令0b00001000 关闭现实
11)4位模式写命令0b00000001 清屏
12)4位模式写命令0b000001(I/D)S 设置光标模式
初始化完成,可以用4位模式写字符
以下是我在M48上调通的程序,虽然没有完全遵循上面的过程,但工作稳定,M48的时钟为1M或8M都能稳定初始化1602
希望大家以后不用再在这个问题上多走弯路.
/********************* LCD 1602 Display Routine Functions *************************/
/* winavr2007 */
/***********************************************************************************/
#include <avr/io.h> //MCU为ATmega48
#include <stdio.h>
#include <util/delay.h>
#define uchar unsigned char
#define LCD_PORT PORTD
#define LCD_DDR DDRD
#define LCD_PIN PIND
#define RS 5
#define RW 6
#define EN 7
#define LCD_DATA ((1<<0)|(1<<1)|(1<<2)|(1<<3))
#define SETBIT(x,y) (x|=(1<<y)) //set bit y in byte x
#define CLRBIT(x,y) (x&=(~(1<<y))) //clear bit y in byte xx
//--------------------------------------------------------------------------------
//延时函数我就不多说了,直接写出来
void Delay_10ms(uchar _time)
{
while( _time>0)
{
_delay_ms(15);
_time--;
}
}
//--------------------------------------------------------------------------------
//设置所需函数
//写数据线命令(四线模式数据要分两次写)
void out_dataline(uchar line)
{
SETBIT(LCD_PORT,EN); //EN=1
LCD_PORT&=0Xf0; //清低四位端口
LCD_PORT|=line/16; //写高四位数据
CLRBIT(LCD_PORT,EN); //EN=0
SETBIT(LCD_PORT,EN); //EN=1
LCD_PORT&=0Xf0; //清低四位端口
LCD_PORT|=line&0x0f; //写低四位数据
CLRBIT(LCD_PORT,EN); //EN=0
}
//一、写指令函数
void write_command(unsigned char com)
{
_delay_us(16); //delay
CLRBIT(LCD_PORT,RS); //RS=0
out_dataline(com);
}
//二、写数据函数(四线模式数据要分两次写)
void write_data(unsigned char data)
{
_delay_us(16); //delay
SETBIT(LCD_PORT,RS); //RS=1
out_dataline(data);
}
//三、初始化LCD1602函数
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
}
//四、写地址函数
//--------------------------------------------------------------------------------
//要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符
//1602液晶内部显示地址
//比如第二行第一个字符的地址是40H,那么是否直接写入40H就可以将光标定位在第二行第一个字符的位置呢?
//这样不行,因为写入显示地址时要求最高位D7恒定为高电平1所以实际写入的数据应该?
//?1000000B(40H)+10000000B(80H)=11000000B(C0H)
//--------------------------------------------------------------------------------
void LCD_set_addr(unsigned char x,unsigned char y)//x:0~15,y:0~1
{
if(y)
write_command(0xc0+x);//第二行显示
else
write_command(0x80+x);//第一行显示
}
//五、写字符函数
void LCD_write_char(unsigned char X,unsigned char Y,
unsigned char data) //列x=0~15,行y=0,1
{
LCD_set_addr( X, Y ); //写地址
write_data(data);
}
//六、写字符串函数
void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s) //列x=0~15,行y=0,1
{
LCD_set_addr( X, Y ); //写地址
while (*s) // 写显示字符
{
write_data( *s );
s ++;
}
}
//到这里我们已经把全部函数写好了下面我们用上面的函数让LCD1602显示
void main(void)
{
LCD_init(); //初始化LCD1602
//显示hello
LCD_write_string(0,0,"hi, Jia Xiaodong");
//显示字母
LCD_write_string(3,1,"Jan-08-2008");
}
用户377235 2013-4-27 21:06
还行。改改更健康!
用户377235 2012-11-19 17:06