想要对LCD1602液晶显示屏有更好的理解,学会看时序图是必不可少的,把手册中的表与时序图结合起来看。先来看一下读操作时序的RS引脚和R/W引脚,这两个引脚先进行变化,因为是读操作,所以是R/W引脚首先要置位高电平,而不管它原来是什么。读指令还是读数据,都是读操作,而且都有可能,所以RS引脚既有可能置位高电平,也有可能置位低电平,而RS和R/W变化了经过tsp1这段时间后,使能引脚E才能从低电平到高电平发生变化。而使能引脚E拉高经过tD这段时间后,LCD1602输出DB的数据就是有效数据了,就可以来读取DB的数据了。读完后,要先把使能E拉低,经过一段时间后RS、R/W和DB才可以变化继续为下一次读写做准备了。
tc:指的是使能引脚E从本次上升沿到下次上升沿的最短时间是是400ns。
tpw:使能引脚E高电平的持续时间最短是150ns。
tr,tf:指使能引脚E的上升沿时间和下降沿时间,不能超过25ns。
tsp1:指的是RS和R/W引脚使能后至少保持30ns,使能引脚E才可以变成高电平。
tHD1:使能引脚E变成变成电平后,至少保持10ns后,RS和R/W才能进行变化。
TD:使能引脚E变成高电平后,最多100ns后,1602就把数据送出来了,就可以正常去读取状态或者数据了。
thd2:指的是读操作过程中,使能引脚E变成高电平后,至少保持20ns后,DB数据总线才可以进行变化。
tsp2:DB数据总线准备好后,至少保持40ns后,使能引脚E才可以从低到高进行使能变化。
thd2:指的是写操作过程中,要引脚E变成低电平后,至少保持10ns后,DB数据总线才可以变化。
#include <STC12C5A60S2.H>
sbit RS = P2^0;
sbit RW = P2^1;
sbit E = P1^2;
#define Data P0
bit flag500ms = 0;//50ms定时标志
unsigned char T0RH=0;//T0重载值的高字节
unsigned char T0RL=0;//T0重载值的低字节
unsigned char code str1[]="Welcome to HPU";//待显示的第一行字符串
unsigned char code str2[]="HPU EDDHPU EDD";//待显示的第二行字符串
void LCD_init();
void LCD_Display(unsigned char x,unsigned char y,
unsigned char *p,unsigned char len);
void ConfigTimer0(unsigned int ms);
void main()
{
unsigned char i;
unsigned char index = 0;//移动索引
unsigned char pdata bufMove1[16+sizeof(str1)+16];//移动显示缓冲区1
unsigned char pdata bufMove2[16+sizeof(str2)+16];//移动显示缓冲区2
EA = 1;//开总中断
ConfigTimer0(10);//配置T0定时10ms
LCD_init();//初始化液晶1602
for(i=0;i<16;i++)//缓冲区开头一段填充为空格
{
bufMove1=' ';
bufMove2=' ';
}
for(i=0;i<(sizeof(str1)-1);i++)//待显示字符拷贝到缓冲区中间位置
{
bufMove1[16+i] = str1;
bufMove2[16+i] = str2;
}
for(i=(16+sizeof(str1)-1);i<sizeof(bufMove1);i++)////缓冲区结尾一段填充为空格
{
bufMove1 = ' ';
bufMove2 = ' ';
}
while(1)
{
if(flag500ms)//每500ms移动一次屏幕
{
flag500ms=0;
LCD_Display(0,0,bufMove1+index,16);//从缓冲区抽出需显示的一段字符显示到液晶上
LCD_Display(0,1,bufMove2+index,16);
index++;//移动索引递增,实现左移
if(index>=(16+sizeof(str1)-1))//起始位置达到字符串尾部后即返回重头开始
{index=0;}
}
}
}
void delay_us(unsigned int n)//微秒级延时函数
{
extern void _nop_(void);
register unsigned char i = n, j = (n>>8);
_nop_(); _nop_(); _nop_();
if ((--i) | j)
{
do
{
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
if (0xFF == (i--)) j--; else {_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();};
} while (i | j);
}
}
void delay_ms(unsigned int n)//毫秒级延时函数
{
while (n--) delay_us(1000);
}
void LCD_Write_Cmd(unsigned char cmd)//向LCD液晶写入一字节命令,cmd为待写入的命令值
{
RS = 0;
RW = 0;
Data = cmd;
delay_us(1);
E = 1;
delay_us(1);
E = 0;
delay_ms(2);
}
void LCD_Write_Data(unsigned char dat)//向LCD液晶写入一字节数据,cmd为待写入的数据值
{
RS = 1;
RW = 0;
Data = dat;
delay_us(1);
E = 1;
delay_us(1);
E = 0;
delay_ms(2);
}
void LcdSetCursor(x,y)//设置显示RAM起始地址,亦即光标位置,(x,y)为对应屏幕上的字符坐标
{
unsigned char addr;
if(y==0)//由输入的屏幕坐标计算显示RAM地址
addr = 0x00+x;//第一行字符从0x00起始
else
addr = 0x40+x;//第二行字符地址从0x40起始
LCD_Write_Cmd(addr|0x80);//设置RAM地址
}
void LCD_Display(unsigned char x,unsigned char y,
unsigned char *p,unsigned char len)//在液晶上显示字符串,(x,y)为对应屏幕上的起始坐标,p为字符串指针,len为需显示的字符长度
{
LcdSetCursor(x,y);//设置起始地址
while (len--)//连续写入len个字符数据
{
LCD_Write_Data(*p++);//先显示p指向的数据,然后p自加1
}
}
void LCD_init()//LCD 初始化函数
{
delay_ms(40);
LCD_Write_Cmd(0x38);
delay_ms(5);
LCD_Write_Cmd(0x38);
delay_ms(1);
LCD_Write_Cmd(0x38);
LCD_Write_Cmd(0x38);//16*2显示,5*7点阵,8位数据接口
LCD_Write_Cmd(0x08);//关闭显示
LCD_Write_Cmd(0x01);//清屏
LCD_Write_Cmd(0x06);//文字不动,地址自动加1
LCD_Write_Cmd(0x0c);//显示器开,光标关闭
}
void ConfigTimer0(unsigned int ms)//配置并启动T0,ms为T0定时时间
{
unsigned int tmp;//临时变量
tmp = 33177600/12;//定时器计数频率
tmp = (tmp*ms)/1000;//计数所需的计数值
tmp = 65536 - tmp;//计算定时器重载值
T0RH = (unsigned char)(tmp>>8);//定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0;//清零T0的控制位
TMOD |= 0x01;//配置T0为模式1
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;//使能T0中断
TR0 = 1;//启动T0
}
void InterruptTimer0() interrupt 1//T0中断服务函数,定时500ms
{
static unsigned char tmr500ms = 0;
TH0 = T0RH;//重新加载重载值
TL0 = T0RL;
tmr500ms++;
if(tmr500ms>=100)
{
tmr500ms=0;
flag500ms=1;
}
}
还请各位高手斧正,谢谢。
|