1602有8位的数据口和4位的数据口,今天我们用到的是8位的数据口。废话不多说,直接上原理图
8位数据口一定要加上拉电阻,要不然驱动不了。
写数据和写命令的时序
清屏指令
开显示指令
显示字符的地址 第一行0x80+0x00开始,第二行0x80+0x40开始
功能配置
上源代码
1.main函数
/**************************************************************************************
* LCD1602液晶显示实验 *
***************************************************************************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include "lcd.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
u8 Disp[]="ROC_CHAO"; //定义数组 为字符串类型
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main(void)
{
u8 i;
LcdInit(); //对屏幕进行初始化
LcdWriteCom(0x80+0x45);//从第二行第五个字符开始显示
for(i=0;i<8;i++)
{
LcdWriteData(Disp[i]);
}
while(1);
}
lcd.h
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS
/**********************************
包含头文件
**********************************/
#include<reg52.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c); //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/
void LcdWriteData(uchar dat) ;
/*LCD1602初始化子程序*/
void LcdInit();
#endif
ldc.c
#include "lcd.h"
/*******************************************************************************
* 函 数 名 : Lcd1602_Delay1ms
* 函数功能 : 延时函数,延时1ms
* 输 入 : c
* 输 出 : 无
* 说 名 : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/
void Lcd1602_Delay1ms(uint c) //误差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/*******************************************************************************
* 函 数 名 : LcdWriteCom
* 函数功能 : 向LCD写入一个字节的命令
* 输 入 : com
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 0; //选择写入命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = com << 4; //发送低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名 : LcdWriteData
* 函数功能 : 向LCD写入一个字节的数据
* 输 入 : dat
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择输入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择写入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = dat << 4; //写入低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名 : LcdInit()
* 函数功能 : 初始化LCD屏
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //开显示 功能设定指令 配置数据总线为7位 显示两行 5*7点阵/字符
LcdWriteCom(0x0f); //开显示显示光标 光标闪烁
LcdWriteCom(0x06); //写一个指针加1 写入字符后光标右移动,屏幕不移动
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点 第一行第一个字符的地址为80H
}
#else
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x32); //将8位总线转为4位总线
LcdWriteCom(0x28); //在四位线下的初始化
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#endif
实验现象
二,自定义字符
对于自带字符的显示,只要把ASCLL码填入lcd中显示就可以了
要显示我们自定义的字符,就要用到LCD中的CGRAM存储器(character generate RAM),而我们之前用的显示自带的字符用到的是DDRAM,两个是不同的。看液晶手册我们知道,CGRAM的容量是64个字节,而一个字符是8个字节,所以一共能显示8个自定义的字符。内部常用字符的显示是从0x20开始的,0x00 ~ 0x0F是专门留给自定义字符显示用的,0x00~0x07和0x80~0x0F显示的内容是一样的,也就是说0x00=0x08,0x01=0x09,以此类推。CGRAM共128个位,地址是0x40~0x7F,128/8=16正好对应的是0x00~0x0F共16个,刚才说了,0x00与0x08对应,0x01与0x09对应,共16个,这并不矛盾!说了这么多,那么怎样显示一个自定义字符呢?
首先我们要清楚LCD1602显示字符的点阵大小,眼力好的可以看出来,LCD1602一个显示字符的位置是5*8的点阵,也就是说它所能显示的点阵图形的大小是5*8的!要显示一个自定义的字符,首先我们要知道所显示自定义字符的点阵数据,也就是在一个5*8的点阵上那个点是黑的(将该点点黑,就是高电平----1),哪个点是白的(该点不显示,为低电平----0),但是我们送入到LCD中的是ASCII码,它是8位的数据,而一个显示字符的点阵大小只是5*8的,显然不够,显示的办法是8*8点阵的前三列不用,也就是不显示,我们只用后面的5列来显示。
然后设定我们是要定义第几个自定义字符,前面已经介绍了,LCD1602最多显示8个自定义字符;然后要规定在液晶的什么位置显示自定义字符,看过数据手册我们知道,第一行第一个位置的地址是0x80,第二行一个位置的地址是0xC0。最后就是要显示我们定义的第几个字符其对应CGRAM地址的关系式是:
0x00:第一个(0x40) 0x01:第二个(0x48)
0x02:第三个(0x50) 0x03:第四个(0x58)
0x04:第五个(0x60) 0x05:第六个(0x68)
0x06:第七个(0x70) 0x07:第八个(0x78)
自定义有两种方式,1.利用取模软件2.自己画5*8点阵,然后自己算
现在介绍取模软件的使用方法
她长这个样子
8*8是因为一个bit8位 每一行都是一个bit 因为1602的是5*8所以前三列不用,不要点亮,这样子只是为了好取数
下面是生成好的自定义字符代码数组,供参考:
uchar code table1[]={0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};//字符℃
uchar code table4[]={0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00};//字符一
uchar code table5[]={0x00,0x0E,0x00,0x00,0x00,0x1F,0x00,0x00};//字符二
uchar code table6[]={0x0E,0x00,0x00,0x0E,0x00,0x00,0x1F,0x00};//字符三
uchar code table7[]={0x1F,0x11,0x15,0x15,0x1F,0x11,0x1F,0x00};//字符四
uchar code table8[]={0x1E,0x08,0x08,0x1E,0x0A,0x0A,0x1F,0x00};//字符五
uchar code table9[]={0x08,0x1F,0x12,0x0F,0x0A,0x1F,0x02,0x00};//字符年
uchar code table10[]={0x0F,0x09,0x0F,0x09,0x0F,0x09,0x13,0x00};//字符月
uchar code table11[]={0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00};//字符日
上主函数代码,其他文件不用改变
/**************************************************************************************
* LCD1602液晶显示实验 *
***************************************************************************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include "lcd.h"
#include "intrins.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
void delay1s(void);
u8 Disp[]="ROC_CHAO "; //定义数组 为字符串类型
unsigned char table[]={0x08,0x1F,0x12,0x0F,0x0A,0x1F,0x02,0x00};//年
unsigned char table1[]={0x0F,0x09,0x0F,0x09,0x0F,0x09,0x13,0x00};//月
unsigned char table2[]={0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00};//日
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main(void)
{
u8 i;
LcdInit(); //对屏幕进行初始化
LcdWriteCom(0x40);//自定义第一个字符 年
for(i=0;i<8;i++)
{
LcdWriteData(table[i]);
}
LcdWriteCom(0x48);//自定义第二个字符 月
for(i=0;i<8;i++)
{
LcdWriteData(table1[i]);
}
LcdWriteCom(0x50);//自定义第二个字符 日
for(i=0;i<8;i++)
{
LcdWriteData(table2[i]);
}
LcdWriteCom(0x80);
LcdWriteData(0x00); //第一个字符显示在 第一排第一个
LcdWriteCom(0x80+0x02);
LcdWriteData(0x01); //第二个字符显示在 第一排第三个
LcdWriteCom(0x80+0x04);
LcdWriteData(0x02); //第三个字符显示在 第一排第五个
while(1);
}
显示效果如下图:
非常完美