【Zedboard】FPGA图像处理 基于ZYNQ完成RGB彩色图像在VGA显示与二值化 Verilog代码实现

【Zedboard】FPGA图像处理 基于ZYNQ完成图像 二值化 Verilog代码实现

目前的成像状态:
在这里插入图片描述
在这里插入图片描述

(一)、配置摄像头传感器并完成完成图像实时采集:

(a)先介绍摄像头配置的模块:
本项目中使用的摄像头传感器芯片是OV5620,硬件接口上:支持标准的SCCB接口,兼容I2C接口,支持PMOD双排接口,本项目中使用的即为PMOD_5M双排接口。帧率:7.5—120fps。输出信号是10位的RGB RAW格式的图像数据。
OV5620的系统框图如图1-1所示:
图1-1
其Block Design模块如下图1-2所示:
1-2
按照OV5620数据手册,在配置摄像头模块中,采用的是I2C协议对OV5620的初始化寄存器进行配置。主要完成的配置信息如下:
图1-3
该配置信息,完成了对OV5620数据手册所描述的对RGB三色数据的增益、AWE增益、图像对比度增幅、输出图像分辨率大小的配置。I2C时序控制图入下图1-4所示。
图1-4
I2C_OV5620_Config模块的具体实现:

//
// Company: 
// Engineer: Tan junfu
// 
// Create Date: 2020/12/06 01:23:35
// Design Name: 
module I2C_OV5620_Config (	//	Host Side
						iCLK,
						iRST_N,
						//	I2C Side
						I2C_SCLK,
						I2C_SDAT	);
//	Host Side
input		iCLK;
input		iRST_N;
//	I2C Side
output		I2C_SCLK;
inout		I2C_SDAT;
//	Internal Registers/Wires
reg	[15:0]	mI2C_CLK_DIV;
reg	[23:0]	mI2C_DATA;
reg			mI2C_CTRL_CLK;
reg			mI2C_GO;
wire		mI2C_END;
wire		mI2C_ACK;
reg	[15:0]	LUT_DATA;
reg	[5:0]	LUT_INDEX;
reg	[3:0]	mSetup_ST;

//	Clock Setting
parameter	CLK_Freq	=	50000000;	//	50	MHz
parameter	I2C_Freq	=	50000;		//	20	KHz
//	LUT Data Number
parameter	LUT_SIZE	=	18;

/	I2C Control Clock	
always@(posedge iCLK or negedge iRST_N)
begin
	if(!iRST_N)
	begin
		mI2C_CTRL_CLK	<=	0;
		mI2C_CLK_DIV	<=	0;
	end
	else
	begin
		if( mI2C_CLK_DIV	< (CLK_Freq/I2C_Freq) )
		mI2C_CLK_DIV	<=	mI2C_CLK_DIV+1;
		else
		begin
			mI2C_CLK_DIV	<=	0;
			mI2C_CTRL_CLK	<=	~mI2C_CTRL_CLK;
		end
	end
end

I2C_Controller 	u0	(	.CLOCK(mI2C_CTRL_CLK),		//	Controller Work Clock
						.I2C_SCLK(I2C_SCLK),		//	I2C CLOCK
 	 	 	 	 	 	.I2C_SDAT(I2C_SDAT),		//	I2C DATA
						.I2C_DATA(mI2C_DATA),		//	DATA:[SLAVE_ADDR,SUB_ADDR,DATA]
						.GO(mI2C_GO),      			//	GO transfor
						.END(mI2C_END),				//	END transfor 
						.ACK(mI2C_ACK),				//	ACK
						.RESET(iRST_N)	);

