|
马上注册,看完整文章,学更多FPGA知识。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
—边缘检测工程:sccb传输模块代码解析 作者:肖肖肖 本文为明德扬原创文章,转载请注明出处!
Sccb传输模块的功能: 按照sccb传输协议的时序,传输上游模块的摄像头配置指令。
一、 设计架构
下图是官方中给出的SCCB时序图,虽然是三线的但是我们也可以用来学习二线的时序。
1. 协议开始:在SIO_C高电平期间,SIO_D由高变低,表示传输的开始。如上图所示,在开始数据传输时,SIO_C要先处于高电平,由SIO_D(即本模块中的 sio_d_w 信号)先保持一段时间高电平并产生下降沿来标志传输开始,其中SIO_D 变低到 SIO_C 变低,最少 100ns。
2. 协议结束:在SIO_C高电平期间,SIO_D由高变低,表示数据传输的停止。如上图所示,在停止数据传输时,SIO_C也要首先置于高电平,由SIO_D(即本模块中的sio_d_w信号)产生上升沿并保持一段时间来标志传输结束,其中 SIO_C 变高到 SIO_D 变高,最少100ns。
3. 其他时候 ,SIO_D 只能在 SIO_C=0 时才变化。
4. 时序图中的 SIO_D 和 SIO_C,分别是本模块中的 sio_d_w 和 sio_c 信号。
5. 写寄存器的过程:a.协议开始;b.发送设备ID编号,等待;c. 发送要存入的寄存器地址,等待;d.发送要写入的数据;e.协议结束。
6. 读寄存器的时序:a.协议开始;b.发送设备ID编号,等待;c. 发送要读取的寄存器地址,等待;d. 协议结束;e.协议开始;f. 发送设备ID编号,等待;g. 读取Byte数据,NACK=1;h. 协议结束。
本模块计数器的架构图如下所示
A、Count_sck:时钟计数器。使用此计数器,来对传输1位数据时钟进行计数。本模块时钟是25M,周期为40ns,而SCCB传输1bit本模块设定为 120*40ns,所以此计数器每次要数120个。
B、Count_bit:位计数器。使用此计数器,要对传输1个阶段的位进行计数。根据sccb传输协议,读寄存器时序和写寄存器时序的位长度都是不一样的,可以通过变量bit_num来确定。
C、Count_duan:阶段计数器。使用此计数器,对读或者写的阶段进行计数。根据sccb传输协议,读寄存器状态需要两个阶段,而写寄存器状态只有一个阶段,所以通过变量duan_num来确定。
二、 举例说明
收到写使能 wen=1(同一时刻过来了 写数据wdata 和 寄存器地址数据sub_addr )时,产生如下波形。
如上图所示,收到 wen=1,并且 sub_addr=8’h12 和 wdata=8’h04,这意味着要向地址为8’h12的寄存器写数据8’h04因此各个信号如下变化:
sio_c 和 sio_d_w:波形首先产生了开始位。然后依次发送 8’h42(固定值)和 x=1,发送 8’h12(sub_addr)和 x=1,发送 8’h04(wdata)和 x=1。之后是结束位,最后是 2 个间隔位(固定发 11)。 至此整个过程结束。
en_sio_d_w:在收到 wr_en=1 开始,en_sio_d_w 就为 1,直到最后结束。
收到读使能ren=1(同一时刻过来了寄存器地址数据sub_addr,而wdata不关心)时,产生如下波形。
如上图所示,收到ren=1,并且sub_addr=8’h12(注意要展开来看,看rd_en=1时刻) ,这意味着要读地址为8’h12的寄存器的数据,因此各个信号如下变化
sio_c、sio_d_w、rdata和rdata_vld:波形首先产生了开始位;然后依次发送8’h42(固定值)和x=1;然后再发送8’h12(sub_addr)和x=1,然后是结束位和间隔位。之后又是开始位,发送8’h43(固定值)和x=1。之后在8个sio_c周期内,取sio_d_r的值并保存到rdata中(8个值保存后,就可以产生1个时钟周期的rdata_vld=1脉冲)。最后是结束位和间隔位(固定发11)。 至此整个过程结束。
en_sio_d_w:在收到rd_en=1开始,en_sio_d_w就为1,直到读取数据为0,读取数据后为1,整个过程结束后又为0。
本模块准备好指示信号rdy在整个波形产生期间,以及wen=1和ren=1时,均为0(rdy可以组合逻辑产生并输出)。
三、 信号意义
信号 | | | | | | | | | | | | | | 当读使能信号为1的时候,本模块进入忙状态,同时进入读数据状态和打开sccb数据总线的三态门。 | | | | | | 当写使能信号为1的时候,本模块进入忙状态,同时进入写数据状态和打开sccb数据总线的三态门。 | | | | | | | | | | | | | | | | | | 设计逻辑:当SCCB传输完成并且是处于读寄存器状态时,表示数据接收完毕,并且一个时钟的有效指示信号。并且sccb传输数据完成时 | | | | | | | | | | | | sccb接口中的时钟信号,频率约为208.3100k。 | | | 设计逻辑:在SCCB时钟高变低时刻(sio_c_h2l)变低;在SCCB时钟低变高时刻(sio_c_l2h)变高。 | | | | | | | | | | | | | | | | | | 设计逻辑:收到读命令或者写命令时,就要立刻打开使能;当整个读写结束时,就要关闭三态门;此外,在读状态的第二个阶段,第10个至18个数据期间,三态门要关闭,因为此时要从总线上获取读到的数据。 | | | | | | 设计逻辑:在数据变换时刻,将待传输数据,按照位计数器的顺序,传输到本信号上。 | | | 工作状态读寄存器状态信号。当其为1时,表示处于读或者写状态。 | | | 设计逻辑:当收到读或者写命令时,就进入工作状态;当阶段计数器数完后,就返回到空闲状态。 | | | 读寄存器状态从收到读使能信号开始到一个完整的sccb传输时序结束。 | | | 1:读寄存器状态,此时sccb时序有两个阶段,每个阶段有21 bit长,第一阶段设备ID编号为8’h42,第二阶段设备ID编号为8’h43,并把对应的sccb时序数据赋给Sccb传输数据信号。 | | | | | | | | | | | | 设计逻辑:收到写命令就处于写状态;收到读命令就处于读状态。写寄存器状态从收到写使能信号开始到一个完整的sccb传输时序结束。 | | | 1:写寄存器状态,此时sccb时序只有一个阶段,该阶段有30 bit长,设备ID编号固定为8’h42,并把对应的sccb时序数据赋给Sccb传输数据信号。 |
下面展出本模块的设计,欢迎进一步交流,如果需要源代码,欢迎与本人联系。 - module sccb(
- clk ,
- rst_n ,
- ren ,
- wen ,
- sub_addr ,
- rdata ,
- rdata_vld ,
- wdata ,
- rdy ,
- sio_c ,
- sio_d_r ,
- en_sio_d_w,
- sio_d_w
- );
- //参数定义
- parameter SIO_C = 120 ;
- //输入信号定义
- input clk ;//25m
- input rst_n ;
- input ren ;
- input wen ;
- input [7:0] sub_addr ;
- input [7:0] wdata ;
- //输出信号定义
- output[7:0] rdata ;
- output rdata_vld;
- output sio_c ;//208kHz
- output rdy ;
- input sio_d_r ;
- output en_sio_d_w;
- output sio_d_w ;
- reg en_sio_d_w;
- reg sio_d_w ;
- //输出信号reg定义
- reg [7:0] rdata ;
- reg rdata_vld;
- reg sio_c ;
- reg rdy ;
- //中间信号定义
- reg [7:0] count_sck ;
- reg [4:0] count_bit ;
- reg [1:0] count_duan ;
- reg flag_add ;
- reg flag_sel ;
- reg [4:0] bit_num ;
- reg [1:0] duan_num ;
- reg [29:0] out_data ;
- wire add_count_sck ;
- wire end_count_sck ;
- wire add_count_bit ;
- wire end_count_bit ;
- wire add_count_duan;
- wire end_count_duan;
- wire sio_c_h2l ;
- wire sio_c_l2h ;
- wire out_data_time ;
- wire rdata_time ;
- wire [7:0] rd_com ;
-
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- count_sck <= 0;
- end
- else if(add_count_sck)begin
- if(end_count_sck)begin
- count_sck <= 0;
- end
- else begin
- count_sck <= count_sck + 1;
- end
- end
- end
- assign add_count_sck = flag_add ;
- assign end_count_sck = add_count_sck && count_sck == SIO_C-1;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- count_bit <= 0;
- end
- else if(add_count_bit)begin
- if(end_count_bit)begin
- count_bit <= 0;
- end
- else begin
- count_bit <= count_bit + 1;
- end
- end
- end
- assign add_count_bit = end_count_sck;
- assign end_count_bit = add_count_bit && count_bit == bit_num+2-1;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- count_duan <= 0;
- end
- else if(add_count_duan)begin
- if(end_count_duan)begin
- count_duan <= 0;
- end
- else begin
- count_duan <= count_duan + 1;
- end
- end
- end
- assign add_count_duan = end_count_bit;
- assign end_count_duan = add_count_duan && count_duan == duan_num-1;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- flag_add <= 0;
- end
- else if(ren || wen)begin
- flag_add <= 1;
- end
- else if(end_count_duan)begin
- flag_add <= 0;
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- flag_sel <= 0;
- end
- else if(wen)begin
- flag_sel <= 0;
- end
- else if(ren)begin
- flag_sel <= 1;
- end
- end
- always @(*)begin
- if(flag_sel==1)begin
- bit_num = 21;
- duan_num = 2;
- end
- else begin
- bit_num = 30;
- duan_num = 1;
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- sio_c <= 1;
- end
- else if(sio_c_h2l)begin
- sio_c <= 0;
- end
- else if(sio_c_l2h)begin
- sio_c <= 1;
- end
- end
- assign sio_c_h2l = count_bit >= 0 && count_bit < (bit_num-2) && add_count_sck && count_sck == SIO_C-1;
- assign sio_c_l2h = add_count_sck && count_sck == SIO_C/2-1;
- always @ (*)begin
- if(flag_sel==1)begin
- out_data <= {1'h0,rd_com,1'h1,sub_addr,1'h1,1'h0,1'h1,9'h0};
- end
- else begin
- out_data <= {1'h0,8'h42,1'h1,sub_addr,1'h1,wdata,1'h1,1'h0,1'h1};
- end
- end
- assign rd_com = (flag_sel==1 && count_duan == 0)? 8'h42 : 8'h43;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- en_sio_d_w <= 0;
- end
- else if(ren || wen)begin
- en_sio_d_w <= 1;
- end
- else if(end_count_duan)begin
- en_sio_d_w <= 0;
- end
- else if(flag_sel==1 && count_duan == 1 && count_bit == 10 && add_count_sck && count_sck == 1-1)begin
- en_sio_d_w <= 0;
- end
- else if(flag_sel==1 && count_duan == 1 && count_bit == 18 && add_count_sck && count_sck == 1-1)begin
- en_sio_d_w <= 1;
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- sio_d_w <= 1;
- end
- else if(out_data_time)begin
- sio_d_w <= out_data[30-count_bit-1];
- end
- end
- assign out_data_time = count_bit >= 0 && count_bit < bit_num && add_count_sck && count_sck == SIO_C/4-1;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- rdata <= 0;
- end
- else if(rdata_time)begin
- rdata[17-count_bit] <= sio_d_r;
- end
- end
- assign rdata_time = flag_sel==1 && count_duan==1 && count_bit>=10 && count_bit<18 && add_count_sck && count_sck==SIO_C/4*3-1;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- rdata_vld <= 0;
- end
- else if(flag_sel==1 && end_count_duan)begin
- rdata_vld <= 1;
- end
- else begin
- rdata_vld <= 0;
- end
- end
- always @(*)begin
- if(ren || wen || flag_add)begin
- rdy = 0;
- end
- else begin
- rdy = 1;
- end
- end
- endmodule
复制代码
|
|