精简版UDP协议是一种资源占用少,不限FPGA硬件平台,纯Verilog代码实现的UDP通信方案,经本人反复验证,稳定性很高,关于精简版UDP请参考我之前写的文章点击查看:精简版UDP
查看我之前写的文章点击查看:精简版UDP后,应该会知道整个UDP方案如下:
但对于板件视频传输而言,ARP协议是不需要的,我们只需用到串并转换和UDP收发即可,再次精简后的UDP架构如下:
下面解释一下图中的关系:
开发板1:视频发送
视频源:这里是OV5640摄像头,当然,其他视频也可以;
数据采集:OV5640摄像头输出的是RGB565格式图像,这里做数据采集,并生成vs、hs、de等时序;
FDMA视频三帧缓存通路:这部分在UDP发送中并没有实际意义,之所以缓存并输出是为了验证视频采集的正确性,也为了和接收板卡的输出进行比较,看传输是否正确,在验证正确后,可删除此部分,关于FDMA视频三帧缓存通路请参考我之前写的文章点击查看:FDMA视频三帧缓存架构
UDP编码:将摄像头采集数据进行数据编码,为UDP发送做准备;
这里的做准备有如下几层意思:
1、数据位宽的转换:RGB565的16bit数据转换到32bit的UDP数据位宽;
2、时钟域的转换,OV5640输出时钟大概30M左右,转换到UDP千兆模式的125M;
3、数据组包:UDP一次传输一行视频数据,称为一个UDP包,每个UDP包都好包含包头和数据段;包头包括固定的4字节帧头和视频行号,一个UDP包组成如下:
UDP发送:使用精简版UDP再精简实现,关于精简版UDP请参考我之前写的文章点击查看:精简版UDP
PHY1:也就是网络PHY芯片,这里不再多讲;
图中的字节数需要好好理解一下;
开发板2:视频接收
PHY2:也就是网络PHY芯片,这里不再多讲;
UDP接收:使用精简版UDP再精简实现,关于精简版UDP请参考我之前写的文章点击查看:精简版UDP
UDP解码:将接收到的UDP数据解码为视频数据,并生成vs、hs、de等时序;
这里的解码有如下几层意思:
1、解包,丢弃帧头,保留有效数据;
2、数据位宽的转换:32bit的UDP数据位宽转换到RGB565的16bit数据;
FDMA视频三帧缓存通路:这部分实现将接收到的UDP视频数据输出到显示器上显示,关于FDMA视频三帧缓存通路请参考我之前写的文章点击查看:FDMA视频三帧缓存架构
发送端主要实现的功能上面已经讲了,这里主要讲如何实现,很显然,只需一个fifo即可;
发送端顶层接口部分如下:
module ov5640_tx_rj45(
input gmii_tx_clk , //发送端时钟
input cam_pclk , //摄像头时钟
input rst_n , //系统复位信号,低电平有效
input tx_done , //以太网发送完成信号
input tx_req , //读数据请求信号
output tx_start_en , //以太网开始发送信号
output [31:0] tx_data , //以太网待发送数据
input cmos_frame_vsync, //输入场信号
input cmos_frame_href , //输入行信号
input cmos_frame_valid, //输入数据有效信号
input [15:0] cmos_data //输入数据
);
关键的部分是数据的组包和fifo读使能的控制,总体思路就是:fifo先缓存一行视频数据,存满后通知UDP来读,然后发起一次UDP发送,就这么简单;
组包部分关键代码如下:
//产生tx端发送数据
always@(posedge gmii_tx_clk)begin
if(!rst_n || frame_flag_t) tx_data <= 0;
else if(tx_req && cnt_tx_req == 0) tx_data <= {16'ha151,cnt_v};
else if(tx_rd_en) tx_data <= tx_rd_data;
else tx_data <= tx_data;
end
接收端是发送端的逆过程,实现方法依然是一个fifo搞定,不同的是接收端不需要跨时钟域,因为直接把接收时钟作为ddr缓存的写入时钟;
接收端顶层接口部分如下:
module rj45_rx_ov5640(
input gmii_rx_clk , //接受端时钟
input rst_n , //系统复位信号,低电平有效
input rec_pkt_done, //以太网单包数据接收完成信号
input rec_en , //以太网接收的数据使能信号
input [31:0] rec_data , //以太网接收的数据
output vout_vs , //输出场vs
output vout_de , //输出场de
output [15:0] vout_rgb565 //输出场rgb565
);
解包部分关键代码如下:
//产生网络帧帧头
always@(posedge gmii_rx_clk)begin
if(!rst_n) rx_frame_flag <= 0;
else if(rec_en_d1 && (pkt_head == 32'ha1510000) && (cnt_rec_en == 1)) rx_frame_flag <= 1;
else if(cnt_rx_frame_flag >= 100) rx_frame_flag <= 0;
else rx_frame_flag <= rx_frame_flag;
end
开发板:Artix7开发板;
网络PHY:RTL8211;
开发环境:vivado2019.1;
输入:OV5640摄像头,720P,RGB565;
输出:网线,HDMI显示器;
Artix7开发板有2个网口,PHY均为RTL8211,用网线连接2个网口实现视频回环收发;
工程BD如下:
工程代码架构如下:
发送端:
开发板:Artix7开发板;
网络PHY:RTL8211;
开发环境:vivado2019.1;
输入:OV5640摄像头,720P,RGB565;
输出:网线;
接收端:
开发板:Kintex7开发板;
网络PHY:B50610;
开发环境:vivado2019.1;
输入:网线;
输出:HDMI显示器;
整个架构需要两块板子,一根网线,当然也有两个工程,这里就不具体展示了,截图太多手软了;
发送端:
开发板:Kintex7开发板;
网络PHY:B50610;
开发环境:vivado2019.1;
输入:OV5640摄像头,720P,RGB565;
输出:网线
接收端:
开发板:Artix7开发板;
网络PHY:RTL8211;
开发环境:vivado2019.1
输入:网线;
输出:HDMI显示器;
整个架构需要两块板子,一根网线,当然也有两个工程,这里就不具体展示了,截图太多手软了;
回环工程如图:
板间收发连接如图:
再来个视频演示:A7发送K7接收的演示;
udp板间视频传输