7.IIC驱动模块的代码

module i2c_ctrl
#(
    parameter DEVICE_ADDR  = 7'b1010_000      , //i2c 设备地址
    parameter SYS_CLK_FREQ = 26'd50_000_000   , //输入系统时钟频率
    parameter SCL_FREQ     = 18'd250_000        //i2c 设备 scl 时钟频率
)
(
    input    wire              sys_clk        ,
    input    wire              sys_rst_n      ,
    input    wire              wr_en          ,
    input    wire              rd_en          ,
    input    wire              addr_num       ,
    input    wire    [15:0]    byte_addr      ,
    input    wire    [7:0]     wr_data        ,
    input    wire              i2c_start      ,
    output   reg               i2c_clk        ,
    output   reg               i2c_end        ,
    output   reg    [7:0]      rd_data        ,
    output   reg               i2c_scl        ,
    inout    wire              i2c_sda      
);
/参数定义//
//这里是为了生成i2c_clk信号,这个信号频率是i2c_scl的4倍,因为从系统时钟/i2c_clk时钟表示一个周期的计数值,
//i2c_clk半个周期翻转一次,所以要÷2,为了生成i2c_clk又要÷4,所以一共是÷8
localparam CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ;
//状态机变量
localparam IDLE          =  'd0  ,
           START_1       =  'd1  ,
           SEND_D_ADDR   =  'd2  ,
           ACK_1         =  'd3  ,
           SEND_B_ADDR_H =  'd4  ,
           ACK_2         =  'd5  ,
           SEND_B_ADDR_L =  'd6  ,
           ACK_3         =  'd7  ,
           WR_DATA       =  'd8  ,
           ACK_4         =  'd9  ,
           STOP          =  'd10 ,
           START_2       =  'd11 ,
           SEND_RD_ADDR  =  'd12 ,
           ACK_5         =  'd13 ,
           RD_DATA       =  'd14 ,
           N_ACK         =  'd15 ;
/线网变量定义//
wire             w_sda_en           ;
wire             w_sda_in           ;
/寄存器变量定义
reg     [7:0]    r_cnt_clk          ;
reg     [3:0]    r_state            ;
reg     [2:0]    r_cnt_i2c_clk      ;
reg     [2:0]    r_cnt_bit          ;
reg              r_cnt_i2c_clk_en   ;
reg              r_ack              ;
reg              r_i2c_sda_reg      ;
reg   [7:0]      r_rd_data_reg      ;
/程序/
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_clk <= 'd0;
    else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
        r_cnt_clk <= 'd0;
    else
        r_cnt_clk <= r_cnt_clk +'d1;
end

always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        i2c_clk <= 'd0;
    else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
        i2c_clk <= ~i2c_clk;
    else
        i2c_clk <= i2c_clk;
end

