LCD1602的显示原理
学单片机的小朋友应该都见过这个显示器--LCD1602,这是最古老最简单的显示屏,上世纪80年代的产物。
它显示的图案如下所示
显示部分放大就是下面这样 ,像素就是一个个小方块,相当于描点法绘图了。
如果把所有像素全部显示出来就是下面这样,全部是小黑点
全部点亮的像素点如下所示,16列,2行,这就是1602名字的来历
每个显示区域又可以细分为35个像素,7行5列
整个屏幕就是35*16*2 = 1120个像素
要想搞清楚这1120个像素的控制,就得先明白一个像素是这么控制的。显示屏的最下面是一层LED背光板,
只要通上电就会发光
在背光板上铺上一层液晶,每一小块的液晶就可以控制一个像素点
当不给液晶施加电压,液晶就是透明的,给某一块液晶施加电压后,就不透光了,看到的就是一个小黑点,常见的LCD背光有白光、蓝光、绿光,所以大家一般看到的要么是黑点,要么是背光。控制像素点就是控制他所对应的液晶的电压高低。
数码管只有8段,8个IO口就够了,可以用单片机直接控制,但是LCD1602有1120个像素点,1120个引脚的单片机我还没遇到,怎么办?这时候显示芯片HD44780U可以出场了。44780有80个引脚
单片机把想要显示的东西发给HD44780U,HD44780U再来控制显示屏就可以
所以这个HD44780U就有点像电脑的显卡了。
LCD1602液晶屏的背面长这个样子,首先映入眼帘的就是那两个大黑点,大黑点里面封装的就是芯片,大的那个是HD44780U,小一点的那个黑点是HD44100H,他的作用后面会讲到。关于芯片的封装,大家可能都觉得芯片 就应该是哪种方形的,有引脚的哪种,其实还有一种COB的封装,就像狗皮膏药一样黏在PCB上,这种封装便宜啊。
LCD1602一共有16个引脚,其中数据引脚又8个,控制这8个引脚的数据,就能控制液晶屏的显示内容。因为每个显示区域就35个像素,描点法也只能描出简单的图案,比如数字,字面,符号等。
比如大些字母A,ASCII码表是0100 0001,对应的数据引脚电平就是:
0 1 0 0 0 0 0 1
低 高 低 低 低 低 低 高
但是这个A在哪个区域显示呢?HD44780U里面给每个显示区域都规定的地址,这个地址就存在芯片内部的DRAM里面,
这个DRAM就相当于显卡的显存了,只是大小只有80个字节,可以同时控制80个字符
LCD1602最多也就能显示32个字符,HD44780U为什么能控制80个字符,因为他是通用的芯片,不是为LCD1602定制的,多的部分可以空着不用。
后面的红色部分就是多余的地址,控制不用
先发送显存的地址,再发送需要显示的内容,这样数据就会被保存在相应的显存里面
比如让液晶屏的第一块显示区域显示大些字母A,就先发送第一块显示区域的地址0x00,再发送A的ASCII码0100 0001,怎么显示那就是显示芯片HD44780U的事情了。
HD44780U的芯片管脚分布如下那些SEG和COM引脚就是控制引脚,是不是似曾相识,数码管的SEG引脚和COM引脚,原理可以说是差不多的。
COM引脚控制行,SEG引脚控制列,如果要显示A,那就是在相应的交叉点输出不同的电压就行了
因为不能同时控制所有行列的电压,只能用扫描的方式,一行一行的控制,是不是也似曾相识,多为数码管的动态显示也是这样的。
扫描第一行,并输出对应的电压
扫描第二行并输出对应的电压
扫描第三行并输出对应的电压
扫描第四行并输出对应的电压
扫描第五行并输出对应的电压
扫描第六行,并输出对应的电压
扫描第七行并输出对应的电压
扫描快一点,根根人眼的视觉残留,就能看到完整的A了
HD44780U的SEG引脚只有40个,5个SEG脚才能控制一列显示区,40个SEG脚一共也只能控制8列显示区啊,LCD1602可是有16列显示区的,那另外的8列的显示区怎么控制,而且前面也说了,HD44780U能控制80个字符,现在怎么才16个。对HD44780U的显存DRAM是可以控制80个字符,但是引脚不够啊,这个时候该HD44100H出场了。
HD44100H就是那个小一点的黑点
它正好也有40个SEG引脚
将HD44780U和HD44100H拼在一起不就好了
加4个HD44100H,换一个能显示80个字符的液晶显示屏,就能显示80个字符了,HD44780U里面的DRAM就只有80个地址,只能显示80个字符。
LCD1602相关的寄存器以及电路
1、认识LCD
LCD == Liquid Crystal Display ==液晶显示器
LCD1602表示一种液晶屏,能同时显示2行16列,共32个字符,这个字符可以是字母,数字,符号等
LCD结构上主要由3部分组成:
液晶:液态晶体,液晶本身不发光,但他在通电的时候可以让光线透过去,不通电时光线透不过去;
光源:这个光源才是点亮LCD的关键;
偏光片:又上下两片,液晶就在上下偏光片之间,改变偏光片的角度就可以让光线在特定的地方透过
上下两片偏光片有点像两个石子交叉的百叶窗,自己去脑补一下。
2、LCD1602的管脚以及管脚功能
管脚序号 |
管脚符号 |
管脚说明 |
1 |
VSS |
电源地, |
2 |
VDD |
电源正极,是LCD1602驱动系统的电源 |
3 |
VO |
液晶显示对比度调整管脚,接正电源是对比度最低,接地时对比度最高,但是对比度太高,就容易看到一些不该看到的东西,俗称“鬼影”,一般在这个脚通过电阻再接地。 |
4 |
RS |
Register Select的缩写,字面意思就是寄存器选择;当RS=1的时候,通讯的是数据;当RS=0的时候,通讯的是命令。 |
5 |
R/W |
读,写选择,当R/W = 1的时候表示读LCD里面的数据,R/W = 0的时候表示往LCD里面写数据。 |
6 |
E |
使能信号,当E=1的时候读取信息,当E由1变为0的时候,液晶模块开始执行命令;要配合控制时序来用。 |
7 |
D0 |
数据第0位,D0--D7是双向的数据线,对LCD模块进行读写。 |
8 |
D1 |
数据第1位 |
9 |
D2 |
数据第2位 |
10 |
D3 |
数据第3位 |
11 |
D4 |
数据第4位 |
12 |
D5 |
数据第5位 |
13 |
D6 |
数据第6位 |
14 |
D7 |
数据第7位 |
15 |
BLA |
背光源正极,可以理解为那个背光LED的正极电源,可以直接接VDD |
16 |
BLK |
背光源负极,可以理解为那个背光LED的负极电源,可以直接接VSS |
3、LCD1602 的RAM地址映射
LCD1602能同时显示2行16列,一共32个位置,每个位置都对应着一个地址,每个地址里面可以促一个字节的字符,需要在哪个位置显示某种字符,就在他对应的地址上写入某种字符。
下图就是这32个显示位置所对应的地址,这个地址也就是DDRAM地址;
其实LCD1602的DDRAM一共有80个地址,依然是两行,每行40个地址,但是LCD1602一行只能同时显示16个地址的数据,剩下的24个在后面隐藏着。怎么办,只能用光标移动的方法把隐藏的部分显示出来。
4、LCD1602的显示设置
比如你想在第1行,第3列的位置显示个字符A,该怎么处理?
首先确定位置,第1行第3列的地址是02,转换为2进制就是0000 0010,但是LCD1602有自己的规矩,就是写地址的时候,最高位必须是1,也就是说写进LCD的地址应该为1000 0010。
然后确定字符,LCD1602模块里面有个字符产生器,简称CGROM(Character Generator ROM)里面存着一些常用的字模,你只需要根据地址来查询就可以了,比如字符A的地址就是0x41。
5、LCD1602的命令列表
6、单片机控制LCD1602电路
LCD1602液晶静态显示
程序实例1:
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
#define DataPort P0
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
判忙函数
------------------------------------------------*/
bit LCD_Check_Busy(void)
{
DataPort= 0xFF;
RS_CLR;
RW_SET;
EN_CLR;
_nop_();
EN_SET;
return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
while(LCD_Check_Busy()); //忙则等待
RS_CLR;
RW_CLR;
EN_SET;
DataPort= com;
_nop_();
EN_CLR;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
while(LCD_Check_Busy()); //忙则等待
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x); //表示第一行
}
else
{
LCD_Write_Com(0xC0 + x); //表示第二行
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*------------------------------------------------
初始化函数
------------------------------------------------*/
void LCD_Init(void)
{
LCD_Write_Com(0x38); /*显示模式设置*/
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); /*显示关闭*/
LCD_Write_Com(0x01); /*显示清屏*/
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
LCD_Init();
LCD_Clear();//清屏
while (1)
{
LCD_Write_Char(7,0,'o');
LCD_Write_Char(8,0,'k');
LCD_Write_String(1,1,"hello world!");
while(1);
}
}
LCD1602液晶动态显示
程序实例2:
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
#define DataPort P0
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
判忙函数
------------------------------------------------*/
bit LCD_Check_Busy(void)
{
DataPort= 0xFF;
RS_CLR;
RW_SET;
EN_CLR;
_nop_();
EN_SET;
return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
while(LCD_Check_Busy()); //忙则等待
RS_CLR;
RW_CLR;
EN_SET;
DataPort= com;
_nop_();
EN_CLR;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
while(LCD_Check_Busy()); //忙则等待
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*------------------------------------------------
初始化函数
------------------------------------------------*/
void LCD_Init(void)
{
LCD_Write_Com(0x38); /*显示模式设置*/
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); /*显示关闭*/
LCD_Write_Com(0x01); /*显示清屏*/
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
unsigned char i;
unsigned char *p;
LCD_Init();
while (1)
{
i = 1;
p = "hello world!";
LCD_Clear();
LCD_Write_String(2,0,"Welcome to");
DelayMs(250);
while (*p)
{
LCD_Write_Char(i,1,*p);
i ++;
p ++;
DelayMs(250);
}
DelayMs(250);
}
}
LCD1602液晶滚动显示
程序实例3:
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
#define DataPort P0
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
判忙函数
------------------------------------------------*/
bit LCD_Check_Busy(void)
{
DataPort= 0xFF;
RS_CLR;
RW_SET;
EN_CLR;
_nop_();
EN_SET;
return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
while(LCD_Check_Busy()); //忙则等待
RS_CLR;
RW_CLR;
EN_SET;
DataPort= com;
_nop_();
EN_CLR;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
while(LCD_Check_Busy()); //忙则等待
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x); //表示第一行
}
else
{
LCD_Write_Com(0xC0 + x); //表示第二行
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*------------------------------------------------
初始化函数
------------------------------------------------*/
void LCD_Init(void)
{
LCD_Write_Com(0x38); /*显示模式设置*/
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); /*显示关闭*/
LCD_Write_Com(0x01); /*显示清屏*/
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
LCD_Init();
LCD_Clear();//清屏
LCD_Write_Char(7,0,'o');
LCD_Write_Char(8,0,'k');
LCD_Write_String(1,1,"hello world!");
while (1)
{
DelayMs(200);
LCD_Write_Com(0x18);//左平移画面 0x1C 是右平移
}
}
LCD1602移动显示
程序实例4:
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
#define DataPort P0
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
判忙函数
------------------------------------------------*/
bit LCD_Check_Busy(void)
{
DataPort= 0xFF;
RS_CLR;
RW_SET;
EN_CLR;
_nop_();
EN_SET;
return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
while(LCD_Check_Busy()); //忙则等待
RS_CLR;
RW_CLR;
EN_SET;
DataPort= com;
_nop_();
EN_CLR;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
while(LCD_Check_Busy()); //忙则等待
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*------------------------------------------------
初始化函数
------------------------------------------------*/
void LCD_Init(void)
{
LCD_Write_Com(0x38); /*显示模式设置*/
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); /*显示关闭*/
LCD_Write_Com(0x01); /*显示清屏*/
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
unsigned char i;
unsigned char *pa,*pb;
LCD_Init();
LCD_Write_Com(0x0f);//光标开,光标闪烁开
while (1)
{
pa = "Welcome to";
pb = "hello world!";
LCD_Clear();
LCD_Write_String(2,0,pa); //显示2行信息
LCD_Write_String(1,1,pb);
for(i=0;i<8;i++) //延时2s左右
DelayMs(250);
LCD_Clear(); //清屏显示其他信息
i = 2;
while (*pa) //循环输入方式显示2行信息
{
LCD_Write_Char(i,0,*pa);
i ++;
pa ++;
DelayMs(250);
}
i=1;
while (*pb)
{
LCD_Write_Char(i,1,*pb);
i ++;
pb ++;
DelayMs(250);
}
DelayMs(250);
}
}
LCD1602液晶按键输入显示
程序实例5:文章来源:https://uudwc.com/A/DNjwr
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
#define DataPort P0
#define KeyPort P1
unsigned char code show_code[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};//转换成液晶显示的字符
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
判忙函数
------------------------------------------------*/
bit LCD_Check_Busy(void)
{
DataPort= 0xFF;
RS_CLR;
RW_SET;
EN_CLR;
_nop_();
EN_SET;
return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
while(LCD_Check_Busy()); //忙则等待
RS_CLR;
RW_CLR;
EN_SET;
DataPort= com;
_nop_();
EN_CLR;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
while(LCD_Check_Busy()); //忙则等待
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*------------------------------------------------
初始化函数
------------------------------------------------*/
void LCD_Init(void)
{
LCD_Write_Com(0x38); /*显示模式设置*/
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); /*显示关闭*/
LCD_Write_Com(0x01); /*显示清屏*/
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void) //键盘扫描函数,使用行列反转扫描法
{
unsigned char cord_h,cord_l;//行列值中间变量
KeyPort=0x0f; //行线输出全为0
cord_h=KeyPort&0x0f; //读入列线值
if(cord_h!=0x0f) //先检测有无按键按下
{
DelayMs(10); //去抖
if((KeyPort&0x0f)!=0x0f)
{
cord_h=KeyPort&0x0f; //读入列线值
KeyPort=cord_h|0xf0; //输出当前列线值
cord_l=KeyPort&0xf0; //读入行线值
while((KeyPort&0xf0)!=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}
}
return(0xff); //返回该值
}
/*------------------------------------------------
按键值处理函数,返回扫键值
------------------------------------------------*/
unsigned char KeyPro(void)
{
switch(KeyScan())
{
case 0x7e:return 0;break;//0 按下相应的键显示相对应的码值
case 0x7d:return 1;break;//1
case 0x7b:return 2;break;//2
case 0x77:return 3;break;//3
case 0xbe:return 4;break;//4
case 0xbd:return 5;break;//5
case 0xbb:return 6;break;//6
case 0xb7:return 7;break;//7
case 0xde:return 8;break;//8
case 0xdd:return 9;break;//9
case 0xdb:return 10;break;//a
case 0xd7:return 11;break;//b
case 0xee:return 12;break;//c
case 0xed:return 13;break;//d
case 0xeb:return 14;break;//e
case 0xe7:return 15;break;//f
default:return 0xff;break;
}
}
/*------------------------------------------------
主函数
------------------------------------------------*/
void main(void)
{
unsigned char i,j,num;
LCD_Init();
LCD_Write_Com(0x0F);//光标开,光标闪烁开
LCD_Write_String(0,0,"Press the key !");
while (1)
{
num=KeyPro();
if(num!=0xff)
{
if((i==0)&&(j==0))//回到第一个字符时清屏
LCD_Clear();//清屏
LCD_Write_Char(0+i,0+j,show_code[num]);//依次显示输入字符
i++;
if(i==16)//如果第一行显示满,转到第二行
{
i=0;j++;
if(j==2)//如果2行都显示满,清屏后重新从第一行显示
{
j=0;
}
}
}
}
}
文章来源地址https://uudwc.com/A/DNjwr