【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

1)实验平台:正点原子领航者ZYNQ开发板2)平台购买地址:https://item.taobao.com/item.htm?&id=6061601087613)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html4)对正点原子FPGA感兴趣的同学可以加群讨论:9942440165)关注正点原子公众号,获取最新资料更新第三十六章 基于OV5640的PL以太网视频传输实验OV5640同OV7725一样,都是OmniV

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:994244016
5)关注正点原子公众号,获取最新资料更新
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

第三十六章 基于OV5640的PL以太网视频传输实验

OV5640同OV7725一样,都是OmniVision(豪威科技)公司生产的CMOS图像传感器。不同的是,OV5640支持更高的分辨率、采集速率,具有更高的图像处理性能,主要应用在手机、数码相机、电脑多媒体等领域。
在“基于OV7725的PL以太网视频传输实验”中,我们成功地在上位机显示软件上实时显示出了摄像头采集的图像。本章我们将使用ZYNQ开发板实现对OV5640的数字图像采集,并通过开发板上的PL以太网接口发送给上位机实时显示。
本章分为以下几个章节:
36.1 简介
36.2 实验任务
36.3 硬件设计
36.4 程序设计
36.5 下载验证
36.1 简介
OV5640是一款1/4英寸单芯片图像传感器,其感光阵列达到25921944(即500W像素),能实现最快15fps QSXVGA(25921944)或者90fps VGA(640*480)分辨率的图像采集。传感器采用OmniVision推出的OmniBSI(背面照度)技术,使传感器达到更高的性能,如高灵敏度、低串扰和低噪声。传感器内部集成了图像处理的功能,包括自动曝光控制(AEC)、自动白平衡(AWB)等。同时该传感器支持LED补光、MIPI(移动产业处理器接口)输出接口和DVP(数字视频并行)输出接口选择、ISP(图像信号处理)以及AFC(自动聚焦控制)等功能。
OV5640的功能框图如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.1 OV5640功能框图
由上图可知,时序发生器(timing generator)控制着感光阵列(image array)、放大器(AMP)、AD转换以及输出外部时序信号(VSYNC、HREF和PCLK),外部时钟XVCLK经过PLL锁相环后输出的时钟作为系统的控制时钟;感光阵列将光信号转化成模拟信号,经过增益放大器之后进入10位AD转换器;AD转换器将模拟信号转化成数字信号,并且经过ISP进行相关图像处理,最终输出所配置格式的10位视频数据流。增益放大器控制以及ISP等都可以通过寄存器(registers)来配置,配置寄存器的接口就是SCCB接口,该接口协议兼容IIC协议。
OV5640使用的是两线式SCCB接口总线,有关SCCB总线的详细介绍可以参考“基于OV7725的PL以太网视频传输实验”中OV7725简介部分。虽然OV5640和OV7725都是采用SCCB接口总线来配置寄存器,但不同的是,OV7725是用8位(1个字节)来表示寄存器地址,而OV5640是用16位(两个字节)表示寄存器地址。
OV5640 SCCB的写传输协议如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.2 OV5640 SCCB写传输协议
上图中的ID ADDRESS是由7位器件地址和1位读写控制位构成(0:写 1:读),OV5640的器件地址为7’h3c,所以在写传输协议中,ID Address(W)= 8’h78(器件地址左移1位,低位补0);Sub-address(H)为高8位寄存器地址,Sub-address(L)为低8位寄存器地址,在OV5640众多寄存器中,有些寄存器是可改写的,有些是只读的,只有可改写的寄存器才能正确写入;Write Data为8位写数据,每一个寄存器地址对应8位的配置数据。
在OV5640正常工作之前,必须先对传感器进行初始化,即通过配置寄存器使其工作在预期的工作模式,以及得到较好画质的图像。因为SCCB的写传输协议和IIC几乎相同,因此我们可以直接使用IIC的驱动程序来配置摄像头。当然这么多寄存器也并非都需要配置,很多寄存器可以采用默认的值。OV公司提供了OV5640的软件应用手册(OV5640 Software Application Note,位于开发板所随附的资料“7_硬件资料/4_OV5640资料/OV5640_camera_module_software_application_notes.pdf”),如果某些寄存器不知道如何配置可以参考此手册,下表是本程序用到的关键寄存器的配置说明。
表 36.1.1 OV5640关键寄存器配置说明
地址
(HEX) 寄存器 默认值
(HEX) 详细说明
0x3008 SYSTEM CTROL0 0x02 Bit[7]:软件复位
Bit[6]:软件电源休眠
0x3016 PAD OUTPUT ENABLE 00 0x00 Bit[1]:闪光灯输出使能
0x3017 PAD OUTPUT ENABLE 01 0x00 输入/输出控制(0:输入 1:输出)
Bit[7]:FREX输出使能
Bit[6]:VSYNC输出使能
Bit[5]:HREF输出使能
Bit[4]:PCLK输出使能
Bit[3:0]:D[9:6]输出使能
0x3018 PAD OUTPUT ENABLE 02 0x00 输入/输出控制(0:输入 1:输出)
Bit[7:2]:D[5:0]输出使能
Bit[1]:GPIO1输出使能
Bit[0]:GPIO0输出使能
0x3019 PAD OUTPUT VALUE 00 0x00 Bit[1]: 闪光灯操作
0:关闭闪光灯
1:打开闪光灯
0x301C PAD SELECT 00 0x00 Bit[1]:闪光灯IO选择
0x3035 SC PLL CONTRL1 0x11 Bit[7:4]:系统时钟分频,用于降低所有的时钟频率
Bit[3:0]:MIPI分频
0x3036 SC PLL CONTRL2 0x69 Bit[7:0]:PLL倍频器(4~252)
在4~127范围内支持任意整数分频
在128~252范围内仅支持偶数分频
0x3808 TIMING DVPHO 0x0A Bit[3:0]:DVP 输出水平像素点数高4位
0x3809 TIMING DVPHO 0x20 Bit[7:0]:DVP 输出水平像素点数低8位
0x380A TIMING DVPVO 0x07 Bit[2:0]:DVP输出垂直像素点数高3位
0x380B TIMING DVPVO 0x98 Bit[7:0]:输出垂直像素点数低8位
0x4300 FORMAT CONTROL 0xF8 Bit[7:4]:数据输出格式
0:RAW
1:Y8
2:YUV444/RGB888
3:YUV422
4:YUV420
5:YUV420(仅在MIPI输出接口有效)
6:RGB565
Bit[3:0]:输出顺序
0:{b[4:0],g[5:3]},{g[2:0],r[4:0]}
1:{r[4:0],g[5:3]},{g[2:0],b[4:0]}
2:{g[4:0],r[5:3]},{r[2:0],b[4:0]}
3:{b[4:0],r[5:3]},{r[2:0],g[4:0]}
4:{g[4:0],b[5:3]},{b[2:0],r[4:0]}
5:{r[4:0],b[5:3]},{b[2:0],g[4:0]}
6~14:不允许
15:{g[2:0],b[4:0]},{r[4:0],g[5:3]}
7:RGB555格式1
8:RGB555格式2
9:RGB444格式1
10:RGB444格式2
11~14:不允许
15:Bypass formatter module
OV5640的寄存器较多,对于其它寄存器的描述可以参考OV5640的数据手册。需要注意的是,OV5640的数据手册并没有提供全部的寄存器描述,而大多数必要的寄存器配置在ov5640的软件应用手册中可以找到,可以结合这两个手册学习如何对OV5640进行配置。
输出图像参数设置
接下来,我们介绍一下OV5640的ISP输入窗口设置、预缩放窗口设置和输出大小窗口设置,这几个设置与我们的正常使用密切相关,有必要了解一下,它们的设置关系如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.3 图像窗口设置
ISP输入窗口设置(ISP Input Size)允许用户设置整个传感器显示区域(physical pixel size,26321951,其中25921944像素是有效的),开窗范围从00~26321951都可以任意设置。也就是上图中的X_ADDR_ST(寄存器地址0x3800、0x3801)、Y_ADDR_ST(寄存器地址0x3802、0x3803)、X_ADDR_END(寄存器地址0x3804、0x3805)和Y_ADDR_END(寄存器地址0x3806、0x3807)寄存器。该窗口设置范围中的像素数据将进入ISP进行图像处理。
预缩放窗口设置(pre-scaling size)允许用户在ISP输入窗口的基础上进行裁剪,用于设置将进行缩放的窗口大小,该设置仅在ISP输入窗口内进行X/Y方向的偏移。可以通过X_OFFSET(寄存器地址0x3810、0x3811)和Y_OFFSET(寄存器地址0x3812、0x3813)进行配置。
输出大小窗口设置(data output size)是在预缩放窗口的基础上,经过内部DSP进行缩放处理,并将处理后的数据输出给外部的图像窗口,图像窗口控制着最终的图像输出尺寸。可以通过X_OUTPUT_SIZE(寄存器地址0x3808、0x3809)和Y_OUTPUT_SIZE(寄存器地址0x380A、0x380B)进行配置。注意:当输出大小窗口与预缩放窗口比例不一致时,图像将进行缩放处理(图像变形),仅当两者比例一致时,输出比例才是1:1(正常图像)。
图 7.5.13.3中,右侧data output size区域,才是OV5640输出给外部的图像尺寸,也就是显示在显示器或者液晶屏上面的图像大小。输出大小窗口与预缩放窗口比例不一致时,会进行缩放处理,在显示器上面看到的图像将会变形。
输出像素格式
OV5640支持多种不同的数据像素格式,包括YUV(亮度参量和色度参量分开表示的像素格式)、RGB(其中RGB格式包含RGB565、RGB555等)以及RAW(原始图像数据),通过寄存器地址0x4300配置成不同的数据像素格式。
由于数据像素格式常用RGB565,我们这里也将ov5640配置为RGB565格式。由上表(表 36.1.1)可知,将寄存器0x4300寄存器的Bit[7:4]设置成0x6即可。OV5640支持调节RGB565输出格式中各颜色变量的顺序,对于我们常见的应用来说,一般是使用RGB或BGR序列。我们在“基于OV7725的PL以太网视频传输实验”的章节中介绍过,OV7725摄像头按照RGB的顺序输出,本章我们将OV5640输出的RGB565的颜色顺序和OV7725保持一致,将寄存器0x4300寄存器的Bit[3:0]设置成0x1。因此,“基于OV7725的PL以太网视频传输实验”章节中的图像采集模块可以直接用来采集OV5640输出的图像。
彩条测试模式
图像传感器配置成彩条测试模式后,会输出彩色的条纹,方便测试图像传感器是否正常工作,通过配置寄存器0x503d的Bit[7]位打开和关闭彩条模式。当需要打开彩条模式时,寄存器0x503d配置成0x80,关闭时配置成0x00,下图为打开彩条模式后图像输出的条纹。
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.4 彩条模式下的图像条纹
LED闪光灯
当外界环境光较暗时,传感器采集图像会受到较大影响,此时可以通过打开LED补光灯来弥补光照不足所带来的影响,就像手机在夜晚拍照时也会打开闪光灯来提高图像质量。通过配置寄存器0x3016=0x02,0x301c=0x02来使能LED补光灯功能;配置寄存器0x3019=0x02打开闪光灯,0x3019=0x00关闭闪光灯。
图像输出时序
接下来,我们介绍一下OV5640的图像数据输出时序,首先我们简单介绍一些定义。
QSXGA,这里指:分辨率为25921944的输出格式,类似的还有:QXGA(20481536)、UXGA(16001200)、SXGA(12801024)、WXGA(1440900)、WXGA(1280800)、XGA(1024768)、SVGA(800600)、VGA(640480)、QVGA(320240)和QQVGA(160120)等。
PCLK:像素时钟,一个PCLK时钟输出一个像素或者半个像素(像素数据的高8位或者低8位)。
VSYNC:帧同步信号。
HREF/HSYNC:行同步信号。
D[9:0]:像素数据,在RGB565格式中,只有高8位是有效的。
tPclk:一个时钟周期 。
tp:一个像素点的周期,在RGB565和YUV422输出格式下,tp=2
tPclk;Raw输出格式下,tp=tPclk。
下图为OV5640输出图像数据的行时序图。
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.5 OV5640行时序图
从上图可以看出,传感器在HREF为高电平的时候输出图像数据,当HREF变高后,每一个 PCLK时钟,输出一个8位或者10位像素数据。比如我们采用QSXGA时序,RGB565格式输出,tp=2tPclk,每2个字节组成一个像素的颜色,这样每行总共输出25922个PCLK,也就是25922个字节。
再来看看帧时序(QSXGA模式,分辨率2592
1944),如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.6 OV5640 QSXGA帧时序
由上图可知,VSYNC的上升沿作为一帧的开始,高电平同步脉冲的时间为5688tp,紧接着等待48276tp时间后,HREF开始拉高,此时输出有效数据;HREF由2592tp个高电平和252tp个低电平构成;最后一行图像数据输出完成之后等待14544tp时间,一帧数据传输结束。所以输出一帧图像的时间实际上是tFrame = 5596992tp。
从OV5640的行时序图和帧时序图可以发现,其输出时序和OV7725是非常相似的,只是时间参数不同而已,大家可以参考“基于OV7725的PL以太网视频传输实验”中帧时序的介绍来学习OV5640的输出时序。
36.2 实验任务
本节实验任务是使用ZYNQ开发板及OV5640摄像头实现图像采集,并通过开发板上的以太网接口发送给上位机实时显示。
36.3 硬件设计
本次实验的硬件电路和“基于OV7725的PL以太网视频传输实验”中的硬件电路是基本相同的,都使用了领航者Zynq开发板上的摄像头扩展接口,IO管脚位置的配置也是一样的。
唯一的不同点在于,原先在“基于OV7725的PL以太网视频传输实验”中的Zynq输出给摄像头扩展接口的“cam_sgm_ctrl”信号,在本实验中变为了Zynq输出给OV5640的“电源休眠模式选择”信号cam_pwdn;且原来的cam_sgm_ctrl是我们需要一直赋值为高电平,但对于OV5640,cam_pwdn我们需要将其一直赋值为低电平,表示不休眠即正常工作模式。
OV5640摄像头的管脚分配如下表所示:
表 36.3.1 OV5640摄像头管脚分配
信号名 方向 管脚 端口说明
cam_pclk input W14 cmos 数据像素时钟
cam_vsync input U12 cmos 场同步信号
cam_href input T12 cmos 行同步信号
cam_rst_n output P14 cmos 复位信号,低电平有效
cam_pwdn output V15 cmos 电源休眠模式选择信号
cam_data[0] input R14 cmos 数据
cam_data[1] input U13 cmos 数据
cam_data[2] input V13 cmos 数据
cam_data[3] input U15 cmos 数据
cam_data[4] input U14 cmos 数据
cam_data[5] input W13 cmos 数据
cam_data[6] input V12 cmos 数据
cam_data[7] input Y14 cmos 数据
emio_sccb_tri_io [0] inout T10 cmos SCCB_SCL线
emio_sccb_tri_io [1] inout T11 cmos SCCB_SDA线
相关的管脚约束如下所示:

