FPGA配置OV5640摄像头及RGB图像数据采集

时间:2023-03-09 23:13:45
FPGA配置OV5640摄像头及RGB图像数据采集

  本文设计思想采用明德扬至简设计法。在做摄像头数据采集处理之前,需要配置OV5640传感器内部寄存器使其按要求正常工作,详细内容请参见《OV5640自动对焦照相模组应用指南》。首先要关注OV5640的上电时序:

FPGA配置OV5640摄像头及RGB图像数据采集

  主控制器控制RESET PWDN两个信号按上电时序要求变化,之后允许ov_config模块配置内部寄存器。这里始终将PWDN拉低。实验中将摄像头分辨率设置为720p,即1280*720 ,帧率为30fps,图像输出格式是RGB565。此时摄像头输入时钟XCLK频率24MHz,输出像素时钟PCLK为84MHz。由于实验使用的是OV5640双目摄像头模组,且XCLK由外部24MHz晶振给出,故ov_config模块整体结构及端口定义如下:

FPGA配置OV5640摄像头及RGB图像数据采集

  setup模块构造上电时序,两个reg_config分别配置一个OV5640摄像头。SCCB_interface子模块负责SCCB协议读写寄存器数据。由于OV5640摄像头内部寄存器地址为16位,因此写寄存器地址阶段分高低字节两次写入。datasheet中给出了OV5640的SCCB ID地址(写),故读ID地址为0X79。

FPGA配置OV5640摄像头及RGB图像数据采集

  SCCB时序图及AC characteristics如下:

FPGA配置OV5640摄像头及RGB图像数据采集

  SCCB时钟SIOC支持最大频率为400KHz,一般选100KHz即可。以下是本人设计的SCCB接口读写时序状态机,写操作:IDLE START WRI_ID WRI_REG WRI_DATA STOP 对应三相写,读操作:IDLE START WRI_ID WRI_REG STOP      START WRI_ID RD_DATA STOP对应两相写和两相读。

FPGA配置OV5640摄像头及RGB图像数据采集

上代码:

