本篇博文介绍的是用51单片机的非标准写协议【上】(LCD1602),包含初识LCD1602,LCD在一个位置显示一个字母的逻辑,LCD1602写时序分析,编程实现LCD1602显示字符C,编程实现LCD1602显示一行。看到这篇博文的朋友,可以先赞再看吗?
一、基本电路标识识别和接线,例如VCC,GND。
二、数电时序图的阅读,高低电平的识别。
三、C变量
四、基本输入输出
五、流程控制
六、函数
七、指针
八,字符串
如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!
LCD1602(Liquid Crystal Display)是一种工业字符型液晶,能够同时显示 16×02 即 32 字符(16列两行)
编号 | 符号 | 引脚说明 | 编号 | 符号 | 引脚说明 |
---|---|---|---|---|---|
1 | VSS | 电源地 | 9 | D2 | 数据 |
2 | VDD | 电源正极 | 10 | D3 | 数据 |
3 | VL | 液晶显示偏压 | 11 | D4 | 数据 |
4 | RS | 数据/命令选择 | 12 | D5 | 数据 |
5 | R/W | 读/写选择 | 13 | D6 | 数据 |
6 | E | 使能信号 | 14 | D7 | 数据 |
7 | D0 | 数据 | 15 | BLA | 背光源正极 |
8 | D1 | 数据 | 16 | BLK | 背光源负极 |
第 1 脚: VSS
为电源地
第 2 脚: VDD
接 5V 正电源
第 3 脚: VL
为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”, 使用时可以通过一个 10K 的电位器调整对比度。(注:电位器可用50K的,接线方式为左右两只引脚接VCC,GND,中间引脚接VL
)
第 4 脚: RS
为寄存器选择,高电平时选择数据寄存器、 低电平时选择指令寄存器。
第 5 脚: R/W
为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 R/W 共同为低电平时可以写入指令或者显示地址,当 RS 为低电平 R/W 为高电平时可以读忙信号,当 RS 为高电平 R/W 为低电平时可以写入数据。
第 6 脚: E
端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。
第 7~14 脚: D0~D7
为 8 位双向数据线。
**第 15 脚:**背光源正极。
**第 16 脚:**背光源负极。
VSS -- GND
VDD -- 5V
VL -- GND
RS -- P1.0
RW -- P1.1
E -- P1.4
A -- 5V
K -- GND
D0到D7 -- P0.1到P0.7
LCD1602
液晶模块内部的控制器共有 11
条控制指令,如表 2 所示:
序号 | 指令 | RS | R/W | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 清显示 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
2 | 光标返回 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * |
3 | 置输入模式 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S |
4 | 显示开/关控制 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B |
5 | 光标或字符移位 | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | * | * |
6 | 置功能 | 0 | 0 | 0 | 0 | 1 | DL | N | F | * | * |
7 | 置字符发生存贮器地址 | 0 | 0 | 0 | 1 | 字符发生存贮器地址(自定义字符) | |||||
8 | 置数据存贮器地址 | 0 | 0 | 1 | 显示数据存贮器地址(在哪里显示) | ||||||
9 | 读忙标志或地址 | 0 | 1 | BF | 计算器地址 | ||||||
10 | 写数到 CGRAM 或 DDRAM | 1 | 0 | 要写的数据内容(显示什么) | |||||||
11 | 从 CGRAM 或 DDRAM 读数 | 1 | 1 | 读出的数据内容 |
1602 液晶模块的读写操作,屏幕和光标的操作都是通过指令编程来实现的。(说明: 1 为高电平、 0 为低电平)
**指令 1:**清显示,指令码 01H,光标复位到地址 00H 位置。
**指令 2:**光标复位,光标返回到地址 00H。
**指令 3:**光标和显示模式设置。 I/D:光标移动方向,高电平右移,低电平左移。 实际上就是控制从左到右写入还是从右至左的写入顺序。 S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效。 S=1 当写一个字符,整屏显示左移(ID=1)或者右移(I/D=0),以得到光标不移动而屏幕移动的效果。 S=0 当写一个字符,整屏显示不移动。
**指令 4:**显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示. C:控制光标的开与关,高电平表示有光标,低电平表示无光标. B:控制光标是否闪烁, 高电平闪烁,低电平不闪烁。
**指令 5:**光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标。 R/L:文字或者光标移动方向, R 表示右移, L 表示左移。
**指令 6:**功能设置命令 DL:高电平时为 8 位总线,低电平时为 4 位总线。 N:低电平时为单行显示,高电平时双行显示。 F:低电平时显示 5×8 的点阵字符,高电平时显示 5×10的点阵字符。
**指令 7:**字符发生器 RAM 地址设置。
指令 8: DDRAM 地址设置。
**指令 9:**读忙信号和光标地址。 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
**指令 10:**写数据。
**指令 11:**读数据。
例如第二行第一个字符
的地址是 40H
,那么是否直接写入 40H
就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7 恒定为高电平 1
所以实际写入的数据应该是
**01000000B(40H)+10000000B(80H)=11000000B(C0H)。**
1602 液晶模块内部的字符发生存储器(CGROM)
已经存储了 160 个不同的点阵字符图形
, 如表 3 所示,这些字符有:阿拉伯数字
,英文字母的大小写
,常用的符号
,和日文假名等
,每一个字符都有一个固定的代码
,比如大写的英文字符“A”
的代码是 01000001B(41H),
显示时模块把地址 41H
中的点阵字符图形显示出来,我们就能看到字母“A”
。
我们可以发现上表中字符“a”的高位为0110
,低位为0001
,高位低位组合一下,便知道其16进制数为61
,十进制数为97
。刚好是字符“a”
的ASSII
码。所以1602字符码是ASSII码,那么显示一个字符就可以在代码中直接定义一个字符。
可以通过第 4 脚: RS
寄存器来解决,当高电平时选择数据寄存器、 低电平时选择指令寄存器。也就是说RS=1,就是传输字符数据,RS=0时就是传送字符位置信息。
LCD1602
数据引脚D0
到D7
LCD1602
控制引脚RS
、RW
、EN
LCD1602
写指令
和写数据
函数LCD1602
检测忙
信号函数LCD1602手册
一般初始化(复位)过程建立LCD609初始化函数
使用51单片机P0
口来传输LCD1602的字符地址
和位置
代码如下
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
P1.0
口接RS
,P1.1
口接RW
,P1.4
口接EN
//控制线定义
/*
RS--P1.0
RW--P1.1
EN--P1.4
*/
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
一样的
,只有RS的值
不一样,RS=0写指令
,RS=1是写数据
。void writeCmd(char cmd)
{
checkBusy(); //检测忙信号
RS = 0; //RS等于零,是写指令操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cmd; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平(这里一定不要忘了延时)
}
void weiteData(char cData)
{
checkBusy(); //检测忙信号
RS = 1; //RS等于1,是写数据操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cData; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
LCD1602数据位
的第8位的值
是否为1
来确定是否忙
,所以会用到while循环
来一直检测。也会用到与
十六进制的0x80
来判断,因为0x80==(1000 0000)2。
void checkBusy()
{
char tmp = 0x80;
dataBuffer = 0x80;
while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
{
RS = 0; //根据手册中指令表可知RS=0,RW=1
RW = 1; //RW=1为读的操作,所以需要配置读的时序图
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
EN = 1; //根据手册中的读时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
tmp = dataBuffer; //读取dataBuffer中的忙信号
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
}
(1) 延时 15ms
(2) 写指令 38H(不检测忙信号)
(3) 延时 5ms
(4) 以后每次写指令,读/写数据操作均需要检测忙信号
(5) 写指令 38H:显示模式设置
(6) 写指令 08H:显示关闭
(7) 写指令 01H:显示清屏
(8) 写指令 06H:显示光标移动设置
(9) 写指令 0CH:显示开及光标设置
void initLCD1602()
{
//(1) 延时 15ms
Delay15ms();
//(2) 写指令 38H(不检测忙信号)
writeCmd(0x38);
//(3) 延时 5ms
Delay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
writeCmd(0x38);
//(6) 写指令 08H:显示关闭
writeCmd(0x08);
//(7) 写指令 01H:显示清屏
writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置
writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
writeCmd(0x0C);
}
void main()
{
char address = 0x80 + 0x05; //字符显示位置
char cData = 'C'; //显示的字符数据
initLCD1602(); //初始化LCD1602
writeCmd(address); //写字符位置
weiteData(cData); //写字符数据
}
#include "reg52.h"
#include "intrins.h"
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
//控制线定义
/*
RS--P1.0
RW--P1.1
EN--P1.4
*/
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
void checkBusy()
{
char tmp = 0x80;
dataBuffer = 0x80;
while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
{
RS = 0; //根据手册中指令表可知RS=0,RW=1
RW = 1; //RW=1为读的操作,所以需要配置读的时序图
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
EN = 1; //根据手册中的读时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
tmp = dataBuffer; //读取dataBuffer中的忙信号
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
}
void writeCmd(char cmd)
{
checkBusy(); //检测忙信号
RS = 0; //RS等于零,是写指令操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cmd; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void weiteData(char cData)
{
checkBusy(); //检测忙信号
RS = 1; //RS等于1,是写数据操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cData; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void initLCD1602()
{
//(1) 延时 15ms
Delay15ms();
//(2) 写指令 38H(不检测忙信号)
writeCmd(0x38);
//(3) 延时 5ms
Delay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
writeCmd(0x38);
//(6) 写指令 08H:显示关闭
writeCmd(0x08);
//(7) 写指令 01H:显示清屏
writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置
writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
writeCmd(0x0C);
}
void main()
{
char address = 0x80 + 0x05; //字符显示位置
char cData = 'C'; //显示的字符数据
initLCD1602(); //初始化LCD1602
writeCmd(address); //写字符位置
weiteData(cData); //写字符数据
}
LCD1602ShowARow
的函数,形参为字符型的行
,字符型的列
,字符型的指针变量str
。switch
来选择哪行显示。void LCD1602ShowARow(char line,char column,char *str)
switch(line)
{
case 1:
break;
case 2:
break;
}
void LCD1602ShowARow(char line,char column,char *str)
{
switch(line) //选择哪行显示
{
case 1: //第一行显示
writeCmd(0x80 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数
while(*str) //判断字符串指针变量不为空执行循环,不用判断字符串结束标志
{
weiteData(*str); //发送字符串单个字符
str++; //字符串指针变量偏移
}
break;
case 2: //第二行显示
writeCmd(0x80 + 0x40 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数,又因为第二行的列从0x40开始
while(*str) //所以是从0x80 + 0x40 + 列数(特别注意)
{
weiteData(*str);
str++;
}
break;
}
}
#include "reg52.h"
#include "intrins.h"
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
//控制线定义
/*
RS--P1.0
RW--P1.1
EN--P1.4
*/
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
void checkBusy()
{
char tmp = 0x80;
dataBuffer = 0x80;
while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
{
RS = 0; //根据手册中指令表可知RS=0,RW=1
RW = 1; //RW=1为读的操作,所以需要配置读的时序图
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
EN = 1; //根据手册中的读时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
tmp = dataBuffer; //读取dataBuffer中的忙信号
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
}
void writeCmd(char cmd)
{
checkBusy(); //检测忙信号
RS = 0; //RS等于零,是写指令操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cmd; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void weiteData(char cData)
{
checkBusy(); //检测忙信号
RS = 1; //RS等于1,是写数据操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cData; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void initLCD1602()
{
//(1) 延时 15ms
Delay15ms();
//(2) 写指令 38H(不检测忙信号)
writeCmd(0x38);
//(3) 延时 5ms
Delay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
writeCmd(0x38);
//(6) 写指令 08H:显示关闭
writeCmd(0x08);
//(7) 写指令 01H:显示清屏
writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置
writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
writeCmd(0x0C);
}
void LCD1602ShowARow(char line,char column,char *str)
{
switch(line) //选择哪行显示
{
case 1: //第一行显示
writeCmd(0x80 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数
while(*str) //判断字符串指针变量不为空执行循环,不用判断字符串结束标志
{
weiteData(*str); //发送字符串单个字符
str++; //字符串指针变量偏移
}
break;
case 2: //第二行显示
writeCmd(0x80 + 0x40 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数,又因为第二行的列从0x40开始
while(*str) //所以是从0x80 + 0x40 + 列数(特别注意)
{
weiteData(*str);
str++;
}
break;
}
}
void main()
{
initLCD1602(); //初始化LCD1602
LCD1602ShowARow(1,6,"MaGe");
LCD1602ShowARow(2,2,"You Handsome");
}
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!