基于 ZYNQ 的双目视觉图像采集系统设计(三)

1、视频流采集设计
       在对 CMOS Sensor 进行了寄存器的初始化配置后,并行数据总线上便开始持续的输出视频数据流。如图1所示,这是 CMOS Sensor 输出 VGA(640*480 分辨率)并行数据视频流协议的时序波形。我们可以看到,场同步信号 VSYNC 的每一个高脉冲表示新的一场图像(或者说是新的一帧图像)马上要开始传输;行同步信号 HREF 为高电平时,表示目前的数据总线 D[7:0]上的数据是有效的视频流。

图1 并行数据视频流协议

       VSYNC 是高电平有效或低电平有效,通常是可以通过 I2C 设置 CMOS Sensor 的寄存器进行更改。OV5640 的 VSYNC 实际上是低电平有效的,即 VSYNC 实际的波形和图 1 正好是反向的。在 image_capture.v 模块内部,我们把 image_sensor_vsync 作为高电平有效信号,这是因为在 zstar.v 模块输入 image_sensor_vsync 信号时,我们做了一下反向操作。

      如图2所示,视频时钟 PCLK 的每个上升沿,有效数据 D[7:0]、行同步信号 HREF 和场同步信号 VSYNC 被锁存到 FPGA 中。

图2 并行数据视频流时序

       一个有效的行将传输 640*2Bytes 的数据,也就是说,一个像素点会有 2Bytes 即 16bits的有效色彩值。对应 R、G、B 的位数分别为 5bits、6bits、5bits。传输的数据总线是 8bits,那么一个像素点对应就有 2 个 8bits 需要传输。每两个字节中的 R、G、B 格式定义如图 3 所示。

图3 RGB 565 输出时序框图

       理解了时序波形,我们再来看看代码中是如何对 CMOS Sensor送来的这组源同步信号进行采集的。先看一下实现图像缓存和时钟域切换的image_capture.v模块的接口定义。从CMOS Sensor 接口的同步信号 image_sensor_vsync , image_sensor_href , 数据总线 image_sensor_data 都是和时钟 image_sensor_pclk 同步的;在经过 DC FIFO 缓存后,产生的控制信号 image_ddr3_wren、image_ddr3_clr,数据总线 image_ddr3_wrdb 将和系统时钟 clk 同步。

module image_capture(
			input clk,	//时钟信号
			input rst_n,	//复位信号				
				//ImageSensor图像采集接口
			input image_sensor_pclk,	//视频时钟
			input image_sensor_vsync,	//视频场同步信号,高电平有效(有效视频传输时该信号拉低)
			input image_sensor_href,	//视频行同步信号
			input[7:0] image_sensor_data,	//视频数据总线		
				//ImageSensor数据写入DDR3接口
			output reg image_ddr3_wren,
			output image_ddr3_clr,
			output[15:0] image_ddr3_wrdb		
		);

       如图4 所示,这里通过一个异步 FIFO 来同步 CMOS Sensor 和 FPGA 内部逻辑。我们只要把image_sensor_pck、image sensor dataimage sensor href 分别作为 FIFO 的写入时钟写入数据和写入使能信号。此外,image sensor vsync 作为这个FIFO 的复位信号,每一新图像前 FIFO 进行一次清空,实际操作中,为了得到稳定有效的复位信号,我们使用内部时钟对这个复位信号打了一拍。这样,我们便把持续不断的视频流有效数据缓存到了 FIFO中。在 FIFO 的读端,判定数据大等于 16*16bit 时,就连续读出这 16 个数据,送到后续模块的DDR3 写缓存 FIFO 中。

图4 image_capture 模块功能框图

       以下逻辑对image_sensor_vsync 信号打一拍,同步到本地时钟域。image_sensor_vsync_l信号将作为 DC FIFO 的复位信号。


//image_sensor_vsync同步到clk时钟域
wire image_sensor_vsync_r;	//帧同步信号,高电平

