【自学嵌入式:51单片机】LCD1602

LCD1602

image
LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其他一些内置特殊字符,还可以有8个自定义字符。

  • 显示容量:16×2个字符,每个字符为5×7点阵

电路

image

引脚 功能
GND(VSS)
VCC(VDD) 电源正极(4.5~5.5V)
VO 对比度调节电压
RS 数据/指令选择,1为数据,0为指令
RW 读/写选择,1为读,0为写
E 使能,1为数据有效,下降沿执行命令
D0~D7 数据输入/输出
BG VCC(A) 背光灯电源正极
BG GND(K) 背光灯电源负极

D0~D7是一个字节的并行口,不用一位一位传输
E相当于一个时钟线,高电平有效,它决定什么时候开始采样信号

【注】图中背光灯的VCC和GND都默认连好了,没法自己控制开关背光灯。

内部结构框图

image
字模库,相当于数码管的段码表(几乎所有能显示的ASCII字符都存在这里,少部分被替换为非标准的ASCII字模),RAM是可以自定义字模,ROM是出场自带的,数据显示区前16列可以显示在屏幕上,不能显示的向左移屏就能显示了。
LCD1602字模

存储器结构

image
这里索引码和ASCII是一样的

指令集

LCD1602指令集

时序结构

写数据/指令

image
最开始要想写数据,置RS为高电平,如果想写指令,RS为低电平,R/W低电平,然后把数据/指令放在D0-D7,E使能端打开

读数据/指令

image
最开始想要读数据,置RS为高电平,想要写指令,RS为低电平,读就是把R/W设为高电平,E使能端打开,经过一段时间TD,它就会把想要发送的数据放在中间,然后I/O口把Valid Data读取,其他和写操作类似。

时序参数

image

操作流程

image

代码实现

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD1602_Init(); //LCD1602初始化
void LCD1602_ShowChar(unsigned char x, unsigned char y, unsigned char Char); //显示字符
void LCD1602_ShowString(unsigned char x, unsigned char y, unsigned char* String); //显示字符串
void LCD1602_ShowNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length); //显示数字
void LCD1602_ShowSignedNum(unsigned char x, unsigned char y, int Number, unsigned char Length); //显示有符号数
void LCD1602_ShowHexNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length); //显示十六进制数字
void LCD1602_ShowBinNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length); //显示二进制数字
void LCD1602_WriteData(unsigned char Data); //写数据
void LCD1602_WriteCommand(unsigned char Command); //写指令

#endif

LCD1602.c

#include <REGX52.H>
#include <intrins.h> // 延时空语句

sbit LCD_RS = P2^6;
sbit LCD_RW = P2^5;
sbit LCD_E = P2^7;
#define LCD_DataPort P0

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

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

//写指令
void LCD1602_WriteCommand(unsigned char Command)
{
	LCD_RS = 0; //写指令,RS为低电平
	LCD_RW = 0; //写
	LCD_DataPort = Command; //指令
	LCD_E = 1; //打开使能
	LCD1602_Delay1ms(); //延迟1ms,写入指令大概40us
	LCD_E = 0; //关闭使能
	LCD1602_Delay1ms();
}

//写数据
void LCD1602_WriteData(unsigned char Data)
{
	LCD_RS = 1; //写数据,RS为低电平
	LCD_RW = 0; //写
	LCD_DataPort = Data; //数据
	LCD_E = 1; //打开使能
	LCD1602_Delay1ms(); //延迟1ms,写入数据大概40us
	LCD_E = 0; //关闭使能
	LCD1602_Delay1ms();
}

//初始化
void LCD1602_Init()
{
	LCD1602_WriteCommand(0x38); //八位数据结构,两行显示,5*7点阵
	LCD1602_WriteCommand(0x0C); //显示开,光标关,闪烁关
	LCD1602_WriteCommand(0x06); //数据读写操作后,光标自动加一,画面不动
	LCD1602_WriteCommand(0x01); //清屏
}

//设置光标位置单独写一个函数
void LCD1602_SetCursor(unsigned char x, unsigned char y)
{
	switch(x)
	{
		case 0:
			LCD1602_WriteCommand(0x80 | y);//或运算,地址设置最高位要置1
			break;
		case 1:
			LCD1602_WriteCommand((0x80 | y)+0x40); //第二行地址有偏移,加偏移量
			break;
	}
}

//显示字符,x和y按下标来
void LCD1602_ShowChar(unsigned char x, unsigned char y, unsigned char Char)
{
	//将行和列进行运算变成实际地址
	//我还是习惯下标为0开始,第0列
	LCD1602_SetCursor(x, y);
	//选完地址后写数据
	LCD1602_WriteData(Char);
}

//显示字符串
void LCD1602_ShowString(unsigned char x, unsigned char y, unsigned char* String)
{
	unsigned char i = 0;
	LCD1602_SetCursor(x, y);
	for(i = 0; String[i] != '\0'; i++)
	{
		LCD1602_WriteData(String[i]);
	}
}

//次方函数
int LCD1602_Pow(int X, int Y)
{
	unsigned char i;
	int Result = 1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

//显示数字
//Length是长度
void LCD1602_ShowNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length)
{
	unsigned char i = 0;
	LCD1602_SetCursor(x, y);
	//除相应10的次方取余获得个十百千万位的数字
	for(i = Length; i>0;i--)
	{
		LCD1602_WriteData('0' + Number/LCD1602_Pow(10, i-1) % 10); //加字符0的ACSII码,转换为字符
	}
}

//显示有符号数字
void LCD1602_ShowSignedNum(unsigned char x, unsigned char y, int Number, unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD1602_SetCursor(x, y);
	//0不加正负号
	if(Number>0)
	{
		LCD1602_WriteData('+');
		Number1 = Number;
	}
	else if(Number < 0)
	{
		LCD1602_WriteData('-');
		Number1 = -Number; //直接取相反数除了-32768以外都能转换,因为有符号int是-32768-32767
	}
	//转成无符号数方便显示
	for(i = Length; i>0;i--)
	{
		LCD1602_WriteData('0' + Number1/LCD1602_Pow(10, i-1) % 10); //加字符0的ACSII码,转换为字符
	}
}

//显示十六进制
void LCD1602_ShowHexNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length)
{
	unsigned char i = 0;
	unsigned char SingleNumber;
	LCD1602_SetCursor(x, y);
	//除相应16的次方取余获得个十百千万位的数字
	//大于等于10,变为字母A-F,要重新偏移一下(对应ASC码)
	for(i = Length; i>0;i--)
	{
		SingleNumber = Number/LCD1602_Pow(16, i-1) % 16;
		if(SingleNumber<10)
		{
			LCD1602_WriteData('0' + SingleNumber);
		}
		else
		{
			LCD1602_WriteData('A' + SingleNumber - 10); //减去10才是从字符A开始的ASCII码,不减去10就不对
		}
	}
}

//显示二进制
void LCD1602_ShowBinNum(unsigned char x, unsigned char y, unsigned int Number, unsigned char Length)
{
	unsigned char i = 0;
	LCD1602_SetCursor(x, y);
	//除相应2的次方取余获得个十百千万位的数字
	for(i = Length; i>0;i--)
	{
		LCD1602_WriteData('0' + Number/LCD1602_Pow(2, i-1) % 2); //加字符0的ACSII码,转换为字符
	}
}

main.c

#include <REGX52.H>
#include "LCD1602.h"

void Delay(unsigned int xms)		//@11.0592MHz
{
	unsigned char i, j;

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


void main()
{
	LCD1602_Init();
	LCD1602_ShowChar(0, 0, 'A');
	LCD1602_ShowString(0, 2, "Hello");
	LCD1602_ShowNum(0, 8, 66, 2);
	LCD1602_ShowSignedNum(0, 11, -88, 2);
	LCD1602_ShowHexNum(1, 0, 0xA5, 2);
	LCD1602_ShowBinNum(1, 3, 0xA5, 8);
	LCD1602_ShowChar(1, 12, 0xDF); //显示°
	//LCD1602_ShowChar(1, 13, 0xE0); //显示α
	LCD1602_ShowChar(1, 13, 'C'); //显示字母C,凑成摄氏度符号
	LCD1602_ShowString(0, 15, "Welcome to China!"); //流动字幕,不流动不够显示
	while(1)
	{
		LCD1602_WriteCommand(0x18); //写指令,左移屏幕一次
		Delay(500); //500ms移动一次
		//移动到头又会循环移动回来
	}
}
posted @ 2025-08-04 12:24  秦瑞迁  阅读(281)  评论(0)    收藏  举报