基于 OV2640 的以太网 RGMII 图像传输系统设计
文章目录
前言
本节主要讲述了一种对数据以行为单位的编码方法。该方法采用摄像头采集数据后经由 FPGA 的 RGMII 接口通过 UDP 协议实现以太网图像传输。以该方法进行 UDP 协议下的以太网图像传输,可以有效缓解 UDP 协议的差错控制缺陷带来的传输质量不佳问题。
提示:任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」 ;不要急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人 。怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」
一、UDP 协议的特点
我们通过以太网进行数据传输时,通常对于一些对数据正确性要求较低
(允许少量丢失)但是不能有较大时延的场景,都会使用 UDP 协议。UDP 协议是传输层的一种协议,该协议具有以下特点:
- 提供不可靠传输。在进行数据传输时 UDP 提供尽最大努力的交付,但不保证可靠交付。
- 高效而无连接性。UDP 协议在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序等问题,如果因为网络原因没有传送到对端,UDP 也不会给应用层返回错误信息。但正因为 UDP 在进行发送时不需要建立连接,所以其在网络开销较小的同时工作时效也相当高。
- UDP 没有拥塞控制。应用层能够更好的控制要发送的数据和发送时间,网络中的拥塞控制也不会影响主机的发送速率,因此UDP协议常常被用于某些对数据发送速率有一定要求,能容忍一些数据的丢失,但是不能允许有较大的时延的场景,如直播,视频等。
- UDP 在现场测控领域,往往面向的是分布化的控制器、监测器等应用。现场测控领域的电磁环境往往较为复杂,基于此,现场通信中,若某一应用要将一组数据传送给网络中的另一个节点,可由 UDP 进程将数据加上报头后传送给 IP 进程。由于 UDP 协议省去了建立连接和拆除连接的过程,取消了重发检验机制,所以能够达到较高的通信速率。
正因为 UDP 协议有如上种种优点,所以可以对 UDP 协议图像传输的策略进行改进,而降低干扰的影响后,UDP 协议仍然可以作为图像传输的一种优质
方案。
二、图像数据编码原理
我们前面说过:以太网图像数据的传输通常采用 UDP 协议,图像数据在传输时一旦受到外界干扰发生数据丢失,而数据的丢失会导致图像发生断层,割裂,压缩等现象。
正因为如此,我们可以进行如下有针对性的改进后,仍保留使用 UDP 协议:如果我们对图像数据进行处理,将摄像头采集到的图像数据按照一定的编号方式例如以行为单位编号后,再通过 UDP 协议经由以太网传输到电脑,电脑接收FPGA 发送的图像数据解析后按照编号将相应的图像内容绘制到电脑显示屏的对应位置上,就能有效解决该问题。
经过该种编码方法改进,当数据在通过 UDP 协议传输的时候,理论上即使发生了少量数据丢失也不会出现断层割裂等效果。而对于图像传输显示这类场景,对于图像数据的准确性要求较低,即使一幅图像中有一两行数据的丢失,只要不是每幅图像都有该丢失情况,在每秒几十上百帧的刷新率下,其影响也极小。为了让上述理论能够更加加通俗易懂,我们结合图形,来对该理论进行讲解。
本次设计原理如图所示:
左边第一张为图像的写入过程,图像数据从左至右被依次写入。
第二张图为正常写入后所应展现出的画面,假设被标绿线的行在传输过程中丢失了,在未对行号进行编码的情况下,将会得到第三张图中被压缩的结果,而后续如果仍有图像传输过来,甚至可能出现两张图像重叠的情况,会影响后续图像数据的显示效果。
如果我们对每行图像数据进行编号,即使数据在传输过程中有部分行的数据丢失了,但因为序号的存在,它们仍会排列在自己所应处的位置。
举例来说:上图中,假设绿色行发生丢失,数据在显示屏上的显示画面如第四张图,被丢失的行由于没有数据,会显示黑色,即使数据丢失,图像也不会发生压缩等现象,只会在当前帧有影响,不会影响后续帧的图像。在每秒几十帧的刷新率下,一两行数据的丢失对人眼而言甚至无法识别到,或者只是看到黑线一闪而过。
在理解了本次设计最终目的的实现原理后,接下来可以开始进行工程的设
计。
三、系统总体设计
我们可以结合前面开发的内容,作如下设计。本次本次板级验证我们将在开发板上结合前面章节学习过的以太网传图工程对设计模块进行验证。
结合板级验证的硬件搭建环境,本章节的原理作如下对应与深化讲解:
- 整个系统在开始工作前,首先按照初始化 table 表对摄像头进行初始化参考基于 OV2640 的图像采集显示系统(camera_init 摄像头初始化模块)。
- 然后对以太网芯片初始化(此模块可以不用),可参考以太网初始化设计(MDIO 控制器);
- 随 后 摄 像 头 将 采 集 到 的 数 据 经 由 DVP 接 口 输 送 给Camera_ETH_Formator模块,也就是本次设计的模块,该模块会根据输入数据行场同步信号的高低电平,以行为单位,对其进行编码。
- 随后数据进入一个双口 FIFO 中,当 FIFO 中的值足够进行一次以太网数据传输时,数据就会被发送模块读出。使用 UDP 协议将读出的数据经过 crc 校验后,经由以太网发送到 PC 机,参考千兆以太网传输层 UDP 协议原理与 FPGA 实现(UDP发送)、RGMII 与 GMII 转换电路设计。
- PC 端使用显示软件接收 FPGA发送的包含行编号的图像数据,解析后还原为图像内容绘制在PC 机显示屏上。
根据前面学习内容的基础,在本章,我们着重讲解图像编码模块的作用,
以上处顶层模块与Camera_ETH_Formator模块外其他模块均在相关文章中讲述,可以参考我的相关文章。
四、图像编码模块介绍
4.1、图像编码模块作用
根据前面介绍的设计思想,本次模块设计的目的是实现对图像数据以行为单位进行编号。如果实现数据以行为单位编号,就需要知道当前输入的数据位
于每帧图像数据的哪一行。对此,我们可以通过计数行场同步信号实现。
模块 Camera_ETH_Formator 在整个系统内,可以起到防止在图像显示的过
程中由于某一行数据的丢失,导致的图像压缩断层现象发生的作用。在加入了该模块后,数据会按照其排列序号一行行进行排列,即使有一行或多行数据丢失,其余行数据也会排列在自己所应处的位置。
综合以上信息,设计模块 Camera_ETH_Formator 的结构如图 :
其中各个信号的含义如表所示:
当 HREF由低电平转为高电平时,DATA开始输入到图像数据编码模块,当
HREF 从高电平转为低电平时代表一行的数据输出完成,当模块中的数据处理完成后便会产生 wrreq 信号,并输出 8 位的 wrdata 数据。
我们以 1280*720 的显示屏为例,在摄像头将采集到的 RGB565 格式数据通过以太网发送给 PC 的显示屏显示的过程中,每一行有两倍 1280 个字节即 2560字节有效数据。依照前文我们介绍的设计思想:为了让部分数据即使在丢失的情况下其余行数据也能显示在自己应处的位置,我们可以在每一行的数据前加上 2 个字节的行号。也就是说,以太网每一帧,需要发送一行的数据外加两个字节的行号。
4.2、图像编码模块功能实现
为了方便理解和设计,我们绘制了本次模块设计各个信号之间的时序图,如图图像编码模块时序图:
上图中:href_r1 和 href_r2 为对摄像头行同步 HREF 信号寄存后得到的信号,data_tmp 为 24 位的寄存器,用来完成对数据的移位转换。
如:时序图中 C0 时刻,当 HREF 出现上升沿时,代表数据开始输入。此时
我们可以用 HREF 与 href_r1 位拼接后的值表示,当{href_r1,HREF }的值为2’b01 时,HREF 的电平由低到高,数据开始输入;当{ href_r1,HREF }的值为2’b10 时,HREF 的电平由高到低,一行数据输入完毕,数据停止输入。
本次模块设计的目的是以行为单位,对数据进行编号,根据 HREF 的电平变化,我们可以利用计数器对行信号进行下降沿次数的累加生成行号,该部分代码实现如下:
//---------------------------------------
//VSYNC为高电平(有效)时,每当HREF1拉低时自加1,为记录行号
always@(posedge PCLK or negedge Rst_n)
if(!Rst_n)
Vcnt <= 0;
else if(!VSYNC)
Vcnt <= 0;
else if({
href_r1,HREF} == 2'b10)
Vcnt <= Vcnt + 1'd1;
//---------------------------------------
代码中:Vcnt 为行号计数器,每当 HREF 电平状态由高变低时,代表一行数据输出完毕,此时我们只需使计数器自加一即可,当VSYNC信号为低电平,即代表一幅图像数据输入完成,此时将计数器清零。完成了行号的产生,接下来就是将行号写入数据中。
如时序图中所示,C0 处检测到 HREF 信号的上升沿,由于该上升沿在时钟上升沿之后才到来,所以实质上该信号是在下一个时钟上升沿到来时才被采集到。该信号被采集后,随即将行号写入 data_tmp 寄存器中。当 2 字节的行号输出完成后,再开始输出图像数据,相应的代码如下:
//---------------------------------------
//对HREF进行计数,用于判断奇偶,对data_tmp进行数据拼接
reg [11:0]HREF_CNT;
always@(posedge PCLK or negedge Rst_n)
if(!Rst_n)
HREF_CNT <= 0;
else if({
href_r1,HREF} == 2'b01)
HREF_CNT <= HREF_CNT + 1;
else if(href_r1)
HREF_CNT <= HREF_CNT + 1;
else if(!href_r1)
HREF_CNT <= 0;
else
HREF_CNT <= HREF_CNT;
//---------------------------------------
//---------------------------------------
//HREF1上升沿时移位传输数据
always@(posedge PCLK or negedge Rst_n)
if(!Rst_n)
data_tmp <= 0;
else if({href_r1,HREF} == 2'b01)
data_tmp <= {
Vcnt[7:0],Vcnt[15:8],DATA};
else if(HREF_CNT[0] == 1)
data_tmp <= {
data_tmp[15:8],DATA,data_tmp[7:0]};
else if(HREF_CNT[0
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!