MCS-51学习小结之1602液晶显示
液晶显示器件(LCD)是一种平板薄型显示器件,它的驱动电压很低、工作电流极小,可以与CMOS电路结合起来组成低功耗系统,广泛应用于各种电子产品中,但是它还是有缺点的,那就是这种器件一般不耐高温,也不耐低温,所以如果工作环境温度很恶劣的话,LCD就得特别“关照”了。
我这次调试用的是非常大众化的1602LCD液晶,截个图看看就知道了,
它是一种专门用来显示字母、数字、符号等的点阵型液晶模块,它由32个5*8点阵字符位组成,每一个点阵字符位都可以显示一个字符,但是它不能显示图形。模块内部自带有160个5*8点阵字型的字符发生器CHROM和8个可由用户自定义的5*8的字符发生器CGRAM。
在调试这个液晶之前,我心里还有点畏惧,因为以前在印象中觉得液晶的驱动程序是很复杂的,那是大二暑假时形成的一种心理状态,但是到了现在,我专门花费几天的时间正儿八经地好好看了1602的DATASHEET,上下来回反复看,终于看出点感觉了!细细琢磨了一下里面的时序图,感觉写它的驱动程序还是不难的。于是乎,开始动手写代码了,写完之后加上RS232通讯联调,意外的是,效果很好!上位机和液晶“互动”得很“默契”!嘿嘿,先给自己赞一个!
我觉得写1602的驱动程序应该好好把握11条指令,DATASHEET里面已经“明文规定”了,自己想要液晶做什么,只管把这11条指令写好再调用就是了,罗列出来如下:
(1)void Clear_display(); //清显示屏指令
(2)void Return_home(); //光标归位指令
(3)void Entry_mode_set(); //输入模式设置指令
(4)void Display_on_or_off(); //显示屏的开关控制指令
(5)void Cursor_or_Display_shift(); //设定显示屏或光标移动方向指令
(6)void _set(); //功能设定指令
(7)void Set_character_address(); //设定CGRAM地址指令
(8)void Set_display_address(); //设定DDRAM地址指令
(9)void Read_busy_flag(); //读取忙信号或AC地址指令
(10)void Write_data(); //将数据写入DDRAM或CGRAM指令
(11)void Read_data(); //从CGRAM或DDRAM读出数据的指令
这些子函数的名字可以随便起,我这里起的函数名是按照DATASHEET里的英文名写的,方便大家理解,这里的11条指令我们一般不会全都用上,只是用上其中几条就可以满足一般的应用了。关于具体怎么写这11条指令,我就不多说了,博友可以依据DATASHEET来写,上面已经说得很清楚了,至于单片机的接口方法,里面也有典型图,可以参照一下。
写驱动程序时,还有要特别注意的就是它的时序图,如果时序图理解不了的话,写驱动程序也就无从谈起了。它的读、写操作时序分别如下所示:
Write Mode Timing Diagram:
Read Mode Timing Diagram:
下面是我写的1602液晶显示代码,通过上位机和液晶进行通讯,可以实时给1602输入信息,1602也可以把数据返回到上位机,实践证明效果很好!
#i nclude "reg52.h"
#i nclude "intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit RS=P2^0; //选择是数据还是指令
sbit RW=P2^1; //选择是读操作还是写操作
sbit E =P2^2; //使能信号
sbit BF=P0^7; //忙碌状态标志位
uchar temp;
//************************************************
void delay(uint z) //大约1ms的延时
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//************************************************
bit Read_busy_flag() //读取忙碌标志位
{
bit result;
RS=0;
RW=1; //进行读操作
E=1; //允许读数据
_nop_();
result=BF;
E=0;
return result;
}
//************************************************
void Write_instruction(uchar order)
{
while(Read_busy_flag()==1);
RS=0;
RW=0; //给液晶写入指令
E=0;
_nop_();
P0=order;//将指令写入液晶
_nop_();
E=1;
_nop_();
E=0; //此时液晶就顺利接收到数据了
}
//************************************************
void Write_data(uchar dat)
{
while(Read_busy_flag()==1);
RS=1;
RW=0; //给液晶写入数据
E=0;
_nop_();
P0=dat; //将数据写入液晶
_nop_();
E=1;
_nop_();
E=0; //此时液晶就顺利接收到数据了
}
//************************************************
void Initialize() //芯片的初始化过程
{
delay(15);
Write_instruction(0x38);
delay(15);
Write_instruction(0x38);
delay(15);
Write_instruction(0x38);
delay(5);
Write_instruction(0x0f); //打开显示屏,有光标出现而且光标闪烁
delay(5);
Write_instruction(0x06); //写入数据后光标右移,显示屏不移动
delay(5);
Write_instruction(0x01); //清显示屏
delay(5);
}
//************************************************
void send_back() //数据返回子程序
{
SBUF=temp;
while(!TI); //等待数据发送完毕
TI=0; //将发送标志位清零
}
//************************************************
void main(void)
{
Initialize();
delay(10);
TMOD=0x20; //定时器1,工作方式2
TH1 =0xfd;
TL1 =0xfd; //给定时器1装初值,设定波特率为9600bps
PCON=0x00; //波特率不进行加倍
SM0 =0;
SM1 =1; //串行工作方式1
REN =1; //允许接收数据
ES =1; //打开串口中断4
TR1 =1; //启动定时器1
EA =1; //打开总中断
while(1);
}
//************************************************
void UART() interrupt 4 //串行口中断服务程序
{
if(RI==1) //液晶接收到数据时该位自动置为1。
{
RI=0;
temp=SBUF;
send_back(); //把接收到的数据返回到上位机
if(temp==01)
{
Write_instruction(0x01); //清显示屏
}
Write_data(temp); //上位机给液晶写数据
}
}