Announcement

Collapse
No announcement yet.

Details of I2C Reads and Writes

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Jonathan Berling
    replied
    Hey AlanG,

    With Thread.sleep(), you're not blocking the op mode thread, right? Because that will cause problems. The I2C read / write should be complete when the port is ready. It gets kind of tricky to keep track of what is going on, since there are three different devices you have to deal with: the phone, the CDIM, and the Bosch IMU. Most of the errors I have when writing I2C drivers is forgetting this.

    It's been a while since I worked with a Bosch IMU, but I assume that 0x50 (0x28 x 2) is the address you'd use with the CDIM. It's only the I2C addresses that have this 7bit/8bit issue. The register addresses should be consistent.

    If you have the source posted somewhere I could take a quick look at it.

    Leave a comment:


  • AlanG
    replied
    Originally posted by Jonathan Berling View Post
    It is possible to read and write from the phone to the I2C controller in the same hardware cycle, but you have to be careful. There are two things you need to be mindful of. First is that just like the other USB devices readI2cCacheFromModule() is an asynchronous operation. The read will happen in the future, as soon as the I2C controller is able. The second thing is if you call both readI2cCacheFromModule() and writeI2cCacheToModule() in the same hardware loop cycle, the write will most likely over write the data you are trying to read. This is why writeI2cPortFlagOnlyToModule() exists, so that you can write only that one byte over the USB bus and not over write the data you are trying to read.



    Yes. It will also correctly update the number of bytes to write based on the length of the byte array. getCopyOfReadBuffer() will return a new buffer, which contains a copy of the data from the correct location in the buffer, it will also be the expected length. The Legacy Module and Core Device Interface Module share the same I2C API.
    Jonathan,

    I think I need some clarification about what do in situations where I have not yet registered a callback, because I'm trying to do immediate I2C "reads" or "writes" one right after the other. After a "PortFlagOnlyToModule" or "CacheToModule" write, how long must I "Thread.sleep", to allow the Core Device Interface Module to actually write the data to my I2c device (Bosch IMU)? I'm thinking that waiting until "isI2cPortReady" may not be long enough for the data to go from CDIM to hardware.

    Also, I have a question about 8-bit vs. 7-bit I2C addressing: The Bosch IMU is hard-wired to answer to either I2C address 0x28 or address 0x29, so I assume that I need to double whichever of those numbers I'm using as "baseI2Caddress" when I do:
    deviceInterface.enableI2cWriteMode(configuredI2CPo rt, baseI2Caddress, registerAddress, byteCount);
    or
    deviceInterface.enableI2cReadMode(configuredI2CPor t, baseI2Caddress, registerAddress, byteCount);
    But the Bosch IMU register set consists of 2 pages of 128 registers, with addresses 0x00 thru 0x7f. Am I correct that I do NOT double the values I use as "registerAddress"?

    Leave a comment:


  • Jonathan Berling
    replied
    It is not guaranteed. Currently all of our software drivers are written in such a way that we don't need to know exactly when the read is complete. We take based action on the most recent value received.

    In the future it would be a good idea to add a timestamp to the I2C methods so you can know exactly when they were received. In the mean time you could always zero out the data portion of the local read buffer before calling readI2cCacheFromModule() and getCopyOfReadBuffer(). When the read buffer changes your read is complete.

    Leave a comment:


  • GearTicks
    replied
    Originally posted by Jonathan Berling View Post
    readI2cCacheFromModule() is an asynchronous operation. The read will happen in the future, as soon as the I2C controller is able. The second thing is if you call both readI2cCacheFromModule() and writeI2cCacheToModule() in the same hardware loop cycle, the write will most likely over write the data you are trying to read.
    So if I were to run enableI2cReadMode() followed by readI2cCacheFromModule() in one hardware cycle, would the result of getCopyOfReadBuffer() in the next hardware cycle be guaranteed to contain the data that was read?

    Leave a comment:


  • dmssargent
    replied
    That I can do. P.S. a simple copy/paste is not going to be usable in our code due to its design

    Leave a comment:


  • FTC0417
    replied
    Originally posted by dmssargent View Post
    Can we borrow your code for use in our library? We will be keeping the comments intact, but not so much the structure of your code.
    dmssargent, could we have a short offline exchange about this? I'd like to first explore the feasibility of more maintainable modes of sharing than a simple copy/paste. Thanks. Bob Atkinson, [email protected]

    Leave a comment:


  • dmssargent
    replied
    Originally posted by FTC0417 View Post
    FWIW, we've written an "I2cDeviceClient" wrapper that makes it significantly easier to create software that models new kindes I2C sensors (our own first sensor is the AdaFruit BNO055 IMU). You just read and write the registers desired and all the complicated mode switching and buffer management is handled for you under the covers. See https://github.com/SwerveRobotics/ftc_app for details.
    Can we borrow your code for use in our library? We will be keeping the comments intact, but not so much the structure of your code.

    Leave a comment:


  • FTC0417
    replied
    FWIW, we've written an "I2cDeviceClient" wrapper that makes it significantly easier to create software that models new kindes I2C sensors (our own first sensor is the AdaFruit BNO055 IMU). You just read and write the registers desired and all the complicated mode switching and buffer management is handled for you under the covers. See https://github.com/SwerveRobotics/ftc_app for details.

    Leave a comment:


  • Jonathan Berling
    replied
    It is possible to read and write from the phone to the I2C controller in the same hardware cycle, but you have to be careful. There are two things you need to be mindful of. First is that just like the other USB devices readI2cCacheFromModule() is an asynchronous operation. The read will happen in the future, as soon as the I2C controller is able. The second thing is if you call both readI2cCacheFromModule() and writeI2cCacheToModule() in the same hardware loop cycle, the write will most likely over write the data you are trying to read. This is why writeI2cPortFlagOnlyToModule() exists, so that you can write only that one byte over the USB bus and not over write the data you are trying to read.

    Would I be correct in assuming that the new method copyBufferIntoWriteBuffer() will be passed a byte[] and put it in the correct location in the buffer?
    Yes. It will also correctly update the number of bytes to write based on the length of the byte array. getCopyOfReadBuffer() will return a new buffer, which contains a copy of the data from the correct location in the buffer, it will also be the expected length. The Legacy Module and Core Device Interface Module share the same I2C API.

    Leave a comment:


  • GearTicks
    replied
    Looking at the internals of the CDIM class, it looks like I2C write buffer has the following format:
    byte 0: MSB indicates whether read mode is activated
    byte 1: device address
    byte 2: register address
    byte 3: number of bytes to read or write
    bytes 4-30: raw data to send
    byte 31: "action flag" (stored in every bit)
    Would I be correct in assuming that the new method copyBufferIntoWriteBuffer() will be passed a byte[] and put it in the correct location in the buffer? It seems that the Legacy Module does a very similar thing except the LSB in byte 0 is always set. Will getCopyOfReadBuffer() also fix this offset? Will the Legacy Module be getting this method as well?

    On a somewhat related note, is it possible to do a read and a write in the same hardware cycle? I was thinking of using the following order of calls:
    1. setI2cPortActionFlag()
    2. readI2cCacheFromModule()
    3. Operate on the result of getCopyOfReadBuffer()
    4. copyBufferIntoWriteBuffer(the next write command)
    5. writeI2cCacheToModule() (does this write the action flag as well?)
    Does that look like it would work?
    Thanks for helping clarify this as I think it will be very interesting to work with custom I2C sensors this year.

    Leave a comment:


  • Jonathan Berling
    replied
    1. Once you set the port action flag and write it to the I2C controller (either the whole buffer, or just the flag) the I2C controller will take action and mark the port as busy. Once it has completed that action (read or write over an I2C bus) it will mark that port as no longer busy.
    2. Yes


    Your plan seems good to me.

    Leave a comment:


  • AlanG
    replied
    Thanks, Jonathan,

    Things are becoming clearer to me. So, let me now ask:
    1. Once I do a setI2cPortActionFlag for a write or a writeI2cPortFlagOnlyToModule for a read, does that I2C port immediately go busy (not ready) until the write or the read is done? (I'm assuming "yes".)
    2. Given that I've specified a certain byte count in my "enableI2cxxxMode" call, is it guaranteed that all those bytes will be read or written before the I2C port becomes ready (not busy) again? (Again, I'm assuming "yes".)

    If that's the case, then I think the situation with the Bosch IMU becomes the following:
    1. I do 2 separate 1-byte "writes" to set its register page ID to zero, then set the REST bit in one of the registers on page 0.
    2. I repeatedly read the one calibration status byte to determine that the IMU has finished its self-calibration. (These are the steps I would want to do after the "ARM" button, but before the "START" button)
    3.I do one more 1-byte write to put the IMU into operational mode.
    4. I enable reading, specifying all of the consecutive data bytes I want to read on a routine basis (high/low byte pairs for 3 gyro Euler angles and 3 accelerometers).
    5. Register a callback which will BOTH present me a new set of IMU output bytes every time the I2C port becomes ready AND do the writeI2cPortFlagOnlyToModule(port) operation which will get me my next new set of IMU output bytes.

    Are my assumptions and design ideas correct?

    Thanks again!

    Leave a comment:


  • Jonathan Berling
    replied
    Hello Alan,
    1. They are 32 bytes long, 27 bytes is usable for I2C data, the other bytes contain information on how to send the data. You can use getI2cReadCache().length / getI2cWriteCache().length to find the length of a buffer. Methods such as enableI2cReadMode and setI2cPortActionFlag operate on the other bytes in this buffer.
    2. The I2C data starts at index I2cController.I2C_BUFFER_START_ADDRESS, which is 4
    3. These are mostly good, but here is some more info:
      • There is no need to call enableI2cReadMode each time, the port stays in read mode once set
      • You also need to write the port action flag to tell the I2C controller to read from the I2C bus again, use writeI2cPortFlagOnlyToModule(port); I would call it right after setting the port action flag. You can mix reads and writes from the phone to the I2C controller, just not from the I2C controller to the I2C device.


    I realize while typing this that I can make all of the xxxToModule methods more clear by renaming them to xxxToController. Think of the "module" in I2cController API as the controller.

    The API is the same on the Core Device Interface Module and the Legacy Module. These are good questions. Let me know if you have any more.

    Leave a comment:


  • AlanG
    started a topic Details of I2C Reads and Writes

    Details of I2C Reads and Writes

    Johnathan or Tom, could you please tell us:

    1. When you call either of the I2cController methods getI2cReadCache() or getI2cWriteCache, how large are the byte arrays which are returned?
    2. At what array index does one start loading bytes into the Write Cache, before the cache is written? 0? 4? Another value?
    What about reading from the Read Cache? Somewhere in this forum I have read that the incoming data starts at array index 4, which would be the 5th byte in the array. Is that correct?
    3. Please tell me if the following is the correct procedure for reading or writing data immediately, without waiting for a callback:
    a. Wait until isI2cPortReady() returns true.
    b. Lock the Read or Write Cache lock.
    c. Copy out of the cache or write into the cache the relevant data bytes (from the previous read or for the next write), starting at the proper cache array index.
    d. Unlock the cache.
    e. enableI2cReadMode (or enableI2cWriteMode).
    f. readI2cCacheFromModule (or writeI2cCacheToModule)
    g. setI2cPortActionFlag
    Primarily, I'm asking whether this procedure is correct for the Core Device Interface Module. Please identify any modifications needed for I2C devices connected to the Legacy Module.

    Thanks!
Working...
X