欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于FPGA的HDMI图片显示

程序员文章站 2022-04-15 23:05:54
第一次写博客,就当是练手了吧QAQ无病呻吟[滑稽]1.导出图片数据2.将数据存入ROM中3.程序设计插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入无病呻吟[滑稽]首先说一下我用的板子,是XILINX的zynq-7020,这也是我学习的第一块FPGA板子,我知道这块板子的难度是很高的,我...

无病呻吟[滑稽]

首先说一下我用的板子,是XILINX的zynq-7020,这也是我学习的第一块FPGA板子,我知道这块板子的难度是很高的,我的想法是一步到位,如果只买单纯的一块像ax-4010那种的话等我以后想要学一些高阶教程还要重新买板子(穷学生党,怎么实惠怎么来,嘿嘿)。回归正题,这个HDMI图像显示是我的一个突发奇想,灵感来自于官方教程的HDMI字符显示部分,他是在ROM中初始化一段字符的数据,然后通过HDMI显示在显示器上。学完之后我就想能不能将一张图片的数据存储到ROM中然后用同样的方式显示出来,说做就做。

1.导出图片数据

首先要获得存入ROM的图片数据,图片是我在网上找的一张萌图,置于萌不萌大佬们自己看(就是萌,不接受大佬们的反驳):
基于FPGA的HDMI图片显示
在博客上搜了用matlab将图片导出.coe文件的办法,这里直接把链接放到下面:
链接: Matlab中将图像数据生成coe文件.
以这个代码为模板稍微修改一下就可以用,亲测代码很好用,这张图片的RGB数据我只导入的R部分的数据,都是重复工作,只是一个小点子,我就没有全部导入,如果你想完全还原这张图片的话你可以弄一个24位宽的数组,然后导出来即可。不过导入图片数据的时候给自己挖了一个巨坑,下面会说到。

2.将数据存入ROM中

例化IP的部分这里我就不说了,直接说结构。我用到的这张图片242x400的,ROM的位宽选择8位,深度就根据图片的尺寸来,是96800,ROM设置成单口,然后将coe文件导入到ROM中这一步就结束了。

3.程序设计

这里说几个图片显示所用到的一些时序的概念,显示器显示图片是按照从左上角一点点扫描到右下角,然后探针又返回到左上角这样的反复循环,这里就有了行和列的概念,对于行来说基本单位是图片的一个像素点,一行所显示的像素点个数是由你所定义的显示标准(分辨率)决定的,对于列来说基本单位是行,列的尺寸也是由显示标准决定。在完整的传输一行数据的过程中有几个时序参数:1.行有效信号:这个参数是根据水平同步信号(HSYNC)得来,HSYNC规定了一张图片(一帧)中每一行数据的有效起始点;2.总行时间(h_total):从HSYNC的一个上升沿到下一个上升沿到来所需要的时间;3.行消隐脉冲:
(sync_h):各行之间的低电平时间。对于列也有几个时许参数:1.垂直同步信号(VSYNC):规定了一幅图片的起点;2.总列时间(v_total);3.场前肩(torch_f):从场计数开始到场同步的时间;场后肩(torch_b):从场同步结束到行同步开始的时间。(这部分是我从《基于FPGA的数字图像处理原理及应用》书上学到的,这里面的场前肩,场后肩我还没有理解…)
从ROM中存储数据是1位的,而1像素是八位数据,所以按照地址查询数据的时候要每个时钟给地址加8来保证一次传输一个像素数据。没有什么其他注意的点了,这里的RGB转HDMI部分我直接用的官方例程给的IP核,按照官方代码搬过来用的,这个可以上淘宝XILINX官方店里面提供的链接下载到。下面直接上代码吧。

//这个是第一个文件,这部分代码实现把从ROM读出来的数据变成RGB数据然后与生成的行同步信号,场同步信号,图片有效信号一起输出给RGB2HDMI。
// An highlighted block
////////////////////////////////////////////////////////////////////////////////
//   color bar generator module                                                 //
//                                                                              //
//  Author: meisq                                                               //
//          msq@qq.com                                                          //
//          ALINX(shanghai) Technology Co.,Ltd                                  //
//          heijin                                                              //
//     WEB: http://www.alinx.cn/                                                //
//     BBS: http://www.heijin.org/                                              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
// Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd                        //
//                    All rights reserved                                       //
//                                                                              //
// This source file may be used and distributed without restriction provided    //
// that this copyright statement is not removed from the file and that any      //
// derivative work contains the original copyright notice and the associated    //
// disclaimer.                                                                  //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

