pasko-zh / brzo_i2c

Brzo I2C is a fast I2C Implementation written in Assembly for the esp8266
GNU General Public License v3.0
245 stars 47 forks source link

INA219 #28

Closed muenznej closed 6 years ago

muenznej commented 6 years ago

Hello pasko, i have found your nice i²c library and wanted to try it out for my (INA219). I tried to modify a simple library and came up with my forked version.

In short: It does not work, but i think i am not completely wrong. Could you have a look at it and give me a hint, how to make this work (at all).

There are basically only to functions that i have changed, and i think the problem is this one:

int16_t INA219::readRegister16(uint8_t reg)
{
    int16_t value;

    Wire.beginTransmission(inaAddress);
    #if ARDUINO >= 100
        Wire.write(reg);
    #else
        Wire.send(reg);
    #endif
    Wire.endTransmission();

    delay(1);

    Wire.beginTransmission(inaAddress);
    Wire.requestFrom(inaAddress, 2);
    while(!Wire.available()) {};
    #if ARDUINO >= 100
        uint8_t vha = Wire.read();
        uint8_t vla = Wire.read();
    #else
        uint8_t vha = Wire.receive();
        uint8_t vla = Wire.receive();
    #endif;
    Wire.endTransmission();

    value = vha << 8 | vla;

    return value;
}

changed to:

int16_t INA219_brzo::readRegister16(uint8_t reg)
{
    int16_t value;

    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_write(&reg, 1, true); // 1 byte, repeat 
    brzo_i2c_end_transaction();

    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_read(&buffer[0], 2, false);
    // uint8_t vha = brzo_i2c_end_transaction();
    // uint8_t vla = brzo_i2c_end_transaction();

    uint8_t vha = buffer[0];
    uint8_t vla = buffer[1];

    brzo_i2c_end_transaction();

    value = vha << 8 | vla;

    return value;
}

kind regards Jürgen P.S.: I really appreciate your work and your incredibly intimedating ASM code!

Disclaimer: I have hardly any experience with C coding and iam very new to all this MCU stuff.

muenznej commented 6 years ago

Its not working! i get zero readings, but the speed seems to be correct :smile:

muenznej commented 6 years ago

Hmm, i changed the code now in various ways, but nothing helped :(

Maybe i give some information about the hardware i use:

pasko-zh commented 6 years ago

Hi Jürgen!

Glad to hear that you like my library 😸

Let's find the issue here...

So, it seems your wiring is correct and the sensor works with the wire library.

Looking at the hardware side:

  1. The breakout board has 10K pull-ups, as you already mentioned, and they far too big for higher speeds, see my comments for the HTU21 breakout board. Use additional pull-ups with lower values, can be as low as 1.5 kOhm.

  2. I had a look at the datasheet of the INA219: It seems that it supports i2c fast mode and high speed mode, however not fast mode plus. E.g., on page 10 it says "supports the transmission protocol for fast (1kHz to 400kHz) [...]" and also the table on page 13 states that. Now, since it supports high speed mode as well, normally such ICs can be "overclocked" in fast mode up to 1 MHz—but this really depends on the IC and on the quality of the signals, especially raise times. Thus, you may try with 400 kHz first, see if it works and then increase SCL speed.

Looking at the software side: (I had only a very very short look at your code. And I have not yet checked, when the INA216 needs a repeated start and all those stuff)

What do you mean "it is not working"? Do you get any brzo i2c error codes? Your sample code seems not to catch them, but it is wise to do so ;-)

muenznej commented 6 years ago

Hi Pasko, thanks for the fast response!

In the meantime I have been "working" on this problem:

I also found some interesting things! The bus itself is working and read and write do work, but some things are just non-sense. Maybe my bitoperations are just wrong?

For example i used:

ina.configure(INA219_RANGE_16V, INA219_GAIN_40MV, INA219_BUS_RES_9BIT, INA219_SHUNT_RES_9BIT_1S, INA219_MODE_SHUNT_CONT);

