vivado学习第三天 vga

vga的显示有很多标准,如下图所示:
vivado学习第三天 vga_第1张图片
实现800*600*60Hz为例。(800为列数,600为行数,60为刷新频率)
行时序:屏幕对应的行数为 628(a+b+c+d=e 段),其中 600(c 段)为
显示行;每行均有行同步信号(a 段),为 4 个行周期的低电平;
列时序:每个显示行包括 1056 列(a+b+c+d=e 段),其中 800(c 段)
为有效显示区,每一行有一个行同步信号(a 段),为 128 个行周期的低电
平。
扫描时钟频率:40MHZ
根据功能可以将vga显示分为以下三个部分:时钟生成模块(产生40MHz的时钟)、vga的行列信号同步模块(用来产生行列同步信号,同时规划显示的范围)、r_g_b信号数据输入模块(将人r、g、b三种颜色信号通过rom进行输入)。
vivado学习第三天 vga_第2张图片
(1)时钟产生模块调用pll锁相环ip核来产生40Mhz的时钟信号
(2)vga行列同步信号产生模块代码

module vga(    //显示分辨率为800 * 600 *60 MHZ
//端口信号:模块的输入输出接口
    input        clk,   //连接至分频时钟,为40MHz
    input        rst_n, //低电平复位

    output reg   vga_hs,//VGA列同步信号
    output reg   vga_vs,//VGA行同步信号
    output       en_zx     //显示有效区域的使能信号
    );

//----------------VGA时序-----------------------------------
//      显示模式        时钟     
//      800*600@60  40Mhz   
//      同步  后沿  有效  前沿  周期
//hs    128 88      800 40      1056
//vs    4       23      600 1       628

//VGA显示相应参数
parameter   hy_all = 11'd1056,  //列时序
                hy_a     = 11'd128,
                hy_b     = 11'd88,
                hy_c   = 11'd800,
                hy_d   = 11'd40,

                vy_all = 11'd628,      //行时序
                vy_a     = 11'd4,
                vy_b     = 11'd23,
                vy_c   = 11'd600,
                vy_d   = 11'd1;

//用计数器限定VGA显示相应区域,通过“行扫描,列填充”的方式            
reg [10:0] cnt_h,cnt_v;             
always@(posedge clk or negedge rst_n) //列计数
    if(!rst_n)
        cnt_h   <= 11'd0;
    else    if(cnt_h == (hy_all-1))
        cnt_h   <= 11'd0;
    else    
        cnt_h <= cnt_h + 1'b1;

always@(posedge clk or negedge rst_n)//在一列计数完之后将行加1
    if(!rst_n)
        cnt_v   <= 11'd0;
    else    if(cnt_v == (vy_all-1))
        cnt_v   <= 11'd0;
    else    if(cnt_h == (hy_all-1))
        cnt_v   <= cnt_v + 1'b1;

//限定列同步信号       
always@(posedge clk or negedge rst_n) 
    if(!rst_n)
        vga_hs  <= 1'b1;
    else if(cnt_h ==(hy_all-1))
        vga_hs  <= 1'b0;
    else if(cnt_h == hy_a)
        vga_hs  <= 1'b1;        

//限定行同步信号       
always@(posedge clk or negedge rst_n)
    if(!rst_n)
       vga_vs   <= 1'b0;
    else if(cnt_v ==(vy_all-1))
        vga_vs  <= 1'b0;
    else   if(cnt_v == vy_a)
        vga_vs  <= 1'b1;

//限定显示有效区域,设定使能信号   
wire [12:0] en1,en2;
wire en;        
assign en1 = (cnt_h >= hy_a + hy_b && cnt_h <= hy_a + hy_b + hy_c)?(cnt_h-hy_a-hy_b):11'd0;//列有效区域
assign en2 = (cnt_v >= vy_a + vy_b && cnt_v <= vy_a + vy_b + vy_c)?(cnt_v-vy_a-vy_b):11'd0;//行有效区域
assign en = (en1 > 0 && en2 > 0 )?1'b1:1'b0;//行、列共同有效区域800*600

