VGA显示8色彩条的实现
使用软件:Quartus II 13.1
使用语言 :Verilog HDL
板载芯片:EP4CE6E22C8
写在前面
由于之前在学校学习的FPGA基础是基于VHDL(Very-High-Speed Integrated Circuit Hardware Description Language)的,但大都已经忘记不太想从头再来,所以再次学习并接触FPGA,想用另一种更接近C语言的HDL(硬件描述语言)–也就是Verilog HDL。
VHDL有entity(实体),architecture(结构体),configuration(配置),package(包),和library(库)。
entity内部主要包括端口的声明,建立一个黑盒子的模型。
architecture内部主要包括行为级的内部逻辑设计,进行各个进程或状态机的配置。
library 用于引用所需要的库文件。常使用库文件
library ieee;
package用于引用需要的包文件。例如,如果要使用std_logic(标准逻辑)类型,应对类型所在包进行声明
use ieee.std_logic_1164.all;
,否则会报错;如果行为级建模是需要运算符重载,则要声明use ieee.std_logic_unsigned.all
,如果需要类型转换从整型转化为逻辑矢量类型的函数CONV_STD_LOGIC_VECTOR(begin,end)
,则需要引用use ieee.std_logic_arith.all
当然与VHDL 类似的 ,Verilog HDL 也有着类似的结构,比如module(模块),procedural(过程块) `include(头文件)对应 于 VHDL的port、process,package。所以如果有C语言和VHDL语言的基础,VerilogHDL是不成问题的。
准备工作
书归正传,了解了使用语言的语法有利于代码的编写和代码的阅读。
我们应该再了解一下VGA相关的知识。
VGA接口
VGA接口是一种D型接口,采用非对称分布连接方式,共有15针.对用户来说,其引出线常用的有5个信号:红色 R,绿色 G,蓝色B,行同步信号HS,场同步信号 VS.HS和 VS的作用是处理输入模拟信号,并联合起来控制VGA的显示时序,分别对应PIN1、PIN2、PIN3、PIN13、PIN14。
是不是有点抽象?这边上图最为直接明了
当然,美术生和 Photoshop使用者应该对色彩不会太陌生,Red、Green、Blue正是三原色,经过不同的混合碰撞,产生色彩缤纷的世界,进而显示再屏幕上,但是硬件内部实现输出的是数字信号,无法被显示在VGA显示屏上,因而需要进行数模转换DAC(Digital Analog Convertor),常用的数模转换芯片是ADV7125,是3个8位视频信号的DAC信号的转换。
但是,芯片开销会比较大,所以一般VGA视频图像信号都使用权电阻网络(如下图所示)来完成
当 R s = 8 R {R_s = 8R } Rs=8R, V o = − V r e f 2 8 D n {V_o = -\frac{V_{ref}}{2^8}D_n} Vo=−28VrefDn, D n 是 高 位 ( M S B ) 或 低 位 ( L S B ) 电 阻 权 重 {D_n}是高位(MSB)或低位(LSB)电阻权重 Dn是高位(MSB)或低位(LSB)电阻权重
VGA 时序
了解了VGA接口的相关知识,进一步了解一下VGA时序的知识,因为书写代码一般是以时序为准。以一般工业标准来说,640*480@60是显示比较稳定且易于掌握的,因而下文皆是根据640*480@60讲述的。
@Attention:640*480@60,表示行列总共有307200个像素点,同时显示一帧图像的速率是60MHz
由上图可知行扫描周期以像素为单位,分别由行同步、后沿、图像有效区域、前沿直到下一帧图像开始,当完成一行的扫描后,场扫描周期开始,同时经过场同步、上沿,图像有效区域、下沿构成。
开始Coding
根据前面的讲解应该对VGA显示有所了解,因而开始Coding。
/* * @author: LangZi * @data:2021.12.26 * @modulename:VGA * @header:include"vga_param.v" */ `include"vga_param.v" module VGA( clock , switch , out_RGB , hsync , vsync ); input clock ; //系统时钟 50MHz input [1:0] switch ; //用于切换模式,显示棋盘格 output [2:0] out_RGB; //VGA输出颜色 output hsync ; //VGA扫描信号 output vsync ; //VGA场扫描信号 reg [9:0] hcount ; //VGA行计数器 reg [9:0] vcount ; //VGA场计数器 reg [2:0] data ; reg [2:0] h_dat ; reg [2:0] v_dat ; reg flag ; //标志位 wire hcount_ov ; //溢出信号 wire vcount_ov ; wire dat_act ; wire hsync ; wire vsync ; reg vga_clk ; //分频操作活得25MHz的时钟 always @(posedge clock) begin vga_clk = ~vga_clk; end //************************VGA驱动******************************* //行计数 always @(posedge vga_clk) begin if (hcount_ov)// 若溢出则清零 hcount <= 10'd0; else hcount <= hcount + 10'd1; end assign hcount_ov = (hcount == hpixel_end); //场计数 always @(posedge vga_clk) begin if (hcount_ov) begin if (vcount_ov) vcount <= 10'd0; else vcount <= vcount + 10'd1; end end assign vcount_ov = (vcount == vline_end); //判断是否处于有效数据区域 assign dat_act = ((hcount >= hdat_begin) && (hcount < hdat_end)) && ((vcount >= vdat_begin) && (vcount < vdat_end)); assign hsync = (hcount > hsync_end); assign vsync = (vcount > vsync_end); assign disp_RGB = (dat_act) ? data : 3'h00; //************************VGA 显示处理操作******************************* always @(posedge vga_clk) begin case(switch[1:0]) 2'd0: data <= h_dat; //行数据 2'd1: data <= v_dat; //列数据 2'd2: data <= (v_dat ^ h_dat); //棋盘格 2'd3: data <= (v_dat ~^ h_dat); //转置棋盘 endcase end always @(posedge vga_clk) //行彩条计数时序 begin if(hcount < 223) v_dat <= 3'h7; else if(hcount < 303) v_dat <= 3'h6; else if(hcount < 383) v_dat <= 3'h5; else if(hcount < 463) v_dat <= 3'h4; else if(hcount < 543) v_dat <= 3'h3; else if(hcount < 623) v_dat <= 3'h2; else if(hcount < 703) v_dat <= 3'h1; else v_dat <= 3'h0; end always @(posedge vga_clk) //列彩条计数时序 begin if(vcount < 94) h_dat <= 3'h7; else if(vcount < 154) h_dat <= 3'h6; else if(vcount < 214) h_dat <= 3'h5; else if(vcount < 274) h_dat <= 3'h4; else if(vcount < 334) h_dat <= 3'h3; else if(vcount < 394) h_dat <= 3'h2; else if(vcount < 454) h_dat <= 3'h1; else h_dat <= 3'h0; end endmodule
时序参数的头文件(vga_param.v)
`ifndef __VGA_PARAM_ `define __VGA_PARAM_640_480_60MHz `define hsync_end 10'd95 `define hdat_begin 10'd143 `define hdat_end 10'd783 `define hpixel_end 10'd799 `define vsync_end 10'd1 `define vdat_begin 10'd34 `define vdat_end 10'd514 `define vline_end 10'd524 `endif
引入头文件,可以提升代码的可移植性,同时可以将文件的代码风格显示的更简洁,易懂
结果显示
写在最后
FPGA的首篇作文,尚有不足,愿共勉。