单片机学习程序员今日看点

51单片机实战:液晶显示器のLCD1602

2017-01-27  本文已影响1085人  兔子泽

文章框架

文章框架

前言

好吧,最终我还是决定把LCD和串口通信分开写。

首先祝各位新春快乐,鸡年大吉。上班的事业有成,上学的天天向上。过大年呢,还真没啥心情码字。

借着爆竹声咱扯会儿LCD(液晶显示器,Liquid Crystal Display)。今天要实战的这款俗称为LCD1602,尤其注意这个1602,他说明了这款显示器的显示能力:每行16个字符,共2行,乃字符显示器(仅ASCII)。


参数

显示容量|芯片工作电压|工作电流|模块最佳工作电压
:---:|:---:|:---:|:---:|:---:
16×2 Char|4.5~5.5V|2.0mA(5.0V)|5.0V

引脚

Vss Vdd VO RS R/W E D0~7 BLA BLK
接地 正极 对比度调节 数据(H)/命令(L)选择端 读(H)/写(L)选择端 使能(Enable)信号 数据口 背光电源正极 背光电源负极

LCD上也有一个单片机,用于控制屏幕显示。我们并不是直接操作那块屏幕,而是与那个单片机交互。
其中D0~7这8个数据口就是用于交互的,为并行传输

指令

因为我们要和LCD内嵌的单片机交互,所以需要指令。下面所列的东西都是当RS为低电平时发送的(若为高电平,就识别为数据)。

第一行 第二行
0x80 0xC0 (0x80+0x40)

模式:

指令 功能
0x38 设置16×2显示,5x7点阵,8位数据接口

方式:

0 0 0 0 1 D C B
||||Display,1:开显示 Cursor,1:显示光标 Blink,1:光标闪烁
0 0 0 0 0 1 N S
|||||Next,1:读/写一个字符后,指针自动加1 Shift,1:写字符时,相对字符静止的屏幕移动

清屏:

指令 功能
0x01 数据指针清0且所有指针清空
0x02 仅数据指针清0
指令 功能
0x10 光标左移
0x14 光标右移
0x18 整体左移
0x1c 整体右移

电平

简单说下,逻辑电路中只有高电平和低电平,也就是程序里面的1和0。但是在物理层面上,它需要一个具体的表现,然后整理成标准。
TTL就是本例中要用到的一种电平,单片机和LCD通过引脚传递电信号,从而达到1和0的传递。
TTL中的低电平(0)表现为0V,高电平(1)表现为5V


实例

界内显示

电路

在Proteus里,1602就是LM016L,除了没有背光灯电源外用法一致(VEE是对比度调节,本例不用)。
RP1为上拉电阻,用于提高电压。由于这款单片机的P0引脚组的电压低于5V,所以需要上拉至5V,达到TTL的标准。
那个圆形的刻着Volts字样的东西是电压表,连接两个没被上拉电压的P0引脚,具体数值看后面的演示图。

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
    
//coefficient of 1602 display(16 row 2 col. 5*7px per Char)
#define LCD_CLEAR 0x01      //宏定义:清屏
#define DISPLAY_MODE_1602 0x38  //宏定义:1602显示模式

#define DISPLAY_OFF 0x08  //宏定义:关显示
#define DISPLAY_ON_NO_CURSOR 0x0c  //宏定义:开显示且无光标
#define DISPLAY_ON_WITH_CURSOR_NO_BLINK 0x0e  //宏定义:开显示且有光标但不闪烁
#define DISPLAY_ON_WITH_CURSOR_BLINK 0x0f  //宏定义:开显示且有光标且闪烁

#define AUTO_BACK_STEP 0x04  //宏定义:读/写时指针自动减1
#define AUTO_NEXT_STEP 0x06  //宏定义:读/写时指针自动加1
#define AUTO_DISPLAY_MOVE_LEFT 0x07  //宏定义:字符相对静止,整屏左移
#define AUTO_DISPLAY_MOVE_RIGHT 0x05  //宏定义:字符相对静止,整屏右移

#define ALL_MOVE_LEFT 0x18  //宏定义:屏幕左移
#define ALL_MOVE_RIGHT 0x1c  //宏定义:屏幕右移
#define CURSOR_MOVE_LEFT 0x10  //宏定义:光标左移
#define CURSOR_MOVE_RIGHT 0x14  //宏定义:光标右移

