VGA协议实践
文章目录
- 前言
- 一、VGA接口定义与传输原理
- 1、VGA接口定义
- 2、传输原理
- 3、不同分辨率对应不同参数
- 二、Verilog编程
- 1、VGA显示彩色条纹
- 2、VGA显示字符
- 3、输出一幅彩色图像
- 4、Quartus操作
- 1、添加PLL核
- 2、添加ROM核
- 三、全部代码
- 四、总结
- 五、参考资料
前言
VGA的全称是Video Graphics Array,即视频图形阵列,是一个使用模拟信号进行视频传输的标准。早期的
CRT显示器由于设计制造上的原因,只能接收模拟信号输入,因此计算机内部的显卡负责进行数模转换,而
VGA接口就是显卡上输出模拟信号的接口。如今液晶显示器虽然可以直接接收数字信号,但是为了兼容显卡上
的VGA接口,也大都支持VGA标准。
环境:
1、Quartus 18.1
2、EP4CE115F29C7
3、vscode
一、VGA接口定义与传输原理
1、VGA接口定义

2、传输原理


从上面两幅图中我们可以看到VGA传输过程中的行同步时序和场同步时序非常类似,一行或一场(又称一帧)数据都分为四个部分:低电平同步脉冲、显示后沿、有效数据段以及显示前沿。
行同步信号HSYNC在一个行扫描周期中完成一行图像的显示,其中在a段维持一段时间的低电平用于 数据同
步,其余时间拉高;在有效数据期间(c段) ,红绿蓝三原色数据通道上输出一-行图像信号,其余时间数据无
效。与之类似,场同步信号在在一个场扫描周期中完成一 帧图像的显示, 不同的是行扫描周期的基本单位是像素点时钟,即完成一个像素点显示所需要的时间;而场扫描周期的基本单位是完成一-行图像 显示所需要的时间。
3、不同分辨率对应不同参数

二、Verilog编程
1)屏幕上显示彩色条纹
2)显示自定义的汉字字符(姓名-学号)
3)输出一幅彩色图像
4) 将行、场同步信号中,故意分别加入一定 ms延时(用delay命令),观察会出现什么现象。
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
- 效果:

2、VGA显示字符
- 取字模:
打开字模选项,选择格式:

注意,这里的学号和名字分开取:


注意:这里的汉字取的是16x16的点阵,数字取的是32x16的点阵。
- 代码:
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[ 64: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 ] = 256'h0000010000000000000000000000000000000000000000000000000000000000;char_line[ 1 ] = 256'h1ff0210020000000000000000000000000000000000000000000000000000000;char_line[ 2 ] = 256'h101011dc17fc0000000000000000000000000000000000000000000000000000;char_line[ 3 ] = 256'h101011141040000018003c003c00180018007e001800180018003c003c000400;char_line[ 4 ] = 256'h1ff0811480400000240042004200240024004200240024002400420042000c00;char_line[ 5 ] = 256'h0000411440400000400042004200420042000400420040004200420042000c00;char_line[ 6 ] = 256'h000047d448400000400002004200420042000400420040004200420042001400;char_line[ 7 ] = 256'h3ff81014084000005c000400020042004200080042005c004200020002002400;char_line[ 8 ] = 256'h0100111410407e00620018000400420042000800420062004200040004002400;char_line[ 9 ] = 256'h0100211410400000420004000800420042001000420042004200080008004400;char_line[ 10 ] = 256'hfffee588e0400000420002001000420042001000420042004200100010007f00;char_line[ 11 ] = 256'h0280254820400000420042002000420042001000420042004200200020000400;char_line[ 12 ] = 256'h0440294820400000220042004200240024001000240022002400420042000400;char_line[ 13 ] = 256'h082021142ffe00001c003c007e0018001800100018001c0018007e007e001f00;char_line[ 14 ] = 256'h3018251420000000000000000000000000000000000000000000000000000000;char_line[ 15 ] = 256'hc006022200000000000000000000000000000000000000000000000000000000;end
end//实例化ROM
rom rom_inst (
.address ( rom_address ),
.clock ( vga_clk ),
.q ( rom_data )
);
endmodule // data_drive
- 效果:

3、输出一幅彩色图像

- 使用软件将图片进行转换:

- 生成的hex文件:

这里没能成功显示
4、Quartus操作
先是新建工程引入.V文件即可,下面是添加两个IP核的操作,一个用于时钟分频,一个用于存储图片。
分别使用640×480 60HZ和800×600 72HZ,对应时钟分别为25M和50M,需要使用PLL进行分频 时钟频率 = 行帧长 × 列帧长 * 刷新率,640 ×480 60HZ对应时钟频率= 800 ×525 × 60 = 25.2M。
1、添加PLL核

- 选择基础时钟:

取消勾选输出使能:

