本文主要是介绍人生绕不开一台LCD1602,虽然它比较丑,接的线也比较多。#非标协议【上】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
人生绕不开一台LCD1602,虽然它比较丑,接的线也比较多。#非标协议【上】
- 前言
- 预备知识
- 1.初识LCD1602
- 1.1概述
- 1.2引脚说明
- 1.3和单片机接线说明
- 1.4LCD1602 的指令说明
- 2.LCD在一个位置显示一个字母的逻辑
- 2.1在哪里显示
- 2.2显示的是什么
- 2.4如何解决D0到D7既要传输字符位置信息又要传输字符数据信息
- 3.LCD1602写时序分析
- 3.1写时序图与写时序参数
- 3.2写时序图分析
- 4.编程实现LCD1602显示字符C
- 4.1编程实现LCD1602显示字符C的核心思路
- 4.2配置LCD1602数据引脚D0到D7
- 4.3**配置LCD1602控制引脚RS、RW、EN**
- 4.4根据写时序图配置LCD1602写指令和写数据函数
- 4.5根据读时序图配置LCD1602检测忙信号函数
- 4.6根据LCD1602手册一般初始化(复位)过程建立LCD609初始化函数
- 4.7主函数整合
- 4.8完整程序代码
- 5.编程实现LCD1602显示一行
- 5.1编程实现LCD1602显示一行核心思路
- 5.2定义一个空类型名为LCD1602ShowARow的函数,形参为字符型的行,字符型的列,字符型的指针变量str。
- 5.3运用C语言中的选择语句switch来选择哪行显示。
- 5.4整体算法见代码
- 5.5完整程序代码
- 5.6 LCD1602接收字符串时不必担心光标偏移问题,LCD1602会自动偏移。
- 结束语
前言
本篇博文介绍的是用51单片机的非标准写协议【上】(LCD1602),包含初识LCD1602,LCD在一个位置显示一个字母的逻辑,LCD1602写时序分析,编程实现LCD1602显示字符C,编程实现LCD1602显示一行。看到这篇博文的朋友,可以先赞再看吗?
预备知识
一、基本电路标识识别和接线,例如VCC,GND。
二、数电时序图的阅读,高低电平的识别。
三、C变量
四、基本输入输出
五、流程控制
六、函数
七、指针
八,字符串
如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!
1.初识LCD1602
1.1概述
LCD1602(Liquid Crystal Display)是一种工业字符型液晶,能够同时显示 16×02 即 32 字符(16列两行)
1.2引脚说明
编号 | 符号 | 引脚说明 | 编号 | 符号 | 引脚说明 |
---|---|---|---|---|---|
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 脚:**背光源负极。
1.3和单片机接线说明
- 电源
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
1.4LCD1602 的指令说明
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:**读数据。
2.LCD在一个位置显示一个字母的逻辑
2.1在哪里显示
例如第二行第一个字符
的地址是 40H
,那么是否直接写入 40H
就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7 恒定为高电平 1
所以实际写入的数据应该是
**01000000B(40H)+10000000B(80H)=11000000B(C0H)。**
2.2显示的是什么
1602 液晶模块内部的字符发生存储器(CGROM)
已经存储了 160 个不同的点阵字符图形
, 如表 3 所示,这些字符有:阿拉伯数字
,英文字母的大小写
,常用的符号
,和日文假名等
,每一个字符都有一个固定的代码
,比如大写的英文字符“A”
的代码是 01000001B(41H),
显示时模块把地址 41H
中的点阵字符图形显示出来,我们就能看到字母“A”
。
我们可以发现上表中字符“a”的高位为0110
,低位为0001
,高位低位组合一下,便知道其16进制数为61
,十进制数为97
。刚好是字符“a”
的ASSII
码。所以1602字符码是ASSII码,那么显示一个字符就可以在代码中直接定义一个字符。
2.4如何解决D0到D7既要传输字符位置信息又要传输字符数据信息
可以通过第 4 脚: RS
寄存器来解决,当高电平时选择数据寄存器、 低电平时选择指令寄存器。也就是说RS=1,就是传输字符数据,RS=0时就是传送字符位置信息。
3.LCD1602写时序分析
3.1写时序图与写时序参数
- 写时序图
- 时序参数
3.2写时序图分析
4.编程实现LCD1602显示字符C
4.1编程实现LCD1602显示字符C的核心思路
- 配置
LCD1602
数据引脚D0
到D7
- 配置
LCD1602
控制引脚RS
、RW
、EN
- 根据写时序图配置
LCD1602
写指令
和写数据
函数 - 根据读时序图配置
LCD1602
检测忙
信号函数 - 根据
LCD1602手册
一般初始化(复位)过程建立LCD609初始化函数
- 主函数整合
4.2配置LCD1602数据引脚D0到D7
-
使用51单片机
P0
口来传输LCD1602的字符地址
和位置
-
代码如下
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
4.3配置LCD1602控制引脚RS、RW、EN
- 使用51单片机的
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;
4.4根据写时序图配置LCD1602写指令和写数据函数
- 写指令和写数据函数的时序都是
一样的
,只有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保持一段时间的低电平
}
4.5根据读时序图配置LCD1602检测忙信号函数
- 检测忙函数可以根据LCD1602手册中指令说明指令9进行配置请看1.4,此时会发现RW=1,那么就会用到读时序图
- 因为要检测
LCD1602数据位
的第8位的值
是否为1
来确定是否忙
,所以会用到while循环
来一直检测。也会用到与
十六进制的0x80
来判断,因为0x80==(1000 0000)2。
- 时序图代码解释
- LCD1602检测忙信号函数
void checkBusy()
{char tmp = 0x80;dataBuffer = 0x80;while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。{RS = 0; //根据手册中指令表可知RS=0,RW=1RW = 1; //RW=1为读的操作,所以需要配置读的时序图EN = 0; //根据手册中的写时序图,EN开始为低电平_nop_(); //执行空函数等待EN拉高EN = 1; //根据手册中的读时序图,EN被拉高_nop_(); //执行空函数等待tpw和tF高电平过程_nop_();tmp = dataBuffer; //读取dataBuffer中的忙信号EN = 0; //根据手册中的写时序图,EN被拉低_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平}
}
4.6根据LCD1602手册一般初始化(复位)过程建立LCD609初始化函数
- 手册相关内容
(1) 延时 15ms
(2) 写指令 38H(不检测忙信号)
(3) 延时 5ms
(4) 以后每次写指令,读/写数据操作均需要检测忙信号
(5) 写指令 38H:显示模式设置
(6) 写指令 08H:显示关闭
(7) 写指令 01H:显示清屏
(8) 写指令 06H:显示光标移动设置
(9) 写指令 0CH:显示开及光标设置
- LCD609初始化函数
void initLCD1602()
{
//(1) 延时 15msDelay15ms();
//(2) 写指令 38H(不检测忙信号)writeCmd(0x38);
//(3) 延时 5msDelay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置writeCmd(0x38);
//(6) 写指令 08H:显示关闭writeCmd(0x08);
//(7) 写指令 01H:显示清屏writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置writeCmd(0x0C);
}
4.7主函数整合
- 主函数代码
void main()
{char address = 0x80 + 0x05; //字符显示位置char cData = 'C'; //显示的字符数据initLCD1602(); //初始化LCD1602writeCmd(address); //写字符位置weiteData(cData); //写字符数据}
4.8完整程序代码
#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=1RW = 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) 延时 15msDelay15ms();
//(2) 写指令 38H(不检测忙信号)writeCmd(0x38);
//(3) 延时 5msDelay5ms();
//(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(); //初始化LCD1602writeCmd(address); //写字符位置weiteData(cData); //写字符数据}
5.编程实现LCD1602显示一行
5.1编程实现LCD1602显示一行核心思路
- 定义一个空类型名为
LCD1602ShowARow
的函数,形参为字符型的行
,字符型的列
,字符型的指针变量str
。 - 运用C语言中的选择语句
switch
来选择哪行显示。 - 整体算法见代码
5.2定义一个空类型名为LCD1602ShowARow的函数,形参为字符型的行,字符型的列,字符型的指针变量str。
void LCD1602ShowARow(char line,char column,char *str)
5.3运用C语言中的选择语句switch来选择哪行显示。
switch(line)
{case 1:break;case 2:break;
}
5.4整体算法见代码
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;}
}
5.5完整程序代码
#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=1RW = 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) 延时 15msDelay15ms();
//(2) 写指令 38H(不检测忙信号)writeCmd(0x38);
//(3) 延时 5msDelay5ms();
//(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(); //初始化LCD1602LCD1602ShowARow(1,6,"MaGe"); LCD1602ShowARow(2,2,"You Handsome");}
5.6 LCD1602接收字符串时不必担心光标偏移问题,LCD1602会自动偏移。
结束语
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!
这篇关于人生绕不开一台LCD1602,虽然它比较丑,接的线也比较多。#非标协议【上】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!