[X]关闭
3

USB3.0摄像头方案之FT601Q

摘要: 本文取名为“USB3.0摄像头方案之FT601Q”是因为我们还有一套“USB3.0 UVC摄像头方案之FT602Q”后续会发布。在完成了前面的电灯,测速后,我们主要对FT601Q官方的库函数使用有了深刻的了解。现在我们做一个比较实用的 ...

本文取名为“USB3.0摄像头方案之FT601Q”是因为我们还有一套“USB3.0 UVC摄像头方案之FT602Q”后续会发布。在完成了前面的电灯,测速后,我们主要对FT601Q官方的库函数使用有了深刻的了解。现在我们做一个比较实用的方案,实现摄像头的采集。当然我们本身的方案主要是用FPGA去采集视频,放到DDR缓存后,上位机读取缓存里面的视频数据,在电脑上显示。打通了FPGA视频数据到上位机后,一些做产品方案的客户可以加入自己的独家算法,实现FPGA的图像处理加速,然后通过上位机人机界面显示,或者做进一步的处理。

1、构架设计

如下图所示,对于FDMA是我们米联客(MSXBO英文注册商标)自定的AXI4总线IP非常适合于图像数据的缓存应用,我们设计的FDMA控制器构架可以很方便的应用于视频的数据缓存。如果你用过米联客的开发板并且之前已经掌握了FMDA IP的使用,那么这部分内容就很简单了。因为对于FT601Q的FPGA代码非常简单。我们对FT601Q的FPGA代码做了一个简单的视频信号转换后接到了FDMA控制器,之前已经开发好的的代码几乎不用修改就可以使用了。这也是我一直强调的构架设计,设计一个优秀的构架,一个可重复利用的构架,可以提高我们的编程效率,以及降低代码的bug.

 

 

2、FPGA BD 设计

下图的BD设计没啥好好说的,对于第一次使用FMDA的人来说,主要是了解FDMA这个IP的使用,那就去阅读我们专门讲解FMDA的章节吧,因为这个BD工程和专门讲解FMDA IP章节的一样,这就是优秀代码构架的好处,不管你用于什么工程,基本上只要做很小的代码修改。

3、USB接口代码

USB接口代码依然非常简单

module ft60x(

// system control

input                  Rstn_i,//fpga reset

output                 USBSS_EN,//power enable   

// FIFO interface    

input                  CLK_i,

inout [31:0]           DATA_io,

inout [3:0]            BE_io,

input                  RXF_N_i,   // ACK_N

input                  TXE_N_i,

output                 OE_N_o,

output                 WR_N_o,    // REQ_N

output                 SIWU_N_o,

output                 RD_N_o,

output                 WAKEUP_o,

output [1:0]           GPIO_o,

output                 usb_rd_en,

output                 usb_frame,

input  [31:0]          usb_data     

);

   

assign USBSS_EN = 1'b1;   

assign WAKEUP_o = 1'b1;

assign GPIO_o   = 2'b00;   

assign SIWU_N_o = 1'b0;

 

wire rstn;

 

(*mark_debug = "true"*) wire            usb_rd_en;

(*mark_debug = "true"*) wire            usb_frame;

 

(*mark_debug = "true"*) wire            RXF_N_i;   // ACK_N

(*mark_debug = "true"*) wire            TXE_N_i;

(*mark_debug = "true"*) reg             OE_N_o;

(*mark_debug = "true"*) reg             WR_N_o;    // REQ_N

(*mark_debug = "true"*) reg             RD_N_o;

 

(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] rd_data;

(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] wr_data;

(*mark_debug = "true"*) wire [3 :0] BE_RD;

(*mark_debug = "true"*) wire [3 :0] BE_WR;

(*mark_debug = "true"*) reg [1:0] USB_S;

 

wire data_rd_valid,data_wr_valid;

assign data_rd_valid = (RD_N_o==1'b0)&&(RXF_N_i==1'b0);

assign data_wr_valid = (WR_N_o==1'b0)&&(TXE_N_i==1'b0);

//read or write flag

