tear086 发表于 2010-2-1 22:08:42

LCD1602驱动

新近写的,水平有限,欢迎拍砖。

实验程序:
---------------------------------------------------
module lcd1602_drive(
input            clk,
input            rst_n,
// LCD1602 Interface
output reg lcd_data,
output         lcd_e,
output reg       lcd_rs,
output         lcd_rw
);

// +++++++++++++++++++++++++++++++++++++
// 分频模块开始
// +++++++++++++++++++++++++++++++++++++
parameter CLK_DIV = 10_0000;            // 分频参数
reg cnt;                         // 分频计数子
reg lcd_clk;                            // 500Hz

// 将50MHz板载时钟分频至500Hz
// 注:计数分频,非等占空比
always @(posedge clk, negedge rst_n)
begin
if(!rst_n)
begin cnt <= 1'b0; lcd_clk <= 0; end
else
begin
    if(cnt <= CLK_DIV)
    begin cnt <= cnt + 1'b1; lcd_clk <= 1; end
    else
    begin cnt <= 1'b0;       lcd_clk <= 0; end
end   
end
// -------------------------------------
// 分频模块结束
// -------------------------------------


// +++++++++++++++++++++++++++++++++++++
// LCD1602驱动模块开始
// +++++++++++++++++++++++++++++++++++++
/*
* 格雷码编码
* 状态数:初始化5个;换行1个;数据32个;空闲1个;共39个。
*/
// 初始化
parameter INIT_0= 8'h00;
parameter INIT_1= 8'h01;
parameter INIT_2= 8'h03;
parameter INIT_3= 8'h02;
parameter INIT_4= 8'h06;
// 显示第一行
parameter ROW1_0= 8'h07;
parameter ROW1_1= 8'h05;
parameter ROW1_2= 8'h04;
parameter ROW1_3= 8'h0C;
parameter ROW1_4= 8'h0D;
parameter ROW1_5= 8'h0F;
parameter ROW1_6= 8'h0E;
parameter ROW1_7= 8'h0A;
parameter ROW1_8= 8'h0B;
parameter ROW1_9= 8'h09;
parameter ROW1_A= 8'h08;
parameter ROW1_B= 8'h18;
parameter ROW1_C= 8'h19;
parameter ROW1_D= 8'h1B;
parameter ROW1_E= 8'h1A;
parameter ROW1_F= 8'h1E;
// 换行
parameter CH_ROW= 8'h1F;
// 显示第二行
parameter ROW2_0= 8'h1D;
parameter ROW2_1= 8'h1C;
parameter ROW2_2= 8'h14;
parameter ROW2_3= 8'h15;
parameter ROW2_4= 8'h17;
parameter ROW2_5= 8'h16;
parameter ROW2_6= 8'h12;
parameter ROW2_7= 8'h13;
parameter ROW2_8= 8'h11;
parameter ROW2_9= 8'h10;
parameter ROW2_A= 8'h30;
parameter ROW2_B= 8'h31;
parameter ROW2_C= 8'h33;
parameter ROW2_D= 8'h32;
parameter ROW2_E= 8'h36;
parameter ROW2_F= 8'h37;
// 空闲
parameter IDLE    = 8'h35;

reg current_state, next_state;    // 现态、次态

// FSM: always1
always @ (posedge lcd_clk, negedge rst_n)
if(!rst_n)current_state <= INIT_0;
else      current_state <= next_state;

