• 基于FPGA的VGA协议实现


    一、VGA介绍

    1.1 VGA原理

    VGA接口
    在这里插入图片描述
    最主要的几根线:
    在这里插入图片描述
    VGA其实就是相当于一块芯片,跟单片机驱动IC一样,满足一定的时序,便能驱动起来。VGA的扫描其实很简单,大致轨迹如下所示:
    在这里插入图片描述
    每扫描完一行,从新开始下一行;每扫完一场,重新开始下一场。
    以下是行扫描,场扫描HS,VS时序图
    在这里插入图片描述
    VS时序如下所示:
    在这里插入图片描述
    可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

    A~B:场消隐期 即同步,相当于还原扫描坐标吧

    B~C:场消隐后肩 相当于准备开始扫描吧

    C~D:场显示期 扫描中,数据有效区域

    D~E:场消隐前肩 完成扫描,相当于准备同步

    HS时序深入分析
    在这里插入图片描述
    可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

    A~B:行消隐期 即同步,相当于还原扫描坐标吧

    B~C:行消隐后肩 相当于准备开始扫描吧

    C~D:行显示期 扫描中,数据有效区域

    D~E:行消隐前肩 完成扫描,相当于准备同步

    综上描述,我们只要知道每个时期的时间,便可以表示出VGA的时序。而FPGA的工作是由固定频率的时钟触发的,因此某固定时间可以用n次触发来表示。因此我们很容易就想到了FPGA常用的计数方法:比如说行扫描,我们计数0~H_total-1。用另一个进程将其划分为4个时期,安标注分配。其实这相当于状态机。

    1.2VGA电路

    VGA接口:R,G,B三通道,直接赋给数字信号,RGB,最多产生8种色彩。这是最基本的。电路如下所示:
    在这里插入图片描述

    二、配置

    打开Quartus,选择IP核
    在这里插入图片描述
    命名之后,将基础时钟改为50M
    在这里插入图片描述
    点击next后,取消该勾选
    在这里插入图片描述
    默认next,直到该界面,c0默认输出50M即可,c1分频到25M,如需其他时钟频率可以自己进行设置在这里插入图片描述
    勾选,finish
    在这里插入图片描述

    三、实现

    3.1 字符显示

    字模软件
    百度网盘:
    https://pan.baidu.com/s/1542q14FRzCawo8WNp8J-Kg
    提取码: jdzg
    在子模提取工具里面输入需要显示的字符并设置字符大小为64*64
    在这里插入图片描述
    然后点击文件-另存为,把图片保存为BMP图片,再点击文件-打开,把保存的BMP图片打开得到整体的字符。
    在这里插入图片描述
    参数设置
    在这里插入图片描述
    保存为.txt格式
    在这里插入图片描述
    在这里插入图片描述
    将得到的字模替换。

    3.2图片显示

    想要显示的图片,但是图片的大小超过了芯片的内存,无法把图片保存进去,故采用一张100*100的图片进行显示。
    在这里插入图片描述
    使用工具把图片转为HEX文件

    工具链接: https://pan.baidu.com/s/1o8ii9ei密码:62uv
    在这里插入图片描述
    图片数据太多需要使用ROM来存储数据
    打开quartus,找到ROM
    在这里插入图片描述
    取消勾选下列选项
    在这里插入图片描述
    刚才生成的,hex
    在这里插入图片描述
    finish
    在这里插入图片描述

    四、代码

    4.1.vga驱动模块

    module vga_dirve (input			wire						clk,            //系统时钟
                      input			wire						rst_n,          //复位
                      input			wire		[ 15:0 ]		rgb_data,       //16位RGB对应值
                      output			wire							vga_clk,    //vga时钟 25M
                      output			reg							h_sync,     //行同步信号
                      output			reg							v_sync,     //场同步信号
                      output			reg		[ 11:0 ]				addr_h, //行地址
                      output			reg		[ 11:0 ]				addr_v,  //列地址
                      output			wire		[ 4:0 ]				rgb_r,  //红基色
                      output			wire		[ 5:0 ]				rgb_g,  //绿基色
                      output			wire		[ 4:0 ]				rgb_b  //蓝基色
    );
    
    // 640 * 480 60HZ
    localparam	 H_FRONT = 16; // 行同步前沿信号周期长
    localparam	 H_SYNC  = 96; // 行同步信号周期长
    localparam	 H_BLACK = 48; // 行同步后沿信号周期长
    localparam	 H_ACT   = 640; // 行显示周期长
    localparam	 V_FRONT = 11; // 场同步前沿信号周期长
    localparam	 V_SYNC  = 2; // 场同步信号周期长
    localparam	 V_BLACK = 31; // 场同步后沿信号周期长
    localparam	 V_ACT   = 480; // 场显示周期长
    
    // 800 * 600 72HZ
    // localparam	 H_FRONT = 40; // 行同步前沿信号周期长
    // localparam	 H_SYNC  = 120; // 行同步信号周期长
    // localparam	 H_BLACK = 88; // 行同步后沿信号周期长
    // localparam	 H_ACT   = 800; // 行显示周期长
    // localparam	 V_FRONT = 37; // 场同步前沿信号周期长
    // localparam	 V_SYNC  = 6; // 场同步信号周期长
    // localparam	 V_BLACK = 23; // 场同步后沿信号周期长
    // localparam	 V_ACT   = 600; // 场显示周期长
    
    
    localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
    localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期
    
    reg			[ 11:0 ]			cnt_h			; // 行计数器
    reg			[ 11:0 ]			cnt_v			; // 场计数器
    reg			[ 15:0 ]			rgb			; // 对应显示颜色值
    
    // 对应计数器开始、结束、计数信号
    wire							flag_enable_cnt_h			;
    wire							flag_clear_cnt_h			;
    wire							flag_enable_cnt_v			;
    wire							flag_clear_cnt_v			;
    wire							flag_add_cnt_v  			;
    wire							valid_area      			;
    
    
    // 25M时钟 行周期*场周期*刷新率 = 800 * 525* 60
    wire							clk_25			;
    // 50M时钟 1040 * 666 * 72
    wire							clk_50			;
    //PLL
    pll	pll_inst (
    	.areset ( ~rst_n ),
    	.inclk0 ( clk ),
    	.c0 ( clk_50 ), //50M
    	.c1 ( clk_25 ), //25M
    	);
    //根据不同分配率选择不同频率时钟
    assign vga_clk = clk_25;
    
    
    // 行计数
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            cnt_h <= 0;
        end
        else if ( flag_enable_cnt_h ) begin
            if ( flag_clear_cnt_h ) begin
                cnt_h <= 0;
            end
            else begin
                cnt_h <= cnt_h + 1;
            end
        end
        else begin
            cnt_h <= 0;
        end
    end
    assign flag_enable_cnt_h = 1;
    assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;
    
    // 行同步信号
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            h_sync <= 0;
        end
        else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
            h_sync <= 1;
        end
            else if ( flag_clear_cnt_h ) begin // 其余为0
            h_sync <= 0;
            end
        else begin
            h_sync <= h_sync;
        end
    end
    
    // 场计数
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            cnt_v <= 0;
        end
        else if ( flag_enable_cnt_v ) begin
            if ( flag_clear_cnt_v ) begin
                cnt_v <= 0;
            end
            else if ( flag_add_cnt_v ) begin
                cnt_v <= cnt_v + 1;
            end
            else begin
                cnt_v <= cnt_v;
            end
        end
        else begin
            cnt_v <= 0;
        end
    end
    assign flag_enable_cnt_v = flag_enable_cnt_h;
    assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
    assign flag_add_cnt_v    = flag_clear_cnt_h;
    
    // 场同步信号
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            v_sync <= 0;
        end
        else if ( cnt_v == V_SYNC - 1 ) begin
            v_sync <= 1;
        end
            else if ( flag_clear_cnt_v ) begin
            v_sync <= 0;
            end
        else begin
            v_sync <= v_sync;
        end
    end
    
    // 对应有效区域行地址 1-640
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            addr_h <= 0;
        end
        else if ( valid_area ) begin
            addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
        end
        else begin
            addr_h <= 0;
        end
    end
    // 对应有效区域列地址 1-480
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            addr_v <= 0;
        end
        else if ( valid_area ) begin
            addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
        end
        else begin
            addr_v <= 0;
        end
    end
    // 有效显示区域
    assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;
    
    
    // 显示颜色
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            rgb <= 16'h0;
        end
        else if ( valid_area ) begin
            rgb <= rgb_data;
        end
        else begin
            rgb <= 16'b0;
        end
    end
    assign rgb_r = rgb[ 15:11 ];
    assign rgb_g = rgb[ 10:5 ];
    assign rgb_b = rgb[ 4:0 ];
    
    endmodule // vga_dirve
    
    
    

    4.2数据模块

    module data_drive (input			wire						vga_clk,
                       input			wire						rst_n,
                       input			wire		[ 11:0 ]		addr_h,
                       input			wire		[ 11:0 ]		addr_v,
                       input			wire		[ 2:0 ]		 key,
                       output			reg		[ 15:0 ]				rgb_data);
    
    localparam	red    = 16'd63488;
    localparam	orange = 16'd64384;
    localparam	yellow = 16'd65472;
    localparam	green  = 16'd1024;
    localparam	blue   = 16'd31;
    localparam	indigo = 16'd18448;
    localparam	purple = 16'd32784;
    localparam	white  = 16'd65503;
    localparam	black  = 16'd0;
    reg [ 383:0 ] char_line[ 89:0 ];
    
    localparam	states_1 = 1; // 彩条
    localparam	states_2 = 2; // 字符
    localparam	states_3 = 3; // 图片
    
    parameter	height = 78; // 图片高度
    parameter	width  = 128; // 图片宽度
    reg			[ 1:0 ]			states_current			; // 当前状态
    reg			[ 1:0 ]			states_next			    ; // 下个状态
    reg			[ 13:0 ]		rom_address				; // ROM地址
    wire			[ 15:0 ]		rom_data				; // 图片数据
    
    wire							flag_enable_out1			; // 文字有效区域
    wire							flag_enable_out2			; // 图片有效区域
    wire							flag_clear_rom_address		; // 地址清零
    wire							flag_begin_h			    ; // 图片显示行
    wire							flag_begin_v			    ; // 图片显示列
    
    //状态转移
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            states_current <= states_1;
        end
        else begin
            states_current <= states_next;
        end
    end
    
    //状态判断
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            states_next <= states_1;
        end
        else if ( key[ 0 ] ) begin
            states_next <= states_1;
        end
            else if ( key[ 1 ] ) begin
            states_next <= states_2;
            end
            else if ( key[ 2 ] ) begin
            states_next <= states_3;
            end
        else begin
            states_next <= states_next;
        end
    end
    
    //状态输出
    always @( * ) begin
        case ( states_current )
            states_1 : begin
                if ( addr_h == 0 ) begin
                    rgb_data = black;
                end
                else if ( addr_h >0 && addr_h <81 ) begin
                    rgb_data = red;
                end
                else if ( addr_h >80 && addr_h <161 ) begin
                    rgb_data = orange;
                end
                else if ( addr_h >160 && addr_h <241 ) begin
                    rgb_data = yellow;
                end
                else if ( addr_h >240 && addr_h <321 ) begin
                    rgb_data = green;
                end
                else if ( addr_h >320 && addr_h <401 ) begin
                    rgb_data = blue;
                end
                else if ( addr_h >400 && addr_h <481 ) begin
                    rgb_data = indigo;
                end
                else if ( addr_h >480 && addr_h <561 ) begin
                    rgb_data = purple;
                end
                else if ( addr_h >560 && addr_h <641 ) begin
                    rgb_data = white;
                end
                else begin
                    rgb_data = black;
                end
                
            end
            states_2 : begin
                if ( flag_enable_out1 ) begin
                    rgb_data = char_line[ addr_v-208 ][ 532 - addr_h ]? white:black;
                end
                else begin
                    rgb_data = black;
                end
            end
            states_3 : begin
                if ( flag_enable_out2 ) begin
                    rgb_data = rom_data;
                end
                else begin
                    rgb_data = black;
                end
                
            end
            default: begin
                case ( addr_h )
                    0 : rgb_data      = black;
                    1 : rgb_data      = red;
                    81 : rgb_data     = orange;
                    161: rgb_data     = yellow;
                    241: rgb_data     = green;
                    321: rgb_data     = blue;
                    401: rgb_data     = indigo;
                    481: rgb_data     = purple;
                    561: rgb_data     = white;
                    default: rgb_data = rgb_data;
                endcase
            end
        endcase
    end
    
    assign flag_enable_out1 = states_current == states_2 && addr_h > 148 && addr_h < 533 && addr_v > 208  && addr_v < 273 ;
    assign flag_begin_h     = addr_h > ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width + 1;
    assign flag_begin_v     = addr_v > ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height + 1;
    assign flag_enable_out2 = states_current == states_3 && flag_begin_h && flag_begin_v;
    
    //ROM地址计数器
    always @( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            rom_address <= 0;
        end
        else if ( flag_clear_rom_address ) begin //计数满清零
            rom_address <= 0;
        end
            else if ( flag_enable_out2 ) begin  //在有效区域内+1
            rom_address <= rom_address + 1;
            end
        else begin  //无效区域保持
            rom_address <= rom_address;
        end
    end
    assign flag_clear_rom_address = rom_address == height * width - 1;
    
    //初始化显示文字
    always@( posedge vga_clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            char_line[0]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[1]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[2]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[3]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[4]=  384'h000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000;
            char_line[5]=  384'h0000000000000000000000000000000000000000000000000000000000000000000000000E0000000000000400000000;
            char_line[6]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[7]=  384'h00000000000004000F8000000100000E0000080000000000000000000000000000000000000000000000000000000000;
            char_line[8]=  384'h00000000000000000000000000000000000000000000000003000E000F8000000180001F0C001C000000000000000000;
            char_line[9]=  384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003FFFF00;
            char_line[10]= 384'h0F00000001FFFFFF0FFFFF00000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[11]= 384'h0000000000000000000000000000000003FFFF001F00000001FFFFFF8FFFFF0000000000000000000000000000000000;
            char_line[12]= 384'h00000000000000000000000000000000000000000000000000000000000000000000000003C01E001E00000001E00F00;
            char_line[13]= 384'h0F001C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[14]= 384'h000000000000000003C01C001E00000001E00F000F001C00000000000000000000000000000000000000000000000000;
            char_line[15]= 384'h0000000000000000000000000000000000000000000000000000000003C03C003C00030001E00F000F001C0000000000;
            char_line[16]= 384'h0001FC00000FE000000FF000000040000007E00003FFFFF00007E0000001FC000007E00000000E00000FE00000004000;
            char_line[17]= 384'h03C038003C00078001E00F000F001C00000000000007FF00007FF800003FFE000000C000001FF80007FFFFF0001FF800;
            char_line[18]= 384'h0007FF00001FF80000000E00007FF8000000C00003C039FFFFFFFFC001E00F000F001C0000000000001E078000E07C00;
            char_line[19]= 384'h00F81F000001C000003C1E0007FFFFF0003C1E00001E0780003C1E0000001E0000E07C000001C00003C070FFFFFFFFE0;
            char_line[20]= 384'h01E00F000F001C0000000000003803C001801E0001E007800007C00000700F0007FFFFE000700F00003803C000700F00;
            char_line[21]= 384'h00003E0001801E000007C00003C070407800000001E00F080F001C0000000000007003E003000F0003C003C001FFC000;
            char_line[22]= 384'h00E0070007C000C000E00700007003E000E0070000003E0003000F0001FFC00003C060007000000001E30F1C0F001C00;
            char_line[23]= 384'h0000000000E003E003000780078001E001FFC00001E00380070000C001E0038000E003E001E0038000007E0003000780;
            char_line[24]= 384'h01FFC00003C0E000F000000001E3FFFE0F001C000000000001C003E007000780070001E00007C00003C003C006000180;
            char_line[25]= 384'h03C003C001C003E003C003C00000FE00070007800007C00003C0E000E000000001E3FFFE0F001C0000000000038003E0;
            char_line[26]= 384'h070007C0070000F00003C00003C001C00600018003C001C0038003E003C001C00000DE00070007C00003C00003C0C001;
            char_line[27]= 384'hE000000001E3C01C0F001C0000000000038001C0078003C00F0000F00003C000078001E00C000300078001E0038001C0;
            char_line[28]= 384'h078001E000019E00078003C00003C00003C0C001C0E0000001E3C01C0F001C000000000007000000078003C00F8000F0;
            char_line[29]= 384'h0003C000078000E00C000300078000E007000000078000E000039E00078003C00003C00003C18003C0F8000001E3C01C;
            char_line[30]= 384'h0FFFFC000000000007000000078003C00F8000F00003C000070000E00C000600070000E007000000070000E000031E00;
            char_line[31]= 384'h078003C00003C00003C1800380F8000001E3C01C0FFFFC000000000007000000030003C00FC000F00003C0000F0000F0;
            char_line[32]= 384'h000006000F0000F0070000000F0000F000071E00030003C00003C00003C1000380F0000001E3C01C0F001C0000000000;
            char_line[33]= 384'h0F000000000003C00FC000F00003C0000F0000F000000C000F0000F00F0000000F0000F000061E00000003C00003C000;
            char_line[34]= 384'h03C3000780F0000001E3C01C0F001C00000000000F000000000003C00FC000F00003C0000F0000F000001C000F0000F0;
            char_line[35]= 384'h0F0000000F0000F0000C1E00000003C00003C00003C1000700F0000001E3C01C0F001800000000000E00000000000780;
            char_line[36]= 384'h078001E00003C0000F000070000018000F0000700E0000000F000070001C1E00000007800003C00003C0800F00F00000;
            char_line[37]= 384'h01E3C01C0F000000000000000E00000000000780000001E00003C0001E000078000038001E0000780E0000001E000078;
            char_line[38]= 384'h00181E00000007800003C00003C0C00E00F0000001E3FFFC0F000000000000000E03F80000000F00000001E00003C000;
            char_line[39]= 384'h1E000078000038001E0000780E03F8001E00007800301E0000000F000003C00003C0601E00F0010001E3FFFC0F000000;
            char_line[40]= 384'h000000001E0FFF0000000E00000003C00003C0001E000078000030001E0000781E0FFF001E00007800701E0000000E00;
            char_line[41]= 384'h0003C00003C0301C00F0030001E3CF1C0F000000000000001E3FFF8000003C00000003C00003C0001E00007800007000;
            char_line[42]= 384'h1E0000781E3FFF801E00007800601E0000003C000003C00003C0383C00F0078001E38F180F0001803FFFFFFC1E7C0FC0;
            char_line[43]= 384'h0000F800000007800003C0001E000078000070001E0000781E7C0FC01E00007800C01E000000F8000003C00003C01C7F;
            char_line[44]= 384'hFFFFFFC001E20F000F0001803FFFFFFC1EF003E0000FE00000000F000003C0001E0000780000E0001E0000781EF003E0;
            char_line[45]= 384'h1E00007801C01E00000FE0000003C00003C01CFFFFFFFFE001E00F000F000180000000001EE001E0000FF80000000E00;
            char_line[46]= 384'h0003C0001E0000780000E0001E0000781EE001E01E00007801801E00000FF8000003C00003C01E7800F0000001E00F00;
            char_line[47]= 384'h0F000180000000001FC001F000007C0000001C000003C0001E0000780001E0001E0000781FC001F01E00007803001E00;
            char_line[48]= 384'h00007C000003C00003C00E3000F0000001E00F000F000180000000001F8000F000000F00000038000003C0001E000078;
            char_line[49]= 384'h0001C0001E0000781F8000F01E00007803001E0000000F000003C00003C00E0000F0000001E00F000F00018000000000;
            char_line[50]= 384'h1F0000F000000780000070000003C0001E0000780003C0001E0000781F0000F01E00007806001E00000007800003C000;
            char_line[51]= 384'h03C00E0000F0000001E00F000F000180000000001F000078000003C00000E0000003C0001E0000780003C0001E000078;
            char_line[52]= 384'h1F0000781E0000780E001E00000003C00003C00003C00F0080F0000001E00F000F0001C0000000001E000078000001C0;
            char_line[53]= 384'h0001C0000003C0001E000078000380001E0000781E0000781E0000780C001E00000001C00003C00003C00F01E0F00000;
            char_line[54]= 384'h01E00F030F8003E0000000001E000078000001E0000380000003C0001E000078000780001E0000781E0000781E000078;
            char_line[55]= 384'h18001E00000001E00003C00003C00F01F0F1000001E00F0787FFFFE0000000001E000078000000E0000700000003C000;
            char_line[56]= 384'h1E000078000780001E0000781E0000781E00007838001E00000000E00003C00003C00E03F8F0C00003FFFFFFC7FFFFE0;
            char_line[57]= 384'h000000001E000078000000F0000E00000003C0000F000070000780000F0000701E0000780F0000703FFFFFFC000000F0;
            char_line[58]= 384'h0003C00003E01E03E0F0600001FFFFFFE1FFFF80000000001E000078000000F0001C00000003C0000F0000F0000F8000;
            char_line[59]= 384'h0F0000F01E0000780F0000F03FFFFFFC000000F00003C00003DFFE07C0F0380000C0000000000000000000000E000078;
            char_line[60]= 384'h000000F0003800000003C0000F0000F0000F80000F0000F00E0000780F0000F000001E00000000F00003C00003C7FC07;
            char_line[61]= 384'h80F01C000000000000000000000000000F000078038000F0007000000003C0000F0000F0000F80000F0000F00F000078;
            char_line[62]= 384'h0F0000F000001E00038000F00003C00003C3F80F00F00E000000000000000000000000000F00007807C000F000E00030;
            char_line[63]= 384'h0003C000070000E0000F8000070000E00F000078070000E000001E0007C000F00003C00003C1F01E00F00F8000000000;
            char_line[64]= 384'h00000000000000000F0000700FC000F001C000300003C000078001E0000F8000078001E00F000070078001E000001E00;
            char_line[65]= 384'h0FC000F00003C00003C0C01E00F007C0000000002000C00000000000070000F00FC000F0038000300003C000078001E0;
            char_line[66]= 384'h001F8000078001E0070000F0078001E000001E000FC000F00003C00003C0003C00F003C000180C003000700000000000;
            char_line[67]= 384'h078000F00FC001E0078000300003C00003C001C0001F800003C001C0078000F003C001C000001E000FC001E00003C000;
            char_line[68]= 384'h03C0007800F003E0001806001C0038000000000003C000E00F8001E0070000600003C00003C003C0001F800003C003C0;
            char_line[69]= 384'h03C000E003C003C000001E000F8001E00003C00003C000F000F001E0001807000E003C000000000003C001E0078003C0;
            char_line[70]= 384'h0E0000E00003C00001E00380001F800001E0038003C001E001E0038000001E00078003C00003C00003C000E000F001F0;
            char_line[71]= 384'h003803800F001E000000000001E001C0078003801C0001E00003C00000E00700001F800000E0070001E001C000E00700;
            char_line[72]= 384'h00001E00078003800003C00003C001C000F000F0003803C007801F000000000000F0038003C007001FFFFFE00007E000;
            char_line[73]= 384'h00700F00001F800000700F0000F0038000700F0000001E0003C007000007E00003C0038000F000E0007803E007800F00;
            char_line[74]= 384'h00000000007C0F0001F01E001FFFFFE0000FF000003C1E00001F8000003C1E00007C0F00003C1E0000003F0001F01E00;
            char_line[75]= 384'h000FF00003C0070000F0006000F001E007C00F8000000000003FFE00007FFC001FFFFFE001FFFF80001FF800001F8000;
            char_line[76]= 384'h001FF800003FFE00001FF800000FFFF8007FFC0001FFFF8003C00C0000F0000000F001E003C00F80000000000007F000;
            char_line[77]= 384'h001FE0001FFFFFE001FFFF800007E000000F00000007E0000007F0000007E000000FFFF8001FE00001FFFF8003C01801;
            char_line[78]= 384'hF0F0000001F001E003C00780000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[79]= 384'h0000000000000000000000000000000003C020007FF0000007E001E00380078000000000000000000000000000000000;
            char_line[80]= 384'h00000000000000000000000000000000000000000000000000000000000000000000000003C000001FE0000007E000C0;
            char_line[81]= 384'h030007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[82]= 384'h000000000000000003C0000007E000000380008000000200000000000000000000000000000000000000000000000000;
            char_line[83]= 384'h0000000000000000000000000000000000000000000000000000000003C0000003C00000000000000000000000000000;
            char_line[84]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[85]= 384'h038000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[86]= 384'h000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000;
            char_line[87]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[88]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
            char_line[89]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
        end
    end
    
    //实例化ROM
    rom	rom_inst (
    .address ( rom_address ),
    .clock ( vga_clk ),
    .q ( rom_data )
    );
    endmodule // data_drive
    
    
    

    4.3按键消抖模块

    module key_debounce(
    	input 	wire	clk,
    	input 	wire 	rst_n,
    	input 	wire 	key,
    	
    	output 	reg 	flag,// 0抖动, 1抖动结束
    	output 	reg	key_value//key抖动结束后的值
    );
    
    parameter MAX_NUM = 20'd1_000_000;
    
    reg [19:0] delay_cnt;//1_000_000
    
    reg key_reg;//key上一次的值
    
    always @(posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    		key_reg <= 1;
    		delay_cnt <= 0;
    	end
    	
    	else begin
    		key_reg <= key;
    		//当key为1 key 为0 表示按下抖动,开始计时
    		if(key_reg  != key  ) begin 
    		   delay_cnt <= MAX_NUM ;
    		end
    		else begin
    		    if(delay_cnt > 0)
    				delay_cnt <= delay_cnt -1;
    			else
    				delay_cnt <= 0;
    		end
    	end
    end
    
    
    //当计时完成,获取key的值
    always @(posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    		flag <= 0;
    		key_value <= 1;
    	end
    
    	else begin
    		
    		// 计时完成 处于稳定状态,进行赋值
    		if(delay_cnt == 1) begin
    			flag <= 1;
    			key_value <= key;
    		end
    		else begin
    			flag <= 0;
    			key_value <= key_value;
    		end
    	end
    end
    
    endmodule
    
    

    4.4顶层模块

    module vga_top (input			wire						clk,
                    input			wire						rst_n,
                    input			wire		[ 2:0 ]		    key,
                    output			wire						vga_clk,
                    output			wire						h_sync,
                    output			wire						v_sync,
                    output			wire		[ 4:0 ]			rgb_r,
                    output			wire		[ 5:0 ]			rgb_g,
                    output			wire		[ 4:0 ]			rgb_b,
                    output			reg		    [ 3:0 ]			led);
    
    reg			[ 27:0 ]			cnt			        ;
    wire		[ 11:0 ]		    addr_h              ;
    wire		[ 11:0 ]		    addr_v              ;
    wire		[ 15:0 ]			rgb_data			;
    wire		[ 2:0 ]			    key_flag			;
    wire		[ 2:0 ]			    key_value			;
    
    //vga模块
    vga_dirve u_vga_dirve(
    .clk      ( clk ),
    .rst_n    ( rst_n ),
    .rgb_data ( rgb_data ),
    .vga_clk  ( vga_clk ),
    .h_sync   ( h_sync ),
    .v_sync   ( v_sync ),
    .rgb_r    ( rgb_r ),
    .rgb_g    ( rgb_g ),
    .rgb_b    ( rgb_b ),
    .addr_h   ( addr_h ),
    .addr_v   ( addr_v )
    );
    
    //数据模块
    data_drive u_data_drive(
    .vga_clk ( vga_clk ),
    .rst_n   ( rst_n ),
    .addr_h  ( addr_h ),
    .addr_v  ( addr_v ),
    .key     ( {key_value[ 2 ] && key_flag[ 2 ], key_value[ 1 ] && key_flag[ 1 ], key_value[ 0 ] && key_flag[ 0 ] } ),
    .rgb_data  ( rgb_data )
    );
    
    
    //按键消抖
    key_debounce u_key_debounce0(
    .clk   ( vga_clk ),
    .rst_n ( rst_n ),
    .key   ( key[ 0 ] ),
    .flag  ( key_flag[ 0 ] ),
    .key_value  ( key_value[ 0 ] )
    );
    
    key_debounce u_key_debounce1(
    .clk   ( vga_clk ),
    .rst_n ( rst_n ),
    .key   ( key[ 1 ] ),
    .flag  ( key_flag[ 1 ] ),
    .key_value  ( key_value[ 1 ] )
    );
    
    key_debounce u_key_debounce2(
    .clk   ( vga_clk ),
    .rst_n ( rst_n ),
    .key   ( key[ 2 ] ),
    .flag  ( key_flag[ 2 ] ),
    .key_value  ( key_value[ 2 ] )
    );
    
    // led
    always @( posedge clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            cnt <= 0;
        end
        else if ( cnt == 50_000_000 - 1 ) begin
            cnt <= 0;
        end
        else begin
            cnt <= cnt + 1;
        end
    end
    always @( posedge clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            led <= 4'b0000;
        end
        else if ( cnt == 50_000_000 -1 )begin
            led <= ~led;
        end
        else begin
            led <= led;
        end
    end
    endmodule // vga_top
    
    

    4.5TCL引脚绑定

    package require ::quartus::project
    
    set_location_assignment PIN_E1 -to clk
    set_location_assignment PIN_E15 -to rst_n
    set_location_assignment PIN_C16 -to h_sync
    set_location_assignment PIN_D15 -to v_sync
    
    set_location_assignment PIN_E16 -to key[0]
    set_location_assignment PIN_M16 -to key[1]
    set_location_assignment PIN_M15 -to key[2]
    
    set_location_assignment PIN_G15 -to led[0]
    set_location_assignment PIN_F16 -to led[1]
    set_location_assignment PIN_F15 -to led[2]
    set_location_assignment PIN_D16 -to led[3]
    
    set_location_assignment PIN_A14 -to rgb_b[4]
    set_location_assignment PIN_B14 -to rgb_b[3]
    set_location_assignment PIN_A15 -to rgb_b[2]
    set_location_assignment PIN_B16 -to rgb_b[1]
    set_location_assignment PIN_C15 -to rgb_b[0]
    
    
    set_location_assignment PIN_A11 -to rgb_g[5]
    set_location_assignment PIN_B11 -to rgb_g[4]
    set_location_assignment PIN_A12 -to rgb_g[3]
    set_location_assignment PIN_B12 -to rgb_g[2]
    set_location_assignment PIN_A13 -to rgb_g[1]
    set_location_assignment PIN_B13 -to rgb_g[0]
    
    set_location_assignment PIN_C8 -to rgb_r[4]
    set_location_assignment PIN_A9 -to rgb_r[3]
    set_location_assignment PIN_B9 -to rgb_r[2]
    set_location_assignment PIN_A10 -to rgb_r[1]
    set_location_assignment PIN_B10 -to rgb_r[0]
    
    
    

    五、效果

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    参考

    ``

    https://blog.csdn.net/weixin_51755670/article/details/118076488
    https://blog.csdn.net/qq_45659777/article/details/124834294

  • 相关阅读:
    测试平台系列(95) 前置条件支持简单的python脚本
    Python爬虫selenium安装谷歌驱动解决办法
    算法----字符串中的最大奇数
    英语语法学习
    QT 使用QPixmap自定义光标 缩放图像模糊问题
    threejs官方demo学习(1):animation
    WebAssembly入门笔记[1]:与JavaScript的交互
    绑定支付卡表单页面html页面前端源码
    棋盘格角点检查
    类和对象学习笔记
  • 原文地址:https://blog.csdn.net/qq_62848601/article/details/139182695