lcd1602如何自定义汉字(verilog)

Posted aslmer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lcd1602如何自定义汉字(verilog)相关的知识,希望对你有一定的参考价值。

今天一鼓作气,再研究了一下如何用LCD1602自定义汉字

1、用字模软件获取汉字所对应的数据(因为嫌麻烦所以直接用了网上一个帖子里有关“电”的数据,如下:04,1f,15,1f,15,15,1f,04,07)帖子链接:http://www.51hei.com/mcu/3696.html

2、主要知识点

(1)lcd1602的11个指令集与lcd1602的基本显示,前两篇文章已经详细说过,链接如下  

http://www.cnblogs.com/aslmer/p/5801363.html

http://www.cnblogs.com/aslmer/p/5819422.html

(2)DDRAM 与 CGRAM 的区别,根据数据手册知道我们可以定义8个汉字,具体更多知识点请自己百度。

(3)主要的两个指令 Set CGRAM Address 、 Write data to RAM 。

3、在上篇文章的基础上修改代码如下:
难点就在状态机的控制,重点修改部分都用红色标注
module lcd_1602_driver(
                clk    ,
                rst_n  ,
                lcd_en ,
                lcd_rw ,
                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 [7:0]    c_state ;
reg [7:0]    n_state ;
wire  [127:0]  row_1;
wire write_flag;
//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   这里本来用的是格雷码,但是由于增添了两个状态,就随机写了f1,f2这两个数字。
parameter         IDLE=    8\'h00  ;                             
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  ;
//----------------------修改1,增添这两个状态
parameter  CGRAM_ADDR   =   8\'hf1;
parameter  CGRAM_DATA   =   8\'hf2;
//-----------------------
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  ;



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,因此此处要分频
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

//-------------------------修改2 因为自定义一个汉字需要写8次地址并且给8次数据,所以用num和num1来控制。
reg [2:0]num,num1;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1\'b0)begin
        num<=0;
    end
    else if(c_state== CGRAM_ADDR&&write_flag) begin
        if(num==7)
            num<=0;
        else
            num<=num+8\'b1;
    end
    else
        num<=num;
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1\'b0)begin
        num1<=0;
    end
    else if(c_state== CGRAM_DATA&&write_flag) begin
        if(num1==7)
            num1<=0;
        else
            num1<=num1+8\'b1;
    end
    else
        num1<=num1;
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 = CGRAM_ADDR   ;
  //------------------------------------------------ 修改3 当8个数据都写进相应地址里的时候,状态机才跳到ROW1_ADDR状态
  CGRAM_ADDR: n_state = CGRAM_DATA   ;   // 8\'hfe;
  CGRAM_DATA:   if(num1==7)
                  n_state = ROW1_ADDR;
                else
                  n_state= CGRAM_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)||(n_state==CGRAM_ADDR))begin //修改4
           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;  //显示功能开,没有光标,且不闪烁,
// ------------------------------------修改5
           CGRAM_ADDR:   begin
                    case(num)
                        0: lcd_data <= 8\'h40;
                        1: lcd_data <= 8\'h41;
                        2: lcd_data <= 8\'h42;
                        3: lcd_data <= 8\'h43;
                        4: lcd_data <= 8\'h44;
                        5: lcd_data <= 8\'h45;
                        6: lcd_data <= 8\'h46;
                        7: lcd_data <= 8\'h47;
                            endcase
                     end

           CGRAM_DATA:  begin
                    case(num1)
                        0: lcd_data <= 8\'h04;
                        1: lcd_data <= 8\'h1f;
                        2: lcd_data <= 8\'h15;
                        3: lcd_data <= 8\'h1f;
                        4: lcd_data <= 8\'h15;
                        5: lcd_data <= 8\'h1f;
                        6: lcd_data <= 8\'h04;
                        7: lcd_data <= 8\'h07;
                    endcase
                    end
   //-------------------------------------修改6第一行显示 i am liu xiao yi  第二行全部显示汉字电
            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 <=8\'h00; //电
               ROW2_1: lcd_data <=8\'h00;
               ROW2_2: lcd_data <=8\'h00;
               ROW2_3: lcd_data <=8\'h00;
               ROW2_4: lcd_data <=8\'h00;
               ROW2_5: lcd_data <=8\'h00;
               ROW2_6: lcd_data <=8\'h00;
               ROW2_7: lcd_data <=8\'h00;
               ROW2_8: lcd_data <=8\'h00;
               ROW2_9: lcd_data <=8\'h00;
               ROW2_A: lcd_data <=8\'h00;
               ROW2_B: lcd_data <=8\'h00;
               ROW2_C: lcd_data <=8\'h00;
               ROW2_D: lcd_data <=8\'h00;
               ROW2_E: lcd_data <=8\'h00;
               ROW2_F: lcd_data <=8\'h00;
           endcase                     
       end
       else
              lcd_data<=lcd_data ;
   end

endmodule

4、显示结果

 

 转载请注明出处:http://www.cnblogs.com/aslmer/p/5819868.html

以上是关于lcd1602如何自定义汉字(verilog)的主要内容,如果未能解决你的问题,请参考以下文章

原创如何使用DE2的1602LCD --之一(quartus)(verilog)(digital logic)

求一片用verilog鱼呀写的基于fpga的lcd1602显示实时数据的程序

microbit使用oled ssd1306液晶显示汉字和lcd1602液晶显示字符串

Arduino:我的液晶lcd1602不会显示文字了

记树莓派的一个项目_RGB1602的实际使用(续——使用日语/添加常用汉字)

lcd1602 Arduino 为啥显示不了字