I2C freeze

I'm not sure yet, but i think that in some situations my mbed is getting stuck inside the i2c read or write. Does anybody had the same problem?

Does anybody knows a "timeout solution" for this problem?

Thanks.

15 Jul 2010

Are you using the single byte methods of read() and write(), or the buffer variants?

There isn't currently any timeout mechanism built into these.

i'm using write(adress, buffer, lenght) and read(address, buffer)

02 Aug 2010

Flávio - what device are you trying to talk to? Also, have you got the pullup resistors on the I2C lines?

The usual cause for the I2C getting stuck is when the device you are talking to is holding the clock line down.

Also, could you publish an example program which exhibits this behaviour?

02 Aug 2010 . Edited: 02 Aug 2010

Probably, the situation as i can remember was something where the master writes and expects to read, but the slave is somehow waiting for more data. I know that it is because some bad code that I made, but anyway, even in a good code it might happen in some situation. So my suggestion is put an timeout when the clock is hold low or make the i2c prosessing parallel with the other functions, like tasks in a rtos... The timeout solution is much easier.

The devices i'm using are 2 uC, one MSP430 talking with the MBED.

02 Aug 2010

Timeout for serial half duplex was already in the future enhancement list. I'll add I2C as well.

ok, thanks, and congrats for the MBED project.

02 Aug 2010 . Edited: 02 Aug 2010

Flavio - I had problems with i2c timeouts on pins 9,10. When I changed to pins 27, 28 my code worked fine. I don't know if it was my code, my connections or perhaps a problem with my mbed but it's working fine now.

I was trying on 9 and 10, I have not tried on pins 27 and 28 yet.

01 Sep 2010 . Edited: 01 Sep 2010

I seem to be having the same problems trying to communicate with the Wii Nunchuck + Motion Plus combo. The combo works fine with and Arduino Mini Pro (5V, even though the Wii is a 3V device) with similar code. But with the mbed I-m running into a brick wall. Usual it will already hang on the first write attempt. It clearly hangs within the I2C code, not in between. I-ve already put a lot of waits into the main code, with little or no effect. Sometimes it will run a little bit into the code, but then hang. It clear hangs on the I2C communication. I-ve even just tried some 4.7kOhm restistors to pull up or pull down the SDA, the SCL or both lines but that makes no difference. Is this due to a bug in the I2C code, possibly due to the higher clock speed of the lpc1768, or more likely hardware related ? Any suggestions ??

EDIT:

Very red faced: not used to working with those pegboard type of proto boards instead of solder wired prototyping. Hadn't realized the side bars on this particular one don;t run down full lenght so the 4.7kohm resistors were hanging in thin air instead of pulling up sda and scl lines. Ones corrected the program does seem to run although the values are not as expected. Futher experimenting....

user avatar Arthur P. Meiners wrote:

..........

EDIT:

Very red faced: not used to working with those pegboard type of proto boards instead of solder wired prototyping. Hadn't realized the side bars on this particular one don;t run down full lenght so the 4.7kohm resistors were hanging in thin air instead of pulling up sda and scl lines. Ones corrected the program does seem to run although the values are not as expected. Futher experimenting....

Hi Arthur, once I had problems with the pull up resistors when I was trying to communicate with a MSP430. I solved it changing the resistors from 10k to 1k. It worked fine, but still having the freeze problem in some cases. (only when I forced the freeze).

26 Dec 2010

Hi All,

I seems to have the same problem, first... I'm new at this stuff, so it is likely that it is just my own error ;-) but:

I’m trying to make an SRF08 to work, I have remember the 2 pullup resistors (1.4K).

And when the SRF08 is powered up it flashes the address, so it is alive…

But my program hangs on the first I2C write…

I have tried to use the SRF08 lib, but same problem…

I have also tried p9+p10 and p28+p27…. But same problem…

Any ideas…my robot is driving around blind and keeps bumping into stuff ;-)

#include "mbed.h"
//#include "SRF08.h"

DigitalOut l1(LED1);
DigitalOut l2(LED2);
DigitalOut l3(LED3);
DigitalOut l4(LED4);
//SRF08 srf08(p9, p10, 0xE0);
Serial pc(USBTX,USBRX);
I2C sonar(p28,p27);

const int addr = 0xE0;
char cmd[2];
char echo[2];

