【基于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]