FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)

FPGA实验记录四:基于FPGA的VGA协议实现

板子:DE2-115
IDE:QuartusII 18.1(Lite)
仿真软件:Modelsim-Alterl
芯片系列:Cydone IV E
芯片名称:EP4CE115F29C7
芯片核心:EP4CE115


文章目录
  • FPGA实验记录四:基于FPGA的VGA协议实现
    • 〇、VGA协议
      • 一、**外部接口**
      • 二、色彩原理
      • 三、扫描方式
      • 四、行场信号(重点)
      • 五、规格参数
    • 一、显示彩条
      • 一、时钟
      • 二、代码
    • 二、显示文字/BMP双色图片
      • 一、原理
      • 二、代码
    • 三、BMP图片显示
      • 一、原理
      • 二、代码
    • 四、总结
    • 五、参考文章
    • 六、源文件

〇、VGA协议 VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。不支持热插拔,不支持音频传输。对于一些嵌入式VGA显示系统,可以在不使用VGA显示卡和计算机的情况下,实现VGA图像的显示和控制。VGA显示器具有成本低、结构简单、应用灵活的优点。对于一名FPGA工程师,尤其是视频图像的方向的学习者,VGA协议是必须要掌握的。
一、外部接口
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

由电路图可以看到,VGA并没有特殊的外部芯片,我们需要关注的其实只有5个信号:HS行同步信号,VS场同步信号,R红基色,G绿基色,B蓝基色。下面慢慢解释这些信号。
二、色彩原理
经过九年义务教育的我们都应该听过三基色,还给老师了的那就在再复习一下。三基色是指通过其他颜色的混合无法得到的“基本色”由于人的肉眼有感知红、绿、蓝三种不同颜色的锥体细胞,因此色彩空间通常可以由三种基本色来表达。这是色度学的最基本原理,即三基色原理。三种基色是相互独立的,任何一种基色都不能有其它两种颜色合成。红绿蓝是三基色,这三种颜色合成的颜色范围最为广泛。我们的RGB信号真是三基色的运用,对这三个信号赋予不同的数值,混合起来便是不同的色彩。
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

设计RGB信号时,既可以R信号、G信号和B信号独立的赋值,最后连到端口上,也可以直接用RGB当做一个整体信号,RGB信号在使用时的位宽有三种常见格式,以你的VGA解码芯片的配置有关。
  1. RGB_8,R:G:B = 3:3:2,即RGB332
2. RGB_16,R:G:B = 5:6:5,即RGB565
2. RGB_24,R:G:B = 8:8:8,即RGB888
三、扫描方式
VGA显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。因此我们一般都采用逐行扫描的方式。
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

四、行场信号(重点)
行场信号共有 4 种模式,即 hsync 和 vsync 的高低状态不同,如下所示:
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

这样就清楚了,大致是若干个HS信号才组合而成一个VS,如果在一副图片中,那正确的时序表示方式应该如下图这样。
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

SYNC是“信号同步”,Back proch和Left border常常加在一起称为“显示后沿”,Addressable video为“显示区域”,Right porder和Front porch常常加在一起称为“显示前沿”,一个时序其实就是先拉高一段较短的“信号同步”时间,然后拉低一段很长的时间,这就是一个回合。同时需要注意,其实也可以完全相反。即先拉低一段时间“信号同步”时间,然后拉高一段很长的时间。
行场同步信号代码:
vga_ctrl.v
`include "vga_param.v" // `define vga_640_480 // `define vga_1920_1080/** * * VGA控制模块 */ module vga_ctrl( inputwireclk, inputwirerst_n, inputwire [23:0]data_display,// 要显示的数据outputreg [10:0]h_addr,// 行地址--数据有效显示区域行地址 outputreg [10:0]v_addr,// 场地址--数据有效显示区域场地址outputreghsync, outputregvsync, outputreg [7:0]vga_r,// R outputreg [7:0]vga_g,// G outputreg [7:0]vga_b,// B // outputwirevga_sync_n, outputregvga_black, outputwirevga_clk// 时钟 ); parameterH_SYNC_START = 1; // 行同步开始 parameterH_SYNC_STOP= `H_Sync_Time; // 行同步结束 parameterH_DATA_START = `H_Sync_Time + `H_Back_Porch + `H_Left_Border; parameterH_DATA_STOP= `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time; parameterV_SYNC_START = 1; // 场同步开始 parameterV_SYNC_STOP= `V_Sync_Time; // 场同步结束 parameterV_DATA_START = `V_Sync_Time + `V_Back_Porch + `V_Top_Border; parameterV_DATA_STOP= `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time; // parameterV_BLANK= `V_Total_Time; //场空白信号总周期长 // parameterH_BLANK= `H_Total_Time; //场空白信号总周期长reg[11:0]cnt_h_addr; // 行地址计数器 wireadd_h_addr; wireend_h_addr; reg[11:0]cnt_v_addr; // 场地址计数器 wireadd_v_addr; wireend_v_addr; // 行地址计数 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt_h_addr <= 12'd0; endelse if (add_h_addr) begin if (end_h_addr) cnt_h_addr <= 12'd0; else cnt_h_addr <= cnt_h_addr + 1'b1; endelse begin cnt_h_addr <= 12'd0; end end assign add_h_addr = 1'b1; assign end_h_addr = ((cnt_h_addr>=`H_Total_Time-1) && add_h_addr); // 场地址计数 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt_v_addr <= 12'd0; endelse if (add_v_addr) begin if (end_v_addr) cnt_v_addr <= 12'd0; else cnt_v_addr <= cnt_v_addr + 1'b1; endelse begin cnt_v_addr <= cnt_v_addr; end end assign add_v_addr = end_h_addr; assign end_v_addr = ((cnt_v_addr>=`V_Total_Time-1) && add_v_addr); // 行同步信号低有效 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin hsync <= 1'b1; endelse if (cnt_h_addr == H_SYNC_START - 1) begin// 行同步开始 hsync <= 1'b0; endelse if (cnt_h_addr == H_SYNC_STOP - 1) begin// 行同步结束 hsync <= 1'b1; endelse begin hsync <= hsync; end end// 场同步信号低有效 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin vsync <= 1'b1; endelse if (cnt_v_addr == V_SYNC_START - 1) begin vsync <= 1'b0; endelse if (cnt_v_addr == V_SYNC_STOP - 1) begin vsync <= 1'b1; endelse begin vsync <= vsync; end endassign vga_clk = clk; // 数据有效显示区域定义 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin h_addr <= 11'd0; endelse if ((cnt_h_addr >= (H_DATA_START - 1)) && (cnt_h_addr <= H_DATA_STOP)) begin h_addr <= cnt_h_addr - H_DATA_START - 1; // 总的 减去 前面的多余部分 endelse begin h_addr <= 11'd0; end end// 数据有效显示区域定义 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin v_addr <= 11'd0; endelse if ((cnt_h_addr >= (V_DATA_START - 1)) && (cnt_h_addr <= V_DATA_STOP)) begin v_addr <= cnt_v_addr - V_DATA_START - 1; endelse begin v_addr <= 11'd0; end end// 显示数据 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin vga_r <= 8'd0; vga_g <= 8'd0; vga_b <= 8'd0; endelse if (((cnt_h_addr >= (H_DATA_START - 1)) && (cnt_h_addr <= H_DATA_STOP)) && ((cnt_v_addr >= (V_DATA_START - 1)) && (cnt_v_addr <= V_DATA_STOP))) begin vga_r <= data_display[23:16]; vga_g <= data_display[15:8]; vga_b <= data_display[7:0]; vga_black <= 1'b1; endelse begin vga_r <= 8'd0; vga_g <= 8'd0; vga_b <= 8'd0; vga_black <= 1'b0; end endendmodule

时序仿真:
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

五、规格参数
`ifdef vga_640_480 `define H_Right_Border 8 `define H_Front_Border 8 `define H_Sync_Time96 `define H_Back_porch40 `define H_Left_Border8 `define H_Data_Time640 `define H_Total_Time800 `define V_Bottom_Borde 8 `define V_Front_Porch2 `define V_Sync_Time2 `define V_Back_porch25 `define V_Top_Border8 `define V_Data_Time480 `define V_Total_Time525//执行操作A `elsif vga_1920_1080 `define H_Right_Border 0 `define H_Front_Border 88 `define H_Sync_Time44 `define H_Back_porch148 `define H_Left_Border0 `define H_Data_Time1920 `define H_Total_Time2200 `define V_Bottom_Borde 0 `define V_Front_Porch4 `define V_Sync_Time5 `define V_Back_porch36 `define V_Top_Border0 `define V_Data_Time1080 `define V_Total_Time1125 //执行操作B`elsif vga_800_480 `define H_Right_Border 0 `define H_Front_Border 40 `define H_Sync_Time128 `define H_Back_porch88 `define H_Left_Border0 `define H_Data_Time800 `define H_Total_Time1056 `define V_Bottom_Borde 8 `define V_Front_Porch2 `define V_Sync_Time2 `define V_Back_porch25 `define V_Top_Border8 `define V_Data_Time480 `define V_Total_Time525 //执行操作B`elsif vga_800_600 `define H_Right_Border 0 `define H_Front_Border 40 `define H_Sync_Time128 `define H_Back_porch88 `define H_Left_Border0 `define H_Data_Time800 `define H_Total_Time1056 `define V_Bottom_Borde 0 `define V_Front_Porch1 `define V_Sync_Time4 `define V_Back_porch23 `define V_Top_Border0 `define V_Data_Time600 `define V_Total_Time628 //执行操作B`elsif vga_1024_600 `define H_Right_Border 0 `define H_Front_Border 24 `define H_Sync_Time136 `define H_Back_porch160 `define H_Left_Border0 `define H_Data_Time1024 `define H_Total_Time1344 `define V_Bottom_Borde 0 `define V_Front_Porch1 `define V_Sync_Time4 `define V_Back_porch23 `define V_Top_Border0 `define V_Data_Time600 `define V_Total_Time628 //执行操作B`elsif vga_1024_768 `define H_Right_Border 0 `define H_Front_Border 24 `define H_Sync_Time136 `define H_Back_porch160 `define H_Left_Border0 `define H_Data_Time1024 `define H_Total_Time1344 `define V_Bottom_Borde 0 `define V_Front_Porch3 `define V_Sync_Time6 `define V_Back_porch29 `define V_Top_Border0 `define V_Data_Time768 `define V_Total_Time806 //执行操作B`elsif vga_1280_720 `define H_Right_Border 0 `define H_Front_Border 110 `define H_Sync_Time40 `define H_Back_porch220 `define H_Left_Border0 `define H_Data_Time1280 `define H_Total_Time1650 `define V_Bottom_Borde 0 `define V_Front_Porch5 `define V_Sync_Time5 `define V_Back_porch20 `define V_Top_Border0 `define V_Data_Time720 `define V_Total_Time750 //执行操作B`elsif vga_1920_1080 `define H_Right_Border 0 `define H_Front_Border 88 `define H_Sync_Time44 `define H_Back_porch148 `define H_Left_Border0 `define H_Data_Time1920 `define H_Total_Time2200 `define V_Bottom_Borde 0 `define V_Front_Porch4 `define V_Sync_Time5 `define V_Back_porch36 `define V_Top_Border0 `define V_Data_Time1080 `define V_Total_Time1125 //执行操作B `else //默认,可以不写`endif

一、显示彩条 FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

一、时钟
640×480 60HZ和800×600 72HZ,对应时钟分别为25M和50M,这里可以直接调用IP核里的PPL分频时钟也可以自己书写分频时钟
自写分频时钟:
module clk_25M_get( inputwireclk, //VGA时 inputwirerst_n, //复位 outputregclk_25M ); always @(posedge clk or negedge rst_n) begin if (!rst_n) begin clk_25M <= 1'b0; end elsebegin clk_25M <=~clk_25M; end end endmodule

IP核调用PLL
  • QuartuasII的IP核栏中搜索PLL点击ALTPLL进入始终设置
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

  • page1系统输入时钟选择50M
    【FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)】FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
    文章图片

  • page2取消locked输出使能(目前不需要这个信号,精简一点更好)
    FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
    文章图片

  • page c1设置分频25M,将division factor参数修改为2
    FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
    文章图片

  • 勾选生成inst文件(方便调用)点击finish
二、代码
彩条显示代码:
data_generate.v
module data_generate( inputwireclk, inputwirerst_n,inputwire [10:0]h_addr,// 行地址--数据有效显示区域行地址 inputwire [10:0]v_addr,// 场地址--数据有效显示区域场地址outputreg [23:0]data_display// 要显示的数据 ); parameter BLACK= 24'h000000, RED= 24'hFF0000, GREEN= 24'h00FF00, BLUE= 24'h0000FF, YELLOW= 24'hFFFF00, CYANRAY= 24'h00FFFF, PURPLE= 24'hFF00FF, GRAY= 24'hC0C0C0, WHITE= 24'hFFFFFF; reg [ 383:0 ] char_line[ 64:0 ]; wirestate_words; assignstate_words = 1; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin data_display <= WHITE; end else begin case (h_addr) 0:data_display <= BLACK; 80:data_display <= RED; 160:data_display <= GREEN; 240:data_display <= BLUE; 320:data_display <= YELLOW ; 400:data_display <= CYANRAY; 480:data_display <= PURPLE ; 560:data_display <= GRAY; default:data_display <= data_display; endcase endend endmodule

使用时将其与行场控制模块代码vga_ctrl放在同一个top文件里就好了
module vga_top( inputwireclk, inputwirerst_n,outputwirehsync, outputwirevsync, outputwire [7:0]vga_r, outputwire [7:0]vga_g, outputwire [7:0]vga_b, outputwirevga_black, outputwirevga_clk ); wire [23:0]data_display; // 要显示的数据wire [10:0]h_addr; // 行地址--数据有效显示区域行地 wire [10:0]v_addr; // 场地址--数据有效显示区域场地wireclk_25M; pll_25m pll_25m_inst ( .areset ( ~rst_n ), .inclk0 ( clk ), .c0 ( clk_25M ) ); data_generate u_data_generate( .clk(clk_25M), .rst_n(rst_n),.h_addr(h_addr),// 行地址--数据有效显示区域行地址 .v_addr(v_addr),// 场地址--数据有效显示区域场地址.data_display(data_display )// 要显示的数据 ); vga_ctrl u_vga_ctrl( .clk(clk_25M), .rst_n(rst_n), .data_display(data_display),// 要显示的数据.h_addr(h_addr),// 行地址--数据有效显示区域行地址 .v_addr(v_addr),// 场地址--数据有效显示区域场地址.hsync(hsync), .vsync(vsync), .vga_r(vga_r),// R .vga_g(vga_g),// G .vga_b(vga_b),// B // .vga_sync_n(vga_sync_n), .vga_black(vga_black), .vga_clk(vga_clk)// 时钟 ) endmodule

原理就是修改data_generate模块内的参数使其输出彩色条纹信号再通过vga_ctrl模块翻译成VGA协议的帧格式就可以了。
二、显示文字/BMP双色图片 FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

一、原理
因为这个方法实际上是把文字做成指定分辨率的图片,比如接下来通过PCL字模工具,我将六个字做成了一张384*64分辨率的BMP双色图片(黑白)
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

字符大小设置为64*64,然后另存为BPM格式的图片,接下来再用PCL工具打开进行字模输出
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

字模输出格式设置一下
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

点击生成字模后得到以下文件:
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

二、代码
同样我们直接修改数据生成模块即可
该图片分辨率为384*64,所以我们需要一个一维数组,并给他赋予初始值,即刚才得到的那堆数据。
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

赋予初始值
always@( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin char_line[ 0 ]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; char_line[ 1 ]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; char_line[ 2 ]= 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; char_line[ 3 ]= 384'h000000000000000000000018000000000000000000004000000000000000020000000200020000000000000000000000; char_line[ 4 ]= 384'h00000000000000000000000C00000000000000000003E000000000004000038000000380038000000000000000000000; char_line[ 5 ]= 384'h0000000000000000000000070000000000000000003FF00000000000E00003F0000003E003E000000000000000000800; char_line[ 6 ]= 384'h000000000000000000000007800000000000000007FFF80000000001F00003C0000003C003C000000000000000001C00; char_line[ 7 ]= 384'h000000000000000000000003C000000000000001FFFFE00007FFFFFFF80003C00000038003C000000000000000003E00; char_line[ 8 ]= 384'h000000000000000000000003E0000000000001FFFFE0000003FFFFFFFC0003C00000038003C0000003FFFFFFFFFFFF00; char_line[ 9 ]= 384'h000000000000000000000001E0000000001FFFFFC000000001800C00000003C00000038003C0010001FFFFFFFFFFFF80; char_line[ 10 ] = 384'h000000000000000000180001E000000001FFE00F0000000000001E00000003C00000038003C0038000C0078000F00000; char_line[ 11 ] = 384'h000000000000000000180001C00002000000001E0000000000001F80002003C00000038003C00FC00000078000F00000; char_line[ 12 ] = 384'h000000000000000000180000800007000000001E0000000000003F00003803C01FFFFFFFFFFFFFE00000078000F00000; char_line[ 13 ] = 384'h0000000000000000001FFFFFFFFFFF800000001E0000000000003C00003E03C00FFFFFFFFFFFFFF00000078000F00000; char_line[ 14 ] = 384'h0000000000000000003FFFFFFFFFFFC00000001C0000C00000007800003E03C00400038003C000000000078000F00000; char_line[ 15 ] = 384'h00000000000000000038000000000FE00000003C0003E0000000F020003C03C00000038003C000000000078000F00000; char_line[ 16 ] = 384'h00000000000000000078000000001F00007FFFFFFFFFF0000000E030003C03C00000038003C000000000078000F00000; char_line[ 17 ] = 384'h00000000000000000078000000001C00003FFFFFFFFFF8000001C01C003C03C00000038003C000000000078000F00000; char_line[ 18 ] = 384'h000000000000000000F000000000380000100078000000000003800E003C03C00000038003C000000000078000F00000; char_line[ 19 ] = 384'h000000000000000001F0000000003000000000780000000000070007003C03C00000038003C000000000078000F00000; char_line[ 20 ] = 384'h000000000000000003F0000000006000000000F00000000000060007803C03C000000300030000000000078000F00000; char_line[ 21 ] = 384'h000000000000000003E0000000004000000000F000000080000C0003E03C03C000000400000000000000078000F00000; char_line[ 22 ] = 384'h00000000000000000000000000002000000001E0000001C000380001F03C03C000000C00000000000000078000F00000; char_line[ 23 ] = 384'h00000000000000000000000000007000000001E0000003E000F0003FF83C03C000001F00000000000000078000F00000; char_line[ 24 ] = 384'h0000000000000300000000000000F8003FFFFFFFFFFFFFF001FFFFFFF83C03C000001F00100000000000078000F00000; char_line[ 25 ] = 384'h0000000000000F8000FFFFFFFFFFFC001FFFFFFFFFFFFFF800FFFE00FC3C03C000003E001C0000000000078000F00180; char_line[ 26 ] = 384'h0000000000001FC0007FFFFFFFFFFE000C0007800000000000FE0E007C3C03C000003C001F0000000000078000F003C0; char_line[ 27 ] = 384'h0000000000003FE000200003C0000000000007800000000000600F807C3C03C0000078001F000C000000078000F007E0; char_line[ 28 ] = 384'h1FFFFFFFFFFFFFF000000003C000000000000F000000000000000F00383C03C0000078001E001E001FFFFFFFFFFFFFF0; char_line[ 29 ] = 384'h0FFFFFFFFFFFFFF800000003C000000000000E000000000000000E00303C03C00000F0001E003F000FFFFFFFFFFFFFF8; char_line[ 30 ] = 384'h07C000000000000000000003C000000000001E000002000000000E00003C03C00000F0001E007F800400070000F00000; char_line[ 31 ] = 384'h000000000000000000000003C000000000001C000003800000000E00003C03C00001E0001E00FC000000070000F00000; char_line[ 32 ] = 384'h000000000000000000008003C000000000003FFFFFFFC00000000E00003C03C00001C0001E01F8000000070000F00000; char_line[ 33 ] = 384'h00000000000000000000E003C000000000007FFFFFFFC00000000E00003C03C00003F8001E03E0000000070000F00000; char_line[ 34 ] = 384'h00000000000000000000F803C00000000000FE000007800000000E00003C03C00007F0001E0FC00000000F0000F00000; char_line[ 35 ] = 384'h00000000000000000001F803C00000000000FE000007800000000E00C03C03C00007E0001E1F000000000F0000F00000; char_line[ 36 ] = 384'h00000000000000000001F003C00080000001DE000007800000000E01E03C03C0000FE0001E3E000000000F0000F00000; char_line[ 37 ] = 384'h00000000000000000001F003C001C00000039E000007800007FFFFFFF03C03C0001EE0001EF8000000000F0000F00000; char_line[ 38 ] = 384'h00000000000000000001E003C003E00000079E000007800003FFFFFFF83C03C0003CE0001FF0000000000F0000F00000; char_line[ 39 ] = 384'h00000000000000000001E003FFFFF000000F1E000007800000000E00003C03C00078E0001FC0000000000E0000F00000; char_line[ 40 ] = 384'h00000000000000000001E003FFFFF800001E1FFFFFFF800000000E00003C03C000F0E0001F00000000001E0000F00000; char_line[ 41 ] = 384'h00000000000000000001C003C0000000003C1FFFFFFF800000000E00003C03C000C0E0007E00000000001E0000F00000; char_line[ 42 ] = 384'h00000000000000000003C003C000000000701E000007800000000E00003C03C00380E001FE00000000001C0000F00000; char_line[ 43 ] = 384'h00000000000000000003E003C000000000E01E000007800000000E00003C03C00700E007DE00000000003C0000F00000; char_line[ 44 ] = 384'h00000000000000000003F003C000000001C01E000007800000000E00003C03C00E00E01F1E00000000003C0000F00000; char_line[ 45 ] = 384'h000000000000000000039003C000000003801E000007800000000E00003C03C01800E0781E0000400000380000F00000; char_line[ 46 ] = 384'h000000000000000000079803C000000006001E000007800000000E00003003C01000E1C01E0000400000780000F00000; char_line[ 47 ] = 384'h000000000000000000070C03C00000000C001E000007800000000E00000003C00000E1001E0000400000700000F00000; char_line[ 48 ] = 384'h0000000000000000000F0603C000000010001E000007800000000E000C0003C00000E0001E0000400000E00000F00000; char_line[ 49 ] = 384'h0000000000000000000E0703C000000000001FFFFFFF800000000E01FC0003C00000E0001E0000400001E00000F00000; char_line[ 50 ] = 384'h0000000000000000001E03C3C000000000001FFFFFFF800000000E3FC00003C00000E0001E0000400001C00000F00000; char_line[ 51 ] = 384'h0000000000000000001C01E3C000000000001E000007800000000FFC000003C00000E0001E0000C00003800000F00000; char_line[ 52 ] = 384'h0000000000000000003C00FBC000000000001E00000780000001FFC0000003C00000E0001E0000E00007000000F00000; char_line[ 53 ] = 384'h00000000000000000038007FC000000000001E000007800000FFFE00000003C00000E0001E0000E0000E000000F00000; char_line[ 54 ] = 384'h00000000000000000070001FF800000000001E000007800007FFE000000C07800000E0001E0000F0001C000000F00000; char_line[ 55 ] = 384'h000000000000000000E0000FFFFFFFF000001E000007800007FF0000000FFF800000E0000F0001F80038000000F00000; char_line[ 56 ] = 384'h000000000000000000C00001FFFFFF8000001FFFFFFF800003F800000001FF800000E0000FFFFFF00070000000F00000; char_line[ 57 ] = 384'h0000000000000000018000003FFFFF0000001FFFFFFF800003C0000000007F800000E0000FFFFFF000C0000000F00000; char_line[ 58 ] = 384'h00000000000000000300000001FFFE0000001E00000780000100000000001F000000E00003FFFFC00380000000F00000; char_line[ 59 ] = 384'h0000000000000000060000000000020000001E00000780000000000000001E000000E000000000000600000000E00000; char_line[ 60 ] = 384'h0000000000000000080000000000000000001E0000070000000000000000080000008000000000000800000000000000; char_line[ 61 ] = 384'h000000000000000000000000000000000000100000040000000000000000000000000000000000000000000000000000; char_line[ 62 ] = 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; char_line[ 63 ] = 384'h000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; end end

**时序逻辑:**在指定位置h[148,533]与v[208,273]的区间内,并且以黑底白字的样式显示,并且背景依然是彩条
always @(posedge clk or negedge rst_n) begin if (!rst_n) begin data_display <= WHITE; end else if (state_words &&h_addr > 148 &&h_addr < 533 && v_addr > 208&& v_addr < 273) begin data_display <= char_line[ v_addr-208 ][ 532 - h_addr ]? WHITE:BLACK; end else begin case (h_addr) 0:data_display <= BLACK; 80:data_display <= RED; 160:data_display <= GREEN; 240:data_display <= BLUE; 320:data_display <= YELLOW ; 400:data_display <= CYANRAY; 480:data_display <= PURPLE ; 560:data_display <= GRAY; default:data_display <= data_display; endcase end end

其他地方不用更改,编译完成后直接烧录即可。
三、BMP图片显示 一、原理
一张640×480的24位的图片的大小超过了芯片内存,所以采用一张96*80的图片进行显示。
原图:
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

这里使用BMP2Mif软件进行位图转换,将其以HEX文件模式输出
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

选择RGB888的模式是因为DE2-115是RGB888版本
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

图片数据太多所以我们需要ROM来存储
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

设置位宽16,大小为96*80=7680
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

不需要它
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

找到之前的hex文件
FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

勾选inst后点击finish即可
二、代码
修改数据生成模块
reg[ 13:0 ]rom_address; // ROM地址 wire[ 15:0 ]rom_data; // 图片数据wireflag_enable_out1; // 文字有效区域 wireflag_enable_out2; // 图片有效区域 wireflag_clear_rom_address; // 地址清零 wireflag_begin_h; // 图片显示行 wireflag_begin_v; // 图片显示列//在数据显示always语句块中添加下列语句 if ( flag_enable_out2 ) begin rgb_data = https://www.it610.com/article/rom_data; end else begin rgb_data = black; end

FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
文章图片

四、总结 学习并掌握了VGA协议。
五、参考文章 咸鱼FPGA: 协议——VGA
醉意丶千层梦: 基于FPGA的VGA显示彩条、字符、图片
六、源文件 https://github.com/Wattson1128/FPGA

    推荐阅读