int main() {
    pc.printf("Start up v.01 \n");
    l1=1;
    wait(1);

    cmd[0] = 0x02;
    cmd[1] = 0x1C;
    l2=1;
    pc.printf("--->Sonar.write\n");
    sonar.write(addr,cmd,2);
    l3=1;
    pc.printf("<---Sonar.write\n");
    cmd[0] = 0x01;
    cmd[1] = 0x1B;
    sonar.write(addr,cmd,2);
    l4=1;

    pc.printf("Link OK to Sonar \n");

    while(1) {
        l1=!l1;

        wait(0.2);
    }
}
26 Dec 2010

Hmmm..and why can I not format/edit my post... sorry....:-/

26 Dec 2010

Hmmm...

The Handbook says:

Quote:

A typical value for the pullup resistors is around 2.2k ohms, connected between the pin and 3v3.

The datasheed for the SRF08 says:

Quote:

The SCL and SDA lines should each have a pull-up resistor to +5v somewhere on the I2C bus

But on all the pictures on this site, i can not see a singel pull-up resistor ?

Currently I'm powering my SRF08 with +5v, and my pull-up resistors also to +5v, but is this right ?

26 Dec 2010 . Edited: 26 Dec 2010

Since mbed inputs are +5v tolerant it is right to conect the pull-up resistors to +5v. Try smaller values for the resistors (1k min) but it should work with 4k7 just fine for 5v. (it depends on length of the wires (breadboard) or on the pcb traces. Also it depends on the i2c bus speed. Try smaller speeds.(maximum capacitive load on the bus lines depends on pull-up resistors and speed)   

About the timeout... in some software implementation (and probably in some hardware also) the i2c master send clock pulses with SDA high (input) until the slave releases the SDA line .. and only then resumes the code.

24 Feb 2011

Simon/Anyone,

I am having occasional hangs on one of the I2C busses. While it is entirely possible for devices on the bus to have a transaction hiccup it is really not acceptable for the processor to go into a permanent hang, or a hang long enough for an aircraft to fall out of the sky.

I would be more than happy to look at the I2C library drivers if I can have access to the source. Maybe I already have access?

The situation is so disappointing that I decided to write a set of simple software I2C routines. These routines recover if the clock does not behave.

Now the next problem - it seems to take 4uS to change the mode of a pin from output to input or vice versa. How can this be true - I have just found the discussion mid last year BTW. Anyway the net result is that the maximimum rate I can currently get out of the software version is around 100KHz. The really depressing news is that this is half the speed of a 16MHz PIC2620 running essentially the same routines.

All help and ideas gratefully received :).

Greg

Very rough first cut of the SW I2C routines.

#define I2CDelay5uS wait_us(5)
#define I2CDelay2uS // wait_us(2)
#define HighLowDelay // wait_us(1)
#define FloatDelay // 

#define I2CSDALow {I2C0SDA.write(0);HighLowDelay;I2C0SDA.output();}
#define I2CSDAFloat {I2C0SDA.input();}
#define I2CSCLLow {I2C0SCL.write(0);HighLowDelay;I2C0SCL.output();}
#define I2CSCLFloat {I2C0SCL.input();FloatDelay;}

void MyI2C::frequency(uint32 f) {
// delay depending on rate
} // frequency

boolean MyI2C::waitclock(void) {
    static uint32 s;

    I2CSCLFloat;        // set SCL to input, output a high
    s = 0;
    while ( !I2C0SCL.read() )
        if ( ++s > 60000 ) {
            Stats[I2CFailS]++;
            return (false);
        }
    return( true );
} // waitclock

void MyI2C::start(void) {
    static boolean r;

    I2CSDAFloat;
    r = waitclock();
    I2CSDALow;
    I2CDelay5uS;
    I2CSCLLow;
} // start

void MyI2C::stop(void) {
    static boolean r;

    I2CSDALow;
    r = waitclock();
    I2CSDAFloat;
    I2CDelay5uS;
} // stop

uint8 MyI2C::blockread(uint8 a, char* S, uint8 l) {
    static uint8 b;
    static boolean err;

    I2C0.start();
    err = I2C0.write(a|1) != I2C_ACK;
    for (b = 0; b < (l - 1); b++)
        S[b] = I2C0.read(I2C_ACK);
    S[l-1] = I2C0.read(I2C_NACK);
    I2C0.stop();

    return( err );
} // blockread

uint8 MyI2C::read(uint8 ack) {
    static uint8 s, d;

    I2CSDAFloat;
    d = 0;
    s = 8;
    do {
        if ( waitclock() ) {
            d <<= 1;
            if ( I2C0SDA.read() ) d |= 1;
            I2CSCLLow;
            I2CDelay2uS;
        } else
            return( 0 );
    } while ( --s );

    I2C0SDA.write(ack);
    HighLowDelay;
    I2C0SDA.output();
    HighLowDelay;

    if ( waitclock() ) {
        I2CSCLLow;
        return( d );
    } else
        return( 0 );
} // read

