【51单片机快速入门指南】6.1:LCD1602的八线、四线控制及自定义符号,完美兼容Proteus仿真

11 篇文章 9 订阅
7 篇文章 3 订阅

普中51-单核-A2
STC89C52
Windows 10 20H2
Proteus 8 Frofessional v8.9 SP2
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0


硬知识

       摘自《通用1602 液晶显示模块使用手册》、《液晶LCD1602(中文资料)—— ball 2010-5-13整理》

显示特性

       单5V电源电压,低功耗、长寿命、高可靠性
       内置192种字符(160个5x7点阵字符和32个5x10点阵字符)
       具有64个字节的自定义字符RAM,可自定义8个5x8点阵字符或四个5x11点阵字符
       显示方式:STN、半透、正显
       驱动方式:1/16DUTY,1/5BIAS
       视角方向:6点
       背光方式:底部LED
       通讯方式:4位或8位并口可选
       标准的接口特性,适配MCS1和M6800系列MPU的操作时序。

接口定义

在这里插入图片描述

操作时序

写操作时序

在这里插入图片描述
在这里插入图片描述

读操作时序

在这里插入图片描述
在这里插入图片描述

寄存器

       本模块内部具有两个8位寄存器:指令寄存器(IR)和地址寄存器(DR),用户可以通过RS和R/W输入信号的组合选择指定的寄存器,进行相应的操作。下表中列出了组合选择方式。

在这里插入图片描述
       指令寄存器IR,内部存储DDRAM和CGRAM中的数据显示的指令代码和地址信息,只能由MPU对其执行写操作:
       数据寄存器DR,内部暂时存储MPU与模块内部DDRAM和CGRAM之间的传送数据,内部操作使DR与DDRAM或者CGRAM之间的数据传送自动进行:
在这里插入图片描述

忙标志位BF

       忙标志BF-1时,表明模块正在进行内部操作,此时不接受任何外部指令和数据。当Rs-0,RW-1以及E为高电平时,BF输出到DB7,每次操作之前最好先进行状态字检测,只有在确认BF-0之后,MPU才能访问模块

地址计数器(AC)

       AC地址计数器是DDRAM或者CGRAM的地址指针。随着IR中指令码的写入,指令码中携带的地址信息自动送入AC中,并行做出AC作为DDRAM的地址指针还是CGRAM的地址指针的选择。
       AC具有自动加1或者减1的功能。当DR与DDRAM或者CGRAM之间完成一次数据传后,AC自动会加1或减1,在RS=0,R/W=1且E为高电平时,AC的内容送到DB6 ~ DB0;
在这里插入图片描述

显示数据寄存器(DDRAM)

       DDRAM存储显示字符的字符码,其容量的大小决定着模块最多可显示的字符数目.DDRAM地址与LCD显示屏上的显示位置的对应关系如下:
在这里插入图片描述

       执行显示移位操作时,对应的DDRAM地址也发生移位,以每行16个字符的显示为例,移位前后的地址对应关系如下:
在这里插入图片描述

