欢迎光临
我们一直在努力

分析一段LCD1602显示字符代码

#include "public.h"
#include "lcd1602.h"

extern void lcd1602_write_cmd(u8 cmd);
extern void lcd1602_write_data(u8 dat);

//管脚定义
sbit LCD1602_RS=P2^6;//数据命令选择
sbit LCD1602_RW=P2^5;//读写选择
sbit LCD1602_E=P2^7; //使能信号

#define LCD1602_DATAPORT P0//宏定义LCD1602数据端口

void main()
{
u8 i=0;
u8 gshowbufa[16]="Hello World";
u8 gshowbufb[27]="welcome to the world of mcu";

lcd1602_init();
lcd1602_show_string(0,0,gshowbufa);
lcd1602_show_string(0,1,gshowbufb);
lcd1602_write_cmd(0x07);//每写一个数据屏幕就要右移一位,就相对于数据来说就是左移了
while(1)
{
lcd1602_write_cmd(0x80);
for(i=0;i<16;i++)
{
lcd1602_write_data(gshowbufa[i]);
delay_ms(500);//滚动速度调节
}
}

}

以上是一段LCD1602显示字符的主函数,其功能为:

在第一行最左侧显示"Hello Word"

在第二行最左侧显示“welcome to the world of mcu",并且字符从左往右循环滚动。

要想让LCD1602显示字符需要经过四个过程:写命令(选择功能)、写数据(装填字符)、初始化(设定功能)、显字符(屏幕显示)。这四个过程分别对应四个函数,之所以没有把这四个函数一起放在上面,是因为

太多了,会翻到怀疑人生

所以,为了便于理解,也便于查阅,我会把这四个函数以及引脚定义统一放在第三节,并一一解释它们的作用。我们先来了解一下,这个LCD1602到底是何方神圣。


一、LCD1602是什么?

LCD,也就是液晶显示屏;02代表可显示两行字符,16代表每行16个字符,因此,LCD1602实际上就是能显示2*16=32个字符的液晶显示屏。

可以看到,LCD上有32个方块,代表显示字符的区域;每个区域中又有若干个小黑片(实际上是5*7个),这个小黑片就是LCD显示的起点。这就是我们要讲的第一个问题

液晶显示原理

当液晶两端不给电压时,近似为透明(完全透光);当液晶两端给电压时,近似为黑灰(完全不透光),这种电压变化会引起液晶的透明度也就是亮度变化,因此很多单片机会在LCD1602旁边设置一个可调电阻,用于调整液晶两端电压,从而控制液晶对比度。

认识字符显示区域的小黑片之后,我们再把视角拉远,看看一个字符区域是怎么工作的。

字符显示原理

LCD1602字符区域有5*7和5*10两种,可以通过初始化设置是哪种,这个我们会在初始化函数中再次见到它。现在主要看5*7,一块字符区域可以看作是7行5列,当行方向通低压,列方向通高压时,对应的小黑片(也就是像素)就会显示

以字母“A”为例,将A按水平方向切成七份,水平方向从上到下依次给低电平(也称作扫描),垂直方向按预先设定好的方式给高电平,连起来就是

基于这样的原理,一块显示区域需要5+7=12个引脚,即使把行方向的7个引脚全部复用,32个显示区域也需要2*7+32*5=174个引脚,单片机看到这么多引脚直接燃尽了。

为此,人们想出在LCD1602中专门再做一个处理器,专门给LCD提供高低压信号,以减轻单片机的引脚负担,这个处理器就叫做HD44780U,相当于计算机中的显卡,它就在LCD1602背面,稍大的圆盘。

HD44780U有16个COM口和40个SEG口,分别连接字符区域的行方向和列方向。这样,两排字符的行方向通过复用,16个COM口是完全足够的,但是列方向需要16*5=80个引脚,而HD44780U只有40个SEG口,剩下的40个引脚就管不了了吗?

当然不是,所以大圆盘旁边配备了一个帮手HD44100H,它也有40个SEG口,刚好补足前者在SEG的空缺,两者连起来,就可以控制整个LCD1602上的字符显示。

我们梳理一下,LCD1602显示的基本单位是电压控制透明度的小液晶,HD44780U在HD44100H的辅助下,控制整块屏幕32个字符区域显示的内容。当然,这只是LCD1602内部的一套规则,单片机要想控制它,必须借助8个数据引脚,这就是下一节的主角D7~D0。


