本文设计思想采用明德扬至简设计法。在做摄像头数据采集处理之前,需要配置OV5640传感器内部寄存器使其按要求正常工作,详细内容请参见《OV5640自动对焦照相模组应用指南》。首先要关注OV5640的上电时序:
主控制器控制RESET PWDN两个信号按上电时序要求变化,之后允许ov_config模块配置内部寄存器。这里始终将PWDN拉低。实验中将摄像头分辨率设置为720p,即1280*720 ,帧率为30fps,图像输出格式是RGB565。此时摄像头输入时钟XCLK频率24MHz,输出像素时钟PCLK为84MHz。由于实验使用的是OV5640双目摄像头模组,且XCLK由外部24MHz晶振给出,故ov_config模块整体结构及端口定义如下:
setup模块构造上电时序,两个reg_config分别配置一个OV5640摄像头。SCCB_interface子模块负责SCCB协议读写寄存器数据。由于OV5640摄像头内部寄存器地址为16位,因此写寄存器地址阶段分高低字节两次写入。datasheet中给出了OV5640的SCCB ID地址(写),故读ID地址为0X79。
SCCB时序图及AC characteristics如下:
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对应两相写和两相读。
上代码:
SCCB读写模块:
`timescale 1ns / 1ps
module sccb_interface(
input clk,
input rst_n,
input wr_en,
input rd_en,
input [8-1:0] id_addr,
input [16-1:0] reg_addr,
input [8-1:0] wr_data,
output reg [8-1:0] 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 = 500;
localparam IDLE = 0 ;
localparam START = 1 ;
localparam WRI_ID = 2 ;
localparam WRI_REG = 3 ;
localparam WRI_DATA = 4;
localparam RD_DATA = 5;
localparam STOP = 6 ;
//计数器
reg [ (9-1):0] div_cnt ;
wire add_div_cnt ;
wire end_div_cnt ;
reg [ (5-1):0] bit_cnt ;
wire add_bit_cnt ;
wire end_bit_cnt ;
reg [5-1:0] N;
(*DONT_TOUCH = "TRUE"*)reg [7-1:0] 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 [18-1:0] regaddr;
reg [16-1:0] reg_addr_tmp;
reg [8-1:0] wr_data_tmp;
wire [9-1:0] idaddr_nc;
reg [8-1:0] id_addr_tmp;
reg rd_oper,rd_flag;
wire [9-1:0] wdata_nc;
wire [8-1:0] id_rwCtrl;
assign rdy = state_c == IDLE && !wr_en && !rd_en;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
div_cnt <= 0;
end
else if(add_div_cnt) begin
if(end_div_cnt)
div_cnt <= 0;
else
div_cnt <= div_cnt+1 ;
end
end
assign add_div_cnt = (state_c != IDLE);
assign end_div_cnt = add_div_cnt && div_cnt == (CYC)-1 ;//5000ns,200KHZ
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
bit_cnt <= 0;
end
else if(add_bit_cnt) begin
if(end_bit_cnt)
bit_cnt <= 0;
else
bit_cnt <= bit_cnt+1 ;
end
end
assign add_bit_cnt = (end_div_cnt);
assign end_bit_cnt = add_bit_cnt && bit_cnt == (N)-1 ;
always@(*)begin
case(state_c)
START: N = 1;
WRI_REG: N = 18; //(8+1)*2 = 18
WRI_ID,WRI_DATA,RD_DATA:N = 9;//8+1
STOP: N = 2;
default:;
endcase
end
//FSM:IDLE START WRI_ID WRI_REG STOP
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) 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 //0
if(idle2start)
state_n = START ;
else
state_n = state_c ;
end
START :begin //1
if(start2wri_id)
state_n = WRI_ID ;
else
state_n = state_c ;
end
WRI_ID :begin //2
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 //3
if(wri_reg2wri_data)
state_n = WRI_DATA;
else if(wri_reg2stop)
state_n = STOP ;
else
state_n = state_c ;
end
WRI_DATA:begin //4
if(wri_data2stop)
state_n = STOP;
else
state_n = state_c;
end
RD_DATA:begin //5
if(rd_data2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP :begin //6
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==1'b0)begin
rd_oper <= 0;
end
else if(stop2start)begin
rd_oper <= 1;
end
else if(rd_oper && stop2idle)
rd_oper <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_flag <= 0;
end
else if(idle2start && rd_en)begin
rd_flag <= 1;
end
else if(stop2idle)
rd_flag <= 0;
end
//SCCB时钟
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
sio_c <= 1'b1;
else if(add_div_cnt && div_cnt == CYC/4-1)
sio_c <= 1;
else if(add_div_cnt && div_cnt == CYC/4+CYC/2-1)
sio_c <= 0;
else if(state_c == IDLE)
sio_c <= 1;//空闲状态sioc为1
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
sio_out <= 1;
end
else if(state_c == START && div_cnt == CYC/2-1)
sio_out <= 0;//开始条件
else if(state_c == WRI_ID)
sio_out <= idaddr_nc[9-1-bit_cnt];
else if(wri_reg2stop || wri_data2stop || rd_data2stop)
sio_out <= 0;
else if(state_c == WRI_REG)
sio_out <= regaddr[18-1-bit_cnt];
else if(state_c == WRI_DATA)
sio_out <= wdata_nc[9-1-bit_cnt];
else if(state_c == RD_DATA && bit_cnt == 9-1)
sio_out <= 1;//NACK
else if(state_c == STOP && div_cnt == CYC/2-1 && bit_cnt == 0)
sio_out <= 1;//结束条件
end
assign idaddr_nc = {id_rwCtrl,1'b1};
assign regaddr = {reg_addr_tmp[15:8],1'b1,reg_addr_tmp[7:0],1'b1};
assign wdata_nc = {wr_data_tmp,1'b1};
assign id_rwCtrl = rd_oper ? {id_addr[7:1],1'b1} : id_addr;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
reg_addr_tmp <= 0;
wr_data_tmp <= 0;
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==1'b0)begin
sio_out_en <= 0;
end
else begin
case(state_c)
START,STOP:begin
sio_out_en <= 1;
end
WRI_ID,WRI_DATA:begin
if(bit_cnt != 9-1)
sio_out_en <= 1;
else
sio_out_en <= 0;
end
WRI_REG:begin
if(bit_cnt != 9-1 && bit_cnt != 18-1)
sio_out_en <= 1;
else
sio_out_en <= 0;
end
RD_DATA:begin
if(bit_cnt == 9-1)
sio_out_en <= 1;
else
sio_out_en <= 0;
end
default:sio_out_en <= 0;
endcase
end
end
//read data
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_data <= 0;
end
else if(state_c == RD_DATA && bit_cnt != 9-1 && div_cnt == CYC/2-1)begin
rd_data[8-1-bit_cnt] <= sio_in;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_vld <= 0;
end
else if(rd_data2stop)begin
rd_vld <= 1;
end
else
rd_vld <= 0;
end
endmodule
sccb_interface
寄存器配置模块:
reg_config`timescale 1ns / 1ps
module reg_config(
input clk,
input rst_n,
input en,
output finish,
inout sio_d,
output sio_c
);
localparam WR_ID = 8'h78;
localparam RW_CTRL = 2'b11;//读
wire sio_out_en;
wire sio_out;
wire sio_in;
reg [9-1:0] reg_cnt;
wire add_reg_cnt,end_reg_cnt;
reg config_flag;
reg [26-1:0] op_reg_data;
wire rdy;
reg wr_en;
reg [16-1:0] reg_addr;
reg [8-1:0] wr_data;
reg config_done;
reg [ (2-1):0] rw_cnt ;
wire add_rw_cnt ;
wire end_rw_cnt ;
reg rd_en;
(*DONT_TOUCH = "TRUE"*)wire [8-1:0] 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 : 1'bz;
assign sio_in = sio_d;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
rw_cnt <= 0;
end
else if(add_rw_cnt) begin
if(end_rw_cnt)
rw_cnt <= 0;
else
rw_cnt <= rw_cnt+1 ;
end
end
assign add_rw_cnt = (config_flag && rdy);
assign end_rw_cnt = add_rw_cnt && rw_cnt == (2)-1 ;//0 write 1 read
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
reg_cnt <= 0;
end
else if(add_reg_cnt)begin
if(end_reg_cnt)
reg_cnt <= 0;
else
reg_cnt <= reg_cnt + 1;
end
end
assign add_reg_cnt = end_rw_cnt;
assign end_reg_cnt = add_reg_cnt && reg_cnt == 261-1;
//配置指令
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
wr_en <= 0;
reg_addr <= 0;
wr_data <= 0;
end
else if(add_rw_cnt && rw_cnt == 0)begin
wr_en <= op_reg_data[25];
reg_addr <= op_reg_data[23:8];
wr_data <= op_reg_data[7:0];
end
else if(end_rw_cnt)begin
rd_en <= op_reg_data[24];
reg_addr <= op_reg_data[23:8];
end
else begin
wr_en <= 0;
rd_en <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
config_flag <= 0;
end
else if(en && !config_flag && !config_done)begin
config_flag <= 1;
end
else if(end_reg_cnt)
config_flag <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
config_done <= 0;
end
else if(end_reg_cnt)begin
config_done <= 1;
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
0:op_reg_data= {RW_CTRL, 24'h310311};// system clock from pad, bit[1]
1:op_reg_data= {RW_CTRL, 24'h300882};// software reset, bit[7]// delay 5ms
2:op_reg_data= {RW_CTRL, 24'h300842};// software power down, bit[6]
3:op_reg_data= {RW_CTRL, 24'h310303};// system clock from PLL, bit[1]
4:op_reg_data= {RW_CTRL, 24'h3017ff};// FREX, Vsync, HREF, PCLK, D[9:6] output enable
5:op_reg_data= {RW_CTRL, 24'h3018ff};// D[5:0], GPIO[1:0] output enable
6:op_reg_data= {RW_CTRL, 24'h30341A};// MIPI 10-bit
7:op_reg_data= {RW_CTRL, 24'h303713};// PLL root divider, bit[4], PLL pre-divider, bit[3:0]
8:op_reg_data= {RW_CTRL, 24'h310801};// PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2] // SCLK root divider, bit[1:0]
9:op_reg_data= {RW_CTRL, 24'h363036};
10:op_reg_data= {RW_CTRL, 24'h36310e};
11:op_reg_data= {RW_CTRL, 24'h3632e2};
12:op_reg_data= {RW_CTRL, 24'h363312};
13:op_reg_data= {RW_CTRL, 24'h3621e0};
14:op_reg_data= {RW_CTRL, 24'h3704a0};
15:op_reg_data= {RW_CTRL, 24'h37035a};
16:op_reg_data= {RW_CTRL, 24'h371578};
17:op_reg_data= {RW_CTRL, 24'h371701};
18:op_reg_data= {RW_CTRL, 24'h370b60};
19:op_reg_data= {RW_CTRL, 24'h37051a};
20:op_reg_data= {RW_CTRL, 24'h390502};
21:op_reg_data= {RW_CTRL, 24'h390610};
22:op_reg_data= {RW_CTRL, 24'h39010a};
23:op_reg_data= {RW_CTRL, 24'h373112};
24:op_reg_data= {RW_CTRL, 24'h360008};// VCM control
25:op_reg_data= {RW_CTRL, 24'h360133};// VCM control
26:op_reg_data= {RW_CTRL, 24'h302d60};// system control
27:op_reg_data= {RW_CTRL, 24'h362052};
28:op_reg_data= {RW_CTRL, 24'h371b20};
29:op_reg_data= {RW_CTRL, 24'h471c50};
30:op_reg_data= {RW_CTRL, 24'h3a1343};// pre-gain = 1.047x
31:op_reg_data= {RW_CTRL, 24'h3a1800};// gain ceiling
32:op_reg_data= {RW_CTRL, 24'h3a19f8};// gain ceiling = 15.5x
33:op_reg_data= {RW_CTRL, 24'h363513};
34:op_reg_data= {RW_CTRL, 24'h363603};
35:op_reg_data= {RW_CTRL, 24'h363440};
36:op_reg_data= {RW_CTRL, 24'h362201}; // 50/60Hz detection 50/60Hz 灯光条纹过滤
37:op_reg_data= {RW_CTRL, 24'h3c0134};// Band auto, bit[7]
38:op_reg_data= {RW_CTRL, 24'h3c0428};// threshold low sum
39:op_reg_data= {RW_CTRL, 24'h3c0598};// threshold high sum
40:op_reg_data= {RW_CTRL, 24'h3c0600};// light meter 1 threshold[15:8]
41:op_reg_data= {RW_CTRL, 24'h3c0708};// light meter 1 threshold[7:0]
42:op_reg_data= {RW_CTRL, 24'h3c0800};// light meter 2 threshold[15:8]
43:op_reg_data= {RW_CTRL, 24'h3c091c};// light meter 2 threshold[7:0]
44:op_reg_data= {RW_CTRL, 24'h3c0a9c};// sample number[15:8]
45:op_reg_data= {RW_CTRL, 24'h3c0b40};// sample number[7:0]
46:op_reg_data= {RW_CTRL, 24'h381000};// Timing Hoffset[11:8]
47:op_reg_data= {RW_CTRL, 24'h381110};// Timing Hoffset[7:0]
48:op_reg_data= {RW_CTRL, 24'h381200};// Timing Voffset[10:8]
49:op_reg_data= {RW_CTRL, 24'h370864};
50:op_reg_data= {RW_CTRL, 24'h400102};// BLC start from line 2
51:op_reg_data= {RW_CTRL, 24'h40051a};// BLC always update
52:op_reg_data= {RW_CTRL, 24'h300000};// enable blocks
53:op_reg_data= {RW_CTRL, 24'h3004ff};// enable clocks
54:op_reg_data= {RW_CTRL, 24'h300e58};// MIPI power down, DVP enable
55:op_reg_data= {RW_CTRL, 24'h302e00};
56:op_reg_data= {RW_CTRL, 24'h430060};// RGB565
57:op_reg_data= {RW_CTRL, 24'h501f01};// ISP RGB
58:op_reg_data= {RW_CTRL, 24'h440e00};
59:op_reg_data= {RW_CTRL, 24'h5000a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on // AEC target 自动曝光控制
60:op_reg_data= {RW_CTRL, 24'h3a0f30};// stable range in high
61:op_reg_data= {RW_CTRL, 24'h3a1028};// stable range in low
62:op_reg_data= {RW_CTRL, 24'h3a1b30};// stable range out high
63:op_reg_data= {RW_CTRL, 24'h3a1e26};// stable range out low
64:op_reg_data= {RW_CTRL, 24'h3a1160};// fast zone high
65:op_reg_data= {RW_CTRL, 24'h3a1f14};// fast zone low// Lens correction for ? 镜头补偿
66:op_reg_data= {RW_CTRL, 24'h580023};
67:op_reg_data= {RW_CTRL, 24'h580114};
68:op_reg_data= {RW_CTRL, 24'h58020f};
69:op_reg_data= {RW_CTRL, 24'h58030f};
70:op_reg_data= {RW_CTRL, 24'h580412};
71:op_reg_data= {RW_CTRL, 24'h580526};
72:op_reg_data= {RW_CTRL, 24'h58060c};
73:op_reg_data= {RW_CTRL, 24'h580708};
74:op_reg_data= {RW_CTRL, 24'h580805};
75:op_reg_data= {RW_CTRL, 24'h580905};
76:op_reg_data= {RW_CTRL, 24'h580a08};
77:op_reg_data= {RW_CTRL, 24'h580b0d};
78:op_reg_data= {RW_CTRL, 24'h580c08};
79:op_reg_data= {RW_CTRL, 24'h580d03};
80:op_reg_data= {RW_CTRL, 24'h580e00};
81:op_reg_data= {RW_CTRL, 24'h580f00};
82:op_reg_data= {RW_CTRL, 24'h581003};
83:op_reg_data= {RW_CTRL, 24'h581109};
84:op_reg_data= {RW_CTRL, 24'h581207};
85:op_reg_data= {RW_CTRL, 24'h581303};
86:op_reg_data= {RW_CTRL, 24'h581400};
87:op_reg_data= {RW_CTRL, 24'h581501};
88:op_reg_data= {RW_CTRL, 24'h581603};
89:op_reg_data= {RW_CTRL, 24'h581708};
90:op_reg_data= {RW_CTRL, 24'h58180d};
91:op_reg_data= {RW_CTRL, 24'h581908};
92:op_reg_data= {RW_CTRL, 24'h581a05};
93:op_reg_data= {RW_CTRL, 24'h581b06};
94:op_reg_data= {RW_CTRL, 24'h581c08};
95:op_reg_data= {RW_CTRL, 24'h581d0e};
96:op_reg_data= {RW_CTRL, 24'h581e29};
97:op_reg_data= {RW_CTRL, 24'h581f17};
98:op_reg_data= {RW_CTRL, 24'h582011};
99:op_reg_data= {RW_CTRL, 24'h582111};
100:op_reg_data= {RW_CTRL, 24'h582215};
101:op_reg_data= {RW_CTRL, 24'h582328};
102:op_reg_data= {RW_CTRL, 24'h582446};
103:op_reg_data= {RW_CTRL, 24'h582526};
104:op_reg_data= {RW_CTRL, 24'h582608};
105:op_reg_data= {RW_CTRL, 24'h582726};
106:op_reg_data= {RW_CTRL, 24'h582864};
107:op_reg_data= {RW_CTRL, 24'h582926};
108:op_reg_data= {RW_CTRL, 24'h582a24};
109:op_reg_data= {RW_CTRL, 24'h582b22};
110:op_reg_data= {RW_CTRL, 24'h582c24};
111:op_reg_data= {RW_CTRL, 24'h582d24};
112:op_reg_data= {RW_CTRL, 24'h582e06};
113:op_reg_data= {RW_CTRL, 24'h582f22};
114:op_reg_data= {RW_CTRL, 24'h583040};
115:op_reg_data= {RW_CTRL, 24'h583142};
116:op_reg_data= {RW_CTRL, 24'h583224};
117:op_reg_data= {RW_CTRL, 24'h583326};
118:op_reg_data= {RW_CTRL, 24'h583424};
119:op_reg_data= {RW_CTRL, 24'h583522};
120:op_reg_data= {RW_CTRL, 24'h583622};
121:op_reg_data= {RW_CTRL, 24'h583726};
122:op_reg_data= {RW_CTRL, 24'h583844};
123:op_reg_data= {RW_CTRL, 24'h583924};
124:op_reg_data= {RW_CTRL, 24'h583a26};
125:op_reg_data= {RW_CTRL, 24'h583b28};
126:op_reg_data= {RW_CTRL, 24'h583c42};
127:op_reg_data= {RW_CTRL, 24'h583dce};// lenc BR offset // AWB 自动白平衡
128:op_reg_data= {RW_CTRL, 24'h5180ff};// AWB B block
129:op_reg_data= {RW_CTRL, 24'h5181f2};// AWB control
130:op_reg_data= {RW_CTRL, 24'h518200};// [7:4] max local counter, [3:0] max fast counter
131:op_reg_data= {RW_CTRL, 24'h518314};// AWB advanced
132:op_reg_data= {RW_CTRL, 24'h518425};
133:op_reg_data= {RW_CTRL, 24'h518524};
134:op_reg_data= {RW_CTRL, 24'h518609};
135:op_reg_data= {RW_CTRL, 24'h518709};
136:op_reg_data= {RW_CTRL, 24'h518809};
137:op_reg_data= {RW_CTRL, 24'h518975};
138:op_reg_data= {RW_CTRL, 24'h518a54};
139:op_reg_data= {RW_CTRL, 24'h518be0};
140:op_reg_data= {RW_CTRL, 24'h518cb2};
141:op_reg_data= {RW_CTRL, 24'h518d42};
142:op_reg_data= {RW_CTRL, 24'h518e3d};
143:op_reg_data= {RW_CTRL, 24'h518f56};
144:op_reg_data= {RW_CTRL, 24'h519046};
145:op_reg_data= {RW_CTRL, 24'h5191f8};// AWB top limit
146:op_reg_data= {RW_CTRL, 24'h519204};// AWB bottom limit
147:op_reg_data= {RW_CTRL, 24'h519370};// red limit
148:op_reg_data= {RW_CTRL, 24'h5194f0};// green limit
149:op_reg_data= {RW_CTRL, 24'h5195f0};// blue limit
150:op_reg_data= {RW_CTRL, 24'h519603};// AWB control
151:op_reg_data= {RW_CTRL, 24'h519701};// local limit
152:op_reg_data= {RW_CTRL, 24'h519804};
153:op_reg_data= {RW_CTRL, 24'h519912};
154:op_reg_data= {RW_CTRL, 24'h519a04};
155:op_reg_data= {RW_CTRL, 24'h519b00};
156:op_reg_data= {RW_CTRL, 24'h519c06};
157:op_reg_data= {RW_CTRL, 24'h519d82};
158:op_reg_data= {RW_CTRL, 24'h519e38};// AWB control // Gamma 伽玛曲线
159:op_reg_data= {RW_CTRL, 24'h548001};// Gamma bias plus on, bit[0]
160:op_reg_data= {RW_CTRL, 24'h548108};
161:op_reg_data= {RW_CTRL, 24'h548214};
162:op_reg_data= {RW_CTRL, 24'h548328};
163:op_reg_data= {RW_CTRL, 24'h548451};
164:op_reg_data= {RW_CTRL, 24'h548565};
165:op_reg_data= {RW_CTRL, 24'h548671};
166:op_reg_data= {RW_CTRL, 24'h54877d};
167:op_reg_data= {RW_CTRL, 24'h548887};
168:op_reg_data= {RW_CTRL, 24'h548991};
169:op_reg_data= {RW_CTRL, 24'h548a9a};
170:op_reg_data= {RW_CTRL, 24'h548baa};
171:op_reg_data= {RW_CTRL, 24'h548cb8};
172:op_reg_data= {RW_CTRL, 24'h548dcd};
173:op_reg_data= {RW_CTRL, 24'h548edd};
174:op_reg_data= {RW_CTRL, 24'h548fea};
175:op_reg_data= {RW_CTRL, 24'h54901d};// color matrix 色彩矩阵
176:op_reg_data= {RW_CTRL, 24'h53811e};// CMX1 for Y
177:op_reg_data= {RW_CTRL, 24'h53825b};// CMX2 for Y
178:op_reg_data= {RW_CTRL, 24'h538308};// CMX3 for Y
179:op_reg_data= {RW_CTRL, 24'h53840a};// CMX4 for U
180:op_reg_data= {RW_CTRL, 24'h53857e};// CMX5 for U
181:op_reg_data= {RW_CTRL, 24'h538688};// CMX6 for U
182:op_reg_data= {RW_CTRL, 24'h53877c};// CMX7 for V
183:op_reg_data= {RW_CTRL, 24'h53886c};// CMX8 for V
184:op_reg_data= {RW_CTRL, 24'h538910};// CMX9 for V
185:op_reg_data= {RW_CTRL, 24'h538a01};// sign[9]
186:op_reg_data= {RW_CTRL, 24'h538b98}; // sign[8:1] // UV adjust UV色彩饱和度调整
187:op_reg_data= {RW_CTRL, 24'h558006};// saturation on, bit[1]
188:op_reg_data= {RW_CTRL, 24'h558340};
189:op_reg_data= {RW_CTRL, 24'h558410};
190:op_reg_data= {RW_CTRL, 24'h558910};
191:op_reg_data= {RW_CTRL, 24'h558a00};
192:op_reg_data= {RW_CTRL, 24'h558bf8};
193:op_reg_data= {RW_CTRL, 24'h501d40};// enable manual offset of contrast// CIP 锐化和降噪
194:op_reg_data= {RW_CTRL, 24'h530008};// CIP sharpen MT threshold 1
195:op_reg_data= {RW_CTRL, 24'h530130};// CIP sharpen MT threshold 2
196:op_reg_data= {RW_CTRL, 24'h530210};// CIP sharpen MT offset 1
197:op_reg_data= {RW_CTRL, 24'h530300};// CIP sharpen MT offset 2
198:op_reg_data= {RW_CTRL, 24'h530408};// CIP DNS threshold 1
199:op_reg_data= {RW_CTRL, 24'h530530};// CIP DNS threshold 2
200:op_reg_data= {RW_CTRL, 24'h530608};// CIP DNS offset 1
201:op_reg_data= {RW_CTRL, 24'h530716};// CIP DNS offset 2
202:op_reg_data= {RW_CTRL, 24'h530908};// CIP sharpen TH threshold 1
203:op_reg_data= {RW_CTRL, 24'h530a30};// CIP sharpen TH threshold 2
204:op_reg_data= {RW_CTRL, 24'h530b04};// CIP sharpen TH offset 1
205:op_reg_data= {RW_CTRL, 24'h530c06};// CIP sharpen TH offset 2
206:op_reg_data= {RW_CTRL, 24'h502500};
207:op_reg_data= {RW_CTRL, 24'h300802}; // wake up from standby, bit[6]
//set OV5640 to video mode 720p
208:op_reg_data= {RW_CTRL, 24'h303521};// PLL input clock =24Mhz, PCLK =84Mhz
209:op_reg_data= {RW_CTRL, 24'h303669};// PLL
210:op_reg_data= {RW_CTRL, 24'h3c0707}; // lightmeter 1 threshold[7:0]
211:op_reg_data= {RW_CTRL, 24'h382047}; // flip
212:op_reg_data= {RW_CTRL, 24'h382100}; // mirror
213:op_reg_data= {RW_CTRL, 24'h381431}; // timing X inc
214:op_reg_data= {RW_CTRL, 24'h381531}; // timing Y inc
215:op_reg_data= {RW_CTRL, 24'h380000}; // HS
216:op_reg_data= {RW_CTRL, 24'h380100}; // HS
217:op_reg_data= {RW_CTRL, 24'h380200}; // VS
218:op_reg_data= {RW_CTRL, 24'h3803fa}; // VS
219:op_reg_data= {RW_CTRL, 24'h38040a}; // HW (HE)
220:op_reg_data= {RW_CTRL, 24'h38053f}; // HW (HE)
221:op_reg_data= {RW_CTRL, 24'h380606}; // VH (VE)
222:op_reg_data= {RW_CTRL, 24'h3807a9}; // VH (VE)
223:op_reg_data= {RW_CTRL, 24'h380805}; // DVPHO (1280)
224:op_reg_data= {RW_CTRL, 24'h380900}; // DVPHO (1280)
225:op_reg_data= {RW_CTRL, 24'h380a02}; // DVPVO (720)->
226:op_reg_data= {RW_CTRL, 24'h380bd0}; // DVPVO (720)->
227:op_reg_data= {RW_CTRL, 24'h380c07}; // HTS
228:op_reg_data= {RW_CTRL, 24'h380d64}; // HTS
229:op_reg_data= {RW_CTRL, 24'h380e02}; // VTS
230:op_reg_data= {RW_CTRL, 24'h380fe4}; // VTS
231:op_reg_data= {RW_CTRL, 24'h381304}; // timing V offset
232:op_reg_data= {RW_CTRL, 24'h361800};
233:op_reg_data= {RW_CTRL, 24'h361229};
234:op_reg_data= {RW_CTRL, 24'h370952};
235:op_reg_data= {RW_CTRL, 24'h370c03};
236:op_reg_data= {RW_CTRL, 24'h3a0202}; // 60Hz max exposure
237:op_reg_data= {RW_CTRL, 24'h3a03e0}; // 60Hz max exposure
238:op_reg_data= {RW_CTRL, 24'h3a0800}; // B50 step
239:op_reg_data= {RW_CTRL, 24'h3a096f}; // B50 step
240:op_reg_data= {RW_CTRL, 24'h3a0a00}; // B60 step
241:op_reg_data= {RW_CTRL, 24'h3a0b5c}; // B60 step
242:op_reg_data= {RW_CTRL, 24'h3a0e06}; // 50Hz max band
243:op_reg_data= {RW_CTRL, 24'h3a0d08}; // 60Hz max band
244:op_reg_data= {RW_CTRL, 24'h3a1402}; // 50Hz max exposure
245:op_reg_data= {RW_CTRL, 24'h3a15e0}; // 50Hz max exposure
246:op_reg_data= {RW_CTRL, 24'h400402}; // BLC line number
247:op_reg_data= {RW_CTRL, 24'h30021c}; // reset JFIFO, SFIFO, JPG
248:op_reg_data= {RW_CTRL, 24'h3006c3}; // disable clock of JPEG2x, JPEG
249:op_reg_data= {RW_CTRL, 24'h471303}; // JPEG mode 3
250:op_reg_data= {RW_CTRL, 24'h440704}; // Quantization sacle
251:op_reg_data= {RW_CTRL, 24'h460b37};
252:op_reg_data= {RW_CTRL, 24'h460c20};
253:op_reg_data= {RW_CTRL, 24'h483716}; // MIPI global timing
254:op_reg_data= {RW_CTRL, 24'h382404}; // PCLK manual divider
255:op_reg_data= {RW_CTRL, 24'h5001a3}; // SDE on, CMX on, AWB on, scale on
256:op_reg_data= {RW_CTRL, 24'h350300}; // AEC/AGC on
257:op_reg_data= {RW_CTRL, 24'h301602}; //Strobe output enable
258:op_reg_data= {RW_CTRL, 24'h3b070a}; //FREX strobe mode1
//strobe flash and frame exposure
259:op_reg_data={RW_CTRL, 24'h3b0083}; //STROBE CTRL: strobe request ON, Strobe mode: LED3
260:op_reg_data={RW_CTRL, 24'h3b0000}; //STROBE CTRL: strobe request OFF
default:op_reg_data={RW_CTRL, 24'h000000};
endcase
end
endmodule
reg_config
上电时序模块:
setup`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 [16-1:0] ms_cnt;
wire add_ms_cnt;
wire end_ms_cnt;
reg [ (5-1):0] wait_cnt ;
wire add_wait_cnt ;
wire end_wait_cnt ;
reg [5-1:0] N;
reg [ (2-1):0] 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 <= 0;
end
else if(add_ms_cnt)begin
if(end_ms_cnt)
ms_cnt <= 0;
else
ms_cnt <= ms_cnt + 1;
end
end
assign add_ms_cnt = !setup_done;
assign end_ms_cnt = add_ms_cnt && ms_cnt== MS_CYC-1; //1_000_000/20 = 50_000
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
wait_cnt <= 0;
end
else if(add_wait_cnt) begin
if(end_wait_cnt)
wait_cnt <= 0;
else
wait_cnt <= wait_cnt+1 ;
end
end
assign add_wait_cnt = (end_ms_cnt);
assign end_wait_cnt = add_wait_cnt && wait_cnt == (N)-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
phase_cnt <= 0;
end
else if(add_phase_cnt) begin
if(end_phase_cnt)
phase_cnt <= 0;
else
phase_cnt <= phase_cnt+1 ;
end
end
assign add_phase_cnt = (end_wait_cnt);
assign end_phase_cnt = add_phase_cnt && phase_cnt == (3)-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
setup_done <= 0;
end
else if(end_phase_cnt)begin
setup_done <= 1;
end
end
always@(*)begin
case(phase_cnt)
0:N = 5;
1:N = 2;
2:N = 21;
default:;
endcase
end
//信号逻辑
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
ov_pwdn1 <= 1;
ov_pwdn2 <= 1;
end
else if(add_phase_cnt && phase_cnt == 0)begin
ov_pwdn1 <= 0;
ov_pwdn2 <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
ov_rst_n1 <= 0;
ov_rst_n2 <= 0;
end
else if(add_phase_cnt && phase_cnt == 1)begin
ov_rst_n1 <= 1;
ov_rst_n2 <= 1;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
init_en <= 0;
end
else if(end_phase_cnt)begin
init_en <= 1;
end
end
endmodule
setup
以下是生成原理图结构及仿真波形:
下面完成最重要的板级调试,先测试SCCB读,再测试SCCB写。
读状态下SIOD信号有响应,并非所有寄存器数据均为0,说明SCCB_interface模块将摄像头内部寄存器初始值读出。接下来把寄存器配置模块WR_CTRL设置为2'b11,检测读取数据是否与写入一致,从而进一步验证SCCB读写时序。
写入数据状态下,第9bit SIO_D信号被从机拉低,从机收到完整字节数据。这再次说明了SCCB的本质就是IIC。读取数据与写入数据基本一致,说明SCCB读写时序无误。现在在是PCLK时钟域下抓取VSYNC HREF DATA波形,ILA可以稳定启动并捕获到信号与DVP接口时序一致,此时摄像头正常工作。
配置完成后开始采集RGB数据。根据RGB565时序,每两个字节数据组成一个像素数据,因此采集输出过程中只需将PCLK时钟上升沿得到的两个数据拼接为16bit像素数据即可。