// FSM: always2
always
begin
case(current_state)
    // 初始化
    INIT_0: next_state = INIT_1;
    INIT_1: next_state = INIT_2;
    INIT_2: next_state = INIT_3;
    INIT_3: next_state = INIT_4;
    INIT_4: next_state = ROW1_0;
    // 显示第一行
    ROW1_0: next_state = ROW1_1;
    ROW1_1: next_state = ROW1_2;
    ROW1_2: next_state = ROW1_3;
    ROW1_3: next_state = ROW1_4;
    ROW1_4: next_state = ROW1_5;
    ROW1_5: next_state = ROW1_6;
    ROW1_6: next_state = ROW1_7;
    ROW1_7: next_state = ROW1_8;
    ROW1_8: next_state = ROW1_9;
    ROW1_9: next_state = ROW1_A;
    ROW1_A: next_state = ROW1_B;
    ROW1_B: next_state = ROW1_C;
    ROW1_C: next_state = ROW1_D;
    ROW1_D: next_state = ROW1_E;
    ROW1_E: next_state = ROW1_F;
    ROW1_F: next_state = CH_ROW;
    // 换行
    CH_ROW: next_state = ROW2_0;
    // 显示第二行
    ROW2_0: next_state = ROW2_1;
    ROW2_1: next_state = ROW2_2;
    ROW2_2: next_state = ROW2_3;
    ROW2_3: next_state = ROW2_4;
    ROW2_4: next_state = ROW2_5;
    ROW2_5: next_state = ROW2_6;
    ROW2_6: next_state = ROW2_7;
    ROW2_7: next_state = ROW2_8;
    ROW2_8: next_state = ROW2_9;
    ROW2_9: next_state = ROW2_A;
    ROW2_A: next_state = ROW2_B;
    ROW2_B: next_state = ROW2_C;
    ROW2_C: next_state = ROW2_D;
    ROW2_D: next_state = ROW2_E;
    ROW2_E: next_state = ROW2_F;
    ROW2_F: next_state = IDLE;
    // 空闲
    IDLE    : next_state = IDLE;   
    default : next_state = INIT_0;
endcase
end

// FSM: always3
always @ (posedge lcd_clk, negedge rst_n)
begin
if(!rst_n)
begin lcd_rs <= 0; lcd_data <= 8'h00; end
else
begin
    case(current_state)      
      INIT_0, CH_ROW, IDLE: lcd_rs <= 0;// 开始写指令      
      ROW1_0, ROW2_0      : lcd_rs <= 1;// 开始写数据
    endcase   
   
    case(current_state)
      // 写指令,初始化
      INIT_0: lcd_data <= 8'h38;
      INIT_1: lcd_data <= 8'h0C;
      INIT_2: lcd_data <= 8'h01;
      INIT_3: lcd_data <= 8'h06;
      INIT_4: lcd_data <= 8'H80;
      // 写数据,显示第一行
      ROW1_0: lcd_data <= "a";
      ROW1_1: lcd_data <= "b";
      ROW1_2: lcd_data <= "c";
      ROW1_3: lcd_data <= "d";
      ROW1_4: lcd_data <= "e";
      ROW1_5: lcd_data <= "f";
      ROW1_6: lcd_data <= "g";
      ROW1_7: lcd_data <= "h";
      ROW1_8: lcd_data <= "i";
      ROW1_9: lcd_data <= "j";
      ROW1_A: lcd_data <= "k";
      ROW1_B: lcd_data <= "l";
      ROW1_C: lcd_data <= "m";
      ROW1_D: lcd_data <= "n";
      ROW1_E: lcd_data <= "o";
      ROW1_F: lcd_data <= "p";
      // 写指令,换行
      CH_ROW: lcd_data <= 8'hC0;
      // 写数据,显示第二行
      ROW2_0: lcd_data <= "A";
      ROW2_1: lcd_data <= "B";
      ROW2_2: lcd_data <= "C";
      ROW2_3: lcd_data <= "D";
      ROW2_4: lcd_data <= "E";
      ROW2_5: lcd_data <= "F";
      ROW2_6: lcd_data <= "G";
      ROW2_7: lcd_data <= "H";
      ROW2_8: lcd_data <= "I";
      ROW2_9: lcd_data <= "J";
      ROW2_A: lcd_data <= "K";
      ROW2_B: lcd_data <= "L";
      ROW2_C: lcd_data <= "M";
      ROW2_D: lcd_data <= "N";
      ROW2_E: lcd_data <= "O";
      ROW2_F: lcd_data <= "P";
      // 写指令,空闲
      IDLE    : lcd_data <= 8'h00;
    endcase
end
end

// 在时钟高电平有效,低电平失效
// 数据方可被锁存
assign lcd_e= lcd_clk;               
assign lcd_rw = 1'b0;                   // 只写