- 设置时钟,clk0默认50M即可,c1设为25M.

- 勾选下面的选项最后点击finish即可添加pll核

2、添加ROM核
- 搜索RO核:

- 把宽度设为16位,然后根据图片的大小填写160x154=24640,根据自己的图片来填写。

- 取消下面的勾选:

- 引入前面生成的hex文件:

- 勾选下面选项然后点击finish:

三、全部代码
- data_drive文件:
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[ 64: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 ] = 256'h0000010000000000000000000000000000000000000000000000000000000000;char_line[ 1 ] = 256'h1ff0210020000000000000000000000000000000000000000000000000000000;char_line[ 2 ] = 256'h101011dc17fc0000000000000000000000000000000000000000000000000000;char_line[ 3 ] = 256'h101011141040000018003c003c00180018007e001800180018003c003c000400;char_line[ 4 ] = 256'h1ff0811480400000240042004200240024004200240024002400420042000c00;char_line[ 5 ] = 256'h0000411440400000400042004200420042000400420040004200420042000c00;char_line[ 6 ] = 256'h000047d448400000400002004200420042000400420040004200420042001400;char_line[ 7 ] = 256'h3ff81014084000005c000400020042004200080042005c004200020002002400;char_line[ 8 ] = 256'h0100111410407e00620018000400420042000800420062004200040004002400;char_line[ 9 ] = 256'h0100211410400000420004000800420042001000420042004200080008004400;char_line[ 10 ] = 256'hfffee588e0400000420002001000420042001000420042004200100010007f00;char_line[ 11 ] = 256'h0280254820400000420042002000420042001000420042004200200020000400;char_line[ 12 ] = 256'h0440294820400000220042004200240024001000240022002400420042000400;char_line[ 13 ] = 256'h082021142ffe00001c003c007e0018001800100018001c0018007e007e001f00;char_line[ 14 ] = 256'h3018251420000000000000000000000000000000000000000000000000000000;char_line[ 15 ] = 256'hc006022200000000000000000000000000000000000000000000000000000000;end
end//实例化ROM
rom rom_inst (
.address ( rom_address ),
.clock ( vga_clk ),
.q ( rom_data )
);
endmodule // data_drive
- key_debounce文件:
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
- vga_dirve文件:
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
- vga_top顶层文件:
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
四、总结
了解了VGA显示的原理,其实就是为每个坐标分配一个RGB三通道的值,就是每个像素,场信号扫描得很快就能连成一幅图像。掌握了两种IP核的使用,就是图片没能成功显示。
五、参考资料
https://blog.csdn.net/qq_45659777/article/details/124834294
https://blog.csdn.net/qq_47281915/article/details/125134764
相关文章:
VGA协议实践
文章目录 前言一、VGA接口定义与传输原理1、VGA接口定义2、传输原理3、不同分辨率对应不同参数 二、Verilog编程1、VGA显示彩色条纹2、VGA显示字符3、输出一幅彩色图像4、Quartus操作1、添加PLL核2、添加ROM核 三、全部代码四、总结五、参考资料 前言 VGA的全称是Video Graphi…...
毕业5年的同学突然告诉我,他已经是年薪30W的自动化测试工程师....
作为一名程序员,都会对自己未来的职业发展而焦虑。一方面是因为IT作为知识密集型的行业,知识体系复杂且知识更新速度非常快,“一日不学就会落后”。 另外一方面,IT又是劳动密集型的行业,不仅业人员多,而且个…...
操作系统原理 —— 进程有哪几种状态?状态之间如何切换?(七)
进程的五种状态 首先我们一起来看一下进程在哪些情况下,会有不同的状态表示。 创建态、就绪态 当我们刚开始运行程序的时候,操作系统把可执行文件加载到内存的时候,进程正在被创建的时候,它的状态是创建态,在这个阶…...
可算是熬出头了,测试4年,费时8个月,入职阿里,涨薪14K
前言 你的努力,终将成就无可替代的自己。 本科毕业后就一直从事测试的工作,和多数人一样,最开始从事点点点的工作,看着自己的同学一步一步往上走,自己还是在原地踏步,说实话这不是自己想要的状态。 一年半…...
5款十分小众的软件,知道的人不多但却很好用
今天推荐5款十分小众的软件,知道的人不多,但是每个都是非常非常好用的,有兴趣的小伙伴可以自行搜索下载。 1.视频直播录制——OBS Studio OBS Studio可以让你轻松地录制和直播你的屏幕、摄像头、游戏等内容。你可以使用OBS Studio来创建多种…...
Linux驱动开发:uboot启动流程详解
前言:uboot作为Linux驱动开发的 “三巨头” 之一,绝对是一座绕不开的大山。当然,即使不去细致了解uboot启动流程依旧不影响开发者对uboot的简单移植。但秉持着知其然知其所以然的学习态度,作者将给读者朋友细致化的过一遍uboot启动…...
分治与减治算法实验: 排序中减治法的程序设计
目录 前言 实验内容 实验目的 实验分析 实验过程 流程演示 写出伪代码 实验代码 代码详解 运行结果 总结 前言 本文介绍了算法实验排序中减治法的程序设计。减治法是一种常用的算法设计技术,它通过减少问题的规模来求解问题。减治法可以应用于排序问题&…...
leetcode两数、三数、四数之和
如有错误,感谢不吝赐教、交流 文章目录 两数之和题目方法一:暴力两重循环(不可取)方法二:HashMap空间换时间 三数之和题目方法一:当然是暴力破解啦方法二:同两数之和的原理,借助Has…...
使用Docker部署wikitten个人知识库
使用Docker部署wikitten个人知识库 一、wikitten介绍1.wikitten简介2.wikitten特点 二、本地实践环境介绍三、本地环境检查1.检查Docker服务状态2.检查Docker版本 四、部署wikitten个人知识库1.创建数据目录2.下载wikitten镜像3.创建wikitten容器4.查看wikitten容器状态5.检查w…...
【MYSQL】Java的JDBC编程(idea连接数据库)
1. 配置 (1)新建一个项目 (2)Build System 那里选择Maven,下一步Create (3)配置pom.xml文件 首先查看自己的MYSQL版本:进入MySQL命令窗口 我的MYSQL版本是8.0版本的. 下一步,…...
机器学习——主成分分析法(PCA)概念公式及应用python实现
机器学习——主成分分析法(PCA) 文章目录 机器学习——主成分分析法(PCA)一、主成分分析的概念二、主成分分析的步骤三、主成分分析PCA的简单实现四、手写体识别数字降维 一、主成分分析的概念 主成分分析(PCA&#x…...
手写axios源码系列二:创建axios函数对象
文章目录 一、模块化目录介绍二、创建 axios 函数对象1、创建 axios.js 文件2、创建 defaults.js 文件3、创建 _Axios.js 文件4、总结 当前篇章正式进入手写 axios 源码系列,我们要真枪实弹的开始写代码了。 因为 axios 源码的代码量比较庞大,所以我们这…...
HTB-Time
HTB-Time 信息收集80端口 立足pericles -> root 信息收集 80端口 有两个功能,一个是美化JSON数据。 一个是验证JSON,并且输入{“abc”:“abc”}之类的会出现报错。 Validation failed: Unhandled Java exception: com.fasterxml.jackson.core.JsonPa…...
零基础C/C++开发到底要学什么?
作者:黑马程序员 链接:https://www.zhihu.com/question/597037176/answer/2999707086 先和我一起看看,C/C学完了可以做什么: 软件工程师:负责设计、开发、测试和维护各类型的软件应用程序;游戏开发&#x…...
OpenStack中的CPU与内存超分详解
目录 什么是超分 CPU超分 查看虚拟机虚拟CPU运行在哪些物理CPU上 内存超分 内存预留 内存共享 如何设置内存预留和内存共享 全局设置 临时设置 什么是超分 超分通常指的是CPU或者GPU的分区或者分割,以在一个物理CPU或GPU内模拟多个逻辑CPU或GPU的功能。这…...
main.m文件解析--@autoreleasepool和UIApplicationMain
iOS 程序入口UIApplicationMain详解,相信大家新建一个工程的时候都会看到一个main.m文件,只不过我们很少了解它,现在我们分析一下它的作用是什么? 一、main.m文件 int main(int argc, char * argv[]) {autoreleasepool {return …...
C语言复习之顺序表(十五)
📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c阶段>——目标C、Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享…...
学系统集成项目管理工程师(中项)系列10_立项管理
1. 系统集成项目管理至关重要的一个环节 2. 重点在于是否要启动一个项目,并为其提供相应的预算支持 3. 项目建议 3.1. Request for Proposal, RFP 3.2. 立项申请 3.3. 项目建设单位向上级主管部门提交的项目申请文件,是对拟建项目提出的总体设想 3…...
电视盒子哪个好?数码小编盘点2023电视盒子排行榜
随着网络剧的热播,电视机又再度受宠,电视盒子也成为不可缺少的小家电。但面对复杂的参数和品牌型号,挑选时不知道电视盒子哪款最好,小编根据销量和用户评价整理半个月后盘点了电视盒子排行榜前五,对电视盒子哪个好感兴…...
flink动态表的概念详解
目录 前言🚩 动态表和持续不断查询 stream转化成表 连续查询 查询限制 表转化为流 前言🚩 传统的数据库SQL和实时SQL处理的差别还是很大的,这里简单列出一些区别: 尽管存在这些差异,但使用关系查询和SQL处理流并…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
