正点原子 发表于 2023-3-16 12:14:41

《ATK-DFPGL22G之FPGA开发指南_V1.0》第五十五章

本帖最后由 正点原子 于 2023-3-16 12:14 编辑

1)实验平台:正点原子紫光PGL22G开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340253-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:435699340





第五十五章 基于OV5640的以太网视频传输实验
OV5640同OV7725一样,都是OmniVision(豪威科技)公司生产的CMOS图像传感器。不同的是,OV5640支持更高的分辨率、采集速率,具有更高的图像处理性能,主要应用在手机、数码相机、电脑多媒体等领域。
在“基于OV7725的以太网视频传输实验”中,我们成功地在上位机显示软件上实时显示出了摄像头采集的图像。本章我们将使用ATK-DFPGL22G开发板实现对OV5640的数字图像采集,并通过开发板上的以太网接口发送给上位机实时显示。
本章分为以下几个章节:55.1简介
55.2实验任务
55.3硬件设计
55.4程序设计
55.5下载验证


55.1简介
在“OV5640摄像头RGB_LCD显示实验”中对OV5640的视频传输时序、SCCB协议以及寄存器的配置信息等内容作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“OV5640摄像头RGB_LCD显示实验”中的OV5640简介部分。
55.2实验任务
本节实验任务是使用ATK-DFPGL22G开发板及OV5640摄像头实现图像采集,并通过开发板上的以太网接口发送给上位机实时显示。
55.3硬件设计
本次实验的硬件电路和“基于OV7725的以太网视频传输实验”中的硬件电路是基本相同的,都使用了ATK-DFPGL22G开发板上的摄像头扩展接口,IO管脚位置的配置也是一样的。
唯一的不同点在于,原先在“基于OV7725的以太网视频传输实验”中的ATK-DFPGL22G输出给摄像头扩展接口的“cam_sgm_ctrl”信号,在本实验中变为了ATK-DFPGL22G输出给OV5640的“电源休眠模式选择”信号cam_pwdn;且原来的cam_sgm_ctrl是我们需要一直赋值为高电平,但对于OV5640,cam_pwdn我们需要将其一直赋值为低电平,表示不休眠即正常工作模式。
由于以太网与摄像头引脚数目较多,且在前面相应的章节中已经给出它们的管脚列表,这里就不重复列出了。
55.4程序设计
图 55.4.1是根据本章实验任务画出的系统框图。对比“基于OV7725的以太网视频传输实验”的系统框图可以发现,我们只是把外设OV7725模块替换成了OV5640模块,其余模块基本相同(IIC配置模块有差异)。时钟IP核模块用于为IIC驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟。I2C驱动模块和I2C配置模块用于初始化OV5640图像传感器;摄像头采集模块负责采集摄像头图像数据,并且把图像数据连接至图像数据封装模块,图像数据封装模块将输入的图像数据进行位拼接,并添加图像的帧头和行场分辨率;以太网顶层模块实现以太网数据的收发;开始传输控制模块控制以太网顶层模块开始/停止发送数据。
图 55.4.1 系统框图
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       eth_rxd   ,   //RGMII输入数据
8       output             eth_txc   ,   //RGMII发送数据时钟   
9       output             eth_tx_ctl,   //RGMII输出数据有效信号
10      output      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         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
26parameterBOARD_MAC = 48'h00_11_22_33_44_55;   
27//开发板IP地址 192.168.1.10
28parameterBOARD_IP= {8'd192,8'd168,8'd1,8'd10};
29//目的MAC地址 ff_ff_ff_ff_ff_ff
30parameterDES_MAC   = 48'hff_ff_ff_ff_ff_ff;   
31//目的IP地址 192.168.1.102   
32parameterDES_IP    = {8'd192,8'd168,8'd1,8'd102};
33
34parameterH_CMOS_DISP = 11'd640;                  //CMOS分辨率--行
35parameterV_CMOS_DISP = 11'd480;                  //CMOS分辨率--列
36parameterTOTAL_H_PIXEL = H_CMOS_DISP + 12'd1216; //水平总像素大小
37parameterTOTAL_V_PIXEL = V_CMOS_DISP + 12'd504;//垂直总像素大小
38
39parameter SLAVE_ADDR = 7'h3c          ; //OV5640的器件地址7'h3c
40parameter BIT_CTRL   = 1'b1         ; //OV5640的字节地址为16位0:8位 1:16位
41parameter CLK_FREQ   = 27'd50_000_000 ; //i2c_dri模块的驱动时钟频率
42parameter 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。
76assignrst_n = sys_rst_n & locked;
77//电源休眠模式选择 0:正常模式 1:电源休眠模式
78assigncam_pwdn= 1'b0;
79assigncam_rst_n = 1'b1;
80
81//例化时钟IP核
82clk_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配置模块   
92i2c_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   //i2c interface
118   .i2c_exec    (i2c_exec),   
119   .bit_ctrl    (BIT_CTRL),   
120   .i2c_rh_wl   (i2c_rh_wl ),   
121   .i2c_addr    (i2c_data),   
122   .i2c_data_w(i2c_data),   
123   .i2c_data_r(i2c_data_r),   
124   .i2c_done    (i2c_done),
125   .i2c_ack   (),
126   .scl         (cam_scl   ),   
127   .sda         (cam_sda   ),   
128   //user interface
129   .dri_clk   (i2c_dri_clk)               //I2C操作时钟
130 );
131
132 //摄像头数据采集模块
133 cmos_capture_data u_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_ctrl u_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         i2c_data_r,    //I2C读出的数据
7       input                i2c_done ,   //I2C寄存器配置完成信号
8       input      cmos_h_pixel ,
9       input      cmos_v_pixel ,
10      input      total_h_pixel, //水平总像素大小
11      input      total_v_pixel, //垂直总像素大小
12      outputreg          i2c_exec ,   //I2C触发执行信号   
13      outputregi2c_data ,   //I2C要配置的地址与数据(高16位地址,低8位数据)
14      outputreg          i2c_rh_wl,   //I2C读写控制信号
15      outputreg          init_done      //初始化完成信号
16      );
17
18//parameter define
19localparamREG_NUM = 8'd250;       //总共需要配置的寄存器个数
20
21//reg define
22reg      start_init_cnt;      //等待延时计数器
23reg       init_reg_cnt;      //寄存器配置个数计数器
24
25//*****************************************************
26//**                  main code
27//*****************************************************
28
29//SCL配置成250KHz,输入的clk时钟频率为1Mhz,周期为1us 20000*1us = 20ms
30//OV5640上电到开始配置SCCB至少等待20ms
31always @(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
37end
38
39//寄存器配置个数计数   
40always @(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;
45end
46
47//i2c触发执行信号   
48always @(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;
57end
58
59//配置I2C读写控制信号
60always @(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;
65end
66
67//初始化完成信号
68always @(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;
73end
74
75//配置寄存器地址与数据
76always @(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            //先读OV5640 ID
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:复位 Bit:电源休眠
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}};
315             //DVP 输出水平像素点数低8位
316             8'd221: i2c_data <= {16'h3809,cmos_h_pixel};
317             //DVP 输出垂直像素点数高3位
318             8'd222: i2c_data <= {16'h380a,{5'd0,cmos_v_pixel}};
319             //DVP 输出垂直像素点数低8位
320             8'd223: i2c_data <= {16'h380b,cmos_v_pixel};
321             //水平总像素大小高5位
322             8'd224: i2c_data <= {16'h380c,{3'd0,total_h_pixel}};
323             //水平总像素大小低8位
324             8'd225: i2c_data <= {16'h380d,total_h_pixel};
325             //垂直总像素大小高5位
326             8'd226: i2c_data <= {16'h380e,{3'd0,total_v_pixel}};
327             //垂直总像素大小低8位   
328             8'd227: i2c_data <= {16'h380f,total_v_pixel};
配置代码较长,省略部分源代码……
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行,是对摄像头需要输出的行场分辨率和行场总像素进行设置的寄存器配置。
55.5下载验证
编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,将网线一端连接开发板的网口,另一端连接电脑的网口,然后将OV5640摄像头连接开发板上的摄像头接口,注意镜头方向朝外,最后连接电源线,如下图所示。
图 55.5.1 开发板硬件连接示意图
接下来我们打开电源开关,并下载程序。程序下载完成后,此时开发板上还看不到现象,我们接下来打开正点原子UDP传输视频显示的上位机,位于资料盘(A盘)/6_软件资料/1_软件/video_transfer文件夹下,如下图所示:
图 55.5.2 打开上位机软件
双击video_transfer.exe即可打开软件,如果软件无法打开,先双击安装上图中的vc_redist.x64.exe,安装完成后再打开video_transfer.exe。
打开后的界面如下图所示。
图 55.5.3 上位机设置界面
按照上图的界面进行设置,这些设置和程序中定义的参数和代码是对应的。设置完成后,点击“打开”按钮,此时上位机会通过网口向开发板发送ASCII码“1”,开发板收到开始命令后,会开始传输图像数据,如下图所示:
图 55.5.4 上位机实时显示画面
由上图可知,开发板传输的图像分辨率是640*480,帧率约为26fps,如果需要停止显示画面的话,只需要点击上方的“关闭”按钮即可。需要注意的是,上位机显示的画面受电脑性能的影响,如果电脑性能较差的话,上位机可能会出现卡顿和崩溃的现象。

页: [1]
查看完整版本: 《ATK-DFPGL22G之FPGA开发指南_V1.0》第五十五章