IIC协议原理以及主机、从机Verilog实现

1.原理以及主机实现

原理可参考【接口时序】6、IIC总线的原理与Verilog实现,原理讲的很详细,其中也给出了IIC主机的实现思路以及Verilog,包括写数据与读数据的过程,分为两个module。但没有给出从机的Verilog实现,因此我按照其思路设计了相应的从机且为一个module内实现主机的写数据与读数据请求,另外我也将主机的写数据与读数据的合并为一个module。

iic_master.v :IIC主机,可实现写数据与读数据请求的时序。

iic_slave.v :IIC从机,可实现接收主机的写数据与读数据请求的时序。

iic_sim.v  :IIC仿真testbench,仿真需要注意的是inout端口sda需要在此文件内实现,单独放到主机或者从机文件中都会出错(一直是x)assign io_sda = sda_mode ? sda_reg: sda_reg_slave;//inout

2.IIC从机的实现

需要先看懂【接口时序】6、IIC总线的原理与Verilog实现

其实按照主机的思路依葫芦画瓢可以有个大概,主要注意几点

1.从机的scl边沿检测,原理很简单,reg一下并保存,01就是上升沿,10下降沿

assign scl_neg = (scl_r == 2'b10) ? 1:0;
assign scl_pos = (scl_r == 2'b01) ? 1:0;
assign sda_pos = (sda_r == 2'b01) ? 1:0;
assign sda_neg = (sda_r == 2'b10) ? 1:0;

always@(posedge clk , posedge arst)
if(arst)
begin
    scl_r <= 0;
    sda_r <= 0;
end
else
begin
    scl_r <= {scl_r[0],i_scl};
    sda_r <= {sda_r[0],io_sda};
end

3.从机不知道主机发的是写数据还是读数据时序,如何检测

读数据时序会有第二个start信号,因此从机在ACK后接收一个字节信号时需要先检测是否有start信号,来决定状态跳转。

GET_BYTE:
    begin
        sda_mode    <= 0;
        sda_reg     <= 0;
        if(i_scl && sda_neg)//检测是否有开始信号,有的话就是读操作
        begin
            state <= START_READ;    
            bit_cnt <= 0;
            receive_buff[0] <= 0;
        end
        else if( scl_neg && bit_cnt == 8)  //需要在低电平中间进行ACK,要提前进入ACK
        begin
            bit_cnt <= 0;
            state <= jump_state; //注意 
            receive_buff <=  receive_buff ;  
        end 
        else if( scl_h_mid && i_scl) //在一次写结束后到下一次写开始的scl的周期会大于500,导致一个周期多次采样,因此要加上&& i_scl
        begin
            bit_cnt <= bit_cnt + 1;
            state <= GET_BYTE;
            receive_buff <= {receive_buff[6:0],io_sda};
        end   
        else
            state <= GET_BYTE;
    end  

4.Verilog细节:主机的发送byte->接收ack与从机的接收byte->发送ack状态跳转的不同

主机代码

SEND_BYTE:
        begin
            scl_en <= 1;
            sda_mode <= 1;
            if(scl_l_mid)
                if(bit_cnt == 8)
                begin
                    bit_cnt <= 0;
                    state <= WAIT_ACK;
                    sda_reg <= sda_reg;
                end
                else
                begin
                    state <= SEND_BYTE;
                    sda_reg <= load_dat[7-bit_cnt] ;
                    bit_cnt <= bit_cnt + 1;
                end
            else
                state <= SEND_BYTE;//其他信号保持
        end       
WAIT_ACK:
        begin
            scl_en <= 1;
            sda_mode <= 0;//
            if(scl_h_mid)
            begin
                ack_flag <= io_sda;
                state <= CHECK_ACK;
            end
            else
            begin
                ack_flag <= ack_flag;
                state <= WAIT_ACK;
            end
        end       

注意到主机发送byte->接收ack条件是if(scl_l_mid) if(bit_cnt == 8),在第9个低电平中间跳转到WAIT_ACK,因为ACK是在接下来的高电平中间检测,这个没问题。

但是从机的接收byte->发送ack条件如果是if(scl_h_mid) if(bit_cnt == 8),那就出大问题了,我就是在这里出的问题。因为是在第9个高电平中间跳转到SEND_ACK,这是不对的,应该在第8个scl的下降沿后的scl_l_mid进行发送ack,因此跳转条件为if(scl_neg && bit_cnt == 8),即3中的代码。

最后,由于完整代码太长就没直接贴上来,有积分的可以直接积分下载,没有积分的可以留言我发给你。

CSDN下载链接://download.csdn.net/download/qq_34070723/12260897

你可能感兴趣的:(Verilog设计基础,经验与经典电路)