设计框图

框图和HDMI彩条显示的框图完全一样,没有改变,只是将之前的HDMI控制模块生产彩条像素信息的内容 改成了显示方块像素的设计:

 

 

 

所以我们只需要修改HDMI控制模块 hdmi_ctrl.v。

 引入坐标

 这次修改,我们引入了像素坐标和方块坐标,方便定位方块位置。

设计代码

代码里面有非常详实的中文注解:

module hdmi_ctrl (

    input     wire                     clk,
    input     wire                         rst_n,

    output    reg        [23:0]      hdmi_rgb,
    output    reg                    hdmi_hs,
    output     reg                             hdmi_vs,
    output    wire                        video_de    //数据使能  
);


//1280*720分辨率时序参数
   localparam            HS_A    =    40;                // synchronous pulse, horizontal
   localparam            HS_B    =    220;                // back porch pulse
   localparam            HS_C    =    1280;                // display interval
   localparam            HS_D    =    110;                // Front porch
   localparam            HS_E    =    1650;                // horizontal cycles

   localparam            VS_A    =    5;                    // synchronous pulse, vertical
   localparam            VS_B    =    20;
   localparam            VS_C    =    720;    
   localparam            VS_D    =    5;    
   localparam            VS_E    =    750;    
    
   localparam            HS_WIDTH    =    11;
    localparam            VS_WIDTH    =    10;

     
     
//方块参数定义
    localparam                 SIDE_W = 11'd40; //屏幕边框宽度
    localparam                 BLOCK_W = 11'd40; //方块宽度
    localparam                 BLUE = 24'b00000000_00000000_11111111; //屏幕边框颜色 蓝色
    localparam                 WHITE = 24'b11111111_11111111_11111111; //背景颜色 白色
    localparam                 BLACK = 24'b00000000_00000000_00000000; //方块颜色 黑色


   reg        [HS_WIDTH - 1:0]        cnt_hs;                // counter for vertical synchronous signal一行的像素个数计数器
   reg        [VS_WIDTH - 1:0]        cnt_vs;                // counter for horizontal synchrous signal行个数计数器
    

    reg             [21:0]                         div_cnt; //时钟分频计数器,用于计数方块移动间隔
    
    reg                                             h_direct; //方块水平移动方向,1:右移,0:左移
    reg                                             v_direct; //方块竖直移动方向,1:向下,0:向上
    
    reg             [10:0]                         block_x = SIDE_W ; //方块左上角横坐标
    reg             [10:0]                        block_y = SIDE_W ; //方块左上角纵坐标
     
    wire             [10:0]                         pixel_xpos_w ; //HDMI 横坐标
    wire             [10:0]                         pixel_ypos_w ; //HDMI 纵坐标
    
    wire                    en_hs;     // dsiplay horizontal enable
   wire                    en_vs;     // display vertical enable
    
    wire                             move_en;   //方块移动使能信号,频率为 100hz


//一行像素个数计数
   always @ (posedge clk, negedge rst_n)
        if (!rst_n)
            cnt_hs <= 0;
        else
            if (cnt_hs < HS_E - 1)
                cnt_hs <= cnt_hs + 1'b1;
            else
                cnt_hs <= 0;
                
 //一帧中的行的个数计数
 always @ (posedge clk, negedge rst_n)
        if (!rst_n)
            cnt_vs <= 0;
        else
            if (cnt_hs == HS_E - 1)
                if (cnt_vs < VS_E - 1)
                    cnt_vs <= cnt_vs + 1'b1;
                else
                    cnt_vs <= 0;
            else
                cnt_vs <= cnt_vs;
 

