本帖最后由 正点原子运营 于 2024-1-10 11:19 编辑
第五十五章 基于OV5640的以太网视频传输实验
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
6)FPGA技术交流QQ群:435699340
OV5640同OV7725一样,都是OmniVision(豪威科技)公司生产的CMOS图像传感器。不同的是,OV5640支持更高的分辨率、采集速率,具有更高的图像处理性能,主要应用在手机、数码相机、电脑多媒体等领域。 在“基于OV7725的以太网视频传输实验”中,我们成功地在上位机显示软件上实时显示出了摄像头采集的图像。本章我们将使用ATK-DFPGL22G开发板实现对OV5640的数字图像采集,并通过开发板上的以太网接口发送给上位机实时显示。 本章分为以下几个章节: 1.1 简介 1.2 实验任务 1.3 硬件设计 1.4 程序设计 1.5 下载验证
1.1 简介在“OV5640摄像头RGB_LCD显示实验”中对OV5640的视频传输时序、SCCB协议以及寄存器的配置信息等内容作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“OV5640摄像头RGB_LCD显示实验”中的OV5640简介部分。
1.2 实验任务本节实验任务是使用ATK-DFPGL22G开发板及OV5640摄像头实现图像采集,并通过开发板上的以太网接口发送给上位机实时显示。
1.3 硬件设计本次实验的硬件电路和“基于OV7725的以太网视频传输实验”中的硬件电路是基本相同的,都使用了ATK-DFPGL22G开发板上的摄像头扩展接口,IO管脚位置的配置也是一样的。 唯一的不同点在于,原先在“基于OV7725的以太网视频传输实验”中的ATK-DFPGL22G输出给摄像头扩展接口的“cam_sgm_ctrl”信号,在本实验中变为了ATK-DFPGL22G输出给OV5640的“电源休眠模式选择”信号cam_pwdn;且原来的cam_sgm_ctrl是我们需要一直赋值为高电平,但对于OV5640,cam_pwdn我们需要将其一直赋值为低电平,表示不休眠即正常工作模式。 由于以太网与摄像头引脚数目较多,且在前面相应的章节中已经给出它们的管脚列表,这里就不重复列出了。
1.4 程序设计图 55.4.1是根据本章实验任务画出的系统框图。对比“基于OV7725的以太网视频传输实验”的系统框图可以发现,我们只是把外设OV7725模块替换成了OV5640模块,其余模块基本相同(IIC配置模块有差异)。时钟IP核模块用于为IIC驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟。I2C驱动模块和I2C配置模块用于初始化OV5640图像传感器;摄像头采集模块负责采集摄像头图像数据,并且把图像数据连接至图像数据封装模块,图像数据封装模块将输入的图像数据进行位拼接,并添加图像的帧头和行场分辨率;以太网顶层模块实现以太网数据的收发;开始传输控制模块控制以太网顶层模块开始/停止发送数据。 FPGA顶层模块(ov5640_udp_pc)例化了以下七个模块:时钟IP核模块(clk_wiz_0)、I2C驱动模块(i2c_dri)、I2C配置模块(i2c_ov5640_rgb565_cfg)、摄像头图像采集模块(cmos_capture_data)、开始传输控制模块(start_transfer_ctrl)、图像数据封装模块(img_data_pkt)和以太网顶层模块模块(eth_top)。 时钟模块(clk_wiz_0):时钟IP核模块通过调用PLL IP核来实现,总共输出2个时钟,频率分别为50Mhz和200Mhz时钟。50Mhz时钟作为IIC驱动模块的操作时钟;200Mhz时钟作为IDELAYCTRL源语的参考时钟。 I2C驱动模块(i2c_dri):I2C驱动模块负责驱动OV5640 SCCB接口总线,用户可根据该模块提供的用户接口可以很方便的对OV5640的寄存器进行配置,该模块和“EEPROM读写实验”章节中用到的I2C驱动模块为同一个模块,有关该模块的详细介绍请大家参考“EEPROM读写实验”章节。 I2C配置模块(i2c_ov5640_rgb565_cfg):I2C配置模块的驱动时钟是由I2C驱动模块输出的时钟提供的,这样方便了I2C驱动模块和I2C配置模块之间的数据交互。该模块寄存需要配置的寄存器地址、数据以及控制初始化的开始与结束,同时该模块输出OV5640的寄存器地址和数据以及控制I2C驱动模块开始执行的控制信号,直接连接到I2C驱动模块的用户接口,从而完成对OV5640传感器的初始化。 摄像头图像采集模块(cmos_capture_data):摄像头采集模块在像素时钟的驱动下将传感器输出的场同步信号、行同步信号以及8位数据转换成16位数据信号,完成对OV5640传感器图像的采集。 开始传输控制模块(start_transfer_ctrl):该模块解析以太网顶层模块接收到的数据,如果收到1个字节的ASCII码“1”,则表示以太网开始传输图像数据;如果收到1个字节的ASCII码“0”,则表示以太网停止传输图像数据。 图像数据封装模块(img_data_pkt):图像数据封装模块负责将输入16位的图像数据,拼接成32位数据,以及添加图像数据的帧头和行场分辨率。该模块控制着以太网发送模块发送的字节数,单次发送一行图像数据的字节数,模块内部例化了一个异步FIFO模块,用于缓存待发送的图像数据。 以太网顶层模块(eth_top):以太网顶层模块实现以太网通信的收发功能,有关该模块的详细介绍请大家参考“以太网UDP测试实验”章节。 顶层模块部分代码如下: - 1 module ov5640_udp_pc(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n , //系统复位信号,低电平有效
- 4 //以太网接口
- 5 input eth_rxc , //RGMII接收数据时钟
- 6 input eth_rx_ctl , //RGMII输入数据有效信号
- 7 input [3:0 eth_rxd , //RGMII输入数据
- 8 output eth_txc , //RGMII发送数据时钟
- 9 output eth_tx_ctl , //RGMII输出数据有效信号
- 10 output [3:0 eth_txd , //RGMII输出数据
- 11 output eth_rst_n , //以太网芯片复位信号,低电平有效
- 12
- 13 //摄像头接口
- 14 input cam_pclk , //cmos 数据像素时钟
- 15 input cam_vsync , //cmos 场同步信号
- 16 input cam_href , //cmos 行同步信号
- 17 input [7:0 cam_data , //cmos 数据
- 18 output cam_rst_n , //cmos 复位信号,低电平有效
- 19 output cam_pwdn , //电源休眠模式选择 0:正常模式 1:电源休眠模式
- 20 output cam_scl , //cmos SCCB_SCL线
- 21 inout cam_sda //cmos SCCB_SDA线
- 22 );
- 23
- 24 //parameter define
- 25 //开发板MAC地址00-11-22-33-44-55
- 26 parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- 27 //开发板IP地址192.168.1.10
- 28 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
- 29 //目的MAC地址ff_ff_ff_ff_ff_ff
- 30 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
- 31 //目的IP地址192.168.1.102
- 32 parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
- 33
- 34 parameter H_CMOS_DISP = 11'd640; //CMOS分辨率--行
- 35 parameter V_CMOS_DISP = 11'd480; //CMOS分辨率--列
- 36 parameter TOTAL_H_PIXEL = H_CMOS_DISP + 12'd1216; //水平总像素大小
- 37 parameter TOTAL_V_PIXEL = V_CMOS_DISP + 12'd504; //垂直总像素大小
- 38
- 39 parameter SLAVE_ADDR = 7'h3c ; //OV5640的器件地址7'h3c
- 40 parameter BIT_CTRL =1'b1 ; //OV5640的字节地址为16位 0:8位 1:16位
- 41 parameter CLK_FREQ =27'd50_000_000 ; //i2c_dri模块的驱动时钟频率
- 42 parameter I2C_FREQ =20'd250_000 ; //I2C的SCL时钟频率,不超过400KHz
复制代码在代码的第24至32行定义了四个参量:开发板MAC地址BOARD_MAC,开发板IP地址 BOARD_IP,目的MAC地址DES_MAC(这里指PC MAC地址),目的IP地址 DES_IP(PC IP地址)。开发板的MAC地址和IP地址是我们随意定义的,只要不和目的MAC 地址和目的IP地址一样就可以,否则会产生地址冲突。目的MAC地址这里写的是公共MAC 地址(48'hff_ff_ff_ff_ff_ff),也可以修改成电脑网口的MAC地址,DES_IP是对应电脑以太网的IP地址,这里定义的四个参数是向下传递的,需要修改MAC地址或者IP地址时直接在这里修改即可,而不用在以太网顶层模块里面修改。 在代码的第34行至37行定义了CMOS水平/垂直方向像素个数和水平/垂直总像素个数,本次实验设置的摄像头分辨率为640*480。 在代码的第39行定义了OV5640的器件地址,其器件地址为7’h3c;第40行定义了寄存器地址的位宽,BIT_CTRL=0表示地址位宽为8位,BIT_CTRL=1表示地址位宽为16位。因为OV5640的地址位宽为16位,所以BIT_CTRL设置为1。 - 76 assign rst_n = sys_rst_n & locked;
- 77 //电源休眠模式选择 0:正常模式 1:电源休眠模式
- 78 assign cam_pwdn = 1'b0;
- 79 assign cam_rst_n = 1'b1;
- 80
- 81 //例化时钟IP核
- 82 clk_wiz_0 u_clk_wiz_0
- 83 (
- 84 .clk_out1 (clk_50m),
- 85 .clk_out2 (clk_200m),
- 86 .reset (~sys_rst_n),
- 87 .locked (locked),
- 88 .clk_in1 (sys_clk)
- 89 );
- 90
- 91 //I2C配置模块
- 92 i2c_ov5640_rgb565_cfg u_i2c_cfg(
- 93 .clk (i2c_dri_clk),
- 94 .rst_n (rst_n),
- 95 .i2c_done (i2c_done),
- 96 .i2c_data_r (i2c_data_r),
- 97 .cmos_h_pixel (H_CMOS_DISP),
- 98 .cmos_v_pixel (V_CMOS_DISP),
- 99 .total_h_pixel (TOTAL_H_PIXEL),
- 100 .total_v_pixel (TOTAL_V_PIXEL),
- 101 .i2c_exec (i2c_exec),
- 102 .i2c_data (i2c_data),
- 103 .i2c_rh_wl (i2c_rh_wl),
- 104 .init_done (cam_init_done)
- 105 );
- 106
- 107 //I2C驱动模块
- 108 i2c_dri
- 109 #(
- 110 .SLAVE_ADDR (SLAVE_ADDR), //参数传递
- 111 .CLK_FREQ (CLK_FREQ ),
- 112 .I2C_FREQ (I2C_FREQ )
- 113 )
- 114 u_i2c_dri(
- 115 .clk (clk_50m ),
- 116 .rst_n (rst_n ),
- 117 //i2cinterface
- 118 .i2c_exec (i2c_exec ),
- 119 .bit_ctrl (BIT_CTRL ),
- 120 .i2c_rh_wl (i2c_rh_wl ),
- 121 .i2c_addr (i2c_data[23:8]),
- 122 .i2c_data_w (i2c_data[7:0]),
- 123 .i2c_data_r (i2c_data_r),
- 124 .i2c_done (i2c_done ),
- 125 .i2c_ack (),
- 126 .scl (cam_scl ),
- 127 .sda (cam_sda ),
- 128 //userinterface
- 129 .dri_clk (i2c_dri_clk) //I2C操作时钟
- 130 );
- 131
- 132 //摄像头数据采集模块
- 133 cmos_capture_datau_cmos_capture_data(
- 134
- 135 .rst_n (rst_n & cam_init_done),
- 136 .cam_pclk (cam_pclk),
- 137 .cam_vsync (cam_vsync),
- 138 .cam_href (cam_href),
- 139 .cam_data (cam_data),
- 140 .cmos_frame_vsync (cmos_frame_vsync),
- 141 .cmos_frame_href (),
- 142 .cmos_frame_valid (img_data_en),
- 143 .cmos_frame_data (img_data)
- 144 );
复制代码OV5640摄像头配置模块和IIC驱动模块实现对OV5640摄像头的初始化,在初始化完成后拉高cam_init_done信号,此时开始通过摄像头数据采集模块接收摄像头输出的图像数据(如程序中第135行代码所示),将输入的8位数据转换成16位RGB565数据。 - 146 //开始传输控制模块
- 147 start_transfer_ctrlu_start_transfer_ctrl(
- 148 .clk (eth_rx_clk),
- 149 .rst_n (rst_n),
- 150 .udp_rec_pkt_done (udp_rec_pkt_done),
- 151 .udp_rec_en (udp_rec_en ),
- 152 .udp_rec_data (udp_rec_data ),
- 153 .udp_rec_byte_num (udp_rec_byte_num),
- 154
- 155 .transfer_flag (transfer_flag) //图像开始传输标志,1:开始传输 0:停止传输
- 156 );
- 157
- 158 //图像封装模块
- 159 img_data_pkt u_img_data_pkt(
- 160 .rst_n (rst_n),
- 161
- 162 .cam_pclk (cam_pclk),
- 163 .img_vsync (cmos_frame_vsync),
- 164 .img_data_en (img_data_en),
- 165 .img_data (img_data),
- 166 .transfer_flag (transfer_flag),
- 167 .eth_tx_clk (eth_tx_clk ),
- 168 .udp_tx_req (udp_tx_req ),
- 169 .udp_tx_done (udp_tx_done ),
- 170 .udp_tx_start_en (udp_tx_start_en),
- 171 .udp_tx_data (udp_tx_data ),
- 172 .udp_tx_byte_num (udp_tx_byte_num)
- 173 );
- 174
- 175 //以太网顶层模块
- 176 eth_top #(
- 177 .BOARD_MAC (BOARD_MAC), //参数例化
- 178 .BOARD_IP (BOARD_IP ),
- 179 .DES_MAC (DES_MAC ),
- 180 .DES_IP (DES_IP )
- 181 )
- 182 u_eth_top(
- 183 .sys_rst_n (rst_n ), //系统复位信号,低电平有效
- 184 .clk_200m (clk_200m),
- 185 //以太网RGMII接口
- 186 .eth_rxc (eth_rxc ), //RGMII接收数据时钟
- 187 .eth_rx_ctl (eth_rx_ctl), //RGMII输入数据有效信号
- 188 .eth_rxd (eth_rxd ), //RGMII输入数据
- 189 .eth_txc (eth_txc ), //RGMII发送数据时钟
- 190 .eth_tx_ctl (eth_tx_ctl), //RGMII输出数据有效信号
- 191 .eth_txd (eth_txd ), //RGMII输出数据
- 192 .eth_rst_n (eth_rst_n ), //以太网芯片复位信号,低电平有效
- 193
- 194 .gmii_rx_clk (eth_rx_clk),
- 195 .gmii_tx_clk (eth_tx_clk),
- 196 .udp_tx_start_en (udp_tx_start_en),
- 197 .tx_data (udp_tx_data),
- 198 .tx_byte_num (udp_tx_byte_num),
- 199 .udp_tx_done (udp_tx_done),
- 200 .tx_req (udp_tx_req ),
- 201 .rec_pkt_done (udp_rec_pkt_done),
- 202 .rec_en (udp_rec_en ),
- 203 .rec_data (udp_rec_data ),
- 204 .rec_byte_num (udp_rec_byte_num)
- 205 );
- 206
- 207 endmodule
复制代码在代码的第147行至156行例化了开始传输控制模块,由该模块端口可知,输入端口信号为以太网接收到的数据,而输出信号为图像开始传输标志(transfer_flag)。transfer_flag信号用于控制以太网发送图像数据的开始和停止,连接至图像数据封装模块。 在代码的第159行至173行例化了图像数据封装模块,该模块输入的端口信号为摄像头图像数据,而输出的udp_tx_start_en(以太网开始发送信号)和udp_tx_byte_num(发送的字节数)连接至以太网顶层模块的以太网发送控制端口,从而控制以太网的UDP发送模块开始发送图像数据。 开始传输控制模块和图像数据封装模块在“基于OV7725的以太网视频传输实验”章节中程序设计部分有着详细的介绍,如果大家对此模块不是很了解的话,请参考“基于OV7725的以太网视频传输实验”章节。 OV5640和OV7725的寄存器配置差异较大,首先是OV5640是用16位数据来表示寄存器地址,而OV7725用8位数据表示寄存器地址。其次是OV5640集成更强大的图像处理功能,为了使OV5640输出比较清晰的图像,需要配置的寄存器更多。OV5640的寄存器配置模块部分代码如下: - 1 module i2c_ov5640_rgb565_cfg
- 2 (
- 3 input clk , //时钟信号
- 4 input rst_n , //复位信号,低电平有效
- 5
- 6 input [7:0 i2c_data_r, //I2C读出的数据
- 7 input i2c_done , //I2C寄存器配置完成信号
- 8 input [12:0 cmos_h_pixel ,
- 9 input [12:0 cmos_v_pixel ,
- 10 input [12:0 total_h_pixel, //水平总像素大小
- 11 input [12:0 total_v_pixel, //垂直总像素大小
- 12 output reg i2c_exec , //I2C触发执行信号
- 13 output reg [23:0 i2c_data , //I2C要配置的地址与数据(高16位地址,低8位数据)
- 14 output reg i2c_rh_wl, //I2C读写控制信号
- 15 output reg init_done //初始化完成信号
- 16 );
- 17
- 18 //parameter define
- 19 localparam REG_NUM = 8'd250 ; //总共需要配置的寄存器个数
- 20
- 21 //reg define
- 22 reg [14:0 start_init_cnt; //等待延时计数器
- 23 reg [7:0 init_reg_cnt ; //寄存器配置个数计数器
- 24
- 25 //*****************************************************
- 26 //** main code
- 27 //*****************************************************
- 28
- 29 //SCL配置成250KHz,输入的clk时钟频率为1Mhz,周期为1us 20000*1us = 20ms
- 30 //OV5640上电到开始配置SCCB至少等待20ms
- 31 always @(posedge clk or negedge rst_n) begin
- 32 if(!rst_n)
- 33 start_init_cnt <= 1'b0;
- 34 else if(start_init_cnt < 15'd20000) begin
- 35 start_init_cnt <= start_init_cnt + 1'b1;
- 36 end
- 37 end
- 38
- 39 //寄存器配置个数计数
- 40 always @(posedge clk or negedge rst_n) begin
- 41 if(!rst_n)
- 42 init_reg_cnt <= 8'd0;
- 43 else if(i2c_exec)
- 44 init_reg_cnt <= init_reg_cnt + 8'b1;
- 45 end
- 46
- 47 //i2c触发执行信号
- 48 always @(posedge clk or negedge rst_n) begin
- 49 if(!rst_n)
- 50 i2c_exec <= 1'b0;
- 51 else if(start_init_cnt == 15'd20000 - 1'b1)
- 52 i2c_exec <= 1'b1;
- 53 else if(i2c_done && (init_reg_cnt < REG_NUM))
- 54 i2c_exec <= 1'b1;
- 55 else
- 56 i2c_exec <= 1'b0;
- 57 end
- 58
- 59 //配置I2C读写控制信号
- 60 always @(posedge clk or negedge rst_n) begin
- 61 if(!rst_n)
- 62 i2c_rh_wl <= 1'b1;
- 63 else if(init_reg_cnt == 8'd2)
- 64 i2c_rh_wl <= 1'b0;
- 65 end
- 66
- 67 //初始化完成信号
- 68 always @(posedge clk or negedge rst_n) begin
- 69 if(!rst_n)
- 70 init_done <= 1'b0;
- 71 else if((init_reg_cnt == REG_NUM) && i2c_done)
- 72 init_done <= 1'b1;
- 73 end
- 74
- 75 //配置寄存器地址与数据
- 76 always @(posedge clk or negedge rst_n) begin
- 77 if(!rst_n)
- 78 i2c_data <= 24'b0;
- 79 else begin
- 80 case(init_reg_cnt)
- 81 //先读OV5640ID
- 82 8'd0 : i2c_data <= {16'h300a,8'h0}; //
- 83 8'd1 : i2c_data <= {16'h300b,8'h0}; //
- 84 8'd2 : i2c_data <= {16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
- 85 8'd3 : i2c_data <= {16'h3008,8'h02}; //正常工作模式
- 配置代码较长,省略部分源代码……
- 312 //设置输出像素个数
- 313 //DVP 输出水平像素点数高4位
- 314 8'd220: i2c_data <= {16'h3808,{4'd0,cmos_h_pixel[11:8]}};
- 315 //DVP 输出水平像素点数低8位
- 316 8'd221: i2c_data <= {16'h3809,cmos_h_pixel[7:0]};
- 317 //DVP 输出垂直像素点数高3位
- 318 8'd222: i2c_data <= {16'h380a,{5'd0,cmos_v_pixel[10:8]}};
- 319 //DVP 输出垂直像素点数低8位
- 320 8'd223: i2c_data <= {16'h380b,cmos_v_pixel[7:0]};
- 321 //水平总像素大小高5位
- 322 8'd224: i2c_data <= {16'h380c,{3'd0,total_h_pixel[12:8]}};
- 323 //水平总像素大小低8位
- 324 8'd225: i2c_data <= {16'h380d,total_h_pixel[7:0]};
- 325 //垂直总像素大小高5位
- 326 8'd226: i2c_data <= {16'h380e,{3'd0,total_v_pixel[12:8]}};
- 327 //垂直总像素大小低8位
- 328 8'd227: i2c_data <= {16'h380f,total_v_pixel[7:0]};
- 配置代码较长,省略部分源代码……
- 346 //彩条测试使能
- 347 8'd245: i2c_data <= {16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
- 348 //测试闪光灯功能
- 349 8'd246: i2c_data <= {16'h3016,8'h02};
- 350 8'd247: i2c_data <= {16'h301c,8'h02};
- 351 8'd248: i2c_data <= {16'h3019,8'h02}; //打开闪光灯
- 352 8'd249: i2c_data <= {16'h3019,8'h00}; //关闭闪光灯
- 353 //只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
- 354 default : i2c_data <= {16'h300a,8'h00}; //器件ID高8位
- 355 endcase
- 356 end
- 357 end
- 358
- 359 endmodule
复制代码I2C配置模块寄存需要配置的寄存器地址、数据以及控制初始化的开始与结束。需要注意的是,由OV5640的数据手册可知,图像传感器上电后到开始配置寄存器需要延时20ms,所以程序中定义了一个延时计数器(start_init_cnt),用于延时20ms。当计数器计数到预设值之后,开始第一次配置传感器即软件复位,目的是让所有的寄存器复位到默认的状态。在代码的第19行定义了总共需要配置的寄存器的个数,如果增加或者删减了寄存器的配置,需要修改此参数。 在程序的第314行至第328行,是对摄像头需要输出的行场分辨率和行场总像素进行设置的寄存器配置。
1.5 下载验证编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,将网线一端连接开发板的网口,另一端连接电脑的网口,然后将OV5640摄像头连接开发板上的摄像头接口,注意镜头方向朝外,最后连接电源线,如下图所示。 接下来我们打开电源开关,并下载程序。程序下载完成后,此时开发板上还看不到现象,我们接下来打开正点原子UDP传输视频显示的上位机,位于资料盘(A盘)/6_软件资料/1_软件/video_transfer文件夹下,如下图所示: 双击video_transfer.exe即可打开软件,如果软件无法打开,先双击安装上图中的vc_redist.x64.exe,安装完成后再打开video_transfer.exe。 打开后的界面如下图所示。 按照上图的界面进行设置,这些设置和程序中定义的参数和代码是对应的。设置完成后,点击“打开”按钮,此时上位机会通过网口向开发板发送ASCII码“1”,开发板收到开始命令后,会开始传输图像数据,如下图所示: 由上图可知,开发板传输的图像分辨率是640*480,帧率约为26fps,如果需要停止显示画面的话,只需要点击上方的“关闭”按钮即可。需要注意的是,上位机显示的画面受电脑性能的影响,如果电脑性能较差的话,上位机可能会出现卡顿和崩溃的现象。 |