IIC从机模块

`timescale 1ns / 1ps

//

// Company:

// Engineer:

//

// Create Date: 2020/03/15 15:59:00

// Design Name:

// Module Name: iic_tx

// Project Name:

// Target Devices:

// Tool Versions:

// Description:

//

// Dependencies:

//

// Revision:

// Revision 0.01 - File Created

// Additional Comments:

//

//


 

module iic_master(

    input clk,

    input arst,

    input i_send_en,

    input i_recv_en,//

    input [6:0] i_dev_addr,

    input [7:0] i_word_addr,

    input [7:0] i_write_dat,

    output reg o_send_done,

    output reg o_recv_done,//

    output reg [7:0] o_recv_dat,

    output reg sda_mode,

    output reg sda_reg,

    output o_scl,

    inout io_sda

    );

parameter   C_DIV_SEL  = 10'd500;

parameter   C_DIV_SEL0 = (C_DIV_SEL>>2) -1,                 // 用来产生IIC总线SCL高电平最中间的标志位

            C_DIV_SEL1 = (C_DIV_SEL>>1) -1,                

            C_DIV_SEL2 = (C_DIV_SEL0 + C_DIV_SEL1) +1,     // 用来产生IIC总线SCL低电平最中间的标志位

            C_DIV_SEL3 = (C_DIV_SEL>>1) +1;                // 用来产生IIC总线SCL下降沿标志位

parameter   IDLE      = 4'd0,    LOAD_DEV_ADDR  = 4'd1,     LOAD_WORD_ADDR  = 4'd2,     LOAD_DATA       = 4'd3,

            START_SIG = 4'd4,    SEND_BYTE      = 4'd5,     WAIT_ACK        = 4'd6,     CHECK_ACK       = 4'd7,

            STOP_SIG  = 4'd8,    W_OR_R_DONE    = 4'd9,     SEND_NOACK      =4'd10,     LOAD_DEV_ADDR_R = 4'd11,

            GET_BYTE  = 4'd12,   RDY_FOR_STOP   = 4'd13;

//

reg [9:0] scl_cnt;

reg       scl_en;

reg [3:0] state;

//reg       sda_mode;

//reg       sda_reg;

reg [7:0] load_dat;

reg [3:0] bit_cnt;

reg       ack_flag;

reg [3:0] jump_state;

reg [7:0] read_dat;

wire scl_l_mid;

wire scl_h_mid;

wire scl_neg,scl_pos;

// assign io_sda = sda_mode ? sda_reg: 1'bz;//inout

//

reg [1:0] scl_r;

assign scl_neg = (scl_r == 2'b10) ? 1:0;

assign scl_pos = (scl_r == 2'b01) ? 1:0;

always@(posedge clk , posedge arst)

if(arst)

    scl_r <= 0;

else

    scl_r <= {scl_r[0],o_scl};

always@(posedge clk ,posedge arst )

if(arst)

    scl_cnt <= 0;

else if(scl_en)

    if(scl_cnt == C_DIV_SEL-1)

        scl_cnt <= 0;

    else

        scl_cnt <= scl_cnt + 1;

else

    scl_cnt <= 0;

   

assign o_scl = (scl_cnt <= C_DIV_SEL1) ? 1 : 0;    

assign scl_l_mid = (scl_cnt == C_DIV_SEL2) ? 1 : 0;    

assign scl_h_mid = (scl_cnt == C_DIV_SEL0) ? 1 : 0;      

///

always @(posedge clk ,posedge arst)

if(arst)begin

    state <= IDLE;

    sda_mode <= 1;      //默认输出

    sda_reg <= 1;       //空闲状态为1

    scl_en <= 0;

    load_dat <= 0;

    bit_cnt <= 0;

    o_send_done <= 0;

    jump_state <= 0;

    ack_flag <= 0;  

    read_dat <= 0;    //  

    o_recv_dat <= 0;  //

    o_recv_done <= 0; //      

end

else if(i_send_en || i_recv_en)

    case(state)

        IDLE:

        begin

            state <= LOAD_DEV_ADDR;        

            sda_mode <= 1;    

            sda_reg <= 1;  

            scl_en <= 0;

            load_dat <= 0;  

            bit_cnt <= 0;      

            o_send_done <= 0;  

            jump_state <= 0;  

            ack_flag <= 0;    

        end

        LOAD_DEV_ADDR:

        begin

            state <= START_SIG;

            load_dat <= {i_dev_addr,1'b0};//第一次写或者读操作都是0

            jump_state <= LOAD_WORD_ADDR;

            //其他保持

        end

        LOAD_WORD_ADDR:

        begin

            state <= SEND_BYTE;

            load_dat <= i_word_addr;

            if(i_send_en)

            begin

                jump_state <= LOAD_DATA;  

            end

            else if(i_recv_en)

            begin

                jump_state <= LOAD_DEV_ADDR_R;//第二次加载,末尾为1

            end  

        end

        LOAD_DEV_ADDR_R:

        begin

            state <= START_SIG;

            load_dat <= {i_dev_addr,1'b1};//读操作第二次是1

            jump_state <= GET_BYTE;

        end

        LOAD_DATA:

        begin

            state <= SEND_BYTE;      

            load_dat <= i_write_dat;

            jump_state <= STOP_SIG;

        end

        START_SIG:

        begin

            scl_en <= 1;

            sda_mode <= 1;

            if(scl_h_mid)

            begin

                state <= SEND_BYTE;

                sda_reg <= 0;

            end

            else

            begin

                state <= START_SIG;

                sda_reg <= sda_reg;

            end

        end

        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      

        CHECK_ACK:

        begin

            scl_en <= 1;

            if(!ack_flag)//ACK有效

                if(scl_neg)

                begin

                    state <= jump_state;

                    sda_mode <= 1;

                    if(i_send_en)sda_reg <= 0;//读取完应答信号以后要把SDA信号设置成输出并拉低,因为如果这个状态后面是停止状态的话,需要SDA信号的上升沿,所以这里提前拉低它

                    else if(i_recv_en) sda_reg <= 1;//把SDA信号设置成输出并拉高方便产生第二次起始信号

                    else sda_reg <= 0;

                end

                else

                    state <= CHECK_ACK;                  

            else

                state <= IDLE;//出错?

        end

        GET_BYTE:

        begin

            scl_en <= 1;

            sda_mode <= 0;

            sda_reg  <= 0;

 /

        if( scl_neg && bit_cnt == 8)  //需要在低电平中间进行ACK

         begin

             bit_cnt <= 0;

             state <= SEND_NOACK; //注意

             read_dat <=  read_dat ;  

             o_recv_dat <= read_dat;

             o_recv_done <= 1;

         end

         else if( scl_h_mid && o_scl) //在一次写结束后到下一次写开始的scl的周期会大于500,导致一个周期多次采样,因此要加上&& i_scl

         begin

             bit_cnt <= bit_cnt + 1;

             state <= GET_BYTE;

             read_dat <= {read_dat[6:0],io_sda};

             o_recv_dat <= 0;

             o_recv_done <= 0;

         end

         else

             state <= GET_BYTE;

 //          

        end

        SEND_NOACK:

        begin

            scl_en <= 1;

            sda_mode    <= 1;

            if(scl_l_mid)

            begin

                state <= RDY_FOR_STOP;

                sda_reg <= 1; //注意 noack 为1

            end

            else

            begin

                state   <= SEND_NOACK;

            end

        end

        RDY_FOR_STOP:

        begin

            scl_en    <= 1;

            sda_mode  <= 1;  

            if(scl_neg)

            begin

                state <= STOP_SIG;

                sda_reg <= 0; //后面是停止状态,需要SDA信号的上升沿,所以这里提前拉低它

            end

            else

            begin

                state <= RDY_FOR_STOP;

            end      

        end

        STOP_SIG:

        begin

            scl_en <= 1;

            sda_mode <= 1;

            if(scl_h_mid)

            begin

                state <= W_OR_R_DONE;

                sda_reg <= 1;        

            end

            else

                state <= STOP_SIG;

        end

        W_OR_R_DONE:

        begin

            state <= IDLE;

            scl_en <= 0;  

            sda_mode <= 1;  

            sda_reg <= 1;    

            load_dat <= 0;  

            bit_cnt <= 0;    

            o_send_done <= 1;

            jump_state <= 0;

            ack_flag <= 0;  

            o_recv_dat <= 0;  

            o_recv_done <= 0;    

        end        

        default:    state <= IDLE;

        endcase

else

begin

    state <= IDLE;

    scl_en <= 0;  

    sda_mode <= 1;  

    sda_reg <= 1;    

    load_dat <= 0;  

    bit_cnt <= 0;    

    o_send_done <= 0;

    jump_state <= 0;

    ack_flag <= 0;        

end

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值