文章目录
- 一、VGA介绍
- 1.1 VGA原理
- 1.2VGA电路
- 二、配置
- 三、实现
- 3.1 字符显示
- 3.2图片显示
- 四、代码
- 4.1.vga驱动模块
- 4.2数据模块
- 4.3按键消抖模块
- 4.4顶层模块
- 4.5TCL引脚绑定
- 参考
一、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时钟 25Moutput 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 ) beginif ( !rst_n ) begincnt_h <= 0;endelse if ( flag_enable_cnt_h ) beginif ( flag_clear_cnt_h ) begincnt_h <= 0;endelse begincnt_h <= cnt_h + 1;endendelse begincnt_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 ) beginif ( !rst_n ) beginh_sync <= 0;endelse if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1h_sync <= 1;endelse if ( flag_clear_cnt_h ) begin // 其余为0h_sync <= 0;endelse beginh_sync <= h_sync;end
end// 场计数
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) begincnt_v <= 0;endelse if ( flag_enable_cnt_v ) beginif ( flag_clear_cnt_v ) begincnt_v <= 0;endelse if ( flag_add_cnt_v ) begincnt_v <= cnt_v + 1;endelse begincnt_v <= cnt_v;endendelse begincnt_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 ) beginif ( !rst_n ) beginv_sync <= 0;endelse if ( cnt_v == V_SYNC - 1 ) beginv_sync <= 1;endelse if ( flag_clear_cnt_v ) beginv_sync <= 0;endelse beginv_sync <= v_sync;end
end// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginaddr_h <= 0;endelse if ( valid_area ) beginaddr_h <= cnt_h - H_SYNC - H_BLACK + 1;endelse beginaddr_h <= 0;end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginaddr_v <= 0;endelse if ( valid_area ) beginaddr_v <= cnt_v -V_SYNC - V_BLACK + 1;endelse beginaddr_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 ) beginif ( !rst_n ) beginrgb <= 16'h0;endelse if ( valid_area ) beginrgb <= rgb_data;endelse beginrgb <= 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 ) beginif ( !rst_n ) beginstates_current <= states_1;endelse beginstates_current <= states_next;end
end//状态判断
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginstates_next <= states_1;endelse if ( key[ 0 ] ) beginstates_next <= states_1;endelse if ( key[ 1 ] ) beginstates_next <= states_2;endelse if ( key[ 2 ] ) beginstates_next <= states_3;endelse beginstates_next <= states_next;end
end//状态输出
always @( * ) begincase ( states_current )states_1 : beginif ( addr_h == 0 ) beginrgb_data = black;endelse if ( addr_h >0 && addr_h <81 ) beginrgb_data = red;endelse if ( addr_h >80 && addr_h <161 ) beginrgb_data = orange;endelse if ( addr_h >160 && addr_h <241 ) beginrgb_data = yellow;endelse if ( addr_h >240 && addr_h <321 ) beginrgb_data = green;endelse if ( addr_h >320 && addr_h <401 ) beginrgb_data = blue;endelse if ( addr_h >400 && addr_h <481 ) beginrgb_data = indigo;endelse if ( addr_h >480 && addr_h <561 ) beginrgb_data = purple;endelse if ( addr_h >560 && addr_h <641 ) beginrgb_data = white;endelse beginrgb_data = black;endendstates_2 : beginif ( flag_enable_out1 ) beginrgb_data = char_line[ addr_v-208 ][ 532 - addr_h ]? white:black;endelse beginrgb_data = black;endendstates_3 : beginif ( flag_enable_out2 ) beginrgb_data = rom_data;endelse beginrgb_data = black;endenddefault: begincase ( 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;endcaseendendcase
endassign 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 ) beginif ( !rst_n ) beginrom_address <= 0;endelse if ( flag_clear_rom_address ) begin //计数满清零rom_address <= 0;endelse if ( flag_enable_out2 ) begin //在有效区域内+1rom_address <= rom_address + 1;endelse 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 ) beginif ( !rst_n ) beginchar_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_000reg key_reg;//key上一次的值always @(posedge clk or negedge rst_n) beginif(!rst_n) beginkey_reg <= 1;delay_cnt <= 0;endelse beginkey_reg <= key;//当key为1 key 为0 表示按下抖动,开始计时if(key_reg != key ) begin delay_cnt <= MAX_NUM ;endelse beginif(delay_cnt > 0)delay_cnt <= delay_cnt -1;elsedelay_cnt <= 0;endend
end//当计时完成,获取key的值
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflag <= 0;key_value <= 1;endelse begin// 计时完成 处于稳定状态,进行赋值if(delay_cnt == 1) beginflag <= 1;key_value <= key;endelse beginflag <= 0;key_value <= key_value;endend
endendmodule
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 ) beginif ( !rst_n ) begincnt <= 0;endelse if ( cnt == 50_000_000 - 1 ) begincnt <= 0;endelse begincnt <= cnt + 1;end
end
always @( posedge clk or negedge rst_n ) beginif ( !rst_n ) beginled <= 4'b0000;endelse if ( cnt == 50_000_000 -1 )beginled <= ~led;endelse beginled <= led;end
end
endmodule // vga_top
4.5TCL引脚绑定
package require ::quartus::projectset_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_syncset_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