zoukankan      html  css  js  c++  java
  • verilog写的LCD1602 显示

    在读本文之前,请先阅读 LCD1602 的 datasheet(百度到处都是) ,熟悉有关的11条指令集。

    LCD1602的11个指令集链接 http://www.cnblogs.com/aslmer/p/5801363.html(这是我总结的11条指令集,中英文结合)


    1、LCD1602基础知识

     

     (2)LCD1602操作流程

     


     2、代码:

    module lcd_1602_driver(
                    clk    ,
                    rst_n  ,
                    lcd_en ,
                    lcd_rw ,  //因为只执行写操作,所以永远为0.
                    lcd_rs ,
                    lcd_data
    
                  );
    input        clk    ;
    input        rst_n  ;
    
    output       lcd_en ;
    output       lcd_rw ;
    output       lcd_rs ;
    output [7:0] lcd_data;
    wire         clk ;
    wire         rst_n  ;
    wire         lcd_en ;
    wire         lcd_rw;
    reg  [7:0]   lcd_data;
    reg          lcd_rs  ;
    reg [5:0]    c_state ;
    reg [5:0]    n_state ;
    wire  [127:0]  row_1;
    wire  [127:0]  row_2;
    assign row_1 ="i am liu xiao yi" ;  //第一行显示的内容
    assign row_2 ="happy everyday !";  //第二行显示的内容
    //----------------------------------------------------------------------
    //initialize
    //first step is waitng more than 20 ms. 数据手册要求的,目的是等待系统上电稳定。
    parameter TIME_20MS = 1000_000 ; //20000000/20=1000_000
    //parameter TIME_15MS = 9'h100 ; //just for test
    parameter TIME_500HZ= 100_000  ; //
    //parameter TIME_500HZ= 4'hf;  //just for test
    //use gray code   
    parameter         IDLE=    8'h00  ;  //因为此状态机一共有40个状态,所以这里用了格雷码,一次只有1位发生改变。00 01 03 02                      
    parameter SET_FUNCTION=    8'h01  ;       
    parameter     DISP_OFF=    8'h03  ;
    parameter   DISP_CLEAR=    8'h02  ;
    parameter   ENTRY_MODE=    8'h06  ;
    parameter   DISP_ON   =    8'h07  ;
    parameter    ROW1_ADDR=    8'h05  ;       
    parameter       ROW1_0=    8'h04  ;
    parameter       ROW1_1=    8'h0C  ;
    parameter       ROW1_2=    8'h0D  ;
    parameter       ROW1_3=    8'h0F  ;
    parameter       ROW1_4=    8'h0E  ;
    parameter       ROW1_5=    8'h0A  ;
    parameter       ROW1_6=    8'h0B  ;
    parameter       ROW1_7=    8'h09  ;
    parameter       ROW1_8=  8'h08  ;
    parameter       ROW1_9=    8'h18  ;
    parameter       ROW1_A=    8'h19  ;
    parameter       ROW1_B=    8'h1B  ;
    parameter       ROW1_C=    8'h1A  ;
    parameter       ROW1_D=    8'h1E  ;
    parameter       ROW1_E=    8'h1F  ;
    parameter       ROW1_F=    8'h1D  ;
    
    parameter    ROW2_ADDR=    8'h1C  ;
    parameter       ROW2_0=    8'h14  ;
    parameter       ROW2_1=    8'h15  ;
    parameter       ROW2_2=    8'h17  ;
    parameter       ROW2_3=    8'h16  ;
    parameter       ROW2_4=    8'h12  ;
    parameter       ROW2_5=    8'h13  ;
    parameter       ROW2_6=    8'h11  ;
    parameter       ROW2_7=    8'h10  ;
    parameter       ROW2_8=    8'h30  ;
    parameter       ROW2_9=    8'h31  ;
    parameter       ROW2_A=    8'h33  ;
    parameter       ROW2_B=    8'h32  ;
    parameter       ROW2_C=    8'h36  ;
    parameter       ROW2_D=    8'h37  ;
    parameter       ROW2_E=    8'h35  ;
    parameter       ROW2_F=    8'h34  ;
    
    
    //20ms的计数器,即初始化第一步
    reg [19:0] cnt_20ms ;
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            cnt_20ms<=0;
        end
        else if(cnt_20ms == TIME_20MS -1)begin
            cnt_20ms<=cnt_20ms;
        end
        else
            cnt_20ms<=cnt_20ms + 1 ;
    end
    wire delay_done = (cnt_20ms==TIME_20MS-1)? 1'b1 : 1'b0 ;
    //----------------------------------------------------------------------
    //500ns  这里是分频,因为LCD1602的工作频率是500HZ,而FPGA是50Mhz,所以要分频
    reg [19:0] cnt_500hz;
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            cnt_500hz <= 0;
        end
        else if(delay_done==1)begin
            if(cnt_500hz== TIME_500HZ - 1)
                cnt_500hz<=0;
            else
                cnt_500hz<=cnt_500hz + 1 ;
        end
        else
            cnt_500hz<=0;
    end
    
    assign lcd_en = (cnt_500hz>(TIME_500HZ-1)/2)? 1'b0 : 1'b1;  //下降沿
    assign write_flag = (cnt_500hz==TIME_500HZ - 1) ? 1'b1 : 1'b0 ;
    
    //set_function ,display off ,display clear ,entry mode set
    //----------------------------------------------------------------------状态机
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            c_state <= IDLE    ;
        end
        else if(write_flag==1) begin
            c_state<= n_state  ;
        end
        else
            c_state<=c_state   ;
    end
    
    always  @(*)begin
        case (c_state)
            IDLE: n_state = SET_FUNCTION ;
    SET_FUNCTION: n_state = DISP_OFF     ;
        DISP_OFF: n_state = DISP_CLEAR   ;
      DISP_CLEAR: n_state = ENTRY_MODE   ;
      ENTRY_MODE: n_state = DISP_ON      ;
      DISP_ON   : n_state = ROW1_ADDR    ;
       ROW1_ADDR: n_state = ROW1_0       ;
          ROW1_0: n_state = ROW1_1       ;
          ROW1_1: n_state = ROW1_2       ;
          ROW1_2: n_state = ROW1_3       ;
          ROW1_3: n_state = ROW1_4       ;
          ROW1_4: n_state = ROW1_5       ;
          ROW1_5: n_state = ROW1_6       ;
          ROW1_6: n_state = ROW1_7       ;
          ROW1_7: n_state = ROW1_8       ;
          ROW1_8: n_state = ROW1_9       ;
          ROW1_9: n_state = ROW1_A       ;
          ROW1_A: n_state = ROW1_B       ;
          ROW1_B: n_state = ROW1_C       ;
          ROW1_C: n_state = ROW1_D       ;
          ROW1_D: n_state = ROW1_E       ;
          ROW1_E: n_state = ROW1_F       ;
          ROW1_F: n_state = ROW2_ADDR    ;
    
       ROW2_ADDR: n_state = ROW2_0       ;
          ROW2_0: n_state = ROW2_1       ;
          ROW2_1: n_state = ROW2_2       ;
          ROW2_2: n_state = ROW2_3       ;
          ROW2_3: n_state = ROW2_4       ;
          ROW2_4: n_state = ROW2_5       ;
          ROW2_5: n_state = ROW2_6       ;
          ROW2_6: n_state = ROW2_7       ;
          ROW2_7: n_state = ROW2_8       ;
          ROW2_8: n_state = ROW2_9       ;
          ROW2_9: n_state = ROW2_A       ;
          ROW2_A: n_state = ROW2_B       ;
          ROW2_B: n_state = ROW2_C       ;
          ROW2_C: n_state = ROW2_D       ;
          ROW2_D: n_state = ROW2_E       ;
          ROW2_E: n_state = ROW2_F       ;
          ROW2_F: n_state = ROW1_ADDR    ;
         default: n_state = n_state      ;
       endcase 
       end   
    
       assign lcd_rw = 0;
       always  @(posedge clk or negedge rst_n)begin
           if(rst_n==1'b0)begin
               lcd_rs <= 0 ;   //order or data  0: order 1:data
           end
           else if(write_flag == 1)begin
               if((n_state==SET_FUNCTION)||(n_state==DISP_OFF)||
                  (n_state==DISP_CLEAR)||(n_state==ENTRY_MODE)||
                  (n_state==DISP_ON ) ||(n_state==ROW1_ADDR)||
                  (n_state==ROW2_ADDR))begin
               lcd_rs<=0 ;
               end 
               else  begin
               lcd_rs<= 1;
               end
           end
           else begin
               lcd_rs<=lcd_rs;
           end     
       end                   
    
       always  @(posedge clk or negedge rst_n)begin
           if(rst_n==1'b0)begin
               lcd_data<=0 ;
           end
           else  if(write_flag)begin
               case(n_state)
    
                     IDLE: lcd_data <= 8'hxx;
             SET_FUNCTION: lcd_data <= 8'h38; //2*16 5*8 8位数据
                 DISP_OFF: lcd_data <= 8'h08;
               DISP_CLEAR: lcd_data <= 8'h01;
               ENTRY_MODE: lcd_data <= 8'h06;
               DISP_ON   : lcd_data <= 8'h0c;  //显示功能开,没有光标,且不闪烁,
                ROW1_ADDR: lcd_data <= 8'h80; //00+80
                   ROW1_0: lcd_data <= row_1 [127:120];
                   ROW1_1: lcd_data <= row_1 [119:112];
                   ROW1_2: lcd_data <= row_1 [111:104];
                   ROW1_3: lcd_data <= row_1 [103: 96];
                   ROW1_4: lcd_data <= row_1 [ 95: 88];
                   ROW1_5: lcd_data <= row_1 [ 87: 80];
                   ROW1_6: lcd_data <= row_1 [ 79: 72];
                   ROW1_7: lcd_data <= row_1 [ 71: 64];
                   ROW1_8: lcd_data <= row_1 [ 63: 56];
                   ROW1_9: lcd_data <= row_1 [ 55: 48];
                   ROW1_A: lcd_data <= row_1 [ 47: 40];
                   ROW1_B: lcd_data <= row_1 [ 39: 32];
                   ROW1_C: lcd_data <= row_1 [ 31: 24];
                   ROW1_D: lcd_data <= row_1 [ 23: 16];
                   ROW1_E: lcd_data <= row_1 [ 15:  8];
                   ROW1_F: lcd_data <= row_1 [  7:  0];
    
                ROW2_ADDR: lcd_data <= 8'hc0;      //40+80
                   ROW2_0: lcd_data <= row_2 [127:120];
                   ROW2_1: lcd_data <= row_2 [119:112];
                   ROW2_2: lcd_data <= row_2 [111:104];
                   ROW2_3: lcd_data <= row_2 [103: 96];
                   ROW2_4: lcd_data <= row_2 [ 95: 88];
                   ROW2_5: lcd_data <= row_2 [ 87: 80];
                   ROW2_6: lcd_data <= row_2 [ 79: 72];
                   ROW2_7: lcd_data <= row_2 [ 71: 64];
                   ROW2_8: lcd_data <= row_2 [ 63: 56];
                   ROW2_9: lcd_data <= row_2 [ 55: 48];
                   ROW2_A: lcd_data <= row_2 [ 47: 40];
                   ROW2_B: lcd_data <= row_2 [ 39: 32];
                   ROW2_C: lcd_data <= row_2 [ 31: 24];
                   ROW2_D: lcd_data <= row_2 [ 23: 16];
                   ROW2_E: lcd_data <= row_2 [ 15:  8];
                   ROW2_F: lcd_data <= row_2 [  7:  0];
               endcase                     
           end
           else
                  lcd_data<=lcd_data ;
       end
    
    endmodule

    3、testbench

    `timescale 1 ns/1 ns
    
    module lcd1602_tb();
    
    //时钟和复位
    reg clk  ;
    reg rst_n;
    
    wire lcd_en ;
    wire lcd_rs ;
    wire lcd_rw ;
    wire [7:0] lcd_data;
            //时钟周期,单位为ns,可在此修改时钟周期。
            parameter CYCLE    = 20;
    
            //复位时间,此时表示复位3个时钟周期的时间。
            parameter RST_TIME = 3 ;
    
            //待测试的模块例化
           lcd_1602_driver u1_lcd_1602_driver(
                   .clk     (clk     ),
                   .rst_n   (rst_n   ),
                   .lcd_en  (lcd_en  ),
                   .lcd_rw  (lcd_rw  ),
                   .lcd_rs  (lcd_rs  ),
                   .lcd_data(lcd_data)
    
                  );
    
    
                //生成本地时钟50M
                initial begin
                    clk = 0;
                    forever
                    #(CYCLE/2)
                    clk=~clk;
                end
    
                //产生复位信号
                initial begin
                    rst_n = 1;
                    #2;
                    rst_n = 0;
                    #(CYCLE*RST_TIME);
                    rst_n = 1;
                end
    
    
                endmodule

    4、modesim 仿真


     5、结果展示及总结

    易错之处:1、忽略了lcd1602的正常工作频率

                  2、显示地址需要加80

                  3、这篇文章只是lcd1602的基本显示,下一目标就是要自定义汉字,会用到CGRAM的相关知识,详情请见下篇文章,链接如下

                   http://www.cnblogs.com/aslmer/p/5819868.html,欢迎提问并给出宝贵意见

     
    转载请注明出处:http://www.cnblogs.com/aslmer/p/5819422.html
  • 相关阅读:
    《别闹了,费曼先生》听书笔记
    提升失败回报率的清单
    《真实的幸福》听书笔记
    JAVA好书之《深入理解Java虚拟机》
    2017第32周五
    2017第32周四
    《具身认知》听书笔记
    javascript的rsa加密和python的rsa解密
    python socket timeout设置
    想使用gevent、mysql、sqlalchemy实现python项目协程异步达到并发的效果
  • 原文地址:https://www.cnblogs.com/aslmer/p/5819422.html
Copyright © 2011-2022 走看看