折腾来折腾去

pikipity的blog

第十章 LCD

什么是LCD

LCD 就是液晶显示器,维基百科的解释在这里,以前的小液晶显示器没有背光,但是现在大多数 LCD 都会加入背光来使得显示器在没有外界光线的情况下也可以使用,但是背光的加入就让能耗上升了。

一般分为下面三种类型:

  • 文字型:内部存储器中内建有文字图形,输入对应的 ASCII code 就可以显示文字了。
  • 绘图型:内部存储器无内建文字图形,所有显示的图形都必须按照 LCD 所对应的格式进行存储。
  • 文字绘图两用型:可在上面两种形式下自由切换。

下面介绍最简单的文字型 LCD1602 为例进行介绍。LCD1602 共两行,每行可以显示16个字符。液晶显示器本身的控制非常复杂,所以我们需要外加一个芯片对其进行控制,一般情况下,在市面上买到的液晶显示器已经内置了这个控制芯片。对于字符型,一般使用 HD44780 控制芯片来进行控制。

LCD1602 接脚示意

LCD1602

按照上图从左到右的顺序,每个接脚的意义为:

  • GND:接地
  • Vcc: 电源(5V)
  • V0:LCD亮度控制,一般用一个变阻器分压的方式来控制这个接脚的电压,一次来调节 LCD 显示的亮度,0V最亮
  • RS:暂存器选择接脚。接高为资料暂存器,接低为指令暂存器
  • R/W:读写选择接脚。接高为读,接低为写。
  • E:致能 LCD 接脚
  • D0~D7:资料线,传输数据
  • Vcc: 背光电源,可以串连一个330Ω的电阻,然后之间连接到5V电源
  • GND:背光地

控制方法

LCD内部寄存器分为指令寄存器和资料寄存器。指令寄存器就是用来写入你要 LCD 执行的指令的地方,例如控制游标的移动,,控制游标的显示方式,读取忙碌旗标等等,当一条命令写入之后,LCD便会执行这条命令。资料寄存器就是用来写入希望LCD显示内容的地方,在资料输入前,必须先指定资料存储的地址,然后再输入资料的内容。

在控制的过程中要注意两点:

  1. 控制 E 来置能 LCD,要根据时序图来控制,简单来说就是:在读写前先拉低 E,再拉高 E,创造一个上升沿,读写结束后,要拉低 E。
  2. LCD 对于数据的处理速度非常慢,所以数据是否读写完毕是一个非常重要的问题,有下面两种方法可以判断数据是否读写完毕:

    1. R/W 接脚接地,一直保持写的状态,每次写完数据之后预留出足够长的时间给LCD处理信号。虽然程序和电路简单,但是预留时间长短难以控制。
    2. R/W 不接地,通过读取 LCD 的忙碌旗标来判断数据是否读取完毕。虽然程序和电路复杂,但是不用考虑预留时间的长短。

LCD模组命令

下面的命令从左到右分别对应的接脚为

