Port I2C code from Arduino to Sercom

atmeli2ctwi

I'm having a MPU-6050 (gyro / accelerometer), it's connected to Arduino over I2C (via Wiring.h) and everything works well. Now I'm trying to port this code to an Atmel SAMD20 (Cortex M0+), which is bundled with Sercom I2C libraries. I have a connection to the device (it seems), and writing all yields OK, but I cannot read anything…

Arduino

const int MPU_addr=0x68;  // I2C address of the MPU-6050

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}

void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true);  // request a total of 14 registers
  AcX = Wire.read() << 8 | Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)

SAMD20

static enum status_code writeByte(struct i2c_master_module* module, const uint8_t byte) 
{
    static uint8_t buffer[1];
    buffer[0] = byte;
    struct i2c_master_packet packet = {
        .address = 0x68,
        .data = buffer,
        .data_length = 1,
    };

    return i2c_master_write_packet_wait_no_stop(module, &packet);
}

struct i2c_master_module module;

struct i2c_master_config config;
i2c_master_get_config_defaults(&config);

enum status_code initStatus = i2c_master_init(&module, SERCOM2, &config);
i2c_master_enable(&module);

enum status_code s1 = writeByte(&module, 0x6b); // PWR_MGMT_1 register
enum status_code s2 = writeByte(&module, 0x0);  // 0x0 wakes module up
i2c_master_send_stop(&module);

enum status_code s3 = writeByte(&module, 0x3b); 
static uint8_t buffer[14];
struct i2c_master_packet packet = {
    .address = 0x68,
    .data = buffer,
    .data_length = 14,
};
enum status_code s4 = i2c_master_read_packet_wait(&module, &packet);

So all my return values are always OK, but the value of buffer is garbage (all 0's except at buffer[10]).

Anyone knows more about I2C and Sercom?

Edit: Reading the who am I byte (0x75) yields in the correct value. So I'm starting to wonder whether the device actually gets out of sleep mode.

Best Answer

So on Arduino it seems like two packets are written (0x6b then 0x0). This is not the case. To start up the sensor needs to send one single package with two bytes value. After I changed the code to reflect that the sensor started working and I could read values.

enum status_code powerOnStatus;
{
    uint8_t retryCount = 0;

    static uint8_t buffer[2] = { 0x6b, 0x0 }; // power on packet
    struct i2c_master_packet packet = {
        .address = 0x68,
        .data = buffer,
        .data_length = 2,
    };

    while ((powerOnStatus = i2c_master_write_packet_wait(&module, &packet)) != STATUS_OK)
    {
        if (++retryCount >= maxRetries) break;
    }
}

I wrote about other issues I ran into while porting the code here.