一、工程介绍
OV5640 摄像头通过 DVP 接口输出视频图像数据,并通过 VGA 接口输出给显示器。FPGA 需要完成的功能包括:OV5640 初始化、DVP 接口数据采集、图像数据缓存、VGA 数据输出。模块设计也相应按照这四个部分进行划分。

二、Verilog 实现
(1)OV5640 初始化
(1.1)SCCB 控制器
ov5640 摄像头初始化需要向其内部配置寄存器写入数据进行配置,实现对图像数据格式、图像大小、图像反转镜像、曝光、补偿等设置。对 ov5640 寄存器的读写操作需要遵循 SCCB 通信协议,不过 SCCB 与 I2C 协议很相似,SCCB 可以兼容 I2C,只是时序细节有区别,不过我工程中还是单独设计了一个 SCCB 控制器。
`timescale 1ns / 1ps // 适用于 ov5640 SCCB 通信 寄存器(字节)地址 16 位,数据 8 位
module SCCB_ctrl(
input wire clk, //系统时钟 100MHz
input wire rst_n, //复位
inout wire sda, //双向数据线(inout)
output wire scl, //输出时钟线
input wire rw_ctrl, //读写使能信号(0 写 1 读)
input wire work_start, //SCCB 启动信号
input wire [6:0] slave_addr, //7bit 从设备地址
input wire [15:0] byte_addr, //16bit 字地址
input wire [7:0] w_data, //8bit 待写数据
output reg [7:0] r_data, //8bit 读取数据
output reg work_done //SCCB 读写完成信号
);
//sda 传输方向控制
reg sda_oe;
reg sda_out;
wire sda_in;
assign sda_in = sda;
assign sda = sda_oe ? (sda_out ? 1'bz : 1'b0) : 1'bz;
//状态机参数
reg [4:0] state;
localparam IDLE = 5'd0,
START = 5'd1,
W_SLAVE_ADDR = 5'd2,
ACK1 = 5'd3,
W_H_BYTE_ADDR = 5'd4,
ACK2 = 5'd5,
W_L_BYTE_ADDR = 5'd6,
ACK3 = 5'd7,
STOP = 5'd8,
W_DATA = 5'd9,
W_ACK = 5'd10,
STOP2 = 5'd11,
START2 = 5'd12,
R_SLAVE_ADDR = 5'd13,
R_ACK = 5'd14,
R_DATA = 5'd15,
N_ACK = 5'd16;
//计数器及参数
reg clk_div;
reg [7:0] cnt_clk;
reg [3:0] cnt_bit;
localparam cnt_max_400khz = 8'd125;
wire scl_half_1;
wire scl_half_0;
wire scl_ack_jump;
assign scl_half_1 = (cnt_clk == cnt_max_400khz >> 1 && clk_div==1'b1);
assign scl_half_0 = (cnt_clk == cnt_max_400khz >> 1 && clk_div==1'b0);
assign scl_ack_jump=((cnt_clk ==(cnt_max_400khz >> 1)-5) && clk_div==1'b0);
//数据寄存器
reg [7:0] w_data_buf;
reg [7:0] r_data_buf;
reg [7:0] w_slave_addr_buf;
reg [7:0] r_slave_addr_buf;
reg [7:0] H_byte_addr_buf;
reg [7:0] L_byte_addr_buf;
reg work_en;
//数据复位、开始工作时寄存数据
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
w_slave_addr_buf <= 8'b0000_0000;
r_slave_addr_buf <= 8'b0000_0001;
H_byte_addr_buf <= 8'b0;
L_byte_addr_buf <= 8'b0;
w_data_buf <= 8'b0;
end
else if (work_start)
begin
w_slave_addr_buf [7:1] <= slave_addr;
r_slave_addr_buf [7:1] <= slave_addr;
w_data_buf <= w_data;
H_byte_addr_buf <= byte_addr[15:8];
L_byte_addr_buf <= byte_addr[7:0];
end
//分频计数器 (400khz 时钟 scl)
always @(posedge clk or negedge rst_n)
if (!work_en || !rst_n)
begin
cnt_clk <= 8'd1;
clk_div <= 1'b1;
end
else if (cnt_clk == cnt_max_400khz)
begin
cnt_clk <= 8'd1;
clk_div <= ~clk_div;
end
else cnt_clk <= cnt_clk + 8'd1;
assign scl = clk_div;
//状态机
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
state <= IDLE;
sda_oe <= 1'b0;
sda_out <= 1'b1;
work_en <= 1'b0;
work_done <= 1'b0;
cnt_bit <= 4'd0;
end
else case(state)
IDLE:
begin
sda_oe <= 1'b0;
sda_out <= 1'b1;
work_done <= 1'b0;
if (work_start)
begin
work_en <= 1'b1;
state <= START;
end
end
START:
begin
sda_oe <= 1'b1;
if (scl_half_1)
begin
sda_out <= 1'b0;
state <= W_SLAVE_ADDR;
end
end
W_SLAVE_ADDR:
begin
sda_oe <= 1'b1;
if (scl_half_0)
begin
if (cnt_bit != 4'd8)
begin
sda_out <= w_slave_addr_buf[7-cnt_bit];
cnt_bit <= cnt_bit + 4'd1;
end
else
begin
state <= ACK1;
cnt_bit <= 4'd0;
end
end
end
ACK1:
begin
sda_oe <= 1'b0;
if (scl_ack_jump) state <= W_H_BYTE_ADDR;
end
W_H_BYTE_ADDR:
begin
sda_oe <= 1'b1;
if (scl_half_0)
begin
if (cnt_bit != 4'd8)
begin
sda_out <= H_byte_addr_buf[7-cnt_bit];
cnt_bit <= cnt_bit + 4'd1;
end
else
begin
state <= ACK2;
cnt_bit <= 4'd0;
end
end
end
ACK2:
begin
sda_oe <= 1'b0;
if (scl_ack_jump) state <= W_L_BYTE_ADDR;
end
W_L_BYTE_ADDR:
begin
sda_oe <= 1'b1;
if (scl_half_0)
begin
if (cnt_bit != 4'd8)
begin
sda_out <= L_byte_addr_buf[7-cnt_bit];
cnt_bit <= cnt_bit + 4'd1;
end
else
begin
state <= ACK3;
cnt_bit <= 4'd0;
end
end
end
ACK3:
begin
sda_oe <= 1'b0;
if (scl_ack_jump)
begin
state <= rw_ctrl ? STOP2 : W_DATA;
sda_out <= rw_ctrl ? 1'b0 : sda_out;
end
end
STOP:
begin
sda_oe <= 1'b1;
if (scl_half_1)
begin
sda_out <= 1'b1;
work_done <= 1'b1;
work_en <= 1'b0;
state <= IDLE;
end
end
W_DATA:
begin
sda_oe <= 1'b1;
if (scl_half_0)
begin
if (cnt_bit != 4'd8)
begin
sda_out <= w_data_buf[7-cnt_bit];
cnt_bit <= cnt_bit + 4'd1;
end
else
begin
state <= W_ACK;
cnt_bit <= 4'd0;
end
end
end
W_ACK:
begin
sda_oe <= 1'b0;
if (scl_ack_jump)
begin
sda_out <= 1'b0;
state <= STOP;
end
end
STOP2:
begin
sda_oe <= 1'b1;
if (scl_half_1)
begin
sda_out <= 1'b1;
state <= START2;
end
end
START2:
begin
sda_oe <= 1'b1;
if (scl_half_1)
begin
sda_out <= 1'b0;
state <= R_SLAVE_ADDR;
end
end
R_SLAVE_ADDR:
begin
sda_oe <= 1'b1;
if (scl_half_0)
begin
if (cnt_bit != 4'd8)
begin
sda_out <= r_slave_addr_buf[7-cnt_bit];
cnt_bit <= cnt_bit + 4'd1;
end
else
begin
state <= R_ACK;
cnt_bit <= 4'd0;
end
end
end
R_ACK:
begin
sda_oe <= 1'b0;
if (scl_ack_jump) state <= R_DATA;
end
R_DATA:
begin
sda_oe <= 1'b0;
if (scl_half_1 && cnt_bit!=4'd8)
begin
r_data_buf[7-cnt_bit] <= sda_in;
cnt_bit <= cnt_bit + 4'd1;
end
if (scl_ack_jump && cnt_bit==4'd8)
begin
state <= N_ACK;
cnt_bit <= 4'd0;
r_data <= r_data_buf;
end
end
N_ACK:
begin
sda_oe <= 1'b1;
if (scl_half_0) sda_out <= 1'b1;
if (scl_ack_jump)
begin
sda_out <= 1'b0;
state <= STOP;
end
end
default: state <= IDLE;
endcase
end
endmodule




