上拉电阻的作用
在嵌入式系统的设计中,常常使用LED的亮灭来表示信息,尤其是一些状态信息。然而,如果需要表示比较复杂的信息(例如显示温度),又如果需要做成最终的商业产品,那么使用液晶显示器则是不二之选。
液晶显示器可以分为字符型液晶显示器和图形液晶显示器。字符型液晶显示器只能在一些预定的位置显示预定义的若干种字符,而图形液晶则可以控制任意一个像素的亮灭。
这里介绍最简单也最常用的字符型液晶显示器LCD1602,外观如图:
1.jpg
2.jpg
之所以叫做LCD1602,是因为这种字符型显示器,能显示16×2个字符,即每行16个字符,共2行。
再来看一下LCD1602的引脚图:
3.jpg
依次为:
1、VSS,接地;
2、VCC,接电源正极(+5V);
3、V0,对比度调节,通过输入的电压的大小可以调节字符显示的对比度,当V0接电源时,对比度最弱,当V0接地时,对比度最强;
4、RS,数据/指令寄存器选择端,当RS=1时,将对数据寄存器进行读写(比如写入一个字符,或读取是否处于忙状态),当RS=0时,将对指令寄存器进行读写(比如命令清屏,命令光标闪烁);
5、R/W,读写选择端,当R/W=1时,表示进行读操作,当R/W=0时,表示进行写操作;通常RS与R/W一起使用,控制是数据寄存器读(RS=1,R/W=1)、对数据寄存器写(RS=1,R/W=0)、对指令存储器读(RS=0,R/W=1)、对指令存储器写(RS=0,R/W=0);
6、E,使能端,当单片机准备好要写入的指令或数据时,通过让E产生一个负跳变(从高电平变为低电平),就可以把指令或数据写入LCD1602,而让E维持高电平时,LCD1602会把数据通过DB0~DB7输出;
7~14、DB0~DB7组成一个字节,与单品机的一组输出引脚(P0、P1、P2、P3等)相连,相当于一个并口输入输出;
15~16、BLA和BLK分别是背光正极与背光负极,它们之间的电压差决定了LCD1602背光亮度。
再来看看LCD1602与单片机的接口电路。本文采用Proteus这款电路仿真软件。在Proteus中,选用LM016L(Proteus中的LCD1602的型号)和AT89C2051单片机(当然,选用AT89C51或 AT89C52或其他的各种兼容的单片机都可以,本文使用AT89C2051仅仅是因为这款引脚少,看的清楚)。接口电路图如下:
4.png
好了,硬件上的事情已经OK啦,接下来看看软件上的事情。
首先是LCD1602的操作时序。在一般的使用中,只需要用到向LCD1602写入指令和数据,几乎用不到读取数据的操作。方便起见,本文就只介绍写入指令和数据的操作。
写入指令的步骤:
1、设置E=0;
2、设置RS=0(即操作指令寄存器);
3、设置R/W=0(即写入);
4、设置并口DB0~DB7(DB0~DB7表示字节即为要写入的指令);
5、设置E=1(先拉高,为了待会儿拉低,产生一个负跳变);
6、设置E=0(拉低,产生一个负跳变);
经过以上6步,DB0~DB7组成的字节所表示的指令即被写入LCD1602。需要注意的是,为了避免由于LCD1602处理速度太慢而导致的失败,最好的每一个步骤前适当延时一下(具体延时看代码)。
写入数据也是类似的,唯一的区别就是设置RS=1:
1、设置E=0;
2、设置RS=1(即操作数据寄存器);
3、设置R/W=0(即写入);
4、设置并口DB0~DB7(DB0~DB7表示字节即为要写入的指令);
5、设置E=1(先拉高,为了待会儿拉低,产生一个负跳变);
6、设置E=0(拉低,产生一个负跳变);
现在就可以把写入指令和写入数据的操作封装成函数,以方便接下来使用。
+++code
#include <reg51.h>

typedef unsigned char uint8;
typedef unsigned char bool;
typedef unsigned int uint16;

#define true 1
#define false 0

sbit LCD1602_RS=P3^4;
sbit LCD1602_RW=P3^3;
sbit LCD1602_E=P3^2;
#define LCD1602_PORT P1

static void lcd1602_delay(uint16 p_time)
{
    while(p_time--);
}

