明德扬小冉 发表于 2019-12-7 11:27:34

【基于FPGA的图像处理工程】边缘检测工程之sccb传输模块代码解析

【基于FPGA的图像处理工程】                                                                     —边缘检测工程: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可以组合逻辑产生并输出)。

      
       三、      信号意义


信号类型意义
clk 输入信号时钟信号,时钟频率为25M。
rst_n 输入信号复位信号,低电平有效。
ren 输入信号读使能信号。
wen 输入信号当读使能信号为1的时候,本模块进入忙状态,同时进入读数据状态和打开sccb数据总线的三态门。
sub_addr输入信号写使能信号。
wdata 输入信号当写使能信号为1的时候,本模块进入忙状态,同时进入写数据状态和打开sccb数据总线的三态门。
rdata 输出信号寄存器地址。
rdata_vld 输出信号读写的寄存器共用的地址。
rdy       输出信号向寄存器写的数据信号。
sio_c 输出信号从寄存器读到的数据信号。
sio_d_r 输入信号从寄存器读到的读出的数据有效指示信号。
en_sio_d_w输出信号设计逻辑:当SCCB传输完成并且是处于读寄存器状态时,表示数据接收完毕,并且一个时钟的有效指示信号。并且sccb传输数据完成时
sio_d_w   输出信号本模块准备好指示信号。
flag_addr    内部信号0:本模块忙,下一个时钟不要发命令过来;
flag_selw    内部信号1:本模块空闲,下一个时钟可以发命令过来。
count_sck 内部信号sccb接口中的时钟信号,频率约为208.3100k。
add_count_sck 内部信号设计逻辑:在SCCB时钟高变低时刻(sio_c_h2l)变低;在SCCB时钟低变高时刻(sio_c_l2h)变高。
end_count_sck 内部信号从sccb数据总线中读到的信号。
count_bit 内部信号设计逻辑:该信号直接取自三态门的数据。
add_count_bit 内部信号sccb三态数据总线中三态门打开使能。
end_count_bit 内部信号0:关闭;
bit_num   内部信号1:打开。
count_duan内部信号设计逻辑:收到读命令或者写命令时,就要立刻打开使能;当整个读写结束时,就要关闭三态门;此外,在读状态的第二个阶段,第10个至18个数据期间,三态门要关闭,因为此时要从总线上获取读到的数据。
add_count_duan内部信号每次写到sccb三态数据总线中的数据。
end_count_duan内部信号设计逻辑:在数据变换时刻,将待传输数据,按照位计数器的顺序,传输到本信号上。
duan_num内部信号工作状态读寄存器状态信号。当其为1时,表示处于读或者写状态。
out_data内部信号设计逻辑:当收到读或者写命令时,就进入工作状态;当阶段计数器数完后,就返回到空闲状态。
sio_c_h2l   内部信号读寄存器状态从收到读使能信号开始到一个完整的sccb传输时序结束。
sio_c_l2h   内部信号1:读寄存器状态,此时sccb时序有两个阶段,每个阶段有21 bit长,第一阶段设备ID编号为8’h42,第二阶段设备ID编号为8’h43,并把对应的sccb时序数据赋给Sccb传输数据信号。
en_sio_d_w_h2l内部信号读或者写状态区分写寄存器状态信号。
en_sio_d_w_l2h内部信号1:读状态
out_data_time 内部信号0: 写状态
rdata_time    内部信号设计逻辑:收到写命令就处于写状态;收到读命令就处于读状态。写寄存器状态从收到写使能信号开始到一个完整的sccb传输时序结束。
rd_com      内部信号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          sub_addr ;
    input          wdata    ;

    //输出信号定义
    output         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          rdata    ;
    reg               rdata_vld;
    reg               sio_c    ;
    reg               rdy      ;

    //中间信号定义
    reg          count_sck   ;
    reg          count_bit   ;
    reg          count_duan    ;
    reg               flag_add      ;
    reg               flag_sel      ;
    reg          bit_num       ;
    reg          duan_num      ;
    reg         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           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;
      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 <= 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










      明德扬专注FPGA研究,我司正在连载两本书籍:《基于FPGA至简设计法实现的图像边缘检测系统》(http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=691)、《ASIC和FPGA时序约束理论与应用》(http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=705),有兴趣点击观看。也欢迎加入群(838209674),及时获取最新的文章信息,个性化问题也可以找我哦:Q1479512800(肖肖肖)。
页: [1]
查看完整版本: 【基于FPGA的图像处理工程】边缘检测工程之sccb传输模块代码解析