register_diff_clk		register_diff_clk_dc2(
							.clk(clk),		
							.rst_n(rst_n),	
							.in_a(image_sensor_vsync),
							.out_b(image_sensor_vsync_r)	
						);
			

DC FIFO 例化如下。


//数据缓存FIFO例化
//将数据从Image Sensor的PCLK时钟域转换到FPGA内的50MHz时钟域
wire[8:0] image_fifo_rd_data_count;	//数据有效个数
reg image_fifo_rd_en;	//FIFO读请求信号
				
fifo_generator_1 	uut_image_cache_fifo (
  .rst(image_sensor_vsync_r),                      // input wire rst
  .wr_clk(image_sensor_pclk),                // input wire wr_clk
  .rd_clk(clk),                // input wire rd_clk
  .din(image_sensor_data),                      // input wire [7 : 0] din
  .wr_en(image_sensor_href),                  // input wire wr_en
  .rd_en(image_fifo_rd_en),                  // input wire rd_en
  .dout(image_ddr3_wrdb),                    // output wire [15 : 0] dout
  .full(),                    // output wire full
  .empty(),                  // output wire empty
  .rd_data_count(image_fifo_rd_data_count)  // output wire [8 : 0] rd_data_count
);

      FIFO IP核设置如图5所示。write width 是数据位宽,选择8。而write depth 指的是深度,即FPGA的FIFO使用多少个位宽为8的寄存器。由于分辨率是 640×480 ,也就是水平方向需要640个寄存器,所以需要至少640深度,但是可以留多点余量,比如1024,以防不够。之所以选择行数据为深度,是因为方便时序的设计。输出read width为16。

图5

        下面的代码设计了一个状态机。在 RFIFO_IDLE 状态下,判断 FIFO 中的可读数据量大于 16 个时(image_fifo_rd_data_count >= 9’d16),进入 RFIFO_RDDB 状态从 FIFO 中连续读取 16 个数据并送往后续模块。

//连续读出16个数据计数控制状态机
parameter RFIFO_IDLE	= 3'd0;
parameter RFIFO_RDDB	= 3'd1;
reg[2:0] rfifo_state;
reg[8:0] dcnt;	//读FIFO数据个数计数器

