0%

51单片机程序(LCD1602)

很久没玩过51单片机了(.. )…,整理一下玩LCD1602模块的一些笔记。

LCD1602液晶显示模块可显示2行、16字/行的字符,绝大部分的LCD1602液晶显示模块都是基于HD77480及其兼容芯片来设计的。

单片机: STC89C52
开发板: HC6800-MS

LCD1602引脚

  • VSS:电源地
  • VDD:电源正极
  • V0:液晶显示偏压信号,连接电位器,用于调节液晶显示的对比度
  • RS:数据/命令选择端,当RS=1时,从D0D7读/写数据;当RS=0时,从D0D7读状态或写指令
  • RW:读/写选择端,当RW=1时,进行读操作;当RW=0时,进行写操作
  • E:使能端。进行读操作时,要使E处于高电平;进行写操作时,要使E处于下降沿
  • D0~D7:并行输入/输出端
  • A:背光源的正极
  • K:背光源的负极

基本操作时序

读操作时序

写操作时序

寄存器

LCD1602模块有两个8位寄存器,分别为指令寄存器和数据寄存器,可以通过RSR/W输入信号的组合来选择指定的寄存器进行相应的操作。

  • 读状态:

    • 输入:RS=0RW=1E=1
    • 输出:<D0~D7>=<状态字>
  • 写指令:

    • 输入:RS=0RW=0<D0~D7>=<指令码>E=<高脉冲>
    • 输出:无
  • 读数据:

    • 输入:RS=1RW=1E=1
    • 输出:<D0~D7>=<数据>
  • 写数据:

    • 输入:RS=1RW=0<D0~D7>=<数据>E=<高脉冲>
    • 输出:无

状态字说明

  • D7:读写操作使能,当D7=1时,禁止读写;当D7=0时,允许读写
  • D6~0:当前数据地址指针的数值

因为LCD1602搭载的芯片运算速度较慢,当模块正在进行内部操作时,不接受任何外部指令和数据,此时,忙标志位BF为高电平。所以每次进行读写操作前,都必须进行读忙,将状态标志位BF读到D7,确保D7=0

LCD1602指令

51单片机可以直接访问LCD1602的指令寄存器和数据寄存器,在LCD1602进行内部操作前,来自51单片机的控制信息和数据将分别暂存于此,这些控制信息和数据的组合即为LCD1602的指令。

清显示

归位

设置输入模式

显示开/关控制

游标或显示移位元

功能设置

CGRAM地址设置

DDRAM地址设置

读忙标志BF和AC

写数据到CGRAM或DDRAM

从CGRAM或DDRAM中读数据

CGROM、DDRAM和CGRAM

在LCD1602模块内,与字符显示相关的存储器有CGROM、DDRAM、CGRAM。对于显示一些常用字符,其字模数据已存储于CGROM中,再调用时,只需使用语法P=<常用字符>;即可调用;但如果需要显示CGROM中不存在的字符,则必须先在CGRAM中自定义,再写入DDRAM。

CGROM

CGROM内存储了192个常用字符的字模数据,只能读出,不能写入。这些字模数据恰好与其对应的字符的ASCII值相等,因此,单片机会根据某常用字符的ASCII值从CGROM中读出与之值相等的字符数据,并将该字符数据写入DDRAM,即可显示对应的常用字符。另外,在CGROM中,地址0x00 ~ 0x07内容为空,由用户在CGRAM中进行自定义,再调用自定义的字符。

DDRAM

DDRAM用于存储从CGROM或CGRAM读出的需要LCD1602显示的字符的字模数据,容量为80字节,地址范围:

  • 0x00 ~ 0x27
  • 0x40 ~ 0x67

其中用于存储需要显示的字符的字模数据的两段为:

  • 0x00 ~ 0x0f
  • 0x40 ~ 0x4f

其余部分用于存储字符显示相关的设置,例如字符的移动。

CGRAM

CGRAM用于存储自定义的字符的字模数据,可自定义8个点阵列大小为5×7的字符或4个点阵列大小为5×11的字符。以5×7模式为例,一个字模数据大小为8字节,需要8个地址来表示,每8个地址组成一个地址段,存储一个字模数据;第一个地址段的起始为0x40,该地址段对应CGROM中的地址0x00;第二个地址段的起始为0x48,该地址段对应CGROM中的地址0x01;第三个地址段的起始为0x50,该地址段对应CGROM中的地址0x02,依此类推。

举个栗子

//显示已有的字符

#include<reg51.h>
#include<intrins.h> //包含有_nop_()函数定义的头文件,若使用12MHz的晶振,_nop_()函数起到延时1微秒的效果,_nop_()函数延时的时长由晶振频率决定
sbit RS=P2^6;
sbit RW=P2^5;
sbit E=P2^7;

