设计思路:
只用LCD进行写操作,写入指令和数据,所以RW一直为0;EN在lcd_clk的上升沿高电平。
这样只需要操作RS信号即可。
写入指令RS为0,写入数据RS为1.
重要之处是:lcd_clk的选择,这里选择的是10KHZ。(这里的主时钟是40MHZ的)
用状态机来写比较方便,其中有2个case语句,一个是对RS的控制和状态的转换,另一个是在对应状态写入数据的选择。
数据在LCD_TOP中,用的是ASCII码,否则要自己去查找手册中相应字符的编码值。如下图
0x20~0x7F为标准的ASCII码,0xA0~0xFF为日文字符和希腊文字符,其余的(0x10~0x1F及0x80~0x9F)没有定义。
====================================================================================================
驱动模块:LCD_drive.v
module LCD_drive(
clk,
rst,
lcd_row1_val,
lcd_row2_val,
lcd_rs,
lcd_rw,
lcd_en,
lcd_data);
input clk;
input rst;
input [127:0] lcd_row1_val;
input [127:0] lcd_row2_val;
output lcd_rs;
output lcd_rw;
output lcd_en;
output [7:0] lcd_data;
parameter TIME = 1999; // 10KHZ
parameter lcd_initial = 0;
parameter row1_addr = 5;
parameter row1 = row1_addr + 1;
parameter row2_addr = row1 + 16;
parameter row2 = row2_addr + 1;
reg [11:0] cnt;
reg lcd_clk;
reg [7:0] lcd_data;
reg [5:0] state;
reg lcd_rs;
assign lcd_rw = 1'b0; //LCD一直是写入模式
assign lcd_en = lcd_clk;
//-------------分频---------------
always @ (posedge clk or negedge rst)
begin
if(!rst)
begin
cnt <= 12'd0;
lcd_clk <= 1'b0;
end
else
begin
if(cnt == TIME)
begin
cnt <= 12'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 12'd1;
end
end
//-----------------------------------------------------------------------
always @ (posedge lcd_clk or negedge rst)
begin
if(!rst)
begin
lcd_rs <= 0;
state <= lcd_initial;
lcd_data <= 8'hxx;
end
else
begin//------------LCD驱动----------------
case(state)
//--初始化--
lcd_initial+0: begin lcd_rs <= 0; state <= lcd_initial+1; end
lcd_initial+1: begin lcd_rs <= 0; state <= lcd_initial+2; end
lcd_initial+2: begin lcd_rs <= 0; state <= lcd_initial+3; end
lcd_initial+3: begin lcd_rs <= 0; state <= row1_addr; end
//--第一行--
row1_addr: begin lcd_rs <= 0; state <= row1+0; end
row1+0: begin lcd_rs <= 1; state <= row1+1; end
row1+1: begin lcd_rs <= 1; state <= row1+2; end
row1+2: begin lcd_rs <= 1; state <= row1+3; end
row1+3: begin lcd_rs <= 1; state <= row1+4; end
row1+4: begin lcd_rs <= 1; state <= row1+5; end
row1+5: begin lcd_rs <= 1; state <= row1+6; end
row1+6: begin lcd_rs <= 1; state <= row1+7; end
row1+7: begin lcd_rs <= 1; state <= row1+8; end
row1+8: begin lcd_rs <= 1; state <= row1+9; end
row1+9: begin lcd_rs <= 1; state <= row1+10; end
row1+10: begin lcd_rs <= 1; state <= row1+11; end
row1+11: begin lcd_rs <= 1; state <= row1+12; end
row1+12: begin lcd_rs <= 1; state <= row1+13; end
row1+13: begin lcd_rs <= 1; state <= row1+14; end
row1+14: begin lcd_rs <= 1; state <= row1+15; end
row1+15: begin lcd_rs <= 1; state <= row2_addr; end
//--第二行--
row2_addr: begin lcd_rs <= 0; state <= row2+0; end
row2+0: begin lcd_rs <= 1; state <= row2+1; end
row2+1: begin lcd_rs <= 1; state <= row2+2; end
row2+2: begin lcd_rs <= 1; state <= row2+3; end
row2+3: begin lcd_rs <= 1; state <= row2+4; end
row2+4: begin lcd_rs <= 1; state <= row2+5; end
row2+5: begin lcd_rs <= 1; state <= row2+6; end
row2+6: begin lcd_rs <= 1; state <= row2+7; end
row2+7: begin lcd_rs <= 1; state <= row2+8; end
row2+8: begin lcd_rs <= 1; state <= row2+9; end
row2+9: begin lcd_rs <= 1; state <= row2+10; end
row2+10: begin lcd_rs <= 1; state <= row2+11; end
row2+11: begin lcd_rs <= 1; state <= row2+12; end
row2+12: begin lcd_rs <= 1; state <= row2+13; end
row2+13: begin lcd_rs <= 1; state <= row2+14; end
row2+14: begin lcd_rs <= 1; state <= row2+15; end
row2+15: begin lcd_rs <= 1; state <= row1_addr; end
default: begin lcd_rs <= 1'bx; state <= lcd_initial; end
endcase
//------------字符显示----------------
case(state)
//--初始化--
lcd_initial+0: lcd_data <= 8'h38; //设置初始工作状态:数据8bit,显示2行,字符为5x7点阵
lcd_initial+1: lcd_data <= 8'h0C; //开显示,字符无闪烁,光标闪烁
lcd_initial+2: lcd_data <= 8'h01; //清屏
lcd_initial+3: lcd_data <= 8'h06; //写入数据光标左移
//--第一行--
row1_addr: lcd_data <= 8'h80; //第一行写入地址
row1+0: lcd_data <= lcd_row1_val[127:120];
row1+1: lcd_data <= lcd_row1_val[119:112];
row1+2: lcd_data <= lcd_row1_val[111:104];
row1+3: lcd_data <= lcd_row1_val[103: 96];
row1+4: lcd_data <= lcd_row1_val[ 95: 88];
row1+5: lcd_data <= lcd_row1_val[ 87: 80];
row1+6: lcd_data <= lcd_row1_val[ 79: 72];
row1+7: lcd_data <= lcd_row1_val[ 71: 64];
row1+8: lcd_data <= lcd_row1_val[ 63: 56];
row1+9: lcd_data <= lcd_row1_val[ 55: 48];
row1+10: lcd_data <= lcd_row1_val[ 47: 40];
row1+11: lcd_data <= lcd_row1_val[ 39: 32];
row1+12: lcd_data <= lcd_row1_val[ 31: 24];
row1+13: lcd_data <= lcd_row1_val[ 23: 16];
row1+14: lcd_data <= lcd_row1_val[ 15: 8];
row1+15: lcd_data <= lcd_row1_val[ 7: 0];
//--第二行--
row2_addr: lcd_data <= 8'h80 + 8'h40;//第二行写入地址
row2+0: lcd_data <= lcd_row2_val[127:120];
row2+1: lcd_data <= lcd_row2_val[119:112];
row2+2: lcd_data <= lcd_row2_val[111:104];
row2+3: lcd_data <= lcd_row2_val[103: 96];
row2+4: lcd_data <= lcd_row2_val[ 95: 88];
row2+5: lcd_data <= lcd_row2_val[ 87: 80];
row2+6: lcd_data <= lcd_row2_val[ 79: 72];
row2+7: lcd_data <= lcd_row2_val[ 71: 64];
row2+8: lcd_data <= lcd_row2_val[ 63: 56];
row2+9: lcd_data <= lcd_row2_val[ 55: 48];
row2+10: lcd_data <= lcd_row2_val[ 47: 40];
row2+11: lcd_data <= lcd_row2_val[ 39: 32];
row2+12: lcd_data <= lcd_row2_val[ 31: 24];
row2+13: lcd_data <= lcd_row2_val[ 23: 16];
row2+14: lcd_data <= lcd_row2_val[ 15: 8];
row2+15: lcd_data <= lcd_row2_val[ 7: 0];
default: lcd_data <= 8'hxx;
endcase
end
end
endmodule
=============================================================================================
测试顶层:LCD_TOP.v
module LCD_TOP(
clk_40m_i,
rst,
lcd_rs,
lcd_rw,
lcd_e,
lcd_data);
input clk_40m_i;
input rst;
output lcd_rs;
output lcd_rw;
output lcd_e;
output [7:0] lcd_data;
//0123456789abcdef//
wire [127:0] row1 = {"abcdefgh12345678"};
wire [127:0] row2 = {" fantasy "};
LCD_drive u1 (
.clk(clk_40m_i),
.rst(rst),
.lcd_row1_val(row1),
.lcd_row2_val(row2),
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_en(lcd_e),
.lcd_data(lcd_data));
endmodule
modelsim测试文件: LCD_tb.v
`timescale 1ns / 1ps
module LCD_tb;
reg clk_40m_i;
reg rst;
wire lcd_rs;
wire lcd_rw;
wire lcd_e;
wire [7:0] lcd_data;
LCD_TOP i1(
.clk_40m_i(clk_40m_i),
.rst(rst),
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_e(lcd_e),
.lcd_data(lcd_data));
initial
begin
clk_40m_i = 0;
rst = 0;
#100 rst = 1;
end
always #20 clk_40m_i = ~clk_40m_i;
endmodule