always @(posedge clk or negedge rst_n)
	if(!rst_n) rfifo_state <= RFIFO_IDLE;
	else begin
		case(rfifo_state)
			RFIFO_IDLE: if(image_fifo_rd_data_count >= 9'd16) rfifo_state <= RFIFO_RDDB;
						else rfifo_state <= RFIFO_IDLE;
			RFIFO_RDDB: if(dcnt >= 9'd16) rfifo_state <= RFIFO_IDLE;
						else rfifo_state <= RFIFO_RDDB;
			default: rfifo_state <= RFIFO_IDLE;
		endcase
	end

      RFIFO_RDDB 状态下,计数器 dcnt 工作,在 dcnt 取值在 1~16 期间,FIFO 读使能信号image_fifo_rd_en 拉高,以读取 FIFO 中的数据。

	//读FIFO数据个数计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) dcnt <= 9'd0;
	else if(rfifo_state == RFIFO_IDLE) dcnt <= 9'd0;
	else if(rfifo_state == RFIFO_RDDB) dcnt <= dcnt+1'b1;
	else dcnt <= 9'd0;

	//读FIFO使能信号产生逻辑
always @(posedge clk or negedge rst_n)
	if(!rst_n) image_fifo_rd_en	<= 1'b0;
	else if((dcnt > 9'd0) && (dcnt <= 9'd16)) image_fifo_rd_en	<= 1'b1;
	else image_fifo_rd_en <= 1'b0;

register_diff_clk.v

module register_diff_clk(
			input clk,		
			input rst_n,	
			input in_a,
			output out_b	
		);
	
reg[1:0] temp;

always @(posedge clk or negedge rst_n)	
	if(!rst_n) temp <= 2'b00;
	else temp <= {temp[0],in_a};

assign out_b = temp[1];	

endmodule

image_capture.v

module image_capture(
			input clk,	//时钟信号
			input rst_n,	//复位信号				
				//ImageSensor图像采集接口
			input image_sensor_pclk,	//视频时钟
			input image_sensor_vsync,	//视频场同步信号,高电平有效(有效视频传输时该信号拉低)
			input image_sensor_href,	//视频行同步信号
			input[7:0] image_sensor_data,	//视频数据总线		
				//ImageSensor数据写入DDR3接口
			output reg image_ddr3_wren,
			output image_ddr3_clr,
			output[15:0] image_ddr3_wrdb		
		);


//image_sensor_vsync同步到clk时钟域
wire image_sensor_vsync_r;	//帧同步信号,高电平

register_diff_clk		register_diff_clk_dc2(
							.clk(clk),		
							.rst_n(rst_n),	
							.in_a(image_sensor_vsync),
							.out_b(image_sensor_vsync_r)	
						);
			

//数据缓存FIFO例化
//将数据从Image Sensor的PCLK时钟域转换到FPGA内的50MHz时钟域
wire[8:0] image_fifo_rd_data_count;	//数据有效个数
reg image_fifo_rd_en;	//FIFO读请求信号
				
fifo_generator_1 	uut_image_cache_fifo (
  .rst(image_sensor_vsync_r),                      // input wire rst
  .wr_clk(image_sensor_pclk),                // input wire wr_clk
  .rd_clk(clk),                // input wire rd_clk
  .din(image_sensor_data),                      // input wire [7 : 0] din
  .wr_en(image_sensor_href),                  // input wire wr_en
  .rd_en(image_fifo_rd_en),                  // input wire rd_en
  .dout(image_ddr3_wrdb),                    // output wire [15 : 0] dout
  .full(),                    // output wire full
  .empty(),                  // output wire empty
  .rd_data_count(image_fifo_rd_data_count)  // output wire [8 : 0] rd_data_count
);


//连续读出16个数据计数控制状态机
parameter RFIFO_IDLE	= 3'd0;
parameter RFIFO_RDDB	= 3'd1;
reg[2:0] rfifo_state;
reg[8:0] dcnt;	//读FIFO数据个数计数器

always @(posedge clk or negedge rst_n)
	if(!rst_n) rfifo_state <= RFIFO_IDLE;
	else begin
		case(rfifo_state)
			RFIFO_IDLE: if(image_fifo_rd_data_count >= 9'd16) rfifo_state <= RFIFO_RDDB;
						else rfifo_state <= RFIFO_IDLE;
			RFIFO_RDDB: if(dcnt >= 9'd16) rfifo_state <= RFIFO_IDLE;
						else rfifo_state <= RFIFO_RDDB;
			default: rfifo_state <= RFIFO_IDLE;
		endcase
	end
	
	//读FIFO数据个数计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) dcnt <= 9'd0;
	else if(rfifo_state == RFIFO_IDLE) dcnt <= 9'd0;
	else if(rfifo_state == RFIFO_RDDB) dcnt <= dcnt+1'b1;
	else dcnt <= 9'd0;

	//读FIFO使能信号产生逻辑
always @(posedge clk or negedge rst_n)
	if(!rst_n) image_fifo_rd_en	<= 1'b0;
	else if((dcnt > 9'd0) && (dcnt <= 9'd16)) image_fifo_rd_en	<= 1'b1;
	else image_fifo_rd_en <= 1'b0;
	

//送给DDR3的数据使能信号和清楚信号产生
	
always @(posedge clk) begin
	image_ddr3_wren <= image_fifo_rd_en;	
end	

assign image_ddr3_clr = image_sensor_vsync_r;
	
endmodule

image_controller.v

module image_controller(
			input clk,	//50MHz时钟
			input rst_n,	//复位信号,低电平有效
				//ImageSensor图像采集接口
			(*mark_debug = "true"*) input image_sensor_pclk,	//视频时钟
			(*mark_debug = "true"*) input image_sensor_vsync,	//视频场同步信号,高电平有效(有效视频传输时该信号拉低)
			(*mark_debug = "true"*) input image_sensor_href,	//视频行同步信号
			(*mark_debug = "true"*) input[7:0] image_sensor_data,	//视频数据总线
				//ImageSensor串行配置接口
			(*mark_debug = "true"*) output image_sensor_scl,	//串行配置IIC时钟信号
			(*mark_debug = "true"*) inout image_sensor_sda,		//串行配置IIC数据信号	
				//ImageSensor复位与低功耗接口
			output image_sensor_reset_n,	//复位接口,低电平有效
			output image_sensor_pwdn,	//低功耗使能信号,高电平有效
				//ImageSensor数据写入DDR3接口
			(*mark_debug = "true"*) output image_ddr3_wren,
			output image_ddr3_clr,
			(*mark_debug = "true"*) output[15:0] image_ddr3_wrdb			
		);

		
	//低功耗使能信号,高电平有效
assign image_sensor_pwdn = ~rst_n;
	
	//复位接口,低电平有效
assign image_sensor_reset_n = rst_n;		



//IIC寄存器初始化配置
wire tiic_init_done;	//IIC配置完成标志位,高电平有效

I2C_OV5640_Init_RGB565		uut_I2C_OV5640_Init_RGB565(
								.clk(clk),		//100MHz
								.rst_n(rst_n),		//Global Reset
								.i2c_sclk(image_sensor_scl),	//I2C CLOCK
								.i2c_sdat(image_sensor_sda),	//I2C DATA
								.config_done(tiic_init_done)	//Config Done
							);



//视频输入缓存控制

image_capture		uut_image_capture(
						.clk(clk),	//时钟信号
						.rst_n(rst_n & tiic_init_done),	//复位信号				
							//ImageSensor采集接口
						.image_sensor_pclk(image_sensor_pclk),	//视频时钟
						.image_sensor_vsync(image_sensor_vsync),	//视频场同步信号,高电平有效(有效视频传输时该信号拉低)
						.image_sensor_href(image_sensor_href),	//视频行同步信号
						.image_sensor_data(image_sensor_data),	//视频数据总线
							//ImageSensor数据写入DDR3接口
						.image_ddr3_wren(image_ddr3_wren),
						.image_ddr3_clr(image_ddr3_clr),
						.image_ddr3_wrdb(image_ddr3_wrdb)		
					);

endmodule

至此整个image_controller已完成,接下来将解析AXI HP 总线读写 DDR3。

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Zynq的摄像头采集系统设计与实现涉及到硬件与软件的两个方面。 在硬件方面,首先需要选择合适的摄像头,并将其连接到Zynq的片上资源中。常见的连接方式包括CMOS接口或者MIPI接口。接下来,需要对信号进行适配,由于摄像头输出的信号格式可能与Zynq的输入格式不一致,所以需要添加适配器或转换器。接着,需要将摄像头采集到的图像通过高速总线传输到Zynq的处理系统中。可以选择使用DDR内存或者片上RAM作为存储缓冲区。最后,可以添加适当的硬件外设,如按钮、LED灯等,以方便控制与显示。 在软件方面,首先需要配置Zynq处理系统FPGA部分,包括设置时钟频率、引脚分配等。接着,需要编写驱动程序与操作系统,以便能够对摄像头进行初始化、配置和图像数据的获取。可以选择使用Linux操作系统,并在其上编写相应的驱动程序。然后,需要编写应用程序,以实现对摄像头的采集控制、图像处理、存储等功能。可以使用OpenCV等常见图像处理库来帮助完成这些功能。 最后,要进行系统的测试与调试,包括验证摄像头与Zynq的连接是否正常、图像采集是否准确、图像处理是否正确等。可以通过显示摄像头采集到的图像、输出处理结果等方式进行验证。如果发现问题,需要检查硬件电路和相应的软件程序,进行必要的更改与修复。 总之,基于Zynq的摄像头采集系统设计与实现涉及到硬件与软件两个方面,需要选择合适的摄像头、进行连接与信号适配,编写驱动程序与操作系统开发应用程序,并进行系统测试与调试,以保证其正常运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值