2.9 I/O口控制1602液晶显示应用实例

2.9.1 液晶屏基础知识

液晶的组成物质是一种有机化合物,是以碳为中心所构成的化合物,在常温下,液晶是处于固体和液体之间的一种物质,具有固体和液体物质的双重特性,利用液晶体的电光效应制作的显示器就是液晶显示器(LCD)。

LCD显示器需要由驱动芯片控制,控制芯片已经集成在显示器中了。最流行的驱动芯片是Hitachi(日立)公司的HD44780(或其他相应的芯片)。

LCD由灰色的像素组成。排列得非常小的灰点组成字符。一个典型的16×2LCD可在两行中显示16个字符,每个字符由5个像素宽、8个像素高组成。如果在显示器上把对比度设得很高,32组5×7像素将变得可见。

以TN型液晶显示器为例,将上下两块制作有透明电极的玻璃,利用胶框对四周进行封接,形成一个很薄的盒。在盒中注入TN型液晶材料。通过特定工艺处理,使TN型液晶的棒状分子平行地排列于上下电极之间,如图2-30所示。

图2-30 TN液晶显示器的基本构造

根据需要制作成不同的电极,就可以实现不同内容的显示。平时液晶显示器呈透亮背景,电极部位加电压后,显示黑色字符或图形,这种显示称“正显示”。如将图2-30中下偏振片转成与上偏振片的偏振方向一致装配,则正相反,平时背景呈黑色,加电压后显示字符部分呈透亮,这种显示称为“负显示”。后者适用于背光源的彩色显示器件。

2.9.2 1602 LCD液晶屏

1602字符液晶是最常用的一种,很具有代表性,其实物图2-31所示。最初的1602液晶使用的是HD44780控制器,现在各个厂家的1602模块基本上都是采用了与之兼容的IC,所以特性上基本都是一致的。

图2-31 1602 LCD液晶屏的实物

(1)1602 LCD显示屏概述

①技术参数

a.显示容量为16×2个字符;

b.芯片工作电压为4.5~5.5V;

c.工作电流为2.0mA(5.0V);

d.模块最佳工作电压为5.0V;

e.字符尺寸为2.95mm×4.35mm(W×H)。

②各引脚定义 1602 LCD采用标准14脚(无背光)或16脚(带背光)接口,各引脚接口说明如表2-16所示。

表2-16 各引脚说明

第1脚:VSS为地电源。

第2脚:VDD接5V正电源。

第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最低,接地时对比度最高。对比度过高时会产生“鬼影”,使用时可以通过一个10kΩ的电位器调整对比度。

第4脚:RS为寄存器选择,高电平时选择数据寄存器,低电平时选择指令寄存器。

第5脚:R/W为读写信号线,高电平进行读操作,低电平进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平、R/W为高电平时可以读忙信号,当RS为高电平、R/W为低电平时可以写入数据。

第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

第7~14脚:D0~D7为8位双向数据线。

第15脚:背光源正极。

第16脚:背光源负极。

(2)1602 LED液晶屏的指令及时序 1602液晶模块内部的控制器共有11条控制指令,如表2-17所示。

表2-17 1602控制指令表

1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。表2-17中,1为高电平,0为低电平。

指令1:清除显示,指令码01H,光标复位到地址00H位置。

指令2:光标复位,光标返回到地址00H。

指令3:光标和显示模式设置。I/D表示光标移动方向,高电平右移,低电平左移,S表示屏幕上所有文字是否左移或右移。高电平表示有效,低电平则无效。

指令4:显示开关控制。D用于控制整体显示的开、关,高电平表示开显示,低电平表示关显示。C用于控制光标的开、关,高电平表示有光标,低电平表示无光标。B用于控制光标是否闪烁,高电平闪烁,低电平不闪烁。

指令5:光标或显示移位。S/C用于在高电平时移动显示的文字,低电平时移动光标。

