Kristian
Kristian

Reputation: 133

Reading I2C sensor value (bit-banging)

I have a very simple I2C bit-banging library for ATTINY85.

#define PORT_SDA PB0
#define PORT_SCL PB2

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

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

void i2c_init(void);
void i2c_start(void);
void i2c_read(void);
void i2c_stop(void);
void i2c_write(uint8_t byte);

void i2c_init()
{
    DDRB |= ( 1 << PORT_SDA );
    DDRB |= ( 1 << PORT_SCL );
}


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


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


void i2c_start( void )
{
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_HIGH( PORT_SDA );
    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_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 );
    }

    SIGNAL_HIGH( PORT_SDA );
    SIGNAL_HIGH( PORT_SCL );
    SIGNAL_LOW( PORT_SCL );
} 

I'm able to successfully write to I2C without any problems. I have tested this code with SSD1306 and LC2404B and everything works great even the timing if VCC is set to 4.2V.

i2c_init();

i2c_start();
    i2c_write( 0xA0 );
    i2c_write( 0x01 );
    i2c_write( 0x13 );
i2c_stop();

While writting works perfectly, I can't seem to be able to trigger any of my I2C modules with ATTINY85 to return me a value that I can read later.

I connected raspberry and GY-521 sensor (since it returns value even if internal address is not set). I can detect the sensor and read a value from it with raspberry the following way:

i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --  


i2cget -y 1 0x68

0x02

This is the output on the oscilloscope:

Raspberry I2C output

I can see the sensor data changing. The problem is I can't seem to replicate the same request from ATTINY85 to the sensor. The sensor simply doesn't respond with the value. I can't seem to understand if the last bit in the first byte is an ACK or 'READ' indicator bit, so I tried different addresses.

i2c_start();
i2c_write(  0b11010001 ); // address: 0xD1

i2c_start();
i2c_write(  0b11010000 ); // address: 0xD0

i2c_start();
i2c_write(  0b10100001 ); // address: 0xA1

i2c_start();
i2c_write(  0b10100000 ); // address: 0xA0

but regardless on what address I use, the sensor simply doesn't respond (in the oscioloscope) and the SDA line stays high after I send the address byte. I also tried to append another start() condition after I sent the address, but still no luck. Any hints on where I'm going wrong? I simply want to make the sensor respond to the ATTINY85 read request so I can later read the value. Thanks!

Upvotes: 2

Views: 1303

Answers (2)

David Grayson
David Grayson

Reputation: 87426

You need to let the SDA line be an input so you can detect whether the slave is sending an ACK or not. You don't have any code to set the SDA line to be an input so there is no way the slave device could send any data back to you; the value you are putting on the SDA line might overpower whatever the slave is trying to do. And to read data back, you need to make an i2c_read function that wiggles the SCL line while SDA is an input. Your I2C implementation is therefore far from complete. You could carefully read the I2C spec from NXP or look for a more complete bit-banging I2C implementation to use as a reference.

Upvotes: 2

iheanyi
iheanyi

Reputation: 3113

I'd suggest reading a couple tutorials on I2C to get a general understanding of the protocol. See https://learn.sparkfun.com/tutorials/i2c for example.

In short, I2C is a two-wire multi-master bus with a clock line and a data line. In any communication, whether it is a read or a write, the master supplies the clock signal. Your i2c_write() implements this with the SCL transitions.

In order to read a value back, you need to also supply the clock for that the slave uses to output data. No clock, no data. So, you need to implement an i2c_read() similar to your i2c write, that generates the clock transitions, and shifts in each bit one at a time.

Upvotes: 2

Related Questions