基于FPGA的OV5640摄像头驱动
一、OV5640的相关介绍
(1)野火的OV5640引脚图
(2)引脚介绍
(3)功能框图
<1>OV5640 的控制寄存器,它根据这些寄存器配置的参数来运行,而这些参数是由外部控制器通过 SIO_C 和 SIO_D 引脚写入的, SIO_C与 SIO_D 使用的通讯协议跟 I2C十分类似。
<2>OV5640的通信、控制信号及外部时钟,其中 PCLK、 HREF 及VSYNC分别是像素同步时钟、行同步信号以及帧同步信号,这与液晶屏控制中的信号是很类似的。 RESETB引脚为低电平时,用于复位整个传感器芯片,PWDN 用于控制芯片进入低功耗模式。注意最后的一个 XCLK引脚,它跟 PCLK
是完全不同的, XCLK 是用于驱动整个传感器芯片的时钟信号,是外部输入到OV5640的信号;而 PCLK是 OV5640输出数据时的同步信号,它是由 OV5640 输出的信号。 XCLK可以外接晶振或由外部控制器提供。
<3>感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。
<4>包含了 DSP 处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元,转换出的数据最终通过Y0-Y9引脚输出,一般来说我们使用 8根据数据线来传输,这时仅使用 Y2-Y9 引脚。
<5>VCM 处理单元,他会通过图像分析来实现图像的自动对焦功能。要实现自动对焦还需要下载自动对焦固件到模组,后面摄像头实验详细介绍这个功能。
二、SCCB时序介绍------与IIC基本相似
注意:当主机向从机进行指令或数据的写入时,串行数据线SDA上的数据在串行时钟SCL
为高电平时写入从机设备,每次只写入一位数据;串行数据线SDA中的数据在串行时钟
SCL为低电平时进行数据更新,以保证在SCL为高电平时采集到SDA数据的稳定状态。
(1)上电时序------主要按照官方文档的时序图来写程序
代码(严格按照时序图完成)
module Power_ctrl
#(
parameter CNT_5MS = 20'd250_000,
parameter CNT_1MS = 20'd50_000,
parameter CNT_20MS = 20'd1000_000
)
(
input wire sys_clk,
input wire rst,
output wire pwdn,
output wire resetb,
output wire sccb_en,
output wire clk_en
);
reg [20:0] cnt;
assign pwdn = (cnt >= CNT_5MS - 1'd1)?1'd0:1'd1;
assign resetb = (cnt >= CNT_5MS + CNT_1MS - 1'd1)?1'd1:1'd0;
assign sccb_en = (cnt >= (CNT_5MS + CNT_1MS + CNT_20MS - 1'd1))?1'd1:1'd0;
assign clk_en = (cnt >= CNT_5MS - 1'd1)?1'd1:1'd0;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt <= 1'd0;
else if(cnt == (CNT_5MS + CNT_1MS + CNT_20MS - 1'd1))
cnt <= cnt;
else
cnt <= cnt + 1'd1;
endmodule
(2)读
IIC:
SSCB:
读程序验证以读ID来实现:
时序图:
时序分析
Start:是用来启动读的标志位,为1时有效
statue:一共13个状态
cnt:这是一个用来统计scl周期个数的变量
cnt_stop_flag:在STOP状态下,需要先用到2个周期来完成STOP的操作,这时cnt_stop_flag为0,当完成后cnt_stop_flag置1
cnt_stop:用来对STOP状态下的计时
I2C_CLK_x:这是一个用来仿真验证的波形,它与scl相差135°的相位,在它的下降沿sda发生改变
cnt_clkx:统计I2C_CLK_x一个周期所需的计数值
cnt_clkx_flag:在cnt_clkx下降沿的前一个周期置一,下降沿处置零
I2C_SDA:sda信号线
Data:{ID,ADD},24位
I2C_CLK:就是SCL
data_flag:DATA状态下,在数据稳定的地方置1一个周期,用来判断什么时候读数据
data:读出的数据
cnt_clk:统计I2C_CLK一个周期所需的计数值
cnt_clkx_flag:在I2C_CLK下降沿的前一个周期置一,下降沿处置零
read_en:结束一次读操作的标志
由于在SSCB时序开始时是sda先产生下降沿,且sda的数据需在scl高电平时保持稳定,所以采用135°相位差的方法来完成,135不是固定的,可以进行一定的修改,sda数据在每个I2C_CLK_x下降沿处改变,在数据发送和接收状态时,每次发8个数据,然后就就进入下一状态,在这里有个问题就是ID号的第八位是省去的,实际的ID就是七位,因为之前没注意,导致第一次仿真无误后上板时出错,查阅资料发现IIC的应答阶段与SSCB不太一样,其实也差不多,就是SSCB的应答处理你可以不用判断是否应答成功,因为SSCB不一定为返回给你正确的应答值,本程序的x表示为应答状态,即不关心状态,在STOP状态时,先利用好两个周期的时间来完成STOP的操作,然后又是和开头的操作一样,重新开始,然后发读的ID,本程序中,sda在所有的x状态和DATA状态都为1’dz,接着开始读数据即可,然后进行NO ACK操作,sda主动置为高电平,完成后就再进行一次STOP操作,结束整个读操作。(注意:SSCB没有IIC的连续读和连续写)
开始位:
停止位:
上面两图第一个信号是scl,第二个是sda
程序
module SSCB_ID
(
input wire sys_clk,
input wire rst,
input wire read_en,
input wire [15:0] addr,
input wire [7:0] ID,
input wire sda_rd,
output reg sda,
output reg scl,
output reg read_end,
output reg [7:0] Data_reg,
output reg [12:0] statue,
output wire [23:0] Cmd
);
localparam
IDLE = 13'b0_0000_0000_0001,
START_ID1 = 13'b0_0000_0000_0010,
x_ack1 = 13'b0_0000_0000_0100,
ADD_H = 13'b0_0000_0000_1000,
x_ack2 = 13'b0_0000_0001_0000,
ADD_L = 13'b0_0000_0010_0000,
x_ack3 = 13'b0_0000_0100_0000,
STOP = 13'b0_0000_1000_0000,
START_ID2 = 13'b0_0001_0000_0000,
x_ack4 = 13'b0_0010_0000_0000,
DATA = 13'b0_0100_0000_0000,
NO = 13'b0_1000_0000_0000,
END = 13'b1_0000_0000_0000;
localparam
CLOCK = 30'd50_000_000,
I2C_CLK = 30'd400_000,
START_MAX = (CLOCK/I2C_CLK)*3/4 - 1'd1,
MAX = CLOCK/I2C_CLK - 1'd1,
MAX_2 = (CLOCK/I2C_CLK)/2 - 1'd1;
//wire [23:0] Cmd;
//reg sda;
assign Cmd = {ID,addr};
//reg sda;
reg [20:0] cnt;
reg [20:0] cnt_stop;
reg cnt_stop_flag;
reg I2C_CLK_x;
reg [20:0] cnt_CLKx;
reg cnt_clkx_flag;
reg data_flag;
reg [20:0] cnt_clk;
reg cnt_clk_flag;
//结束标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
read_end <= 1'd0;
else if(statue == END && cnt_clk == MAX - 1'd1)
read_end <= 1'd1;
else
read_end <= 1'd0;
//相移135°的I2C信号计数
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_CLKx <= 1'd0;
else if(cnt_CLKx == MAX)
cnt_CLKx <= 1'd0;
else if(read_en == 1'd1 && (statue == STOP) && cnt_stop_flag == 1'd1)
cnt_CLKx <= cnt_CLKx + 1'd1;
else if(read_en == 1'd1
&& (statue !=STOP)
&& statue != NO && statue != END)
cnt_CLKx <= cnt_CLKx + 1'd1;
else
cnt_CLKx <= 1'd0;
//相移135°的I2C信号周期更替标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clkx_flag <= 1'd0;
else if(cnt_CLKx == MAX - 1'd1)
cnt_clkx_flag <= 1'd1;
else
cnt_clkx_flag <= 1'd0;
//相移135°的I2C信号产生
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
I2C_CLK_x <= 1'd1;
else if((statue == STOP && cnt_stop_flag != 1'd1)
|| (statue == x_ack3 && cnt_clkx_flag == 1'd1)
|| (statue == DATA && cnt == 6'd8 && cnt_clkx_flag == 1'd1)
|| (statue == NO && statue == END))
I2C_CLK_x <= 1'd1;
else if(cnt_CLKx <= MAX_2 -1'd1 && read_en == 1'd1)
I2C_CLK_x <= 1'd0;
else if(cnt_CLKx <= MAX - 1'd1 && read_en == 1'd1)
I2C_CLK_x <= 1'd1;
else if(read_en != 1'd1)
I2C_CLK_x <= 1'd1;
//STOP状态时的计数停
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_stop_flag <= 1'd0;
else if(statue == STOP && cnt == 2'd2 && cnt_stop == MAX)
cnt_stop_flag <= 1'd1;
else if(statue == START_ID2)
cnt_stop_flag <= 1'd0;
//STOP状态下让I2C总线停止两个周期的计
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_stop <= 1'd0;
else if(statue == STOP && cnt_stop == MAX)
cnt_stop <= 1'd0;
else if(statue == STOP && cnt_stop_flag != 1'd1)
cnt_stop <= cnt_stop + 1'd1;
else
cnt_stop <= 1'd0;
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clk <= 1'd0;
else if(statue == STOP || statue == IDLE)
cnt_clk <= 1'd0;
else if(cnt_clk == MAX)
cnt_clk <= 1'd0;
else
cnt_clk <= cnt_clk + 1'd1;
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
scl <= 1'd1;
else if(statue == END && cnt_clk_flag == 1'd1)
scl <= 1'd1;
else if(statue == STOP || (statue == x_ack3 && cnt_clkx_flag == 1'd1))
scl <= 1'd1;
else if(statue == STOP && cnt_stop_flag == 1'd1 && cnt_clkx_flag == 1'd1)
scl <= 1'd0;
else if(statue == IDLE && cnt_CLKx == START_MAX)
scl <= 1'd0;
else if(cnt_clk <= MAX_2 && read_en == 1'd1 && statue != IDLE)
scl <= 1'd0;
else if(cnt_clk < MAX && read_en == 1'd1 && statue != IDLE)
scl <= 1'd1;
else if(cnt_clk == MAX && read_en == 1'd1 && statue != IDLE)
scl <= 1'd0;
else if(read_en != 1'd1)
scl <= 1'd1;
//全局计数
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt <= 1'd0;
else if(statue == STOP)
begin
if(cnt_stop == MAX && cnt == 2'd2)
cnt <= 1'd0;
else if(cnt_stop == MAX)
cnt <= cnt + 1'd1;
else
cnt <= cnt;
end
else if(statue == NO && statue == END)
cnt <= 1'd0;
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
cnt <= 1'd0;
else if(cnt_clkx_flag == 1'd1)
cnt <= cnt + 1'd1;
else
cnt <= cnt;
//I2C信号周期更替标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clk_flag <= 1'd0;
else if(cnt_clk == MAX - 1'd1)
cnt_clk_flag <= 1'd1;
else
cnt_clk_flag <= 1'd0;
//data数据记录标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
data_flag <= 1'd0;
else if(statue == DATA && cnt_clk == START_MAX)
data_flag <= 1'd1;
else
data_flag <= 1'd0;
//数据存储
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
Data_reg <= 1'd0;
else if(statue == DATA && data_flag == 1'd1)
Data_reg <= {Data_reg[6:0],sda_rd};
else
Data_reg <= Data_reg;
//状态机跳转
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
statue <= IDLE;
else case(statue)
IDLE:
if(read_en == 1'd1 && cnt_CLKx == START_MAX)
statue <= START_ID1;
START_ID1:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack1;
x_ack1:
if(cnt_clkx_flag == 1'd1)
statue <= ADD_H;
ADD_H:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack2;
x_ack2:
if(cnt_clkx_flag == 1'd1)
statue <= ADD_L;
ADD_L:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack3;
x_ack3:
if(cnt_clkx_flag == 1'd1)
statue <= STOP;
STOP:
if(cnt_stop_flag == 1'd1 && cnt_CLKx == START_MAX)
statue <= START_ID2;
START_ID2:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack4;
x_ack4:
if(cnt_clkx_flag == 1'd1)
statue <= DATA;
DATA:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= NO;
NO:
if(cnt_clk_flag == 1'd1)
statue <= END;
END:
if(cnt_clk_flag == 1'd1)
statue <= IDLE;
default:statue <= IDLE;
endcase
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
sda <= 1'd1;
else case(statue)
IDLE:
if(read_en == 1'd1)
sda <= 1'd0;
else
sda <= 1'd1;
START_ID1:
if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
sda <= Cmd[22 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
sda <= 1'd0;
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack1:
if(cnt_clkx_flag == 1'd1)
sda <= Cmd[15 - cnt];
ADD_H:
if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
sda <= Cmd[15 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack2:
if(cnt_clkx_flag == 1'd1)
sda <= Cmd[7 - cnt];
ADD_L:
if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
sda <= Cmd[7 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack3:
if(cnt_clkx_flag == 1'd1)
sda <= 1'd0;
STOP:
if(cnt_stop_flag != 1'd1 && cnt == 1'd0)
sda <= 1'd0;
else if(cnt_stop_flag == 1'd1)
sda <= 1'd0;
else
sda <= 1'd1;
START_ID2:
if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
sda <= Cmd[22 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
sda <= 1'd1;
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack4:
sda <= 1'd0;
DATA:
if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd1;
else
sda <= 1'd0;
NO:
if(cnt_clk_flag == 1'd1)
sda <= 1'd0;
else
sda <= 1'd1;
END:
if(cnt_clk_flag == 1'd1)
sda <= 1'd1;
default:sda <= 1'd1;
endcase
/*
assign sda_x = (statue == x_ack1 || statue == x_ack2 || statue == x_ack3
|| statue == x_ack4 || (statue == DATA))?1'dz:sda;
*/
endmodule
结果演示:
(3) 写
IIC与SSCB通用
设置寄存器时会用到
SSCB写寄存器部分
module SSCB_WR
(
input wire sys_clk,
input wire rst,
input wire WR_en,
input wire [15:0] addr,
input wire [7:0] ID,
input wire [7:0] data,
output reg sda,
output reg scl,
output reg WR_end,
output reg [9:0] statue
);
localparam
IDLE = 10'b0000000001,
START_ID1 = 10'b0000000010,
x_ack1 = 10'b0000000100,
ADD_H = 10'b0000001000,
x_ack2 = 10'b0000010000,
ADD_L = 10'b0000100000,
x_ack3 = 10'b0001000000,
DATA = 10'b0010000000,
x_ack4 = 10'b0100000000,
END = 10'b1000000000;
localparam
CLOCK = 30'd50_000_000,
I2C_CLK = 30'd400_000,
START_MAX = (CLOCK/I2C_CLK)*3/4 - 1'd1,
MAX = CLOCK/I2C_CLK - 1'd1,
MAX_2 = (CLOCK/I2C_CLK)/2 - 1'd1;
wire [23:0] Cmd;
//reg sda;
assign Cmd = {ID,addr};
reg [20:0] cnt;
reg I2C_CLK_x;
reg [20:0] cnt_CLKx;
reg cnt_clkx_flag;
reg [20:0] cnt_clk;
reg cnt_clk_flag;
//结束标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
WR_end <= 1'd0;
else if(statue == END && cnt_clk == MAX - 1'd1)
WR_end <= 1'd1;
else
WR_end <= 1'd0;
//相移135°的I2C信号计数
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_CLKx <= 1'd0;
else if(cnt_CLKx == MAX)
cnt_CLKx <= 1'd0;
else if(WR_en == 1'd1 && statue != x_ack4 && statue != END)
cnt_CLKx <= cnt_CLKx + 1'd1;
else
cnt_CLKx <= 1'd0;
//相移135°的I2C信号周期更替标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clkx_flag <= 1'd0;
else if(cnt_CLKx == MAX - 1'd1)
cnt_clkx_flag <= 1'd1;
else
cnt_clkx_flag <= 1'd0;
//相移135°的I2C信号产生
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
I2C_CLK_x <= 1'd1;
else if((statue == DATA && cnt == 6'd8 && cnt_clkx_flag == 1'd1)
|| (statue == x_ack4 && statue == END))
I2C_CLK_x <= 1'd1;
else if(cnt_CLKx <= MAX_2 -1'd1 && WR_en == 1'd1)
I2C_CLK_x <= 1'd0;
else if(cnt_CLKx <= MAX - 1'd1 && WR_en == 1'd1)
I2C_CLK_x <= 1'd1;
else if(WR_en != 1'd1)
I2C_CLK_x <= 1'd1;
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clk <= 1'd0;
else if(statue == IDLE)
cnt_clk <= 1'd0;
else if(cnt_clk == MAX)
cnt_clk <= 1'd0;
else
cnt_clk <= cnt_clk + 1'd1;
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
scl <= 1'd1;
else if(statue == END && cnt_clk_flag == 1'd1)
scl <= 1'd1;
else if(statue == IDLE && cnt_CLKx == START_MAX)
scl <= 1'd0;
else if(cnt_clk <= MAX_2 && WR_en == 1'd1 && statue != IDLE)
scl <= 1'd0;
else if(cnt_clk < MAX && WR_en == 1'd1 && statue != IDLE)
scl <= 1'd1;
else if(cnt_clk == MAX && WR_en == 1'd1 && statue != IDLE)
scl <= 1'd0;
else if(WR_en != 1'd1)
scl <= 1'd1;
//全局计数
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt <= 1'd0;
else if(statue == x_ack4 && statue == END)
cnt <= 1'd0;
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
cnt <= 1'd0;
else if(cnt_clkx_flag == 1'd1)
cnt <= cnt + 1'd1;
else
cnt <= cnt;
//I2C信号周期更替标志
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_clk_flag <= 1'd0;
else if(cnt_clk == MAX - 1'd1)
cnt_clk_flag <= 1'd1;
else
cnt_clk_flag <= 1'd0;
//状态机跳转
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
statue <= IDLE;
else case(statue)
IDLE:
if(WR_en == 1'd1 && cnt_CLKx == START_MAX)
statue <= START_ID1;
START_ID1:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack1;
x_ack1:
if(cnt_clkx_flag == 1'd1)
statue <= ADD_H;
ADD_H:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack2;
x_ack2:
if(cnt_clkx_flag == 1'd1)
statue <= ADD_L;
ADD_L:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack3;
x_ack3:
if(cnt_clkx_flag == 1'd1)
statue <= DATA;
DATA:
if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
statue <= x_ack4;
x_ack4:
if(cnt_clk_flag == 1'd1)
statue <= END;
END:
if(cnt_clk_flag == 1'd1)
statue <= IDLE;
default:statue <= IDLE;
endcase
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
sda <= 1'd1;
else case(statue)
IDLE:
if(WR_en == 1'd1)
sda <= 1'd0;
else
sda <= 1'd1;
START_ID1:
if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
sda <= Cmd[22 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
sda <= 1'd0;
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack1:
if(cnt_clkx_flag == 1'd1)
sda <= Cmd[15 - cnt];
ADD_H:
if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
sda <= Cmd[15 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack2:
if(cnt_clkx_flag == 1'd1)
sda <= Cmd[7 - cnt];
ADD_L:
if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
sda <= Cmd[7 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack3:
if(cnt_clkx_flag == 1'd1)
sda <= data[7 - cnt];
DATA:
if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
sda <= data[7 - cnt];
else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
sda <= 1'd0;
x_ack4:
if(cnt_clk_flag == 1'd1)
sda <= 1'd0;
else
sda <= 1'd0;
END:
if(cnt_clk_flag == 1'd1)
sda <= 1'd1;
default:sda <= 1'd1;
endcase
/*
assign sda_x = (statue == x_ack1 || statue == x_ack2 || statue == x_ack3
|| statue == x_ack4 || (statue == DATA))?1'dz:sda;
*/
endmodule
SSCB寄存器地址和数据来源(应用了野火的代码)
module OV5640_cfg
(
input wire sys_clk,
input wire rst,
input wire start,
input wire write_end,
output wire [23:0] cfg_data,
output reg WR_en,
output reg init_end
);
parameter REG_NUM = 8'd251 ;
wire [23:0] cfg_data_reg[REG_NUM-1:0] ;
reg [15:0] cnt;
reg [8:0] number;
reg cnt_en;
parameter delay = 16'd9999;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt_en <= 1'd0;
else if(write_end == 1'd1 && init_end != 1'd1)
cnt_en <= 1'd1;
else if(cnt == delay)
cnt_en <= 1'd0;
else
cnt_en <= cnt_en;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
init_end <= 1'd0;
else if(number == REG_NUM && cnt == delay)
init_end <= 1'd1;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
cnt <= 1'd0;
else if(cnt == delay)
cnt <= 1'd0;
else if(WR_en != 1'd1 && start == 1'd1 && init_end != 1'd1 && cnt_en == 1'd1)
cnt <= cnt + 1'd1;
else
cnt <= 1'd0;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
number <= 1'd0;
else if(write_end == 1'd1 && number != REG_NUM)
number <= number + 1'd1;
else
number <= number;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
WR_en <= 1'd0;
else if(write_end == 1'd1)
WR_en <= 1'd0;
else if(number != REG_NUM && cnt == delay)
WR_en <= 1'd1;
else if(number == 1'd0 && start == 1'd1)
WR_en <= 1'd1;
assign cfg_data = (number != REG_NUM)?cfg_data_reg[number]:1'd0;
//cfg_data_reg:寄存器配置数据暂存 ID REG_ADDR REG_VAL
assign cfg_data_reg[000] = {16'h3103, 8'h11};
assign cfg_data_reg[001] = {16'h3008, 8'h82};
assign cfg_data_reg[002] = {16'h3008, 8'h42};
assign cfg_data_reg[003] = {16'h3103, 8'h03};
assign cfg_data_reg[004] = {16'h3017, 8'hff};
assign cfg_data_reg[005] = {16'h3018, 8'hff};
assign cfg_data_reg[006] = {16'h3034, 8'h1A};
assign cfg_data_reg[007] = {16'h3037, 8'h13};
assign cfg_data_reg[008] = {16'h3108, 8'h01};
assign cfg_data_reg[009] = {16'h3630, 8'h36};
assign cfg_data_reg[010] = {16'h3631, 8'h0e};
assign cfg_data_reg[011] = {16'h3632, 8'he2};
assign cfg_data_reg[012] = {16'h3633, 8'h12};
assign cfg_data_reg[013] = {16'h3621, 8'he0};
assign cfg_data_reg[014] = {16'h3704, 8'ha0};
assign cfg_data_reg[015] = {16'h3703, 8'h5a};
assign cfg_data_reg[016] = {16'h3715, 8'h78};
assign cfg_data_reg[017] = {16'h3717, 8'h01};
assign cfg_data_reg[018] = {16'h370b, 8'h60};
assign cfg_data_reg[019] = {16'h3705, 8'h1a};
assign cfg_data_reg[020] = {16'h3905, 8'h02};
assign cfg_data_reg[021] = {16'h3906, 8'h10};
assign cfg_data_reg[022] = {16'h3901, 8'h0a};
assign cfg_data_reg[023] = {16'h3731, 8'h12};
assign cfg_data_reg[024] = {16'h3600, 8'h08};
assign cfg_data_reg[025] = {16'h3601, 8'h33};
assign cfg_data_reg[026] = {16'h302d, 8'h60};
assign cfg_data_reg[027] = {16'h3620, 8'h52};
assign cfg_data_reg[028] = {16'h371b, 8'h20};
assign cfg_data_reg[029] = {16'h471c, 8'h50};
assign cfg_data_reg[030] = {16'h3a13, 8'h43};
assign cfg_data_reg[031] = {16'h3a18, 8'h00};
assign cfg_data_reg[032] = {16'h3a19, 8'hf8};
assign cfg_data_reg[033] = {16'h3635, 8'h13};
assign cfg_data_reg[034] = {16'h3636, 8'h03};
assign cfg_data_reg[035] = {16'h3634, 8'h40};
assign cfg_data_reg[036] = {16'h3622, 8'h01};
assign cfg_data_reg[037] = {16'h3c01, 8'h34};
assign cfg_data_reg[038] = {16'h3c04, 8'h28};
assign cfg_data_reg[039] = {16'h3c05, 8'h98};
assign cfg_data_reg[040] = {16'h3c06, 8'h00};
assign cfg_data_reg[041] = {16'h3c07, 8'h08};
assign cfg_data_reg[042] = {16'h3c08, 8'h00};
assign cfg_data_reg[043] = {16'h3c09, 8'h1c};
assign cfg_data_reg[044] = {16'h3c0a, 8'h9c};
assign cfg_data_reg[045] = {16'h3c0b, 8'h40};
assign cfg_data_reg[046] = {16'h3810, 8'h00};
assign cfg_data_reg[047] = {16'h3811, 8'h10};
assign cfg_data_reg[048] = {16'h3812, 8'h00};
assign cfg_data_reg[049] = {16'h3708, 8'h64};
assign cfg_data_reg[050] = {16'h4001, 8'h02};
assign cfg_data_reg[051] = {16'h4005, 8'h1a};
assign cfg_data_reg[052] = {16'h3000, 8'h00};
assign cfg_data_reg[053] = {16'h3004, 8'hff};
assign cfg_data_reg[054] = {16'h300e, 8'h58};
assign cfg_data_reg[055] = {16'h302e, 8'h00};
assign cfg_data_reg[056] = {16'h4300, 8'h61};
assign cfg_data_reg[057] = {16'h501f, 8'h01};
assign cfg_data_reg[058] = {16'h440e, 8'h00};
assign cfg_data_reg[059] = {16'h5000, 8'ha7};
assign cfg_data_reg[060] = {16'h3a0f, 8'h30};
assign cfg_data_reg[061] = {16'h3a10, 8'h28};
assign cfg_data_reg[062] = {16'h3a1b, 8'h30};
assign cfg_data_reg[063] = {16'h3a1e, 8'h26};
assign cfg_data_reg[064] = {16'h3a11, 8'h60};
assign cfg_data_reg[065] = {16'h3a1f, 8'h14};
assign cfg_data_reg[066] = {16'h5800, 8'h23};
assign cfg_data_reg[067] = {16'h5801, 8'h14};
assign cfg_data_reg[068] = {16'h5802, 8'h0f};
assign cfg_data_reg[069] = {16'h5803, 8'h0f};
assign cfg_data_reg[070] = {16'h5804, 8'h12};
assign cfg_data_reg[071] = {16'h5805, 8'h26};
assign cfg_data_reg[072] = {16'h5806, 8'h0c};
assign cfg_data_reg[073] = {16'h5807, 8'h08};
assign cfg_data_reg[074] = {16'h5808, 8'h05};
assign cfg_data_reg[075] = {16'h5809, 8'h05};
assign cfg_data_reg[076] = {16'h580a, 8'h08};
assign cfg_data_reg[077] = {16'h580b, 8'h0d};
assign cfg_data_reg[078] = {16'h580c, 8'h08};
assign cfg_data_reg[079] = {16'h580d, 8'h03};
assign cfg_data_reg[080] = {16'h580e, 8'h00};
assign cfg_data_reg[081] = {16'h580f, 8'h00};
assign cfg_data_reg[082] = {16'h5810, 8'h03};
assign cfg_data_reg[083] = {16'h5811, 8'h09};
assign cfg_data_reg[084] = {16'h5812, 8'h07};
assign cfg_data_reg[085] = {16'h5813, 8'h03};
assign cfg_data_reg[086] = {16'h5814, 8'h00};
assign cfg_data_reg[087] = {16'h5815, 8'h01};
assign cfg_data_reg[088] = {16'h5816, 8'h03};
assign cfg_data_reg[089] = {16'h5817, 8'h08};
assign cfg_data_reg[090] = {16'h5818, 8'h0d};
assign cfg_data_reg[091] = {16'h5819, 8'h08};
assign cfg_data_reg[092] = {16'h581a, 8'h05};
assign cfg_data_reg[093] = {16'h581b, 8'h06};
assign cfg_data_reg[094] = {16'h581c, 8'h08};
assign cfg_data_reg[095] = {16'h581d, 8'h0e};
assign cfg_data_reg[096] = {16'h581e, 8'h29};
assign cfg_data_reg[097] = {16'h581f, 8'h17};
assign cfg_data_reg[098] = {16'h5820, 8'h11};
assign cfg_data_reg[099] = {16'h5821, 8'h11};
assign cfg_data_reg[100] = {16'h5822, 8'h15};
assign cfg_data_reg[101] = {16'h5823, 8'h28};
assign cfg_data_reg[102] = {16'h5824, 8'h46};
assign cfg_data_reg[103] = {16'h5825, 8'h26};
assign cfg_data_reg[104] = {16'h5826, 8'h08};
assign cfg_data_reg[105] = {16'h5827, 8'h26};
assign cfg_data_reg[106] = {16'h5828, 8'h64};
assign cfg_data_reg[107] = {16'h5829, 8'h26};
assign cfg_data_reg[108] = {16'h582a, 8'h24};
assign cfg_data_reg[109] = {16'h582b, 8'h22};
assign cfg_data_reg[110] = {16'h582c, 8'h24};
assign cfg_data_reg[111] = {16'h582d, 8'h24};
assign cfg_data_reg[112] = {16'h582e, 8'h06};
assign cfg_data_reg[113] = {16'h582f, 8'h22};
assign cfg_data_reg[114] = {16'h5830, 8'h40};
assign cfg_data_reg[115] = {16'h5831, 8'h42};
assign cfg_data_reg[116] = {16'h5832, 8'h24};
assign cfg_data_reg[117] = {16'h5833, 8'h26};
assign cfg_data_reg[118] = {16'h5834, 8'h24};
assign cfg_data_reg[119] = {16'h5835, 8'h22};
assign cfg_data_reg[120] = {16'h5836, 8'h22};
assign cfg_data_reg[121] = {16'h5837, 8'h26};
assign cfg_data_reg[122] = {16'h5838, 8'h44};
assign cfg_data_reg[123] = {16'h5839, 8'h24};
assign cfg_data_reg[124] = {16'h583a, 8'h26};
assign cfg_data_reg[125] = {16'h583b, 8'h28};
assign cfg_data_reg[126] = {16'h583c, 8'h42};
assign cfg_data_reg[127] = {16'h583d, 8'hce};
assign cfg_data_reg[128] = {16'h5180, 8'hff};
assign cfg_data_reg[129] = {16'h5181, 8'hf2};
assign cfg_data_reg[130] = {16'h5182, 8'h00};
assign cfg_data_reg[131] = {16'h5183, 8'h14};
assign cfg_data_reg[132] = {16'h5184, 8'h25};
assign cfg_data_reg[133] = {16'h5185, 8'h24};
assign cfg_data_reg[134] = {16'h5186, 8'h09};
assign cfg_data_reg[135] = {16'h5187, 8'h09};
assign cfg_data_reg[136] = {16'h5188, 8'h09};
assign cfg_data_reg[137] = {16'h5189, 8'h75};
assign cfg_data_reg[138] = {16'h518a, 8'h54};
assign cfg_data_reg[139] = {16'h518b, 8'he0};
assign cfg_data_reg[140] = {16'h518c, 8'hb2};
assign cfg_data_reg[141] = {16'h518d, 8'h42};
assign cfg_data_reg[142] = {16'h518e, 8'h3d};
assign cfg_data_reg[143] = {16'h518f, 8'h56};
assign cfg_data_reg[144] = {16'h5190, 8'h46};
assign cfg_data_reg[145] = {16'h5191, 8'hf8};
assign cfg_data_reg[146] = {16'h5192, 8'h04};
assign cfg_data_reg[147] = {16'h5193, 8'h70};
assign cfg_data_reg[148] = {16'h5194, 8'hf0};
assign cfg_data_reg[149] = {16'h5195, 8'hf0};
assign cfg_data_reg[150] = {16'h5196, 8'h03};
assign cfg_data_reg[151] = {16'h5197, 8'h01};
assign cfg_data_reg[152] = {16'h5198, 8'h04};
assign cfg_data_reg[153] = {16'h5199, 8'h12};
assign cfg_data_reg[154] = {16'h519a, 8'h04};
assign cfg_data_reg[155] = {16'h519b, 8'h00};
assign cfg_data_reg[156] = {16'h519c, 8'h06};
assign cfg_data_reg[157] = {16'h519d, 8'h82};
assign cfg_data_reg[158] = {16'h519e, 8'h38};
assign cfg_data_reg[159] = {16'h5480, 8'h01};
assign cfg_data_reg[160] = {16'h5481, 8'h08};
assign cfg_data_reg[161] = {16'h5482, 8'h14};
assign cfg_data_reg[162] = {16'h5483, 8'h28};
assign cfg_data_reg[163] = {16'h5484, 8'h51};
assign cfg_data_reg[164] = {16'h5485, 8'h65};
assign cfg_data_reg[165] = {16'h5486, 8'h71};
assign cfg_data_reg[166] = {16'h5487, 8'h7d};
assign cfg_data_reg[167] = {16'h5488, 8'h87};
assign cfg_data_reg[168] = {16'h5489, 8'h91};
assign cfg_data_reg[169] = {16'h548a, 8'h9a};
assign cfg_data_reg[170] = {16'h548b, 8'haa};
assign cfg_data_reg[171] = {16'h548c, 8'hb8};
assign cfg_data_reg[172] = {16'h548d, 8'hcd};
assign cfg_data_reg[173] = {16'h548e, 8'hdd};
assign cfg_data_reg[174] = {16'h548f, 8'hea};
assign cfg_data_reg[175] = {16'h5490, 8'h1d};
assign cfg_data_reg[176] = {16'h5381, 8'h1e};
assign cfg_data_reg[177] = {16'h5382, 8'h5b};
assign cfg_data_reg[178] = {16'h5383, 8'h08};
assign cfg_data_reg[179] = {16'h5384, 8'h0a};
assign cfg_data_reg[180] = {16'h5385, 8'h7e};
assign cfg_data_reg[181] = {16'h5386, 8'h88};
assign cfg_data_reg[182] = {16'h5387, 8'h7c};
assign cfg_data_reg[183] = {16'h5388, 8'h6c};
assign cfg_data_reg[184] = {16'h5389, 8'h10};
assign cfg_data_reg[185] = {16'h538a, 8'h01};
assign cfg_data_reg[186] = {16'h538b, 8'h98};
assign cfg_data_reg[187] = {16'h5580, 8'h06};
assign cfg_data_reg[188] = {16'h5583, 8'h40};
assign cfg_data_reg[189] = {16'h5584, 8'h10};
assign cfg_data_reg[190] = {16'h5589, 8'h10};
assign cfg_data_reg[191] = {16'h558a, 8'h00};
assign cfg_data_reg[192] = {16'h558b, 8'hf8};
assign cfg_data_reg[193] = {16'h501d, 8'h40};
assign cfg_data_reg[194] = {16'h5300, 8'h08};
assign cfg_data_reg[195] = {16'h5301, 8'h30};
assign cfg_data_reg[196] = {16'h5302, 8'h10};
assign cfg_data_reg[197] = {16'h5303, 8'h00};
assign cfg_data_reg[198] = {16'h5304, 8'h08};
assign cfg_data_reg[199] = {16'h5305, 8'h30};
assign cfg_data_reg[200] = {16'h5306, 8'h08};
assign cfg_data_reg[201] = {16'h5307, 8'h16};
assign cfg_data_reg[202] = {16'h5309, 8'h08};
assign cfg_data_reg[203] = {16'h530a, 8'h30};
assign cfg_data_reg[204] = {16'h530b, 8'h04};
assign cfg_data_reg[205] = {16'h530c, 8'h06};
assign cfg_data_reg[206] = {16'h5025, 8'h00};
assign cfg_data_reg[207] = {16'h3008, 8'h02};
assign cfg_data_reg[208] = {16'h3035, 8'h11};
assign cfg_data_reg[209] = {16'h3036, 8'h46};
assign cfg_data_reg[210] = {16'h3c07, 8'h08};
assign cfg_data_reg[211] = {16'h3820, 8'h47};
assign cfg_data_reg[212] = {16'h3821, 8'h07};
assign cfg_data_reg[213] = {16'h3814, 8'h31};
assign cfg_data_reg[214] = {16'h3815, 8'h31};
assign cfg_data_reg[215] = {16'h3800, 8'h00};
assign cfg_data_reg[216] = {16'h3801, 8'h00};
assign cfg_data_reg[217] = {16'h3802, 8'h00};
assign cfg_data_reg[218] = {16'h3803, 8'hfa};//Y起始地址
assign cfg_data_reg[219] = {16'h3804, 8'h0a};
assign cfg_data_reg[220] = {16'h3805, 8'h3f};
assign cfg_data_reg[221] = {16'h3806, 8'h06};//Y结束地址
assign cfg_data_reg[222] = {16'h3807, 8'ha9};
assign cfg_data_reg[223] = {16'h3808, 8'h03};//水平输出宽度
assign cfg_data_reg[224] = {16'h3809, 8'h20};
assign cfg_data_reg[225] = {16'h380a, 8'h01};//垂直输出宽度
assign cfg_data_reg[226] = {16'h380b, 8'he0};
assign cfg_data_reg[227] = {16'h380c, 8'h07};//总水平大小
assign cfg_data_reg[228] = {16'h380d, 8'h64};
assign cfg_data_reg[229] = {16'h380e, 8'h02};//总垂直大小
assign cfg_data_reg[230] = {16'h380f, 8'he4};
assign cfg_data_reg[231] = {16'h3813, 8'h04};//垂直偏移
assign cfg_data_reg[232] = {16'h3618, 8'h00};
assign cfg_data_reg[233] = {16'h3612, 8'h29};
assign cfg_data_reg[234] = {16'h3709, 8'h52};
assign cfg_data_reg[235] = {16'h370c, 8'h03};
assign cfg_data_reg[236] = {16'h3a02, 8'h17};
assign cfg_data_reg[237] = {16'h3a03, 8'h10};
assign cfg_data_reg[238] = {16'h3a14, 8'h17};
assign cfg_data_reg[239] = {16'h3a15, 8'h10};
assign cfg_data_reg[240] = {16'h4004, 8'h02};
assign cfg_data_reg[241] = {16'h3002, 8'h1c};
assign cfg_data_reg[242] = {16'h3006, 8'hc3};
assign cfg_data_reg[243] = {16'h4713, 8'h03};
assign cfg_data_reg[244] = {16'h4407, 8'h04};
assign cfg_data_reg[245] = {16'h460b, 8'h35};
assign cfg_data_reg[246] = {16'h460c, 8'h22};
assign cfg_data_reg[247] = {16'h4837, 8'h22};
assign cfg_data_reg[248] = {16'h3824, 8'h02};
assign cfg_data_reg[249] = {16'h5001, 8'ha3};
assign cfg_data_reg[250] = {16'h3503, 8'h00};
endmodule
(4)顶层测试
顶层代码
module OV5640_Top
(
input wire sys_clk,
input wire rst,
inout wire sda,
output wire scl,
output wire pwdn,
output wire resetb,
output wire [9:0] statue_WR,
output wire [12:0] statue_RD
);
reg read_en;
wire [15:0] addr;
wire read_end;
wire sccb_en;
wire clk_en;
wire start;
wire [7:0] Data_reg;
wire [23:0] cfg_data;
wire init_end;
wire write_end;
wire WR_en;
wire scl_rd;
wire scl_wr;
wire sda_rd;
wire sda_wr;
reg number;
/*
wire [9:0] statue_WR;
wire [12:0] statue_RD;
*/
wire [7:0] ID = 8'h3C;
wire [15:0] rd_add = 16'h3808;
wire [23:0] Cmd;
assign start = (sccb_en == 1'd1 && clk_en == 1'd1)?1'd1:1'd0;
assign sda = (init_end == 1'd1)?((statue_RD == 13'b0_0000_0000_0100
|| statue_RD == 13'b0_0000_0001_0000
|| statue_RD == 13'b0_0000_0100_0000
|| statue_RD == 13'b0_0010_0000_0000
|| statue_RD == 13'b0_0100_0000_0000)?1'dz:sda_rd)
:sda_wr;
assign scl = (init_end == 1'd1)?scl_rd:scl_wr;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
number <= 1'd1;
else if(number == 1'd1 && read_end == 1'd1 && init_end == 1'd1)
number <= 1'd0;
always@(posedge sys_clk or negedge rst)
if(rst == 1'd0)
read_en <= 1'd0;
else if(read_end == 1'd1)
read_en <= 1'd0;
else if(init_end == 1'd1 && number == 1'd1)
read_en <= 1'd1;
Power_ctrl
#(
.CNT_5MS(20'd250_000),
.CNT_1MS(20'd50_000),
.CNT_20MS(20'd1000_000)
)
Power_ctrl_inst
(
.sys_clk(sys_clk),
.rst(rst),
.pwdn(pwdn),
.resetb(resetb),
.sccb_en(sccb_en),
.clk_en(clk_en)
);
OV5640_cfg OV5640_cfg_inst
(
.sys_clk(sys_clk),
.rst(rst),
.start(start),
.write_end(write_end),
.cfg_data(cfg_data),
.WR_en(WR_en),
.init_end(init_end)
);
SSCB_WR SSCB_WR_inst
(
.sys_clk(sys_clk),
.rst(rst),
.WR_en(WR_en),
.addr(cfg_data[23:8]),
.ID(ID),
.data(cfg_data[7:0]),
.sda(sda_wr),
.scl(scl_wr),
.WR_end(write_end),
.statue(statue_WR)
);
SSCB_ID SSCB_ID_inst
(
.sys_clk(sys_clk),
.rst(rst),
.read_en(read_en),
.addr(rd_add),
.ID(ID),
.sda_rd(sda),
.sda(sda_rd),
.scl(scl_rd),
.read_end(read_end),
.Data_reg(Data_reg),
.statue(statue_RD),
.Cmd(Cmd)
);
endmodule
测试结果
该测试是将刚才写于的寄存器数据读出来判断是否一致(该寄存器地址为16‘h3808)
(5) 使用inout的注意事项
<1>inout只能用wire来赋值
<2>inout在赋值时,要使用三态逻辑门来赋值(也有其他的方法,但最好还是用这个),且赋值的内容必须包含高阻态。
<3>尽量不要在底层使用inout,不然会有一些警告。
<4>底层不要数据赋值为z
<5>规范使用inout,不然可能导致电路短路
以上几点,如果不遵循,可能编译通过了,但生成的电路会不满足你的设计要求。
三、摄像头数据读取
数据输出时序: