因为数据输入需EN引脚的一个高脉冲,其实就是EN 引脚
的一个上升沿
EN=0; //先;拉低
P0=cmd; //准备写入的数据
EN=1;//拉高,产生一个上升沿,写数据到1602
你好:
LCD 1602的响应速度相对于单片机的速度来说是偏慢的。
举个简单的例子,把一桶油通过漏斗向一个瓶子里倒,倒油的速度,即流量必须维持在一定范围之内,倒得太快油会从漏斗顶部溢出来,这样就浪费掉了。我们通过眼睛可以判断并使油面保持在顶面以下,以漏斗的额定流量来倒油,这样效率最高。
而对于单片机来说,1602好比那个瓶子漏斗,写入1602中要显示的数据好比油,如果以单片机的高运行速度向1602写数据就很可能造成上面所说的溢出,比如连续写入abc,结果只显示出了a,这是因为1602的显示芯片每次都要花时间来处理输入的ascii码数据,并把它显示出来。而我们却不容易主动地去控制写入数据的速度,所以1602使用忙信号就有必要了,每次单片机只有检测到忙信号为0,即不忙时,才向1602发数据。比如要显示abc,则这样 *** 作,写a---判忙---写b---判忙---写c---判忙。这样就不会出错了。
这几年推出的lcd,像手机的屏响应速度就比较快,而1602这个古董我用示波器测过,大约40us左右的忙处理时间,而很多速度快的单片机的指令周期都是ns级的。也就是说单片机相当一段时间都在‘等’LCD。
#include<reg52h>
#define uchar unsigned char
#define uint unsigned int
void busy(void)
{ uchar temp=0x80; //初始化temp最高位为1,使得能够进入下面
//的while循环
P0=0xff;(P0就是8个数据口)
rs=0; //设置命令 *** 作
rw=1; //设置读 *** 作
en=1; //使能
delay(100);(这是设的延时函数,不用解释)
while(temp & 0x80) //判忙,一旦表达式为假,即temp最高位为0,
//则表示1602不忙,跳出while
{temp=P0;delay(20);} //把p0的的高位读入temp,延时
en=0; //关闭使能信号
}
每次读写 *** 作都要调用这个busy函数
看门狗最好是在液晶初始化之前关,不然会不停地复位,当然就没有显示了。最好改成
WDTCN=0xde;//看门狗关
WDTCN=0xad;
PORT_Init();
SYSCLK_Init();
lcd_init(); // 对lcd初始化
#include
<reg51h>
#define
uchar
unsigned
char
#define
uint
unsigned
int
uchar
xpos
,ypos;
sbit
key=P2^0;
sbit
rs=P3^0;
sbit
rw=P3^1;
sbit
e=P3^2;
void
nop(){}
void
delay(uint
z)
//延时
{
uint
x,y;
for(x=z;x>0;x--)
for(y=112;y>0;y--);
}
void
check()
//判忙
{
rs=0;
rw=1;
e=0;
P1=0xff;
e=1;
nop();
nop();
while(P1&0x80){}
}
void
wrc(uchar
com)
//写指令
{
check();
rs=0;
rw=0;
e=1;
P1=com;
nop();
e=0;
nop();
}
void
wrd(uchar
date)
//写数据
{
check();
rs=1;
rw=0;
e=1;
P1=date;
nop();
e=0;
nop();
}
void
init()
//初始化
{
wrc(0x38);
wrc(0x01);
wrc(0x0c);
wrc(0x06);
}
void
lcdpos()
//内部数据地址指针定位
{
xpos&=0x01;
ypos&=0x1f;
if(xpos==0x00)
wrc(ypos|0x80);
else
wrc((ypos+0x40)|0x80);
}
void
lcdw(uchar
x,uchar
y,uchar
s)
//在指定的坐标下写字符串
{
xpos=x;
for(ypos=y;ypos<20;ypos++)
{
lcdpos();
wrd(s);
s++;
}
}
void
main()
{
init();
cgram();
while(1)
{
lcdw(0,0,"##########
");
lcdw(1,1,"!!!!!!!!!!
");
lcdw(2,2,"&&&&&&&&&&
");
lcdw(3,3,"$$$$$$$$$$
");
}
}
您好,//以下几条指令是在CE使能端产生一个正脉冲,使LCD响应指令digitalWrite(Enable,LOW); //先强制为低电平
delayMicroseconds(1); //延时1微秒digitalWrite(Enable,HIGH); //然后跳到高delayMicroseconds(1); //延时1微秒digitalWrite(Enable,LOW); //再回到低电平delayMicroseconds(1); //再延时一下}
//LCD数据写入函数void LcdDataWrite(int value) {int i = 0;digitalWrite(DI, HIGH); digitalWrite(RW, LOW); //RW端允许接收数据for (i=DB[0]; i <= DB[7]; i++) { //意思同上一个函数 digitalWrite(i,value &01); value >>= 1;}digitalWrite(Enable,LOW); //意思同上一个函数delayMicroseconds(1);digitalWrite(Enable,HIGH);delayMicroseconds(1);digitalWrite(Enable,LOW);delayMicroseconds(1); }
void setup (void) {int i = 0;for (i=Enable; i <= DI; i++) { //设置端口状态 pinMode(i,OUTPUT);}
//以下是LCD开机初始化的指令,多次执行0x38是确保初始化的成功率delay(100);LcdCommandWrite(0x38); // 8线2行的LCD用0x38 delay(64); LcdCommandWrite(0x38); delay(50); LcdCommandWrite(0x38); delay(20); LcdCommandWrite(0x06); //设定输入方式,增量不移位 delay(20); LcdCommandWrite(0x0E); //忘了 delay(20); LcdCommandWrite(0x01); //忘了
delay(100); LcdCommandWrite(0x80); //开显示,光标不闪 delay(20); }
提醒一下: 一般CE使能端在1602中, 大部分是在脉冲的下降沿生效。
void delay_1ms(uint x)//延时
{ uchar j; //加一变量 uchar i,j;
while((x--)!=0) //改为for(i=0;i<x;i++)
{ for(j=0;j<125;j++) ;
}
}
//检查LCD忙状态
bit lcd_busy() //其实大多数都delayms(5)代替的,尤其是仿真时不能用此函数
{
bit result;
rs=0;
rw=0;
e = 1;
delay_1ms(1);
result = (bit)(data&0x80); //不知data是不是已经#define了
e = 0;
return(result);
}
//写指令数据到LCD
void write1602_com(uchar com)
{while(lcd_busy());
rs = 0;
rw = 0;
e = 0;
delay_1ms(1);
data= com;
delay_1ms(1);
e = 1;
delay_1ms(1);
e = 0;
}
//写显示数据到LCD
void write1602_data(uchar dat)
{while(lcd_busy());
rs = 1;
rw = 0;
e = 0;
data = dat;
delay_1ms(1);
e = 1;
delay_1ms(1);
e = 0;
}
void start(void) //LCD初始化
{delay_1ms(15);
write1602_com(0x38);
delay_1ms(5);
write1602_com(0x38);
delay_1ms(5);
write1602_com(0x38);
delay_1ms(5);
write1602_com(0x0F);//显示开,关光标
delay_1ms(5);
write1602_com(0x06);//移动光标
delay_1ms(5);
write1602_com(0x01);//清除LCD的显示内容
delay_1ms(5);
}
/ 设定显示位置 /
/
void lcd_data(uchar dat) //1602两行地址不是连续的,第二行是0xC0开始的
{
write1602_com(dat|0x80); //数据指针=80+地址变量
}
/
void lcd_data(uchar dat)
{
unsigned char p;
if (pos>=0x10)
p=pos+0xb0;
else
p=pos+0x80;
write1602_com(p);
write1602_data(c);
}
void main()
{
start();
delay_1ms(10);
write1602_com(0x01); //已经清屏,不知道这句的意义
delay_1ms(10);
delay_1ms(5);
lcd_data(0x00);
delay_1ms(5);
for(i=0;i<12;i++)
{
if(i==13){ //建议这句话加上大括号,放到后边,放在这影响不好
while(1);}
write1602_data(table2[i]);//应先写地址再写数据,而你这地址是不变的,最终导致数组数据在
//一个地址上陆续显示,不知道会是啥效果,并且希望你的数组不是const的。
//可以写个连续显示的函数,加指针的
delay_1ms(1);
}
}
我这1602的程序及仿真还有很多,可以向我联系的
51单片机对lcd1602一些基础程序
#include <intrinsh>
#define dataport P1
sbit RS=P2^ 6;
sbit RW=P2^5;
sbit EN=P2^4;
//========================
//=========================
void waitfor() //检测忙信号函数
{
dataport=0xff;
RS=0;RW=1;_nop_(); //选择指令寄存器 读 *** 作
EN=1;_nop_(); //使能 *** 作
while(dataport&0x80); //如果最高位是1 表示1602正忙 原地踏步 忙完后芯片会将高位拉低
EN=0;
}
//======================
void writedata(unsigned char dataw) //写数据到lcm
{
waitfor(); //测忙
RS=1;RW=0;_nop_(); //选择数据寄存器 写 *** 作
dataport=dataw;_nop_(); //将数据送到数据口
EN=1;_nop_();_nop_();EN=0; //使能
}
//==========================
void writecmd(unsigned char cmd) //写命令到lcm
{
waitfor();
RS=0;RW=0;_nop_();
dataport=cmd;_nop_();
EN=1;_nop_();_nop_();EN=0;
}
//===========================
void init(void) // 初始化函数
{
writecmd(0x38); //功能设定 8位数据传输 双行显示
writecmd(0x0c);//显示器开关
writecmd(0x01);//清屏
writecmd(0x06);//字符进入模式 每进入一个字符光标向右移动一格 原有字符不动
//我在刚开始学的时候不知道下一个字符显示在哪 是和AC值有关还是和光标位置有关
//最后摸索出来是只和光标定位有关 现在还是不知道Ac值有什么用
}
//=========================
void location(unsigned char x,unsigned char y) //确实坐标函数
{
unsigned char temp;
temp=x&0x0f; //只要x数据的后四位
if(y){temp=temp|0x40;} //第一行为0 第二行为1 如果y=1则地址加0x40
temp|=0x80; //DDRAM地址的命令DB7为一
writecmd(temp);
}
//==============================
void displyonechar(unsigned char x,unsigned char y,unsigned char dataw) //显示一个字符函数
{
location(x,y);
writedata(dataw);
}
//=======================================
void displylistchar(unsigned char x,unsigned char y,unsigned char p) //显示字符串
{
while(p) //当一个字符型数组读完时P指的为零
{
displyonechar(x,y,(p++));
x++;
}
}
//=====================================================
void writecgram(unsigned char address,unsigned char p) //写CGRAM的数据
{
unsigned char i=8;
writecmd(address); //CGRAM里的地址 初始值0x40 每次加0x80
while(i--)
{
writedata(p);
p++;
}
}
//=====================================================
void displyonecharacter(unsigned char x,unsigned char y,unsigned char address,unsigned char p) //显示一个自定义字符
{
unsigned char i=8;
writecmd(address); //CGRAM里的地址 初始值0x40 每次加0x08
while(i--)
{
writedata(p);
p++;
}
//============================================================
location(x,y); //设定要显示的位置
writedata((address&=0x3f)/0x08); //要从CGRAM中读出数据在1602上显示 搞了半天发现CGRAM里的地址
} //和DDRAM里的地址有上面的转换关系
//========================================================
void displynumber(unsigned char x,unsigned char y,unsigned long num) //显示一个整数
{
unsigned int number[8];
int k,gh;
for(k=0;;k++)
{
(number+k)=(unsigned int)(num%10);//强制类型转换
num=num/10;
if(num==0)break;
}
for(gh=k;gh>=0;gh--)
{
displyonechar(x,y,((number+gh)+48));
x++;
}
}
//字型码
uchar code nin[]={0x08,0x0f,0x12,0x0f,0,0x1f,0x02,0x02};// "年"
uchar code yue[]={0x0f,0x09,0x0f,0x09,0x0f,0x09,0x0b,0x11};// "月"
uchar code ri[]={0x1f,0x11,0x11,0x1f,0x11,0x11,0x11,0x1f};// "日"
显示汉字
displyonecharacter(0,0,0x40,nin);
displyonecharacter(1,0,0x80,yue);
displyonecharacter(1,0,0xc0,ri);
给你一个lcd1602和键盘的程序,你稍加修改就可以用了#include "AT89X51h"
#include "lcd_1602c"
#include "keyc"
main()
{
unsigned char key;
LCD_Initial();
GotoXY(0,0);
Print("wait you press!");
while(1)
{
key=keyscan1();
if(key!=255)
{
GotoXY(0,1);
LCD_Write(1,key/100+0x30);
LCD_Write(1,key%100/10+0x30);
LCD_Write(1,key%10+0x30);
}
}
}
//端口定义
#define DBPort P0 //LCD数据端口
sbit LcdRs = P2^0;
sbit LcdRw = P2^1;
sbit LcdEn = P2^2;
sbit Lcdbf = P0^7; //LCD忙标志 Busy Flagvoid delay(unsigned int t) //延时
{
while(t--);
}void LCD_Wait(void) //读忙状态
{
LcdRs=0;
LcdRw=1;
LcdEn=1;delay(1);LcdEn=0; //下降沿
while(Lcdbf)
{
LcdEn=0;delay(1);LcdEn=1; //仿真才需要此语句,实际硬件中不需要
}
} void LCD_Write(bit style, unsigned char input) //写数据1/命令0
{
LcdRs=style;
LcdRw=0;
DBPort=input;
LcdEn=1;delay(1);LcdEn=0;
LCD_Wait();
}void LCD_Initial(void) //初始化LCD
{
LCD_Write(0,0x38); //8位数据端口,2行显示,57点阵
delay(300);
LCD_Write(0,0x0c); //显示模式
LCD_Write(0,0x01); //清屏
LCD_Write(0,0x06); //输入模式
delay(300);
}void GotoXY(unsigned char x, unsigned char y) //移动光标到指定位置
{
if(y==0) LCD_Write(0,0x80|x);
if(y==1) LCD_Write(0,0xc0|x);
}void Print(unsigned char str) //指定坐标输出字符串
{
while(str) LCD_Write(1,str++);
}
//P1接44键盘
unsigned char keyscan1() //行列法1
{
unsigned char row,rol;
P1=0x0f;
if(P1!=0x0f)
{
delay(10); //延时消抖
if (P1_0==0) row=0;
else if(P1_1==0) row=1;
else if(P1_2==0) row=2;
else if(P1_3==0) row=3;
else return(255); P1=0xf0;
if (P1_4==0) rol=0;
else if(P1_5==0) rol=1;
else if(P1_6==0) rol=2;
else if(P1_7==0) rol=3;
else return(255); while(P1!=0xf0); //等待释放
return(row4+rol);
}
else return(255);
}
以上就是关于刚学1602液晶,C51单片机写程序时,写入LCD命令,请问帮忙解释下下面程序中EN=0开始后面的几条语句,谢谢全部的内容,包括:刚学1602液晶,C51单片机写程序时,写入LCD命令,请问帮忙解释下下面程序中EN=0开始后面的几条语句,谢谢、LCD 1602显示字符,编写程序的时候,为何要弄一个忙信号判断函数这个有什么作用、用C8051F020 写 LCD 1602 液晶程序遇到的问题 液晶始终不显示等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)