//状态转移
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_state <= IDLE;
    else
    case(r_state)
        IDLE         : 
            begin
                if(i2c_start == 'd1)
                    r_state <= START_1;
                else 
                    r_state <= IDLE;
            end
        START_1      : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= SEND_D_ADDR;
                else
                    r_state <= START_1;
            end
        SEND_D_ADDR  : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_1;
                else
                    r_state <= SEND_D_ADDR;
            end
        ACK_1        : 
            begin
                if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
                    if(addr_num == 'd1)
                        r_state <= SEND_B_ADDR_H;
                    else
                        r_state <= SEND_B_ADDR_L;
                end
                else
                    r_state <= ACK_1;
            end
        SEND_B_ADDR_H: 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_2;
                else
                    r_state <= SEND_B_ADDR_H;
            end
        ACK_2        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= SEND_B_ADDR_L;
                else
                    r_state <= ACK_2;
            end
        SEND_B_ADDR_L: 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_3;
                else
                    r_state <= SEND_B_ADDR_L;
            end
        ACK_3        : 
            begin
                if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
                    if(wr_en == 'd1)
                        r_state <= WR_DATA;
                    else if(rd_en == 'd1)
                        r_state <= START_2;
                    else
                        r_state <= r_state;
                end
                else
                    r_state <= ACK_3;
            end
        WR_DATA      : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_4;
                else
                    r_state <= WR_DATA;
            end
        ACK_4        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= STOP;
                else
                    r_state <= ACK_4;
            end
        STOP         : 
             begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd3)
                    r_state <= IDLE;
                else
                    r_state <= STOP;
            end
        START_2      : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= SEND_RD_ADDR;
                else
                    r_state <= START_2;
            end
        SEND_RD_ADDR : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_5;
                else
                    r_state <= SEND_RD_ADDR;
            end
        ACK_5        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= RD_DATA;
                else
                    r_state <= ACK_5;
            end
        RD_DATA      : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= N_ACK;
                else
                    r_state <= RD_DATA;
            end
        N_ACK        : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= STOP;
                else
                    r_state <= N_ACK;
            end
        default      :r_state <= IDLE;
        endcase
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_i2c_clk_en <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        r_cnt_i2c_clk_en <= 'd0;
    else if(i2c_start == 'd1)
        r_cnt_i2c_clk_en <= 'd1;
    else
        r_cnt_i2c_clk_en <= r_cnt_i2c_clk_en;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_i2c_clk <= 'd0;
    else if(r_cnt_i2c_clk == 'd3)
        r_cnt_i2c_clk <= 'd0;
    else if(r_cnt_i2c_clk_en == 'd1)
        r_cnt_i2c_clk <= r_cnt_i2c_clk + 'd1;
    else 
        r_cnt_i2c_clk <= 'd0;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_bit <= 'd0;
    else if(r_state == START_1 || r_state == ACK_1 || r_state == ACK_2 
            ||r_state == ACK_3 || r_state == ACK_4 || r_state == ACK_5 
            ||r_state == START_2 || r_state == IDLE || r_state == N_ACK
            ) 
        r_cnt_bit <= 'd0;
    else if((r_state == SEND_D_ADDR || r_state == SEND_B_ADDR_H || r_state == SEND_B_ADDR_L
            || r_state == SEND_RD_ADDR || r_state == RD_DATA  || r_state == WR_DATA) 
            && r_cnt_bit == 'd7 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= 'd0;
    else if(r_cnt_i2c_clk_en == 'd1 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= r_cnt_bit+'d1;
    else
        r_cnt_bit <= r_cnt_bit;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        i2c_end <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        i2c_end <= 'd1;
    else 
        i2c_end <= 'd0;
end

always@(*)begin
    if(!sys_rst_n)
        i2c_scl <=  'd1;
    case(r_state)
        IDLE        :
                i2c_scl <= 'd1;
        START_1       :
            begin
                if(r_cnt_i2c_clk == 'd3)
                    i2c_scl <= 'd0;
                else
                    i2c_scl <= 'd1;
            end
        SEND_D_ADDR , SEND_B_ADDR_H , SEND_B_ADDR_L , WR_DATA 
        , ACK_1 , ACK_2 , ACK_3 , ACK_4 , ACK_5 , RD_DATA
        , START_2 , SEND_RD_ADDR , N_ACK:
            begin
                if(r_cnt_i2c_clk == 'd1 || r_cnt_i2c_clk == 'd2)
                    i2c_scl <= 'd1;
                else
                    i2c_scl <= 'd0;
            end
        STOP          :
            begin
                if(r_cnt_i2c_clk == 'd0 && r_cnt_bit == 'd0)
                    i2c_scl <= 'd0;
                else
                    i2c_scl <= 'd1;
            end
        default : i2c_scl <= 'd1;
        endcase
end

always@(*)begin
    if(!sys_rst_n)
        r_i2c_sda_reg <= 'd0;
    else
    case(r_state)
    IDLE          :r_i2c_sda_reg <= 'd1;
    START_1       :
        begin
            if(r_cnt_i2c_clk <= 'd0)
                r_i2c_sda_reg <= 'd1;
            else
                r_i2c_sda_reg <= 'd0;
        end
    SEND_D_ADDR   :
        begin
            if(r_cnt_bit <= 'd6)
                r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
            else
                r_i2c_sda_reg <= 'd0;
        end
    ACK_1         :r_i2c_sda_reg <= 'd1;
    SEND_B_ADDR_H :r_i2c_sda_reg <= byte_addr[15 - r_cnt_bit];
    ACK_2         :r_i2c_sda_reg <= 'd1;
    SEND_B_ADDR_L :r_i2c_sda_reg <= byte_addr[7 - r_cnt_bit];
    ACK_3         :r_i2c_sda_reg <= 'd1;
    WR_DATA       :r_i2c_sda_reg <= wr_data[7 - r_cnt_bit];
    ACK_4         :r_i2c_sda_reg <= 'd1;
    STOP          :
        begin
            if(r_cnt_bit == 'd0 && r_cnt_i2c_clk < 'd3)
                r_i2c_sda_reg <= 'd0;
            else
                r_i2c_sda_reg <= 'd1;
        end
    START_2       :
        begin
            if(r_cnt_i2c_clk <='d1)
                r_i2c_sda_reg <= 'd1;
            else
                r_i2c_sda_reg <= 'd0;
        end
    SEND_RD_ADDR  :
        begin
            if(r_cnt_bit <= 'd6)
                r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
            else
                r_i2c_sda_reg <= 'd1;
        end
    ACK_5         :r_i2c_sda_reg <= 'd1;
    RD_DATA       :r_i2c_sda_reg <= 'd1;
    N_ACK         :r_i2c_sda_reg <= 'd1;
    default       :r_i2c_sda_reg <= 'd1;
    endcase
end

assign w_sda_en = ((r_state == ACK_1) || (r_state == ACK_2) || (r_state == ACK_3) || (r_state == ACK_4)
                    || (r_state == ACK_5) || (r_state == RD_DATA))? 'd0 : 'd1;
                    
assign i2c_sda = (w_sda_en == 'd1)? r_i2c_sda_reg : 'bz;

assign w_sda_in = i2c_sda;
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
    if(!sys_rst_n)
        r_ack <= 'd0;
    else 
    case(r_state)
    IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
    WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
        r_ack <= 1'd1;
    ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
        if(r_cnt_i2c_clk == 'd0)
            r_ack <= w_sda_in;
        else 
            r_ack <= r_ack;
    default:r_ack <= 1'd1;
    endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_ack <= 'd0;
    else 
    case(r_state)
    IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
    WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
        r_ack <= 1'd1;
    ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
        if(r_cnt_i2c_clk == 'd1)
            r_ack <= w_sda_in;
        else 
            r_ack <= r_ack;
    default:r_ack <= 1'd1;
    endcase
end
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
    if(!sys_rst_n)
        r_rd_data_reg <= 'd0;
    else 
    case(r_state)
    IDLE:
        r_rd_data_reg <= 'd0;
    RD_DATA:
        begin
            if(r_cnt_i2c_clk == 'd2)
                r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
            else
                r_rd_data_reg <= r_rd_data_reg;
        end
    default :r_rd_data_reg <= r_rd_data_reg;
    endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_rd_data_reg <= 'd0;
    else 
    case(r_state)
    IDLE:
        r_rd_data_reg <= 'd0;
    RD_DATA:
        begin
            if(r_cnt_i2c_clk == 'd1)
                r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
            else
                r_rd_data_reg <= r_rd_data_reg;
        end
    default :r_rd_data_reg <= r_rd_data_reg;
    endcase
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rd_data <= 8'd0;
    else if(r_state == RD_DATA && r_cnt_bit == 3'd7 && r_cnt_i2c_clk == 2'd3)
        rd_data <= r_rd_data_reg;
    else
        rd_data <= rd_data;
end


endmodule

 注意:和野火官方的代码相比我这里做了两处修改,分别是对r_ack,和r_rd_data_reg信号的赋值上。如果用野火的代码,它用的是组合逻辑赋值的方式,但是在组合逻辑中如果把自己的值赋值给自己会产生锁存器,这是我们不想要的,在编译的时候会报警告
 所以在这里,我们r_ack和r_rd_data_reg用时序逻辑的方式进行赋值,效果是一样的。这里讲一下r_ack的赋值,野火的代码逻辑是一到ACK_1,ACK_2,ACK_3,ACK_4,ACK_5,就采样信号的输入,然后保持不变。但是我们在时序逻辑中r_ack的赋值时ACK_1,ACK_2,ACK_3,ACK_4,ACK_5状态下信号稳定的时候再去采样,感觉会更好一点,同时能产生锁存器的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值