espruino / Espruino

The Espruino JavaScript interpreter - Official Repo
http://www.espruino.com/
Other
2.77k stars 746 forks source link

I2C Repeated Start #390

Closed tomgidden closed 9 years ago

tomgidden commented 10 years ago

I'm trying to write a library for MPL3115A2 (note, NOT the existing MPL115A2) but according to the Arduino library along with various Google results, it appears that it needs Repeated Start capability on I2C: I think this means not sending a STOP command after a read or a write.

Whether or not this is correct behaviour for I2C slave devices (which as far as I can see should be able to support with and without repeated starts), it's been an issue on Raspberry Pi (since fixed), and was added to Arduino in v.1.0.1.

Related code (from the MPL3115A2 Arduino driver):

// These are the two I2C functions in this sketch.
byte MPL3115A2::IIC_Read(byte regAddr)
{
  // This function reads one byte over IIC
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(regAddr);  // Address of CTRL_REG1
  Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
  Wire.requestFrom(MPL3115A2_ADDRESS, 1); // Request the data...
  return Wire.read();
}

and also, http://raspberry.znix.com/2013/03/raspberry-pi-and-repeated-start-i2c.html

I'd guess this might be best presented as an extra optional boolean parameter on I2C.readFrom and I2C.writeTo, although the fix for #370 might scupper that.

gfwilliams commented 10 years ago

Thanks. Yes, that's a pain.

Are there any other patterns apart from write then read? I guess it might end up being easier to just have a new function that did just that.

Or perhaps:

I2C.writeTo({address:0x55, stop:false}, data);
I2C.readFrom({address:0x55, stop:false}, count);
tomgidden commented 10 years ago

I don't know of any other patterns, but it's possible I guess. Maybe keep writeTo and readFrom as-is for a higher-level capability where it doesn't matter, but also build beginTransmission,write,requestFrom,read and endTransmission to match Arduino when greater control is needed. A bit like fopen/fread/fclose vs. open/read/close in C.

It would be handy to have an I2C.readRegister(address, register, count=1) convenience function, although I don't know how widespread Repeated Start is. I've only programmed a few I2C devices, and this is the first one I've encountered that needs Repeated Start. I don't know whether other devices allow it, prefer it or prohibit it. Apparently it is an official thing, though: http://www.i2c-bus.org/repeated-start-condition/

gfwilliams commented 10 years ago

I'm sort of against re-implementing Arduino's functions, because they tend to complicate things and provide more opportunity for error. Also a silicon problem on the STM32F1 means that you can totally lock up the I2C peripheral if you send a STOP at the wrong time, so it's good not to let people have that choice :)

I don't think that allowing an object instead of a numeric address would cause any backwards compatibility problems though - at least not unless people were doing something really nasty.

gfwilliams commented 10 years ago

Just tried to fix this... it should work like:

I2C.writeTo({address:0x55, stop:false}, data);
I2C.readFrom({address:0x55, stop:false}, count);

I don't have anything to test with though, so please let me know how this works.

A build should be up soon at: http://www.espruino.com/binaries/git/commits/a939ef6285ca9c6f639d4edaaf86fe7606176829

gfwilliams commented 10 years ago

Looks like this may not work - but I'll have to get some hardware to test with first.

tomgidden commented 10 years ago

@gfwilliams : unfortunately I'm not going to have a chance to test this for a while, thanks to real life. However, I'd be happy to buy you an MPL3115A2 if you want to have a go with it... if so, email or DM me with postal address.

tomgidden commented 10 years ago

@gfwilliams I just tested this with 1v70:

    var _read_reg = function (_i2c, _reg) {
        _i2c.writeTo({address:MPL3115A2_ADDRESS /* 0x60 */, stop:false}, _reg);
        var bs = _i2c.readFrom(MPL3115A2_ADDRESS, 1);
        return bs[0];
    };

    var whoami = _read_reg(_i2c, MPL3115A2_WHO_AM_I /* 0x0C */);
    console.log("WHOAMI (should return 196 apparently): "+whoami);

returns:

WHOAMI: undefined
Uncaught InternalError: Timeout on I2C Read BUSY

I've been using https://github.com/sparkfun/MPL3115A2_Breakout/blob/master/firmware/MPL3115A2/MPL3115A2.ino#L355 as a reference.

I'm currently using BMP180 instead as it's cheaper, more accurate, and simpler. However the MPL3115A2 does have on-board altitude calculation (lower memory footprint?) and has interrupt lines (ultra low power capability). The I2C Repeated Start is, however, probably worth fixing for other devices I suppose.

gfwilliams commented 10 years ago

Hi - thanks for testing it out (and for the offer of a sensor)... I've actually just bought one though - when it arrives I'll see if I can fix the repeated start issue. It should only be a few lines I hope!

tomgidden commented 10 years ago

Incidentally, while hacking the libraries on nrf51, I noticed that the MPU6050 code write/read routine for that SDK uses Repeated Start too. I believe it's optional on that sensor.

errantspark commented 9 years ago

I'm trying to get an MMA8451 (http://www.adafruit.com/datasheets/Freescale_MMA8451QR1.pdf) working and it doesn't seem like the repeated start is working properly? I get

 Uncaught InternalError: Timeout on I2C Read BUSY

when I try to read after writing an address with stop set to false. Reading gives me the same 7 bytes every time.

Relevant Arduino code: https://github.com/adafruit/Adafruit_MMA8451_Library/blob/master/Adafruit_MMA8451.cpp

gfwilliams commented 9 years ago

Ok, thanks. Will try and look into it when I get back to the UK.

What board was this on? The I2C implementations are different on different chips

gfwilliams commented 9 years ago

What board were you using? a Pico?

errantspark commented 9 years ago

yup, using a pico

gfwilliams commented 9 years ago

Fixed. On the MPL3115, the following code now works:

function r(addr) {
  i2c.writeTo({address:0x60, stop:false}, addr);
  return i2c.readFrom(0x60,1)[0];
}

var i2c = I2C3;
i2c.setup({sda:B4,scl:A8});
r(0x0c)==196; // WHOAMI
errantspark commented 8 years ago

Just came back to this project, using 1v85 and I cannot get it to connect on I2C1. On I2C3 (on a different board) it works fine. Unfortunately for my current project i'm using those pins for SPI so a way to get this working with I2C1 would be super helpful. Same Uncaught InternalError: Timeout on I2C Write BUSY error as before. Using the code snippet from above but different (obviously) addresses.

gfwilliams commented 8 years ago

You sure it's not that you're missing pullup resistors or having SDA/SCL mixed? The code is identical for I2C1/2 and 3 - so the chances of it working on one and not the other are pretty minimal.