//产生行同步信号输出
    always @ (posedge clk, negedge rst_n)
        if (!rst_n)
            hdmi_hs <= 1'b1;
        else
            if (cnt_hs < HS_A - 1) //同步之前hdmi_hs信号都是低, 同步之后(a)hdmi_hs信号是高
                hdmi_hs <= 1'b0;
            else
                hdmi_hs <= 1'b1;
                     
                     
 //产生场同步信号输出              
    always @ (posedge clk, negedge rst_n)
        if (!rst_n)
            hdmi_vs <= 1'b1;
        else
            if (cnt_vs < VS_A - 1) //同步之前hdmi_vs 信号都是低, 同步之后(a)hdmi_vs 信号是高
                hdmi_vs <= 1'b0;
            else
                hdmi_vs <= 1'b1;
        
    assign en_hs = (cnt_hs > HS_A + HS_B - 1)&& (cnt_hs < HS_E - HS_D);//en_hs 将有效数据c段标出来了,在有效数据c段en_hs 才为高,否则为低
    assign en_vs = (cnt_vs > VS_A + VS_B - 1) && (cnt_vs < VS_E - VS_D);//en_vs 将有效数据q段标出来了,在有效数据q段en_vs 才为高,否则为低
   
    
    assign video_de = en_hs && en_vs;//将HDMI显示的有效像素点位置全部标注出来了
    
    //产生像素点坐标                
    assign pixel_xpos_w = video_de ? (cnt_hs - (HS_A + HS_B - 1'b1)) : 10'd0;
    assign pixel_ypos_w = video_de ? (cnt_vs - (VS_A + VS_B - 1'b1)) : 10'd0;

//方块移动使能,每10ms移动一次
    assign move_en = (div_cnt == 22'd742500) ? 1'b1 : 1'b0;

//通过对 vga 驱动时钟计数,实现时钟分频(实现100M频率,也就是实现10ms计数)
    always @(posedge clk ) begin 
        if (!rst_n)
            div_cnt <= 22'd0;
        else begin
            if(div_cnt < 22'd742500)
                div_cnt <= div_cnt + 1'b1;
            else
                div_cnt <= 22'd0; //计数达 10ms 后清零
        end
    end

//当方块移动到边界时,改变移动方向
    always @(posedge clk ) begin
        if (!rst_n) begin
            h_direct <= 1'b1; //方块初始水平向右移动
            v_direct <= 1'b1; //方块初始竖直向下移动
        end
        else
        begin
        if(block_x == SIDE_W - 1'b1) //到达左边界时,水平向右
            h_direct <= 1'b1; 
        else //到达右边界时,水平向左
        if(block_x == HS_C - SIDE_W - BLOCK_W)
            h_direct <= 1'b0; 
        else
            h_direct <= h_direct;

        if(block_y == SIDE_W - 1'b1) //到达上边界时,竖直向下
                    v_direct <= 1'b1; 
        else //到达下边界时,竖直向上
        if(block_y == VS_C - SIDE_W - BLOCK_W)
            v_direct <= 1'b0; 
        else
            v_direct <= v_direct;
        end
    end

//根据方块移动方向,改变其纵横坐标
    always @(posedge clk ) begin 
        if (!rst_n) begin
            block_x <= SIDE_W; //方块初始位置横坐标
            block_y <= SIDE_W; //方块初始位置纵坐标
        end
        else if(move_en) begin
            if(h_direct)
                block_x <= block_x + 1'b1; //方块向右移动
            else
                block_x <= block_x - 1'b1; //方块向左移动

            if(v_direct)
                block_y <= block_y + 1'b1; //方块向下移动
            else
                block_y <= block_y - 1'b1; //方块向上移动
            end
        else begin
            block_x <= block_x;
            block_y <= block_y;
        end
    end

//给不同的区域绘制不同的颜色
    always @(posedge clk ) begin 
        if (!rst_n)
            hdmi_rgb <= BLACK;
        else begin
            if( (pixel_xpos_w < SIDE_W) || (pixel_xpos_w >= HS_C - SIDE_W) || (pixel_ypos_w < SIDE_W) || (pixel_ypos_w >= VS_C - SIDE_W))
            hdmi_rgb <= BLUE; //绘制屏幕边框为蓝色
            else
            if( (pixel_xpos_w >= block_x) && (pixel_xpos_w < block_x + BLOCK_W) && (pixel_ypos_w >= block_y) && (pixel_ypos_w < block_y + BLOCK_W))
            hdmi_rgb <= BLACK; //绘制方块为黑色
        else
            hdmi_rgb <= WHITE; //绘制背景为白色
        end
    end

endmodule

 

实验现象

 

相关阅读:

友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(一)——HDMI简介
友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(二)——TMDS算法
友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(三)——DE10-Nano 的HDMI电路简介

友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(四)——实验源码及现象演示

ADV7513芯片控制解读(中文版)——基于DE10-NANO开发板——C5G 开发板

基于FPGA控制的ADV7513芯片的I2C代码解析

DE10-NANO 的HDMI显示彩条实验遇到显示器显示“超频”

 

本实验参考视频:https://www.bilibili.com/video/BV1QF41137Kf?p=91