二、单片机如何控制LCD1602?

LCD1602共有16个引脚,我将它们分成四部分:电源引脚、控制引脚、数据引脚、背光板

电源引脚:VSS,VDD分别是LCD1602的电源和接地端;V0是LCD1602两端电压,通过控制V0可以调节液晶显示的对比度。总之,这三个端口是不需要在代码中专门设定的,所以了解一下就行。

控制引脚:RS(寄存器选择)、RW(读写控制)、E(使能),这三个引脚在写指令函数中扮演重要角色,这里就先不提,因为它们不是这一节的主角。

背光板引脚:A、K,这部分与电源引脚一样,不需要在代码中专门设定,只是控制背光板亮灭。

数据引脚:D0~D7,这八个引脚与LCD1602的主控芯片相连,单片机通过向D0~D7输入数据来控制LCD1602的主控芯片,从而控制液晶屏的显示。

单片机控制LCD1602显示无非是两个问题:显示什么?在哪显示?

“显示什么”需要用到LCD1602的一个寄存器

CGRAM(字符发生存储器)

CGRAM预先把240个常用字符对应的高低压信号存起来,并且用ASCII码作为这些信号的索引。比如显示“B”,我们向数据引脚输入0100 0010,对应于下图的“B”(注意最上一行是高四位,最左一行是低四位)

“在哪显示”需要LCD1602的另一个寄存器

DDRAM(显示数据存储器)

为了更精准地让指定区域显示指定字符,HD44780U为每块字符区域都设计了一个字节DDRAM。所谓DDRAM,就相当于计算机的显存,先指定某一块字符区域,再向其输送字符信息。一共两行,每行40个字节,共80个字节。两行显示方式下,DDRAM地址范围为:第一行00H~27H,第二行40H~67H。

LCD1602显示最核心的CGRAM和DDRAM我们都了解了一遍,可以明确其显示的逻辑顺序是:

查ASCII表确定字符——设置数据存储器地址(DDRAM)——输入ASCII码——CGRAM生成字符

在熟练掌握LCD1602内部运作规律后,我们再回头看看开头没有写出但十分重要的四大函数。


三、写命令、写数据、显字符、初始化

这四个函数按重要性排序,先讲讲使用频率最高的写命令函数

1、写命令

void lcd1602_write_cmd(u8 cmd)
{
LCD1602_RS=0;//选择命令
LCD1602_RW=0;//选择写
LCD1602_E=0;
LCD1602_DATAPORT=cmd;//准备命令
delay_ms(1);
LCD1602_E=1;//使能脚E先上升沿写入
delay_ms(1);
LCD1602_E=0;//使能脚E后负跳变完成写入
}

写命令首先要将RS和RW置0,这是因为所有指令(除了写数据和读数据外)的前两位都置0;然后再给E一个上升沿,相当于按下了“确认键”,确认执行这个指令。以下是LCD1602的指令表:

清屏不用多说,我们看一下几个常用指令该如何使用:

I/D(光标移动方向,高右移,低左移);

S(所有文字是否移动,高是,低否)

D(是否开启显示功能,高开,低关);

C(是否有光标,高是,低否);

B(光标是否闪烁,高是,低否)

S/C(光标移动方式,高平光标独立移动,低平光标与显示一起移动);

R/L(光标移动方向,低平向左移动,高平向右移动)

DL(控制数据接口宽度,高平8位,低平4位);

N(显示行数,高平显示两行,低平显示一行);

F(点阵大小,高平5*10点阵,低平5*7点阵)

2、写数据

void lcd1602_write_data(u8 dat)
{
LCD1602_RS=1;//选择数据
LCD1602_RW=0;//选择写
LCD1602_E=0;
LCD1602_DATAPORT=dat;//准备数据
delay_ms(1);
LCD1602_E=1;//使能脚E先上升沿写入
delay_ms(1);
LCD1602_E=0;//使能脚E后负跳变完成写入
}

RS高电平选择数据,低电平选择指令;RW高电平读,低电平写。

因此,RS置1,RW置0就表示写数据。这个函数关键问题是

这个数据是什么?