assign rd_data  =  (USB_S==2'd1) ? DATA_io : 32'd0;//read data dir

assign BE_RD    =  (USB_S==2'd1) ? BE_io   : 4'd0;

assign DATA_io  =  (USB_S==2'd2) ? wr_data : 32'bz;// write data dir

assign BE_io    =  (USB_S==2'd2) ? BE_WR   : 4'bz;// write data dir

assign BE_WR    =  4'b1111;

 

assign wr_data = usb_data;

 

(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [15:0]rd_cnt;

 

(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [31:0] fram_cmd;

 

always @(posedge CLK_i)begin

    if(!rstn)begin

        fram_cmd <= 32'd0;

    end

    else if(data_rd_valid) begin

        fram_cmd <= rd_data;

    end

    else begin

        if(fram_cmd> 32'd0)begin

            fram_cmd <= fram_cmd - 1'b1;

        end

    end

end

 

assign usb_frame = (fram_cmd> 32'd2000);

assign usb_rd_en = data_wr_valid;

 

 

always @(posedge CLK_i)begin

    if(!rstn)begin

        USB_S <= 2'd0;

        OE_N_o <= 1'b1;

        RD_N_o <= 1'b1;

        WR_N_o <= 1'b1;

    end

    else begin

        case(USB_S)

        0:begin

            OE_N_o <= 1'b1;

            RD_N_o <= 1'b1;

            WR_N_o <= 1'b1;

            if((!RXF_N_i)) begin

                USB_S  <= 2'd1;

                OE_N_o <= 1'b0;  

            end

            else if((!TXE_N_i))begin

                USB_S  <= 2'd2;

            end

        end

        1:begin

            RD_N_o <= 1'b0;  

            if(RXF_N_i) begin

                USB_S  <= 2'd0;

                RD_N_o <= 1'b1;

                OE_N_o <= 1'b1;     

            end

        end

        2:begin

            WR_N_o <= 1'b0;

            if(TXE_N_i) begin

                USB_S  <= 2'd0;

                WR_N_o <= 1'b1;

             end

        end

        3:begin

            USB_S <= 2'd0;

        end

        endcase                

    end

end

 

 

Delay_rst_0 Delay_rst_inst

(

    .clk_i(CLK_i),

    .rstn_i(Rstn_i),

    .rst_o(rstn)

 );

 

  

endmodule

请注意这三个信号

usb_rd_en  此信号接到FDMA读接口的R0_rden_i代表读数据使能

usb_frame  此信号接到FDMA读接口的R0_FS_i代表读数据帧信号

usb_data   此信号接到FDMA读接口的R0_data_o代表读到的有效数据

 

always @(posedge CLK_i)begin

    if(!rstn)begin

        fram_cmd <= 32'd0;

    end

    else if(data_rd_valid) begin

        fram_cmd <= rd_data;

    end

    else begin

        if(fram_cmd> 32'd0)begin

            fram_cmd <= fram_cmd - 1'b1;

        end

    end

end

assign usb_frame = (fram_cmd> 32'd2000);

assign usb_rd_en = data_wr_valid;

 

再看其中的关键设计,fram_cmd计数器用保存上位机发过来的帧信号,这里就是用计数器值去产生读通道的帧同步信号。这个设计非常巧妙,不耗费代码,但是却实现了功能。

 

再看我们用data_wr_valid信号去实现了读使能,由于USB的数据对于FT601Q来说最大4KB,每次4KB一包完成后,必然有个停顿,进行下次一传输,正好我们就可以用这个信号作为每次的同步读取信号,类似行同步信号。

 

4、在线逻辑分析仪观察信号

 

比如下图是我们抓紧到的在线逻辑分析仪观察关键信号的截图

下图是帧同步

下图可以理解未场同步,每次最大4KB数据

5、测试结果

5.1 用测试数据观察

通过FPGA内部产生测试数据观察图像采集是否真确,这种方法成本最低,最有效,最有利于分析问题,如果测试图像输出没问题,基本上后面加入摄像头后问题就不大了。对于初学者,切勿急功近利,我们开发这么久了,还要注意循序渐进呢。我们这个USB3.0方案,从硬件筹备到目前开发的软件比较齐全都快1年多了。当然有了我们的方案你也许只要1天就搞定了。所以买一套板子买的是一套具备非常大价值的方案,以及学习资料。一个字”值”!

测试图像是1280X720P的,可以看到最大帧率可以到37FPS(上位机定时器是30ms采集一次)

5.2 5640摄像头采集

 

5.3 9V034摄像头采集


路过

雷人

握手
1

鲜花

鸡蛋

刚表态过的朋友 (1 人)

发表评论

最新评论

引用 I'mpossible 2020-4-1 16:25
感谢分享
引用 I'mpossible 2020-4-1 16:25
感谢分享
引用 duyingshuai 2020-2-24 11:12
学习

查看全部评论(3)

本文作者
2019-9-11 16:30
  • 7
    粉丝
  • 6110
    阅读
  • 3
    回复

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B
相关分类
热门评论
排行榜