1.Proteus 8 仿真器
2.keil 5 编辑器
注意:proteus8 中没有lcd1602本篇仿真采用LM016L代替
引脚 | 说明 |
VSS | 地 |
VDD | 电源 (+5V) |
V0 | 对比度调整,接正电源对比度最弱,接地对比度最高(对比度高时会产生"鬼影",使用时可以通过一个10K的电位器调整对比度) |
RS | 寄存器选择,高电平时为数据寄存器,低电平时为指令寄存器 |
R/W?? | R/W为读写信号线,高电平时进行读操作,低电平时进行写操作 |
E | 使能端,下降沿使能 |
DB0~DB7 | 数据总线 |
BLA | 背光电源正极 |
BLK | 背光电源负极 |
RS | R/W | 说明 |
0 | 0 | 写入指令寄存器 |
0 | 1 | 读取指令寄存器 |
1 | 0 | 写入数据寄存器 |
1 | 1 | 读取数据寄存器 |
写操作时序图
写指令寄存器
RS低电平,R/W低电平,E低电平,写入DB0~DB7并行数据---(>40ns)---E高电平---(>150ns)---E低电平---(>40ns)---下一段写入
写数据寄存器
RS高电平,R/W低电平,E低电平,写入DB0~DB7并行数据---(>40ns)---E高电平---(>150ns)---E低电平---(>40ns)---下一段写入
读操作时序图
读指令寄存器
RS低电平,R/W高电平,E低电平---(>30ns)---E高电平---(>15ns)---读取DB0~DB7并行数据---(>150ns)---E低电平---(>40ns)---下一段读取
读数据寄存器
RS高电平,R/W高电平,E低电平---(>30ns)---E高电平---(>15ns)---读取DB0~DB7并行数据---(>150ns)---E低电平---(>40ns)---下一段读取
LCD1602的忙闲可以通过读取忙标志位(BF)来判断。忙标志位(BF)是一个8位寄存器,用于指示LCD1602是否处于忙状态。如果BF寄存器的第7位为1,则表示LCD1602处于忙状态,此时无法进行读写操作;如果BF寄存器的第7位为0,则表示LCD1602处于空闲状态,可以进行读写操作。
可以通过以下步骤来读取忙标志位(BF):
需要注意的是,在读取忙标志位(BF)之前,需要先向LCD1602发送一个写命令(0x80)或写数据(0x00)的指令,以确保LCD1602处于准备状态。同时,在读取忙标志位(BF)之后,需要根据LCD1602的状态来决定是否需要等待一段时间再执行后续操作。
用于清除DDRAM和AC(内存和光标寄存器)的数值,用于将1602当前屏幕的显示清空(无显示)
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
将1602屏幕的光标(当前字符显示点)回归原点(可以预先设置,通常来说是左上角)
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * |
用于设置1602的光标和画面移动方式。其中,I/D=1:数据读、写操作后,AC(光标寄存器)自动加1;I/D=0:数据读、写操作后,AC(光标寄存器)自动减1;S=1:数据读、写操作,画面平移;S=0:数据读、写操作,画面保持不变。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S |
用于设置显示,光标及闪烁开、关。其中,D表示显示开关:D=1 为开,D=0 为关;C表示光标开关:C=1 为开,C=0 为关;B表示闪烁开关:B=1 为开,B=0 为关。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B |
用于在不影响DDRAM(内存)的情况下使光标、画面移动。其中,S/C=1 :画面平移一个字符位;S/C=0 :光标平移一个字符位;R/L=1 :右移;R/L=0 :左移。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | * | * |
用于设置工作方式(初始化指令)。其中,DL=1 :8位数据接口;DL=0 :4位数据接口;N=1 :分两行显示;N=0 :在同一行显示;F=1 :5x10点阵字符;F=0 :5x7点阵字符。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 0 | 1 | DL | N | F | * | * |
用于设置CGRAM的地址。A5~A0对应的地址为0x00~0x3F。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 0 | 1 | A5 | A4 | A3 | A2 | A1 | A0 |
用于设置DDRAM的地址。N=0 :在一行中显示,此时A6~A0对应0x00~0x4F:N=1 :分两行显示,首行A6~A0对应0x00~0x2F,次行A6~A0对应0x40~0x64.
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 0 | 1 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
其中,BF=1 表示当前忙;BF=0 表示已经准备好。此时,AC值意义为最后一次地址设置(CGRAM或DDRAM)定义。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
0 | 1 | BF | AC6 | AC5 | AC4 | AC3 | AC2 | AC1 | AC0 |
用于将地址写入DDRAM以使1602液晶显示出相应的图形或将用户自创的图形存入CGRAM内。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | 0 | 数据 |
根据当前设置的地址,把CGRAM或DDRAM数据读出。
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | 1 | 数据 |
(1)延时 15ms
(2)写指令 38H( 不检测忙信号 )
(3)延时 5ms
(4)以后每次写指令,读 / 写数据操作均需要检测忙信号
(5)写指令 38H :显示模式设置
(6)写指令 08H :显示关闭
(7)写指令 01H :显示清屏
(8)写指令 06H :显示光标移动设置
(9)写指令 0CH :显示开及光标设置
注意:?先行后纵? 例如感叹号? !=? 0010 0001? =? ?0x21
位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
第一行 | 00H | 01H | 02H | 03H | 04H | 05H | 06H | 07H | 08H | 09H | 0AH | 0BH | 0CH | 0DH | 0EH | 0FH |
第二行 | 40H | 41H | 42H | 43H | 44H | 45H | 46H | 47H | 48H | 49H | 4AH | 4BH | 4CH | 4DH | 4EH | 4FH |
注意:位置选择时最高位必须为1,第一行最左边地址为 0x00 | 0x80 ;第二行为 0x40 | 0x80 ? ?
#include <reg52.h>
#include "Delay.h"
unsigned char text[]={"Hello world"};
unsigned char text2[]={"Old_man"};
unsigned char i;
sbit RS=P2^0; //LCD数据/命令选择方式端口定义
sbit RW=P2^1; //LCD读写使能端口定义
sbit E=P2^2; //LCD使能端口定义
sbit bflag=P0^7; //LCD忙指示端口,高电平为忙
void busy_1602() //判忙忙闲?
{
do
{
P0=0x00;
RS=0; //RS=0,RW=1时才可读忙信号
RW=1;
E=1;
delay_ms(1);
bflag=P0;
E=0;
}while(bflag); //当其值为0时,表示不忙,才可以接收命令或者数据信号
}
void wirte_com(unsigned char com) //函数功能:写指令函数
{
busy_1602();
RS=0; //当RS=0,RW=0时,表明写入的是命令
RW=0;
E=0;
P0=com; //当使能由高到低时,LCD执行相应命令
E=1;
delay_ms(1);
E=0;
}
void wirte_data(unsigned char dat) //函数功能:写数据函数
{
busy_1602();
RS=1; //当RS=1,RW=0时,表明写入的是数据
RW=0;
E=0;
P0=dat; //当使能由高到低时,LCD执行相应命令
E=1;
delay_ms(1);
E=0;
}
void lcd_pos(unsigned char pos) //函数功能:指定数据显示地址
{
wirte_com(pos | 0x80); //最高位必须为1,第一行最左边地址为0x00;第二行为0x40
}
void init_1602() //函数功能:设置LCD_1602的开显示 光标不闪烁等功能
{
//(1)延时 15ms
delay_ms(15);
//(2)写指令 38H(不检测忙信号)
wirte_com(0x38);
//(3)延时 5ms
delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
wirte_com(0x38);
//(6)写指令 08H:显示关闭
wirte_com(0x08);
//(7)写指令 01H:显示清屏
wirte_com(0x01);
//(8)写指令 06H:显示光标移动设置
wirte_com(0x06);
//(9)写指令 0CH:显示开及光标设置}
wirte_com(0x0c);
}
void main(void)
{
init_1602();
lcd_pos(0x00); //第一行
for(i=0;i<11;i++)
{
wirte_data(text[i]);
}
lcd_pos(0x40); //第二行
for(i=0;i<7;i++)
{
wirte_data(text2[i]);
}
while(1)
{
}
}
解决问题:当我们直接写入0x01时 LCD1602并不会显示1这个数字 (我们给LCD1602十六进制他只会从显示的ASCLL码表中找到该十六进制所对应的内容并将其显示)
LCD1602.c
#include <REGX52.H>
#include "Delay.h"
//引脚配置:
sbit LCD_RS=P2^0;
sbit LCD_RW=P2^1;
sbit LCD_EN=P2^2;
#define LCD_DataPort P0
void busy_1602() //判忙忙闲?
{
do
{
LCD_DataPort=0x00;
LCD_RS=0; //RS=0,RW=1时才可读忙信号
LCD_RW=1;
LCD_EN=1;
delay_ms(1);
LCD_DataPort;
LCD_EN=0;
}while(LCD_DataPort); //当其值为0时,表示不忙,才可以接收命令或者数据信号
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
busy_1602();
LCD_RS=0;
LCD_RW=0;
LCD_EN=0;
LCD_DataPort=Command;
LCD_EN=1;
delay_ms(1);
LCD_EN=0;
delay_ms(1);
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
busy_1602();
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
LCD_DataPort=Data;
LCD_EN=1;
delay_ms(1);
LCD_EN=0;
delay_ms(1);
}
/**
* @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 if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,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,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(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @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(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @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,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @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(Number/LCD_Pow(2,i-1)%2+'0');
}
}
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,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
提示:C51学习中对此部分理解下就可以,ASCLL码与十六进制的互相转换。