这个问题好像有点奇怪,但是是必要的。写数据中的数据,实际上就是字符数据。比如说,我想显示字母“A”,首先指定地址,下一步就是lcd1602_write_data("A")。ASCII表已有的字符,输入时会直接转换为ASCII码,然后CGRAM会生成对应字符。但是要注意,写数据函数只管显示字符,不管显示位置,因此需要下面这个函数来指定字符显示位置。


3、显字符

void lcd1602_show_string(u8 x,u8 y,u8 *str)
{
u8 i=0;

if(y>1||x>15)return;//行列参数不对则强制退出

if(y<1)//第1行显示
{
while(*str!='\\0')//字符串是以'\\0'结尾,只要前面有内容就显示
{
if(i<16-x)//如果字符长度超过第一行显示范围,则在第二行继续显示
{
lcd1602_write_cmd(0x80+i+x);//第一行显示地址设置
}
else
{
lcd1602_write_cmd(0x40+0x80+i+x-16);//第二行显示地址设置
}
lcd1602_write_data(*str);//显示内容
str++;//指针递增
i++;
}
}
else//第2行显示
{
while(*str!='\\0')
{
if(i<16-x) //如果字符长度超过第二行显示范围,则在第一行继续显示
{
lcd1602_write_cmd(0x80+0x40+i+x);
}
else
{
lcd1602_write_cmd(0x80+i+x-16);
}
lcd1602_write_data(*str);
str++;
i++;
}
}
}

x表示字符显示的起始位置,y表示字符显示行位置(第一行或第二行)。由于第一行显示与第二行显示代码整体上是一样的,所以我们缩小范围,看看第一行显示时的核心代码。

while(*str!='\\0')//字符串是以'\\0'结尾,只要前面有内容就显示
{
if(i<16-x)//如果字符长度超过第一行显示范围,则在第二行继续显示
{
lcd1602_write_cmd(0x80+i+x);//第一行显示地址设置
}
else
{
lcd1602_write_cmd(0x40+0x80+i+x-16);//第二行显示地址设置
}
lcd1602_write_data(*str);//显示内容
str++;//指针递增
i++;
}

C语言中字符串以'\\0'结尾,它是字符串的终止标志。所以,while(*str!='\\0')的含义是执行到字符串结尾,循环的次数就是字符串长度。接下来看一次循环中都发生了什么:

若设定x=0,即字符串从最左边开始显示,此时if条件满足,执行if语句中的

lcd_1602_write_cmd(0x80+i+x);

x=0忽略不计,i=0,在第二节DDRAM部分了解过,0x80表示第一行第一列的地址,再看下一段

lcd1602_write_data(*str);

*str表示字符串str的指针,此时指向str的第一个字符。结合上面的写函数,就是在第一行第一列(0x80)显示str字符串的第一个字符。接下来是赋值。

str++;
i++;

表明指针从字符串第一位指向了第二位,同时i++表明显示地址从0x80变到0x81,右移一位。

我们来总结一下这个函数的流程:

判断字符是否结尾——设置显示地址——显示字符——地址+1,指针+1——设置下一个地址和下一个字符

4、初始化

void lcd1602_init(void)
{
lcd1602_write_cmd(0x38);
lcd1602_write_cmd(0x0c);
lcd1602_write_cmd(0x06);
lcd1602_write_cmd(0x01);
}

初始化只有四条指令,我们结合前面指令表对应着看看都初始化了哪些功能。

0x38——0011 1000

DL置1,数据接口宽度为8;N置1,显示两行字符;F置0,点阵大小为5*7

0x0c——0000 1100

D置1,开启显示功能;C置1,显示光标;B置0,光标不闪烁

0x06——0000 0110

I/D置1,光标向右移动;S置0,文字不移动

初始化函数相当于为LCD显示设定了功能,即8位数据接口,2行显示,5*7规格的带光标的显示屏

四、写在最后

感谢你能看到这里,LCD1602虽然时至今日仍然很受欢迎,但与当今热门的显示屏相比已经算是垂暮老人了,市面上好一点的计算机,显存能达到25GB,而LCD1602的显存DDRAM只有80B。除了存储劣势外,LCD1602还极易出问题,比如LCD亮但只有一排白方块,或者干脆显示乱码。只有真正去了解这款上世纪80年代的产物,才能体会到当初人们不断追求更高更快设备的野心和追求。LCD1602我们尚可驾驭,那么更快更强的显示器我们一定会得心应手。

赞(0)
未经允许不得转载:171主机测评 » 分析一段LCD1602显示字符代码
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址