3

I'm running the piece of code below to do a I2C loopback interface on an Arduino UNO rev3. The Arduino board acts as slave in the I2C bus and after it has received a packet it raises a line that triggers a read operation by the master. The Arduino simply answers back with the same data it received in the first place. The code below seems to work well:

#include <stdint.h>
#include <Wire.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

#define I2C_DATA_SIZE 350
#define I2C_IRQ_PIN   13
typedef struct {
  uint8_t   data[I2C_DATA_SIZE];
  uint16_t  count;
  uint16_t  size;
  bool      ready;
} i2c_packet_t;

static i2c_packet_t i2c_pkt;

void setup() {
  Serial.begin(115200);           // start serial for output

  Wire.begin(0x29);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent);
  memset(&i2c_pkt, 0, sizeof(i2c_packet_t));
  pinMode(I2C_IRQ_PIN, OUTPUT);
  digitalWrite(I2C_IRQ_PIN, false);

  /* Disable internall pullups */
  #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
    // deactivate internal pull-ups for twi
    // as per note from atmega8 manual pg167
    cbi(PORTC, 4);
    cbi(PORTC, 5);
  #else
    // deactivate internal pull-ups for twi
    // as per note from atmega128 manual pg204
    cbi(PORTD, 0);
    cbi(PORTD, 1);
  #endif


}

void loop() {
  digitalWrite(I2C_IRQ_PIN, i2c_pkt.ready);
  delay(100);
}

void requestEvent(void) {
  digitalWrite(I2C_IRQ_PIN, false);
  Wire.write(i2c_pkt.data, i2c_pkt.size);
  memset(&i2c_pkt, 0, sizeof(i2c_packet_t));
}

void receiveEvent(int howMany) {
  if (i2c_pkt.count == I2C_DATA_SIZE) {
    Serial.println("I2C packet too large");
  }
  for (int i = 0; i < howMany; i++) {
    i2c_pkt.data[i2c_pkt.count++] = Wire.read();
    if (i2c_pkt.count == 2) {
      i2c_pkt.size = (i2c_pkt.data[0] << 8) | i2c_pkt.data[1];
      i2c_pkt.ready = true;
    }
  }
}

I am aware that the Wire callbacks run on I2C interrupts so I tried to move the Wire.write() bit toloop(). I simply added a request in i2c_packet_t and now loop() and requestEvent() look like this:

void loop() {
  digitalWrite(I2C_IRQ_PIN, !i2c_pkt.request && i2c_pkt.ready);
  if (i2c_pkt.request) {
      if (i2c_pkt.ready) {
      digitalWrite(I2C_IRQ_PIN, false);
      Wire.write(i2c_pkt.data, i2c_pkt.size);
      memset(&i2c_pkt, 0, sizeof(i2c_packet_t));
    } else {
      Serial.println("ERROR: I2C request but no data available");
    }
  }
}

void requestEvent(void) {
    i2c_pkt.request = true;
}

For some reason, though, that solution does not work: the master device always receives "0xFF". I'm using a Nordic dev board for the master, which as internal pull-up resistors on the SDA and SCL lines.

That has me really puzzled, so any ideas are welcome.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
Genís
  • 1,468
  • 2
  • 13
  • 24
  • I have almost no experience with this kind of thing, but something that occurs to me is that if you're depending on hardware to write values, you might consider declaring `static i2c_packet_t i2c_pkt;` as `volatile`. The keyword is there specifically for that reason. – AndyG Mar 30 '17 at 11:58

0 Answers0