ARMmbed / mbed-os

Arm Mbed OS is a platform operating system designed for the internet of things
https://mbed.com
Other
4.67k stars 2.98k forks source link

LPC1549 can't communicate with mbed-based I2C slave #3263

Closed neilt6 closed 6 years ago

neilt6 commented 7 years ago

I'm unable to communicate with an mbed-based I2C slave using an LPC1549. Here is the code I'm using to reproduce the problem:

I2C Master Code (running on LPC1549)

#include "mbed.h"

I2C i2c(SDA, SCL);
char buff[13];

int main()
{
    i2c.frequency(100000);

    //Write some data to the slave
    sprintf(buff, "Hello World!");
    if (i2c.write(0xAA, buff, sizeof(buff)) != 0) {
        printf("Write error!\n");
    }
    printf("Master sent: \"%s\"\n", buff);

    //Read the data back from the slave
    memset(buff, 0, sizeof(buff));
    if (i2c.read(0xAA, buff, sizeof(buff)) != 0) {
        printf("Read error!\n");
    }
    printf("Master got: \"%s\"\n", buff);

    while (1);
}

Output

Write error!
Master sent: "Hello World!"
Read error!
Master got: ""

I2C Slave Code (running on LPC11U24)

#include "mbed.h"

I2CSlave i2c(p28, p27);
char buff[13];

int main()
{
    i2c.frequency(100000);
    i2c.address(0xAA);

    while (1) {
        int rxStatus = i2c.receive();
        if (rxStatus == I2CSlave::WriteAddressed) {
            i2c.read(buff, sizeof(buff));
            printf("Slave got: \"%s\"\n", buff);
        } else if (rxStatus == I2CSlave::ReadAddressed) {
            i2c.write(buff, sizeof(buff));
            printf("Slave sent: \"%s\"\n", buff);
        }
    }
}

Output

Slave got: "Hello World!"

The I2C master code works fine on an LPC11U68, but throws up errors on an LPC1549. I've traced these errors to _i2capi.c line 122 and line 187, but I haven't been able to figure out what's causing them.

neilt6 commented 7 years ago

I think I may have found the culprit, the i2c_slave_read() implementation for the LPC11Uxx microcontrollers (and possibly others) appears to be flawed:

int i2c_slave_read(i2c_t *obj, char *data, int length) {
    int count = 0;
    int status;

    do {
        i2c_clear_SI(obj);
        i2c_wait_SI(obj);
        status = i2c_status(obj);
        if((status == 0x80) || (status == 0x90)) {
            data[count] = I2C_DAT(obj) & 0xFF;
        }
        count++;
    } while (((status == 0x80) || (status == 0x90) ||
            (status == 0x060) || (status == 0x70)) && (count < length));

    /* The previous loop exited after the expected number of bytes were received, but before
       the master set the stop condition, which means this insidious piece of code will run
       every time. Since when do slaves issue stop conditions anyway, is that even legal? */
    if(status != 0xA0) {
        i2c_stop(obj);
    }

    i2c_clear_SI(obj);

    return count;
}

As indicated by my comments, the routine is issuing it's own stop condition before the master is able to, which is probably causing the I2C peripheral on the LPC1549 to enter a state that isn't handled by the library (arbitration lost perhaps?). At any rate, patching the routine to wait for a stop condition from the master fixed my problem. Would someone else care to verify my findings so that we can start working on a PR?

ghost commented 6 years ago

GitHib issue review: Closed due to inactivity. Please re-file if critical issues found.