本文章为笔者学习国产高云FPGA的学习记录,如有不妥请指正。
本实验任务是使用开发板及 OV5640 摄像头实现640*480@30Hz 的图像采集,并通过以太网发送上位机并实时显示图像。(本实验参照其它开源学习资料进行的修改与调试,基于HDMI显示的进阶)
由系统总体框图可知,FPGA 顶层模块例化了以下6个模块,分别是工程顶层(ov5640_udp_rgmii)、时钟锁相环(pll)、以太网 PHY 芯片的寄存器配置模块(pyh_reg_config)、摄像头编号模块(Camera_ETH_Formator)、UDP 协议发送模块(UDP_Send)、gmii 接口到 rgmii转换模块(gmii_to_rgmii)
ov5640_udp_rgmii :工程顶层,例化了 UDP 发送和摄像头 DVP 接口图像数据捕获编码模块, 实现完整的图像采集传输功能。
PLL:锁相环,用于产生各个模块所需工作时钟。
phy_reg_config:以太网 PHY 芯片的寄存器配置模块,控制 mdio_bit_shift 模块对以太网 PHY 的指定寄存器进行读写操作以完成以太网 PHY 的工作模式设置。
mdio_bit_shift:MDIO底层传输协议实现,完成基于 MDIO 协议对 PHY 寄存器的一次 读或者写操作。
Camera_ETH_Formator:编号模块,对摄像头输出的数据以行为单位进行编号,以便于以太网 上位机正确接收图像。
UDP_Send :协议发送模块, 该模块将用户输入的需要发送的数据内容经过 UDP、 IP、 MAC 层协议层层打包后,通过 RGMII 接口输出给以太网 PHY 芯片, 以完成数据的发送。
async_fifo:UDP发送数据缓存 FIFO,用户将需要发送的数据写入该 FIFO,然后 由 UDP_Send 中的发送逻辑将数据读出并最终以 UDP 协议发送。
CRC32_D8:CRC32 校验逻辑, 计算经由 MAC 层发送的数据的 CRC 值并附加在 MAC 结尾发送。
gmii_to_rgmii:gmii 接口到 rgmii 接口转换模块,实现 gmii 到 rgmii 的转换。
由于代码部分过多,此处只附上顶层代码部分,如有需要可以私信联系。
module ov5640_udp_rgmii(
input clk,
input rst_n,
// eth interface
output eth_gtxc,
output [3:0] eth_txd,
output eth_txen,
output eth_mdc,
inout eth_mdio,
output eth_rst_n,
//cmos interface
output camera_sclk,
inout camera_sdat,
input camera_vsync,
input camera_href,
input camera_pclk,
output camera_xclk,
output camera_rst_n,
output camera_pwdn,
input [7:0] camera_data
);
wire clk_125m;
wire clk_50m;
wire clk_24m;
wire locked;
wire global_rst_n = locked;
assign camera_xclk = clk_24m;
assign clk_50m = clk;
Gowin_rPLL Gowin_rPLL(
.clkout(clk_125m), //output clkout
.lock(locked), //output lock
.reset(~rst_n), //input reset
.clkin(clk) //input clkin
);
camera_rpll camera_rpll(
.clkout(clk_24m), //output clkout
.clkin(clk_50m) //input clkin
);
wire phy_init;
phy_reg_config phy_reg_config_inst(
.Clk(clk_50m),
.Rst_n(global_rst_n),
.Phy_rst_n(eth_rst_n),
.mdc(eth_mdc),
.mdio(eth_mdio),
.Phy_init_done(phy_init)
);
parameter IMAGE_WIDTH = 640; //图片宽度
parameter IMAGE_HEIGHT = 480; //图片高度
//摄像头初始化配置
wire Init_Done;
camera_init
#(
.CAMERA_TYPE ( "ov5640" ),//"ov5640"
.IMAGE_TYPE ( 0 ),// 0: RGB; 1: JPEG
.IMAGE_WIDTH ( IMAGE_WIDTH ),// 图片宽度
.IMAGE_HEIGHT( IMAGE_HEIGHT ),// 图片高度
.IMAGE_FLIP_EN ( 0 ),// 0: 不翻转,1: 上下翻转
.IMAGE_MIRROR_EN( 0 ) // 0: 不镜像,1: 左右镜像
)camera_init
(
.Clk (clk_50m ),
.Rst_n (locked ),
.Init_Done (Init_Done ),
.camera_rst_n(camera_rst_n ),
.camera_pwdn (camera_pwdn ),
.i2c_sclk (camera_sclk ),
.i2c_sdat (camera_sdat )
);
wire fifo_wrreq;
wire fifo_aclr;
wire [7:0] fifo_wrdata;
//以太网图像行号编号逻辑
Camera_ETH_Formator Camera_ETH_Formator(
.PCLK(camera_pclk),
.Rst_n(global_rst_n),
.Init_Done(Init_Done),
.HREF(camera_href),
.VSYNC(camera_vsync),
.DATA(camera_data),
.fifo_aclr(fifo_aclr),
.wrdata(fifo_wrdata),
.wrreq(fifo_wrreq)
);
wire [12:0] fifo_usedw;
wire GMII_GTXC;
assign GMII_GTXC = clk_125m;
wire [7:0] GMII_TXD;
wire GMII_TXEN;
UDP_Send UDP_Send(
.Clk(),
.GMII_GTXC(GMII_GTXC),
.GMII_TXD(GMII_TXD),
.GMII_TXEN(GMII_TXEN),
.Rst_n(Init_Done),
.Tx_Done(),
.data_length(IMAGE_WIDTH*2+2 ),//IMAGE_WIDTH*2+2
.des_ip(32'hc0_a8_00_03),//hc0_a8_00_03; 192.168.0.3
.des_mac(48'hFF_FF_FF_FF_FF_FF),
.des_port(16'd6102 ),
.src_ip(32'hc0_a8_00_02),//hc0_a8_65_02; 192.168.0.2
.src_mac(48'h00_0a_35_01_fe_c0),
.src_port(16'd5000),
.wrclk(camera_pclk),
.wrdata(fifo_wrdata),
.wrreq(fifo_wrreq),
.aclr(fifo_aclr),
.wrusedw(fifo_usedw)
);
gmii_to_rgmii gmii_to_rgmii(
.reset_n(rst_n),
.gmii_tx_clk(GMII_GTXC),
.gmii_txd(GMII_TXD),
.gmii_txen(GMII_TXEN),
.gmii_txer(0),
.rgmii_tx_clk(eth_gtxc),
.rgmii_txd(eth_txd),
.rgmii_txen(eth_txen)
);
endmodule
调试记录:在调试过程中主要遇到两个问题,烧录程序后网口正常发送上位机图像不显示(此处使用小梅哥摄像头上位机软件)。
1.由于分辨率问题可能需要开启巨型帧进行传输,通常以太网一次可以传输的字节数<1500byte,开启巨型帧后可以提高至9000byte,但是也不是一定需要开启,根据实际情况进行判别,例如笔者使用640*480@30的图像其大小远小于标准以太网MTU的限制故可不开启。
2.最好使用固定座子的摄像头接口而非采用飞线的方式,杜邦线的长度和插槽的松动会引起图像数据的波动,导致图像出现闪频等现象。