VGA画像ディスプレイ(ROMベース)

下からの時間厳守の原子FPGA開発ガイド、良かったと思ったので引っ越しました。

1.VGAの概要

ポータル:VGA入門

2.実験内容

パイオニア開発ボードのVGAインターフェイスを使用して、モニター画面の中央にカラー画像を表示します。ディスプレイの解像度は640 * 480、リフレッシュレートは60hz、画像サイズは100 * 100です。

3、プログラム設計

図21.4.1は、この章の実験タスクに従って描かれたシステムブロック図です。その中で、分周器モジュールはピクセルクロックの生成を担当し、VGA駆動モジュールはラインとフィールドの同期信号とピクセルポイントの垂直および水平座標を生成し、VGAディスプレイモジュールは画像データを出力し、ROMは次の目的で使用されます。表示する必要のある画像を保存します。

ここに画像の説明を挿入します
VGAディスプレイモジュールのROMは、IPコアをインスタンス化することによって実現される読み取り専用メモリであり、FPGAのオンチップストレージリソースを使用します。画像データを含むmifファイルでROMIPコアを初期化する必要がありますが、元のデータを保存する代わりに、RGBから変換されたRGB565形式でデータを保存します。つまり、1ピクセルは3つの8ビットデータに対応しなくなります。 、ただし、16ビットデータに対応します。

システムブロック図からわかるように、FPGAパーツには次の4つのモジュールが含まれています。

  • トップレベルモジュール(vga_colorbar)
  • クロック分周器モジュール(vga_pll)
  • VGAディスプレイモジュール(vga_display)
  • VGAドライバーモジュール(vga_driver)。

その中で、他の3つのモジュールはトップレベルモジュールでインスタンス化されます。

各モジュールのポートと信号の接続を図21.4.5に示します。
ここに画像の説明を挿入します
クロック分周器モジュール(vga_pll)は、フェーズロックループ(PLL)IPコアを呼び出すことによって実装されます。実験タスクに必要な解像度とリフレッシュレートによると、この実験でVGAディスプレイに使用されるピクセルクロックは25.175Mhzです。解像度がそれほど高くないため、25Mhzを出力するようにフェーズロックループIPコアを設定できます。ピクセルクロックとしてのクロック。

VGAドライバーモジュール(vga_driver)は、ピクセルクロックによって駆動され、VGAタイミングパラメーターに従って水平同期(vga_hs)および垂直同期(vga_vs)信号を出力します。同時に、VGAドライバモジュールは、VGAディスプレイモジュール(vga_display)が画像を描画するために呼び出すために、ピクセルの垂直座標と水平座標も出力する必要があります。

トップレベルモジュールのコードは次のとおりです。

module vga_rom_pic(
    input           sys_clk,        //系统时钟
    input           sys_rst_n,      //复位信号
    //VGA接口                          
    output          vga_hs,         //行同步信号
    output          vga_vs,         //场同步信号
    output  [15:0]  vga_rgb         //红绿蓝三原色输出 
    ); 

//wire define
wire         vga_clk_w;             //PLL分频得到25Mhz时钟
wire         locked_w;              //PLL输出稳定信号
wire         rst_n_w;               //内部复位信号
wire [15:0]  pixel_data_w;          //像素点数据
wire [ 9:0]  pixel_xpos_w;          //像素点横坐标
wire [ 9:0]  pixel_ypos_w;          //像素点纵坐标    
    
//*****************************************************
//**                    main code
//***************************************************** 
//待PLL输出稳定之后,停止复位
assign rst_n_w = sys_rst_n && locked_w;
   
vga_pll	u_vga_pll(                  //时钟分频模块
	.inclk0         (sys_clk),    
	.areset         (~sys_rst_n),
    
	.c0             (vga_clk_w),    //VGA时钟 25M
	.locked         (locked_w)
	); 

vga_driver u_vga_driver(
    .vga_clk        (vga_clk_w),    
    .sys_rst_n      (rst_n_w),    

    .vga_hs         (vga_hs),       
    .vga_vs         (vga_vs),       
    .vga_rgb        (vga_rgb),      
    
    .pixel_data     (pixel_data_w), 
    .pixel_xpos     (pixel_xpos_w), 
    .pixel_ypos     (pixel_ypos_w)
    ); 
    