CGROM

       HD44780内置了192个常用字符的字模,存于字符产生器CGROM(Character Generator ROM)中。在CGROM中,模块已经以8位二进制数的形式,生成了5x8点阵的字符字模组字符字模(一个字符对应一组字模),字符字模是与显示字符点阵相对应的8x8矩阵位图数据(与点阵行相对应的矩阵行的高三位为“0"),同时每一组字符字模都有一个由其在CGROM中存放地址的高八位数据组成的字符码对应。
在这里插入图片描述

CGRAM

       另外还有8个允许用户自定义的字符产生RAM,称为CGRAM(Character Generator RAM)
       就单屏结构的模块而言,字符码地址范围为00H ~ FFH,其中00H ~ 07H字符码与用户在CGRAM中生成的自定义图形字符的字模组相对应:至于双屏或者多屏结构的模块,由于各显示屏结构部分的工作分别由独立的使能信号E控制,因而各结构部分间字符的发生互不影响,每一显示屏结构部分的字符码地址范围为00H ~ FFH,其中00H ~ 07H字符码与用户在CGRAM中生成的自定义图形字符的字模组相对应。

指令

清屏指令

在这里插入图片描述

光标归位指令

在这里插入图片描述

进入模式设置指令

在这里插入图片描述

显示开关控制指令

在这里插入图片描述

设定显示屏或光标移动方向指令

在这里插入图片描述
在这里插入图片描述

功能设定指令

在这里插入图片描述

设定CGRAM地址指令

在这里插入图片描述

设定DDRAM地址指令

在这里插入图片描述

读取忙信号或AC地址指令

在这里插入图片描述

数据写入DDRAM或CGRAM指令

在这里插入图片描述

从 CGRAM或DDRAM读出数据的指令

在这里插入图片描述

示例程序

       stdint.h【51单片机快速入门指南】1:基础知识和工程创建

在这里插入图片描述

LCD1602.c

#include <STC89C5xRC.H>
#include "stdint.h"
#include "LCD1602.h"

//******************以下内容移植时需修改******************

void delay_ms(int i);

#define LCD1602_Port P0

sbit RS_Pin = P2 ^ 6;			//寄存器选择位,RS位
sbit WR_Pin = P2 ^ 5;			//读写选择位,RW
sbit EN_Pin = P2 ^ 7;			//使能信号位,E位

void LCD1602_Delay()
{
//主频快就加延时
}

void LCD1602_RS_H()
{
	RS_Pin = 1;
}

void LCD1602_RS_L()
{
	RS_Pin = 0;
}

void LCD1602_WR_H()
{
	WR_Pin = 1;
}

void LCD1602_WR_L()
{
	WR_Pin = 0;
}

void LCD1602_EN_H()
{
	EN_Pin = 1;
}

void LCD1602_EN_L()
{
	EN_Pin = 0;
}

void LCD1602_Write_Port(uint8_t Data)
{
#ifdef USE_4_Pin
	LCD1602_Port &= 0x0F;
	LCD1602_Port |= (Data << 4);
#else
	LCD1602_Port = Data;
#endif
}

uint8_t LCD1602_Read_Port()
{
#ifdef USE_4_Pin	//4线下好像没有意义
	return 0;
#else
	return LCD1602_Port;
#endif
}

//******************以上内容移植时需修改******************

unsigned char code LCD1602_DIY_Char[64]=	//8个自定义字符 地址为0x00~0x07
{
	0x02,0x04,0x0F,0x12,0x0F,0x0A,0x1F,0x02,//年
	0x0F,0x09,0x0F,0x09,0x0F,0x09,0x09,0x11,//月
	0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00,//日
	0x16,0x09,0x08,0x08,0x08,0x09,0x06,0x00,//℃
	0x00,0x11,0x0A,0x04,0x0A,0x11,0x00,0x00,//×
	0x00,0x04,0x00,0x1F,0x00,0x04,0x00,0x00,//÷
	0x00,0x0E,0x11,0x11,0x11,0x0A,0x1B,0x00,//Ω
	0x00,0x11,0x11,0x11,0x19,0x16,0x10,0x00,//μ
};

/*****************************************************
函数功能:判断液晶模块的忙碌状态
返回值:1: 忙碌; 0: 不忙
***************************************************/
uint8_t LCD1602_Busy_Test(void)
{
#ifdef USE_4_Pin
	delay_ms(2);
	return 0;
#else
	return ((LCD1602_Operation(0, 1, 0) & 0x80) && 1);
#endif
}

uint8_t LCD1602_Operation(uint8_t Cmd_Data_Flag, uint8_t Write_Read_Flag, uint8_t Data)
{
	if(Cmd_Data_Flag)
		LCD1602_RS_H();
	else
		LCD1602_RS_L();
	if(Write_Read_Flag)
		LCD1602_WR_H();
	else
		LCD1602_WR_L();
	LCD1602_Delay();		//给硬件反应时间
	if(Write_Read_Flag)
	{
		LCD1602_EN_H();
		LCD1602_Delay();	//给硬件反应时间
		Data = LCD1602_Read_Port();
	}
	else
	{
		LCD1602_EN_L();
		LCD1602_Write_Port(Data);
		LCD1602_Delay();	//给硬件反应时间
		LCD1602_EN_H();
		LCD1602_Delay();	//给硬件反应时间
	}
	LCD1602_EN_L();
	return Data;
}

/*****************************************************
函数功能:将一个字节写入液晶模块
入口参数:Byte 字节, Cmd_Data_Flag 0为命令, 1为数据
***************************************************/
void LCD1602_Write_Byte(uint8_t Byte, uint8_t Cmd_Data_Flag)
{
	while (LCD1602_Busy_Test());	//如果忙就等待
#ifdef USE_4_Pin
	LCD1602_Operation(Cmd_Data_Flag, 0, Byte >> 4);
	while (LCD1602_Busy_Test());
	LCD1602_Operation(Cmd_Data_Flag, 0, Byte & 0x0F);
#else
	LCD1602_Operation(Cmd_Data_Flag, 0, Byte);
#endif
}

void LCD1602_DIY_Char_Init(void)
{
	unsigned char i;
	LCD1602_Write_Byte(LCD1602_Set_CGRAM_Addr, 0);	//开始写入你要显示的自定义字符、汉字代码	
	for(i = 0; i < 64; i++)
	{
		LCD1602_Write_Byte(LCD1602_DIY_Char[i], 1);	//开始写入你要显示的自定义字符、汉字代码
	}
}

/*****************************************************
函数功能:指定字符显示的实际地址
入口参数:row 行:0或1 col:列
***************************************************/
void LCD1602_Write_Address(uint8_t row, uint8_t col)
{
	if(row)
		LCD1602_Write_Byte(col | 0xc0, 0);	
	else
		LCD1602_Write_Byte(col | 0x80, 0);	//显示位置的确定方法规定为"80H+地址码x"
}

void LCD1602_Write_Str(char *Str)
{
    while (*Str)						//检测字符串结束标志
    {
        LCD1602_Write_Byte(*Str++, 1);	//发送当前字符
    }
}

/*****************************************************
函数功能:对LCD的显示模式进行初始化设置
***************************************************/
void LCD1602_Init(void)
{
#ifdef USE_4_Pin
	LCD1602_Write_Byte(LCD1602_Cursor_Return, 0);
	LCD1602_Write_Byte(LCD1602_Config | LCD1602_4PIN | LCD1602_2Row | LCD1602_5X7, 0);
#else
	LCD1602_Write_Byte(LCD1602_Config | LCD1602_8PIN | LCD1602_2Row | LCD1602_5X7, 0);
#endif
	LCD1602_Write_Byte(LCD1602_Display_Switch | Display_Enable | Cursor_Enable | Cursor_Blink_Enable, 0);
	LCD1602_Write_Byte(LCD1602_Cursor_Screen_Mode | Cursor_Right_Mode | Screen_Static_Mode, 0);
	LCD1602_DIY_Char_Init();
	LCD1602_Write_Byte(LCD1602_Clr, 0);	//清屏幕指令,将以前的显示内容清除
}

LCD1602.h

#ifndef LCD1602_H_
#define LCD1602_H_

#define USE_8_Pin
//#define USE_4_Pin

#ifdef USE_8_Pin
#ifdef USE_4_Pin
#error
#endif
#endif

#define LCD1602_Clr					0x01
#define LCD1602_Cursor_Return		0x02

#define LCD1602_Cursor_Screen_Mode	0x04
	#define Cursor_Left_Mode		0x00	
	#define Cursor_Right_Mode		0x02
	#define Screen_Static_Mode		0x00
	#define Screen_Right_Mode		0x01

#define LCD1602_Display_Switch		0x08
	#define Display_Disable			0x00
	#define Display_Enable			0x04
	#define Cursor_Disable			0x00
	#define Cursor_Enable			0x02
	#define Cursor_Blink_Disable	0x00
	#define Cursor_Blink_Enable		0x01

#define LCD1602_Cursor_Screen_Move	0x10
	#define Cursor_Left				0x00
	#define Cursor_Right			0x04
	#define Screen_Left				0x08
	#define Screen_Right			0x0c

#define LCD1602_Config				0x20
	#define LCD1602_4PIN			0x00
	#define LCD1602_8PIN			0x10
	#define LCD1602_1Row			0x00
	#define LCD1602_2Row			0x08
	#define LCD1602_5X7				0x00
	#define LCD1602_5X10			0x04

#define LCD1602_Set_CGRAM_Addr		0x40
#define LCD1602_Set_DDRAM_Addr		0x80

uint8_t LCD1602_Busy_Test(void);
uint8_t LCD1602_Operation(uint8_t Cmd_Data_Flag, uint8_t Write_Read_Flag, uint8_t Data);
void LCD1602_Write_Byte(uint8_t Byte, uint8_t Cmd_Data_Flag);
void LCD1602_Write_Address(uint8_t row, uint8_t col);
void LCD1602_Write_Str(char *Str);
void LCD1602_Init(void);

#endif

测试程序

四线和八线在LCD1602.h中控制
在这里插入图片描述

main.c

#include <STC89C5xRC.H>
#include "intrins.h"
#include "stdint.h"
#include "LCD1602.h"

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}


