FPGA字符型LCD模块控制
2013-01-13
标签:

字符型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的时间余量。

下面该轮到您们去实践了。

链接

更多的来自http://www.hantronix.com/关于LCD的应用资料

一份很好的LCD接口参考资料

可能会用到的工具/仪表
相关文章
推荐文章
热门文章
章节目录
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号