static void lcd1602_write_cmd(uint8 p_cmd)
{
    lcd1602_delay(100);
    LCD1602_E=0;
    lcd1602_delay(10);
    LCD1602_RS=0;
    lcd1602_delay(10);
    LCD1602_RW=0;
    lcd1602_delay(10);
    LCD1602_PORT=p_cmd;
    lcd1602_delay(10);
    LCD1602_E=1;
    lcd1602_delay(50);
    LCD1602_E=0;
}

static void lcd1602_write_data(uint8 p_data)
{
    lcd1602_delay(100);
    LCD1602_E=0;
    lcd1602_delay(10);
    LCD1602_RS=1;
    lcd1602_delay(10);
    LCD1602_RW=0;
    lcd1602_delay(10);
    LCD1602_PORT=p_data;
    lcd1602_delay(10);
    LCD1602_E=1;
    lcd1602_delay(50);
    LCD1602_E=0;
}
---code
其中实现的lcd1602_write_cmd和lcd1602_write_data分别实现了向LCD1602写入一个字节的指令与写入一个字节的数据的功能。
接下来就是要介绍一下如何通过指令来使用LCD1602的各个功能。以下7个函数是我封装完的几个最常用的功能,可以直接使用。如果接口电路不同,只需要修改上面的引脚定义部分:
+++code
sbit LCD1602_RS=P3^4;
sbit LCD1602_RW=P3^3;
sbit LCD1602_E=P3^2;
#define LCD1602_PORT P1
---code
各个功能与相应的代码如下:
1、初始化为16×2字符,每个字符5×8点阵的形式:
+++code
void lcd1602_init()
{
    uint8 t_cmd=0x38;
    lcd1602_write_cmd(t_cmd);
    t_cmd=0x06;
    lcd1602_write_cmd(t_cmd);
}
---code
2、显示/隐藏字符、显示/隐藏光标、闪烁/静止光标:
+++code
void lcd1602_display_switch(bool p_show_char,bool p_show_cursor,bool p_flash_cursor)
{
    uint8 t_cmd=0x08;
    if(p_show_char)
    {
        t_cmd|=0x04;
    }
    if(p_show_cursor)
    {
        t_cmd|=0x02;
    }
    if(p_flash_cursor)
    {
        t_cmd|=0x01;
    }
    lcd1602_write_cmd(t_cmd);
}
---code
3、向左/向右移动光标:
+++code
void lcd1602_cursor_shift(bool p_right)
{
    uint8 t_cmd;
    if(p_right)
    {
        t_cmd=0x14;
    }
    else
    {
        t_cmd=0x10;
    }
    lcd1602_write_cmd(t_cmd);
}
---code
4、清屏:
+++code
void lcd1602_clear()
{
    uint8 t_cmd=0x01;
    lcd1602_write_cmd(t_cmd);
}
---code
5、光标归位:
+++code
void lcd1602_cursor_return()
{
    uint8 t_cmd=0x02;
    lcd1602_write_cmd(t_cmd);
}
---code
6、在指定坐标设置字符(p_x=0~15,p_y=0~1):
+++code
void lcd1602_char_set(uint8 p_x,uint8 p_y,char p_char)
{
    uint8 t_cmd=0x80;
    p_x&=0x0f;
    p_y&=0x01;
    t_cmd|=p_x;
    if(p_y)
    {
        t_cmd|=0x40;
    }
    lcd1602_write_cmd(t_cmd);
    lcd1602_write_data(p_char);
}
---code
7、从指定位置开始设置字符串(p_x=0~15,p_y=0~1,p_string必须是以0结尾的字符串):
+++code
void lcd1602_string_set(uint8 p_x,uint8 p_y,const char* p_string)
{
    uint8 t_x,t_y;
    p_x&=0x0f;
    p_y&=0x01;
    for(t_y=p_y;t_y<2;t_y++)
    {
        for(t_x=p_x;t_x<16&&(*p_string)!=0;t_x++)
        {
            lcd1602_char_set(t_x,t_y,*(p_string++));
        }
    }
}
---code
可以使用如下的测试程序
+++code
#include <reg51.h>
#include "lcd1602.h"

void main()
{
    lcd1602_init();
    lcd1602_display_switch(true,true,true);
    lcd1602_string_set(0,0,"hello");
    lcd1602_string_set(4,1,"world!");
    while(1);
}
---code
能够看到结果:
5.jpg