vga_display u_vga_display(
    .vga_clk        (vga_clk_w),
    .sys_rst_n      (rst_n_w),
    
    .pixel_xpos     (pixel_xpos_w),
    .pixel_ypos     (pixel_ypos_w),
    .pixel_data     (pixel_data_w)
    );   
    
endmodule 

トップレベルモジュールは、主に残りのモジュールのインスタンス化を完了します。IPコアをクロック周波数分割に使用する場合、システムのパワーオンリセット後にPLLによって出力される25Mhzクロックには、次の時間がかかることに注意してください。安定した状態になります。PLL出力が安定した後、ロックされたフラグ信号がHighにプルアップされます(ライン29)。

VGAドライバモジュールとディスプレイモジュールの両方がPLLによって出力されたピクセルクロックによって駆動されるため、残りのモジュールは、PLL出力が安定するまでリセットされたままである必要があります。プログラムの22行目に示されているように、内部リセット信号rst_n_wは、システムリセット信号sys_rst_nとPLL出力安定性フラグ信号をAND演算することによって取得されます。この信号をVGAドライブモジュールとディスプレイモジュールのリセット信号として使用すると、システムのリセット後の不安定なピクセルクロックによって引き起こされるVGAタイミングエラーを回避できます。

VGAドライバモジュールのコードは次のとおりです。

module vga_driver(
    input           vga_clk,      //VGA驱动时钟
    input           sys_rst_n,    //复位信号
    //VGA接口                          
    output          vga_hs,       //行同步信号
    output          vga_vs,       //场同步信号
    output  [15:0]  vga_rgb,      //红绿蓝三原色输出
    
    input   [15:0]  pixel_data,   //像素点数据
    output  [ 9:0]  pixel_xpos,   //像素点横坐标
    output  [ 9:0]  pixel_ypos    //像素点纵坐标    
    );                             
                                                        
//parameter define  
parameter  H_SYNC   =  10'd96;    //行同步
parameter  H_BACK   =  10'd48;    //行显示后沿
parameter  H_DISP   =  10'd640;   //行有效数据
parameter  H_FRONT  =  10'd16;    //行显示前沿
parameter  H_TOTAL  =  10'd800;   //行扫描周期

parameter  V_SYNC   =  10'd2;     //场同步
parameter  V_BACK   =  10'd33;    //场显示后沿
parameter  V_DISP   =  10'd480;   //场有效数据
parameter  V_FRONT  =  10'd10;    //场显示前沿
parameter  V_TOTAL  =  10'd525;   //场扫描周期
          
//reg define                                     
reg  [9:0] cnt_h;
reg  [9:0] cnt_v;

//wire define
wire       vga_en;
wire       data_req; 

//*****************************************************
//**                    main code
//*****************************************************
//VGA行场同步信号
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//使能RGB565数据输出
assign vga_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
                 
//RGB565数据输出                 
assign vga_rgb = vga_en ? pixel_data : 16'd0;

//提前一个周期请求像素点颜色数据输入                
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素点坐标                
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

//行计数器对像素时钟计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//场计数器对行计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end

endmodule 

プログラムの14行目から25行目は、変数宣言により、解像度が640 * 480、リフレッシュレートが60hzの場合に、VGAタイミングシーケンスのさまざまなパラメーターを定義します。

プログラムの59行目から69行目は、ラインカウンタcnt_hを介してピクセルクロックをカウントし、ラインスキャン期間後にそれをクリアして、カウントを再開します。プログラムの71行目から81行目は、フィールドカウンタcnt_vを通過する行をカウントします。つまり、cnt_vは1行をスキャンした後に1ずつインクリメントされ、1フィールドスキャン期間後にクリアされ、カウントを再開します。

ラインおよびフィールドカウンタの値をVGAタイミングシーケンスのパラメータと比較することで、ラインおよびフィールドの同期信号が低レベルの同期状態にあるとき、およびRGB565形式の画像データがいつであるかを判断できます(38行目から48)が出力されます。プログラム行50〜57は、現在のピクセルの垂直座標と水平座標を出力します。ピクセルのカラーデータは、座標出力後の次のクロックサイクルで受信できるため、データ要求信号data_reqは1クロックサイクル進んでいます。データ出力イネーブル信号vga_en。