void MyI2C::blockwrite(uint8 a, const char* S, uint8 l) {
    static uint8 b;
    static boolean r;

    I2C0.start();
    r = I2C0.write(a) == I2C_ACK;  // use this?
    for ( b = 0; b < l; b++ )
        r |= I2C0.write(S[b]);
    I2C0.stop();

} // blockwrite

uint8 MyI2C::write(uint8 d) {
    static uint8 s, r;

    for ( s = 0; s < 8; s++)
    {
        if ( d & 0x80 ) {
            I2CSDAFloat;
        } else {
            I2CSDALow;
        }

        if ( waitclock() ) {
            I2CSCLLow;
            d <<= 1;
        } else
            return(I2C_NACK);
    }

    I2CSDAFloat;
    if ( waitclock() ) {
        r = I2C0SDA.read();
        I2CSCLLow;
        return( r );
    } else
        return(I2C_NACK);

} // write

24 Feb 2011

Hi Greg,

I've just published a test version of the mbed library including a simple I2C timeout, which you are welcome to try.

If you delete the mbed library from your program, and then choose "Import Library..." on the same program and use the URL: http://mbed.org/projects/libraries-testing/svn/bugfix-671, you'll get a version of the mbed library that will timeout on an incorrect I2C operation. Not refined yet, but would be interested if this solves your problem.

Simon

11 Mar 2011

Excellent - thanks Simon. I will do some long runs and let you know. How long is the current timeout?

Greg

.... no its broken. I2C may work but the PWM outputs are now broken. I deleted the mbed library and imported the bugfix. I did not rename it. Compiles fine.

Simon,

I returned to testing bugfix-671 and currently it sets SCL and SDA high and then hangs for about 3 seconds returing both signals to ground. The PWM outputs I have also hang for approximately the same time so there appears to be an interaction between them. This seems to be different to when I initially tested it when it ran but with the PWM channels being clobbered.

It would be nice to get a fix to this. I would be happy to look at the libraries myself. My software I2C seems to work fine but is way too slow for the flight application. So far the software versions has not recorded a hang so the partially exonerates the attached devices.

Thanks Greg

Addendum:

My software I2C routines seem to be working fine at 400KHz with some pulse stretching occuring during interrupts. I was forced to use the port libraries to get respectable pin tristate and i/o times. I am still baffled as to why the slowness of the pin libraries continues and the port workaround using pin masks is necessary :(. I will rewrite the pin libraries if I can find the time.

Greg

20 Apr 2011

I communicate with 8 devices at 20 times a second. My I2C seems to be stalling anywhere between 10 and 45 minutes of operation.

Is there an update on the I2C library or a workaround, to make it recover.

I can setup a watchdog timer, but it is far from ideal.

Any suggestions?

26 Apr 2011

I tried http://mbed.org/projects/libraries-testing/svn/bugfix-671 but that seems to hang during initialization part of the time and return strange data the other part of the time.

Is the I2C soft library published somewhere so I can try it out?

Kind regards

Jesper

26 Apr 2011

Hi Jesper.

I also tried 671 but it corrupted other pin values as you can see written up in my last post. I have not had any further response from the mbed guys.

I have just posted an update to UAVX on my pages. The files you need to look at are i2c.c and harness.c. The routines work happily as master at around 400KHz. There are short timeouts in the code but I have not seen any errors in my app and I am updating a large number addresses at up to 1KHz.

Best I can do at the moment. Down with the flu.

Greg

26 Apr 2011

Hi Greg,

I will take a look at it immediately.

Thanx and get well soon

Jesper

26 Apr 2011

Im a bit confused about the mbed1768Pins array and the setup of the pins that the i2c.c library uses.

const uint8 mbed1768Pins[32] = { // Maping of mbed pins to LPC 1768 "port" pins
    255,255,255,255,255,9,8,7,6,0,
    1,18,17,15,16,23,24,25,26,62,
    63,69,68,67,66,65,64,11,10,5,
    4,255
};

I am using the basic I2C pins on the mbed. (sda on pin 9 and scl on pin 10) Would that mean that I should define as follows?

#define I2C0SDASet (1<<mbed1768Pins[5&0x1f]) // 9
#define I2C0SDAPort (mbed1768Pins[5&0x0f]>>5)
PortInOut I2C0SDA(Port0, I2C0SDASet );

#define I2C0SCLSet (1<<mbed1768Pins[28&0x1f]) // 10
#define I2C0SCLPort (mbed1768Pins[28&0x0f]>>5)
PortInOut I2C0SCL(Port0, I2C0SCLSet );

kind regards

Jesper

27 Apr 2011

Hi Jesper,

I think you just change my 27 & 28 to your 9 & 10. What you are doing with this code is breaking out a pin number on the number of one of the 5 or so LPC1768 ports. I was forced to go down this very indirect quick and dirty path because the speed of library pin operations is so pathetically slow compared to the port operations. I was given a reason why this was(is?) so but basically if the mbed is to be taken seriously someone in support should get in there and fix the libraries including those for I2C comms. My brain is feeling like steaming custard at the moment but several uS to flip a pin's state on an LPC1768 is very sad. My 18F2620 PIC @ 16MHz, which was imported by Noah on his boat, is faster.

I may be able to help more when I feel a little better meanwhile I would just substitute your 9 & 10 for my 27 & 28 and see what happens. Do you have a logic analyser?

Cheers Greg

27 Apr 2011

Hi Greg,

First of all thank you for your help. I have been growing increasingly frustrated by not being able to trust the I2C libraries included.

I am on it, and have the full day set aside to give your lib a shot.

I have a logic analyzer from saleae.com, that I have been using to track down where I2C communication fails. It just seem to stop after 10-45 minutes of operation. Usually communicating with the same chip. Its worth noting that both SDA and SCL are left high. This could mean that its not a I2C bus error.

Jesper

27 Apr 2011

I too have the Saleae - nice device - very useful with these problems. Maybe you could swap temporarily to the pins I am using to reduce the number of variables.

Cheers Greg

27 Apr 2011

I too have the Saleae - nice device - very useful with these problems. Maybe you could swap temporarily to the pins I am using to reduce the number of variables.

I need to tidy up the code as it was a quick effort. I still live in hope that the pin libraries will be improved at least.

Cheers Greg

27 Apr 2011

I just got it working. :)

