51单片机应用开发---LCD1602显示应用
实现目标
1、了解LCD1602液晶屏;
2、掌握驱动程序的编写;
3. 具体目标:在屏幕上显示字符。
一、LCD1206概述
1.1 定义
LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符(如汉字)。
显示容量: 16—表示每行显示16个字符;02—表示显示两行。
1.2 实物与管脚
(1)实物
(2)管脚定义
1.3 内部结构框图
屏幕: 16*2
字模库:类似于数码管的数码表
DDRAM (data display RAM):前16列与屏幕一一对应的
AC: address counter
显示流程:在数据显示区写入相应的值(ASCII码),之后通过字模库找到对应的字符,将其显示到屏幕上。
1.显示原理
如上图,每个字符由 5X8 点阵组成(也可选用 5X10),想要实现显示,只需如下图:
例:以 5X7 点阵为例, 显示字符 A
0 代表灭,1 代表亮
只需将想要显示的字符的对应位置1,就能显示该字符LCD1602 固化了字模寄存器,即 CGROM 和 CGRAM,存储了192个常用字符的字模。
(1) 在哪个位置显示?
LCD1602 实际有80个字节的DDRAM(数据显示区),只不过 LCD1602 只有 16X2 个位置,后面很多位置显示不出来,可以使用 指令5 “光标或显示移动指令” 使字符慢慢移动到可见的显示范围内,看到字符的移动效果。
LCD1602可以显示16*2个字符且通过D0-D7八个引脚传输数据八位数据,每一个显示的位置都对应上图的一个地址。例如我想在第一行的第三个位置显示,那么就可以锁定上表中的“02”,换算成二进制就是0000 0010。听上去很简单,但是LCD1602有个特点就是写入显示地址时要求最高位 D7 恒定为高电平。所以我们想显示真实的地址应该为1000 0010。
(2)具体显示什么?
在LCD1602的手册中给出了这样一个表格(CGRAM+CGROM(字模库)),我们想显示表格中的字符只需获取某个字符的高位和低位即可。 例如:要显示A,则索引码为0100 0001 该表 行是低四位,列是高四位。十六进制为0X41,刚好对应 ‘A’ 的ASCLL码 65 .
1.4 LCD1602指令集
常用指令:
0x80+0xdd:dd为地址,这条命令用于设置显示起点坐标
0x0c :开显示,无光标,光标不闪烁 。一般做带键盘输入的才加入光标,如计算器。常用的计量显示不显示光标。
0x06 :写一个数据,地址指针加1,由1602地址表可以看出,实际上就是设置成从左往右写数据而已。
0x38 : 设置显示模式,16x2显示 5x7点阵,8位数据接口。端口不够用时,这个命令也可以换用4位数据接口的。
0x01 :清屏。
1.5 LCD1602操作流程
(1) 初始化
发送指令0x38 //八位数据接口,两行显示,5*7点阵
发送指令0x0C //显示开,光标关,闪烁关
发送指令0x06 //数据读写操作后,光标自动加一,画面不动
发送指令0x01 //清屏
(2) 显示字符
发送指令 0x80 | AC //设置光标位置(之所以要将AC与0x80进行或运算,是因为DDRAM地址设置指令的DB7为1,故发送指令的最高位要为1)
【将光标设置在一行一列输入1000 0000,1为设置,剩下为00H】LCD_WriteCommand(0x80|(Column-1));
【将光标设置在二行一列输入1100 0000,1为设置,剩下为40H(二进制100 0000转化为十进制为64,十六进制40转化为十进制也是64)】LCD_WriteCommand(0x80|(Column-1)+0x40);
发送数据 //发送要显示的字符数据
1.6 时序结构
写数据/指令(对于LCD来说,一般不需要进行读操作)
写数据:先将RS置1,再将R/W置0,之后将数据写入对应I0口,将 E 置1,则此时数据有效;写数据完成后;E置0 。写指令同理
驱动编写:
(1) 管脚定义
#include "LCD1602.h"
#include <REGX52.H>
//管脚定义
sbit LCD_RW=P2^5;
sbit LCD_RS=P2^6;
sbit LCD_E =P2^7;
#define LCD_DataPort P0
(2)写命令
/**
* @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();
}
(3)写数据
/**
* @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();
}
(4)LCD1602初始化
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init(void)
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
(5)设置光标位置
/**
* @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);
}
}
(6)显示一个字符
/**
* @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);
}
(7)显示字符串
/**
* @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]);
}
}
(8)X的Y次方
/**
* @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;
}
(9)显示数字
/**
* @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);
}
}
(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;//int型-32768~32767
}
for(i=Length;i>0;i--)
{
LCD_WriteData('0'+Number1/LCD_Pow(10,i-1)%10);
}
}
二、原理图设计
三、程序设计
3.1 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
3.2 LCD1602.c 文件
将前面的各项驱动程序模块添加到LCD1602.c文件中。
3.3 测试程序
#include <REGX52.H>
#include "LCD1602.h"
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
LCD_Init();
LCD_ShowString(1,16,"day day up!");
while(1)
{//指令集0x18:画面向左平移一个字符位,但光标不动。移屏
LCD_WriteCommand(0x18);
Delay500ms();
}
}