板子:DE2-115
IDE:QuartusII 18.1(Lite)
仿真软件:Modelsim-Alterl
芯片系列:Cydone IV E
芯片名称:EP4CE115F29C7
芯片核心:EP4CE115
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解码芯片的配置有关。
2. RGB_16,R:G:B = 5:6:5,即RGB565
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(
input wire clk ,
input wire rst_n ,
input wire [23:0] data_display, // 要显示的数据
output reg [10:0] h_addr , // 行地址--数据有效显示区域行地址
output reg [10:0] v_addr , // 场地址--数据有效显示区域场地址
output reg hsync ,
output reg vsync ,
output reg [7:0] vga_r , // R
output reg [7:0] vga_g , // G
output reg [7:0] vga_b , // B
// output wire vga_sync_n ,
output reg vga_black ,
output wire vga_clk // 时钟
);
parameter H_SYNC_START = 1; // 行同步开始
parameter H_SYNC_STOP = `H_Sync_Time; // 行同步结束
parameter H_DATA_START = `H_Sync_Time + `H_Back_Porch + `H_Left_Border;
parameter H_DATA_STOP = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time;
parameter V_SYNC_START = 1; // 场同步开始
parameter V_SYNC_STOP = `V_Sync_Time; // 场同步结束
parameter V_DATA_START = `V_Sync_Time + `V_Back_Porch + `V_Top_Border;
parameter V_DATA_STOP = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time;
// parameter V_BLANK = `V_Total_Time; //场空白信号总周期长
// parameter H_BLANK = `H_Total_Time; //场空白信号总周期长
reg [11:0] cnt_h_addr; // 行地址计数器
wire add_h_addr;
wire end_h_addr;
reg [11:0] cnt_v_addr; // 场地址计数器
wire add_v_addr;
wire end_v_addr;
// 行地址计数
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_h_addr <= 12'd0;
end
else if (add_h_addr) begin
if (end_h_addr)
cnt_h_addr <= 12'd0;
else
cnt_h_addr <= cnt_h_addr + 1'b1;
end
else 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;
end
else if (add_v_addr) begin
if (end_v_addr)
cnt_v_addr <= 12'd0;
else
cnt_v_addr <= cnt_v_addr + 1'b1;
end
else 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;
end
else if (cnt_h_addr == H_SYNC_START - 1) begin // 行同步开始
hsync <= 1'b0;
end
else if (cnt_h_addr == H_SYNC_STOP - 1) begin // 行同步结束
hsync <= 1'b1;
end
else begin
hsync <= hsync;
end
end
// 场同步信号 低有效
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
vsync <= 1'b1;
end
else if (cnt_v_addr == V_SYNC_START - 1) begin
vsync <= 1'b0;
end
else if (cnt_v_addr == V_SYNC_STOP - 1) begin
vsync <= 1'b1;
end
else begin
vsync <= vsync;
end
end
assign vga_clk = clk;
// 数据有效显示区域定义
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
h_addr <= 11'd0;
end
else if ((cnt_h_addr >= (H_DATA_START - 1)) && (cnt_h_addr <= H_DATA_STOP)) begin
h_addr <= cnt_h_addr - H_DATA_START - 1; // 总的 减去 前面的多余部分
end
else begin
h_addr <= 11'd0;
end
end
// 数据有效显示区域定义
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
v_addr <= 11'd0;
end
else if ((cnt_h_addr >= (V_DATA_START - 1)) && (cnt_h_addr <= V_DATA_STOP)) begin
v_addr <= cnt_v_addr - V_DATA_START - 1;
end
else 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;
end
else 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;
end
else begin
vga_r <= 8'd0;
vga_g <= 8'd0;
vga_b <= 8'd0;
vga_black <= 1'b0;
end
end
endmodule
时序仿真:
`ifdef vga_640_480
`define H_Right_Border 8
`define H_Front_Border 8
`define H_Sync_Time 96
`define H_Back_porch 40
`define H_Left_Border 8
`define H_Data_Time 640
`define H_Total_Time 800
`define V_Bottom_Borde 8
`define V_Front_Porch 2
`define V_Sync_Time 2
`define V_Back_porch 25
`define V_Top_Border 8
`define V_Data_Time 480
`define V_Total_Time 525
//执行操作A
`elsif vga_1920_1080
`define H_Right_Border 0
`define H_Front_Border 88
`define H_Sync_Time 44
`define H_Back_porch 148
`define H_Left_Border 0
`define H_Data_Time 1920
`define H_Total_Time 2200
`define V_Bottom_Borde 0
`define V_Front_Porch 4
`define V_Sync_Time 5
`define V_Back_porch 36
`define V_Top_Border 0
`define V_Data_Time 1080
`define V_Total_Time 1125
//执行操作B
`elsif vga_800_480
`define H_Right_Border 0
`define H_Front_Border 40
`define H_Sync_Time 128
`define H_Back_porch 88
`define H_Left_Border 0
`define H_Data_Time 800
`define H_Total_Time 1056
`define V_Bottom_Borde 8
`define V_Front_Porch 2
`define V_Sync_Time 2
`define V_Back_porch 25
`define V_Top_Border 8
`define V_Data_Time 480
`define V_Total_Time 525
//执行操作B
`elsif vga_800_600
`define H_Right_Border 0
`define H_Front_Border 40
`define H_Sync_Time 128
`define H_Back_porch 88
`define H_Left_Border 0
`define H_Data_Time 800
`define H_Total_Time 1056
`define V_Bottom_Borde 0
`define V_Front_Porch 1
`define V_Sync_Time 4
`define V_Back_porch 23
`define V_Top_Border 0
`define V_Data_Time 600
`define V_Total_Time 628
//执行操作B
`elsif vga_1024_600
`define H_Right_Border 0
`define H_Front_Border 24
`define H_Sync_Time 136
`define H_Back_porch 160
`define H_Left_Border 0
`define H_Data_Time 1024
`define H_Total_Time 1344
`define V_Bottom_Borde 0
`define V_Front_Porch 1
`define V_Sync_Time 4
`define V_Back_porch 23
`define V_Top_Border 0
`define V_Data_Time 600
`define V_Total_Time 628
//执行操作B
`elsif vga_1024_768
`define H_Right_Border 0
`define H_Front_Border 24
`define H_Sync_Time 136
`define H_Back_porch 160
`define H_Left_Border 0
`define H_Data_Time 1024
`define H_Total_Time 1344
`define V_Bottom_Borde 0
`define V_Front_Porch 3
`define V_Sync_Time 6
`define V_Back_porch 29
`define V_Top_Border 0
`define V_Data_Time 768
`define V_Total_Time 806
//执行操作B
`elsif vga_1280_720
`define H_Right_Border 0
`define H_Front_Border 110
`define H_Sync_Time 40
`define H_Back_porch 220
`define H_Left_Border 0
`define H_Data_Time 1280
`define H_Total_Time 1650
`define V_Bottom_Borde 0
`define V_Front_Porch 5
`define V_Sync_Time 5
`define V_Back_porch 20
`define V_Top_Border 0
`define V_Data_Time 720
`define V_Total_Time 750
//执行操作B
`elsif vga_1920_1080
`define H_Right_Border 0
`define H_Front_Border 88
`define H_Sync_Time 44
`define H_Back_porch 148
`define H_Left_Border 0
`define H_Data_Time 1920
`define H_Total_Time 2200
`define V_Bottom_Borde 0
`define V_Front_Porch 4
`define V_Sync_Time 5
`define V_Back_porch 36
`define V_Top_Border 0
`define V_Data_Time 1080
`define V_Total_Time 1125
//执行操作B
`else
//默认,可以不写
`endif
640×480 60HZ和800×600 72HZ,对应时钟分别为25M和50M,这里可以直接调用IP核里的PPL分频时钟也可以自己书写分频时钟
自写分频时钟:
module clk_25M_get(
input wire clk , //VGA时
input wire rst_n , //复位
output reg clk_25M
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
clk_25M <= 1'b0 ;
end
else begin
clk_25M <= ~clk_25M;
end
end
endmodule
IP核调用PLL
page1系统输入时钟选择50M
page2取消locked输出使能(目前不需要这个信号,精简一点更好)
page c1设置分频25M,将division factor参数修改为2
勾选生成inst文件(方便调用)点击finish
彩条显示代码:
data_generate.v
module data_generate(
input wire clk ,
input wire rst_n ,
input wire [10:0] h_addr , // 行地址--数据有效显示区域行地址
input wire [10:0] v_addr , // 场地址--数据有效显示区域场地址
output reg [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 ];
wire state_words;
assign state_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
end
end
endmodule
使用时将其与行场控制模块代码vga_ctrl放在同一个top文件里就好了
module vga_top(
input wire clk,
input wire rst_n,
output wire hsync ,
output wire vsync ,
output wire [7:0] vga_r ,
output wire [7:0] vga_g ,
output wire [7:0] vga_b ,
output wire vga_black ,
output wire vga_clk
);
wire [23:0] data_display; // 要显示的数据
wire [10:0] h_addr ; // 行地址--数据有效显示区域行地
wire [10:0] v_addr ; // 场地址--数据有效显示区域场地
wire clk_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协议的帧格式就可以了。
因为这个方法实际上是把文字做成指定分辨率的图片,比如接下来通过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
其他地方不用更改,编译完成后直接烧录即可。
一张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 ; // 图片数据
wire flag_enable_out1 ; // 文字有效区域
wire flag_enable_out2 ; // 图片有效区域
wire flag_clear_rom_address ; // 地址清零
wire flag_begin_h ; // 图片显示行
wire flag_begin_v ; // 图片显示列
//在数据显示always语句块中添加下列语句
if ( flag_enable_out2 ) begin
rgb_data = rom_data;
end
else begin
rgb_data = black;
end
学习并掌握了VGA协议。
咸鱼FPGA: 协议——VGA
醉意丶千层梦: 基于FPGA的VGA显示彩条、字符、图片
https://github.com/Wattson1128/FPGA