void delay_ms(int i)
{
	while(i--)
		Delay1ms();
}

void main(void)
{
	char i;

	LCD1602_Init();
	LCD1602_Write_Address(0, 0);
	LCD1602_Write_Str("0123456789ABCDEF");
	LCD1602_Write_Address(1, 0);
	for(i = 0; i < 8; ++i)
		LCD1602_Write_Byte(i, 1);

	while(1)
	{	

	}
}

实验现象

在这里插入图片描述

仿真现象

需要注意的是,Proteus仿真时存储模式需设为Small才能正确显示,很神秘。
在这里插入图片描述

八线制

在这里插入图片描述

四线制

在这里插入图片描述

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是一个完整的51单片机实现红外线遥控台灯的代码,并且提供了proteus仿真测试的方法,你可以先在proteus中测试代码的正确性,再将代码烧录到实际硬件中。 代码如下: ``` #include <reg52.h> #define IR_PORT P3 // 红外线接收器连接的IO口 #define RELAY_PORT P1 // 继电器连接的IO口 #define IR_CODE_POWER 0x00 // 红外线遥控器的按键码 sbit RELAY = RELAY_PORT^0; // 继电器连接的IO口 void delay_us(unsigned int us) // 延时函数,单位:微秒 { while(us--) { _nop_(); _nop_(); } } void delay_ms(unsigned int ms) // 延时函数,单位:毫秒 { while(ms--) { delay_us(1000); } } void init_IR() // 初始化红外线接收器 { IR_PORT = 0xff; // 初始化为高电平 EX0 = 1; // 开启外部中断0 EA = 1; // 开启总中断 } void IR_ISR() interrupt 0 // 红外线接收器中断服务程序 { unsigned char IR_CODE = 0; // 红外线遥控器的按键码 unsigned char i = 0; unsigned int j = 0; if(IR_PORT == 0) // 判断是否为红外线信号 { delay_us(700); // 等待信号头 if(IR_PORT == 0) // 确认信号头 { delay_us(1700); // 等待数据位 for(i=0; i<8; i++) // 读取按键码 { j = j << 1; delay_us(600); if(IR_PORT == 0) { j++; } delay_us(1100); } IR_CODE = (unsigned char)j; // 转换为按键码 if(IR_CODE == IR_CODE_POWER) // 判断是否为开关键 { RELAY = ~RELAY; // 控制继电器,实现开关灯 } } } } void main() { init_IR(); // 初始化红外线接收器 while(1); } ``` 在proteus中进行仿真测试的步骤如下: 1. 在proteus中新建一个电路图,将51单片机、红外线接收器、继电器等元件拖入电路图中。 2. 连接电路图中各个元件之间的引脚,注意要将红外线接收器的输出引脚连接到单片机的外部中断0引脚。 3. 在proteus中打开示波器窗口,将红外线接收器的输出引脚连接到示波器的通道1。 4. 将上面提供的代码复制粘贴到Keil C中,编译生成HEX文件。 5. 在proteus中打开单片机的属性窗口,选择HEX文件,烧录到单片机中。 6. 在proteus中运行仿真,使用红外线遥控器对接收器进行操作,观察示波器的波形变化,检查继电器的开关是否正常。 希望这个代码和仿真测试对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乙酸氧铍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值