Bug? LPC11U24 + I2C + mbed_rtos = 0.57% i2c error rate

16 Nov 2012

I have an app which simply loops reading an i2c device then pushes it out via a serial port. I initially implemented this using only the mbed library which is added by default when a new project is created. Specifically Serial, I2C (400,000 freq), DigitalOut, and DigitalIn classes. Plain vanilla - single threaded - not even using interrupts - no wait calls. My read routine:

        ret_val         = num_bytes;
        port            = i2c_ports[i2c_port];
        eight_bit_addr  = (i2c_addr & 0x7F) << 1;
        port->start();
        if (port->write(eight_bit_addr) &&
                port->write(reg_addr)) {
            port->start(); // <-----<<<< Start in question
            if (port->write(eight_bit_addr | 0x1)) {
                while (num_bytes-- != 1) {
                    *(current++)    = port->read(1);
                }
                *current    = port->read(0);
            } else
                ret_val = -1;
        } else
            ret_val = -1;
        port->stop();

This works fine. In a test app I cycled through 100,000 samples several times without issue. As soon as I added the mbed_rtos library and compiled (without even using any of the functionality - not even including a header - no source change on either the mbed target code or my test app) I started getting I2C problems. I noticed them because I started getting odd data (nothing but 0xD1) frequent enough for my eye to notice it scrolling through the test app streaming data. I haven't done extensive runs to estimate statistical confidences but the failure rate is around 0.57% the few times I've done 100,000 sample runs.

I didn't immediately make the connection of the rtos influencing the behavior. I double checked my code and wiring connections then pulled out my trusty Open Bench logic sniffer to see what is going on.

What I found is the START condition following the register address write is missing, thus the switch to reading data doesn't occur as expected. As soon I as remove the mbed-rtos library, behavior goes back to zero errors on 100,000 sample runs.

bad read

Here is a good read for comparison:

good read

I didn't yet hookup my LPC1768 so I don't know if it is limited to just this model. If I'm doing something wrong please let me know, otherwise I guess this is a bug.

Regards, Anthony Loeppert

16 Nov 2012

I could expect it would switch to another thread with RTOS, but just skipping a start is weird.

However what I wonder first is why you made such a complicated read function. Your 17 lines of code are equivalent to:

port            = i2c_ports[i2c_port];
eight_bit_addr  = i2c_addr << 1;

//Actual I2C code
port -> write(eight_bit_addr, reg_addr, 1, true);
port -> read(eight_bit_addr, current, num_bytes);

You can if you want add if statement to only read when write was succesfull (and I might now be switching up some datatypes, but the compiler will tell you that soon enough).

If it still happens you can try otherwise the MODI2C library, to see what that does. However it is only available for the LPC1768 (dont have 11u24 so never could make it for that), only supports the write/read statements as done in this post, not manually writing a byte, and to be fair I should do some more testing on it.

16 Nov 2012

Hey Erik. Thanks for your response and suggestion. I tested your suggestion and have found the i2c code you post not to be equivalent to my function. I cleaned up those else statements, but the functional part of my function is unchanged. As you can see, I added a compilation flag to try the code you suggested. I looked at the API again and it would seem your code should work fine, however it did not.

    TRIG_ON(0)
    if (i2c_port < i2c_num_ports) {
        port            = i2c_ports[i2c_port];
        eight_bit_addr  = (i2c_addr & 0x7F) << 1;
#ifdef I2CTEST
        port->write((int)eight_bit_addr, (const char *)&reg_addr, 1, true);
        TRIG_ON(1)
        port->read((int)eight_bit_addr, (char *)current, (int)num_bytes);
        TRIG_OFF(1)
        ret_val = num_bytes;
#else
        port->start();
        if (port->write(eight_bit_addr) &&
                port->write(reg_addr)) {
            TRIG_ON(1)
            port->start();
            if (port->write(eight_bit_addr | 0x1)) {
                ret_val = num_bytes;
                while (num_bytes-- != 1) {
                    *(current++)    = port->read(1);
                }
                *current    = port->read(0);
            }
            TRIG_OFF(1)
        }
        port->stop();
#endif // i2ctest
    }
    TRIG_OFF(0)



I2CTEST on with code as above: Notice the missing START between write and read calls and the read doesn't even continue for the expected number of bytes (which would have been garbage anyway). write true


I2CTEST on with one line change (changing last arg to false) which gets data out of the part successfully however adds an extraneous and undesirable START STOP before the read.

port->write((int)eight_bit_addr, (const char *)&reg_addr, 1, false);

write false


I2CTEST off for comparison, working fine: mine

All these tests were done without the mbed_rtos lib, only the mbed library - so there seems to be more going on than just adding the rtos library, however it breaks code which works perfectly well without it.

I'll take a look at your MODI2C library sometime soon. It looks interesting and potentially addresses some optimizations I'm considering.

Regards, Anthony

16 Nov 2012

Well this I would put under the category weird. Doing it on your I2CTEST way should work fine: I use it all the time to talk with I2C sensors and if it isnt transmitting a restart condition it definately shouldnt be getting anything back from those sensors. I dont know how much other code you still got, but it is probably an idea to start throwing away everything to test really only the I2C code. MODI2C can be a nice start for you if you want to make it interrupt based (it is really a large boost in performance if you need quite some I2C traffic), but here something else must be going really wrong.

Sorry I cant help you more with it, you can try what the LPC1768 does.

Btw which logic analyzer do you use? I dont have one, but those plots look really handy. (Could have used one when I wrote MODI2C :P)

16 Nov 2012

Erik - wrote:

Well this I would put under the category weird. Doing it on your I2CTEST way should work fine: I use it all the time to talk with I2C sensors and if it isnt transmitting a restart condition it definately shouldnt be getting anything back from those sensors. I dont know how much other code you still got, but

If I had the source to the I2C library (as far as I know, it isn't available) I'd look further. I agree it is weird, but I probably won't spend more time than I already have on debugging it. I have code that works and I'm not desperate for the mbed_rtos library. I feel there is a bug in the underlying implementation but if everyone else doesn't see what I see, I guess it is just me :) Perhaps no one looked too closely at the i2c traffic coming out, I don't know.

Erik - wrote:

Btw which logic analyzer do you use? I dont have one, but those plots look really handy. (Could have used one when I wrote MODI2C :P)

http://dangerousprototypes.com/docs/Open_Bench_Logic_Sniffer It has a very small capture buffer but has a (relatively high - more than enough for mbed speeds) capture rate, and is sufficient to capture i2c traffic. Cheap too! $50.00. (avoid sparkfun - who I do a lot of business with - for this one, they're charging $20 more for it) Well worth the money to sort out issues like this. I didn't have success capturing 1.8V signals with it, but for 3.3V, no problems.

Regards, Anthony

19 May 2013