读取时I2C异常延迟问题

读取时I2C异常延迟问题,c,avr,i2c,C,Avr,I2c,我一直在努力让我的态度85位的爆炸I2C(读/写)。我有以下配置: PB0 = SDA PB1 = LED PB2 = SCL 我可以毫无问题地写,但只有在读循环中有我的'delay()'函数的情况下,才可以读,到目前为止还不错: char i2c_read(void) { uint8_t B = 0; DDRB &= 0b11111110; // switch PB0 to input for ( int bit = 0; bit < 0x08; bi

我一直在努力让我的态度85位的爆炸I2C(读/写)。我有以下配置:

PB0 = SDA
PB1 = LED
PB2 = SCL
我可以毫无问题地写,但只有在读循环中有我的'delay()'函数的情况下,才可以读,到目前为止还不错:

char i2c_read(void)
{
    uint8_t B = 0;
    DDRB &= 0b11111110; // switch PB0 to input

    for ( int bit = 0; bit < 0x08; bit++ )
    {        
        delay(); // <--!!!!!!!!! the root of all evil

        SIGNAL_HIGH( PORT_SCL );

        B <<= 1;

        if( PINB & (1 << PB0 ) )
        {
            B |= 1;              
        } 
        else
        {
            B |= 0;              
        }

        SIGNAL_LOW( PORT_SCL );
    }

    DDRB |= 0b00000001; // switch PB0 as output

    i2c_nack();

    return B;
}
我怀疑我可能“锁定”了一个完美的延迟,该延迟产生了另一个设备所期望的适当信号长度,因此我尝试使用for循环和示波器进行相同的延迟:

void delay()
{
   for( int i=0; i<20; i++){ }
}
奇怪的是I2C又停止工作了!只有当我将PB1设置为高/低时,它才起作用。我仍然无法理解,我是否恰好确定了所需的完美延迟,只是碰巧打开PB1比打开PB3花费的时间更少,或者它与电路本身有关,LED在I2C上执行某种上拉/下拉功能(请原谅我的无知,我是初学者),但是PB1也没有连接到I2C线路

有人能解释一下为什么只有在我打开/关闭PB1时它才起作用,而不是真正的延迟吗?谢谢

全文来源:

#define PORT_SDA PB0
#define PORT_SCL PB2

#define SIGNAL_HIGH(PORT) PORTB |=  ( 1 << PORT )
#define SIGNAL_LOW(PORT)  PORTB &= ~( 1 << PORT )

void delay();
void LED_ON(void);
void LED_OFF(void);

void i2c_init(void);
void i2c_start(void);
char i2c_read(void);
void i2c_stop(void);
void i2c_nack(void);
void i2c_ack(void);
void i2c_ack_slave(void);
void i2c_write(uint8_t byte);

void i2c_init()
{
    DDRB = 0b00000010; // TODO: should be removed once the weird delay issue is solved
    DDRB |= ( 1 << PORT_SDA );
    DDRB |= ( 1 << PORT_SCL );
}

void i2c_start( void )
{
    SIGNAL_LOW(  PORT_SCL );
    SIGNAL_HIGH( PORT_SDA );
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_LOW(  PORT_SDA );
    SIGNAL_LOW(  PORT_SCL );
}

void i2c_stop( void )
{
    SIGNAL_LOW(  PORT_SCL );
    SIGNAL_LOW(  PORT_SDA );
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_HIGH( PORT_SDA );
}

void i2c_ack(void)
{
   SIGNAL_LOW(  PORT_SDA );
   SIGNAL_HIGH( PORT_SCL );
   SIGNAL_LOW(  PORT_SCL );
   SIGNAL_HIGH( PORT_SDA );
}

void i2c_nack(void)
{
   SIGNAL_HIGH( PORT_SDA );
   SIGNAL_HIGH( PORT_SCL );
   SIGNAL_LOW(  PORT_SCL );
}

void i2c_ack_slave(void)
{
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_LOW( PORT_SCL );
}

void i2c_write(uint8_t byte)
{
    uint8_t bit;

    for ( bit = 0; bit < 0x08; bit++ )
    {
        if( ( byte << bit ) & 0x80 )
            SIGNAL_HIGH( PORT_SDA );
        else
            SIGNAL_LOW( PORT_SDA );

        SIGNAL_HIGH( PORT_SCL );
        SIGNAL_LOW( PORT_SCL );
    }

    // Clear both lines (needed?)
    SIGNAL_LOW( PORT_SCL );
    SIGNAL_LOW( PORT_SDA );

    i2c_ack();
}

char i2c_read(void)
{
    uint8_t B = 0;
    DDRB &= 0b11111110; // switch PB0 to input

    for ( int bit = 0; bit < 0x08; bit++ )
    {        
        delay(); // <-- the root of all evil

        SIGNAL_HIGH( PORT_SCL );

        B <<= 1;

        if( PINB & (1 << PB0 ) )
        {
            B |= 1;              
        } 
        else
        {
            B |= 0;              
        }

        SIGNAL_LOW( PORT_SCL );
    }

    DDRB |= 0b00000001; // switch PB0 as output

    i2c_nack();

    return B;
}


void delay()
{
    LED_ON();
    LED_OFF();
}


void LED_ON( void )
{
    PORTB |= 0b00000010;
}


void LED_OFF( void )
{
    PORTB &= 0b11111101;
}
#定义端口SDA PB0
#定义端口\u SCL PB2

#定义信号_HIGH(PORT)PORTB |=(1I2c定义了信号的一些最小计时-这里重要的是SCL的高时间和低时间-在允许下一次转换到相反状态之前,SCL应该稳定的时间量。 这些计时为典型的~5µs,精确数字应取自数据表

read
循环结束时的循环大约需要2到3条指令,这取决于编译器的工作。AVR指令大约需要200纳秒,取决于您的时钟频率,因此(没有延迟)SCL低约600纳秒,给定或获取-这太短了,至少对于您的特定应用程序来说显然是如此“另一端设备”

当您在被调用函数中插入函数调用和端口访问时,您插入了足够多的指令,使SCL保持在低水平足够长的时间,以便正常工作

在您的代码中,高时间并不是问题所在,因为您让AVR在SCL高的时候执行更多的指令-显然,足够长的时间来保持SCL高

您在延迟功能中切换端口引脚的事实与此无关-唯一的相关性是您需要在SCL较低时花费一些时间。显然,您目前所做的只是浪费端口引脚等待一段时间-使用
delay\u us
,试验延迟,而不是延迟。但请检查“另一端的“数据表”对于所需的精确定时,4-5µs应该可以

为什么你的延迟循环不起作用?很可能是被编译器优化掉了,因为编译器意识到你在这个空循环中没有做任何相关的事情


理想情况下,您应该尝试在大约SCL高相位的中间读取SDA-使用8位的未滚动循环和一些
delay\u us
扩展,这样应该可以很好地工作。

您实际上是在读取SCL线上升沿上的SDA线;也许您的设备还没有准备好?您是否尝试在更近的位置读取SDA而是下降沿?即,
信号高(端口SCL);\u延迟(1);B=(B
void delay()
{
    LED_ON();
    LED_OFF();
}

void LED_ON( void )
{
    PORTB |= 0b00001000; // PB3
}


void LED_OFF( void )
{
    PORTB &= 0b11110111; // PB3
}
#define PORT_SDA PB0
#define PORT_SCL PB2

#define SIGNAL_HIGH(PORT) PORTB |=  ( 1 << PORT )
#define SIGNAL_LOW(PORT)  PORTB &= ~( 1 << PORT )

void delay();
void LED_ON(void);
void LED_OFF(void);

void i2c_init(void);
void i2c_start(void);
char i2c_read(void);
void i2c_stop(void);
void i2c_nack(void);
void i2c_ack(void);
void i2c_ack_slave(void);
void i2c_write(uint8_t byte);

void i2c_init()
{
    DDRB = 0b00000010; // TODO: should be removed once the weird delay issue is solved
    DDRB |= ( 1 << PORT_SDA );
    DDRB |= ( 1 << PORT_SCL );
}

void i2c_start( void )
{
    SIGNAL_LOW(  PORT_SCL );
    SIGNAL_HIGH( PORT_SDA );
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_LOW(  PORT_SDA );
    SIGNAL_LOW(  PORT_SCL );
}

void i2c_stop( void )
{
    SIGNAL_LOW(  PORT_SCL );
    SIGNAL_LOW(  PORT_SDA );
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_HIGH( PORT_SDA );
}

void i2c_ack(void)
{
   SIGNAL_LOW(  PORT_SDA );
   SIGNAL_HIGH( PORT_SCL );
   SIGNAL_LOW(  PORT_SCL );
   SIGNAL_HIGH( PORT_SDA );
}

void i2c_nack(void)
{
   SIGNAL_HIGH( PORT_SDA );
   SIGNAL_HIGH( PORT_SCL );
   SIGNAL_LOW(  PORT_SCL );
}

void i2c_ack_slave(void)
{
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_LOW( PORT_SCL );
}

void i2c_write(uint8_t byte)
{
    uint8_t bit;

    for ( bit = 0; bit < 0x08; bit++ )
    {
        if( ( byte << bit ) & 0x80 )
            SIGNAL_HIGH( PORT_SDA );
        else
            SIGNAL_LOW( PORT_SDA );

        SIGNAL_HIGH( PORT_SCL );
        SIGNAL_LOW( PORT_SCL );
    }

    // Clear both lines (needed?)
    SIGNAL_LOW( PORT_SCL );
    SIGNAL_LOW( PORT_SDA );

    i2c_ack();
}

char i2c_read(void)
{
    uint8_t B = 0;
    DDRB &= 0b11111110; // switch PB0 to input

    for ( int bit = 0; bit < 0x08; bit++ )
    {        
        delay(); // <-- the root of all evil

        SIGNAL_HIGH( PORT_SCL );

        B <<= 1;

        if( PINB & (1 << PB0 ) )
        {
            B |= 1;              
        } 
        else
        {
            B |= 0;              
        }

        SIGNAL_LOW( PORT_SCL );
    }

    DDRB |= 0b00000001; // switch PB0 as output

    i2c_nack();

    return B;
}


void delay()
{
    LED_ON();
    LED_OFF();
}


void LED_ON( void )
{
    PORTB |= 0b00000010;
}


void LED_OFF( void )
{
    PORTB &= 0b11111101;
}