指令6:功能设置命令。DL在高电平时为4位总线,低电平时为8位总线。N在低电平时为单行显示,高电平时双行显示。F在低电平时显示5×7的点阵字符,高电平时显示5×10的点阵字符。

指令7:字符发生器RAM地址设置。

指令8:DDRAM地址设置。

指令9:读忙信号和光标地址。BF为忙标志位,高电平表示忙碌,此时模块不能接收命令或者数据,如果为低电平表示空闲。

指令10:写数据。

指令11:读数据。

与HD44780相兼容的芯片时序表如表2-18所示。

表2-18 基本操作时序表

读写操作时序如图2-32和图2-33所示。

图2-32 读操作时序

图2-33 写操作时序

(3)1602 LCD液晶屏的RAM地址映射及标准字库表 液晶显示模块显示较慢,所以在执行每条指令之前一定要确认模块的忙标志是否为低电平,如果忙标志为低电平则表示空闲,否则此指令失效。显示字符时要先输入显示字符地址,即告诉模块在哪里显示字符,如图2-34是1602 LCD的内部显示地址。

图2-34 1602 LCD内部显示地址

例如,第二行第一个字符的地址是40H,那么是否直接写入40H就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位D7恒定为高电平1,所以,实际写入的数据应该是01000000B(40H)+10000000B(80H)=11000000B(C0H)。

在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标自动右移,无须人工干预。每次输入指令前都要判断液晶模块是否处于忙碌状态。

1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形。这些字符包括阿拉伯数字、英文字母的大小写、常用的符号、日文假名等,每一个字符都有一个固定的代码,例如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,就能看到字母“A”。

(4)1602 LCD的一般初始化(复位)过程 1602 LCD的初始化过程如图2-35所示,详细过程如下。

图2-35 1602 LCD初始化过程

延时15ms。

写指令38H(不检测忙信号)。

延时5ms。

写指令38H(不检测忙信号)。

延时5ms。

写指令38H(不检测忙信号)。

以后每次写指令、读/写数据操作均需要检测忙信号。

写指令38H:显示模式设置。

写指令08H:显示关闭。

写指令01H:显示清屏。

写指令06H:显示光标移动设置。

写指令0CH:显示开及光标设置。

2.9.3 硬件设计

1602 LCD与ATmega128单片机的硬件连接如图2-36所示,其中,1602 LCD的AD0~AD7与ATmega128单片机的端口A相连,控制端口RS、RW、EC分别与端口F的PF1、PF2、PF3相连。

图2-36 1602 LCD与ATmega128接线原理图

2.9.4 程序设计

以下为1602 LCD的显示程序。程序使用ICCAVR环境编译,硬件调试通过。程序使1602 LCD显示两行字符:第一行显示“welcome to”,第二行显示“qing dao”。

目的:1602液晶显示。

功能:1602液晶显示。

时钟频率:内部1MHz。

编译环境:ICC-AVR6.31。

使用硬件:1602液晶。

结果:1602液晶第一行显示“welcome to”,第二行显示“qing dao”。

操作要求:调节液晶对比度旋钮R28使液晶达到最佳显示。

程序清单如下:

(1)头文件部分

    # include< string.h>
    # include< stdio.h>
    # include< delay.h>
    # include< iom128v.h>

(2)宏定义部分

    # define uchar unsigned char
    # define uint unsigned int
    # define RS_CLR PORTF &= ~(1< < PF1)    //RS置低
    # define RS_SET PORTF 1= (1< < PF1)       //RS置高
    # define RS0CLR PORTF&= ~(1< < PF2)    //RW置低
    # define RW_SET PORTF 1= (1< < PF2)    //RW置高
    # define EN_CLR PORTF&= ~(1< < PF3)    //E置低
    # define EN_SET PORTF|~(1< < PF3)      //E置高
    # define Datd - IO  PORTA             //液晶数据口
    # define Data_DDR  DDRA             //数据口方向寄存器
    # define D_LE0    PORTD&= ~(1< < PD4) //数码管段控制位为0,锁存端口数据
    # define D_LE1    PORTAD|= (1< < PD4) //数码管段控制位为 1,锁存器输出与端口一致
    # define W_LE0    PORTD &= ~(1< < PD5) //数码管位控制位为 0
    # define W_LE1    PORTD|= (1< < PD5)  //数码管位控制位为 1

