51单片机_LCD1602液晶显示
仿真电路图:
一、 LCD1602 简介
普中开发板原理图:
DDRAM(显示数据RAM)地址映射
LCD1602内部有80字节的DDRAM,但只有前32字节对应到实际显示位置:
行号物理位置DDRAM地址(十六进制)实际显示地址(写入命令)说明第1行1-16字符0x00 - 0x0F0x80 + 列号 (0x80-0x8F)标准显示区域第1行17-40字符0x10 - 0x270x90 - 0xA7不显示,但可用于滚动第2行1-16字符0x40 - 0x4F0xC0 + 列号 (0xC0-0xCF)标准显示区域第2行17-40字符0x50 - 0x670xD0 - 0xE7不显示,但可用于滚动实际使用的显示地址表格
第一行显示地址(从0开始计数):
列位置实际地址命令值备注第1列0x000x80第一行起始第2列0x010x81第3列0x020x82第4列0x030x83第5列0x040x84第6列0x050x85第7列0x060x86第8列0x070x87第9列0x080x88第10列0x090x89第11列0x0A0x8A第12列0x0B0x8B第13列0x0C0x8C第14列0x0D0x8D第15列0x0E0x8E第16列0x0F0x8F第一行末尾第二行显示地址(从0开始计数):
列位置实际地址命令值备注第1列0x400xC0第二行起始第2列0x410xC1第3列0x420xC2第4列0x430xC3第5列0x440xC4第6列0x450xC5第7列0x460xC6第8列0x470xC7第9列0x480xC8第10列0x490xC9第11列0x4A0xCA第12列0x4B0xCB第13列0x4C0xCC第14列0x4D0xCD第15列0x4E0xCE第16列0x4F0xCF第二行末尾地址确定原理
为什么地址是0x80+列号?
LCD1602的指令集中规定:
- 设置DDRAM地址的命令格式:1 A6 A5 A4 A3 A2 A1 A0
- 二进制:1xxxxxxx,所以最高位始终为1
- 十六进制:0x8x(第一行)或0xCx(第二行)
地址相关的重要指令
指令二进制十六进制功能说明清屏0000 00010x01清除显示,地址归0归位0000 00100x02地址归0,显示回原位进入模式0000 01100x06地址自动递增,显示不移显示开关0000 11000x0C显示开,光标关,闪烁关功能设置0011 10000x388位数据,2行显示,5×8点阵地址设置1xxx xxxx0x8x或0xCx设置DDRAM地址指令集(11条指令):
表1
(1)指令1:清屏。指令码01H,光标复位到地址00H。
(2)指令2:光标复位。光标复位到地址00H。
(3)指令3:输入方式设置。
其中,I/D表示光标的移动方向,高电平右移,低电平左移;S表示显示屏上所有文字是否左移或右移,高电平表示有效,低电平表示无效。
(4)指令4:显示开关控制。
其中,D用于控制整体显示的开与关,高电平表示开显示,低电平表示关显示;C用于控制光标的开与关,高电平表示有光标,低电平表示无光标;B用于控制光标是否闪烁,高电平闪烁,低电平不闪烁。
(5)指令5:光标或字符移位控制。
其中,S/C表示在高电平时移动显示的文字,低电平时移动光标。
(6)指令6:功能设置命令。
其中,DL表示在高电平时为4位总线,低电平时为8位总线;N表示在低电平时为单行显示,高电平时双行显示;F表示在低电平时显示5×7的点阵字符,高电平时显示5×10的点阵字符。
(7)指令7:字符发生器RAM地址设置。
(8)指令8:DDRAM地址设置。
(9)指令9:读忙信号和光标地址。
其中,BF为忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平则表示不忙。
(10)指令10:写数据。
(11)指令11:读数据。
常用指令:
0x80+0xdd:dd为地址,这条命令用于设置显示起点坐标
0x0c :开显示,无光标,光标不闪烁 。一般做带键盘输入的才加入光标,如计算器。常用的计量显示不显示光标。
0x06 :写一个数据,地址指针加1,由1602地址表可以看出,实际上就是设置成从左往右写数据而已。
0x38 : 设置显示模式,16x2显示 5x7点阵,8位数据接口。端口不够用时,这个命令也可以换用4位数据接口的。
0x01 :清屏。
表2
二、显示原理
如上图,每个字符由 5X8 点阵组成(也可选用 5X10),想要实现显示,只需如下图:
例:以 5X7 点阵为例, 显示字符 A
0 代表灭,1 代表亮
只需将想要显示的字符的对应位置1,就能显示该字符
LCD1602 固化了字模寄存器,即 CGROM 和 CGRAM,存储了192个常用字符的字模。
字模库:
该表 行是低四位,列是高四位
想要显示哪个字符,只需查表,换算为十六进制,写入LCD1602即可。
例: 想要显示字符 A
需要向 LCD1602 写入 0x41(0100 0001)
三、显示位置
LCD1602 实际有80个字节的DDRAM,
只不过 LCD1602 只有 16X2 个位置,后面很多位置显示不出来,可以使用 指令5“光标或显示移动指令” 使字符慢慢移动到可见的显示范围内,看到字符的移动效果。
所以LCD1602的实际显示位置是,第一行:00 ~ 0F,第二行:40 ~ 4F
注意:
如图, 指令8,D7位恒为 1,
在实际向LCD1602传入数据显示地址时,需要 < 地址+0x80 >
例:向LCD1602的第一行第一列写数据,传入的地址应为: (0x00+0x80)
三、LCD1602 操作
①写操作时序图:
②读操作时序图:
③时序时间参数:
1、忙检测
为什么需要忙检测??
单片机和 LCD1602 的工作速度存在差异,单片机速度快,所以单片机向 LCD1602 传数据时,LCD1602 可能正在处理上一次的数据,处在忙状态,为了防止数据丢失或出错,所以单片机就需要等待,待 LCD1602 处理完时在进行下一次数据的传送。
忙检测代码:
由指令9,需判断 BF 的状态- /****************** 忙检测 *********************
- LCD_Data 与 0x80 进行或运算,判断 bit7 位状态
- 若 LCD_Data 的 bit7 位是 0,则 LCD 不忙
- 若 LCD_Data 的 bit7 位是 1,则 LCD 忙
- ***********************************************/
- void LCD_Check_Busy(){
- uchar temp;
- LCD_Data = 0xff // 十六进制:1111 1111
- LCD_RS = 0; // 0 指令
- LCD_RW = 1; // 1 读
- do{
- LCD_E = 1; // 拉高
- temp = LCD_Data; // 将 LCD 状态保存在 temp 中,用于判忙
- LCD_E = 0; // 负跳变使能
- }while(temp & 0x80); // 结果为 1,LCD 忙,继续循环;结果为 0,LCD 不忙,可以进行后面的操作
- }
复制代码 综合练习:
1)二行1列显示数字6
- #include <REGX52.H> // 加载头文件
- #include "Delay.h"
- sbit RS = P2^6; // 数据命令选择位声明
- sbit RW = P2^5; // 读写选择位声明
- sbit EN = P2^7; // 使能位声明
- #define LCD_DATA P0
- void main()
- {
- // 初始化LCD(必须)
- Delay(15); // 等待LCD稳定
-
- // 初始化指令
- RS = 0; RW = 0;
- LCD_DATA = 0x38; // 8位数据,2行显示,5x8点阵
- EN = 1; Delay(1); EN = 0; Delay(1);
-
- LCD_DATA = 0x0C; // 显示开,光标关
- EN = 1; Delay(1); EN = 0; Delay(1);
-
- LCD_DATA = 0x06; // 地址自动+1
- EN = 1; Delay(1); EN = 0; Delay(1);
-
- LCD_DATA = 0x01; // 清屏
- EN = 1; Delay(1); EN = 0; Delay(2); // 清屏需要更长时间
-
- while(1)
- {
- // 1. 设置显示位置(第二行第一位)
- RS = 0; RW = 0; // 写指令
- LCD_DATA = 0xC0; // 第二行第一位的地址
- EN = 1; Delay(1); EN = 0; Delay(1);
-
- // 2. 显示字符'6'
- RS = 1; RW = 0; // 写数据
- LCD_DATA = '6'; // 字符'6'的ASCII码是0x36
- EN = 1; Delay(1); EN = 0; Delay(1);
-
- Delay(1000); // 延时1秒,避免太快重复写入
- }
- }
复制代码 2) 模块化编码
main.c- #include <REGX52.H>
- #include "LCD1602.h"
- #include "Delay.h"
- void main()
- {
- LCD_Init(); //LCD初始化
- LCD_ShowChar(1,1,'A'); //在1行1列显示字符A
- LCD_ShowString(1,3,"Hello"); //在1行3列显示字符串Hello
- LCD_ShowNum(1,9,66,2); //在1行9列显示数字66,长度为2
- LCD_ShowSignedNum(1,12,-88,2); //在1行12列显示有符号数字-88,长度为2
- LCD_ShowHexNum(2,1,0xA5,2); //在2行1列显示十六进制数字0xA5,长度为2
- LCD_ShowBinNum(2,4,0xA5,8); //在2行4列显示二进制数字0xA5,长度为8
- LCD_ShowChar(2,13,0xDF); //在2行13列显示编码为0xDF的字符
- LCD_ShowChar(2,14,'C'); //在2行14列显示字符C
- while(1)
- {
- }
- }
复制代码 LCD1602.c- #include <REGX52.H>
- //引脚定义
- sbit LCD_RS=P2^6;
- sbit LCD_RW=P2^5;
- sbit LCD_E=P2^7;
- #define LCD_DataPort P0
- /**
- * @brief LCD1602延时函数,12MHz调用可延时1ms
- * @param 无
- * @retval 无
- */
- void LCD_Delay() //@12.000MHz 1ms
- {
- unsigned char i, j;
- i = 2;
- j = 239;
- do
- {
- while (--j);
- } while (--i);
- }
- /**
- * @brief LCD1602写命令
- * @param Command 要写入的命令
- * @retval 无
- */
- void LCD_WriteCommand(unsigned char Command)
- {
- LCD_RS=0;
- LCD_RW=0;
- LCD_DataPort=Command;
- LCD_E=1;
- LCD_Delay();
- LCD_E=0;
- LCD_Delay();
- }
- /**
- * @brief LCD1602写数据
- * @param Data 要写入的数据
- * @retval 无
- */
- void LCD_WriteData(unsigned char Data)
- {
- LCD_RS=1;
- LCD_RW=0;
- LCD_DataPort=Data;
- LCD_E=1;
- LCD_Delay();
- LCD_E=0;
- LCD_Delay();
- }
- /**
- * @brief LCD1602初始化函数
- * @param 无
- * @retval 无
- */
- void LCD_Init(void)
- {
- LCD_WriteCommand(0x38);
- LCD_WriteCommand(0x0C);
- LCD_WriteCommand(0x06);
- LCD_WriteCommand(0x01);
- }
- /**
- * @brief LCD1602设置光标位置
- * @param Line 行位置,范围:1~2
- * @param Column 列位置,范围:1~16
- * @retval 无
- */
- void LCD_SetCursor(unsigned char Line,unsigned char Column)
- {
- if(Line==1)
- {
- LCD_WriteCommand(0x80|(Column-1));
- }
- else
- {
- LCD_WriteCommand(0x80|(Column-1)+0x40);
- }
- }
- /**
- * @brief 在LCD1602指定位置上显示一个字符
- * @param Line 行位置,范围:1~2
- * @param Column 列位置,范围:1~16
- * @param Char 要显示的字符
- * @retval 无
- */
- void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
- {
- LCD_SetCursor(Line,Column);
- LCD_WriteData(Char);
- }
- /**
- * @brief 在LCD1602指定位置开始显示所给字符串
- * @param Line 起始行位置,范围:1~2
- * @param Column 起始列位置,范围:1~16
- * @param String 要显示的字符串
- * @retval 无
- */
- void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)
- {
- unsigned char i;
- LCD_SetCursor(Line,Column);
- for(i=0;String[i]!='\0';i++)
- {
- LCD_WriteData(String[i]);
- }
- }
- /**
- * @brief 返回值=X的Y次方
- */
- int LCD_Pow(int X,int Y)
- {
- unsigned char i;
- int Result=1;
- for(i=0;i<Y;i++)
- {
- Result*=X;
- }
- return Result;
- }
- /**
- * @brief 在LCD1602指定位置开始显示所给数字
- * @param Line 起始行位置,范围:1~2
- * @param Column 起始列位置,范围:1~16
- * @param Number 要显示的数字,范围:0~65535
- * @param Length 要显示数字的长度,范围:1~5
- * @retval 无
- */
- void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
- {
- unsigned char i;
- LCD_SetCursor(Line,Column);
- for(i=Length;i>0;i--)
- {
- LCD_WriteData('0'+Number/LCD_Pow(10,i-1)%10);
- }
- }
- /**
- * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
- * @param Line 起始行位置,范围:1~2
- * @param Column 起始列位置,范围:1~16
- * @param Number 要显示的数字,范围:-32768~32767
- * @param Length 要显示数字的长度,范围:1~5
- * @retval 无
- */
- void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
- {
- unsigned char i;
- unsigned int Number1;
- LCD_SetCursor(Line,Column);
- if(Number>=0)
- {
- LCD_WriteData('+');
- Number1=Number;
- }
- else
- {
- LCD_WriteData('-');
- Number1=-Number;
- }
- for(i=Length;i>0;i--)
- {
- LCD_WriteData('0'+Number1/LCD_Pow(10,i-1)%10);
- }
- }
- /**
- * @brief 在LCD1602指定位置开始以十六进制显示所给数字
- * @param Line 起始行位置,范围:1~2
- * @param Column 起始列位置,范围:1~16
- * @param Number 要显示的数字,范围:0~0xFFFF
- * @param Length 要显示数字的长度,范围:1~4
- * @retval 无
- */
- void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
- {
- unsigned char i;
- unsigned char SingleNumber;
- LCD_SetCursor(Line,Column);
- for(i=Length;i>0;i--)
- {
- SingleNumber=Number/LCD_Pow(16,i-1)%16;
- if(SingleNumber<10)
- {
- LCD_WriteData('0'+SingleNumber);
- }
- else
- {
- LCD_WriteData('A'+SingleNumber-10);
- }
- }
- }
- /**
- * @brief 在LCD1602指定位置开始以二进制显示所给数字
- * @param Line 起始行位置,范围:1~2
- * @param Column 起始列位置,范围:1~16
- * @param Number 要显示的数字,范围:0~1111 1111 1111 1111
- * @param Length 要显示数字的长度,范围:1~16
- * @retval 无
- */
- void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
- {
- unsigned char i;
- LCD_SetCursor(Line,Column);
- for(i=Length;i>0;i--)
- {
- LCD_WriteData('0'+Number/LCD_Pow(2,i-1)%2);
- }
- }
复制代码 LCD1602.h- #ifndef __LCD1602_H__
- #define __LCD1602_H__
- void LCD_Init(void);
- void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
- void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
- void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
- void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
- void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
- void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
- #endif
复制代码 Delay.c- void Delay(unsigned int xms)
- {
- unsigned char i, j;
- while(xms--)
- {
- i = 2;
- j = 239;
- do
- {
- while (--j);
- } while (--i);
- }
- }
复制代码 Delay.h- #ifndef __DELAY_H__
- #define __DELAY_H__
- void Delay(unsigned int xms);
- #endif
复制代码
3)实现正计时显示
只需要修改 main.c ,其余部分同上- #include <REGX52.H>
- #include "LCD1602.h"
- #include "Delay.h"
- unsigned int hour = 0;
- unsigned int minute = 0;
- unsigned int second = 0;
- /**
- * 更新时间函数
- */
- void UpdateTime()
- {
- second++;
-
- if(second >= 60)
- {
- second = 0;
- minute++;
- }
-
- if(minute >= 60)
- {
- minute = 0;
- hour++;
- }
-
- if(hour >= 24)
- {
- hour = 0;
- }
- }
- /**
- * 主函数
- */
- void main()
- {
- // LCD初始化
- LCD_Init();
-
- // 显示初始界面
- LCD_ShowString(1, 1, "F_timing:");
-
- while(1)
- {
- // 显示小时
- LCD_ShowNum(2, 1, hour, 2);
- // 显示分钟
- LCD_ShowNum(2, 4, minute, 2);
- // 显示秒数
- LCD_ShowNum(2, 7, second, 2);
-
- // 显示冒号
- LCD_ShowChar(2, 3, ':');
- LCD_ShowChar(2, 6, ':');
-
- // 延时1秒
- Delay(1000);
-
- // 更新时间
- UpdateTime();
- }
- }
复制代码
附加内容:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |