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协议是必须要掌握的。
一、外部接口
文章图片
文章图片
由电路图可以看到,VGA并没有特殊的外部芯片,我们需要关注的其实只有5个信号:HS行同步信号,VS场同步信号,R红基色,G绿基色,B蓝基色。下面慢慢解释这些信号。
二、色彩原理
经过九年义务教育的我们都应该听过三基色,还给老师了的那就在再复习一下。三基色是指通过其他颜色的混合无法得到的“基本色”由于人的肉眼有感知红、绿、蓝三种不同颜色的锥体细胞,因此色彩空间通常可以由三种基本色来表达。这是色度学的最基本原理,即三基色原理。三种基色是相互独立的,任何一种基色都不能有其它两种颜色合成。红绿蓝是三基色,这三种颜色合成的颜色范围最为广泛。我们的RGB信号真是三基色的运用,对这三个信号赋予不同的数值,混合起来便是不同的色彩。
文章图片
设计RGB信号时,既可以R信号、G信号和B信号独立的赋值,最后连到端口上,也可以直接用RGB当做一个整体信号,RGB信号在使用时的位宽有三种常见格式,以你的VGA解码芯片的配置有关。
- RGB_8,R:G:B = 3:3:2,即RGB332
2. RGB_24,R:G:B = 8:8:8,即RGB888
三、扫描方式
VGA显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。因此我们一般都采用逐行扫描的方式。
文章图片
四、行场信号(重点)
行场信号共有 4 种模式,即 hsync 和 vsync 的高低状态不同,如下所示:
文章图片
文章图片
文章图片
文章图片
这样就清楚了,大致是若干个HS信号才组合而成一个VS,如果在一副图片中,那正确的时序表示方式应该如下图这样。
文章图片
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
时序仿真:
文章图片
五、规格参数
`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
一、显示彩条
文章图片
一、时钟
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进入始终设置
文章图片
- page1系统输入时钟选择50M
【FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)】
文章图片
- page2取消locked输出使能(目前不需要这个信号,精简一点更好)
文章图片
- page c1设置分频25M,将division factor参数修改为2
文章图片
- 勾选生成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双色图片
文章图片
一、原理
因为这个方法实际上是把文字做成指定分辨率的图片,比如接下来通过PCL字模工具,我将六个字做成了一张384*64分辨率的BMP双色图片(黑白)
文章图片
字符大小设置为64*64,然后另存为BPM格式的图片,接下来再用PCL工具打开进行字模输出
文章图片
字模输出格式设置一下
文章图片
点击生成字模后得到以下文件:
文章图片
二、代码
同样我们直接修改数据生成模块即可
该图片分辨率为384*64,所以我们需要一个一维数组,并给他赋予初始值,即刚才得到的那堆数据。
文章图片
赋予初始值
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的图片进行显示。
原图:
文章图片
这里使用BMP2Mif软件进行位图转换,将其以HEX文件模式输出
文章图片
选择RGB888的模式是因为DE2-115是RGB888版本
文章图片
图片数据太多所以我们需要ROM来存储
文章图片
设置位宽16,大小为96*80=7680
文章图片
不需要它
文章图片
找到之前的hex文件
文章图片
勾选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
文章图片
四、总结 学习并掌握了VGA协议。
五、参考文章 咸鱼FPGA: 协议——VGA
醉意丶千层梦: 基于FPGA的VGA显示彩条、字符、图片
六、源文件 https://github.com/Wattson1128/FPGA
推荐阅读
- FPGA|FPGA基础协议二(I2C读写E2PROM)
- 人工智能|【CV】图像数据预处理详解
- FPGA|FPGA图像处理(一)(边缘检测)
- 算法|基于MATLAB的图像去噪与边缘检测技术
- 计算机视觉|IEEE(基于轻量级特征增强卷积神经网络的低空小目标检测)
- 大数据|如何挖掘银行外部数据价值(零编码接入,分钟级服务化)
- 大数据|程序员该如何构建面向未来的前端架构!
- 程序员|要啥女朋友(大神教你用Python人工智能制作AI机器人)
- 人工智能|人工智能、机器人、编程啥关系((科普))