create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
create_clock -period 8.000 -name eth_rxc [get_ports eth_rxc]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

set_property -dict {PACKAGE_PIN G15 IOSTANDARD LVCMOS33} [get_ports eth_rst_n]
set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS33} [get_ports eth_rxc]
set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports eth_rx_ctl]
set_property -dict {PACKAGE_PIN B19 IOSTANDARD LVCMOS33} [get_ports {eth_rxd[0]}]
set_property -dict {PACKAGE_PIN A20 IOSTANDARD LVCMOS33} [get_ports {eth_rxd[1]}]
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports {eth_rxd[2]}]
set_property -dict {PACKAGE_PIN H16 IOSTANDARD LVCMOS33} [get_ports {eth_rxd[3]}]

set_property -dict {PACKAGE_PIN B20 IOSTANDARD LVCMOS33} [get_ports eth_txc]
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports eth_tx_ctl]
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports {eth_txd[0]}]
set_property -dict {PACKAGE_PIN C20 IOSTANDARD LVCMOS33} [get_ports {eth_txd[1]}]
set_property -dict {PACKAGE_PIN D19 IOSTANDARD LVCMOS33} [get_ports {eth_txd[2]}]
set_property -dict {PACKAGE_PIN D20 IOSTANDARD LVCMOS33} [get_ports {eth_txd[3]}]