// -------------------------------------
// LCD1602驱动模块结束
// -------------------------------------

endmodule
---------------------------------------------------

实验现象:
http://cache.amobbs.com/bbs_upload782111/files_25/ourdev_531559.png
(原文件名:抓图-1.png)

Quartus II 综合报告:
http://cache.amobbs.com/bbs_upload782111/files_25/ourdev_531565.png
(原文件名:抓图-2.png)

tear086 发表于 2010-2-20 00:15:30

改进版测试文件

module lcd1602_test(
input      CLOCK_50,                // 板载时钟50MHz
input      Q_KEY,                   // 板载按键RST
// LCD1602 Interface
output LCD1602_DATA,            // LCD1602数据总线               
output       LCD1602_E,               // LCD1602使能
output       LCD1602_RS,            // LCD1602指令数据选择
output       LCD1602_RW               // LCD1602读写选择
);

// 0 ~ (8*16-1) = 128
// 16bits             -> 0123456789ABCDEF <-
wire row1_val = "A Lcd Disp Test ";
wire row2_val = "Amy-studio Pub";


// 例化LCD1602驱动
lcd1602_drive u0(
.clk(CLOCK_50),
.rst_n(Q_KEY),
// LCD1602 Input Value
.row1_val(row1_val),
.row2_val(row2_val),
// LCD1602 Interface
.lcd_data(LCD1602_DATA),
.lcd_e(LCD1602_E),
.lcd_rs(LCD1602_RS),
.lcd_rw(LCD1602_RW)
);

endmodule

完整代码(不晓得什么意思,ourdev一次能贴的代码太短)点击此处下载 ourdev_534318.zip(文件大小:125K) (原文件名:_10_lcd1602_test.zip)

vermon 发表于 2010-2-20 00:27:24

好帖记号!

lmldai 发表于 2010-3-10 20:50:34

学习了

anlimql 发表于 2010-3-11 22:25:12

好贴

amy-studio 发表于 2010-3-13 11:59:41

上图


http://cache.amobbs.com/bbs_upload782111/files_27/ourdev_538135.JPG
(原文件名:lcd1602液晶显示.JPG)

http://cache.amobbs.com/bbs_upload782111/files_27/ourdev_538136.JPG
(原文件名:lcd1602数字钟.JPG)

ruguo 发表于 2010-4-10 21:38:04

为什么我烧进去lcd1602上面什么都没有?

wuyongqing1960 发表于 2010-4-11 09:17:49

好贴,mark

vermon 发表于 2010-4-11 09:20:59

这个要记好非常好

ber619 发表于 2010-4-21 15:07:29

以为是HAL层驱动~~

pebble 发表于 2010-6-22 11:49:15

多谢lz的程序,我的一开始频率设的太高,总是不稳定,后来把频率降下来就好了

hbchf 发表于 2010-11-18 19:25:41

mark

tt88050643 发表于 2010-11-28 15:57:21

mark

AG17 发表于 2010-12-1 00:21:16

mark

suxilong 发表于 2010-12-1 09:29:54

txt文档中的://在Quartus II中,使用Assignments-Import Assignment,导入该txt文件。


这个有什么作用?是不是输入管脚!!!!

suxilong 发表于 2010-12-1 17:16:36

// 将50MHz板载时钟分频至500Hz
// 注:计数分频,非等占空比
always @(posedge clk, negedge rst_n)
begin
if(!rst_n)
begin cnt <= 1'b0; lcd_clk <= 0; end
else
begin
    if(cnt <= CLK_DIV)
    begin cnt <= cnt + 1'b1; lcd_clk <= 1; end
    else
    begin cnt <= 1'b0;       lcd_clk <= 0; end
end   
这一段分频到时有什么用》?????


尤其是这句话!!!!assign lcd_e= lcd_clk;

lb693294195 发表于 2011-11-17 18:01:56

回复【15楼】suxilong 小苏
-----------------------------------------------------------------------

高电平锁存,下降沿有效
页: [1]
查看完整版本: LCD1602驱动