RS  R/W  D7  D6  D5  D4  D3  D2  D1  D0
  1. 向指令寄存器中写入的指令: 游标指示的就是如果未重新指定资料存放的地址,新写入的资料就会在游标处显示。

    • 功能设定:

      0 0 0 0 1 DL N F X X
      

      DL: 资料长度。DL=0,高4位。DL=1,8位

      N: 可以显示的行数。N=0,一行。N=1,两行。

      F: 字形设定。F=0,5*7点阵。F=1,5*10点阵。

    • 显示器控制:

      0 0 0 0 0 0 1 D C B
      

      D: 显示器开关

      C:是否用游标显示位元位置

      B:是否让游标闪烁

    • 输入模式设定:

      0 0 0 0 0 0 0 1 I/D S
      
      S I/D 动作
      0 0 字元不动,游标左移
      0 1 字元不动,游标右移
      1 0 游标不动,字元右移
      1 1 游标不动,字元左移
    • 清除显示(显示屏清零,游标回归左上角):

      0 0 0 0 0 0 0 0 0 1
      
    • 游标归位(资料不变,仅将游标移至左上角位置):

      0 0 0 0 0 0 0 0 1 X
      
    • 游标移位控制:

      0 0 0 0 0 1 S/C R/L X X
      
      S/C R/L 动作
      0 0 游标左移
      0 1 游标右移
      1 0 整屏左移
      1 1 整屏右移
  2. 从指令寄存器读取忙碌旗标

    0 1 BF A6 A5 A4 A3 A2 A1 A0
    

    BF: 忙碌旗标

    A0~A6: 游标所在位置

  3. 向资料寄存器写入资料:资料寄存器分为两个 – CGRAM 和 DDRAM。在写入资料之前要先判断两件事:

    1. 现在游标所在的位置是否是下一笔数据要显示的位置。如果是,就直接向 DDRAM 写入资料就可以了;如果不是就移动游标到需要的位置或是从新指定 DDRAM 的地址。两行每行8个字的地址如下:

      第一行:从0x000x0F

      第二行:从0x400x4F

    2. 要显示的字符在 ASCII 码中是否有对应。如果有,直接写入对应的 ASCII 码就可以了;如果没有就需要先在 CGRAM 中将这个字符造出来,造字分为两步,首先,指定这个新字符在 CGRAM 中存放的地址,然后向这个地址中写入要造的字符就可以了。每一个字符都是一个8*8的点阵,也就是说,你要连续写入8次数据。需要注意的是,左边三列有空出来,一般为了美观,最下面一行要空出来,下面是一个笑脸的造字示意图

      存放在```0x40```的一个笑脸

      可以用来存放新字符的 CGRAM 的地址如下:0x400x480x500x580x680x700x78。要使用的时候,就和写入 ASCII 码一样,直接向 DDRAM 中写入 CGRAM 的地址就可以了。

    写入资料的时候,先写入地址(如果地址就是光标位置就不用写入地址了),然后写入资料就可以了。

    命令如下:

    • CGRAM 地址写入(由于地址的高四位至少也是4,所以A7=0,A6=1)

      0 0 0 1 A5 A4 A3 A2 A1 A0
      
    • DDRAM 地址写入(A7必须是1,后面的7位按照提到的方法确定)

      0 0 1 A6 A5 A4 A3 A2 A1 A0
      
    • 向 DDRAM 和 CGRAM 中写入资料:

      1 0 D7 D6 D5 D4 D3 D2 D1 D0
      
    • 读取 DDRAM 和 CGRAM 中的资料:

      1 1 D7 D6 D5 D4 D3 D2 D1 D0
      

例程

为了方便,这里用C语言编写例程,我们只要把命令放入一个.h头文件中,在需要的时候调用这个头文件里的函数就可以。我这里电路接法:

LCD1602例程接法

ADDR0就是P1.0,ADDR1就是P1.1,ENLCD1就是P1.5,DB0~DB7就是P0.0~P0.7。头文件 LCD.h 如下

#ifndef _LCD_H_
#define _LCD_H_

sbit ADDR0=P1^0;
sbit ADDR1=P1^1;
sbit ENCLD1=P1^5;
sbit DB0=P0^0;
sbit DB1=P0^1;
sbit DB2=P0^2;
sbit DB3=P0^3;
sbit DB4=P0^4;
sbit DB5=P0^5;
sbit DB6=P0^6;
sbit DB7=P0^7;

void waiting()
{
    P0=0xFF;
    while(DB7)
    {
        ENLCD1=0;
        ADDR0=0;
        ADDR1=1;
        ENLCD1=1;
    }
    ENLCD1=0;
}

void write_cmd(unsigned char cmd)
{
    waiting();
    ENLCD1=0;
    ADDR0=0;
    ADDR1=0;
    P0=cmd;
    ENLCD1=1;
    ENLCD1=0;
}

void write_data(unsigned char dat)
{
    waiting();
    ENLCD1=0;
    ADDR0=1;
    ADDR1=0;
    P0=dat;
    ENLCD1=1;
    ENLCD1=0;
}

void write_string(unsigned char addr,unsigned char *p)
{
    addr=addr|0x80;
    write_cmd(addr);
    while(*p!='\0')
    {
        write_data(*p++);
    }
}

void LCD_init()
{
    write_cmd(0x38);
    write_cmd(0x0C);
    write_cmd(0x06);
    write_cmd(0x01);
}

#endif

如果要输出一句"Hello World!“的话,主程序可以这样写:

#include <REG52.h>
#include <LCD.h>
#include <intrins.h> 

unsigned char code World[]={"Hello World!"};

void main()
{
    LCD_init();
    write_string(0x00,World);
    write_string(0x42,World);
    while(1);
}


Comments