(3)微秒级延时函数

    void delay_us(unsigned int n)
      {
        if(n= = 0)
        {
        return;
        }
      while(- - n);
      }

(4)毫秒级延时函数

    void delay_ms(unsigned char i)
    {
    unsigned char a,b;
    for (a= 1;a< i;a+ + )
      {
    for (b= 1;b;b+ + )
    {  ;  }
      }
    }

(5)显示屏命令写入函数

    void LCD_write_com(unsigned char com)
    RS_CLR;
    RW_CLR;
    EN_SET;
    PORTB= com;
    delay_us(5);
    EN_CLR;
    }

(6)显示屏数据写入函数

    void LCD_write_data(unsigned char data)
    {
    RS_SET;
    RW_CLR;
    EN_SET;
    PORTB= data;
    DELAY_US(5);
    EN_CLR;
    }

(7)显示屏清空显示函数

    void LCD_cear(void)
    {
    LCD_write_com(0x01);
    Delay_ms(5);}

(8)显示屏字符串入函数

    void LCD_write_atr(unsigned,char x,unsigned char y,unsigned char*s)
    {
      if(y= = 0)
      {
      LCD_write_com(0x80+ X);
      }
      else
      {
      LCD_write_com(0xC0+ x);
      }
      while(*s)
        {
      LCD_write_data(*s)
        s+ + ;
        }
      }

(9)显示屏单字符写入函数

    void LCD_write_char(unsigned char x,unsigned char y,unsigned char data)
    {
        if(y= = 0)
        {
        LCD_write_com(0x80+ x);
        }
        else
        {
        LCD_write_com(0Xc0+ x);
        }
        LCD_write_data(darta);
        }

(10)显示屏初始化函数

    void LCD_init(void)
    {
        DDRA= 0xAFF;             //I/O口方向设置
        DDRA|= (1< < PA5)|(1< < PA6) (1< < PA7);
        LCD_write_com(0x38);    //显示模式设置
        delay - nms(5);
        LCD_write_com(0x08);    //显示关闭
        delay_nms(5);
        LCD_write_com(0x01);    //显示清屏
        delay_nms(5);
        LCD_write_com(0x06);    //显示光标移动设置
        delay_nms(5);
        LCD_write_com(0x0C);    //显示开及光标设置
        delay_nms(5);
        }

(11)初始化子程序

    void system_inie()
    {
      Data_IO= 0xFF;         //电平设置
      Data_DDR= 0xFF;       //方向输出
      PORTF= 0xFF;           //电平设置
      PORTD= 0xFF;           //方向输出
      DDRD= 0xFF;
      D_LE1;                 //关闭数码管,以免显示乱码
      W_LDE1;
      Data_IO= 0xFF;           //关闭数码管
      W_LE0;
      }

(12)主函数

    void main(void)
    {
        unsingned char i;
        unsigned char *p;
    system_initr();                 //系统初始化,设置 I/O口属性
        delay_nms(100);           //延时 100ms
        LCD_init();               //液晶参数初始化设置
    while(1)
    {    i= 1;
        P= qing dao;             //字符串输出显示
        LCD_clear();
        LCD_write_str(0,0,welcme to);
        delayt_nms(250);
        while(*p) {
          LCD_write_char(i,1,*p);  //单个字符输出显示
          i+ + ;
          p+ + ;
          delay_nms(250);        //延时 250ms
          }
          Delay_nms(250);
          }
      }