FPGA——用VGA时序显示图像原理详解(1)_居安士的博客-CSDN博客_vga时序
FPGA——用VGA时序显示图像原理详解(2)_居安士的博客-CSDN博客_vga显示时序
关于VGA时序的原理可以看上面2篇(建议先看),这一篇接(2)写VGA的代码实现
首先关于VGA时序,我们一共需要控制的信号线有5条:行同步+场同步+红+绿+蓝
- input clk,
- input rst,
- output hs ,//行同步信号
- output vs ,//场同步信号
- output de ,//有效数据
- output [7:0] rgb_r,//red
- output [7:0] rgb_g,//green
- output [7:0] rgb_b //blue
我们需要控制何时把hs和vs拉高,拉低,就需要根据数据手册上,不同图像的像素来设置前肩,后肩,同步信号,数据有效信号(如何查看数据手册已经在前面解析过)这里建议大家使用条件编译的方法来练习,这样可以方便我们后续图像像素变化时进行修改
- //640x480 25.175Mhz 按照数据手册修改参数
- `ifdef VIDEO_640_480
- parameter H_ACTIVE = 16'd640; //行有效
- parameter H_FP = 16'd16; //行前肩
- parameter H_SYNC = 16'd96; //行同步
- parameter H_BP = 16'd48; //行后肩
- parameter V_ACTIVE = 16'd480; //场有效
- parameter V_FP = 16'd10; //场前肩
- parameter V_SYNC = 16'd2; //场同步
- parameter V_BP = 16'd33; //场后肩
- parameter HS_POL = 1'b0; //
- parameter VS_POL = 1'b0; //
- `endif
设置好了这些参数之后,我们需要设置2个计数器,分别是行计数器和场计数器,当一行计数完成之后,场计数器+1
- //行计数器进行计数
- always@(posedge clk)begin
- if(rst)begin
- h_cnt<=12'd0;
- end
- else if(h_cnt==H_total-1)begin
- h_cnt<=12'd0;
- end
- else begin
- h_cnt<=h_cnt+12'd1;
- end
- end
- //场计数器进行计数
- always@(posedge clk)begin
- if(rst)begin
- v_cnt<=12'd0;
- end
- else if(h_cnt==H_total-1)begin//行同步信号拉高一次,v_cnt+1
- if(v_cnt==V_total-1)begin
- v_cnt<=12'd0;
- end
- else begin
- v_cnt<=v_cnt+12'd1;
- end
- end
- else begin
- v_cnt<=v_cnt;
- end
- end
接下来我们就可以根据计数器,输出行同步信号和场同步信号
这里,计数是从前肩开始,所以当行计数器=前肩,行同步信号拉高,计数器=前肩+行同步,行同步信号拉低
- //产生行同步信号
- always@(posedge clk)begin
- if(reset)begin
- hs_reg<=1'd0;
- end
- else if(h_cnt==H_FP-1)begin
- hs_reg<=1'd1;
- end
- else if(h_cnt==H_FP+H_SYNC-1)begin
- hs_reg<=1'd0;
- end
- else begin
- hs_reg<=hs_reg;
- end
- end
场同步信号类似,需要注意的是,每一个场计数器里面都有行总个数个周期(eg: v_cnt=V_HP到v_cnt=V_HP+1时里面其实有640个clk,在哪个clk拉高呢?),但是我们的场计数器只能在一个确定的时刻拉高,所以这里还需&&上一个特定的时刻
- //产生场同步信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- vs_reg <= 1'd0;
- else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//场同步信号开始,&&(h_cnt == H_FP - 1)目的是在多个时钟周期里面选择一个时钟拉高场同步
- vs_reg <= 1'd1;//HS_POL=1
- else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//场同步信号结束
- vs_reg <= 1'd0;
- else
- vs_reg <= vs_reg;
- end
数据有效的指示信号de,是在行有效&&场有效时输出
行有效是在当行计数器=前肩+行同步+后肩,行同步信号拉高,计数器=前肩+行同步+后肩+数据个数,行同步信号拉低
场有效信号与场同步信号相似,也需要&&上一个特定的时刻
- //产生行有效信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- h_active <= 1'b0;
- else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//行有效开始
- h_active <= 1'b1;
- else if(h_cnt == H_TOTAL - 1)//行有效结束
- h_active <= 1'b0;
- else
- h_active <= h_active;
- end
-
- //产生场有效信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- v_active <= 1'd0;
- else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//场有效开始
- v_active <= 1'b1;
- else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1)) //场有效结束
- v_active <= 1'b0;
- else
- v_active <= v_active;
- end
接下来需要实现RGB分量的输出,这里实现彩条数据的输出,只需要给RGB某个颜色的值,就可以实现某个颜色的输出。
- reg[7:0] rgb_r_reg; //红
- reg[7:0] rgb_g_reg; //绿
- reg[7:0] rgb_b_reg; //蓝
-
- always@(posedge clk)begin
- if(rst)begin
- rgb_r_reg<=8'd0;
- rgb_g_reg<=8'd0;
- rgb_b_reg<=8'd0;
- end
- else begin
- rgb_r_reg<=8'd0;
- rgb_g_reg<=8'd0;
- rgb_b_reg<=8'hff;
- end
- end
-
- assign rgb_r = rgb_r_reg;
- assign rgb_g = rgb_g_reg;
- assign rgb_b = rgb_b_reg;
仿真一下:
可以看出RGB分量输出没问题, 貌似也是一幅图一幅图地输出了,我们放大看一下计数情况
首先看行同步信号输出情况,可以看出行计数器一共计数800,是对的,行前肩结束之后,行同步信号拉高了,之后持续了行同步时间,没有问题
再看看场同步信号,确实是一幅图传输完毕后输出一次
de信号也是符合的
完整代码如下:
- module VGA(
- input clk,
- input rst,
- output hs ,//行同步信号
- output vs ,//场同步信号
- output de ,//有效数据
- output [7:0] rgb_r,//red
- output [7:0] rgb_g,//green
- output [7:0] rgb_b //blue
- );
-
- //640x480 25.175Mhz 按照数据手册修改参数
- //`ifdef VIDEO_640_480
- parameter H_ACTIVE = 16'd640; //行有效
- parameter H_FP = 16'd16; //行前肩
- parameter H_SYNC = 16'd96; //行同步
- parameter H_BP = 16'd48; //行后肩
- parameter V_ACTIVE = 16'd480; //场有效
- parameter V_FP = 16'd10; //场前肩
- parameter V_SYNC = 16'd2; //场同步
- parameter V_BP = 16'd33; //场后肩
- parameter HS_POL = 1'b0; //
- parameter VS_POL = 1'b0; //
- //`endif
-
- parameter H_total=H_ACTIVE+H_FP+H_SYNC+H_BP;//行总时间
- parameter V_total=V_ACTIVE+V_SYNC+V_FP+V_BP;//场总时间
-
- reg hs_reg;//行同步信号寄存
- reg vs_reg;//场同步信号寄存
- //定义行场计数器
- reg [11:0]h_cnt;
- reg [11:0]v_cnt;
-
- reg[7:0] rgb_r_reg; //红
- reg[7:0] rgb_g_reg; //绿
- reg[7:0] rgb_b_reg; //蓝
-
- always@(posedge clk)begin
- if(rst)begin
- rgb_r_reg<=8'd0;
- rgb_g_reg<=8'd0;
- rgb_b_reg<=8'd0;
- end
- else begin
- rgb_r_reg<=8'd0;
- rgb_g_reg<=8'd0;
- rgb_b_reg<=8'hff;
- end
- end
-
- assign rgb_r = rgb_r_reg;
- assign rgb_g = rgb_g_reg;
- assign rgb_b = rgb_b_reg;
-
-
- reg h_active; //行有效
- reg v_active; //场有效
- wire video_active; //数据有效
- assign video_active = h_active & v_active;//数据有效=行有效且列有效
- assign de=video_active;
-
- //行计数器进行计数
- always@(posedge clk)begin
- if(rst)begin
- h_cnt<=12'd0;
- end
- else if(h_cnt==H_total-1)begin
- h_cnt<=12'd0;
- end
- else begin
- h_cnt<=h_cnt+12'd1;
- end
- end
- //场计数器进行计数
- always@(posedge clk)begin
- if(rst)begin
- v_cnt<=12'd0;
- end
- else if(h_cnt==H_total-1)begin//行同步信号拉高一次,v_cnt+1
- if(v_cnt==V_total-1)begin
- v_cnt<=12'd0;
- end
- else begin
- v_cnt<=v_cnt+12'd1;
- end
- end
- else begin
- v_cnt<=v_cnt;
- end
- end
-
-
- //产生行同步信号
- always@(posedge clk)begin
- if(rst)begin
- hs_reg<=1'd0;
- end
- else if(h_cnt==H_FP-1)begin
- hs_reg<=1'd1;
- end
- else if(h_cnt==H_FP+H_SYNC-1)begin
- hs_reg<=1'd0;
- end
- else begin
- hs_reg<=hs_reg;
- end
- end
- //产生场同步信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- vs_reg <= 1'd0;
- else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//场同步信号开始,&&(h_cnt == H_FP - 1)目的是在多个时钟周期里面选择一个时钟拉高场同步
- vs_reg <= 1'd1;
- else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//场同步信号结束
- vs_reg <= 1'd0;
- else
- vs_reg <= vs_reg;
- end
- //产生行有效信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- h_active <= 1'b0;
- else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//行有效开始
- h_active <= 1'b1;
- else if(h_cnt == H_total - 1)//行有效结束
- h_active <= 1'b0;
- else
- h_active <= h_active;
- end
- //产生场有效信号
- always@(posedge clk or posedge rst)
- begin
- if(rst == 1'b1)
- v_active <= 1'd0;
- else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//场有效开始
- v_active <= 1'b1;
- else if((v_cnt == V_total - 1) && (h_cnt == H_FP - 1)) //场有效结束
- v_active <= 1'b0;
- else
- v_active <= v_active;
- end
-
-
-
- endmodule