阶段目标:I2C 配置摄像头寄存器,DVP视频流接收,将捕获的视频帧写入 DDR2,通过 VDMA 显示实时摄像头画面
输出结果:VGA 显示器实时显示 OV5640 摄像头采集的画面
设备:NEXYS A7开发板
架构梳理:
- 使用 I2C (SCCB 协议) 访问
0x3C器件地址,读取0x300A/B寄存器获取0x5640ID。 - 自定义 DVP to AXI-Stream IP:编写精简 Verilog,利用
PCLK将 8 位数据 (D[7:0]) 拼接为 16 位 RGB565,并转换 VSYNC/HREF 为tuser/tvalid/tlast握手信号。 - VDMA S2MM 闭环:重新开启 VDMA 写通道,将摄像头数据源源不断地写入 DDR 显存的首地址。
系统总体架构

硬件模块配置
1. OV5640 摄像头接口
作用:提供视频流信号
手写模块暴露接口
`timescale 1ns / 1ps
module ov5640_dvp_to_axis #(
parameter integer H_ACTIVE = 640,
parameter integer V_ACTIVE = 480,
parameter integer FIFO_ADDR_WIDTH = 12,
parameter VSYNC_ACTIVE_HIGH = 1'b1,
parameter HREF_ACTIVE_HIGH = 1'b1,
parameter RGB565_BYTE_ORDER = 1'b0,
parameter SWAP_RB = 1'b0
) (
input wire pclk,
input wire rstn,
input wire cam_vsync,
input wire cam_href,
input wire [7:0] cam_data,
output wire m_axis_video_tvalid,
input wire m_axis_video_tready,
output wire [23:0] m_axis_video_tdata,
output wire [0:0] m_axis_video_tuser,
output wire [0:0] m_axis_video_tlast,
output wire probe_pixel_valid,
output wire [15:0] probe_rgb565,
output wire [15:0] probe_x,
output wire [15:0] probe_y,
output reg probe_overflow
);
reg vsync_d;
reg href_d;
reg [7:0] data_d;
reg byte_phase;
reg [7:0] first_byte;
reg [15:0] rgb565;
reg [15:0] x_cnt;
reg [15:0] y_cnt;
reg line_has_pixel;
reg sof_pending;
reg frame_active;
localparam integer FIFO_DEPTH = (1 << FIFO_ADDR_WIDTH);
localparam integer FIFO_COUNT_WIDTH = FIFO_ADDR_WIDTH + 1;
localparam [FIFO_COUNT_WIDTH-1:0] FIFO_DEPTH_COUNT = (1 << FIFO_ADDR_WIDTH);
reg [25:0] fifo_mem [0:FIFO_DEPTH-1];
reg [FIFO_ADDR_WIDTH-1:0] fifo_wr_ptr;
reg [FIFO_ADDR_WIDTH-1:0] fifo_rd_ptr;
reg [FIFO_COUNT_WIDTH-1:0] fifo_count;
reg pixel_push;
reg [23:0] pixel_data;
reg pixel_user;
reg pixel_last;
wire fifo_empty = (fifo_count == {FIFO_COUNT_WIDTH{1'b0}});
wire fifo_full = (fifo_count == FIFO_DEPTH_COUNT);
wire fifo_pop = m_axis_video_tready && !fifo_empty;
wire fifo_push = pixel_push && !fifo_full;
wire vsync_active = VSYNC_ACTIVE_HIGH ? cam_vsync : ~cam_vsync;
wire href_active = HREF_ACTIVE_HIGH ? cam_href : ~cam_href;
wire vsync_active_d = VSYNC_ACTIVE_HIGH ? vsync_d : ~vsync_d;
wire href_active_d = HREF_ACTIVE_HIGH ? href_d : ~href_d;
wire vsync_start = vsync_active && !vsync_active_d;
wire vsync_end = !vsync_active && vsync_active_d;
wire href_end = !href_active && href_active_d;
wire [4:0] pix_r5 = RGB565_BYTE_ORDER ? cam_data[7:3] : first_byte[7:3];
wire [5:0] pix_g6 = RGB565_BYTE_ORDER ? {cam_data[2:0], first_byte[7:5]} :
{first_byte[2:0], cam_data[7:5]};
wire [4:0] pix_b5 = RGB565_BYTE_ORDER ? first_byte[4:0] : cam_data[4:0];
wire [7:0] pix_r = {pix_r5, pix_r5[4:2]};
wire [7:0] pix_g = {pix_g6, pix_g6[5:4]};
wire [7:0] pix_b = {pix_b5, pix_b5[4:2]};
wire [23:0] pixel_rgb888 = SWAP_RB ? {pix_b, pix_g, pix_r} : {pix_r, pix_g, pix_b};
always @(posedge pclk) begin
if (!rstn) begin
vsync_d <= 1'b0;
href_d <= 1'b0;
data_d <= 8'h00;
byte_phase <= 1'b0;
first_byte <= 8'h00;
rgb565 <= 16'h0000;
x_cnt <= 16'd0;
y_cnt <= 16'd0;
line_has_pixel <= 1'b0;
sof_pending <= 1'b0;
frame_active <= 1'b0;
fifo_wr_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_rd_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_count <= {FIFO_COUNT_WIDTH{1'b0}};
pixel_push <= 1'b0;
pixel_data <= 24'h000000;
pixel_user <= 1'b0;
pixel_last <= 1'b0;
probe_overflow <= 1'b0;
end else begin
vsync_d <= cam_vsync;
href_d <= cam_href;
data_d <= cam_data;
pixel_push <= 1'b0;
if (fifo_pop) begin
fifo_rd_ptr <= fifo_rd_ptr + 1'b1;
end
if (vsync_start) begin
x_cnt <= 16'd0;
y_cnt <= 16'd0;
byte_phase <= 1'b0;
line_has_pixel <= 1'b0;
sof_pending <= 1'b0;
frame_active <= 1'b0;
fifo_wr_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_rd_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_count <= {FIFO_COUNT_WIDTH{1'b0}};
end else if (vsync_end) begin
x_cnt <= 16'd0;
y_cnt <= 16'd0;
byte_phase <= 1'b0;
line_has_pixel <= 1'b0;
sof_pending <= 1'b1;
frame_active <= 1'b1;
fifo_wr_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_rd_ptr <= {FIFO_ADDR_WIDTH{1'b0}};
fifo_count <= {FIFO_COUNT_WIDTH{1'b0}};
end else if (href_end) begin
x_cnt <= 16'd0;
byte_phase <= 1'b0;
line_has_pixel <= 1'b0;
if (line_has_pixel && (y_cnt < V_ACTIVE)) begin
y_cnt <= y_cnt + 1'b1;
end
if (line_has_pixel && (y_cnt == V_ACTIVE - 1)) begin
frame_active <= 1'b0;
end
end else if (frame_active && href_active && !vsync_active) begin
if (!byte_phase) begin
first_byte <= cam_data;
byte_phase <= 1'b1;
end else begin
byte_phase <= 1'b0;
rgb565 <= RGB565_BYTE_ORDER ? {cam_data, first_byte} : {first_byte, cam_data};
if ((x_cnt < H_ACTIVE) && (y_cnt < V_ACTIVE)) begin
pixel_push <= 1'b1;
pixel_data <= pixel_rgb888;
pixel_user <= sof_pending;
pixel_last <= (x_cnt == H_ACTIVE - 1);
sof_pending <= 1'b0;
line_has_pixel <= 1'b1;
x_cnt <= x_cnt + 1'b1;
if (fifo_full && !fifo_pop) begin
probe_overflow <= 1'b1;
end
end
end
end else begin
byte_phase <= 1'b0;
end
if (fifo_push) begin
fifo_mem[fifo_wr_ptr] <= {pixel_user, pixel_last, pixel_data};
fifo_wr_ptr <= fifo_wr_ptr + 1'b1;
end
case ({fifo_push, fifo_pop})
2'b10: fifo_count <= fifo_count + 1'b1;
2'b01: fifo_count <= fifo_count - 1'b1;
default: fifo_count <= fifo_count;
endcase
end
end
assign m_axis_video_tvalid = !fifo_empty;
assign m_axis_video_tdata = fifo_mem[fifo_rd_ptr][23:0];
assign m_axis_video_tuser = fifo_mem[fifo_rd_ptr][25];
assign m_axis_video_tlast = fifo_mem[fifo_rd_ptr][24];
assign probe_pixel_valid = pixel_push;
assign probe_rgb565 = rgb565;
assign probe_x = x_cnt;
assign probe_y = y_cnt;
endmodule
2. I2C 控制器(SCCB 协议)
作用:简化I2C通信
频率和系统主频保持一致,启动参考
3. VDMA 双通道配置
启用 S2MM(写通道),配置和读通道保持一致即可
时钟方案
Clocking Wizard 配置
注意这块要看手上摄像头模块有没有自己的晶振,有的话需要引入这个pclk,没有的话需要自己在Clocking Wizard里分出来一个24M。
pclk供给vdma的读通道时钟和手写ov5640_dvp_to_axis_0的时钟
接线
Run Connection Automation
摄像头输入链路 :OV5640->ov5640_dvp_to_axis_0->VDMA S2MM->AXI SmartConnect->MIG DDR2
显示输出链路:VDMA MM2S->AXI SmartConnect->AXI4-Stream to Video Out
尾声
Generate Output Products
Create HDL Wrapper
分配引脚生成.xdc
综合实现生成比特流
关于OV5640的初始化表
图像质量不足的可以参考一下这一套,调校了几版效果还可以
static const ov5640_reg_t ov5640_isp_regs[] = {
{0x3A0F, 0x30}, {0x3A10, 0x28}, {0x3A1B, 0x30}, {0x3A1E, 0x26},
{0x3A11, 0x60}, {0x3A1F, 0x14}, {0x3A18, 0x00}, {0x3A19, 0x7C},
{0x5000, 0xA7}, {0x5001, 0xA3},
/* LENC lens shading correction */
{0x5800, 0x23}, {0x5801, 0x14}, {0x5802, 0x0F}, {0x5803, 0x0F},
{0x5804, 0x12}, {0x5805, 0x26}, {0x5806, 0x0C}, {0x5807, 0x08},
{0x5808, 0x05}, {0x5809, 0x05}, {0x580A, 0x08}, {0x580B, 0x0D},
{0x580C, 0x08}, {0x580D, 0x03}, {0x580E, 0x00}, {0x580F, 0x00},
{0x5810, 0x03}, {0x5811, 0x09}, {0x5812, 0x07}, {0x5813, 0x03},
{0x5814, 0x00}, {0x5815, 0x01}, {0x5816, 0x03}, {0x5817, 0x08},
{0x5818, 0x0D}, {0x5819, 0x08}, {0x581A, 0x05}, {0x581B, 0x06},
{0x581C, 0x08}, {0x581D, 0x0E}, {0x581E, 0x29}, {0x581F, 0x17},
{0x5820, 0x11}, {0x5821, 0x11}, {0x5822, 0x15}, {0x5823, 0x28},
{0x5824, 0x46}, {0x5825, 0x26}, {0x5826, 0x08}, {0x5827, 0x26},
{0x5828, 0x64}, {0x5829, 0x26}, {0x582A, 0x24}, {0x582B, 0x22},
{0x582C, 0x24}, {0x582D, 0x24}, {0x582E, 0x06}, {0x582F, 0x22},
{0x5830, 0x40}, {0x5831, 0x42}, {0x5832, 0x24}, {0x5833, 0x26},
{0x5834, 0x24}, {0x5835, 0x22}, {0x5836, 0x22}, {0x5837, 0x26},
{0x5838, 0x44}, {0x5839, 0x24}, {0x583A, 0x26}, {0x583B, 0x28},
{0x583C, 0x42}, {0x583D, 0xCE},
{0x5180, 0xFF}, {0x5181, 0xF2}, {0x5182, 0x00}, {0x5183, 0x14},
{0x5184, 0x25}, {0x5185, 0x24}, {0x5186, 0x09}, {0x5187, 0x09},
{0x5188, 0x09}, {0x5189, 0x75}, {0x518A, 0x54}, {0x518B, 0xE0},
{0x518C, 0xB2}, {0x518D, 0x42}, {0x518E, 0x3D}, {0x518F, 0x56},
{0x5190, 0x46}, {0x5191, 0xF8}, {0x5192, 0x04}, {0x5193, 0x70},
{0x5194, 0xF0}, {0x5195, 0xF0}, {0x5196, 0x03}, {0x5197, 0x01},
{0x5198, 0x04}, {0x5199, 0x12}, {0x519A, 0x04}, {0x519B, 0x00},
{0x519C, 0x06}, {0x519D, 0x82}, {0x519E, 0x38},
{0x5381, 0x1E}, {0x5382, 0x5B}, {0x5383, 0x08}, {0x5384, 0x0A},
{0x5385, 0x7E}, {0x5386, 0x88}, {0x5387, 0x7C}, {0x5388, 0x6C},
{0x5389, 0x10}, {0x538A, 0x01}, {0x538B, 0x98},
{0x5480, 0x01}, {0x5481, 0x08}, {0x5482, 0x14}, {0x5483, 0x28},
{0x5484, 0x51}, {0x5485, 0x65}, {0x5486, 0x71}, {0x5487, 0x7D},
{0x5488, 0x87}, {0x5489, 0x91}, {0x548A, 0x9A}, {0x548B, 0xAA},
{0x548C, 0xB8}, {0x548D, 0xCD}, {0x548E, 0xDD}, {0x548F, 0xEA},
{0x5490, 0x1D},
{0x5580, 0x06}, {0x5583, 0x40}, {0x5584, 0x10}, {0x5589, 0x10},
{0x558A, 0x00}, {0x558B, 0xF8}, {0x501D, 0x40},
{0x5300, 0x08}, {0x5301, 0x30}, {0x5302, 0x10}, {0x5303, 0x00},
{0x5304, 0x08}, {0x5305, 0x30}, {0x5306, 0x08}, {0x5307, 0x16},
{0x5309, 0x08}, {0x530A, 0x30}, {0x530B, 0x04}, {0x530C, 0x06},
{0x5025, 0x00},
{OV5640_REG_END, 0x00}
};