//将屏幕的中间部分以使能信号的方式划分出来(根据行、列的限定范围:图片大小为32*16
wire en_h,en_v;
assign en_h = (en && (cnt_h >= hy_a + hy_b + 10'd400 && cnt_h <= hy_a + hy_b + 10'd431))?1'b1:1'b0;//限定列区域:32
assign en_v = (en && (cnt_v >= vy_a + vy_b + 10'd300 && cnt_v <= vy_a + vy_b + 10'd315))?1'b1:1'b0;//限定行区域:16

assign en_zx = (en_h && en_v)?1'b1:1'b0; //将行区域与列区域综合形成屏幕的中间区域32*16规划出来 显示李磊的字样

endmodule

(3)rgb数据输入模块

module r_g_b(    //颜色控制模块
//端口信号:模块的输入输出接口
    input           clk,   //连接至分频时钟,为40MHz
    input       rst_n, //低电平复位
    input       en_zx,  //红色使能信号

    output [2:0]  vga_r, //红色(3位:根据数值的变化,控制颜色的深浅)
    output [2:0]  vga_g, //绿色(3位:根据数值的变化,控制颜色的深浅)
    output [1:0]  vga_b  //蓝色(2位:根据数值的变化,控制颜色的深浅)
    );

wire [7:0]  data;  //rom取出的值寄存器
reg  [6:0]  cnt_ad;//rom地址控制信号
reg  [3:0]  cnt_zx;//对应一个数据的8位相应的计数信号

//例化调用rom       
rom rom(
    .address(cnt_ad),
    .clock(clk),
    .q(data)
    );

//通过计数器取地址0-63
wire  en_ad;  //地址变化的使能信号:每取完一个8位数据后,地址加1,从而取下一个8位数据
always@(posedge clk or negedge rst_n)
    if(!rst_n)
            cnt_zx <= 0;
    else if(en_zx)
                begin
                    if(cnt_zx == 7)
                        cnt_zx <= 0;
                    else
                        cnt_zx <= cnt_zx + 1'b1;    
                end
    else
            cnt_zx <= 0;

assign  en_ad = (cnt_zx == 5)?1'b1:1'b0;    //当计数到5时,将信号拉高一个时钟,那么en_ad作为地址计数的使能信号,驱支cnt_ad在下一个时钟,(即cnt_zx==6)时地址加1,
                                                        //地址控制信号cnt_ad又在下一个时钟(cnt_zx==7)时传入rom (这是常见的时序问题,值得思考一下)

//在使能信号en_ad为高电平时地址加1
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt_ad <= 0;
    else if(cnt_ad <= 63)
             begin
                if(en_ad)
                    cnt_ad <= cnt_ad + 1'b1;    
             end
    else
        cnt_ad <= 0;


//在使能区域内,通过查找表的方式,把数据的8位依次从低到高取出                    
reg vga_r_r;
always@(*)
    if(en_zx)
    case(cnt_zx)
        0: vga_r_r <= data[7];  //取第0位
        1: vga_r_r <= data[6];  //取第1位
        2: vga_r_r <= data[5];  //取第2位
        3: vga_r_r <= data[4];  //取第3位
        4: vga_r_r <= data[3];  //取第4位
        5: vga_r_r <= data[2];  //取第5位
        6: vga_r_r <= data[1];  //取第6位
        7: vga_r_r <= data[0];  //取第7位
        default:;
    endcase 

//将1位数据通过拼接符号组合成3位数据:增加颜色的深度
assign vga_r = (en_zx)?{3{vga_r_r}}:3'b000;//在32*16的有效区域内字符用红色表示
assign vga_g = 3'b000;
assign vga_b = 2'b00; 
endmodule

(4)rom模块采用调用ip核的方式实现,通过mif文件来初始化rom中的数据。

你可能感兴趣的:(vga,VIVADO)