**这是本文档旧的修订版!**
文本LCD模块的控制
文本LCD模块便宜且易于使用微控制器或FPGA进行接口。
这是一个1行x 16个字符的模块:
要控制LCD模块,您需要11个IO引脚来驱动8位数据总线和3个控制信号。3个控制信号是:
E:启用或“ LCD选择”。高活跃。 读/写:读/写。0写入,1读取。 RS:寄存器选择,0表示命令字节,1表示数据字节。 大多数LCD模块都基于HD44780芯片或兼容的。查看Wikipedia以获取更多信息。
7位设计 让我们从FPGA板驱动LCD模块。 这是我们设计的框图:
Pluto从PC串行端口接收数据,对其进行反序列化,然后将其发送到LCD模块。解串器与串行接口项目中的模块相同,因此此处仅对其进行实例化。
LCD 模块模块(clk,RxD,LCDRS,LCDRW,LCDE,LCDDataBus); 输入 clk,RxD; 输出 LCDRS,LCDRW,LCDE; 输出 [7:0] LCDDataBus;
连线 RxDdataready; 线 [7:0] RxDdata; asyncreceiver解串器(.clk(clk)、. RxD(RxD)、. RxDdataready(RxDdataready)、. RxDdata(RxDdata)); 每当串行端口提供一个字节时,“ RxDdataready”将在一个时钟周期内处于活动状态。
PC通过串口以8位模式向我们发送数据。理想情况下,我们需要从PC接收9位,以便我们可以驱动8位数据总线和LCD模块的“ RS”线。现在,让我们使用接收到的数据的MSB(第7位)来驱动“ RS”,并将仅7位发送到数据总线。
分配 LCDRS = RxDdata [7]; 分配 LCDDataBus = {1'b0,RxDdata [6:0]}; 仅发送7位到模块,在前面加一个'0'使8位 分配 LCD_RW = 0; 我们从不读取LCD模块,因此R / W线接地。 最后一个麻烦是“ E”信号需要长时间激活,即220ns。从FPGA的角度来看,这很长,因为我使用的是25MHz时钟(周期为40ns)。因此,“ E”至少需要驱动5.5个时钟。在这里,我们使用一个计数器对时钟进行计数,将其驱动7个时钟。 reg [2:0]计数;如果(RxDdataready |(count!= 0))count ⇐ count + 1; 总是 @(posege clk) “ E”信号是通过寄存器创建的,因此可以保证无干扰。 reg LCDE; 总是 @(posedge CLK)LCDE ⇐(计数!= 0); 波形如下所示: HDL设计在这里。 该软件 我们初始化LCD并发送一些要显示的数据。 这是初始化LCD模块并显示“ hello”的C代码。 void main() { OpenComm(); 初始化LCD模块
WriteCommByte(0x38); // 8位模式下的“功能集” WriteCommByte(0x0F); //光标打开时“ Display ON” WriteCommByte(0x01); //“ Clear Display”,最多可能需要1.64ms,因此延迟 Sleep(2);
//显示“你好” WriteCommByte('h'+ 0x80); WriteCommByte('e'+ 0x80); WriteCommByte('l'+ 0x80); WriteCommByte('l'+ 0x80); WriteCommByte('o'+ 0x80);
CloseComm();
} 完整的代码在这里。
要获取有关HD44780指令集的更多信息,请在此处检查。
8位设计 主要缺点是较早的设计是我们仅向LCD数据总线发送7位。这是一个问题,因为无法再使用LCD模块的设置DD RAM地址命令。
一种简单的解决方法是使用转义符。我们选择了字符0x00。
新协议如下:
要发送命令字节,请在其前面加上0x00。 要发送数据字节,只需发送它,不需要前缀。 新的C代码是:
void main() {
OpenComm();
//初始化LCD模块 WriteCommByte(0x00); WriteCommByte(0x38); // 8位模式下的“功能集” WriteCommByte(0x00); WriteCommByte(0x0F); //光标打开时“显示打开” WriteCommByte(0x00); WriteCommByte(0x01); //“ Clear Display”,最多可能需要1.64ms,因此延迟 Sleep(2);
WriteCommByte('h'); WriteCommByte('e'); WriteCommByte('l'); WriteCommByte('l'); WriteCommByte('o');
WriteCommByte(0x00); WriteCommByte(0xC0); //进入LCD WriteCommByte('e')的后半部分; WriteCommByte('v'); WriteCommByte('e'); WriteCommByte('r'); WriteCommByte('y'); WriteCommByte('o'); WriteCommByte('n'); WriteCommByte('e');
CloseComm();
} 新的HDL代码如下所示:
LCD 模块模块(clk,RxD,LCDRS,LCDRW,LCDE,LCDDataBus); 输入 clk,RxD; 输出 LCDRS,LCDRW,LCDE; 输出 [7:0] LCDDataBus;
连线 RxDdataready; 线 [7:0] RxDdata; asyncreceiver deserialer(.clk(clk)、. RxD(RxD)、. RxDdataready(RxDdataready)、. RxDdata(RxDdata));
分配 LCDRW = 0; 分配 LCDDataBus = RxD_data;
电线 ReceivedEscape = RxDdataready&(RxDdata == 0); 电线 ReceivedData = RxDdataready&(RxDdata!= 0);
reg [2:0]计数; 总是 @(posege clk)如果(Received_Data |(count!= 0))count ⇐ count + 1;
激活LCDE为6个时钟,所以在25MHz的,这是6x40ns = 240ns REG LCDE; 总是 @(posedge CLK) 如果(LCDE == 0) LCDE ⇐ ReceivedData; 否则 LCDE ⇐(count!= 6); reg LCDinstruction; 总是 @(posedge CLK) 如果(LCDinstruction == 0) LCDinstruction ⇐ ReceivedEscape; 否则 LCD_instruction ⇐(count!= 7); 分配 LCDRS =〜LCDinstruction; 终端模块 HD44780规范显示,“ E”变低后,“ RS”必须在10ns内有效。因此,您会注意到这里“ E”仅被驱动6个时钟,并且“ LCD_instruction”标志仅在时钟7之后被复位,以提供25ns的空间。 那就是所有人!轮到您尝试了。