|
字符型LCD模块价格便宜且便于和单片机或者FPGA连接。下面是一个1X16的字符型LCD模块。
为了控制LCD模块,一共需要11个引脚,其中包括8根数据线和3跟控制信号。这3个控制信号分别为: - E:使能信号或者说是LCD片选信号,高有效
- R/W:读/写信号,0:写,1:读
- RS:寄存器选择,0:选择命令寄存器,1:选择数据寄存器
大多数的字符型LCD模块都是基于HD44780或者兼容芯片的,从这里可以得到更为详细的资料。 7位设计 下面用我们FPGA板来驱动这个LCD模块,下面是硬件连接框图。
Pluto板从PC机的串行口接收数据,完成串并转换后发送到LCD模块。串并转换模块的设计与串行接口项目中的一样,所以这里只需要将其实例化即可。 module LCDmodule(clk, RxD, LCD_RS, LCD_RW, LCD_E, LCD_DataBus); input clk, RxD; output LCD_RS, LCD_RW, LCD_E; output [7:0] LCD_DataBus; wire RxD_data_ready; wire [7:0] RxD_data; async_receiver deserialer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data)); 每次从串行口接收到1个字节的数据,RxD_data_ready变为有效,且持续一个时钟周期。 PC机通过串行口,以8位的模式发送数据,所以理论上我们需要从PC机接收9位数据才能驱动8位数据和LCD模块的RS线。现在,我们使用接收到的数据的最高位(bit 7)来驱动"RS",并且只发送7位数据到数据总线上。 assign LCD_RS = RxD_data[7]; assign LCD_DataBus = {1'b0, RxD_data[6:0]}; ? // 只发送7位数据到数据到LCD模块, 最高位补0以凑足8位 assign LCD_RW = 0; 因为我们不需要从LCD模块读取数据,所以R/W引脚直接连接到地。 下一个问题是"E"的有效信号需要持续220nS,这对与使用25MHz(每周期40nS)时钟信号的FPGA来说是比较长的时间了。所以"E"至少需要连续驱动5.5个时钟周期,这里我们驱动其7个时钟周期。 reg [2:0] count; always @(posedge clk) if(RxD_data_ready | (count!=0)) count <= count + 1; "E"信号由寄存器产生以避免毛刺。 reg LCD_E; always @(posedge clk) LCD_E <= (count!=0); 产生的波形如下所示 :
完整的HDL代码在这里下载。 软件 我们需要初始化LCD模块并向其发送一些数据,使之显示出来。
下面是初始化LCD模块并在其上显示"hello"的C语言代码。 void main() { OpenComm(); // 初始化LCD模块 WriteCommByte(0x38); // 设置成8位模式 WriteCommByte(0x0F); // 显示光标 WriteCommByte(0x01); // 清屏,此操作将需要1.64ms,所以延时 Sleep(2); // 显示 "hello" WriteCommByte('h' + 0x80); WriteCommByte('e' + 0x80); WriteCommByte('l' + 0x80); WriteCommByte('l' + 0x80); WriteCommByte('o' + 0x80); CloseComm(); } 完整的代码在这里下载。 点击这里查看更多的关于HD44780的指令集。 8位设计 前面的设计的主要弊端在于我们实际上只使用了7位数据总线,从而导致LCD模块的"设置 DD RAM 地址"的命令不能执行。 一个比较简单的解决办法是使用“标志字符”。这里我选择0x00,因为他没有被HD44780的命令使用。(具体从这里查看) 新的协议如下所示 : - 发送命令字节之前,先发送该“标志字符”。
- 发送数据字节时,直接发送,不需要在其前增加“标志字符”。
新的C语言代码如下所示: void main() { OpenComm(); // 初始化 WriteCommByte(0x00); WriteCommByte(0x38); // 设置成8位模式 WriteCommByte(0x00); WriteCommByte(0x0F); // 显示光标 WriteCommByte(0x00); WriteCommByte(0x01); // 清屏,此操作将需要1.64ms,所以延时 Sleep(2); WriteCommByte('h'); WriteCommByte('e'); WriteCommByte('l'); WriteCommByte('l'); WriteCommByte('o'); WriteCommByte(0x00); WriteCommByte(0xC0); // 继续显示下半部分 WriteCommByte('e'); WriteCommByte('v'); WriteCommByte('e'); WriteCommByte('r'); WriteCommByte('y'); WriteCommByte('o'); WriteCommByte('n'); WriteCommByte('e'); CloseComm(); } 新的HDL代码如下所示: module LCDmodule(clk, RxD, LCD_RS, LCD_RW, LCD_E, LCD_DataBus); input clk, RxD; output LCD_RS, LCD_RW, LCD_E; output [7:0] LCD_DataBus; wire RxD_data_ready; wire [7:0] RxD_data; async_receiver deserialer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data)); assign LCD_RW = 0; assign LCD_DataBus = RxD_data; wire Received_Escape = RxD_data_ready & (RxD_data==0); wire Received_Data = RxD_data_ready & (RxD_data!=0); reg [2:0] count; always @(posedge clk) if(Received_Data | (count!=0)) count <= count + 1; // LCD_E 有效6个时钟周期, 所以输入25MHz的时钟时, 结果为 6x40ns=240ns reg LCD_E; always @(posedge clk) if(LCD_E==0) LCD_E <= Received_Data; else LCD_E <= (count!=6); reg LCD_instruction; always @(posedge clk) if(LCD_instruction==0) LCD_instruction <= Received_Escape; else LCD_instruction <= (count!=7); assign LCD_RS = ~LCD_instruction; endmodule HD44780的数据手册中表示“E”变低后,“RS”需要继续有效10个纳秒。在这里你需要记得:"E"只驱动了6个时钟周期,并且 "LCD_instruction" 标志在第七个时钟后复位,留了25ns的时间余量。
下面该轮到您们去实践了。
下一篇链接:FPGA/CPLD和USB技术无损图像采集卡原理
上一篇链接:FPGA的信号完整性测试与分析
|