上一节向大家介绍了摄像头配置的SCCB协议发送器的Verilog实现,这一节则向大家介绍配置信息存储模块:ovcfg_info的Verilog实现。
学习目标
- 配置模块Verilog实现
系统框图
从图中我们可以看到,ov7725_cfg模块的作用类似一个存储器,根据SCCB模块输出的 i2c_clk 与done,确定输送进SCCB模块地址接口和数据接口的数据。
因此我们可以有一个思路出现:首先数据的更新要赶在TRAN1状态之前,而恰好在STOP3状态结束的后四分之一周期,done信号发生一个跳变。因此可以用触发器检测done信号对数据进行计数。
代码实现
接口
sys_resetn | 复位信号 |
i2c_clk | 触发时钟信号 |
done | 配置完成信号 |
i2c_addr | 地址数据输出 |
i2c_data | 配置参数输出 |
Verilog代码
/*
By WWD 2022/9/3
OV7725 cfg V1.0
转载请注明出处!
*/
module ovcfg_info(
input wire i2c_clk,
input wire sys_resetn,
input wire done,
output wire [7:0] i2c_addr,
output wire [7:0] i2c_data
);
reg[6:0] info_cnt;
wire[15:0] cfg_data_reg [68:0];
assign cfg_data_reg[00] = {8'h3d, 8'h03};
assign cfg_data_reg[01] = {8'h15, 8'h00};
assign cfg_data_reg[02] = {8'h17, 8'h23};
assign cfg_data_reg[03] = {8'h18, 8'ha0};
assign cfg_data_reg[04] = {8'h19, 8'h07};
assign cfg_data_reg[05] = {8'h1a, 8'hf0};
assign cfg_data_reg[06] = {8'h32, 8'h00};
assign cfg_data_reg[07] = {8'h29, 8'ha0};
assign cfg_data_reg[08] = {8'h2a, 8'h00};
assign cfg_data_reg[09] = {8'h2b, 8'h00};
assign cfg_data_reg[10] = {8'h2c, 8'hf0};
assign cfg_data_reg[11] = {8'h0d, 8'h41};
assign cfg_data_reg[12] = {8'h11, 8'h00};
assign cfg_data_reg[13] = {8'h12, 8'h06};
assign cfg_data_reg[14] = {8'h0c, 8'hd0};
assign cfg_data_reg[15] = {8'h42, 8'h7f};
assign cfg_data_reg[16] = {8'h4d, 8'h09};
assign cfg_data_reg[17] = {8'h63, 8'hf0};
assign cfg_data_reg[18] = {8'h64, 8'hff};
assign cfg_data_reg[19] = {8'h65, 8'h00};
assign cfg_data_reg[20] = {8'h66, 8'h00};
assign cfg_data_reg[21] = {8'h67, 8'h00};
assign cfg_data_reg[22] = {8'h13, 8'hff};
assign cfg_data_reg[23] = {8'h0f, 8'hc5};
assign cfg_data_reg[24] = {8'h14, 8'h11};
assign cfg_data_reg[25] = {8'h22, 8'h98};
assign cfg_data_reg[26] = {8'h23, 8'h03};
assign cfg_data_reg[27] = {8'h24, 8'h40};
assign cfg_data_reg[28] = {8'h25, 8'h30};
assign cfg_data_reg[29] = {8'h26, 8'ha1};
assign cfg_data_reg[30] = {8'h6b, 8'haa};
assign cfg_data_reg[31] = {8'h13, 8'hff};
assign cfg_data_reg[32] = {8'h90, 8'h0a};
assign cfg_data_reg[33] = {8'h91, 8'h01};
assign cfg_data_reg[34] = {8'h92, 8'h01};
assign cfg_data_reg[35] = {8'h93, 8'h01};
assign cfg_data_reg[36] = {8'h94, 8'h5f};
assign cfg_data_reg[37] = {8'h95, 8'h53};
assign cfg_data_reg[38] = {8'h96, 8'h11};
assign cfg_data_reg[39] = {8'h97, 8'h1a};
assign cfg_data_reg[40] = {8'h98, 8'h3d};
assign cfg_data_reg[41] = {8'h99, 8'h5a};
assign cfg_data_reg[42] = {8'h9a, 8'h1e};
assign cfg_data_reg[43] = {8'h9b, 8'h3f};
assign cfg_data_reg[44] = {8'h9c, 8'h25};
assign cfg_data_reg[45] = {8'h9e, 8'h81};
assign cfg_data_reg[46] = {8'ha6, 8'h06};
assign cfg_data_reg[47] = {8'ha7, 8'h65};
assign cfg_data_reg[48] = {8'ha8, 8'h65};
assign cfg_data_reg[49] = {8'ha9, 8'h80};
assign cfg_data_reg[50] = {8'haa, 8'h80};
assign cfg_data_reg[51] = {8'h7e, 8'h0c};
assign cfg_data_reg[52] = {8'h7f, 8'h16};
assign cfg_data_reg[53] = {8'h80, 8'h2a};
assign cfg_data_reg[54] = {8'h81, 8'h4e};
assign cfg_data_reg[55] = {8'h82, 8'h61};
assign cfg_data_reg[56] = {8'h83, 8'h6f};
assign cfg_data_reg[57] = {8'h84, 8'h7b};
assign cfg_data_reg[58] = {8'h85, 8'h86};
assign cfg_data_reg[59] = {8'h86, 8'h8e};
assign cfg_data_reg[60] = {8'h87, 8'h97};
assign cfg_data_reg[61] = {8'h88, 8'ha4};
assign cfg_data_reg[62] = {8'h89, 8'haf};
assign cfg_data_reg[63] = {8'h8a, 8'hc5};
assign cfg_data_reg[64] = {8'h8b, 8'hd7};
assign cfg_data_reg[65] = {8'h8c, 8'he8};
assign cfg_data_reg[66] = {8'h8d, 8'h20};
assign cfg_data_reg[67] = {8'h0e, 8'h65};
assign cfg_data_reg[68] = {8'h09, 8'h00};
always@(posedge i2c_clk or negedge sys_resetn)begin
if(sys_resetn == 1'b0)
info_cnt <= 7'd0;
else if(info_cnt == 7'd69)
info_cnt <= 7'd0;
else if(done == 1'b1)
info_cnt <= info_cnt +7'd1;
end
assign i2c_addr = cfg_data_reg[info_cnt][15:8];
assign i2c_data = cfg_data_reg[info_cnt][7:0];
endmodule
测试验证
TB文件
/*
By WWD 2022/9/3
转载请注明出处
*/
`timescale 1ns/1ns
module tb_topovcfg();
reg sys_reset_n;
reg sys_clk;
reg i2c_start;
wire i2c_clk;
wire done;
wire [7:0] i2c_addr;
wire [7:0] i2c_data;
initial begin
sys_reset_n = 1'b0;
sys_clk = 1'b1;
i2c_start = 1'b0;
#20
i2c_start = 1'b1;
sys_reset_n = 1'b1;
/*#2000
byte_addr = 8'b1000_1111;
i2c_data = 8'b1111_0100;*/
end
always #10 sys_clk = ~sys_clk; //50MHz时钟
SCCB u1(
.sys_reset_n(sys_reset_n),
.sys_clk(sys_clk),
.i2c_start(i2c_start),
.byte_addr(i2c_addr),
.i2c_data(i2c_data),
.i2c_clk(i2c_clk),
.done(done),
.SDA(),
.SCL()
);
ovcfg_info u2(
.i2c_clk(i2c_clk),
.sys_resetn(sys_reset_n),
.done(done),
.i2c_addr(i2c_addr),
.i2c_data(i2c_data)
);
endmodule
Modelsim波形图
我们随意验证第一组数据与第二组数据:
assign cfg_data_reg[00] = {8'h3d, 8'h03};
assign cfg_data_reg[01] = {8'h15, 8'h00};
发现完全符合要求!
并且可以看到,当计数到68后,计数器归零,并且对应数据完全正确。
这里留给大家一个小任务,请大家改造这个模块,使得配置的过程只经历一遍。
下一节,我们将介绍如何设计OV7725的数据采集模块,敬请期待!