Go Down

Topic: Wire library impossible to write register address with read-bit-device-address? (Read 99 times) previous topic - next topic

wrongbad

I've been researching a lot about the wire library and really struggling to see the rationale behind the API decisions.
So I'm trying to use a TLE493D-A2B6 hall sensor, and I added a snapshot of the datasheet for one of the read commands. Basically the sequence is master sends address with read-bit, writes register offset, then reads the value. As far as I can tell wire library will always set address write bit when writing, so I can't send the register offset in the read command. Am I missing something?



https://www.infineon.com/dgdl/Infineon-TLE493D-A2B6-UM-v01_01-UserManual-v01_11-EN.pdf?fileId=5546d46262b31d2e01633b4bba5f3c5a

hzrnbgy

I'm not sure how you would accomplish this in MASTER READ mode since after receiving the address ACK bit from the SLAVE, the MASTER expects to clock IN data from the SLAVE. Even the datasheet does not have a "status" code describing the behavior above.

wrongbad

hzrnbgy to clarify, are you saying this looks like the sensor is not conforming to the i2c specification?

hzrnbgy

Looks like it. By the I2C spec, after an ACK from a SLAVE+READ address, the MASTER samples the SDA line for each SCL clock. Normally, for something like you are trying to accomplish, you first do a MASTER WRITE (SLAVE+W) first and send a register address, then you do a repeated START by sending the SLAVE+R address and reading the response from slave. I'm looking at the low-level I2C drivers I have for the Atmega328P, and I can't find a way to send a SLAVE+R and write a byte just after it. Maybe you can do it with a bit-bang I2C driver as there are no rules when using such.

wrongbad

Ok, I managed to make a bit bang i2c which performs the special write+read command. And in the process, I figured out that I can just use what the datasheet calls "1 byte read mode" setting in register 0x11 which is compatible with vanilla i2c drivers.

For fun here's the bit bang code

Code: [Select]

static const bool ACK = 0;
static const bool NACK = 1;
static const bool WRITE = 0;
static const bool READ = 1;

class bbi2c
{   
public:
    bbi2c(byte scl_pin, byte sda_pin, long bitrate = 100000)
    :   scl(scl_pin), sda(sda_pin), freq(bitrate)
    {   up(scl);
        up(sda);
    }
    operator bool()
    {   return good;
    }
    bbi2c & start(byte addr, byte rw)
    {   good = true;
        down(sda);
        tick();
        down(scl);
        return send((addr<<1)|(rw&1));
    }
    bbi2c & finish()
    {   down(sda);
        up(scl);
        up(sda);
        return *this;
    }
    bbi2c & send(byte data)
    {   if(!good) { return *this; }
        for(byte i=0 ; i<8 ; i++)
        {   writeBit((data<<i)&0x80);
        }
        good &= (readBit() == ACK);
        return *this;
    }
    bbi2c & recv(byte * data, bool ack=true)
    {   if(!good) { return *this; }
        *data = 0;
        for(byte i=0 ; i<8 ; i++)
        {   *data |= readBit()<<(7-i);
        }
        writeBit(ack ? ACK : NACK);
        return *this;
    }

private:
    const byte scl;
    const byte sda;
    const long freq;
    bool good = true;
   
    byte readBit()
    {   up(sda);
        tick();
        up(scl);
        tick();
        byte bit = digitalRead(sda);
        down(scl);
        return bit;
    }
    void writeBit(byte bit)
    {   setdata(bit);
        tick();
        up(scl);
        tick();
        down(scl);
    }
    void setdata(byte val)
    {   if(val) { up(sda); }
        else { down(sda); }
    }
    void down(byte pin)
    {   digitalWrite(pin, 0);
        pinMode(pin, OUTPUT);
    }
    void up(byte pin)
    {   pinMode(pin, INPUT_PULLUP);
        digitalWrite(pin, 1);
        // stretch or RC rise
        for(int i=1000 ; i && pin==scl && !digitalRead(pin) ; i--);
    }
    void tick()
    {   delayMicroseconds(500000/freq);
    }
};

bbi2c i2c(19, 18);
int addr = 53;

void setup()
{   Serial.begin(9600);
    delay(100); 
   
    // TLE493D-A2B6
    // parity, 2 byte read, no INT, master control mode
    i2c.start(addr,WRITE).send(0x11).send(0x85).finish();
    // no temp, adc on read, parity bit
    i2c.start(addr,WRITE).send(0x11).send(0x85).finish();
}

void loop()
{   delay(5000);
    // register 0, trigger adc
    i2c.start(addr,READ).send(0x80);
    for(int i=0 ; i<7 ; i++)
    {   byte buff = 0;
        i2c.recv(&buff);
        Serial.printf("got %d 0x%x\n", i, buff);
    }
    Serial.printf("recv good %d\n", (bool)i2c.finish());
}

Go Up