char Text0[]="Hello,"; //定义LCD1602第一行显示的字符
char Text1[]="world!"; //定义LCD1602第二行显示的字符

int ReadBusy() //定义一个用于读忙的函数
{
int temp;
RS=0;
RW=1;
_nop_();
P0=0xff; //在电子电路中,高电平容易被低电平拉低,而低电平一般不能被高电平拉高,所以在读取某I/O端数据时,将该I/O端置1,确保原来的数据不受影响
_nop_();
E=1;
_nop_();
temp=P0; //读取LCD1602的状态字
_nop_();
E=0;
return(temp&0x80);
}

void Delay5ms() //定义一个延时5毫秒的函数
{
unsigned char i,j;
i=59;
j=90;
do
{
while(--j);
}
while(--i);
}

void Write_Com(char com) //定义一个用于写命令的函数
{
while(ReadBusy());
RS=0;
RW=0;
_nop_();
P0=com; //准备写入命令
_nop_();
E=1; //当使能端为高电平时,写入命令
Delay5ms();
E=0; //命令写入完成后,要将使能端的高电平拉低
Delay5ms();
}

void Write_Dat(char dat) //定义一个用于写数据的函数
{
while(ReadBusy());
RS=1;
RW=0;
_nop_();
P0=dat;
_nop_();
E=1;
Delay5ms();
E=0;
Delay5ms();
}

void init() //定义一个用于初始化LCD1602的函数
{
Delay5ms(); //延时15毫秒
Delay5ms();
Delay5ms();
Write_Com(0x38); //设置显示模式
Delay5ms();
Write_Com(0x38);
Delay5ms();
Write_Com(0x38);
Write_Com(0x0c); //设置显示开关及光标
}

void main()
{
int i,j;
init();
Write_Com(0x80); //设置指针指向显示第一行的第一个字符的DDRAM地址
for(i=0;i<6;i++)
{
Write_Dat(Text0[i]);
}
Write_Com(0x80+0x40); //设置指针指向显示第二行的第一个字符的DDRAM地址
for(j=0;j<6;j++)
{
Write_Dat(Text1[j]);
}
}
//显示自定义字符

#include<reg51.h>
#include<intrins.h>
sbit RS=P2^6;
sbit RW=P2^5;
sbit E=P2^7;

char Image0[]={0x08,0x1F,0x12,0x0F,0x0A,0x1F,0x02,0x00}; //汉字“年”的字模数据
char Image1[]={0x0F,0x09,0x0F,0x09,0x0F,0x09,0x13,0x00}; //汉字“月”的字模数据
char Image2[]={0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00}; //汉字“日”的字模数据

int ReadBusy()
{
int temp;
RS=0;
RW=1;
_nop_();
P0=0xff;
_nop_();
E=1;
_nop_();
temp=P0;
_nop_();
E=0;
return(temp&0x80);
}

void Delay5ms()
{
unsigned char i,j;
i=59;
j=90;
do
{
while(--j);
}
while(--i);
}

void Write_Com(char com)
{
while(ReadBusy());
RS=0;
RW=0;
_nop_();
P0=com;
_nop_();
E=1;
Delay5ms();
E=0;
Delay5ms();
}

void Write_Dat(char dat)
{
while(ReadBusy());
RS=1;
RW=0;
_nop_();
P0=dat;
_nop_();
E=1;
Delay5ms();
E=0;
Delay5ms();
}

void init()
{
Delay5ms();
Delay5ms();
Delay5ms();
Write_Com(0x38);
Delay5ms();
Write_Com(0x38);
Delay5ms();
Write_Com(0x38);
Write_Com(0x0c);
}

void main()
{
int x,y,z;
init();
Write_Com(0x40); //设置指针指向CGRAM中第一个地址段的起始(5x7模式中一个字模数据大小为8字节,即需要8个地址来表示)
for(x=0;x<sizeof(Image0);x++) //写入字模数据
{
Write_Dat(Image0[x]);
}
Write_Com(0x48);
for(y=0;y<sizeof(Image1);y++)
{
Write_Dat(Image1[y]);
}
Write_Com(0x50);
for(z=0;z<sizeof(Image2);z++)
{
Write_Dat(Image2[z]);
}
Write_Com(0x80+0x00); //0x80+0x00等效于0x80
Write_Dat(0x00); //将CGRAM中第一个地址段存储的字模数据写入DDRAM
Write_Com(0x80+0x02);
Write_Dat(0x01);
Write_Com(0x80+0x04);
Write_Dat(0x02);
}

本文内容如有错误,请在评论区多多指教,非常感谢φ(≧ω≦*)♪。

参考资料

51单片机LCD1602程序详解

超详细讲解,LCD1602电路与驱动

基于51单片机点亮LCD1602+自定义字符

单片机 1602 CGRAM、CGROM及DDRAM的作用