前言
LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符。LCD1602主要是做调试用的,它可以帮助我们显示程序的执行结果,让程序在单片机外部可视化
规格参数
LCD1602主要参数:
- 显示字符:16×2个字符
- 工作电压:4.5~5V
- 工作电流:2.0mA
- 工作温度:-20°C~70°C
- 模块最佳工作电压:5.0V
- 单个字符尺寸2.95×4.35(W×Hmm)
- 引脚:16脚
LCD1602是一种点阵型的显示模块,总共有2行16列,也就是可以同时显示32个字符,每个字符都是由5*7的液晶矩阵显示,这样算下来总共有2*16*5*7等于1120颗点阵组成,然而只需要14个引脚即可控制它在任何位置显示任何字符:
第1脚:GND(接地)
第2脚:VSS(接电源+5v)
第3脚:VO(对比度调节电压)接高电平1时对比度最低,低电平0时对比度最高,可用电位器调节
第4脚:RS(数据/命令选择)接高电平1时选择数据寄存器,低电平0时选择指令寄存器
第5脚:R/W(读写选择)接高电平1时进行读操作,低电平0时进行写操作
第6脚:E(使能引脚)使能,接高电平1时读取信息,下降沿执行命令
第7~14脚:D0~D7(双向数据端)可以收发数据,是主要控制的引脚
15,16脚:分别为背光的正负极
内部构造及其原理
LCD1602内置了CGRAM和CGROM字模库,里面包含了ASCLL码表的所有字符和一些特殊字符的字模数据,字模数据就是在5*7的点阵里点亮哪几个点可以显示其相应的字符,再由引脚控制输出在哪个位置上,即可完成显示丰富多样的内容的任务。这个过程很像数码管的显示,字模库就是数码管段选的数组,外部引脚控制输出在哪个位置就是数码管的位选,当然其中详细的流程要比数码管复杂得多。
还有需要注意的一点是:我们想要显示的字符实际是先写在DDRAM(数据显示区)里的,再把DDRAM存储的数据对照字模库输出到屏幕上,而这个DDRAM的矩阵是40*2的,屏幕只有16*2,也就是说数据显示区可以存储两个半屏幕的数据,但是只能显示前16列,为什么要这样设计呢?这是因为芯片里有一个虚拟光标,可以通过指令来控制光标的移动,从而控制屏幕的移动,并且当移动到末尾又会重新回到起始位置,这样就可以实现屏幕循环滚动的效果。
关于时序,由于我们在使用过程中几乎不会用到读数据的操作,所以这里只介绍写数据的指令:
对照上面的引脚定义来分析此图,首先不难看出共有两种时序信号,分别是写指令和写数据(其实是四种,还有读指令和读数据)写指令是用来控制操作的,例如光标移动等,写数据就写入要显示的字符。当RS配置为为高电平1,R/W为低电平0时即为写数据模式,一旦检测到使能引脚E的上升沿跳变便开始写入D0~D7对应的数据,这样就完成了一次写数据,再不断循环此过程就完成了数据的写入,总的来说这个时序逻辑还是很简单易懂的。
至于指令部分就不一一介绍了,可以参照下面的表格:
函数的功能都是遵照表格上的指令来实现的,其实这款显示屏的功能还是相当丰富的!
程序源码
程序部分不必过分深究,只需要知道有哪些函数,分别是什么功能,了解函数的参数类型,并且会用即可,一般都是封装成模块直接拿来调用的。
#include <REGX52.H>
//引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0
void LCD_Delay() //@12.000MHz 1ms
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
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();
}
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();
}
void LCD_Init(void)
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else
{
LCD_WriteCommand(0x80|(Column-1)+0x40);
}
}
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
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]);
}
}
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
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);
}
}
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);
}
}
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);
}
}
}
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);
}
}
下面是头文件和对应函数的说明:
#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);
//LCD_Init() LCD1602初始化
//LCD_ShowChar() 显示单字符
//LCD_ShowString() 显示字符串
//LCD_ShowNum() 显示十进制数
//LCD_ShowSignedNum 显示有符号数
//LCD_ShowHexNum() 显示十六进制数
//LCD_ShowBinNum() 显示二进制数
#endif
比如我们再在1行1列显示字符A,在1行3列显示字符串Hello,在1行9列显示数字66,在1行12列显示有符号数字-88,在2行1列显示十六进制数字0xA5,在2行4列显示二进制数字0xA5,在2行13列显示编码为0xDF的字符,在2行14列显示摄氏度字符,就可以这样写:
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
void main()
{
LCD_Init();
LCD_ShowChar(1,1,'A');
LCD_ShowString(1,3,"Hello");
LCD_ShowNum(1,9,66,2);
LCD_ShowSignedNum(1,12,-88,2);
LCD_ShowHexNum(2,1,0xA5,2);
LCD_ShowBinNum(2,4,0xA5,8);
LCD_ShowChar(2,13,0xDF);
LCD_ShowChar(2,14,'C');
while(1){}
}
显示效果:
总结
LCD1602是学习51单片机较好的工具,也是初步了解寄存器和时序较好的选择,结合其他外设可以开发简单的密码锁,温度计等有趣的小项目。