VGAディスプレイモジュールのコードは次のとおりです。

module vga_display(
    input             vga_clk,              //VGA驱动时钟
    input             sys_rst_n,            //复位信号
    
    input      [ 9:0] pixel_xpos,           //像素点横坐标
    input      [ 9:0] pixel_ypos,           //像素点纵坐标    
    output     [15:0] pixel_data            //像素点数据
    );    

//parameter define    
parameter  H_DISP = 10'd640;                //分辨率——行
parameter  V_DISP = 10'd480;                //分辨率——列

localparam POS_X  = 10'd270;                //图片区域起始点横坐标
localparam POS_Y  = 10'd190;                //图片区域起始点纵坐标
localparam WIDTH  = 10'd100;                //图片区域宽度
localparam HEIGHT = 10'd100;                //图片区域高度
localparam TOTAL  = 14'd10000;              //图案区域总像素数
localparam BLACK  = 16'b00000_000000_00000; //屏幕背景色

//reg define
wire        rom_rd_en;                      //读ROM使能信号
reg  [13:0] rom_addr;                       //读ROM地址
reg         rom_valid;                      //读ROM数据有效信号

//wire define   
wire [15:0] rom_data;                       //ROM输出数据

//*****************************************************
//**                    main code
//*****************************************************

//从ROM中读出的图像数据有效时,将其输出显示
assign pixel_data = rom_valid ? rom_data : BLACK; 

//当前像素点坐标位于图案显示区域内时,读ROM使能信号拉高
assign rom_rd_en = (pixel_xpos >= POS_X) && (pixel_xpos < POS_X + WIDTH)
                    && (pixel_ypos >= POS_Y) && (pixel_ypos < POS_Y + HEIGHT)
                     ? 1'b1 : 1'b0;

//控制读地址
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        rom_addr   <= 14'd0;
    end
    else if(rom_rd_en) begin
        if(rom_addr < TOTAL - 1'b1)
            rom_addr <= rom_addr + 1'b1;    //每次读ROM操作后,读地址加1
        else
            rom_addr <= 1'b0;               //读到ROM末地址后,从首地址重新开始读操作
    end
    else
        rom_addr <= rom_addr;
end

//从发出读使能到ROM输出有效数据存在一个时钟周期的延时
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) 
        rom_valid <= 1'b0;
    else
        rom_valid <= rom_rd_en;
end

//通过调用IP核来例化ROM
pic_rom	pic_rom_inst(
	.clock   (vga_clk),
	.address (rom_addr),
	.rden    (rom_rd_en),
	.q       (rom_data)
	);

endmodule 

コードの14行目から19行目は、一連の変数を宣言しています。これは、画像のサイズ、画面に表示される位置などを変更するのに便利です。画像表示の位置は、の垂直座標と水平座標によって指定されます。画像表示領域の左上隅。

画像はROMに格納されるため、VGAディスプレイモジュールの主なタスクは、ROM内の画像データを適切な期間で読み取り、表示できるように、ROMの読み取りイネーブルと読み取りアドレスを制御することです。コードの36行目から39行目は、現在のピクセルの垂直座標と水平座標を決定し、画像表示領域にあるときにROM読み取りイネーブル信号rom_rd_enをHighにプルします。41行目から54行目は、読み出し動作中に読み出しアドレスを順次累積し、画像データを順次読み出します。アドレス指定されていないアドレスが読み込まれると、読み出しアドレスがクリアされ、ROM内の画像の最初のピクセルデータが再度読み取られます。

ROMの読み出し処理では、読み出しイネーブルが発行されてからROMが有効なデータを出力するまでにクロックサイクルの遅延があります。したがって、ROMデータの有効な信号rom_validは、図のようにrom_rd_enだけ1クロックサイクル遅延する必要があります。プログラムの56行目から62行目。

おすすめ

転載: blog.csdn.net/qq_39507748/article/details/113256160