在CMOS摄像头sensor里面,发送初始化命令的接口叫SSBC,其实就是为了规避IIC的专利权,当然现在没有这个问题了。
SCCB协议实现(因为类似iic 所以下面的信号名全按iic起的)
SCCB有两根信号线或者三根线
- 时钟线iic_clk 位宽 1bit
- 数据线iic_data 位宽 1bit
- 片选信号SIO_E 位宽 1bit(只有一个设备的话可以没有他)
强调: - 时钟线始终掌控在主机手里,从机根据时钟信号配合工作。
- iic_data是一个三态门,双向数据线,既可以由主机控制,也可以由从机控制
状态:
空闲状态 iic_clk= 1; iic_data = 1;
开始标志 在iic_clk 为高电平的时候iic_data 产生一个下降沿
结束标志 在iic_clk位高电平的时候,iic_data产生一个上升沿 释放掉对总线的控制权。
类比uart学习:总共分为两大阶段
第一大阶段
空闲状态
产生开始标志
写从机设备ID 从机在高电平中间抓取iic_data 的数据。一次写八个时钟周期具体为7位ID地址+1位读写控制R/!W 此时的读写控制必须为写不管你是想要读还是写 就是这么不讲理 所以第八位为0
第九个时钟周期 放弃对iic_data 的控制 从机控制iic_data产生一个低电平的ACK (即下文的don’t care)
写寄存器地址 同写从机设备ID(但是全是地址,没有读写控制位)
第九个时钟周期 同上
先总结一个下:ID地址(7位ID地址+1位读写控制+don’tcare) + 要写的寄存器地址(8位寄存器地址+don’t care)
此时算是确定了设备对象和要写的寄存器地址
在此插播一段SCCB和IIC的区别。
因为SCCB没有重复起始的概念,因此在SCCB的读周期中,当主机发送完片内寄存器地址后,必须发送总线停止条件。不然在发送读命令时,从机将不能产care响应信号。即每一个传输都要有开始和结束来释放总线 (start +sotp),这也是SCCB与I2C不同的一个地方。而I2C只需要产生一个start就可以了。
重复起始的概念:在主控器控制总线期间完成了一次数据通信(发送或接收)之后,如果想继续占用总线再进行一次数据通信(发送或接收),而又不释放总线,就需要利用重启动Sr信号时序。重启动信号Sr既作为前一次数据传输的结束,又作为后一次数据传输的开始。利用重启动信号的优点是,在前后两次通信之间主控器不需要释放总线,这样就不会丢失总线的控制权,即不让其他主器件节点抢占总线。
第二大阶段
接下来才是读写的不同:
假设是写:
- 则只需要在向里面数据就行,就像写ID和写地址一样简单。同时对方必然会给你发一个ACK信号,这就是个握手信号,少不了的。
- 写完后产生结束标志
但是读操作这个调皮孩子就麻烦很多了
假设是读: - 产生一个结束标志 释放掉对总线的控制权。
- 产生一个开始标志 在申请到总线的控制权。
- 向里面再写一次设备ID+1位读写控制位 此时读写控制为读,也就是1。当你写完设备ID+读写控制位后,从机就翻身独立把歌唱,拿到了数据线的控制权(ack时,也是从机掌控数据线的控制权,但是毕竟太短,不够过瘾)
- 拿到控制权后,从机会保在的iic_clk每个高电平时候iic_data线上的数据稳定有效。
- 当从机给主机传输了8bit数据后,主机给从机发送一个NA_ACK 。该响应为高电平。
- 产生一个stop标志
这就算一次读操作完成了。
当然了理想很丰满,现实很骨感。写操作很快就实现,读用了4天的时间,期间又是查手册看时序,又是用chipscrop抓信号,时序图都符合,但是一直读到的是全零。身心俱疲,实在懒得改了,就先这样吧。
代码奉上,有人要是看出来哪有问题,记得告诉我。
`timescale 1ns/1ns
module iic_control(
input sys_clk , //sys_clk = 20KHz
input rst_n ,
//config_reg interface
input w_data_en ,
input r_ov_en ,
input [ 7:0] config_addr ,
input [ 7:0] config_data ,
output reg config_end ,
output reg r_end ,
output reg [ 7:0] r_data ,
//ov7670
output reg iic_clk , //10KHz
inout iic_data
);
parameter IDLE = 4'd0 ;
parameter START = 4'd1 ;
parameter ID_ADDR = 4'd2 ;
parameter ACK_ID = 4'd3 ;
parameter REG_ADDR = 4'd4 ;
parameter ACK_AD = 4'd5 ;
parameter W_DATA = 4'd6 ;
parameter ACK_W = 4'd7 ;
parameter R_STOP = 4'd8 ;
parameter R_START = 4'd9 ;
parameter R_ID_ADDR = 4'd10 ;
parameter ACK_RI = 4'd11 ;
parameter R_DATA = 4'd12 ;
parameter NA_ACK = 4'd13 ;
parameter STOP = 4'd14 ;
reg [ 7:0] id_addr_w = 8'h42;
reg [ 7:0] id_addr_r = 8'h43;
(*KEEP = "TRUE"*)
reg [ 3:0] state ;
(*KEEP = "TRUE"*)
reg [ 3:0] tx_cnt ;
reg wen ;
reg ren ;
reg iic_data_r ;
reg [ 7:0] r_data_r ;
//(*KEEP = "TRUE"*)
//reg iic_in_d ;
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
state <= IDLE ;
else case(state)
IDLE : if(w_data_en || r_ov_en)
state <= START ;
START : state <= ID_ADDR ;
ID_ADDR : if(tx_cnt == 15 )
state <= ACK_ID ;
ACK_ID : if(iic_clk && !iic_data) //iic_clk 在低电平的时候且从机响应
state <= REG_ADDR ;
REG_ADDR : if(tx_cnt == 15)
state <= ACK_AD ;
ACK_AD : if(iic_clk && !iic_data)
if(wen)
state <= W_DATA ;
else if(ren)
state <= R_STOP ;
//分支一 写
W_DATA : if(tx_cnt == 15)
state <= ACK_W ;
ACK_W : if(iic_clk && !iic_data)
state <= STOP ;
//分支二 读
R_STOP : if(iic_clk )
state <= R_START ;
R_START : state <= R_ID_ADDR ;
R_ID_ADDR : if(tx_cnt == 15)
state <= ACK_RI ;
ACK_RI : if(iic_clk && !iic_data)
state <= R_DATA ;
R_DATA : if(tx_cnt == 15)
state <= NA_ACK ;
NA_ACK : if(iic_clk )
state <= STOP ;
STOP : if(iic_clk && iic_data)
state <= IDLE ;
default : state <= IDLE ;
endcase
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
tx_cnt <= 'd0;
else case (state)
IDLE,START : tx_cnt <= 'd0;
//发送设备ID数据的计数
ID_ADDR : tx_cnt <= tx_cnt + 1'b1;
ACK_ID : tx_cnt <= 'd0;
//寄存器地址计数
REG_ADDR : tx_cnt <= tx_cnt + 1'b1;
ACK_AD : tx_cnt <= 'd0;
//写数据计数
W_DATA : tx_cnt <= tx_cnt + 1'b1;
ACK_W : tx_cnt <= 'd0;
//读寄存器地址计数
R_ID_ADDR : tx_cnt <= tx_cnt + 1'b1;
ACK_RI : tx_cnt <= 'd0;
R_DATA : tx_cnt <= tx_cnt + 1'b1;
NA_ACK : tx_cnt <= 'd0;
//停止
STOP : tx_cnt <= 'd0;
default : tx_cnt <= 'd0;
endcase
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
iic_clk <= 1'd1;
else if(state == IDLE || state == R_STOP || state == STOP)
iic_clk <= 1'd1;
else
iic_clk <= ~iic_clk;
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
wen <= 1'd1;
else if(state == IDLE)
wen <= w_data_en;
else if(state == STOP)
wen <= 1'd0;
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
ren <= 1'd1;
else if(state == IDLE)
ren <= r_ov_en;
else if(state == STOP)
ren <= 1'd0;
end
always@(negedge sys_clk or negedge rst_n)begin
if(!rst_n)
iic_data_r <= 1'd1;
else case(state)
IDLE : iic_data_r <= 1'd1;
START : iic_data_r <= 1'd0;
ID_ADDR : iic_data_r <= id_addr_w [7 - tx_cnt[3:1]];
ACK_ID : iic_data_r <= 1'dz;
REG_ADDR : iic_data_r <= config_addr [7 - tx_cnt[3:1]];
ACK_AD : iic_data_r <= 1'dz;
W_DATA : iic_data_r <= config_data [7 - tx_cnt[3:1]];
ACK_W : iic_data_r <= 1'dz;
R_STOP : if(iic_clk) iic_data_r <= 1'd1;
else iic_data_r <= 1'd0;
R_START : iic_data_r <= 1'd0;
R_ID_ADDR : iic_data_r <= id_addr_r [7 - tx_cnt[3:1]];
ACK_RI : iic_data_r <= 1'dz;
R_DATA : iic_data_r <= 1'dz;
NA_ACK : iic_data_r <= 1'd1;
STOP : if(iic_clk) iic_data_r <= 1'd1;
else iic_data_r <= 1'd0;
default : iic_data_r <= 1'd1;
endcase
end
always@(negedge sys_clk or negedge rst_n)begin
if(!rst_n)
r_data_r <= 'hff;
else if(state == R_DATA && iic_clk)
r_data_r <= {r_data_r[6:0],iic_data};
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
r_data <= 'h00;
else if(ren && state == STOP)
r_data <= r_data_r;
end
assign iic_data = iic_data_r;
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
config_end <= 'd0;
else if( wen && state == STOP)
config_end <= 1'd1;
else
config_end <= 1'd0;
end
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
r_end <= 'd0;
else if(ren && state == STOP)
r_end <= 1'd1;
else
r_end <= 1'd0;
end
endmodule
没有回复内容