歡迎您光臨本站 登入註冊首頁

FPGA字元型LCD模塊控制

admin @ 2014-03-26 , reply:0

概述

字元型LCD模塊價格便宜且便於和單片機或者FPGA連接。下面是一個1X16的字元型LCD模塊。 為了控制LCD模塊,一共需要11個引腳,其中包括8根數據線和3跟控制信號。這3個控制信號分別為……

字元型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介面參考資料


[admin via 研發互助社區 ] FPGA字元型LCD模塊控制已經有3789次圍觀

http://cocdig.com/docs/show-post-43128.html