which sets some registers in the setup part, but when i use the reading code it gives:


Mode:                 Shunt and Bus, Continuous
Range:                32V
Gain:                 +/- 320mV
Bus resolution:       12-bit
Shunt resolution:     12-bit / 1 sample
Max possible current: 0.40 A
Max current:          0.40 A
Max shunt voltage:    0.04 V
Max power:            6.40 W
510029 µs; 510.02899 µs/READ; 0.0000000000000000 mA;0.0000000000 V

As you can see, many parameters are not correct. I think these are default values. (This exact same code works for the wire version perfectly!)

Greetz Jürgen

muenznej commented 6 years ago

Ok, i thought about the latest result and i come to the conclusion that if the (default) modes are read out correctly, the read function has to be working. If this would not be the case, the code would throw "unknown" but it does not! This is in agreement with the finding that the settings are defaults, and hence not overwritten by the write command! Since the Bus is as fast as i set it up, the startup itself also has to be correct. This leaves only

void ICACHE_RAM_ATTR INA219_brzo::writeRegister16(uint8_t reg, uint16_t val)
{
    /*
    uint8_t vla = (uint8_t)val;
    val >>= 8;
    _buffer[1] = (uint8_t)val;
    _buffer[2] = vla;
    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_write(&_buffer[0], 1, true);  // 1 byte, repeat
    brzo_i2c_write(&_buffer[1], 2, false); // 2 byte, no repeat
    */

    /* val = 1011 1111 1001 0110
       val >> 8: 0000 0000 1011 1111
       0xFF = 1111 1111
       (val >> 8) & 0xFF: 1011 1111 // seems to be some implicity typecast to byte
    */
    _buffer[0] = reg;
    _buffer[1] = (val >> 8) & 0xFF;  // high BYTE of DWORD
    _buffer[2] = val & 0xFF;    // low BYTE of DWORD
    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_write(&_buffer[0], 1, true);  // Set Register
    brzo_i2c_write(&_buffer[1], 2, false);  // Write 2Bytes
    uint8_t _ecode = brzo_i2c_end_transaction();

    if (_ecode != 0) // on error
    {
        Serial.println(_ecode);
    }
}

to be faulty. (But i could be wrong with my conclusions as well)

greetz Jürgen

P.S.: Yes, my comments are confusing and generally reflect the state of my mind regarding this problem ;-)

muenznej commented 6 years ago

Cool, i did it :dancing_men:

As i thought the problem was the writing part: The INA219 uses 1 Byte register and 2 Byte values, and it seems that you have to send them at once with a single

  brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
   brzo_i2c_write(&_buffer[0], 3, true);  // 1Byte Register + 2Bytes data
   uint8_t _ecode = brzo_i2c_end_transaction();

I will update the code and make it a bit less ugly!

Thanks for your support and the library Mr. Pasko :smile:

P.S.: I just tested it for 100k, 400k, 800k, and 1000k with 2k2 resistors and everything works very well.

pasko-zh commented 6 years ago

Cool that it works now! 👍 (my other life keeps me busy from time to time, thus sorry for my late response)

Yes, in the datasheet it states that after sending register pointer address (1), two bytes of data (2) need to be transfered without a START nor STOP between (1) and (2).

Concerning i2c high speed mode: You need different hardware, see here under electrical characteristics. The esp8266 does not support HS mode (you would have to use additional hardware components).

Looking at the code in your last comment, I have the following remarks:

muenznej commented 6 years ago

Since you are sending 3 bytes from the beginning of _buffer you can simply use brzo_i2c_write(_buffer, 3, false)

There are no repeated starts anymore and i amended many parts of the code, and fixed some minor bugs. It can be found here.

thx brzo pasko

P.S.: When i add more sensors i ll come back to this issues section i guess :sweat_smile: