之前的基于友晶科技 FPGA开发板 DE2-115、DE1-SOC 和 DE10-Standard 的VGA彩条显示设计(ADV7123)设计是让VGA显示器显示彩条。 那如果显示图片呢?
那我们得调用一个ROM IP, 将图片存储在ROM里面, 显示的时候直接从ROM 里面读取出来。
ROM里面可以存放mif和hex格式的文件。
获取mif文件
我们先把图片转换成mif文件。
找到一个你想显示的图片,通过Windows自带的画图工具打开它,调整大小(比如调整到48*48*24bit),去掉保持纵横比,选择像素。
然后另存为时选择24bit bmp格式:
然后网络上下载一个bmp转mif的小工具(下载地址https://blog.csdn.net/baidu_34971492/article/details/106659642 或者 BMP2Mif.exe ):
调用ROM IP
得到mif文件以后。
在IP Catalog里面搜索ROM,选择ROM 1-PORT:
设置24位,4096字节(深度的设置一定要大于图片的大小,稍微设置大一点没关系)。
点击Browse,选择项目路径下mif文件,完成后点击next,最后点击Finish结束ROM IP 的设置并把该rom.qip文件添加到工程里面。
代码分析
完整源码
vga.v文件跟基于友晶科技 FPGA开发板 DE2-115、DE1-SOC 和 DE10-STANDARD 的VGA彩条显示设计(ADV7123)一样,无需更改。
module vga( input wire clk,//系统参考时钟 input wire rst_n,//复位 output wire [23:0] vga_rgb,//输出给ADV7123的R、G、B三分量数据 output wire vga_hs,//行同步信号 output wire vga_vs,//场同步信号 output wire vga_clk,//像素时钟 output wire vga_blank_n,//消隐信号 output wire vga_sync_n//复合同步控制IOG输入 ); wire clk_25m; wire pll_locked; pll pll_inst (//例化PLL .rst ( ~rst_n ), .refclk ( clk ), .outclk_0 ( clk_25m ), .locked ( pll_locked) ); vga_ctrl vga_ctrl_inst (//例化VGA控制模块 .clk (clk_25m ), .rst_n (pll_locked ), .vga_rgb (vga_rgb ), .vga_hs (vga_hs ), .vga_blanck_n (vga_blank_n), .vga_vs (vga_vs ) ); //If not IOG, Sync input should be tied to 0; assign vga_sync_n=1'b0; assign vga_clk=clk_25m; endmodule
vga_ctl.v文件需要修改, 完整代码如下:
module vga_ctrl ( input wire clk,//像素时钟 input wire rst_n,//复位 output reg [23:0] vga_rgb,//输出给ADV7123的R、G、B三分量数据 output reg vga_hs,//行同步信号 output reg vga_vs,//场同步信号 output wire vga_blanck_n//消隐信号 ); //================ VGA PLL clk = 25M 640x480x60 ========================================================= parameter HS_A = 96; // 行同步信号周期长 parameter HS_B = 48; // 行同步后沿信号周期长 parameter HS_C = 640; // 行显示周期长 parameter HS_D = 16; // 行同步前言信号周期长 parameter HS_E = 800; // 行周期长 parameter VS_A = 2; // 场同步信号周期长 parameter VS_B = 33; // 场同步后沿信号周期长 parameter VS_C = 480; // 行显示周期长 parameter VS_D = 10; // 场同步前言信号周期长 parameter VS_E = 525; // 场周期长 parameter HS_WIDTH = 10; // 行周期计数器位宽 parameter VS_WIDTH = 10; // 场周期计数器位宽 parameter height = 48; // 图片高度 parameter width = 48; // 图片宽度 reg [HS_WIDTH - 1:0] cnt_hs; // 行周期计数器 reg [VS_WIDTH - 1:0] cnt_vs; // 场周期计数器 reg [13:0] cnt_rom_address; // rom地址计数器 reg [11:0] addr_h; //行地址 reg [11:0] addr_v; //列地址 wire en_hs; // 行显示使能 wire en_vs; // 场显示使能 wire en; // VGA 有效显示区使能信号 wire flag_clear_rom_address;//地址计数器清零标志 wire flag_begin_h; //图片行显示有效标志 wire flag_begin_v; //图片场显示有效标志 wire flag_enable_out2; //图片的有效显示区标志 wire [23:0] rom_data; // 图片数据 // 行周期计数器的实现 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) vga_hs <= 1'b1; else if (cnt_hs < HS_A - 1) //同步之前vga_hs信号都是低, 同步之后(a)vga_hs信号是高 vga_hs <= 1'b0; else vga_hs <= 1'b1; // 场同步信号时序 always @ (posedge clk, negedge rst_n) if (!rst_n) vga_vs <= 1'b1; else if (cnt_vs < VS_A - 1) //同步之前vga_vs 信号都是低, 同步之后(a)vga_vs 信号是高 vga_vs <= 1'b0; else vga_vs <= 1'b1; //实例化ROM rom rom_inst ( .address (cnt_rom_address), .clock (clk), .q (rom_data) ); //读出ROM数据 always @( posedge clk or negedge rst_n) begin if(!rst_n)begin vga_rgb <= 24'b000000000000000000000000; end else if ( flag_enable_out2 ) begin vga_rgb <= rom_data; end else begin vga_rgb <= 24'b000000000000000000000000; end end //ROM地址计数器 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin cnt_rom_address <= 0;//复位地址清零 end else if ( flag_clear_rom_address ) begin //计数满清零 cnt_rom_address <= 0; end else if ( flag_enable_out2 ) begin //在有效区域内+1 cnt_rom_address <= cnt_rom_address + 1; end else begin //无效区域内地址不变 cnt_rom_address <= cnt_rom_address; end end // 对应有效区域行地址 1-640 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin addr_h <= 0; end else if ( en ) begin addr_h <= cnt_hs - HS_A - HS_B + 1; end else begin addr_h <= 0; end end // 对应有效区域列地址 1-480 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin addr_v <= 0; end else if ( en ) begin addr_v <= cnt_vs -VS_A - VS_B + 1; end else begin addr_v <= 0; end end //当地址计数器计数到48x48-1时 地址计数器计清零标志置1。 assign flag_clear_rom_address = cnt_rom_address == height * width - 1; assign flag_begin_h = addr_h > ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width + 1; assign flag_begin_v = addr_v > ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height + 1; assign flag_enable_out2 = flag_begin_h && flag_begin_v; 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_hs 才为高,否则为低 assign en = en_hs && en_vs;//将vga显示的有效像素点位置全部标注出来了 assign vga_blanck_n = en; endmodule
引脚分配
测试结果
往期相关:
基于友晶科技FPGA开发板 DE0-CV、DE10-Lite 上实现VGA彩条显示设计(权电阻网络) - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板DE10-NANO开发板的HDMI 方块移动实验 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)