在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 8761|回复: 37

[求助] 关于I2C设计遇到麻烦(从机MAX9611)

[复制链接]
发表于 2015-1-29 19:10:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
我自己通过阅读I2C总线协议和MAX9611规格书,设计了I2C的主机,不过下载到FPGA验证时,通过示波器发现从机应答始终没有,而且不给输出,就是说从机的SDA端一直处于高阻接收状态,无论地址给的对和错出来的应答都是一样。

我不知道原因出在哪里,感觉我设计的时序是满足器件要求和协议要求(单个主机单个从机,没有考虑仲裁、中断等其他条件),各位麻烦看一下我的代码,指导一下,非常感谢!!!

下面我的设计:

module I2C(clk,  //20MHz工作时钟
           rst,     //异步低电平复位
           cs,     //下降沿触发模块工作
           r_w,    //0写,1读
           add,    //从机地址
           reg_add,    //寄存器地址
           data_i,     //要写的数据
           data_o,    //读的数据
           scl,          //scl
           sda,       //sda
           A0,        //MAX9611的设置,0就行了(与总线无关)
           A1        //MAX9611的设置,0就行了(与总线无关)
           );

input clk,rst;
input cs;
input r_w;
input[6:0] add;
input[7:0] reg_add;
input[7:0] data_i;
output[7:0] data_o;
output scl;
inout sda;
output A0;
output A1;

wire clk,rst;
wire cs;
wire r_w;
wire[6:0] add;
wire[7:0] data_i;
reg[7:0] data_o;
reg scl;
wire sda;

reg cs0,cs1;
wire STA;

reg[3:0] cnt;
reg[2:0] clk_cnt;
reg busy;
reg[4:0] state;
reg ACK;
reg sda_o;
wire sda_i;
reg ctrl;
reg f;


assign A0=0;
assign A1=0;

always@(posedge clk or negedge rst)      //cs延迟两个时钟周期,用于采集信号下降沿
    if(!rst)
        begin
            cs0<=1;
            cs1<=1;
        end
    else
        begin
            cs0<=cs;
            cs1<=cs0;
        end

assign STA=cs0&!cs1;    //下降沿开始

always@(posedge clk or negedge rst)    //主机工作
    if(!rst) busy<=0;
    else if(STA) busy<=1;
    else if(state==0) busy<=0;


always@(posedge clk or negedge rst)      //采用400KHz的scl频率,其中一个scl时钟分了5段过程
    if(!rst)
        begin
            cnt<=0;
            clk_cnt<=0;
        end
    else if(busy)
        begin
           if(cnt==4'd9)
                begin
                    cnt <= 0;
                    if(clk_cnt==3'd4) clk_cnt <= 0;
                    else clk_cnt <= clk_cnt+1;
                end
           else cnt<=cnt+1;
        end
    else
        begin
            cnt <= 0;
            clk_cnt <= 0;
        end


always@(posedge clk or negedge rst)        //状态机
    if(!rst) state<=0;
    else case(state)
        5'd0: if(STA) state<=5'd1;         //起始
        5'd1: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd2;      //写地址最高位
        5'd2: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd3;
        5'd3: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd4;
        5'd4: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd5;
        5'd5: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd6;
        5'd6: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd7;
        5'd7: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd8;
        5'd8: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd9;      //写地址最低位
        5'd9: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd10;    //读写位
        5'd10: if(clk_cnt==3'd4&cnt==4'd9)             //地址应答
                    if(ACK==1) state<=5'd29;
                    else if(f) state<=5'd11;
                    else state<=5'd20;
        5'd11: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd12;    //写数据最高位(寄存器地址也是在这写)
        5'd12: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd13;
        5'd13: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd14;
        5'd14: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd15;
        5'd15: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd16;
        5'd16: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd17;
        5'd17: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd18;
        5'd18: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd19;  //写数据最低位
        5'd19: if(clk_cnt==3'd4&cnt==4'd9)         //写应答
                    if(ACK==1) state<=5'd29;
                    else if(r_w==0) state<=5'd11;
                    else state<=5'd30;
        5'd20: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd21;     //读地址最高位
        5'd21: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd22;
        5'd22: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd23;
        5'd23: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd24;
        5'd24: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd25;
        5'd25: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd26;
        5'd26: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd27;
        5'd27: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd28;    //读地址最低位
        5'd28: if(clk_cnt==3'd4&cnt==4'd9)        //应答
                    if(ACK==1) state<=5'd29;
                    else state<=5'd20;
        5'd29: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd0;      //结束
        5'd30: if(clk_cnt==3'd4&cnt==4'd9) state<=5'd2;      //重新开始
        default: state<=5'd0;
    endcase

//对scl和sda_o的操作,根据规格书的时序要求,这里400Kscl的高度电平的时间设置会比较紧,但没啥问题
always@(posedge clk or negedge rst)
    if(!rst)      
        begin
            scl<=1;
            sda_o<=1;
            f<=1;
            ctrl<=0;
        end
    else case(state)
        5'd0: begin
                scl<=1;
                sda_o<=1;
                f<=1;
                ctrl<=0;
              end
        5'd1: if(clk_cnt==3'b000)
                begin
                    scl<=1;
                    sda_o<=1;
                    ctrl<=0;
                end
              else if(clk_cnt==3'b001)
                begin
                    scl<=1;
                    sda_o<=1;
                end
              else if(clk_cnt==3'b010)
                begin
                    scl<=1;
                    sda_o<=0;
                end
              else if(clk_cnt==3'b011)
                begin
                    scl<=1;
                    sda_o<=0;
                end
              else if(clk_cnt==3'b100)
                begin
                    scl<=0;
                    sda_o<=0;
                end
        5'd2: begin
                ctrl<=0;
                sda_o<=add[6];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd3: begin
                ctrl<=0;
                sda_o<=add[5];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd4: begin
                ctrl<=0;
                sda_o<=add[4];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd5: begin
                ctrl<=0;
                sda_o<=add[3];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd6: begin
                ctrl<=0;
                sda_o<=add[2];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd7: begin
                ctrl<=0;
                sda_o<=add[1];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd8: begin
                ctrl<=0;
                sda_o<=add[0];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd9: begin
                if(f) sda_o<=0;
                else sda_o<=1;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; ctrl<=1;end  end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd10: begin
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; if(f) ctrl<=0; end end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd11: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[7];
                else sda_o<=data_i[7];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd12: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[6];
                else sda_o<=data_i[6];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd13: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[5];
                else sda_o<=data_i[5];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd14: begin
                if(f) sda_o<=reg_add[4];
                else sda_o<=data_i[4];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd15: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[3];
                else sda_o<=data_i[3];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd16: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[2];
                else sda_o<=data_i[2];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd17: begin
                ctrl<=0;
                if(f) sda_o<=reg_add[1];
                else sda_o<=data_i[1];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd18: begin
                if(f) sda_o<=reg_add[0];
                else sda_o<=data_i[0];
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; ctrl<=1; end end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd19: begin
                sda_o<=0;
                if(clk_cnt==3'b000) begin scl<=0; if(cnt==0) f<=!f; end
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; ctrl<=0; end end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd20: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd21: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) scl<=1;
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd22: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd23: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd24: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd25: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd26: begin
                ctrl<=1;
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) scl<=0; end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd27: begin
                sda_o<=0;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; ctrl<=0; end end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd28: begin
                sda_o<=ACK;
                if(clk_cnt==3'b000) scl<=0;
                else if(clk_cnt==3'b001) scl<=0;
                else if(clk_cnt==3'b010) scl<=1;
                else if(clk_cnt==3'b011) begin if(cnt==4'b0100) begin scl<=0; if(!ACK) ctrl<=1; end end
                else if(clk_cnt==3'b100) scl<=0;
              end
        5'd29: if(clk_cnt==3'b000)
                begin
                    ctrl<=0;
                    scl<=0;
                    sda_o<=0;
                    f<=1;
                end
              else if(clk_cnt==3'b001)
                begin
                    scl<=1;
                    sda_o<=0;
                    f<=1;
                end
              else if(clk_cnt==3'b010)
                begin
                    scl<=1;
                    sda_o<=0;
                    f<=1;
                end
              else if(clk_cnt==3'b011)
                begin
                    scl<=1;
                    sda_o<=1;
                    f<=1;
                end
              else if(clk_cnt==3'b100)
                begin
                    scl<=1;
                    sda_o<=1;
                    f<=1;
                end
        5'd30: if(clk_cnt==3'b000)
                begin
                    ctrl<=0;
                    sda_o<=1;
                    scl<=0;
                end
              else if(clk_cnt==3'b001)
                begin
                    sda_o<=1;
                    if(cnt==4'b0010) scl<=1;
                end
              else if(clk_cnt==3'b010)
                begin
                    scl<=1;
                    if(cnt==4'b0110) sda_o<=0;
                end
              else if(clk_cnt==3'b011)
                begin
                    scl<=1;
                    sda_o<=0;
                end
              else if(clk_cnt==3'b100)
                begin
                    scl<=0;
                    sda_o<=0;
                end
        default: begin
                    ctrl<=0;
                    scl<=1;
                    sda_o<=1;
                 end
        endcase

//应答信号的采集
always@(posedge clk or negedge rst)
    if(!rst) ACK<=0;
    else if(state==5'd10||state==5'd19)
            begin
                if(clk_cnt==3'b010&&sda_i==1'b1) ACK<=1;
            end
    else if(state==5'd28) ACK<=1;
    else ACK<=0;

reg[7:0] data;
always@(posedge scl or negedge rst)     //读数据
    if(!rst)
              data<=0;
    else case(state)
            5'd10: data<=0;
            5'd19: data<=0;
            5'd20: data[7]<=sda_i;
            5'd21: data[6]<=sda_i;
            5'd22: data[5]<=sda_i;
            5'd23: data[4]<=sda_i;
            5'd24: data[3]<=sda_i;
            5'd25: data[2]<=sda_i;
            5'd26: data[1]<=sda_i;
            5'd27: data[0]<=sda_i;
            5'd28: data<=0;
            default: data<=0;
         endcase


always@(posedge clk or negedge rst)
    if(!rst)
        data_o<=0;
    else if(state==5'd28)
        data_o<=data;


assign sda=(ctrl)? 1'bz : sda_o;      //三态门
assign sda_i=sda;

endmodule

附上MAX9611的规格书,麻烦大家看看我问题在哪了,谢谢!!!
MAX9611AUB __规格书_Datasheet_资料.pdf (2.41 MB, 下载次数: 26 )
发表于 2015-1-30 09:12:50 | 显示全部楼层
回复 1# 子宁Zing


   可能是I/O驱动问题,一般来说I2C总线采用open-drain连接,需要外部上拉电阻。
 楼主| 发表于 2015-1-30 09:49:57 | 显示全部楼层
回复 2# rvnistelrooy

就是在两条线上加个上拉电阻吗??
 楼主| 发表于 2015-1-30 09:56:42 | 显示全部楼层
回复 2# rvnistelrooy

MAX9611对scl和sda的输入电流没有最低要求,但是不能大于200nA,而且我用示波器测量两条线的电压,均可以达到3.3V
发表于 2015-1-30 10:02:26 | 显示全部楼层
代码太长不看了。不知道你代码有什么问题,你自己仿真,有一个地方肯定有问题,scl和sda输出是OD的,所以你把scl和sda赋值0,然后控制scl和sda的oe信号,这样要不是高阻要不就输出0,高阻你接上啦其实就是相当于输出高。就是把你设计里的scl和sda变为scl_oe和sda_oe,再sda=0,scl=0。
 楼主| 发表于 2015-1-30 10:31:32 | 显示全部楼层
回复 5# wgej1987

     非常谢谢!
    上拉我还没接,不过我有控制sda的使能就是ctrl,在FPGA上实现的时候,确认sda在应答位主机已经设成高阻了,不过这时MAX9611也是高阻,线上的电荷没有变化(我发现在主机设成高阻之前是什么状态,拉高之后就是什么状态,电压有一点点变化幅度)。
    我还有一个问题,从机对scl有操作吗?scl应该不需要控制吧,我直接主机输出
发表于 2015-1-30 11:15:33 | 显示全部楼层
scl正常的协议好像是从机不控制的,不过我以前写过一个从机会把scl拉低的的i2c从机,就是从机来不及响应主机的命令,会在回完ACK后把scl拉低,等处理完命令再释放,不过这好像不是标准的i2c,已经有点忘了.你应该不用考虑。
 楼主| 发表于 2015-1-30 11:34:47 | 显示全部楼层
回复 7# wgej1987

   恩,规格书中也没有描述对SCL的操作。
   还有一个问题,我不知道自己理解对不对,关于应答的时序,应答时间是从第8位的scl下降沿开始到应答位的scl下降沿结束的吗??我就是在这段时间将sda设成高阻的
发表于 2015-1-30 11:57:08 | 显示全部楼层
恩,sda数据阶段只在scl为低的时候变换
 楼主| 发表于 2015-1-30 14:27:03 | 显示全部楼层
回复 9# wgej1987


    非常感谢,我再试试
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条

×

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-28 15:20 , Processed in 0.048276 second(s), 9 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表