It data looks very nice, and I have the robustness to disconnect my I2C bus and reconnect it again.

It has been a real hack & slash job.

28 Apr 2011

Excellent Jesper.

If you see any improvements/bugs etc let me know so I can mod my version.

Cheer Greg

28 Apr 2011

I am not sure how I do a library, so I will just post here what works for me.

MyI2C.h

#include "mbed.h"

#ifndef MyI2C_H
#define MyI2C_H

#define I2C_ACK  true
#define I2C_NACK false

#define I2C_WRITE 0
#define I2C_READ 1

extern bool I2C0AddressResponds(uint8_t);

class MyI2C 
{
private:
    bool waitclock(void);
public:
    //void frequency(uint32_t f);
    void start(void);
    void stop(void);
    uint8_t blockread(uint8_t r, char* b, uint8_t);
    uint8_t read(uint8_t r);
    bool blockwrite(uint8_t a, const char* b, uint8_t l);
    uint8_t write(uint8_t d);
};

#endif

MyI2C.cpp

#include "MyI2C.h"

const uint8_t mbed1768Pins[32] = { // Maping of mbed pins to LPC 1768 "port" pins
    255,255,255,255,255,9,8,7,6,0,
    1,18,17,15,16,23,24,25,26,62,
    63,69,68,67,66,65,64,11,10,5,
    4,255
};

#define I2C0SDASet (1<<mbed1768Pins[9&0x1f]) // 9
#define I2C0SDAPort (mbed1768Pins[9&0x0f]>>5)
PortInOut I2C0SDA(Port0, I2C0SDASet );

#define I2C0SCLSet (1<<mbed1768Pins[10&0x1f]) // 10
#define I2C0SCLPort (mbed1768Pins[10&0x0f]>>5)
PortInOut I2C0SCL(Port0, I2C0SCLSet );

MyI2C I2C0;

bool I2C0AddressResponds(uint8_t);

uint16_t I2CError[256];

void SDelay(uint16_t d) 
{ // 1.25 + 0.0475 * n uS ~0.05uS per click
    volatile int16_t v;
    for (v = 0; v < d ; v++ ) {};
}  // SDelay

#define SCLLowStartT SDelay(10)
#define DataLowPadT SDelay(16) // 10
#define SCLLowPadT SDelay(6)
#define SCLHighT SDelay(10)

