这一篇在干嘛?
工业控制里最常见的人机界面之一就是字符型液晶。本实验用 FPGA 以 HDL 语言直接「裸写时序」驱动一块 LCD2004 字符液晶——它有 4 行、每行能显示 20 个字符。下载程序后,液晶上会显示「Welcome to Digital chip design and Application Lab」(欢迎来到数字芯片设计和应用实验室)。
实验目标与原理
实验目的
通过本次实验,掌握使用 HDL 语言操作 2004 字符液晶的方法。
液晶显示基础
在工业控制中,显示器件向来是很重要的一环。随着科技的迅速发展,显示器件的种类也越来越多,目前主流的显示器件就是液晶显示器。液晶显示器简称 LCD 显示器,它是利用液晶经过处理后能够改变光线的传输方向的特性实现显示信息的。
液晶显示器按其功能分为三类:笔端式液晶显示器、字符点阵式液晶显示器、图像点阵式液晶显示器。前两种能够显示数字、字符等,而图形点阵式液晶显示器还可以显示汉字和任意图形。
本设计选用经济实惠的字符型液晶显示器 LCD2004 作为显示器件。LCD2004 可以显示四行,每行 20 个字符,采用 +5V 电源供电,外围电路配置简单。
LCD2004 的内部结构
LCD2004 内部主要由 LCD 显示屏(LCD PANEL)、控制器(controller)、驱动器(driver)和偏压产生电路构成。

图23.1 原理方框图
控制器内部主要由指令寄存器 IR、数据寄存器 DR、忙标志 BF、地址计数器 AC、显示缓冲区 DDRAM、字符发生器 CGROM、用户自定义字符发生器 CGRAM 以及时序发生电路组成。
其中最关键的是字符发生存储器(CGROM):它已经存储了 160 个 5×8 和 32 个 5×11 不同的点阵字符图形,有阿拉伯数字、英文字母的大小写、常用的符号和日文假名等,每一个字符都有一个固定的代码。比如大写的英文字母「A」的代码是 01000001B(41H),显示时模块把地址 41H 中的点阵字符图形显示出来,我们就能看到字母「A」。通过工作方式设置指令设置字符点阵可为 5×8 或者 5×11 显示,每一个字符点阵下面为光标显示。
换句话说,FPGA 要显示「A」,并不需要自己画出 A 的点阵,只需要把字节 41H 写进 DDRAM,CGROM 会自动把它翻译成对应的 5×8 点阵图形送到屏上。这就是「字符型」液晶和「图形点阵」液晶最大的区别。