//================================================================================
//  Revision History:
//  Date          By            Revision    Change Description
//--------------------------------------------------------------------------------
//2013/4/16                    1.0          Original
//2013/4/18                    1.1          vs timing
//2013/5/7                     1.2          remove some warning
//2017/7/17                    1.3      
//*******************************************************************************/
`include "video_define.v"
module color_bar(
	input                 clk,           //pixel clock
	input                 rst,           //reset signal high active
	output                hs,            //horizontal synchronization
	output                vs,            //vertical synchronization
	output                de,            //video valid
	output[7:0]           rgb_r,         //video red data
	output[7:0]           rgb_g,         //video green data
	output[7:0]           rgb_b          //video blue data
);
//video timing parameter definition
`ifdef  VIDEO_1280_720
parameter H_ACTIVE = 16'd1280;           //horizontal active time (pixels)
parameter H_FP = 16'd110;                //horizontal front porch (pixels)
parameter H_SYNC = 16'd40;               //horizontal sync time(pixels)
parameter H_BP = 16'd220;                //horizontal back porch (pixels)
parameter V_ACTIVE = 16'd720;            //vertical active Time (lines)
parameter V_FP  = 16'd5;                 //vertical front porch (lines)
parameter V_SYNC  = 16'd5;               //vertical sync time (lines)
parameter V_BP  = 16'd20;                //vertical back porch (lines)
parameter HS_POL = 1'b1;                 //horizontal sync polarity, 1 : POSITIVE,0 : NEGATIVE;
parameter VS_POL = 1'b1;                 //vertical sync polarity, 1 : POSITIVE,0 : NEGATIVE;
`endif

//480x272 9Mhz
`ifdef  VIDEO_480_272
parameter H_ACTIVE = 16'd480; 
parameter H_FP = 16'd2;       
parameter H_SYNC = 16'd41;    
parameter H_BP = 16'd2;       
parameter V_ACTIVE = 16'd272; 
parameter V_FP  = 16'd2;     
parameter V_SYNC  = 16'd10;   
parameter V_BP  = 16'd2;     
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//640x480 25.175Mhz
`ifdef  VIDEO_640_480
parameter H_ACTIVE = 16'd640; 
parameter H_FP = 16'd16;      
parameter H_SYNC = 16'd96;    
parameter H_BP = 16'd48;      
parameter V_ACTIVE = 16'd480; 
parameter V_FP  = 16'd10;    
parameter V_SYNC  = 16'd2;    
parameter V_BP  = 16'd33;    
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//800x480 33Mhz
`ifdef  VIDEO_800_480
parameter H_ACTIVE = 16'd800; 
parameter H_FP = 16'd40;      
parameter H_SYNC = 16'd128;   
parameter H_BP = 16'd88;      
parameter V_ACTIVE = 16'd480; 
parameter V_FP  = 16'd1;     
parameter V_SYNC  = 16'd3;    
parameter V_BP  = 16'd21;    
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//800x600 40Mhz
`ifdef  VIDEO_800_600
parameter H_ACTIVE = 16'd800; 
parameter H_FP = 16'd40;      
parameter H_SYNC = 16'd128;   
parameter H_BP = 16'd88;      
parameter V_ACTIVE = 16'd600; 
parameter V_FP  = 16'd1;     
parameter V_SYNC  = 16'd4;    
parameter V_BP  = 16'd23;    
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif

//1024x768 65Mhz
`ifdef  VIDEO_1024_768
parameter H_ACTIVE = 16'd1024;
parameter H_FP = 16'd24;      
parameter H_SYNC = 16'd136;   
parameter H_BP = 16'd160;     
parameter V_ACTIVE = 16'd768; 
parameter V_FP  = 16'd3;      
parameter V_SYNC  = 16'd6;    
parameter V_BP  = 16'd29;     
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//1920x1080 148.5Mhz
`ifdef  VIDEO_1920_1080
parameter H_ACTIVE = 16'd1920;
parameter H_FP = 16'd88;
parameter H_SYNC = 16'd44;
parameter H_BP = 16'd148; 
parameter V_ACTIVE = 16'd1080;
parameter V_FP  = 16'd4;
parameter V_SYNC  = 16'd5;
parameter V_BP  = 16'd36;
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif
parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;//horizontal total time (pixels)
parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;//vertical total time (lines)
//define the RGB values for 8 colors
//parameter WHITE_R       = 8'hff;
//parameter WHITE_G       = 8'hff;
//parameter WHITE_B       = 8'hff;
//parameter YELLOW_R      = 8'hff;
//parameter YELLOW_G      = 8'hff;
//parameter YELLOW_B      = 8'h00;                                
//parameter CYAN_R        = 8'h00;
//parameter CYAN_G        = 8'hff;
//parameter CYAN_B        = 8'hff;                                
//parameter GREEN_R       = 8'h00;
//parameter GREEN_G       = 8'hff;
//parameter GREEN_B       = 8'h00;
//parameter MAGENTA_R     = 8'hff;
//parameter MAGENTA_G     = 8'h00;
//parameter MAGENTA_B     = 8'hff;
//parameter RED_R         = 8'hff;
//parameter RED_G         = 8'h00;
//parameter RED_B         = 8'h00;
//parameter BLUE_R        = 8'h00;
//parameter BLUE_G        = 8'h00;
//parameter BLUE_B        = 8'hff;
//parameter BLACK_R       = 8'h00;
//parameter BLACK_G       = 8'h00;
//parameter BLACK_B       = 8'h00;
reg hs_reg;                      //horizontal sync register
reg vs_reg;                      //vertical sync register
reg hs_reg_d0;                   //delay 1 clock of 'hs_reg'
reg vs_reg_d0;                   //delay 1 clock of 'vs_reg'
reg[11:0] h_cnt;                 //horizontal counter
reg[11:0] v_cnt;                 //vertical counter
reg[11:0] active_x;              //video x position 
reg[11:0] active_y;              //video y position 
reg[7:0] rgb_r_reg;              //video red data register
reg[7:0] rgb_g_reg;              //video green data register
reg[7:0] rgb_b_reg;              //video blue data register
reg h_active;                    //horizontal video active
reg v_active;                    //vertical video active
wire video_active;               //video active(horizontal active and vertical active)
reg video_active_d0;             //delay 1 clock of video_active
/**********User Define************/
reg [19:0]osd_ram_addr;
reg pos_vs_d0;
reg pos_vs_d1;
reg region_active;
reg region_active_d0;
//reg region_active_d1;
//reg region_active_d2;
wire [7:0]q;
//wire[11:0] 		pos_x;		//X坐标
//wire[11:0] 		pos_y;		//Y坐标
//wire       		pos_hs;
//wire       		pos_vs;
//wire       		pos_de;
//wire      [7:0]video_r;
//wire      [7:0]video_g;
//wire      [7:0]video_b;

parameter OSD_WIDTH   =  12'd242;	//设置OSD的宽度,可根据字符生成软件设置
parameter OSD_HEGIHT  =  12'd400;	//设置OSD的高度,可根据字符生成软件设置
/*********************************/
assign hs = hs_reg_d0;
assign vs = vs_reg_d0;
assign video_active = h_active & v_active;
assign de = video_active_d0;
assign rgb_r = rgb_r_reg;
assign rgb_g = rgb_g_reg;
assign rgb_b = rgb_b_reg;
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		begin
			hs_reg_d0 <= 1'b0;
			vs_reg_d0 <= 1'b0;
			video_active_d0 <= 1'b0;
		end
	else
		begin
			hs_reg_d0 <= hs_reg;
			vs_reg_d0 <= vs_reg;
			video_active_d0 <= video_active;
		end
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		h_cnt <= 12'd0;
	else if(h_cnt == H_TOTAL - 1)//horizontal counter maximum value
		h_cnt <= 12'd0;
	else
		h_cnt <= h_cnt + 12'd1;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		active_x <= 12'd0;
	else if(h_cnt >= H_FP + H_SYNC + H_BP - 1)//horizontal video active
		active_x <= h_cnt - (H_FP[11:0] + H_SYNC[11:0] + H_BP[11:0] - 12'd1);
	else
		active_x <= active_x;
end
always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		active_y <= 12'd0;
	else if(v_cnt >= V_FP + V_SYNC + V_BP - 1)//horizontal video active
		active_y <= v_cnt - (V_FP[11:0] + V_SYNC[11:0] + V_BP[11:0] - 12'd1);
	else
		active_y <= active_y;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		v_cnt <= 12'd0;
	else if(h_cnt == H_FP  - 1)//horizontal sync time
		if(v_cnt == V_TOTAL - 1)//vertical counter maximum value
			v_cnt <= 12'd0;
		else
			v_cnt <= v_cnt + 12'd1;
	else
		v_cnt <= v_cnt;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		hs_reg <= 1'b0;
	else if(h_cnt == H_FP - 1)//horizontal sync begin
		hs_reg <= HS_POL;
	else if(h_cnt == H_FP + H_SYNC - 1)//horizontal sync end
		hs_reg <= ~hs_reg;
	else
		hs_reg <= hs_reg;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		h_active <= 1'b0;
	else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//horizontal active begin
		h_active <= 1'b1;
	else if(h_cnt == H_TOTAL - 1)//horizontal active end
		h_active <= 1'b0;
	else
		h_active <= h_active;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		vs_reg <= 1'd0;
	else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//vertical sync begin
		vs_reg <= HS_POL;
	else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//vertical sync end
		vs_reg <= ~vs_reg;  
	else
		vs_reg <= vs_reg;
end

always@(posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		v_active <= 1'd0;
	else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//vertical active begin
		v_active <= 1'b1;
	else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1)) //vertical active end
		v_active <= 1'b0;   
	else
		v_active <= v_active;
end

//always@(posedge clk or posedge rst)
//begin
//	if(rst == 1'b1)
//		begin
//			rgb_r_reg <= 8'h00;
//			rgb_g_reg <= 8'h00;
//			rgb_b_reg <= 8'h00;
//		end
//	else if(video_active)
//		if(active_x == 12'd0)
//			begin
//				rgb_r_reg <= WHITE_R;
//				rgb_g_reg <= WHITE_G;
//				rgb_b_reg <= WHITE_B;
//			end
//		else if(active_x == (H_ACTIVE/8) * 1)
//			begin
//				rgb_r_reg <= YELLOW_R;
//				rgb_g_reg <= YELLOW_G;
//				rgb_b_reg <= YELLOW_B;
//			end         
//		else if(active_x == (H_ACTIVE/8) * 2)
//			begin
//				rgb_r_reg <= CYAN_R;
//				rgb_g_reg <= CYAN_G;
//				rgb_b_reg <= CYAN_B;
//			end
//		else if(active_x == (H_ACTIVE/8) * 3)
//			begin
//				rgb_r_reg <= GREEN_R;
//				rgb_g_reg <= GREEN_G;
//				rgb_b_reg <= GREEN_B;
//			end
//		else if(active_x == (H_ACTIVE/8) * 4)
//			begin
//				rgb_r_reg <= MAGENTA_R;
//				rgb_g_reg <= MAGENTA_G;
//				rgb_b_reg <= MAGENTA_B;
//			end
//		else if(active_x == (H_ACTIVE/8) * 5)
//			begin
//				rgb_r_reg <= RED_R;
//				rgb_g_reg <= RED_G;
//				rgb_b_reg <= RED_B;
//			end
//		else if(active_x == (H_ACTIVE/8) * 6)
//			begin
//				rgb_r_reg <= BLUE_R;
//				rgb_g_reg <= BLUE_G;
//				rgb_b_reg <= BLUE_B;
//			end 
//		else if(active_x == (H_ACTIVE/8) * 7)
//			begin
//				rgb_r_reg <= BLACK_R;
//				rgb_g_reg <= BLACK_G;
//				rgb_b_reg <= BLACK_B;
//			end
//		else
//			begin
//				rgb_r_reg <= rgb_r_reg;
//				rgb_g_reg <= rgb_g_reg;
//				rgb_b_reg <= rgb_b_reg;
//			end         
//	else
//		begin
//			rgb_r_reg <= 8'h00;
//			rgb_g_reg <= 8'h00;
//			rgb_b_reg <= 8'h00;
//		end
//end


always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        begin
            rgb_r_reg <= 8'h00;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'h00;
         end
    else if(video_active)
        if(region_active_d0 == 1'b1)
            begin
                
                rgb_r_reg <= q;
                rgb_g_reg <= 8'h00;
                rgb_b_reg <= 8'h00;
            end  
        else
            begin
                rgb_r_reg <= 8'hff;
                rgb_g_reg <= 8'hff;
                rgb_b_reg <= 8'hff;
            end     
    
    else
        begin
            rgb_r_reg <= 8'h00;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'h00;
        end
end


always @(posedge clk)
begin
    pos_vs_d0 <= vs_reg_d0;
    pos_vs_d1 <= pos_vs_d0;
end

always@(posedge clk)
begin
	region_active_d0 <= region_active;
//	region_active_d1 <= region_active_d0;
//	region_active_d2 <= region_active_d1;
end

always@(posedge clk)
begin
	if(active_y >= 12'd9 && active_y <= 12'd9 + OSD_HEGIHT - 12'd1 && active_x >= 12'd9 && active_x  <= 12'd9 + OSD_WIDTH - 12'd1)
		region_active <= 1'b1;
	else
		region_active <= 1'b0;
end

always @(posedge clk)
begin
    if(pos_vs_d0 == 1'b0 && pos_vs_d1 == 1'b1)
       osd_ram_addr <= 20'd0;
    else if(region_active == 1'b1)
        osd_ram_addr <= osd_ram_addr + 20'd8; 
end
osd_rom osd_rom_m0(
    	.clka                       (clk                    ),   
        .ena                        (1'b1                   ),     
        .addra                      (osd_ram_addr[19:3]     ),     //生成的字符一个点为1bit,由于数据宽度为8bit,因此8个周期检查一次数据
        .douta                      (q                      )  

);

ila ila_m0(
    .clk(clk),
    .probe0(rgb_r_reg),
    .probe1(rgb_g_reg),
    .probe2(rgb_b_reg),
    .probe3(active_x),
    .probe4(active_y),
    .probe5(q),
    .probe6(region_active),
    .probe7(osd_ram_addr)
);
//timing_gen_xy timing_gen_xy_m0(
//	.rst_n    (rst    ),
//	.clk      (clk     ),
//	.i_hs     (hs_reg_d0     ),
//	.i_vs     (vs_reg_d0     ),
//	.i_de     (video_active_d0     ),
//	.i_data   ({{rgb_r_reg},{rgb_g_reg},{rgb_b_reg}}   ),
//	.o_hs     (pos_hs   ),
//	.o_vs     (pos_vs   ),
//	.o_de     (pos_de   ),
//	.o_data   ({{video_r},{video_g},{video_b}} ),
//	.x        (pos_x    ),
//	.y        (pos_y    )
//);
endmodule 
// 头文件,将各个模块连接到一起
module top(
    input sys_clk,
    output hdmi_oen,
    output TMDS_clk_n,
    output TMDS_clk_p,
    output [2:0]TMDS_data_n,
    output [2:0]TMDS_data_p
);
wire video_clk;
wire video_clk_5x;
wire video_hs;
wire video_vs;
wire video_de;
wire[7:0] video_r;
wire[7:0] video_g;
wire[7:0] video_b;

color_bar hdmi_color_bar(
	.clk(video_clk),
	.rst(1'b0),
	.hs(video_hs),
	.vs(video_vs),
	.de(video_de),
	.rgb_r(video_r),
	.rgb_g(video_g),
	.rgb_b(video_b)
);

video_clock video_clock_m0
(
     // Clock in ports
    .clk_in1(sys_clk),
      // Clock out ports
    .clk_out1(video_clk),
    .clk_out2(video_clk_5x),
      // Status and control signals
    .reset(1'b0),
    .locked()
 );
 
rgb2dvi_0 rgb2dvi_m0 (
	// DVI 1.0 TMDS video interface
	.TMDS_Clk_p(TMDS_clk_p),
	.TMDS_Clk_n(TMDS_clk_n),
	.TMDS_Data_p(TMDS_data_p),
	.TMDS_Data_n(TMDS_data_n),
	.oen(hdmi_oen),
	//Auxiliary signals 
	.aRst_n(1'b1), //-asynchronous reset; must be reset when RefClk is not within spec
	
	// Video in
	.vid_pData({video_r,video_g,video_b}),
	.vid_pVDE(video_de),
	.vid_pHSync(video_hs),
	.vid_pVSync(video_vs),
	.PixelClk(video_clk),
	.SerialClk(video_clk_5x)// 5x PixelClk
); 
  
endmodule
//video_define.v
//这部分代码是定义你的显示标准的
`define	VIDEO_1280_720

运行结果

运行结果如下图所示:
基于FPGA的HDMI图片显示

这里我只导入了图片的R数据,背景设置成了红色,这些在代码部分都有体现。

下面讲讲我遇到的一个坑吧,就是在将图片输出成coe文件的时候,一定一定要注意你存的数据是按照行存的还是按照列存的,我在刚开始写的时候并没有注意导致把行列数据存反了,后来debug才发现。bug图长得有点丑,为了不破坏萌兔兔的美感我就不放上来了。

总结

第一次写博客,说实话有点不是很会写,没有像写论文一样用很书面的语言,我写博客的目的是为了总结自己学到的东西,之前看着夏雨闻老师的Verilog书的时候就萌生了写博客的念头,后来因为在忙研究方向而没有开始,我的研究方向是和fpga没有半毛钱瓜葛的,一个是数电,一个是光传感,人生嘛,总会遇到很多不如意的事情,我不能凭借自己的意志决定自己的研究方向,但是我并不打算妥协,这三年的经历很大程度会决定我后半生的工作方向,我是希望能把我感兴趣的东西变成我吃饭的家伙,只能对自己说———努力吧骚年!!!!!!!!!!

本文地址:https://blog.csdn.net/lang_jiaming/article/details/107441101