FPGA——OV5640摄像头初始化实现及仿真
一、设计思路
1、步骤
摄像头有一个上电复位信号(低有效)
摄像头在系统复位之后,摄像头上电复位信号要保持稳定的低电平至少1ms
1ms之后之后,拉高上电复位信号
拉高上电复位信号后20ms之后,摄像头才能进入工作模式
进入工作模式之后,通过SCCB将初始化的参数写入摄像头寄存器内
摄像头初始化完成
上电复位信号延迟计数器,参数ROM地址计数器
2、仿真方法
因为SCCB协议是由IIC协议移植过来的,而IIC有应答位,而SCCB是无关项,虽然板级验证是没有问题的,但是在仿真过程中IIC状态机是要接收ACK才可以正常工作的,所有要将IIC代码中的ACK信号改一下
sda == 0 改成 sda == 1
二、摄像头初始化代码
module camera_init(
clk ,
rst_n ,
init_done , //摄像头初始化完成标志信号
camera_rst_n,
camera_pwdn , //ov5640掉电控制信号
scl , //sccb 时钟信号
sda //sccb 串行数据信号
);
localparam RGB = 0;
localparam JPEG = 1;
localparam LUT_W = 8;
localparam Q_W = 24;
parameter IMAGE_FLIP = 1'b0; //图像上下翻转,高有效
parameter IMAGE_MIRROR = 1'b0; //图像左右镜像,高有效
parameter IMAGE_TYPE = RGB; //输出图像类型
parameter IMAGE_WIDTH = 800; //图像宽度
parameter IMAGE_HEIGHT = 480; //图像高度
parameter DEVICE_ADDR = 8'h78;//摄像头器件地址
//计数器参数
localparam CNTRST_N = 1050000;
// localparam CNTRST_N = 21'd1100000;
localparam CNTRST_W = 21;
localparam UP_N = 50000;//摄像头复位信号拉高时间点
// localparam UP_N = 21'd100000;
localparam CNTADDR_W = LUT_W;
input clk;
input rst_n;
output init_done;
output camera_pwdn;
output camera_rst_n;
output scl;
inout sda;
reg init_done;
wire camera_pwdn;
reg camera_rst_n;
wire scl;
wire sda;
//中间变量
wire [LUT_W-1:0] lut_max_addr; //不同图像输出格式下查找表最大地址
reg work_flag;
wire [Q_W-1:0] q;
reg rst_done;
//计数器变量
reg [CNTRST_W-1:0] cnt_camrst;
wire add_cnt_camrst;
wire end_cnt_camrst;
reg [CNTADDR_W-1:0]cnt_addr;
wire add_cnt_addr;
wire end_cnt_addr;
//SCCB模块变量
wire sccb_done;
reg wr;
//根据图像参数生成对应的电路(节约FPGA电路资源)
generate
//RGB输出格式
if(IMAGE_TYPE == RGB)begin
assign lut_max_addr = 252;
case({IMAGE_FLIP,IMAGE_MIRROR})
2'b00:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像不翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h40;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h7;
end
2'b01:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像不翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h40;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h0;
end
2'b10:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h47;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h7;
end
2'b11:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h47;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h0;
end
endcase
end
//JPEG输出格式
else begin
assign lut_max_addr = 250;
case({IMAGE_FLIP,IMAGE_MIRROR})
2'b00:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像不翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h40;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h7;
end
2'b01:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像不翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h40;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h0;
end
2'b10:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h47;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h7;
end
2'b11:
begin
ov5640_init_table_rgb ov5640_init_table_rgb(
.clk (clk),
.addr (cnt_addr),
.q (q)
);
//更改摄像头初始化查找表的初始化参数,图像翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
defparam ov5640_init_table_rgb.IMAGE_WIDTH = IMAGE_WIDTH;
defparam ov5640_init_table_rgb.IMAGE_HEIGHT = IMAGE_HEIGHT;
defparam ov5640_init_table_rgb.IMAGE_FLIP = 8'h47;
defparam ov5640_init_table_rgb.IMAGE_MIRROR = 4'h0;
end
endcase
end
endgenerate
//pwdn为低电平时,器件才能开始工作
assign camera_pwdn = 0;
//摄像头复位信号计时计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n)
cnt_camrst <= 0;
else if(add_cnt_camrst)begin
if(end_cnt_camrst)
cnt_camrst <= 0;
else
cnt_camrst <= cnt_camrst + 1'b1;
end
end
assign add_cnt_camrst = !work_flag && !rst_done;
assign end_cnt_camrst = add_cnt_camrst && cnt_camrst == CNTRST_N - 1;
//初始化配置查找表地址计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n)
cnt_addr <= 0;
else if(add_cnt_addr)begin
if(end_cnt_addr)
cnt_addr <= 0;
else
cnt_addr <= cnt_addr + 1'b1;
end
end
assign add_cnt_addr = work_flag && sccb_done;
assign end_cnt_addr = add_cnt_addr && cnt_addr == lut_max_addr - 1;
//在上电之后1ms拉高摄像头复位信号
always @(posedge clk or negedge rst_n)begin
if(rst_n)
camera_rst_n <= 0;
else if(add_cnt_camrst && cnt_camrst == UP_N - 1)
camera_rst_n <= 1;
end
//初始化工作标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n)
work_flag <= 0;
else if(end_cnt_camrst)
work_flag <= 1;
else if(end_cnt_addr)
work_flag <= 0;
end
//摄像头复位信号初始化完成标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n)
rst_done <= 0;
else if(end_cnt_camrst)
rst_done <= 1;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n)
wr <= 0;
else if((end_cnt_camrst || add_cnt_addr) && !end_cnt_addr)
wr <= 1;
else
wr <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n)
init_done <= 0;
else if(end_cnt_addr)
init_done <= 1;
// else
// init_done <= 1;
end
iic_module iic_module(
.rst_n ((!rst_n)),
.clk (clk),
.waddr_num (2'd2),
.device_addr (DEVICE_ADDR),
.word_addr (q[23:8]),
.wr (wr),
.wr_data (q[7:0]),
.rd (1'b0),
.rd_data (),
.done (sccb_done),
.scl (scl),
.sda (sda),
.wr_data_vld (),
.rd_data_vld ()
);
endmodule