图23.2 机械尺寸图
引脚定义
LCD2004 共 16 个引脚:
- 第 1 脚:Vss 为地电源。
- 第 2 脚:VDD 接 5V 正电源。
- 第 3 脚:V0 为液晶显示器对比度调整端,接正电源时对比度较弱,接地电源时对比度较高,对比度过高时会产生「鬼影」。使用时可以通过一个 10K 的电位器调整对比度。
- 第 4 脚:RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
- 第 5 脚:R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 RW 共同为低电平时可以写入指令或者显示地址;当 RS 为低电平、RW 为高电平时可以读忙信号;当 RS 为高电平、RW 为低电平时可以写入数据。
- 第 6 脚:E 端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。
- 第 7~14 脚:D0~D7 为 8 位双向数据线。通过工作方式设置指令设置数据位宽度为 8 位或 4 位(D4~D7 有效)。
- 第 15 脚:BLA 背光电源正极。
- 第 16 脚:BLK 背光电源负极。
把 RS、R/W 和 E 三根控制线的组合记牢,操作 LCD 就成功了一半:
| RS | R/W | 操作 |
|---|---|---|
| 0 | 0 | 写指令或显示地址 |
| 0 | 1 | 读忙信号 |
| 1 | 0 | 写数据 |
| 1 | 1 | 读数据 |
控制指令表
图23.3 液晶模块控制指令表:
| 指令 | RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |
| 1 | 清屏 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 2 | 光标返回 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * |
| 3 | 输入模式 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S |
| 4 | 显示控制 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B |
| 5 | 光标/字符移位 | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | * | * |
| 6 | 功能 | 0 | 0 | 0 | 0 | 1 | DL | N | F | * | * |
| 7 | 置字符发生器地址 | 0 | 0 | 0 | 1 | 字符发生存贮器地址 | |||||
| 8 | 置数据存贮器地址 | 0 | 0 | 1 | 显示数据存贮器地址 | ||||||
| 9 | 读忙标志和地址 | 0 | 1 | BF | 计数器地址 | ||||||
| 10 | 写数据到指令7.8所设地址 | 1 | 0 | 要写的数据 | |||||||
| 11 | 从指令7.8所设的地址读数据 | 1 | 1 | 读出的数据 | |||||||
各指令的含义:
- 指令 1:清显示,光标复位到地址 00H 位置。
- 指令 2:光标复位,光标返回到地址 0H。
- 指令 3:光标和显示模式设置。I/D:光标移动方向,高电平右移,低电平左移;S:屏幕上所有文字是否左移或者右移,高电平表示有效,低电平则无效。
- 指令 4:显示开关控制。D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示;C:控制光标的开和关,高电平表示有光标,低电平表示无光标;B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。
- 指令 5:光标或显示移位。S/C 高电平时移动显示的文字,低电平时移动光标;R/L 高向左低向右。
- 指令 6:功能设置命令。DL:高电平时为 4 位总线,低电平时为 8 位总线;N:低电平时为单行显示,高电平时双行显示;F:低电平显示 5×8 的点阵字符,高电平时显示 5×11 的点阵液晶。
- 指令 7:字符发生器 RAM 地址设置。地址 = 字符地址 × 8 + 字符行数(将一个字符分成 5×8 点阵,一次写入一行,8 行就组成一个字符)。
- 指令 8:置显示地址。第一行为 00H~0FH,第二行为 40H~4FH,第三行为 10H~1FH,第四行为 50H~5FH。
- 指令 9:读忙信号和光标地址。BF 为忙标志位,高电平表示忙,此时模块不能接收命令或者数据;低电平表示不忙。
- 指令 10:写数据。
- 指令 11:读数据。
FPGA 侧的写时序是怎样的
对照引脚定义,FPGA 每往 LCD 写一个字节,都要走完同一个「四拍」动作:
- 把 8 位数据/指令码放到 lcd_data[7:0] 上;
- 设置 lcd_rs(选指令寄存器还是数据寄存器)和 lcd_rw(写操作拉低);
- lcd_en 拉高,维持一段时间,让数据在液晶侧稳定;
- lcd_en 由高跳变成低——液晶模块就在这个下降沿锁存数据、开始执行。
这四拍在 HDL 里通常用一个状态机实现:状态机按节拍先送指令(初始化序列),再逐字节送出要显示的字符串,每写一个字节后插入足够的等待周期(或读忙标志)再写下一个。50MHz 系统时钟对一个毫秒级的慢外设来说太快了,所以 FPGA 里同样要靠分频/计数器把节奏放慢到微秒级——这和驱动数码管、点阵的「分频节拍」思想完全一致。
指令 8 的地址映射不是连续的
注意 LCD2004 四行的 DDRAM 地址是 00H~0FH、40H~4FH、10H~1FH、50H~5FH——第 1、2 行之间跳了 30H,第 2、3 行又回到 10H。这是字符液晶沿用 HD44780 控制器的经典地址布局。如果按「每行加 20」的想当然算法去设置地址,第 3、4 行就会显示不出来。另外,控制器执行每条指令都需要时间,写指令前应通过指令 9 查询忙标志 BF,或用足够长的等待计数确保上一步执行完毕。
典型初始化流程
上电后对 LCD2004 的标准操作顺序是:等待电源稳定 → 功能设置(指令 6,选 8 位总线、多行显示、5×8 点阵)→ 显示控制(指令 4,开显示、关光标)→ 清屏(指令 1)→ 输入模式(指令 3,写一个字节后地址自动加一)→ 逐字节写数据(指令 10)。每一步都把 RS 拉低送指令字节、E 端给一个「高→低」的使能跳变,最后显示数据时把 RS 拉高即可。
实验仿真
根据描述语言进行仿真测试,分析波形。
引脚分配
管脚约束如下:
set_pin_assignment { lcd_data[0] } { LOCATION = H13; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[1] } { LOCATION = H14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[2] } { LOCATION = J14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[3] } { LOCATION = J13; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[4] } { LOCATION = K12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[5] } { LOCATION = L14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[6] } { LOCATION = M14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_data[7] } { LOCATION = P11; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_en } { LOCATION = G12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_rs } { LOCATION = D14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { lcd_rw } { LOCATION = G11; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { sys_clk } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }set_pin_assignment { sys_rstn } { LOCATION = A2; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
信号含义:lcd_data[7:0] 对应液晶的 D0~D7 八位数据线,lcd_rs(D14)、lcd_rw(G11)、lcd_en(G12)分别对应 RS、R/W、E 三根控制线,sys_clk(R7)是系统时钟,sys_rstn(A2)是复位。
上板流程
- 将 LCD2004 字符液晶模块插在开发板 J1 口上;
- 编译工程并下载程序;
- 观察液晶显示。
现象与总结
下载程序之后,可以看到液晶上显示「Welcome to Digital chip design and Application Lab」,欢迎来到数字芯片设计和应用实验室。

本实验的价值在于:LCD2004 是一款「慢速但经典」的并行外设,操作它需要完整走一遍「状态机 + 使能跳变 + 忙等待」的流程。这套思路完全适用于 LCD1602、LCD12864 等一切沿用 HD44780 类控制器的字符液晶。掌握了指令表和三根控制线的配合,以后换任何字符液晶都只是「换一张指令表」而已。
自测
自测
- LCD2004 一次能显示多少个字符?它采用几伏电源供电?
- RS 和 R/W 的四种电平组合分别对应什么操作?E 端在什么时候使液晶模块执行命令?
- 想在屏幕上显示字母「A」,FPGA 需要写入什么数据?由哪个部件把它变成点阵图形?
- LCD2004 第二行、第三行的 DDRAM 显示地址范围分别是多少?为什么不能按「每行地址连续加 20」来推算?
- 忙标志 BF 的作用是什么?BF 为高电平时模块处于什么状态?