51单片机快速入门之 LCD1602 液晶显示屏
Proteus 电路图 :
74HC595 拓展电路可以不用,给 p0-p17 添加上拉电阻也可以!,我这里是方便读取和节省电阻线路
(因为之前不知道 在没有明确循环的情况下,Keil编译器可能会在main()中自动添加类似以下的汇编代码:jump main ; 跳转到main函数的开始处
) 所以显示效果就成了 输入 删除 输入 删除
期间各种排查,所以后面的电路图有所更改
指令集 :
代码实现:
#include <STC89C5xRC.H>
//定义接口
#define sbp P1
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay(unsigned int t) ;//声明
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) ;//声明
void LCD1602RED(unsigned char RSB,unsigned char RWB); //声明
void main()
{
LCD1602(0,0,0x01);//清屏指令
LCD1602(0,0,0x0e);//打开显示 光标不闪烁
LCD1602(0,0,0x30);//设置8位使用
LCD1602(1,0,0x30);//写入0
while(1); //防止keil 添加的自动跳回起点导致无限循环执行main函数
}
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) //1602写入操作
{
RS=RSA; //传递寄存器选择
RW=RWA; //传递读写选择
E=1; //允许读取 1602
sbp=date;
delay(200);
E=0; //拉低使能控制 下降沿允许写入! 1602
}
void LCD1602RED(unsigned char RSB,unsigned char RWB) //读取操作
{
unsigned char reddat;//声明一个中间变量
E=1;//允许读取
reddat=P0;//获取 P0端口信息
}
void delay(unsigned int t) //简单延迟函数
{
while(t--);
}
在Keil等嵌入式开发环境中,如果你没有在
main
函数中明确地包含一个无限循环(如while(1)
),编译器可能会自动在main
函数的末尾添加代码,以确保程序不会意外退出。这种自动添加的代码通常是一个跳转指令,它会跳回到
main
函数的开始处,从而形成一个隐式的无限循环。这样做是为了确保单片机程序能够持续运行,因为单片机通常不需要像桌面应用程序那样有一个明确的退出过程。相反,它们需要不断地执行其预定的任务,直到被外部事件(如复位按钮)终止。
效果展示:
显示16个字符代码:
#include <STC89C5xRC.H>
//定义接口
#define sbp P1
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay(unsigned int t) ;//声明
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) ;//声明
void LCD1602RED(unsigned char RSB,unsigned char RWB); //声明
void main()
{
unsigned char y; //声明一个变量
LCD1602(0,0,0x01);//清屏指令
LCD1602(0,0,0x0e);//打开显示 光标不闪烁
LCD1602(0,0,0x30);//设置8位使用
for(y=0;y<16;y++){
LCD1602(1,0,0x30);//写入0
}
while(1);
}
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) //1602写入操作
{
RS=RSA; //传递寄存器选择
RW=RWA; //传递读写选择
E=1; //允许读取 1602
sbp=date;
delay(200);
E=0; //拉低使能控制 下降沿允许写入! 1602
}
void LCD1602RED(unsigned char RSB,unsigned char RWB) //读取操作
{
unsigned char reddat;//声明一个中间变量
E=1;//允许读取
reddat=P0;//获取 P0端口信息
}
void delay(unsigned int t) //简单延迟函数
{
while(t--);
}
显示效果:
显示中文:
因为1602中一般不会储存中文字库,所以我们得自己建立.
首先我们知道其有两种点阵模式 也就是说 2行16列 (16字符) 还可以向下细分为 5x7点阵/字符 或者 5x10 点阵/字符 这意味着每个字符占据5列宽和7行高的空间
首先我们用操作单个字符 以王字为例(这里要用到1602的CGRAM:自定义字符)
1602液晶模块中的CGRAM(Character Generator RAM)是用户可以自定义的字符图形存储区。它允许用户创建和存储自定义的字符图案,从而在液晶屏幕上显示独特的字符或图形。
CGRAM的基本概念
-
存储容量:CGRAM通常有8个字节的存储空间,每个字节可以存储一个自定义字符的点阵图案。
-
地址范围:CGRAM的地址范围通常是0x40到0x47,对应于8个自定义字符的存储位置。
由上图结合数据手册和下方点位图可知 :
列为 4 3 2 1 0
行为(注意该图只是第一个字符00H 的 | 第二个字符01H 的第一行 0x48 )
数据手册中明确指出我们可自定义 8 个 字符 00H-07H 或者 08H-0FH (00H==08H)
所以我们想要显示王这个字应该如下
4 3 2 1 0
0x40 行 1 1 1 1 1
0x41 行 0 0 1 0 0
0x42 行 0 0 1 0 0
0x43 行 1 1 1 1 1
0x44 行 0 0 1 0 0
0x45 行 0 0 1 0 0
0x46 行 1 1 1 1 1
0x47 行 有光标时: 光标会占用这一行!
代码如下 :
可以用循环简化,为了更直观,便于理解这里没有用循环!
#include <STC89C5xRC.H>
//定义接口
#define sbp P1
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay(unsigned int t) ;//声明
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) ;//声明
void LCD1602RED(unsigned char RSB,unsigned char RWB); //声明
void main()
{
unsigned char y; //声明一个变量
LCD1602(0,0,0x01);//清屏指令
LCD1602(0,0,0x0e);//打开显示 光标不闪烁
LCD1602(0,0,0x30);//设置8位使用
LCD1602(0,0,0x40);//选中 自定义字符 00H 第一行
LCD1602(1,0,0x1f);//存入第一行数据 0001 1111
LCD1602(0,0,0x41);//选中 自定义字符 00H 第二行
LCD1602(1,0,0x04);//存入第二行数据 0000 0100
LCD1602(0,0,0x42);//选中 自定义字符 00H 第三行
LCD1602(1,0,0x04);//存入第三行数据 0000 0100
LCD1602(0,0,0x43);//选中 自定义字符 00H 第四行
LCD1602(1,0,0x1f);//存入第四行数据 0001 1111
LCD1602(0,0,0x44);//选中 自定义字符 00H 第五行
LCD1602(1,0,0x04);//存入第五行数据 0000 0100
LCD1602(0,0,0x45);//选中 自定义字符 00H 第六行
LCD1602(1,0,0x04);//存入第六行数据 0000 0100
LCD1602(0,0,0x46);//选中 自定义字符 00H 第七行
LCD1602(1,0,0x1f);//存入第七行数据 0001 1111
LCD1602(0,0,0x80); //设置显示在那个字符 DDRAM 一行显示时范围为 00H-4FH
LCD1602(1,0,0x00); //显示自定义字符!
while(1);
}
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) //1602写入操作
{
RS=RSA; //传递寄存器选择
RW=RWA; //传递读写选择
E=1; //允许读取 1602
sbp=date;
delay(200);
E=0; //拉低使能控制 下降沿允许写入! 1602
}
void LCD1602RED(unsigned char RSB,unsigned char RWB) //读取操作
{
unsigned char reddat;//声明一个中间变量
E=1;//允许读取
reddat=P0;//获取 P0端口信息
}
void delay(unsigned int t) //简单延迟函数
{
while(t--);
}
显示效果:
1602的忙碌状态 :
可以称呼其为 锁
但是需要硬件支持,也就是说 得 8个io 对应单片机 8个引脚 直接链接
其原理为(读取模式下,判断最高位否为0)
首先设置 单片机 所有 io为 高电平 ,初始化!
P1=1;
然后进入读取操作
RS=0;
RW=1; //选择读!
E=0;
delay(100);
E=1; //读取状态
此时 如果 其忙,不能接受数据 p1.7-p1.0顺序 当p1.7 为 还是为高电平 1
表示1602 很忙 不干其他活 不接受任何外部指令和数据!
当p1.7为 0时 表示其不忙 无论是读还是写都可以!
懒人必备之字符串输出:
原理:
void LCDDD(unsigned char *strr)
是一个函数,它接受一个指向无符号字符的指针strr
作为参数。这个函数的作用是在LCD1602显示屏上显示一个字符串。
while(*strr)
是一个循环,它会一直执行直到指针strr
所指向的字符为空字符('\0')通常是C语言字符串的结束标志。
strr++;
将指针strr
向前移动一个字符,指向下一个字符。可以理解为“往右移动”,因为它意味着指针从当前位置向字符串或数组的尾部方向移动。
这里要说一下 DDRAM
1602的DDRAM(Display Data RAM)是显示数据随机存储器的简称,常应用于LCD1602等字符设备中。DDRAM是一个80字节的RAM,能够最多存储80个8位字符代码作为显示数据,对应于显示屏上的各个位置。在LCD1602中,第一行的地址为00H到27H,第二行为40H到67H。
比方说我们要在00H显示 这里先设置
RS=0 RW=0 E=1 发送设置 延时 E=0
00H 0000 0000 但是这里设置DDRAM最高位固定是一
所以 是 1000 0000 0x80 为第一行第一个字符
同理第二行第一个是 40 0100 0000 因固定位 所以 是1100 0000 0xc0
代码实现:
#include <STC89C5xRC.H>
//定义接口
#define sbp P1
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay(unsigned int t) ;//声明
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) ;//声明
void LCD1602RED(unsigned char RSB,unsigned char RWB); //声明
unsigned char y=0; //声明一个变量
void LCDDD(unsigned char *strr); //声明
void main()
{
LCD1602(0,0,0x01);//清屏指令
LCD1602(0,0,0x0e);//打开显示 光标不闪烁
LCD1602(0,0,0x38);//设置8位使用 这里要为设置显示为2行
LCDDD("abcdefghigklmn0123456789");
while(1);
}
void LCDDD(unsigned char *strr)
{
while(*strr) {
LCD1602(1,0,*strr); //写入当前位置字符
strr++; //往右移动
y++; //字符检测当字符超出时执行换行
if(y==16) {
LCD1602(0,0,0xC0) ;//切换到第二行
}
}
}
void LCD1602(unsigned char RSA,unsigned char RWA,unsigned char date) //1602写入操作
{
RS=RSA; //传递寄存器选择
RW=RWA; //传递读写选择
E=1; //允许读取 1602
sbp=date;
delay(200);
E=0; //拉低使能控制 下降沿允许写入! 1602
}
void delay(unsigned int t) //简单延迟函数
{
while(t--);
}