• altera小实验——LCD1602显示


    所用板子为altera DE2板子,FPGA为Cyclone II:EP2C35F672C6,quartus版本为13.0

    1.LCD规格与接口

    DE2板子上的LCD为16*2,是最简单的LCD显示屏。

    数据储存器地址为第一行00H~0fH,第二行40H~4fH。但是需要注意的是,在需要向数据存贮器赋值时,需要赋值为80H~8fH和c0H~cfH,因为只有第一位置1数据存贮器地址地址输入才为有效。

    接下来看下LCD用户接口,在此截取DE2用户手册。


    用户接口标注如下:

    [7:0]Data    //8位数据总线
    LCD_EN      //使能信号
    LCD_RW     //读/写选择信号
    LCD_RS      //数据/命令选择信号
    LCD_BLON  //背光灯亮灭
    LCD_ON      //总开关
    其中,LCD_ON LCD_BLON可暂时不关注,使用时置为1即可。

    LCD_EN LCD_RS LCD_RW Data在时序中需要格外关注。

    2.LCD时序图

    LCD的时序图极为简单,概括来讲就是:

    LCD_EN=1时进行数据交互(因此可令LCD_EN = CLK_LCD,数据操作均发生在时钟高有效时段)

    LCD_RW=0时写入数据,LCD_RW=1时读出数据(基本没有用到)

    LCD_RS=0时操作指令,LCD_RS=1时操作数据

    整体时序图如下(来自网络),里面的细节时序可暂时忽略。


    3.LCD基本操作指令

    LCD的操作整体上分为两类:指令配置与数据读写显示。指令表如下(来源网络)。


    指令的解读(摘取自网络):

    指令1:清屏,光标同时复位至00H位置(左上角);

    指令2:光标复位,即光标复位至00H;

    指令3:光标与显示移动设置。I/D:光标移动方向,高电平右移,低电平左移;S:屏幕上所有文字是否左移或右移,高电平表示有效,低电平表示无效;

    指令4:显示开关控制。D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示。C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁;

    指令5:光标或显示移位 S/C :高电平时显示移动的文字,低电平时移动光标;

    指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符;功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符。

    指令7:字符发生器RAM地址设置;

    指令8:DDRAM地址设置;

    指令9:读忙信号和光标地址 BF:忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平表示不忙。

    4.如何显示一个字符

    字符产生器CGROM中内置了192个常用字符,自定义字符产生器CGRAM还允许用户自定义8个字符。内置的192个字符如下


    如要显示“A”,则需要在指令10阶段时候写入数据为8‘b0100_0001。

    本次将大小写字母以参数形式写在了character.v中。主程序中可以直接调用。

    parameter space = 8'b0010_0000;
    
    parameter CAPITAL_A = 8'b0100_0001;
    parameter CAPITAL_B = 8'b0100_0010;
    parameter CAPITAL_C = 8'b0100_0011;
    parameter CAPITAL_D = 8'b0100_0100;
    parameter CAPITAL_E = 8'b0100_0101;
    parameter CAPITAL_F = 8'b0100_0110;
    parameter CAPITAL_G = 8'b0100_0111;
    parameter CAPITAL_H = 8'b0100_1000;
    parameter CAPITAL_I = 8'b0100_1001;
    parameter CAPITAL_J = 8'b0100_1010;
    parameter CAPITAL_K = 8'b0100_1011;
    parameter CAPITAL_L = 8'b0100_1100;
    parameter CAPITAL_M = 8'b0100_1101;
    parameter CAPITAL_N = 8'b0100_1110;
    parameter CAPITAL_O = 8'b0100_1111;
    parameter CAPITAL_P = 8'b0101_0001;
    parameter CAPITAL_Q = 8'b0101_0001;
    parameter CAPITAL_R = 8'b0101_0010;
    parameter CAPITAL_S = 8'b0101_0011;
    parameter CAPITAL_T = 8'b0101_0100;
    parameter CAPITAL_U = 8'b0101_0101;
    parameter CAPITAL_V = 8'b0101_0110;
    parameter CAPITAL_W = 8'b0101_0111;
    parameter CAPITAL_X = 8'b0101_1000;
    parameter CAPITAL_Y = 8'b0101_1001;
    parameter CAPITAL_Z = 8'b0101_1010;
    
    parameter LOWERCASE_a = 8'b0110_0001;
    parameter LOWERCASE_b = 8'b0110_0010;
    parameter LOWERCASE_c = 8'b0110_0011;
    parameter LOWERCASE_d = 8'b0110_0100;
    parameter LOWERCASE_e = 8'b0110_0101;
    parameter LOWERCASE_f = 8'b0110_0110;
    parameter LOWERCASE_g = 8'b0110_0111;
    parameter LOWERCASE_h = 8'b0110_1000;
    parameter LOWERCASE_i = 8'b0110_1001;
    parameter LOWERCASE_j = 8'b0110_1010;
    parameter LOWERCASE_k = 8'b0110_1011;
    parameter LOWERCASE_l = 8'b0110_1100;
    parameter LOWERCASE_m = 8'b0110_1101;
    parameter LOWERCASE_n = 8'b0110_1110;
    parameter LOWERCASE_o = 8'b0110_1111;
    parameter LOWERCASE_p = 8'b0111_0001;
    parameter LOWERCASE_q = 8'b0111_0001;
    parameter LOWERCASE_r = 8'b0111_0010;
    parameter LOWERCASE_s = 8'b0111_0011;
    parameter LOWERCASE_t = 8'b0111_0100;
    parameter LOWERCASE_u = 8'b0111_0101;
    parameter LOWERCASE_v = 8'b0111_0110;
    parameter LOWERCASE_w = 8'b0111_0111;
    parameter LOWERCASE_x = 8'b0111_1000;
    parameter LOWERCASE_y = 8'b0111_1001;
    parameter LOWERCASE_z = 8'b0111_1010;

    5.程序分析

    1)接口

    module work(
      ClOCK_50,
      KEY,
      LCD_RW,
      LCD_EN,
      LCD_RS,
      LCD_DATA,
      LCD_ON,
      LCD_BLON
      );
    
    input ClOCK_50;
    input [3:0]KEY;
    output LCD_RW, LCD_EN, LCD_RS, LCD_ON, LCD_BLON;
    output [8:0]LCD_DATA;
    
    assign LCD_RW = rw;
    assign LCD_EN = CLK_500Hz;
    assign LCD_RS = rs;
    assign LCD_ON = 1;
    assign LCD_BLON = 1;
    assign LCD_DATA = data;
    
    `include "character.v" 

    主时钟为50MHz,之后需要进行手动分频,LCD时钟不能过快。

    !KEY[1]作为复位信号。其余信号均输出至LCD。

    2)状态与参数设置

    /*共14个状态:1个空闲状态,9个设置状态,两行读写包括地址与数据共计4个状态*/
    parameter s_idle         = 4'd0;
    parameter s_clear        = 4'd1;
    parameter s_cursor       = 4'd2;
    parameter s_inputmode    = 4'd3;
    parameter s_switchmode   = 4'd4;
    parameter s_shiftmode    = 4'd5;
    parameter s_setfunction  = 4'd6;
    parameter s_setgeneraddr = 4'd7;
    parameter s_setdataaddr1 = 4'd8;
    parameter s_readbasy     = 4'd9;
    parameter s_writecgram1  = 4'd10;
    parameter s_readram      = 4'd11;
    parameter s_setdataaddr2 = 4'd12;
    parameter s_writecgram2  = 4'd13;
    
    parameter IDLE         = 8'bzzzz_zzzz;
    parameter CLEAR        = 8'b0000_0001;  //清屏
    parameter CURSOR       = 8'b0000_0010;  //光标返回
    parameter INPUTMODE    = 8'b0000_0110;  //置输入模式 _01(I/D)S
    parameter SWITCHMODE   = 8'b0000_1111;  //显示开关控制 _1DCB
    parameter SHIFTMODE    = 8'b0001_1100;  //光标或字符移位 _(S/R)(R/L)**
    parameter SETFUNCTION  = 8'b0011_1100;  //置功能 00_001(DL)_NF**
    parameter SETGENERADDR = 8'b0100_0000;  //置字符发生存贮地址
    parameter SETDATAADDR  = 8'b1000_0000;  //置数据存贮器地址 起始地址
    3)状态机跳转,并非每个状态都是必须的,参考了其他程序后,状态机如下

    reg [3:0]state;
    reg rw, rs;
    reg [7:0]data;
    reg [7:0]addr;
    always @(posedge clk or posedge rst) begin
      if (rst) begin
        // reset
        state <= s_idle;
        data  <= 8'b0;
        rw    <= 1'b0;
        rs    <= 1'b0;
        addr  <= 8'b1000_0000;
      end
      else begin
        case (state)
          s_idle        :begin
                            state <= s_clear;
                            data  <= IDLE;
                            rw    <= rw;
                            rs    <= rs;
                         end
          s_clear       :begin
                            state <= s_inputmode;
                            data  <= CLEAR;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_inputmode   :begin
                            state <= s_switchmode;
                            data  <= INPUTMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_switchmode  :begin
                            state <= s_shiftmode;
                            data  <= SWITCHMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_shiftmode   :begin
                            state <= s_setfunction;
                            data  <= SHIFTMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_setfunction :begin
                            state <= s_setdataaddr1;
                            data  <= SETFUNCTION;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_setdataaddr1:begin
                            state <= s_writecgram1;
                            data  <= addr;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_writecgram1 :begin
                            if (addr == 8'b1000_1111) begin
                              state <= s_setdataaddr2;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= 8'b1100_0000;
                            end
                            else begin
                              state <= s_writecgram1;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= addr + 1'b1;
                            end                        
                         end
          s_setdataaddr2:begin
                            state <= s_writecgram2;
                            data  <= addr;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_writecgram2 :begin
                            if (addr == 8'b1100_1111) begin
                              state <= s_setdataaddr1;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= 8'b1000_0000;
                            end
                            else begin
                              state <= s_writecgram2;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= addr + 1'b1;
                            end                        
                         end
          default       :begin
                            state <= state;
                            data  <= data;
                            rw    <= rw;
                            rs    <= rs;
                         end
        endcase
      end
    end
    4)函数:设置每一个地址所显示数据(此处可DIY)

    function [7:0]lcd_data;
      input [7:0]lcd_addr;
      begin
        case(lcd_addr)
          8'h81  :lcd_data = CAPITAL_I;
          8'h83  :lcd_data = LOWERCASE_a;
          8'h84  :lcd_data = LOWERCASE_m;
          8'h86  :lcd_data = CAPITAL_G;
          8'h87  :lcd_data = CAPITAL_J;
          8'h88  :lcd_data = CAPITAL_M;
          8'hc1  :lcd_data = CAPITAL_W;
          8'hc2  :lcd_data = LOWERCASE_h;
          8'hc3  :lcd_data = LOWERCASE_o;
          8'hc5  :lcd_data = LOWERCASE_a;
          8'hc6  :lcd_data = LOWERCASE_r;
          8'hc7  :lcd_data = LOWERCASE_e;
          8'hc9  :lcd_data = LOWERCASE_y;
          8'hca  :lcd_data = LOWERCASE_o;
          8'hcb  :lcd_data = LOWERCASE_u;
          default:lcd_data = space;
        endcase
      end
    endfunction
    5)时钟分频

    parameter CLK_LCD_times = 19'd030000;
    reg [22:0]cnt;
    reg CLK_LCD;
    
    always@(posedge ClOCK_50 or posedge rst) begin
      if(rst) begin
        cnt <= 19'd0;
        CLK_500Hz <= 1'b0;
      end
      else if(cnt == CLK_LCD_times) begin
        cnt <= 19'd0;
        CLK_LCD <= ~CLK_LCD;
      end
      else begin 
        cnt <= cnt+1'b1;
      end 
    end
    至此程序完成。文末附上完整代码。

    6.程序结果



    ps.完整代码如下

    module work(
      ClOCK_50,
      KEY,
      LCD_RW,
      LCD_EN,
      LCD_RS,
      LCD_DATA,
      LCD_ON,
      LCD_BLON
      );
    
    input ClOCK_50;
    input [3:0]KEY;
    output LCD_RW, LCD_EN, LCD_RS, LCD_ON, LCD_BLON;
    output [8:0]LCD_DATA;
    
    assign LCD_RW = rw;
    assign LCD_EN = CLK_LCD;
    assign LCD_RS = rs;
    assign LCD_ON = 1;
    assign LCD_BLON = 1;
    assign LCD_DATA = data;
    
    `include "character.v" 
    
    /*共14个状态:1个空闲状态,9个设置状态,两行读写包括地址与数据共计4个状态*/
    parameter s_idle         = 4'd0;
    parameter s_clear        = 4'd1;
    parameter s_cursor       = 4'd2;
    parameter s_inputmode    = 4'd3;
    parameter s_switchmode   = 4'd4;
    parameter s_shiftmode    = 4'd5;
    parameter s_setfunction  = 4'd6;
    parameter s_setgeneraddr = 4'd7;
    parameter s_setdataaddr1 = 4'd8;
    parameter s_readbasy     = 4'd9;
    parameter s_writecgram1  = 4'd10;
    parameter s_readram      = 4'd11;
    parameter s_setdataaddr2 = 4'd12;
    parameter s_writecgram2  = 4'd13;
    
    parameter IDLE         = 8'bzzzz_zzzz;
    parameter CLEAR        = 8'b0000_0001;  //清屏
    parameter CURSOR       = 8'b0000_0010;  //光标返回
    parameter INPUTMODE    = 8'b0000_0110;  //置输入模式 _01(I/D)S
    parameter SWITCHMODE   = 8'b0000_1111;  //显示开关控制 _1DCB
    parameter SHIFTMODE    = 8'b0001_1100;  //光标或字符移位 _(S/R)(R/L)**
    parameter SETFUNCTION  = 8'b0011_1100;  //置功能 00_001(DL)_NF**
    parameter SETGENERADDR = 8'b0100_0000;  //置字符发生存贮地址
    parameter SETDATAADDR  = 8'b1000_0000;  //置数据存贮器地址 起始地址
    
    wire clk, rst;
    assign clk = CLK_LCD;
    assign rst = !KEY[1];
    
    reg [3:0]state;
    reg rw, rs;
    reg [7:0]data;
    reg [7:0]addr;
    always @(posedge clk or posedge rst) begin
      if (rst) begin
        // reset
        state <= s_idle;
        data  <= 8'b0;
        rw    <= 1'b0;
        rs    <= 1'b0;
        addr  <= 8'b1000_0000;
      end
      else begin
        case (state)
          s_idle        :begin
                            state <= s_clear;
                            data  <= IDLE;
                            rw    <= rw;
                            rs    <= rs;
                         end
          s_clear       :begin
                            state <= s_inputmode;
                            data  <= CLEAR;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_inputmode   :begin
                            state <= s_switchmode;
                            data  <= INPUTMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_switchmode  :begin
                            state <= s_shiftmode;
                            data  <= SWITCHMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_shiftmode   :begin
                            state <= s_setfunction;
                            data  <= SHIFTMODE;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_setfunction :begin
                            state <= s_setdataaddr1;
                            data  <= SETFUNCTION;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_setdataaddr1:begin
                            state <= s_writecgram1;
                            data  <= addr;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_writecgram1 :begin
                            if (addr == 8'b1000_1111) begin
                              state <= s_setdataaddr2;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= 8'b1100_0000;
                            end
                            else begin
                              state <= s_writecgram1;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= addr + 1'b1;
                            end                        
                         end
          s_setdataaddr2:begin
                            state <= s_writecgram2;
                            data  <= addr;
                            rw    <= 1'b0;
                            rs    <= 1'b0;
                         end
          s_writecgram2 :begin
                            if (addr == 8'b1100_1111) begin
                              state <= s_setdataaddr1;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= 8'b1000_0000;
                            end
                            else begin
                              state <= s_writecgram2;
                              data  <= lcd_data(addr);
                              rw    <= 1'b0;
                              rs    <= 1'b1;
                              addr  <= addr + 1'b1;
                            end                        
                         end
          default       :begin
                            state <= state;
                            data  <= data;
                            rw    <= rw;
                            rs    <= rs;
                         end
        endcase
      end
    end
    
    function [7:0]lcd_data;
      input [7:0]lcd_addr;
      begin
        case(lcd_addr)
          8'h81  :lcd_data = CAPITAL_I;
          8'h83  :lcd_data = LOWERCASE_a;
          8'h84  :lcd_data = LOWERCASE_m;
          8'h86  :lcd_data = CAPITAL_G;
          8'h87  :lcd_data = CAPITAL_J;
          8'h88  :lcd_data = CAPITAL_M;
          8'hc1  :lcd_data = CAPITAL_W;
          8'hc2  :lcd_data = LOWERCASE_h;
          8'hc3  :lcd_data = LOWERCASE_o;
          8'hc5  :lcd_data = LOWERCASE_a;
          8'hc6  :lcd_data = LOWERCASE_r;
          8'hc7  :lcd_data = LOWERCASE_e;
          8'hc9  :lcd_data = LOWERCASE_y;
          8'hca  :lcd_data = LOWERCASE_o;
          8'hcb  :lcd_data = LOWERCASE_u;
          default:lcd_data = space;
        endcase
      end
    endfunction
    
    parameter CLK_LCD_times = 19'd030000;
    reg [22:0]cnt;
    reg CLK_LCD;
    
    always@(posedge ClOCK_50 or posedge rst) begin
      if(rst) begin
        cnt <= 19'd0;
        CLK_500Hz <= 1'b0;
      end
      else if(cnt == CLK_LCD_times) begin
        cnt <= 19'd0;
        CLK_LCD <= ~CLK_LCD;
      end
      else begin 
        cnt <= cnt+1'b1;
      end 
    end
    
    endmodule















  • 相关阅读:
    Frameworks.Entity.Core 5 EntityValidation
    Frameworks.Entity.Core 4
    大叔 Frameworks.Entity.Core 3 Predicate
    大叔 Frameworks.Entity.Core 2 PageList
    Frameworks.Entity.Core 1
    大叔 EF 来分析 EntityFrameworks.Data.Core 2
    大叔 EF 来分析 EntityFrameworks.Data.Core 1
    基于 Lind.DDD 的 权限管理系统
    HttpApplication IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable ps url System.Web.dll
    MVC 记录
  • 原文地址:https://www.cnblogs.com/mingmingruyue99/p/7202014.html
Copyright © 2020-2023  润新知