SCCB读写模块:

 `timescale 1ns / 1ps

 module sccb_interface(
input clk,
input rst_n, input wr_en,
input rd_en,
input [-:] id_addr,
input [-:] reg_addr,
input [-:] wr_data,
output reg [-:] rd_data,
output reg rd_vld,
output rdy, output reg sio_c,
output reg sio_out_en,
output reg sio_out,
input sio_in
); parameter CYC = ; localparam IDLE = ;
localparam START = ;
localparam WRI_ID = ;
localparam WRI_REG = ;
localparam WRI_DATA = ;
localparam RD_DATA = ;
localparam STOP = ; //计数器
reg [ (-):] div_cnt ;
wire add_div_cnt ;
wire end_div_cnt ;
reg [ (-):] bit_cnt ;
wire add_bit_cnt ;
wire end_bit_cnt ;
reg [-:] N;
(*DONT_TOUCH = "TRUE"*)reg [-:] state_c,state_n;
wire idle2start,start2wri_id,wri_id2wri_reg,wri_id2rd_data, wri_reg2wri_data,wri_reg2stop,wri_data2stop,rd_data2stop,stop2start,stop2idle;
wire [-:] regaddr;
reg [-:] reg_addr_tmp;
reg [-:] wr_data_tmp;
wire [-:] idaddr_nc;
reg [-:] id_addr_tmp;
reg rd_oper,rd_flag;
wire [-:] wdata_nc;
wire [-:] id_rwCtrl; assign rdy = state_c == IDLE && !wr_en && !rd_en; always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
div_cnt <= ;
end
else if(add_div_cnt) begin
if(end_div_cnt)
div_cnt <= ;
else
div_cnt <= div_cnt+ ;
end
end
assign add_div_cnt = (state_c != IDLE);
assign end_div_cnt = add_div_cnt && div_cnt == (CYC)- ;//5000ns,200KHZ always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
bit_cnt <= ;
end
else if(add_bit_cnt) begin
if(end_bit_cnt)
bit_cnt <= ;
else
bit_cnt <= bit_cnt+ ;
end
end
assign add_bit_cnt = (end_div_cnt);
assign end_bit_cnt = add_bit_cnt && bit_cnt == (N)- ; always@(*)begin
case(state_c)
START: N = ;
WRI_REG: N = ; //(8+1)*2 = 18
WRI_ID,WRI_DATA,RD_DATA:N = ;//8+1
STOP: N = ;
default:;
endcase
end //FSM:IDLE START WRI_ID WRI_REG STOP
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end
//write:IDLE START WRI_ID WRI_REG WRI_DATA STOP IDLE...
//read: IDLE START WRI_ID WRI_REG STOP START WRI_ID RD_DATA STOP IDLE... always @(*) begin
case(state_c)
IDLE :begin //
if(idle2start)
state_n = START ;
else
state_n = state_c ;
end
START :begin //
if(start2wri_id)
state_n = WRI_ID ;
else
state_n = state_c ;
end
WRI_ID :begin //
if(wri_id2wri_reg)
state_n = WRI_REG ;
else if(wri_id2rd_data)
state_n = RD_DATA;
else
state_n = state_c ;
end
WRI_REG :begin //
if(wri_reg2wri_data)
state_n = WRI_DATA;
else if(wri_reg2stop)
state_n = STOP ;
else
state_n = state_c ;
end
WRI_DATA:begin //
if(wri_data2stop)
state_n = STOP;
else
state_n = state_c;
end
RD_DATA:begin //
if(rd_data2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP :begin //
if(stop2start)
state_n = START;
else if(stop2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end assign idle2start = state_c==IDLE && (wr_en || rd_en);
assign start2wri_id = state_c==START && (end_bit_cnt);
assign wri_id2wri_reg = state_c==WRI_ID && (end_bit_cnt && !rd_oper);
assign wri_id2rd_data = state_c==WRI_ID && (end_bit_cnt && rd_oper);
assign wri_reg2wri_data = state_c==WRI_REG && (end_bit_cnt && !rd_flag);
assign wri_reg2stop = state_c==WRI_REG && (end_bit_cnt && rd_flag);
assign wri_data2stop = state_c==WRI_DATA && (end_bit_cnt);
assign rd_data2stop = state_c==RD_DATA && (end_bit_cnt);
assign stop2start = state_c==STOP && (end_bit_cnt && rd_flag && !rd_oper);
assign stop2idle = state_c==STOP && (end_bit_cnt && (!rd_flag || rd_oper)); always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_oper <= ;
end
else if(stop2start)begin
rd_oper <= ;
end
else if(rd_oper && stop2idle)
rd_oper <= ;
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_flag <= ;
end
else if(idle2start && rd_en)begin
rd_flag <= ;
end
else if(stop2idle)
rd_flag <= ;
end //SCCB时钟
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)
sio_c <= 'b1;
else if(add_div_cnt && div_cnt == CYC/-)
sio_c <= ;
else if(add_div_cnt && div_cnt == CYC/+CYC/-)
sio_c <= ;
else if(state_c == IDLE)
sio_c <= ;//空闲状态sioc为1
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
sio_out <= ;
end
else if(state_c == START && div_cnt == CYC/-)
sio_out <= ;//开始条件
else if(state_c == WRI_ID)
sio_out <= idaddr_nc[--bit_cnt];
else if(wri_reg2stop || wri_data2stop || rd_data2stop)
sio_out <= ;
else if(state_c == WRI_REG)
sio_out <= regaddr[--bit_cnt];
else if(state_c == WRI_DATA)
sio_out <= wdata_nc[--bit_cnt];
else if(state_c == RD_DATA && bit_cnt == -)
sio_out <= ;//NACK
else if(state_c == STOP && div_cnt == CYC/- && bit_cnt == )
sio_out <= ;//结束条件
end assign idaddr_nc = {id_rwCtrl,'b1};
assign regaddr = {reg_addr_tmp[:],'b1,reg_addr_tmp[7:0],1'b1};
assign wdata_nc = {wr_data_tmp,'b1}; assign id_rwCtrl = rd_oper ? {id_addr[:],'b1} : id_addr; always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
reg_addr_tmp <= ;
wr_data_tmp <= ;
end
else if(wr_en || rd_en)begin
reg_addr_tmp <= reg_addr;
wr_data_tmp <= wr_data;
end
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
sio_out_en <= ;
end
else begin
case(state_c)
START,STOP:begin
sio_out_en <= ;
end
WRI_ID,WRI_DATA:begin
if(bit_cnt != -)
sio_out_en <= ;
else
sio_out_en <= ;
end
WRI_REG:begin
if(bit_cnt != - && bit_cnt != -)
sio_out_en <= ;
else
sio_out_en <= ;
end
RD_DATA:begin
if(bit_cnt == -)
sio_out_en <= ;
else
sio_out_en <= ;
end
default:sio_out_en <= ;
endcase
end
end //read data
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_data <= ;
end
else if(state_c == RD_DATA && bit_cnt != - && div_cnt == CYC/-)begin
rd_data[--bit_cnt] <= sio_in;
end
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_vld <= ;
end
else if(rd_data2stop)begin
rd_vld <= ;
end
else
rd_vld <= ;
end endmodule

sccb_interface

寄存器配置模块:

 `timescale 1ns / 1ps

 module reg_config(
input clk,
input rst_n, input en,
output finish, inout sio_d,
output sio_c
); localparam WR_ID = 'h78;
localparam RW_CTRL = 'b11;//读
wire sio_out_en;
wire sio_out;
wire sio_in;
reg [-:] reg_cnt;
wire add_reg_cnt,end_reg_cnt;
reg config_flag;
reg [-:] op_reg_data;
wire rdy;
reg wr_en;
reg [-:] reg_addr;
reg [-:] wr_data;
reg config_done;
reg [ (-):] rw_cnt ;
wire add_rw_cnt ;
wire end_rw_cnt ;
reg rd_en;
(*DONT_TOUCH = "TRUE"*)wire [-:] rd_data;
(*DONT_TOUCH = "TRUE"*)wire rd_vld; sccb_interface sccb_interface(
.clk (clk) ,
.rst_n (rst_n) ,
.wr_en (wr_en) ,
.rd_en (rd_en),
.id_addr (WR_ID) ,
.reg_addr (reg_addr) ,
.wr_data (wr_data) ,
.rd_data (rd_data),
.rd_vld (rd_vld),
.rdy (rdy) ,
.sio_c (sio_c) ,
.sio_out_en(sio_out_en) ,
.sio_out (sio_out) ,
.sio_in (sio_in)
); assign sio_d = sio_out_en ? sio_out : 'bz;
assign sio_in = sio_d; always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
rw_cnt <= ;
end
else if(add_rw_cnt) begin
if(end_rw_cnt)
rw_cnt <= ;
else
rw_cnt <= rw_cnt+ ;
end
end
assign add_rw_cnt = (config_flag && rdy);
assign end_rw_cnt = add_rw_cnt && rw_cnt == ()- ;//0 write 1 read always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
reg_cnt <= ;
end
else if(add_reg_cnt)begin
if(end_reg_cnt)
reg_cnt <= ;
else
reg_cnt <= reg_cnt + ;
end
end assign add_reg_cnt = end_rw_cnt;
assign end_reg_cnt = add_reg_cnt && reg_cnt == -; //配置指令
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
wr_en <= ;
reg_addr <= ;
wr_data <= ;
end
else if(add_rw_cnt && rw_cnt == )begin
wr_en <= op_reg_data[];
reg_addr <= op_reg_data[:];
wr_data <= op_reg_data[:];
end
else if(end_rw_cnt)begin
rd_en <= op_reg_data[];
reg_addr <= op_reg_data[:];
end
else begin
wr_en <= ;
rd_en <= ;
end
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
config_flag <= ;
end
else if(en && !config_flag && !config_done)begin
config_flag <= ;
end
else if(end_reg_cnt)
config_flag <= ;
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
config_done <= ;
end
else if(end_reg_cnt)begin
config_done <= ;
end
end assign finish = config_done && rdy; always@(*)
begin//op_reg_data [25] wr [24] rd [23:8] reg_addr [7:0] wr_data
case(reg_cnt)
//15fps VGA YUV output // 24MHz input clock, 24MHz PCLK
:op_reg_data= {RW_CTRL, 'h310311};// system clock from pad, bit[1]
:op_reg_data= {RW_CTRL, 'h300882};// software reset, bit[7]// delay 5ms
:op_reg_data= {RW_CTRL, 'h300842};// software power down, bit[6]
:op_reg_data= {RW_CTRL, 'h310303};// system clock from PLL, bit[1]
:op_reg_data= {RW_CTRL, 'h3017ff};// FREX, Vsync, HREF, PCLK, D[9:6] output enable
:op_reg_data= {RW_CTRL, 'h3018ff};// D[5:0], GPIO[1:0] output enable
:op_reg_data= {RW_CTRL, 'h30341A};// MIPI 10-bit
:op_reg_data= {RW_CTRL, 'h303713};// PLL root divider, bit[4], PLL pre-divider, bit[3:0]
:op_reg_data= {RW_CTRL, 'h310801};// PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2] // SCLK root divider, bit[1:0]
:op_reg_data= {RW_CTRL, 'h363036};
:op_reg_data= {RW_CTRL, 'h36310e};
:op_reg_data= {RW_CTRL, 'h3632e2};
:op_reg_data= {RW_CTRL, 'h363312};
:op_reg_data= {RW_CTRL, 'h3621e0};
:op_reg_data= {RW_CTRL, 'h3704a0};
:op_reg_data= {RW_CTRL, 'h37035a};
:op_reg_data= {RW_CTRL, 'h371578};
:op_reg_data= {RW_CTRL, 'h371701};
:op_reg_data= {RW_CTRL, 'h370b60};
:op_reg_data= {RW_CTRL, 'h37051a};
:op_reg_data= {RW_CTRL, 'h390502};
:op_reg_data= {RW_CTRL, 'h390610};
:op_reg_data= {RW_CTRL, 'h39010a};
:op_reg_data= {RW_CTRL, 'h373112};
:op_reg_data= {RW_CTRL, 'h360008};// VCM control
:op_reg_data= {RW_CTRL, 'h360133};// VCM control
:op_reg_data= {RW_CTRL, 'h302d60};// system control
:op_reg_data= {RW_CTRL, 'h362052};
:op_reg_data= {RW_CTRL, 'h371b20};
:op_reg_data= {RW_CTRL, 'h471c50};
:op_reg_data= {RW_CTRL, 'h3a1343};// pre-gain = 1.047x
:op_reg_data= {RW_CTRL, 'h3a1800};// gain ceiling
:op_reg_data= {RW_CTRL, 'h3a19f8};// gain ceiling = 15.5x
:op_reg_data= {RW_CTRL, 'h363513};
:op_reg_data= {RW_CTRL, 'h363603};
:op_reg_data= {RW_CTRL, 'h363440};
:op_reg_data= {RW_CTRL, 'h362201}; // 50/60Hz detection 50/60Hz 灯光条纹过滤
:op_reg_data= {RW_CTRL, 'h3c0134};// Band auto, bit[7]
:op_reg_data= {RW_CTRL, 'h3c0428};// threshold low sum
:op_reg_data= {RW_CTRL, 'h3c0598};// threshold high sum
:op_reg_data= {RW_CTRL, 'h3c0600};// light meter 1 threshold[15:8]
:op_reg_data= {RW_CTRL, 'h3c0708};// light meter 1 threshold[7:0]
:op_reg_data= {RW_CTRL, 'h3c0800};// light meter 2 threshold[15:8]
:op_reg_data= {RW_CTRL, 'h3c091c};// light meter 2 threshold[7:0]
:op_reg_data= {RW_CTRL, 'h3c0a9c};// sample number[15:8]
:op_reg_data= {RW_CTRL, 'h3c0b40};// sample number[7:0]
:op_reg_data= {RW_CTRL, 'h381000};// Timing Hoffset[11:8]
:op_reg_data= {RW_CTRL, 'h381110};// Timing Hoffset[7:0]
:op_reg_data= {RW_CTRL, 'h381200};// Timing Voffset[10:8]
:op_reg_data= {RW_CTRL, 'h370864};
:op_reg_data= {RW_CTRL, 'h400102};// BLC start from line 2
:op_reg_data= {RW_CTRL, 'h40051a};// BLC always update
:op_reg_data= {RW_CTRL, 'h300000};// enable blocks
:op_reg_data= {RW_CTRL, 'h3004ff};// enable clocks
:op_reg_data= {RW_CTRL, 'h300e58};// MIPI power down, DVP enable
:op_reg_data= {RW_CTRL, 'h302e00};
:op_reg_data= {RW_CTRL, 'h430060};// RGB565
:op_reg_data= {RW_CTRL, 'h501f01};// ISP RGB
:op_reg_data= {RW_CTRL, 'h440e00};
:op_reg_data= {RW_CTRL, 'h5000a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on // AEC target 自动曝光控制
:op_reg_data= {RW_CTRL, 'h3a0f30};// stable range in high
:op_reg_data= {RW_CTRL, 'h3a1028};// stable range in low
:op_reg_data= {RW_CTRL, 'h3a1b30};// stable range out high
:op_reg_data= {RW_CTRL, 'h3a1e26};// stable range out low
:op_reg_data= {RW_CTRL, 'h3a1160};// fast zone high
:op_reg_data= {RW_CTRL, 'h3a1f14};// fast zone low// Lens correction for ? 镜头补偿
:op_reg_data= {RW_CTRL, 'h580023};
:op_reg_data= {RW_CTRL, 'h580114};
:op_reg_data= {RW_CTRL, 'h58020f};
:op_reg_data= {RW_CTRL, 'h58030f};
:op_reg_data= {RW_CTRL, 'h580412};
:op_reg_data= {RW_CTRL, 'h580526};
:op_reg_data= {RW_CTRL, 'h58060c};
:op_reg_data= {RW_CTRL, 'h580708};
:op_reg_data= {RW_CTRL, 'h580805};
:op_reg_data= {RW_CTRL, 'h580905};
:op_reg_data= {RW_CTRL, 'h580a08};
:op_reg_data= {RW_CTRL, 'h580b0d};
:op_reg_data= {RW_CTRL, 'h580c08};
:op_reg_data= {RW_CTRL, 'h580d03};
:op_reg_data= {RW_CTRL, 'h580e00};
:op_reg_data= {RW_CTRL, 'h580f00};
:op_reg_data= {RW_CTRL, 'h581003};
:op_reg_data= {RW_CTRL, 'h581109};
:op_reg_data= {RW_CTRL, 'h581207};
:op_reg_data= {RW_CTRL, 'h581303};
:op_reg_data= {RW_CTRL, 'h581400};
:op_reg_data= {RW_CTRL, 'h581501};
:op_reg_data= {RW_CTRL, 'h581603};
:op_reg_data= {RW_CTRL, 'h581708};
:op_reg_data= {RW_CTRL, 'h58180d};
:op_reg_data= {RW_CTRL, 'h581908};
:op_reg_data= {RW_CTRL, 'h581a05};
:op_reg_data= {RW_CTRL, 'h581b06};
:op_reg_data= {RW_CTRL, 'h581c08};
:op_reg_data= {RW_CTRL, 'h581d0e};
:op_reg_data= {RW_CTRL, 'h581e29};
:op_reg_data= {RW_CTRL, 'h581f17};
:op_reg_data= {RW_CTRL, 'h582011};
:op_reg_data= {RW_CTRL, 'h582111};
:op_reg_data= {RW_CTRL, 'h582215};
:op_reg_data= {RW_CTRL, 'h582328};
:op_reg_data= {RW_CTRL, 'h582446};
:op_reg_data= {RW_CTRL, 'h582526};
:op_reg_data= {RW_CTRL, 'h582608};
:op_reg_data= {RW_CTRL, 'h582726};
:op_reg_data= {RW_CTRL, 'h582864};
:op_reg_data= {RW_CTRL, 'h582926};
:op_reg_data= {RW_CTRL, 'h582a24};
:op_reg_data= {RW_CTRL, 'h582b22};
:op_reg_data= {RW_CTRL, 'h582c24};
:op_reg_data= {RW_CTRL, 'h582d24};
:op_reg_data= {RW_CTRL, 'h582e06};
:op_reg_data= {RW_CTRL, 'h582f22};
:op_reg_data= {RW_CTRL, 'h583040};
:op_reg_data= {RW_CTRL, 'h583142};
:op_reg_data= {RW_CTRL, 'h583224};
:op_reg_data= {RW_CTRL, 'h583326};
:op_reg_data= {RW_CTRL, 'h583424};
:op_reg_data= {RW_CTRL, 'h583522};
:op_reg_data= {RW_CTRL, 'h583622};
:op_reg_data= {RW_CTRL, 'h583726};
:op_reg_data= {RW_CTRL, 'h583844};
:op_reg_data= {RW_CTRL, 'h583924};
:op_reg_data= {RW_CTRL, 'h583a26};
:op_reg_data= {RW_CTRL, 'h583b28};
:op_reg_data= {RW_CTRL, 'h583c42};
:op_reg_data= {RW_CTRL, 'h583dce};// lenc BR offset // AWB 自动白平衡
:op_reg_data= {RW_CTRL, 'h5180ff};// AWB B block
:op_reg_data= {RW_CTRL, 'h5181f2};// AWB control
:op_reg_data= {RW_CTRL, 'h518200};// [7:4] max local counter, [3:0] max fast counter
:op_reg_data= {RW_CTRL, 'h518314};// AWB advanced
:op_reg_data= {RW_CTRL, 'h518425};
:op_reg_data= {RW_CTRL, 'h518524};
:op_reg_data= {RW_CTRL, 'h518609};
:op_reg_data= {RW_CTRL, 'h518709};
:op_reg_data= {RW_CTRL, 'h518809};
:op_reg_data= {RW_CTRL, 'h518975};
:op_reg_data= {RW_CTRL, 'h518a54};
:op_reg_data= {RW_CTRL, 'h518be0};
:op_reg_data= {RW_CTRL, 'h518cb2};
:op_reg_data= {RW_CTRL, 'h518d42};
:op_reg_data= {RW_CTRL, 'h518e3d};
:op_reg_data= {RW_CTRL, 'h518f56};
:op_reg_data= {RW_CTRL, 'h519046};
:op_reg_data= {RW_CTRL, 'h5191f8};// AWB top limit
:op_reg_data= {RW_CTRL, 'h519204};// AWB bottom limit
:op_reg_data= {RW_CTRL, 'h519370};// red limit
:op_reg_data= {RW_CTRL, 'h5194f0};// green limit
:op_reg_data= {RW_CTRL, 'h5195f0};// blue limit
:op_reg_data= {RW_CTRL, 'h519603};// AWB control
:op_reg_data= {RW_CTRL, 'h519701};// local limit
:op_reg_data= {RW_CTRL, 'h519804};
:op_reg_data= {RW_CTRL, 'h519912};
:op_reg_data= {RW_CTRL, 'h519a04};
:op_reg_data= {RW_CTRL, 'h519b00};
:op_reg_data= {RW_CTRL, 'h519c06};
:op_reg_data= {RW_CTRL, 'h519d82};
:op_reg_data= {RW_CTRL, 'h519e38};// AWB control // Gamma 伽玛曲线
:op_reg_data= {RW_CTRL, 'h548001};// Gamma bias plus on, bit[0]
:op_reg_data= {RW_CTRL, 'h548108};
:op_reg_data= {RW_CTRL, 'h548214};
:op_reg_data= {RW_CTRL, 'h548328};
:op_reg_data= {RW_CTRL, 'h548451};
:op_reg_data= {RW_CTRL, 'h548565};
:op_reg_data= {RW_CTRL, 'h548671};
:op_reg_data= {RW_CTRL, 'h54877d};
:op_reg_data= {RW_CTRL, 'h548887};
:op_reg_data= {RW_CTRL, 'h548991};
:op_reg_data= {RW_CTRL, 'h548a9a};
:op_reg_data= {RW_CTRL, 'h548baa};
:op_reg_data= {RW_CTRL, 'h548cb8};
:op_reg_data= {RW_CTRL, 'h548dcd};
:op_reg_data= {RW_CTRL, 'h548edd};
:op_reg_data= {RW_CTRL, 'h548fea};
:op_reg_data= {RW_CTRL, 'h54901d};// color matrix 色彩矩阵
:op_reg_data= {RW_CTRL, 'h53811e};// CMX1 for Y
:op_reg_data= {RW_CTRL, 'h53825b};// CMX2 for Y
:op_reg_data= {RW_CTRL, 'h538308};// CMX3 for Y
:op_reg_data= {RW_CTRL, 'h53840a};// CMX4 for U
:op_reg_data= {RW_CTRL, 'h53857e};// CMX5 for U
:op_reg_data= {RW_CTRL, 'h538688};// CMX6 for U
:op_reg_data= {RW_CTRL, 'h53877c};// CMX7 for V
:op_reg_data= {RW_CTRL, 'h53886c};// CMX8 for V
:op_reg_data= {RW_CTRL, 'h538910};// CMX9 for V
:op_reg_data= {RW_CTRL, 'h538a01};// sign[9]
:op_reg_data= {RW_CTRL, 'h538b98}; // sign[8:1] // UV adjust UV色彩饱和度调整
:op_reg_data= {RW_CTRL, 'h558006};// saturation on, bit[1]
:op_reg_data= {RW_CTRL, 'h558340};
:op_reg_data= {RW_CTRL, 'h558410};
:op_reg_data= {RW_CTRL, 'h558910};
:op_reg_data= {RW_CTRL, 'h558a00};
:op_reg_data= {RW_CTRL, 'h558bf8};
:op_reg_data= {RW_CTRL, 'h501d40};// enable manual offset of contrast// CIP 锐化和降噪
:op_reg_data= {RW_CTRL, 'h530008};// CIP sharpen MT threshold 1
:op_reg_data= {RW_CTRL, 'h530130};// CIP sharpen MT threshold 2
:op_reg_data= {RW_CTRL, 'h530210};// CIP sharpen MT offset 1
:op_reg_data= {RW_CTRL, 'h530300};// CIP sharpen MT offset 2
:op_reg_data= {RW_CTRL, 'h530408};// CIP DNS threshold 1
:op_reg_data= {RW_CTRL, 'h530530};// CIP DNS threshold 2
:op_reg_data= {RW_CTRL, 'h530608};// CIP DNS offset 1
:op_reg_data= {RW_CTRL, 'h530716};// CIP DNS offset 2
:op_reg_data= {RW_CTRL, 'h530908};// CIP sharpen TH threshold 1
:op_reg_data= {RW_CTRL, 'h530a30};// CIP sharpen TH threshold 2
:op_reg_data= {RW_CTRL, 'h530b04};// CIP sharpen TH offset 1
:op_reg_data= {RW_CTRL, 'h530c06};// CIP sharpen TH offset 2
:op_reg_data= {RW_CTRL, 'h502500};
:op_reg_data= {RW_CTRL, 'h300802}; // wake up from standby, bit[6] //set OV5640 to video mode 720p
:op_reg_data= {RW_CTRL, 'h303521};// PLL input clock =24Mhz, PCLK =84Mhz
:op_reg_data= {RW_CTRL, 'h303669};// PLL
:op_reg_data= {RW_CTRL, 'h3c0707}; // lightmeter 1 threshold[7:0]
:op_reg_data= {RW_CTRL, 'h382047}; // flip
:op_reg_data= {RW_CTRL, 'h382100}; // mirror
:op_reg_data= {RW_CTRL, 'h381431}; // timing X inc
:op_reg_data= {RW_CTRL, 'h381531}; // timing Y inc
:op_reg_data= {RW_CTRL, 'h380000}; // HS
:op_reg_data= {RW_CTRL, 'h380100}; // HS
:op_reg_data= {RW_CTRL, 'h380200}; // VS
:op_reg_data= {RW_CTRL, 'h3803fa}; // VS
:op_reg_data= {RW_CTRL, 'h38040a}; // HW (HE)
:op_reg_data= {RW_CTRL, 'h38053f}; // HW (HE)
:op_reg_data= {RW_CTRL, 'h380606}; // VH (VE)
:op_reg_data= {RW_CTRL, 'h3807a9}; // VH (VE)
:op_reg_data= {RW_CTRL, 'h380805}; // DVPHO (1280)
:op_reg_data= {RW_CTRL, 'h380900}; // DVPHO (1280)
:op_reg_data= {RW_CTRL, 'h380a02}; // DVPVO (720)->
:op_reg_data= {RW_CTRL, 'h380bd0}; // DVPVO (720)->
:op_reg_data= {RW_CTRL, 'h380c07}; // HTS
:op_reg_data= {RW_CTRL, 'h380d64}; // HTS
:op_reg_data= {RW_CTRL, 'h380e02}; // VTS
:op_reg_data= {RW_CTRL, 'h380fe4}; // VTS
:op_reg_data= {RW_CTRL, 'h381304}; // timing V offset
:op_reg_data= {RW_CTRL, 'h361800};
:op_reg_data= {RW_CTRL, 'h361229};
:op_reg_data= {RW_CTRL, 'h370952};
:op_reg_data= {RW_CTRL, 'h370c03};
:op_reg_data= {RW_CTRL, 'h3a0202}; // 60Hz max exposure
:op_reg_data= {RW_CTRL, 'h3a03e0}; // 60Hz max exposure
:op_reg_data= {RW_CTRL, 'h3a0800}; // B50 step
:op_reg_data= {RW_CTRL, 'h3a096f}; // B50 step
:op_reg_data= {RW_CTRL, 'h3a0a00}; // B60 step
:op_reg_data= {RW_CTRL, 'h3a0b5c}; // B60 step
:op_reg_data= {RW_CTRL, 'h3a0e06}; // 50Hz max band
:op_reg_data= {RW_CTRL, 'h3a0d08}; // 60Hz max band
:op_reg_data= {RW_CTRL, 'h3a1402}; // 50Hz max exposure
:op_reg_data= {RW_CTRL, 'h3a15e0}; // 50Hz max exposure
:op_reg_data= {RW_CTRL, 'h400402}; // BLC line number
:op_reg_data= {RW_CTRL, 'h30021c}; // reset JFIFO, SFIFO, JPG
:op_reg_data= {RW_CTRL, 'h3006c3}; // disable clock of JPEG2x, JPEG
:op_reg_data= {RW_CTRL, 'h471303}; // JPEG mode 3
:op_reg_data= {RW_CTRL, 'h440704}; // Quantization sacle
:op_reg_data= {RW_CTRL, 'h460b37};
:op_reg_data= {RW_CTRL, 'h460c20};
:op_reg_data= {RW_CTRL, 'h483716}; // MIPI global timing
:op_reg_data= {RW_CTRL, 'h382404}; // PCLK manual divider
:op_reg_data= {RW_CTRL, 'h5001a3}; // SDE on, CMX on, AWB on, scale on
:op_reg_data= {RW_CTRL, 'h350300}; // AEC/AGC on
:op_reg_data= {RW_CTRL, 'h301602}; //Strobe output enable
:op_reg_data= {RW_CTRL, 'h3b070a}; //FREX strobe mode1
//strobe flash and frame exposure
:op_reg_data={RW_CTRL, 'h3b0083}; //STROBE CTRL: strobe request ON, Strobe mode: LED3
:op_reg_data={RW_CTRL, 'h3b0000}; //STROBE CTRL: strobe request OFF default:op_reg_data={RW_CTRL, 'h000000};
endcase
end endmodule

reg_config

上电时序模块:

 `timescale 1ns / 1ps

 module setup(
input clk,//50MHZ
input rst_n,
output reg init_en, output reg ov_pwdn1,
output reg ov_rst_n1,
output reg ov_pwdn2,
output reg ov_rst_n2
); parameter MS_CYC = 50_000; reg [-:] ms_cnt;
wire add_ms_cnt;
wire end_ms_cnt;
reg [ (-):] wait_cnt ;
wire add_wait_cnt ;
wire end_wait_cnt ;
reg [-:] N;
reg [ (-):] phase_cnt ;
wire add_phase_cnt ;
wire end_phase_cnt ;
reg setup_done; //ms计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ms_cnt <= ;
end
else if(add_ms_cnt)begin
if(end_ms_cnt)
ms_cnt <= ;
else
ms_cnt <= ms_cnt + ;
end
end assign add_ms_cnt = !setup_done;
assign end_ms_cnt = add_ms_cnt && ms_cnt== MS_CYC-; //1_000_000/20 = 50_000 always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
wait_cnt <= ;
end
else if(add_wait_cnt) begin
if(end_wait_cnt)
wait_cnt <= ;
else
wait_cnt <= wait_cnt+ ;
end
end
assign add_wait_cnt = (end_ms_cnt);
assign end_wait_cnt = add_wait_cnt && wait_cnt == (N)- ; always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
phase_cnt <= ;
end
else if(add_phase_cnt) begin
if(end_phase_cnt)
phase_cnt <= ;
else
phase_cnt <= phase_cnt+ ;
end
end
assign add_phase_cnt = (end_wait_cnt);
assign end_phase_cnt = add_phase_cnt && phase_cnt == ()- ; always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
setup_done <= ;
end
else if(end_phase_cnt)begin
setup_done <= ;
end
end always@(*)begin
case(phase_cnt)
:N = ;
:N = ;
:N = ;
default:;
endcase
end //信号逻辑
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
ov_pwdn1 <= ;
ov_pwdn2 <= ;
end
else if(add_phase_cnt && phase_cnt == )begin
ov_pwdn1 <= ;
ov_pwdn2 <= ;
end
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
ov_rst_n1 <= ;
ov_rst_n2 <= ;
end
else if(add_phase_cnt && phase_cnt == )begin
ov_rst_n1 <= ;
ov_rst_n2 <= ;
end
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
init_en <= ;
end
else if(end_phase_cnt)begin
init_en <= ;
end
end endmodule

setup  

以下是生成原理图结构及仿真波形:

FPGA配置OV5640摄像头及RGB图像数据采集

FPGA配置OV5640摄像头及RGB图像数据采集

  下面完成最重要的板级调试,先测试SCCB读,再测试SCCB写。  

FPGA配置OV5640摄像头及RGB图像数据采集

FPGA配置OV5640摄像头及RGB图像数据采集

  读状态下SIOD信号有响应,并非所有寄存器数据均为0,说明SCCB_interface模块将摄像头内部寄存器初始值读出。接下来把寄存器配置模块WR_CTRL设置为2'b11,检测读取数据是否与写入一致,从而进一步验证SCCB读写时序。

FPGA配置OV5640摄像头及RGB图像数据采集

FPGA配置OV5640摄像头及RGB图像数据采集

  写入数据状态下,第9bit SIO_D信号被从机拉低,从机收到完整字节数据。这再次说明了SCCB的本质就是IIC。读取数据与写入数据基本一致,说明SCCB读写时序无误。现在在是PCLK时钟域下抓取VSYNC HREF DATA波形,ILA可以稳定启动并捕获到信号与DVP接口时序一致,此时摄像头正常工作。

FPGA配置OV5640摄像头及RGB图像数据采集

FPGA配置OV5640摄像头及RGB图像数据采集

FPGA配置OV5640摄像头及RGB图像数据采集

  配置完成后开始采集RGB数据。根据RGB565时序,每两个字节数据组成一个像素数据,因此采集输出过程中只需将PCLK时钟上升沿得到的两个数据拼接为16bit像素数据即可。