FPGA I2C通讯无应答

2020-02-23 10:51发布

1.示波器得到的信号,如下。从图中可以看出,I2C时序:停止-开始-从机地址(0x80)-SDA拉高阻等待从机应答,但主机却迟迟得不到应答,同时,经过二十多个时钟后,会出现图中一开始的突然拉低,然后进入下一个状态。这个是什么情况?
wifi0s01625832558IMG_20140411_163121.jpg
2.另外,对于上图,还有个问题在于,不管是SCL还是SDA,VPP都在几百mv,这个也不对。以前都用单片机上拉电阻,但对于FPGA应该怎么写呢?
3.以前I2C通讯正常的时候,配置结果总是有不对的情况,SCL和SDA都是通过10cm左右杜邦线,连接FPGA于CMOS模块。这块是不是有什么影响?还是通过调整上拉电阻就可以解决呢?
谢过各位~
原始程序:
`timescale 1ns / 1ps

module iic(
                        led,led2,
                        clk,
                        scl,sda,rst_n,
                );

//外部接口
input clk;                // 50MHz
input rst_n;        //复位信号,低有效
output scl;                // I2C时钟端口
inout sda;                // I2C的数据端口
//---------------------------------------------
//分频部分
reg[2:0] cnt;        // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg[8:0] cnt_delay;        //500循环计数,产生iic所需要的时钟
reg scl_r;                //时钟脉冲寄存器
assign scl = scl_r;        //产生iic所需要的时钟

always @ (posedge clk or negedge rst_n)
        if(!rst_n) cnt_delay <= 9'd0;
        else if(cnt_delay == 9'd499) cnt_delay <= 9'd0;        //计数到10us为scl的周期,即100KHz
        else cnt_delay <= cnt_delay+1'b1;        //计算时间
        
always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) cnt <= 3'd5;
        else begin
                case (cnt_delay)
                        9'd124:        cnt <= 3'd1;        //cnt=1:scl高电平中间,用于数据采样
                        9'd249:        cnt <= 3'd2;        //cnt=2:scl下降沿
                        9'd374:        cnt <= 3'd3;        //cnt=3:scl低电平中间,用于数据变化
                        9'd499:        cnt <= 3'd0;        //cnt=0:scl上升沿
                        default: cnt <= 3'd5;
                        endcase
                end
end


`define SCL_POS                (cnt==3'd0)                //cnt=0:scl上升沿
`define SCL_HIG                (cnt==3'd1)                //cnt=1:scl高电平中间,用于数据采样
`define SCL_NEG                (cnt==3'd2)                //cnt=2:scl下降沿
`define SCL_LOW                (cnt==3'd3)                //cnt=3:scl低电平中间,用于数据变化


always @ (posedge clk or negedge rst_n)
        if(!rst_n) scl_r <= 1'b0;
        else if(cnt==3'd0) scl_r <= 1'b1;        //scl信号上升沿
           else if(cnt==3'd2) scl_r <= 1'b0;        //scl信号下降沿

//---------------------------------------------
                //读、写时序
parameter IDLE1        =6'd0;
parameter START1       =6'd1;  
parameter SEND_A1      =6'd2;
parameter SLV_ACK1    =6'd3;
parameter SEND_D1      =6'd4;
parameter SLV_ACK2    =6'd5;
parameter SEND_D1_HIGH     =6'd6;
parameter SLV_ACK3    =6'd7;
parameter SEND_D1_LOW   =6'd8;
parameter SLV_ACK4  =6'd9;
parameter STOP1        =6'd10;

reg[5:0] cstate;        //状态寄存器
reg sda_r;                //输出数据寄存器
reg sda_link;        //输出数据sda信号inout方向方向        
reg[3:0] num;        //
assign sda = sda_link ? sda_r:1'bz;


always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
                        cstate <= IDLE1;
                        sda_r <= 1'b1;
                        sda_link <= 1'b0;
                        num <= 4'd0;
                end
        else           
                case (cstate)
                        IDLE1:        
                                begin
                                        sda_link <= 1'b1;                        //数据线sda为input
                                        sda_r <= 1'b1;                        
                                        db_r <= `DEVICE_WRITE;        //送器件地址(写操作)
                                        cstate <= START1;               
                                end
                        START1:
                                begin
                                        if(`SCL_HIG)
                                                begin                //scl为高电平期间
                                                        sda_link <= 1'b1;        //数据线sda为output
                                                        sda_r <= 1'b0;                //拉低数据线sda,产生起始位信号
                                                        cstate <= SEND_A1 ;
                                                        num <= 4'd0;                //num计数清零
                                                end
                                        else
                                                cstate <= START1; //等待scl高电平中间位置到来
                                end
                        SEND_A1 :        begin
                                        if(`SCL_LOW) begin
                                                        if(num == 4'd8) begin        
                                                                        num <= 4'd0;                        //num计数清零
                                                                        sda_r <= 1'b1;
                                                                                //sda置为高阻态(input)
                                                                        cstate <= SLV_ACK1;
                                                                end
                                                        else begin
                                                                        cstate <= SEND_A1 ;
                                                                        num <= num+1'b1;
                                                                        case (num)
                                                                                4'd0: sda_r <= db_r[7];
                                                                                4'd1: sda_r <= db_r[6];
                                                                                4'd2: sda_r <= db_r[5];
                                                                                4'd3: sda_r <= db_r[4];
                                                                                4'd4: sda_r <= db_r[3];
                                                                                4'd5: sda_r <= db_r[2];
                                                                                4'd6: sda_r <= db_r[1];
                                                                                4'd7: sda_r <= db_r[0];
                                                                                default: ;
                                                                                endcase
                                    
                                                                end
                                                end
     
                                        else cstate <= SEND_A1 ;
                                end
                        SLV_ACK1:        begin

                                        if(`SCL_HIG) begin        //考虑应答位
                                                        sda_link <= 1'b0;
                                                        if(sda===1'b0)begin
                                                        cstate <= SEND_D1;        //从机响应信号
                                                        db_r <= `BYTE_ADDR;        // 1地址        
                                                        end
                                                end
                                        else cstate <= SLV_ACK1;                //等待从机响应
                                end
                        SEND_D1:        begin
                                        if(`SCL_LOW) begin
                                                        if(num==4'd8) begin        
                                                                        num <= 4'd0;                        //num计数清零
                                                                        sda_r <= 1'b1;
                                                                        sda_link <= 1'b0;                //sda置为高阻态(input)
                                                                        cstate <= SLV_ACK2  ;
                                                                end
                                                        else begin
                                                                        sda_link <= 1'b1;                //sda作为output
                                                                        num <= num+1'b1;
                                                                        case (num)
                                                                                4'd0: sda_r <= db_r[7];
                                                                                4'd1: sda_r <= db_r[6];
                                                                                4'd2: sda_r <= db_r[5];
                                                                                4'd3: sda_r <= db_r[4];
                                                                                4'd4: sda_r <= db_r[3];
                                                                                4'd5: sda_r <= db_r[2];
                                                                                4'd6: sda_r <= db_r[1];
                                                                                4'd7: sda_r <= db_r[0];
                                                                                default: ;
                                                                                endcase
                                 
                                                                        cstate <= SEND_D1;                                       
                                                                end
                                                end

                                        else cstate <= SEND_D1;                                
                                end
                        SLV_ACK2 :        begin
               
                                        if(`SCL_HIG) begin        //考虑应答位
                                                        sda_link <= 1'b0;
                                                        if(sda===1'b0)
                                                begin                //从机响应信号
                                                                cstate <= SEND_D1_HIGH;         //写操作
                                                                db_r <= `WRITE_DATA_H;        //写入的数据        
                                                           end
                                                end
                                        else
                                        cstate <= SLV_ACK2 ;        //等待从机响应
                                end
                        SEND_D1_HIGH:        begin
                                        if(`SCL_LOW) begin
                                                        if(num==4'd8) begin        
                                                                        num <= 4'd0;                        //num计数清零
                                                                        sda_r <= 1'b1;
                                                                        sda_link <= 1'b0;                //sda置为高阻态(input)
                                                                        cstate <= SLV_ACK3 ;
                                                                end
                                                        else begin
                                                                        sda_link <= 1'b1;                //sda作为output
                                                                        num <= num+1'b1;
                                                                        case (num)
                                                                                4'd0: sda_r <= db_r[7];
                                                                                4'd1: sda_r <= db_r[6];
                                                                                4'd2: sda_r <= db_r[5];
                                                                                4'd3: sda_r <= db_r[4];
                                                                                4'd4: sda_r <= db_r[3];
                                                                                4'd5: sda_r <= db_r[2];
                                                                                4'd6: sda_r <= db_r[1];
                                                                                4'd7: sda_r <= db_r[0];
                                                                                default: ;
                                                                                endcase
                  
                                                                        cstate <= SEND_D1_HIGH;                                       
                                                                end
                                                end

                                        else cstate <= SEND_D1_HIGH;                                
                                end
                        SLV_ACK3 :        begin
  
                                                if(`SCL_HIG) begin        //考虑应答位
                                                        sda_link <= 1'b0;
                                                        if(sda===1'b0)
                                                begin                //从机响应信号
                                                                cstate <= SEND_D1_LOW;         //写操作
                                                                db_r <= `WRITE_DATA_L;        //写入的数据        
                                                                        end
                                                end
                                        else cstate <= SLV_ACK3 ;        //等待从机响应
                                end
                        SEND_D1_LOW:        begin
                                        if(`SCL_LOW) begin
                                                        if(num==4'd8) begin        
                                                                        num <= 4'd0;                        //num计数清零
                                                                        sda_r <= 1'b1;
                                                                        sda_link <= 1'b0;                //sda置为高阻态(input)
                                                                        cstate <= SLV_ACK4 ;
                                                                end
                                                        else begin
                                                                        sda_link <= 1'b1;                //sda作为output
                                                                        num <= num+1'b1;
                                                                        case (num)
                                                                                4'd0: sda_r <= db_r[7];
                                                                                4'd1: sda_r <= db_r[6];
                                                                                4'd2: sda_r <= db_r[5];
                                                                                4'd3: sda_r <= db_r[4];
                                                                                4'd4: sda_r <= db_r[3];
                                                                                4'd5: sda_r <= db_r[2];
                                                                                4'd6: sda_r <= db_r[1];
                                                                                4'd7: sda_r <= db_r[0];
                                                                                default: ;
                                                                                endcase
               
                                                                        cstate <= SEND_D1_LOW;                                       
                                                                end
                                                end

                                        else cstate <= SEND_D1_LOW;                                
                                end
                        SLV_ACK4 :        begin

                                                if(`SCL_HIG) begin        //考虑应答位
                                                        sda_link <= 1'b0;
                                                        if(sda===1'b0)
                                                begin                //从机响应信号
                                                                cstate <= STOP1;         //写操作
                                                                sda_r <= 1'b1;
                                                                //db_r <= `WRITE_DATA;        //写入的数据
                                                        end
                                                end
                                        else
                                        cstate <= SLV_ACK4 ;        //等待从机响应
                                end
                        STOP1:        begin
                                        if(`SCL_LOW) begin
                                                        sda_link <= 1'b1;
                                                        sda_r <= 1'b0;
                                                        cstate <= STOP1;
                                                end
                                        else if(`SCL_HIG) begin
                                                        sda_r <= 1'b1;        //scl为高时,sda产生上升沿(结束信号)
                                                        cstate <= IDLE1;
                                                end
                                        else cstate <= STOP1;
                           end  
                        endcase                                
end
endmodule
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
9条回答
dxzky
2020-02-24 01:09
haitaox 发表于 2014-4-18 12:59
1.SDA处于半高的状态我之前碰到过,FPGA是I2C主设备,艾默生的电源是从设备。在I2C协议中,允许从设备延 ...

thx~最后问题已经排除了,一方面是由于我们对阻塞和非阻塞幅值的理解有问题,导致高阻状态有误,另一方面是设备本身存在一定的问题,从而导致了最后的现象。不过还是很感谢你的回答~

一周热门 更多>