51单片机1602液晶驱动程序(二)模拟口线方式<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
LCM1602除了总线方式控制外,还可以用模拟口线方式控制,模拟口线线路图如下:
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
程序如下:
/*=========================================================
SMC<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />1602A(16*2)模拟口线接线方式 连接线图:
---------------------------------------------------
|LCM-----51 | LCM-----51 | LCM------51 |
---------------------------------------------|
|DB0-----P1.0 | DB4-----P1.4 | RW-------P2.0 |
|DB1-----P1.1 | DB5-----P1.5 | RS-------P2.1 |
|DB2-----P1.2 | DB6-----P1.6 | E--------P2.2 |
|DB3-----P1.3 | DB7-----P1.7 | VLCD 接 1K 电阻到 GND|
---------------------------------------------------
[注:AT89S51 使用 12M晶体震荡器]
=========================================================*/
#include <reg51.h>
sbit LCM_RW=P2^0; //定义引脚
sbit LCM_RS =P2^1;
sbit LCM_E =P2^2;
#define LCM_Data P1
#define Busy 0x80 //用于检测 LCM 状态字中的 Busy 标识
void WriteDataLCM(unsigned char WDLCM);
void WriteCommandLCM(unsigned char WCLCM,BuysC);
unsigned char ReadDataLCM(void);
unsigned char ReadStatusLCM(void); void LCMInit(void);
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);
void Delayms(unsigned int n);
void dellay(unsigned int h);
unsigned char code blog_adr[] = {"EDNchina"};
unsigned char code email[] = {"tengjingshu@126.com"};
void main(void)
{
//Delay400Ms(); //启动等待,等 LCM 讲入工作状态
LCMInit(); //LCM 初始化
DisplayListChar(6, 0, blog_adr);
DisplayListChar(0, 0, email);
while(1);
}
//写数据 RS="H",RW=L,D0~D7=数据,E=高脉冲
void WriteDataLCM(unsigned char WDLCM)
{
dellay(100);
LCM_E = 0;
LCM_RS = 1;
LCM_RW = 0;
LCM_Data = WDLCM;
//dellay(100); //短暂延时,代替检测忙状态
//ReadStatusLCM(); //检测忙
LCM_E = 1;
LCM_E = 0;
}
//写指令 RS="L",RW=L,D0~D7=指令码,E=高脉冲
void WriteCommandLCM(unsigned char WCLCM,BuysC)
//BuysC 为 0 时忽略忙检测
{
//if (BuysC) ReadStatusLCM(); //根据需要检测忙
dellay(100); //短暂延时,代替检测忙状态
LCM_E = 0;
LCM_RS = 0;
LCM_RW = 0;
LCM_Data = WCLCM;
LCM_E = 1;
LCM_E = 0;
}
//读数据 RS="H",RW=H,E=H
unsigned char ReadDataLCM(void)
{
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 1;
return(LCM_Data);
}
//读状态 RS="L",RW=H,E=H
unsigned char ReadStatusLCM(void)
{
LCM_Data = 0xFF;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 1;
//while (LCM_Data & Busy); //检测忙信号
return(LCM_Data);
}
void LCMInit(void) //LCM 初始化
{
LCM_Data = 0;
Delayms(15);
WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
Delayms(5);
WriteCommandLCM(0x38,0);
Delayms(5);
WriteCommandLCM(0x38,0);
WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCM(0x08,1); //关闭显示
WriteCommandLCM(0x01,1); //显示清屏
WriteCommandLCM(0x06,1); // 显示光标移动设置
WriteCommandLCM(0x0C,1); // 显示开及光标设置
}
//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制 X 不能大于 15,Y 不能大于 1
if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
X |= 0x80; //算出指令码
WriteCommandLCM(X, 1); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
//按指定位置显示一串字符
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
unsigned char ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //限制 X 不能大于 15,Y 不能大于 1
while (DData[ListLength]>0x1f) //若到达字串尾则退出
{
if (X <= 0xF) //X 坐标应小于 0xF
{
DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
ListLength++; X++;
}
}
}
//延时程序
void Delayms(unsigned int n)
{
unsigned int i,j;
for(j=n;j>0;j--)
for(i=112;i>0;i--);
}
/**************************************************
** 函数名称: dellay
** 入口参数:h(unsigned int型)
** 出口参数:无
** 功能描述: 短暂延时,使用12MHz晶体,约0.01MS
****************************************************/
void dellay(unsigned int h)
{
while(h--); //0.01MS
}
本文程序下载:https://static.assets-stash.eet-china.com/album/old-resources/2009/4/10/296a56e8-0084-4087-950b-d50c57d389ff.rar
要注意的是在读写程序中,没有用 “检测忙”,其实对于1602来说,没有检测忙信号对于实际来说还好,因为常常因为检测忙,而使1602没显示(一直处于忙检测中)。“忙检测”用一个小延时代替。
对于LCM1602来说,读写时序最重要。
LCM1602写操作时序
//写数据 RS="H",RW=L,D0~D7=数据,E=高脉冲
void WriteDataLCM(unsigned char WDLCM)
{
dellay(100); //短暂延时,代替检测忙状态
LCM_E = 0;
LCM_RS = 1;
LCM_RW = 0;
LCM_Data = WDLCM;
LCM_E = 1;
LCM_E = 0;
}
//写指令 RS="L",RW=L,D0~D7=指令码,E=高脉冲
void WriteCommandLCM(unsigned char WCLCM)
{
dellay(100); //短暂延时,代替检测忙状态
LCM_E = 0;
LCM_RS = 0;
LCM_RW = 0;
LCM_Data = WCLCM;
LCM_E = 1;
LCM_E = 0;
}
上面两个分别为写数据函数和写命令函数,检测忙已用小延时代替。其实这个时序好像不太严格,但要保证的是E高脉冲时,写的数据/命令是有效的。
好像函数也可以写成这样:
void WriteCommandLCM(unsigned char WCLCM)
{
dellay(100); //短暂延时,代替检测忙状态
LCM_Data = WCLCM;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 0;
dellay(100);
LCM_E = 1;
}
//按指定位置显示一串字符
函数DisplayListChar的作用是在指定位置显示一串字符,其中有一句
“while (DData[ListLength]>0x1f) //若到达字串尾则退出”
为什么要大于0x20呢?
unsigned char code blog_adr[] = {"EDNchina"};
unsigned char code email[] = {"tengjingshu@126.com"};
用单引号’’ ( )括起来的字符为字符的ASCII码值,而不是字符串。
用双引号””(shift+ )括起来的一串字符,成为字符串常量。C编译器会自动地在字符末尾加上结束符’\0’(NULL) (ASCII码为0x00也就是00H)。
char a[]={“Bei Jing”};
char a[]={‘B’,’e’,’I’,’ ‘,’J’,’i’,’n’,’g’,’\0’};
两者是等价的,数组的每个元素为对应字符的ASCII码,如a[3]数组a的第四个元素是’ ‘空格,则a[3]里面放着的是空格’ ‘的ASCII码0x20。
还要注意的是数组的元素数目一定要比字符多一个。以便C编译器自动在其后面加入结束符’\0’。
下面是ASCII表(0~127)
NUL | VT 垂直制表 | SYN 空转同步 |
SOH 标题开始 | FF 走纸控制 | ETB 信息组传送结束 |
STX 正文开始 | CR 回车 | CAN 作废 |
ETX 正文结束 | SO 移位输出 | EM 纸尽 |
EOY 传输结束 | SI 移位输入 | SUB 换置 |
ENQ 询问字符 | DLE 空格 | ESC 换码 |
ACK 承认 | DC1 设备控制1 | FS 文字分隔符 |
BEL 报警 | DC2 设备控制2 | GS 组分隔符 |
BS 退一格 | DC3 设备控制3 | RS 记录分隔符 |
HT 横向列表 | DC4 设备控制4 | US 单元分隔符 |
LF 换行 | NAK 否定 | DEL 删除 |
摘自:http://hi.baidu.com/ffxung/blog/item/cdccc8e93259533ab90e2d5e.html
可以知道
‘\0’ ASCII码为0x00
‘\n’ASCII码为0x0A
那知道为什么有这句了吧
“while (DData[ListLength]>0x1F) //若到达字串尾则退出”
因为大于0x1f才能显示字符,小于和等于0x1f的都是键盘控制符。
当然我们也可以检测’\0’(0x00)
“while (DData[ListLength]!='\0') //检测到字符串结束符则退出”
用户145263 2009-4-11 08:27