#define I2CSDALow {I2C0SDA.write(0);I2C0SDA.output();SCLLowPadT;}
#define I2CSDAFloat {I2C0SDA.input();SCLLowPadT;}
#define I2CSCLLow {I2C0SCL.write(0);I2C0SCL.output();}
#define I2CSCLFloat {I2C0SCL.input();SCLHighT;}

void MyI2C::start(void)
{
    I2CSDAFloat;
    bool r = waitclock();
    I2CSDALow;
    SCLLowStartT;
    I2CSCLLow;
} // start

void MyI2C::stop(void)
{
    I2CSDALow;
    bool r = waitclock();
    I2CSDAFloat;
    SCLLowStartT;
} // stop

bool MyI2C::waitclock(void)
{
    static uint32_t s;
    I2CSCLFloat;        // set SCL to input, output a high
    s = 0;
    while ( I2C0SCL.read() == 0 )
    {
        if ( ++s > 16000 ) 
        { // ~1mS
            I2CError[0]++;
            // possible add SCL cycles here to attempt to force device reset
            //Stats[I2CFailS]++;
            return (false);
        }
    }
    return( true );
} // waitclock

uint8_t MyI2C::read(uint8_t ack) 
{
    static uint8_t s, d;
    I2CSDAFloat;
    d = 0;
    s = 8;
    do 
    {
        if ( waitclock() ) 
        {
            d <<= 1;
            if ( I2C0SDA.read() ) 
            {
                d |= 1;
                I2CSCLLow;
                DataLowPadT;
            }
            else 
            {
                I2CSCLLow;
                SDelay(10);//DataLowPadT;
            }
        } 
        else
        {
            return( 0 );
        }
    } while ( --s );
    if ( ack == I2C_NACK )
    {
        I2C0SDA.write(0xffff); // Port write with mask selecting SDA - messy
    }
    else
    {
        I2C0SDA.write(0);
    }
    I2C0SDA.output();
    SCLLowPadT;
    if ( waitclock() ) 
    {
        I2CSCLLow;
        return( d );
    } 
    else
    {
        return( 0 );
    }
} // read

uint8_t MyI2C::write(uint8_t d) 
{
    static uint8_t s, r;
    for ( s = 0; s < 8; s++) 
    {
        if ( d & 0x80 ) 
        {
            I2CSDAFloat;
        } 
        else 
        {
            I2CSDALow;
        }

        if ( waitclock() ) 
        {
            I2CSCLLow;
            d <<= 1;
        } 
        else
        {
            return(I2C_NACK);
        }
    }

    I2CSDAFloat;
    if ( waitclock() ) {
        if ( I2C0SDA.read() )
            r = I2C_NACK;
        else
            r = I2C_ACK;
        I2CSDALow;// kill runt pulses
        I2CSCLLow;
        return ( r );
    } 
    else
    {
//   I2CSCLLow;
        return(I2C_NACK);
    }

} // write

uint8_t MyI2C::blockread(uint8_t a, char* S, uint8_t l)
{
    static uint8_t b;
    static bool err;

    I2C0.start();
    err = I2C0.write(a|1) != I2C_ACK;
    for (b = 0; b < (l - 1); b++)
        S[b] = I2C0.read(I2C_ACK);
    S[l-1] = I2C0.read(I2C_NACK);
    I2C0.stop();

    return( err );
} // blockread

bool MyI2C::blockwrite(uint8_t a, const char* S, uint8_t l)
{
    static uint8_t b;

    I2C0.start();
    if ( I2C0.write(a) != I2C_ACK ) goto BlockWriteError; // use this?
    for ( b = 0; b < l; b++ )
        if ( I2C0.write(S[b]) != I2C_ACK ) goto BlockWriteError;
    I2C0.stop();

    return(false);

BlockWriteError:
    I2C0.stop();

    return(true);

} // blockwrite

bool I2C0AddressResponds(uint8_t s) {
    static bool r;
    I2C0.start();
    r = I2C0.write(s) == I2C_ACK;
    I2C0.stop();
    return (r);
} // I2C0AddressResponds

I use it by defining like this in a file

#include "MyI2C.h"

extern MyI2C I2C0;

...

#define ACCADDR (0x53<<1)
char buf[20];

bool initAcc()
{
    //Start sampling
    buf[0]=0x2D;
    buf[1]=0x08;
    
    //Set range 2G
    buf[2]=0x31;
    buf[3]=0x0B;
    return I2C0.blockwrite(ACCADDR, buf, 2) || I2C0.blockwrite(ACCADDR, &buf[2], 2);
}


...


It would be nice to have it written up in a published library of some sort. But im not sure how its done.

And it needs to mention the source: UAVXArm-GKE