<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        LCD1602顯示之FPGA

        作者:kb129   來源:kb129   點擊數:  更新時間:2014年06月08日   【字體:

            本科準備電賽的時候,學習單片機,由于自己沒有LCD,沒有碰。剛開始學FPGA時候,剛準備學LCD,自己又出了些事,后來一直忙到現在。如今再來看看LCD,感觸頗多,廢話不多說,代碼加文檔走起。

         

              lcd1602應該算是一個難點,不管是對于單片機的學習還是FPGA的學習。因為里面涉及到時序分析,地址度寫,數據讀寫,指令讀寫,建立時間和保持時間,還有操作流程會變的復雜,這些都給剛學習的人一些困惑或是難以理解。在我的博文中,一直貫徹的理念是,希望能夠起到拋磚引玉的作用,盡量將思考的過程描述清楚,而不是簡單的寫出相關知識和代碼。

        1.文檔分析:

             關于lcd1602的文檔有很多,自己搜索一下網。市面上絕大部分都是基于HD44780,所以這里的控制顯示等都是一樣的。HD44780內置了DDRAM、CGROM、CGRAM.其中要讓lcd顯示就需要將DDRAM的相應地址寫入數據。




        上圖可知DDRAM一共有40個地址,但是對應于1602顯示,只能有32個地址有效。這是因為1602可以顯示上下兩行,每一行顯示16符號,一共顯示32個符號,每個顯示對應于DDRAM一個地址。例如,我需要在1602的第一行最左邊顯示一個字母A。首先找到第一行最左邊對應DDRAM的地址是什么,查看上圖可知是:00H,然后大寫字母A對應于ASCII中為41H,此時我們只需要給DDRAM的00H地址寫個數據41H即可顯示了。

        問題2:為何寫個41H,就可以顯示為"A"呢?

               對于這個問題,就需要理解CGROM和CGRAM的作用。在芯片HD44780中內置了192個常用字符的字模,存于CGROM(character generate ROM)中,還有8個允許用戶自定義字符(也就是可以顯示八個中文字)的RAM,也就是CGRAM。具體描述為下圖:


        可以從上圖分析A在字模中代碼:高4位為0100,低4位為0001.所以組成8位就變成了41H,這就說明了為何寫入41H就可以顯示“A”。

        上圖紅框里面表示為CGRAM,字模代碼為:00H-0FH;ASCII的字模代碼為:20H-7FH;日文和希臘字符的字模代碼為:A0H-FFH;10H-1FH和80H-9FH沒有使用。

        問題3:我要任意顯示一個字母,數字怎么辦?

              這個問題是接著上面一個問題而言,具體就是:在1602中我要在某一行某個位置顯示我想要的數字或是字母,我應該對應DDRAM地址寫個什么樣的八位數據?例如,我想顯示“1”,那不是就寫個01H呢?此時就需要一個思維轉換,我們要顯示的“1”不再是一個數據,而是需要轉換為一個圖案,可以看到上圖有1的圖案,該圖案對應了31H,所以需要顯示一個“1”,我們就需要給1602的數據總線(DB7--DB0)輸入31H。以此類推,例如我們需要輸入kb129 is a good man,于是就需要給1602順序輸入:6BH,62H,31H,32H,39H ,20H(空格),69H,73H,20H,61H,20H,67H,6FH,6FH,64H,20H,6DH,61H,6EH。

        問題4:那顯示漢字怎么辦呢? 

               問題2中解決了顯示任意一個字母和數字,但是漢字在圖中找不到漢字,怎么辦?這時候需要使用CGRAM了,先用字模軟件,將對應漢字的變為二進制數。


        例如我想要顯示一個“電”字,由于1602中顯示的圖案為5*7或是5*10,所以在8*8中左邊三列不能使用。得到8列八位數據:04, 1F, 15,1F, 15,1F, 04,07.

        然后就需要將這8個8bit數據寫入CGRAM中,寫CGRAM需要使用指令:


        可以設置地址指針自加一模式,所以如果我們想把“電”這個字方在第1個CGRAM中,也就是對應DDRAM中的00H,就需要將地址寫為DB7--DB0:0100_0000.然后將數據04, 1F, 15,1F, 15,1F, 04,07依次寫進CGRAM中。這樣在CGROM字符的字模中00H就代表了“電”。

        最后就是顯示,也就是如果需要將“電”顯示在1602中,就講地址指針指向DDRAM,然后寫數據為00H。

        2.關注時序

              上面已經了解到要讓1602顯示就需要將特地的地址輸入特地的值,如果你剛接觸1602,此時一定特別想了解怎么將上面的思路轉換為verilog代碼。但是一定需要注意時序問題,1602的讀寫時序為:




        這里特別注意了setup和 hold time,但是實際使用中由于1602顯示不需要很高的時序,所以我們只需降低1602工作的時鐘就可以很容易滿足1602的時序要求。

            在上篇博文中已經說了1602顯示原理,在這篇主要是講講怎么將那些原理轉換為verilog代碼。1602的顯示有很多種選擇,一共有11條指令,可以根據不同設置,達到不同的顯示,下面給了verilog顯示代碼,可以以此為基礎,修改為具體應用。由于明天是五一勞動節,所以祝大家五一快樂。

        代碼顯示結果:


        verilog代碼:

        1.代碼頂層

        //data: 2014-04-28
        //addr: kb129
        //info: this is the top of the LCD
        module lcd_1602(
                 clk_50M,
                 rst,
                 en,
                 RS,
                 RW,
                data
         );
        input      clk_50M;
        input      rst;
        output    en;
        output    RS;
        output    RW;
        output    [7:0] data;
        wire        clk_500;
        clk50M_500   u_clk50M_500
        (
            .clk_50M(clk_50M),
            .rst(rst),
            .clk_500(clk_500)
         ); 
        lcd_show u_lcd_show
        (
             .clk_LCD(clk_500),
             .rst(rst),
             .en(en),
             .RS(RS),
             .RW(RW),
             .data(data)
         );
        endmodule
        2.時鐘模塊

        //data: 2014-04-28
        //addr: kb129
        //info: change the clk 50Mhz to 500Hz
        module clk50M_500(
              clk_50M,
              rst,
              clk_500
         );
        input         clk_50M;
        input         rst;
        output      clk_500;

        reg    [8:0] cnt_1;
        reg    [7:0] cnt_2;
        reg           clk_500hz;
        always@(posedge clk_50M)
        begin
              if(!rst)
                   begin
                         clk_500hz <= 0;
                             cnt_1   <= 0;
                             cnt_2   <= 0;
                    end
         else if(cnt_2==8'd199)
                    begin
                            cnt_2 <= 0;
                            if(cnt_1==9'd499)
                                  begin
                                        cnt_1   <= 0;
                                        clk_500hz <= ~clk_500hz;
                                   end
                          else
                                 cnt_1   <= cnt_1+1;
                     end
                else    cnt_2   <= cnt_2+1;
        end 
        assign clk_500 = clk_500hz;
        endmodule

        3.顯示模塊

        //module: lcd_show.v
        //data:2014-04-30
        //addr: kb129         
        //info: this is all the lcd module ,can show 8 zhongwen .
        module lcd_show(
                clk_LCD,
                rst,
                en,
                RS,
                RW,
                data
        );
        input        clk_LCD;  // 500Hz
        input        rst;      
        output     en,RS,RW;
        output   reg  [7:0]  data;
        reg                 RS,en_sel;
        reg      [4:0]    disp_count;
        reg      [4:0]   wrtie_count;
        reg      [2:0]   num;
        reg      [3:0]   state;
        parameter   clear_lcd           = 4'b0000,                    //清屏并光標復位
                          set_disp_mode   = 4'b0001,                    //設置顯示模式:8位2行5x7點陣   
                          disp_on             = 4'b0010,                   //顯示器開、光標不顯示、光標不允許閃爍
                              shift_down    = 4'b0011,                    //文字不動,光標自動右移
                            write_cgram    = 4'b0100,                    //寫中文進入CGRAM,以顯示中文  
                          write_data_first  = 4'b0101,                    //寫入第一行顯示的數據
                     write_data_second  = 4'b0110,                    //寫入第二行顯示的數據
                                         idel     = 4'b0111;                    //空閑狀態   
        assign  RW = 1'b0;                            //RW=0時對LCD模塊執行寫操作
        assign  en = en_sel ? clk_LCD : 1'b0;
           
        reg [7:0] data_character  [7:0];    //this is 五   00H
        reg [7:0] data_character2 [7:0];    //節           01H
        reg [7:0] data_character3 [7:0];   //日           02H
        always @(posedge clk_LCD )
        begin
            data_character[0] <= 8'h00;
            data_character[1] <= 8'h1e;
            data_character[2] <= 8'h08;
            data_character[3] <= 8'h1e;
            data_character[4] <= 8'h0a;
            data_character[5] <= 8'h0a;
            data_character[6] <= 8'h1F;
            data_character[7] <= 8'h00;
            data_character2[0] <= 8'h0A;
            data_character2[1] <= 8'h1f;
            data_character2[2] <= 8'h0A;
            data_character2[3] <= 8'h1f;
            data_character2[4] <= 8'h05;
            data_character2[5] <= 8'h05;
            data_character2[6] <= 8'h05;
            data_character2[7] <= 8'h04;
            data_character3[0] <= 8'h00;
            data_character3[1] <= 8'h1F;
            data_character3[2] <= 8'h11;
            data_character3[3] <= 8'h11;
            data_character3[4] <= 8'h1f;
            data_character3[5] <= 8'h11;
            data_character3[6] <= 8'h11;
            data_character3[7] <= 8'h1f;
        end

        reg [7:0]     data_first_line      [15:0];  //first line show data
        reg [7:0]     data_second_line [15:0];  //second line show data
        always @(posedge clk_LCD )
        begin
           data_first_line[0] <= 8'h54;
           data_first_line[1] <= 8'h6F;
           data_first_line[2] <= 8'h20;
           data_first_line[3] <= 8'h6d;
           data_first_line[4] <= 8'h79;
           data_first_line[5] <= 8'h20;
           data_first_line[6] <= 8'h66;
           data_first_line[7] <= 8'h72;
           data_first_line[8] <= 8'h69;
           data_first_line[9] <= 8'h65;
           data_first_line[10] <= 8'h6e;
           data_first_line[11] <= 8'h64;
           data_first_line[12] <= 8'h73;
           data_first_line[13] <= 8'h8a;
         data_second_line[1] <= 8'h00;
         data_second_line[2] <= 8'h2d;
         data_second_line[3] <= 8'h01;
         data_second_line[4] <= 8'h02;
         data_second_line[5] <= 8'h68;
         data_second_line[6] <= 8'h61;
         data_second_line[7] <= 8'h70;
         data_second_line[8] <= 8'h70;
         data_second_line[9] <= 8'h79;
        end

        always @(posedge clk_LCD or negedge rst)
        begin
           if(!rst)
              begin
                  state         <= clear_lcd;             //復位:清屏并光標復位  
                  RS             <= 1'b0;                  //復位:RS=0時為寫指令;                      
                  data          <= 8'b0;                  //復位:使DB8總線輸出全0
                  en_sel        <= 1'b1;                  //復位:開啟夜晶使能信號
                  disp_count <= 5'b0;
                         num   <= 3'b0;
                wrtie_count <= 5'b0;
              end
           else
              case(state)
              clear_lcd:                               //初始化LCD模塊
                     begin          //清屏并光標復位
                        state  <= set_disp_mode;
                        data  <= 8'h01;               
                     end
              set_disp_mode:        //設置顯示模式:8位2行5x8點陣 
                     begin
                        state  <= disp_on;
                        data  <= 8'h38;                              
                     end
              disp_on:            //顯示器開、光標不顯示、光標不允許閃爍
                     begin
                        state  <= shift_down;
                        data  <= 8'h0c;                           
                     end
              shift_down:        //文字不動,光標自動右移 
                    begin
                        state  <= write_cgram;
                        data  <= 8'h06;                         
                    end
              write_cgram:       //寫CGRAM
                    begin
            case(num)
            0:begin
                     data  <= 8'h40;        //the first character addr
                     num   <= num+1;
                     state <= write_cgram;
              end
            1:begin
                     if(wrtie_count==8)
                          begin
                                data <= 8'h48;  //the second character addr
                                RS   <= 1'b0;
                                num  <= num+1;
                                state<= write_cgram;
                                wrtie_count <= 0;
                         end
                   else
                         begin
                                data <= data_character[wrtie_count];
                                RS   <= 1'b1;
                                wrtie_count <= wrtie_count + 1'b1;
                                state     <= write_cgram;
                           end          
              end
            2:begin
                    if(wrtie_count==8)
                          begin
                                  data <= 8'h50;  //the second character addr
                                  RS   <= 1'b0;
                                  num  <= num+1;
                                  state<= write_cgram;
                                  wrtie_count <= 0;
                           end
                     else
                           begin
                                data <= data_character2[wrtie_count];
                                RS   <= 1'b1;
                                wrtie_count <= wrtie_count + 1'b1;
                                state     <= write_cgram;
                         end      
              end
            3:begin
                    if(wrtie_count==8)
                           begin
                                   data <= 8'h80;  //the DDROM first line start addr
                                   RS   <= 1'b0;

                                   state<= write_data_first;
                                  wrtie_count <= 0;
                           end
                   else
                           begin
                                 data <= data_character3[wrtie_count];
                                 RS   <= 1'b1;
                                 wrtie_count <= wrtie_count + 1'b1;
                                state     <= write_cgram;
                           end      
              end
               endcase
           end
              write_data_first:              //顯示第一行                         
                    begin
                        if(disp_count == 14)                      
                            begin
                                data    <= 8'hc2;               
                                RS     <= 1'b0;
                                disp_count   <= 4'b0;
                                state    <= write_data_second;        
                            end
                        else
                            begin
                                data    <= data_first_line[disp_count];
                                RS     <= 1'b1;                  
                                disp_count   <= disp_count + 1'b1;
                                state    <= write_data_first;
                            end
                    end
              write_data_second:                      //顯示第二行
                    begin
                        if(disp_count == 9)
                            begin
                                en_sel   <= 1'b0;
                                RS    <= 1'b0;
                                disp_count  <= 4'b0;
                                state   <= idel;                     
                            end
                        else
                            begin
                                data    <= data_second_line[disp_count+1];
                                RS     <= 1'b1;
                                disp_count   <= disp_count + 1'b1;
                                state    <= write_data_second;
                            end             
                    end
              idel:            //寫完進入空閑狀態
                    begin
                        state <=  idel;             //在Idel狀態循環 
                    end
              default:  state <= clear_lcd;         //若state為其他值,則將state置為Clear_Lcd
              endcase
        end
        endmodule

        4.11條指令詳細說明

        NO1:


        NO.2

        NO.3

        NO.4

        NO.5

        NO.6

        NO.7

        NO.8 設定DDRAM地址

        NO.9

        NO.10

        NO.11 從CGRAM和DDRAM中讀取數據





        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