#define FIRST_ROW 0x80  //宏定义:第一行头地址
#define SECOND_ROW FIRST_ROW+0x40  //宏定义:第二行头地址


uchar code fst[] = "Hello World!";  //第一行要显示的数据数组
uchar code sec[] = "";  //第二行要显示的数据数组
uchar num;  //字符计数
sbit enable = P0^5;  //使能端
sbit RS = P0^7;  //数据/命令切换
sbit RW = P0^6;  //读/写切换

sbit anode = P0^0;  //连接电压表阳极
sbit cathode = P0^1;  //连接电压表阴极

//粗制的延时器,没走一次这个函数大约为1ms,适用于11.0592MHz及附近
void delay(uint z)
{
    uint x,y;
    for(x=z;x>0;x--)
        for(y=110;y>0;y--);
}

//写命令
void writeCmd(uchar cmd)
{
    RS = 0;  //切换为写命令模式
    P2 = cmd;
    delay(1);  //注意
    enable = 1;  //执行!
    delay(1);  //注意
    enable = 0; //执行完毕!
}

void writeDat(uchar dat){
    RS = 1; //切换为数据模式
    P2 = dat;
    delay(1);  //注意
    enable = 1;
    delay(1);  //注意
    enable = 0;
}

//初始化函数
void init(){
    anode = 1;
    cathode = 0;
    
    RW = 0;  //写模式,本例只往LCD写数据
    enable = 0;
    writeCmd(DISPLAY_MODE_1602);  //发送命令:1602模式
    writeCmd(DISPLAY_ON_WITH_CURSOR_BLINK);  //发送命令:开始显示并闪烁光标
    writeCmd(AUTO_NEXT_STEP);  //发送命令:数据指针自动加1
    writeCmd(LCD_CLEAR);  //发送命令:清屏
}

void main(){
    init();
    writeCmd(FIRST_ROW);  //发送命令:开始从第一行写入
    for(num=0;num<=12;num++){
        writeDat(fst[num]);  //发送数据,每次一字节
    }
    while(1);
}

好,说一下上面代码中标//注意的地方,全都是delay(1)
为了什么呢,不是蛋疼,是因为单片机给LCD传送信号时,数据是要放在数据线上的,要是LCD还没读完单片机给它发的啥就把内容撤走的话,就会造成数据丢失。
就是这个道理,为了传输稳定,所以延时一小会儿。这个延时的数值需要大家自己去试,并不一定所有的情况都延时大约1ms就够的。

GIF.gif

越界显示

本例用于显示字符数超过16个的情况。
代码改动

uchar code fst[] = "1234567890ABCDEF";  //第一行要显示的数据数组
uchar code sec[] = "1234567890ABCDEFGHIJK";  //第二行要显示的数据数组
void init(){
    anode = 1;
    cathode = 0;
    
    RW = 0;
    enable = 0;
    writeCmd(DISPLAY_MODE_1602);
    writeCmd(DISPLAY_ON_NO_CURSOR);  //换成不闪的,虽然跟这个新需求没什么联系,就是给你演示下效果
    writeCmd(AUTO_NEXT_STEP);
    writeCmd(LCD_CLEAR);
}
void main(){
    init();
    writeCmd(FIRST_ROW);  //发送命令:开始从第一行写入
    for(num=0;num<=16;num++){
        writeDat(fst[num]);
    }
    writeCmd(SECOND_ROW);  //发送命令:开始从第二行写入
    for(num=0;num<=20;num++){
        writeDat(sec[num]);
    }
    while(1){
        //向左移动三次,每次间隔500ms
        for(num=0;num<=3;num++) {
            writeCmd(ALL_MOVE_LEFT);  //发送命令:整屏左移
            delay(500);
        }
        delay(1000);  //暂停大约1s
        //向右移动三次,每次间隔500ms
        for(num=0;num<=3;num++) {
            writeCmd(ALL_MOVE_RIGHT);  //发送命令:整屏右移
            delay(500);
        }
        delay(3000);  //暂停大约3s后开始下一轮
    }
}

效果

效果
移屏只移3个字符距离,所以并没有把第二行的K显示出来。

结语

这次单讲LCD的入门应用,送给不爱看春晚的你。前两天搞定了科二考试,年后准备科三了。《扯单》系列的一周目大概还差两三篇就完结了,下集预告:串口应用。好了,看完文章实践实践后就该打麻将打麻将,该放炮仗放炮仗吧!总之大家吃好玩好。

上一篇下一篇

猜你喜欢

热点阅读