一、设计思路
- IMAGE_SEQUENCE模块的作用是:给输入的每一行像素RGB565数据编号,并将编号的2字节数据写入FIFO,标志图像每一行像素的开始
- FIFO_CTR信号状态机实现
- 上位机软件是大端模式,所有输入以太网MAC模块的数据应该也是大端模式
二、IMAGE_SEQUENCE模块
module camera_image_sequence(
clk ,
rst_n ,
data_vld_in ,
data_in ,
h_begin_in ,
h_end_in ,
haddr_in ,
vaddr_in ,
data_out ,
wr_en
);
localparam DATA_W = 16;
localparam HADDR_W = 12;
localparam VADDR_W = 12;
input clk;
input rst_n;
input data_vld_in;
input [DATA_W-1:0] data_in;
input h_begin_in;
input h_end_in;
input [HADDR_W-1:0] haddr_in;
input [VADDR_W-1:0] vaddr_in;
output [DATA_W-1:0] data_out;
output wr_en;
reg [DATA_W-1:0] data_out;
reg wr_en;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_out <= 0;
else if(h_begin_in)
data_out <= {vaddr_in[7:0],4\'b0,vaddr_in[11:8]};
else if(data_vld_in)
data_out <= {data_in[7:0],data_in[15:8]};
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
wr_en <= 0;
else if(h_begin_in)
wr_en <= 1;
else if(data_vld_in)
wr_en <= 1;
else
wr_en <= 0;
end
endmodule
三、FIFO_CTR模块
module fifo_ctr(
rst_n ,
wr_clk ,
rd_clk ,
data_in ,
wr_en ,
eth_data_vld,
eth_tx_done ,
eth_tx_en ,
dout
);
parameter IMAGE_WIDTH = 800;
parameter IMAGE_HEIGHT = 640;
//状态机参数
localparam IDLE = 3\'b001;
localparam EN_ETH = 3\'b010;
localparam WAIT_TX_DONE = 3\'b100;
localparam STATE_W = 3;
localparam DIN_W = 16;
localparam DOUT_CNT_W = 14;
localparam DOUT_W = 8;
input rst_n;
input wr_clk;
input rd_clk;
input [DIN_W-1:0] data_in;
input wr_en;
input eth_data_vld;
input eth_tx_done;
output eth_tx_en;
output dout;
reg eth_tx_en;
//模块变量
wire rd_en;
wire full;
wire empty;
wire [DOUT_CNT_W-1:0] rd_data_count;
wire [DOUT_W-1:0] dout;
//状态机变量
reg [STATE_W-1:0] state_c;
reg [STATE_W-1:0] state_n;
wire idle2en_eth;
wire en_eth2wait_tx_done;
wire wait_tx_done2idle;
camera2eth_fifo camera2eth_fifo (
.wr_rst((!rst_n)), // input wire wr_rst
.rd_rst((!rst_n)), // input wire rd_rst
.wr_clk(wr_clk), // input wire wr_clk
.rd_clk(rd_clk), // input wire rd_clk
.din(data_in), // input wire [15 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.rd_data_count(rd_data_count) // output wire [13 : 0] rd_data_count
);
/****************** FIFO输出端 ******************/
always @(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
always @(*)begin
case(state_c)
IDLE:begin
if(idle2en_eth)
state_n = EN_ETH;
else
state_n = state_c;
end
EN_ETH:begin
if(en_eth2wait_tx_done)
state_n = WAIT_TX_DONE;
else
state_n = state_c;
end
WAIT_TX_DONE:begin
if(wait_tx_done2idle)
state_n = IDLE;
else
state_n = state_c;
end
default:state_n = IDLE;
endcase
end
assign idle2en_eth = state_c == IDLE && (rd_data_count >= (IMAGE_WIDTH<<1) + 2);
assign en_eth2wait_tx_done = state_c == EN_ETH && eth_tx_en;
assign wait_tx_done2idle = state_c == WAIT_TX_DONE && eth_tx_done;
always @(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
eth_tx_en <= 0;
else if(state_c == EN_ETH)
eth_tx_en <= 1;
else
eth_tx_en <= 0;
end
assign rd_en = eth_data_vld;
endmodule
四、摄像头捕获数据模块
module dvp_capture(
rst_n ,
clk ,
vsync ,
hsync ,
data ,
pix_data ,
image_state ,
haddr ,
vaddr ,
h_begin ,
h_end ,
data_vld
);
parameter IMAGE_WIDTH = 800;
parameter IMAGE_HEIGHT = 480;
localparam DATA_W = 8;
localparam DATA_PIX = 16;
localparam HADDR_W = 12;
localparam VADDR_W = 12;
input rst_n;
input clk;
input vsync; //场同步信号
input hsync; //行同步信号
input [DATA_W-1:0] data;
output [DATA_PIX-1:0] pix_data; //摄像头输出的565像素数据
output image_state; //摄像头初始化完成开始输出数据标志信号
output [HADDR_W-1:0] haddr; //行地址
output [VADDR_W-1:0] vaddr; //场地址
output data_vld; //输出数据有效信号
output h_begin; //行同步开始,标志信号
output h_end; //行同步接收,标志信号
reg [DATA_PIX-1:0] pix_data; //摄像头输出的565像素数据
reg image_state; //摄像头初始化完成开始输出数据标志信号
reg [HADDR_W-1:0] haddr; //行地址
reg [VADDR_W-1:0] vaddr; //场地址
reg data_vld; //输出数据有效信号
reg h_begin;
reg h_end;
//中间变量
reg pre_vsync;
reg pre_hsync;
reg [DATA_W-1:0] pre_data;
//计数器变量
reg [HADDR_W-1:0] cnt_hsync;
wire add_cnt_hsync;
wire end_cnt_hsync;
reg [VADDR_W-1:0] cnt_vsync;
wire add_cnt_vsync;
wire end_cnt_vsync;
reg [4-1:0] cnt_frame;
wire add_cnt_frame;
wire end_cnt_frame;
reg output_frame;
//打一拍,快速IO,优化时序,边沿检测
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
pre_vsync <= 0;
pre_hsync <= 0;
pre_data <= 0;
end
else begin
pre_vsync <= vsync;
pre_hsync <= hsync;
pre_data <= data;
end
end
//行同步计数器,行同步有效时加1,行同步信号失效时结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_hsync <= 0;
else if(add_cnt_hsync)begin
if(end_cnt_hsync)
cnt_hsync <= 0;
else
cnt_hsync <= cnt_hsync + 1\'b1;
end
end
assign add_cnt_hsync = pre_hsync;
assign end_cnt_hsync = add_cnt_hsync && {pre_hsync,hsync} == 2\'b10;
//场同步信号计数器,行同步计数完成时加1,场同步达到要求的像素值结束,当输出图像不稳定在场同步拉高时清0,
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_vsync <= 0;
else if(add_cnt_vsync)begin
if(end_cnt_vsync)
cnt_vsync <= 0;
else
cnt_vsync <= cnt_vsync + 1\'b1;
end
else if({pre_vsync,vsync} == 2\'b01)
cnt_vsync <= 0;
end
assign add_cnt_vsync = end_cnt_hsync;
assign end_cnt_vsync = add_cnt_vsync && cnt_vsync == IMAGE_HEIGHT - 1;
//图像帧数计数器,出现场同步信号脉冲时加一,数到十帧图像结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_frame <= 0;
else if(add_cnt_frame)begin
if(end_cnt_frame)
cnt_frame <= 0;
else
cnt_frame <= cnt_frame + 1\'b1;
end
end
assign add_cnt_frame = {pre_vsync,vsync} == 2\'b10 && !output_frame;
assign end_cnt_frame = add_cnt_frame && cnt_frame == 10 - 1;
//舍弃每次系统开始运行后的前 10 帧图像的数据,以确保输出图像稳定
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
output_frame <= 0;
else if(end_cnt_frame)
output_frame <= 1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
pix_data <= 0;
else
pix_data <= {pix_data[7:0],pre_data};
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_vld <= 0;
else if(cnt_hsync[0] == 1 && add_cnt_hsync && output_frame)
data_vld <= 1;
else
data_vld <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
image_state <= 0;
else if(pre_vsync)
image_state <= 1;
else if(end_cnt_vsync)
image_state <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
vaddr <= 0;
else
vaddr <= cnt_vsync;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
haddr <= 0;
else
haddr = cnt_hsync[7:1];
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
h_begin <= 0;
else if({pre_hsync,hsync} == 2\'b01 && output_frame)
h_begin <= 1;
else
h_begin <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
h_end <= 0;
else if({pre_hsync,hsync} == 2\'b10)
h_end <= 1;
else
h_end <= 0;
end
endmodule
五、摄像头初始化模块
上一个随笔写了
https://www.cnblogs.com/cnlntr/p/14412096.html
六、以太网发送模块
上一个随笔写了
https://www.cnblogs.com/cnlntr/p/14431114.html
七、顶层模块
module ov5640_udp_gmii(
rst_n ,
clk ,
camera_rst_n ,
camera_scl ,
camera_sda ,
camera_clk_in ,
camera_vsync ,
camera_hsync ,
camera_data ,
gmii_clk ,
gmii_en ,
gmii_tx_data ,
camera_clk24M ,
eth_rst_n ,
led
);
parameter IMAGE_WIDTH = 1280;
parameter IMAGE_HEIGHT = 720;
parameter DST_MAC = 48\'hB0_6E_BF_02_11_C8;
parameter SRC_MAC = 48\'h00_0a_35_01_fe_c0;
parameter DST_IP = 32\'hc0_a8_00_03;
parameter SRC_IP = 32\'hc0_a8_00_02;
parameter DST_PORT = 16\'d6000;
parameter SRC_PORT = 16\'d5000;
localparam CAM_DATA_W = 8;
localparam PIX_DATA_W = 16;
localparam VADDR_W = 12;
localparam DOUT_W = 8;
localparam GMII_DATA_W = 8;
localparam LED_W = 2;
input rst_n;
input clk;
input camera_clk_in;
input camera_vsync;
input camera_hsync;
input [CAM_DATA_W-1:0] camera_data;
output camera_rst_n;
output camera_scl;
output gmii_clk;
output gmii_en;
output [GMII_DATA_W-1:0] gmii_tx_data;
output camera_clk24M;
output eth_rst_n;
output [LED_W-1:0] led;
inout camera_sda;
wire camera_rst_n;
wire camera_scl;
wire gmii_clk;
wire gmii_en;
wire [GMII_DATA_W-1:0] gmii_tx_data;
wire camera_clk24M;
wire eth_rst_n;
wire [2-1:0] led;
wire camera_sda;
//中间变量
wire rst_p;
//模块连线
wire clk50M;
wire clk125M;
wire clk24M;
wire locked;
wire [PIX_DATA_W-1:0] pix_data;
wire [VADDR_W-1:0] vaddr;
wire h_begin;
wire data_vld;
wire [PIX_DATA_W-1:0] camera_data_out;
wire wr_en;
wire eth_data_vld;
wire eth_tx_done;
wire eth_tx_en;
wire [DOUT_W-1:0] fifo_dout;
wire init_done;
assign rst_p = locked;
assign camera_clk24M = clk24M;
assign eth_rst_n = locked;
assign led = {locked,init_done};
// wire clk300M;
// ila ila (
// .clk(clk300M), // input wire clk
// .probe0(camera_clk_in), // input wire [0:0] probe0
// .probe1(1\'b0), // input wire [0:0] probe1
// .probe2(1\'b0), // input wire [0:0] probe2
// .probe3(1\'b0), // input wire [0:0] probe3
// .probe4(16\'b0), // input wire [15:0] probe4
// .probe5(1\'b0), // input wire [0:0] probe5
// .probe6(1\'b0), // input wire [0:0] probe6
// .probe7(1\'b0), // input wire [0:0] probe7
// .probe8(1\'b0), // input wire [0:0] probe8
// .probe9(1\'b0) // input wire [0:0] probe9
// );
pll pll(
.clk_out1(clk50M) ,
.clk_out2(clk125M),
.clk_out3(clk24M) ,
// .clk_out4(clk300M),
.resetn(rst_n) ,
.locked(locked) ,
.clk_in1(clk)
);
camera_init #(
.IMAGE_WIDTH (IMAGE_WIDTH) ,
.IMAGE_HEIGHT (IMAGE_HEIGHT)
)
camera_init(
.clk (clk50M),
.rst_n (rst_p),
.init_done (init_done), //摄像头初始化完成标志信号
.camera_rst_n(camera_rst_n),
.camera_pwdn (), //ov5640掉电控制信号
.scl (camera_scl), //sccb 时钟信号
.sda (camera_sda) //sccb 串行数据信号
);
BUFG BUFG_inst(
.O(camera_clk_out),
.I(camera_clk_in)
);
dvp_capture #(
.IMAGE_WIDTH (IMAGE_WIDTH) ,
.IMAGE_HEIGHT (IMAGE_HEIGHT)
)
dvp_capture(
.rst_n (rst_p),
.clk (camera_clk_out),
.vsync (camera_vsync),
.hsync (camera_hsync),
.data (camera_data),
.pix_data (pix_data),
.image_state (),
.haddr (),
.vaddr (vaddr),
.h_begin (h_begin),
.h_end (),
.data_vld (data_vld)
);
camera_image_sequence camera_image_sequence(
.clk (camera_clk_out),
.rst_n (rst_p),
.data_vld_in (data_vld),
.data_in (pix_data),
.h_begin_in (h_begin),
.vaddr_in (vaddr),
.data_out (camera_data_out),
.wr_en (wr_en)
);
fifo_ctr #(
.IMAGE_WIDTH (IMAGE_WIDTH) ,
.IMAGE_HEIGHT (IMAGE_HEIGHT)
)
fifo_ctr(
.rst_n (rst_p),
.wr_clk (camera_clk_out),
.rd_clk (clk125M),
.data_in (camera_data_out),
.wr_en (wr_en),
.eth_data_vld(eth_data_vld),
.eth_tx_done (eth_tx_done),
.eth_tx_en (eth_tx_en),
.dout (fifo_dout)
);
eth_udp_tx_gmii eth_udp_tx_gmii(
.clk (clk125M),
.rst_n (rst_p),
.tx_en (eth_tx_en),
.tx_done (eth_tx_done),
.dst_mac (DST_MAC),
.src_mac (SRC_MAC),
.dst_ip (DST_IP),
.src_ip (SRC_IP),
.dst_port (DST_PORT),
.src_port (SRC_PORT),
.gmii_clk (gmii_clk),
.data_len ((IMAGE_WIDTH<<1) + 2),
.data_vld (eth_data_vld),
.gmii_en (gmii_en),
.gmii_tx (gmii_tx_data),
.data_in (fifo_dout)
);
endmodule
八、仿真验证文件
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2021/02/24 19:03:39
// Design Name:
// Module Name: ov5640_udp_gmii_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ov5640_udp_gmii_tb();
parameter WIDTH = 1280;
parameter HEIGHT = 720;
localparam CAM_DATA_W = 8;
localparam PIX_DATA_W = 16;
localparam HADDR_W = 12;
localparam VADDR_W = 12;
localparam DOUT_W = 8;
localparam GMII_DATA_W = 8;
localparam LED_W = 2;
reg rst_n;
reg clk;
reg camera_clk_in;
reg camera_vsync;
reg camera_hsync;
reg [CAM_DATA_W-1:0] camera_data;
wire camera_rst_n;
wire camera_scl;
wire gmii_clk;
wire gmii_en;
wire [GMII_DATA_W-1:0] gmii_tx_data;
wire camera_clk24M;
wire eth_rst_n;
wire [LED_W-1:0] led;
wire camera_sda;
ov5640_udp_gmii #(
.IMAGE_WIDTH(WIDTH) ,
.IMAGE_HEIGHT(HEIGHT)
)
ov5640_udp_gmii(
.rst_n (rst_n),
.clk (clk),
.camera_rst_n (camera_rst_n),
.camera_scl (camera_scl),
.camera_sda (camera_sda),
.camera_clk_in (camera_clk_in),
.camera_vsync (camera_vsync),
.camera_hsync (camera_hsync),
.camera_data (camera_data),
.gmii_clk (gmii_clk),
.gmii_en (gmii_en),
.gmii_tx_data (gmii_tx_data),
.camera_clk24M (camera_clk24M),
.eth_rst_n (eth_rst_n),
.led (led)
);
//时钟周期,单位ns,在这里修改时钟周期
parameter CYCLE = 20;
parameter CAM_CYCLE = 26;
//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;
//生成本地时钟50M
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
initial begin
camera_clk_in = 0;
forever
#(CAM_CYCLE/2)
camera_clk_in=~camera_clk_in;
end
//产生复位信号
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE * RST_TIME);
rst_n = 1;
end
integer i = 0;
integer j = 0;
//输入信号din0赋值方式
initial begin
#1;
camera_data = 8\'hff;
camera_vsync = 0;
camera_hsync = 0;
#(10*CYCLE);
//开始赋值,模拟前10帧图像不稳定
repeat(3)begin
camera_vsync = 1;
#(10*CYCLE)
camera_vsync = 0;
#(20*CYCLE)
for(i = 0;i < 6;i = i + 1)begin
for(j = 0;j < (5<<1);j = j + 1)begin
camera_hsync = 1;
camera_data = camera_data - 1;
#80;
end
camera_hsync = 0;
#(20*CYCLE);
end
end
repeat(3)begin
camera_vsync = 1;
#(10*CYCLE)
camera_vsync = 0;
#(20*CYCLE)
for(i = 0;i < 2;i = i + 1)begin
for(j = 0;j < (5<<1);j = j + 1)begin
camera_hsync = 1;
camera_data = camera_data - 1;
#80;
end
camera_hsync = 0;
#(20*CYCLE);
end
end
repeat(15)begin
camera_vsync = 1;
#(10*CYCLE)
camera_vsync = 0;
#(20*CYCLE)
for(i = 0;i < HEIGHT;i = i + 1)begin
for(j = 0;j < (WIDTH<<1);j = j + 1)begin
camera_hsync = 1;
camera_data = camera_data - 1;
#80;
end
camera_hsync = 0;
#(20*CYCLE);
end
end
#1000;
$stop;
end
endmodule