Friday, September 30, 2011

I2C: mbed reading from Arduino

Quite some time ago, I covered how to get two AVRs talking to each other using the I2C protocol.

Here's how to get an mbed and AVR talking with I2C, with the mbed acting as Master and the AVR acting as Slave.

Let's say you want the mbed to query the Arduino to respond with 4 bytes of data.

The mbed will write an address byte with the read bit set, using the Arduino's address, and then it'll request to read four bytes.  The protocol exchange looks like this:

1. Send a start sequence
2. Send 0xC1 ( I2C address of the slave with the R/W bit high (odd address)
3. Read data bytes from slave
4. Send the stop sequence.

(source: I2C Tutorial)

On the Arduino, call Wire.begin(7), where 7 is the Arduino's I2C address. That tells the Arduino I2C peripheral what messages to listen for. Then call Wire.onRequest() specifying a handler function that is called when the master requests data.

void handleI2CReceive(int numBytes)
{
  char command = Wire.receive(); // pretty much just ignore the command

  return;
}

void handleI2CRequest()
{
  byte data[4];

  // the code below just sends
  // data from the global variable
  // box, a struct with 4 char members
  //
  data[0] = box.x1;
  data[1] = box.y1;
  data[2] = box.x2;
  data[3] = box.y2;
  
  Wire.send(data, 4);
  
  return;
}

void setup() {
  Wire.begin(I2C_ADDRESS);

  box.x1 = box.x2 = box.y1 = box.y2 = 0;

  Wire.onRequest(handleI2CRequest);
  Wire.onReceive(handleI2CReceive);
}
On the mbed, use the "raw" I2C library's start(), write(), read(), stop() methods and manually set the address. Take the I2C slave address, left shift once, and set bit 0 high to indicate a read operation. Then read four bytes. Like this:

        cam.start();
        data[0] = (0x7<<1 | 0x01); // send address + !write = 1
        cam.write(data[0]);       // send address
        data[0] = cam.read(1);
        data[1] = cam.read(1);
        data[2] = cam.read(1);
        data[3] = cam.read(0);    // don't ack the last byte
        cam.stop();

This only works when the slave has one "register". Suppose you have an Arduino that can respond with one of several ADC readings. The protocol exchange would first include the master writing the desired register number to the slave, sending another start, then reading the data. The Arduino would have to accept the register number and store that until the next read request, responding with the correct register's value.

2 comments:

  1. For the Arduino code what is box? The code seems incomplete.

    ReplyDelete
  2. It's excerpted from a project. It's meant to illustrate all the relevant code for the handshake and transmission of data.

    The box variable is just a struct with 4 bytes of data, x1, y1, x2, y2. So that part of the code is just sending 4 bytes of data.

    Let me know if I can help with anything specific.

    ReplyDelete