#CAMERA
create_clock -period 40.000 -name cmos_pclk [get_ports cam_pclk]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_IBUF]
set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports cam_pclk]
set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports cam_rst_n]
set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports cam_sgm_ctrl]
set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[0]}]
set_property -dict {PACKAGE_PIN U13 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[1]}]
set_property -dict {PACKAGE_PIN V13 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[2]}]
set_property -dict {PACKAGE_PIN U15 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[3]}]
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[4]}]
set_property -dict {PACKAGE_PIN W13 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[5]}]
set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[6]}]
set_property -dict {PACKAGE_PIN Y14 IOSTANDARD LVCMOS33 IOB TRUE} [get_ports {cam_data[7]}]
set_property -dict {PACKAGE_PIN U12 IOSTANDARD LVCMOS33} [get_ports cam_vsync]
set_property -dict {PACKAGE_PIN T12 IOSTANDARD LVCMOS33} [get_ports cam_href]
set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports cam_scl]
set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports cam_sda]

36.4 程序设计
图 7.5.13.1是根据本章实验任务画出的系统框图。对比“基于OV7725的以太网视频传输实验”的系统框图可以发现,我们只是把外设OV7725模块替换成了OV5640模块,其余模块基本相同(IIC配置模块有差异)。时钟IP核模块用于为IIC驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟。I2C驱动模块和I2C配置模块用于初始化OV5640图像传感器;摄像头采集模块负责采集摄像头图像数据,并且把图像数据连接至图像数据封装模块,图像数据封装模块将输入的图像数据进行位拼接,并添加图像的帧头和行场分辨率;以太网顶层模块实现以太网数据的收发;开始传输控制模块控制以太网顶层模块开始/停止发送数据。
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.1 系统框图
顶层模块的原理图如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.2 顶层模块原理图
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核模块通过调用MMCM 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     //i2c interface
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     //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        [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              //先读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[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行,是对摄像头需要输出的行场分辨率和行场总像素进行设置的寄存器配置。
36.5 下载验证
将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接;将网线一端连接开发板的网口,另一端连接电脑的网口;将OV5640摄像头连接开发板上的摄像头接口,注意镜头方向朝外,如下图所示。
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.1 开发板硬件连接示意图
接下来我们打开电源开关,并下载程序。程序下载完成后,此时开发板上还看不到现象,我们接下来打开正点原子UDP传输视频显示的上位机,位于资料盘(A盘)/6_软件资料/1_软件/video_transfer文件夹下,如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.2 打开上位机软件
双击video_transfer.exe即可打开软件,如果软件无法打开,先双击安装上图中的vc_redist.x64.exe,安装完成后再打开video_transfer.exe。
打开后的界面如下图所示。
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.3 上位机设置界面
按照上图的界面进行设置,这些设置和程序中定义的参数和代码是对应的。设置完成后,点击“打开”按钮,此时上位机会通过网口向开发板发送ASCII码“1”,开发板收到开始命令后,会开始传输图像数据,如下图所示:
【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

图 7.5.13.4 上位机实时显示画面
由上图可知,开发板传输的图像分辨率是640*480,帧率约为26fps,如果需要停止显示画面的话,只需要点击上方的“关闭”按钮即可。需要注意的是,上位机显示的画面受电脑性能的影响,如果电脑性能较差的话,上位机可能会出现卡顿和崩溃的现象。

版权声明:本文为博主正点原子原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/weixin_55796564/article/details/122561166

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年1月18日 下午3:32
下一篇 2022年1月18日 下午3:39

相关推荐