//	Config Control	
always@(posedge mI2C_CTRL_CLK or negedge iRST_N)
begin
	if(!iRST_N)
	begin
		LUT_INDEX	<=	0;
		mSetup_ST	<=	0;
		mI2C_GO		<=	0;
	end
	else
	begin
		if(LUT_INDEX<LUT_SIZE)
		begin
			case(mSetup_ST)
			0:	begin
					mI2C_DATA	<=	{8'h60,LUT_DATA};
					mI2C_GO		<=	1;
					mSetup_ST	<=	1;
				end
			1:	begin
					if(mI2C_END)
					/*begin
						if(!mI2C_ACK)
						mSetup_ST	<=	2;
						else
						mSetup_ST	<=	0;							
						mI2C_GO		<=	0;*/
						begin
				//	mI2C_WR     <=	0;
					mI2C_GO		<=	0;
					if(~mI2C_ACK)			//ACK ACTIVE
						mSetup_ST	<=	2;	//INDEX ++
					else
						mSetup_ST	<=	0;	//Repeat Transfer						
					
					end
				end
			2:	begin
					LUT_INDEX	<=	LUT_INDEX+1;
					mSetup_ST	<=	0;
				end
			endcase
			end
		else
			begin
		//	Config_Done <= 1'b1;
			LUT_INDEX 	<= LUT_INDEX;
			mSetup_ST	<=	0;
			mI2C_GO		<=	0;
		//	mI2C_WR     <=	0;
			end
		

	end
end

/	Config Data LUT	  //	
always
begin
	case(LUT_INDEX)

	0	:	LUT_DATA	<=	16'h0000;
	1	:	LUT_DATA	<=	16'h1140;
	2	:	LUT_DATA	<=	16'h8107;//dsp	
	3	:	LUT_DATA	<=	16'hB8F0;//red Gain limit
	4	:	LUT_DATA	<=	16'hB9F0;//Green Gain limit
	5	:	LUT_DATA	<=	16'hBAF0;//blue Gain limit
	6	:	LUT_DATA	<=	16'h1620;//Green Gain
	7	:	LUT_DATA	<=	16'h0220;//Red Gain
	8	:	LUT_DATA	<=	16'h0120;//Blue Gain
	
	9	:	LUT_DATA	<=	16'h1240;//1280*960
	10	:	LUT_DATA	<=	16'h0cc8;//1280*960
//	11	:	LUT_DATA	<=	16'h1712;//H Start
//	12	:	LUT_DATA	<=	16'h18b4;//H End
//	13	:	LUT_DATA	<=	16'h1901;//V Start
//	14	:	LUT_DATA	<=	16'h1af4;//V End

	//9	:	LUT_DATA	<=	16'h1240;//1.3MEGA
default:		LUT_DATA	<=	16'dx ;
	endcase
end

endmodule

(二)、采集模块的设计

在这里插入图片描述

Capture_prepare模块是图像预处理模块,主要是由于寄存器配置好后或系统复位时会有1ms的延时(来自OV5620数据手册),并且前10帧数据也是不能使用的。因此该模块完成了采集延时处理与数据整合输出有效并且能够正确使用的图像数据。

constant模块是一些实际可调参数,比如画面分辨率,图像采集区域的坐标配置等。

CCD_Capture模块则是数据采集的主模块,完成下OV5620传感器像素时序下对原数据的采集与帧转化,并且标记每一帧的地址,与之后的缓存模块进行交互。

//
// Company: 
// Engineer: Tan junfu
// 
// Create Date: 2020/12/06 00:23:35
// Design Name: 
module CCD_Capture(	oDATA,
					oDVAL,
					oX_Cont,
					oY_Cont,
					oTX_Cont,
					oTY_Cont,
					oFrame_Cont,
					oSYNC,
					
					iX_POS,
					iY_POS,
					iDATA,
					iFVAL,
					iLVAL,
					iSTART,
					iEND,
					iCLK,
					iRST_N,

					ADDR
					);
//--------------------------------------------------//
parameter	DATA_SIZE	=	10;
//--------------------------------------------------//
input	[DATA_SIZE-1:0]	iDATA;
input			iFVAL;
input			iLVAL;
input			iSTART;
input			iEND;
input			iCLK;
input			iRST_N;
input		[15:0]	iX_POS;
input		[15:0]	iY_POS;
output	[DATA_SIZE-1:0]	oDATA;
output	[15:0]	oTX_Cont;
output	[15:0]	oTY_Cont;
output	[15:0]	oX_Cont;
output	[15:0]	oY_Cont;
output	[31:0]	oFrame_Cont;
output			oDVAL;
output			oSYNC;

output reg  [18:0]  ADDR;

reg		[15:0]	mX_POS;
reg		[15:0]	mY_POS;
reg				rSYNC;
reg	[15:0]	TX_Cont;
reg	[15:0]	TY_Cont;
reg	[15:0]	now_TX_Cont;
reg	[15:0]	now_TY_Cont;
reg				Pre_FVAL;
reg				Pre_LVAL;
reg				mCCD_FVAL;
reg				mCCD_LVAL;
reg				mPOS_VAL;
reg		[DATA_SIZE-1:0]	mCCD_DATA;
reg		[15:0]	X_Cont;
reg		[15:0]	Y_Cont;
reg		[31:0]	Frame_Cont;
reg				mSTART;

assign	oX_Cont		=	X_Cont;
assign	oY_Cont		=	Y_Cont;
assign	oTX_Cont	=	TX_Cont;
assign	oTY_Cont	=	TY_Cont;
assign	oFrame_Cont	=	Frame_Cont;
assign	oDATA		=	mCCD_DATA;
assign	oDVAL		=	mCCD_FVAL&mCCD_LVAL&mPOS_VAL;
assign	oSYNC		=	rSYNC;

always@(posedge iCLK or negedge iRST_N)
begin
	if(!iRST_N)
	mSTART	<=	0;
	else
	begin
		if(iSTART)
		mSTART	<=	1;
		if(iEND)
		mSTART	<=	0;		
	end
end

reg [15:0] cnt;

always@(posedge iCLK or negedge iRST_N)
begin
	if(!iRST_N)
	begin
		Pre_FVAL	<=	0;
		mCCD_FVAL	<=	0;
		mCCD_LVAL	<=	0;
		mCCD_DATA	<=	0;
		X_Cont		<=	0;
		Y_Cont		<=	0;
		TX_Cont		<=	0;
		TY_Cont		<=	0;
		//mX_POS		<=iX_POS;
		//mY_POS		<=iY_POS;
	end
	else
	begin
		Pre_FVAL	<=	iFVAL;
		Pre_LVAL	<=	iLVAL;
		
		
		if( ({Pre_FVAL,iFVAL}==2'b01) && mSTART )
		//if( ({Pre_FVAL,iFVAL}==2'b01))
		begin
		mCCD_FVAL	<=	1;
		rSYNC			<=	1;
		mX_POS		<=	iX_POS;
		mY_POS		<=	iY_POS;
		ADDR <= 0;
		end
		else if({Pre_FVAL,iFVAL}==2'b10)
		begin
		mCCD_FVAL	<=	0;
		rSYNC		<=	0;
		
		 if(cnt>10)
			begin
			cnt<=0;
			TX_Cont<= now_TX_Cont;
			TY_Cont<= now_TY_Cont;
			end
		 else cnt<=cnt+1;
		 
		end
		else rSYNC		<=	0;
		
		mCCD_LVAL	<=	iLVAL;
		mCCD_DATA	<=	iDATA;
		
		if((X_Cont	>= mX_POS)	&&(X_Cont	<	mX_POS+640)&&	(Y_Cont	>=	mY_POS)	&&	(Y_Cont	<	mY_POS+480))
		begin
		mPOS_VAL	<=	1;
		ADDR <= ADDR +1;
		end
	   else
		mPOS_VAL	<=	0;

      if(Y_Cont	>=	mY_POS+480)
      ADDR<=0;


		if(mCCD_FVAL)
		 begin
		   rSYNC		<=	0;
			if(mCCD_LVAL)
			 begin
				 X_Cont	<=	X_Cont+1;
				 now_TX_Cont<=X_Cont;
//				 if(ADDR<307199)
//				 
				 if(TX_Cont<X_Cont)TX_Cont<=X_Cont;
			 end
			else
			 begin
				X_Cont	<=	0;
			 end
		 
			 if({Pre_LVAL,iLVAL}==2'b01)  
			  begin
			  Y_Cont	<=	Y_Cont+1;
			   now_TY_Cont<=Y_Cont;
			  if(TY_Cont<Y_Cont)TY_Cont<=Y_Cont;
			  end
		 end
		else 
			begin
			Y_Cont<=0;
			end
	end
end


always@(posedge iCLK or negedge iRST_N)
begin
	if(!iRST_N)
	Frame_Cont	<=	0;
	else
	begin
		//if( ({Pre_FVAL,iFVAL}==2'b01) && mSTART )
		if( ({Pre_FVAL,iFVAL}==2'b01)  )
		Frame_Cont	<=	Frame_Cont+1;
	end
end

endmodule

(三)、图像数据在DDR和BRAM中的两种缓存方案

本项目中两种技术方案均已实现,现在以BRAM为例说明:
Block Design图如下所示:
在这里插入图片描述
该模块完成了图像的帧缓存,采用双通道与流水线的设计,加快数据读写速度,提高了图像缓存的效率。由于Zedboard的VGA显示采用的是RGB444的格式,因此该RAM的深度也是12位。
使用多端口存储器并不能“真正意义”上地提高存储器的带宽,实际上,存储器的位宽和大小均没有发生变化。但是多端口存储器提供了多个存取接口,也就是多个数据总线和地址总线。多个端口可以同时提供读写功能,理论上将带宽提升了一倍。在本项目中使用多端口比不使用多端口快了1.56倍的读写速度。
为了提高时钟的利用效率,将存储器以一个与系统其他部分更高的速度运行,可以允许在一个系统时钟内对存储器进行两次或多次访问。存储器应该和时钟同步,这样能避免同步问题。双倍数据速率(DDR)存储器就是这样的一种存储器。它能在时钟的上升沿和下降沿各传送一次数据,即每个时钟周期传送两次数据。对于时钟速度较低或者具有高速存储器的系统来说,使用一个较高频率的RAM时钟才是可行的。
此外,当数据每隔几个时钟进入到流水线处理时,整个设计都能以较高的时钟速率运行。这种“多阶段”设计,时钟周期的数目或阶段与像素时钟有关。多阶段设计不仅增加了带宽,而且在不同阶段重复使用硬件而减少了计算硬件。
BRAM IP核配置过程:

在这里插入图片描述
在这里插入图片描述
模块例化:

frame_buffer u_fb (
					.clka(CMOS_PIXCLK),
					.wea(value_1),
					.addra(wr_address),
					.dina({mCMOS_R[9:6],mCMOS_G[9:6],mCMOS_B[9:6]}),
					
					.clkb(VGA_CTRL_CLK),
					.addrb(rd_address),
					.doutb({Image_Read_R[9:6],Image_Read_G[9:6],Image_Read_B[9:6]})
				);

(四)、图像数据在VGA上成像显示

因为涉及到二值化,所以还需要编写一个算法模块再到VGA显示模块。
在这里插入图片描述
最终经过RGB565转化到RGB444格式:
在这里插入图片描述
关于VGA_display模块的代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: Tan junfu
// 
// Create Date: 2020/12/06 01:23:35
// Design Name: 

module                  vga_display(
input                   clk25              ,
output   [3:0]          vga_red             ,
output   [3:0]          vga_green           ,
output   [3:0]          vga_blue            ,
output                  vga_hsync           ,
output                  vga_vsync           ,
output   [9:0]          HCnt                ,
output   [9:0]          VCnt                ,
output   [18:0]         frame_addr          ,
input    [11:0]         frame_pixel
                    
);

//parameters define
parameter hRez        = 640;      
parameter hStartSync  = 640+16;   
parameter hEndSync    = 640+16+96;
parameter hMaxCount   = 800;      
                                  
parameter vRez        = 480;      
parameter vStartSync  = 480+10;   
parameter vEndSync    = 480+10+2; 
parameter vMaxCount   = 480+10+2+3;
                                           
parameter hsync_active = 0;    
parameter vsync_active = 0;    



reg [9:0]         hCounter=0 ;
reg [9:0]         vCounter=0	;
reg [18:0]        address =0;
reg               blank   =1; 
reg vga_hsync_reg           ;
reg vga_vsync_reg           ;
                       
assign frame_addr = address;
assign	HCnt = hCounter;
assign	VCnt = vCounter;

always@(posedge clk25)begin
            if ( hCounter == hMaxCount-1) begin
			 	hCounter <= 0;
			 	if (vCounter == vMaxCount-1) 
			 		vCounter <= 0;
			 	else
			 		vCounter <= vCounter+1;
			 		
			end 
			 	
			else
			 	hCounter <= hCounter+1;
		
end


reg [3:0] vga_red_reg;
reg [3:0] vga_green_reg;
reg [3:0] vga_blue_reg;

assign    vga_red    =   vga_red_reg  ;
assign    vga_green  =     vga_green_reg;
assign    vga_blue   =    vga_blue_reg   ;

always@(posedge clk25)begin
    if(blank == 0 )begin
        vga_red_reg   <= frame_pixel[11:8];
        vga_green_reg <= frame_pixel[7:4];
        vga_blue_reg  <= frame_pixel[3:0];  
    end
    else begin
         vga_red_reg  <= 4'b0000;
         vga_green_reg<= 4'b0000; 
         vga_blue_reg <= 4'b0000; 
    end
end






always@(posedge clk25)begin
    if (vCounter  >= vRez) begin
				address <= 0;
				blank <= 1;
	end
	else begin
            if (hCounter < 640) begin
                blank <=0;
                address <= address+1;
            end
            else
                blank <= 1;
				
	end 
end		
	
			
always@(posedge clk25)begin
    if (hCounter > hStartSync && hCounter <= hEndSync) 
        vga_hsync_reg <= hsync_active;
    else
        vga_hsync_reg <= ~hsync_active;
end	


always@(posedge clk25)begin
    if (vCounter >= vStartSync && vCounter < vEndSync) 
        vga_vsync_reg <= vsync_active;
    else
        vga_vsync_reg <= ~vsync_active;
end				


assign vga_vsync =	vga_vsync_reg;	
assign vga_hsync =	vga_hsync_reg;	

endmodule

(五)、最终图像显示效果

在这里插入图片描述
在这里插入图片描述

(六)、项目中遇到的问题与解决方法

难题一:VGA成像失败,显示器呈花屏。
解决方法:认真检查数据位,最终发现是RGB处理模块映射后高低位需要转换。并且需要从RGB565取高位后到RGB444格式再传入VGA接口。转换模块如下:
在这里插入图片描述
难题二:采集失败,显示器呈黑屏,无数据。
解决方法:通过仿真得知,摄像头在初始化和复位后的前10~20帧数据是无效的,输出的数据并不是采集的真实图像数据。故做了延时模块,添加了帧计数器,并成功解决。
在这里插入图片描述
难题三:加入了算法模块即——二值化转换后画面混乱,无法正常观测图像。
解决方法:使用锁相环PLL时钟分频IP核,重新分配VGA显示模块与算法模块的时钟,因为从RAM中取出图像数据成像时,要使用独立时钟源。

//end

以上。需要完整工程代码的朋友请私信我。

  • 7
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值