Non-Blocking I2C using DMAC

Here's the "MPU6050_Gyroscope_V2.ino" example code, using two MPU6050 gyroscope/accelerometer devices.

The first device is on the standard SCL/SDL pins, the second on D3 and D4, (use D2 instead of D4 if you're using the Arduino M0 Pro or M0):

#include <I2C_DMAC.h>
#include <wiring_private.h>

#define MPU6050_ADDRESS 0x68                                // Device address when ADO = 0
#define PWR_MGMT_1      0x6B
#define CONFIG          0x1A
#define GYRO_CONFIG     0x1B
#define ACCEL_CONFIG    0x1C
#define GYRO_XOUT_H     0x43

uint8_t data[6];
uint8_t data1[6];

I2C_DMAC I2C1(&sercom2, 4, 3);                              // Create (instantiate) a second I2C1 object with sercom2, on pins D3 and D4

void setup() 
{
  SerialUSB.begin(115200);                                  // Activate the native USB port
  while(!SerialUSB);                                        // Wait for the native USB to be ready
  
  I2C.begin(400000);                                        // Start I2C bus at 400kHz          
  I2C.writeByte(MPU6050_ADDRESS, PWR_MGMT_1, 0x01);         // Wake up the MPU6050 device
  delay(100);                                               // Wait for the MPU6050 to settle   
  I2C.writeByte(MPU6050_ADDRESS, CONFIG, 0x4);              // Set the gyro/accel filter to 20Hz  
  I2C.writeByte(MPU6050_ADDRESS, GYRO_CONFIG, 0x1 << 3);    // Set full scale range to +/-500 degrees/s 
  I2C.writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0x1 << 3);   // Set full scale range to +/-4g
  I2C.initWriteRegAddr(MPU6050_ADDRESS, GYRO_XOUT_H);       // Set-up DMAC to write to MPU6050 register pointer
  I2C.initReadBytes(MPU6050_ADDRESS, data, sizeof(data));   // Set DMAC to read the data
  I2C.attachReadCallback(gyro1Callback);                    // Attach a read callback function to I2C
  
  I2C1.begin(400000);                                       // Start I2C bus at 400kHz 
  pinPeripheral(3, PIO_SERCOM_ALT);                         // Assign D3 to SERCOM2 I2C SDA
  pinPeripheral(4, PIO_SERCOM_ALT);                         // Assign D4 to SERCOM2 I2C SCL
  I2C1.setWriteChannel(2);                                  // Set the I2C1 DMAC write channel to 2
  I2C1.setReadChannel(3);                                   // Set the I2C1 DMAC read channel to 3
  I2C1.writeByte(MPU6050_ADDRESS, PWR_MGMT_1, 0x01);        // Wake up the MPU6050 device
  delay(100);                                               // Wait for the MPU6050 to settle   
  I2C1.writeByte(MPU6050_ADDRESS, CONFIG, 0x4);             // Set the gyro/accel filter to 20Hz  
  I2C1.writeByte(MPU6050_ADDRESS, GYRO_CONFIG, 0x1 << 3);   // Set full scale range to +/-500 degrees/s  
  I2C1.writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0x1 << 3);  // Set full scale range to +/-4g
  I2C1.initWriteRegAddr(MPU6050_ADDRESS, GYRO_XOUT_H);      // Set-up DMAC to write to MPU6050 register pointer
  I2C1.initReadBytes(MPU6050_ADDRESS, data1, sizeof(data1));// Set DMAC to read the data
  I2C1.attachReadCallback(gyro2Callback);                   // Attach a read callback function to I2C1
}

void loop() 
{ 
  I2C.write();                                              // DMAC write to set MPU6050 register pointer to start of the data
  // Add your concurrent code here...
  while(I2C.writeBusy);                                     // Wait for synchronization 
  I2C.read();
  // Add your concurrent code here...
  while(I2C.readBusy);                                      // Wait for synchronization 
  //SerialUSB.print(F("Gyro 1: "));                           // Acquire raw gyro values from MPU6050 device 1
  SerialUSB.print((int16_t)((data[0] << 8) | data[1]));     
  SerialUSB.print(F("   "));
  SerialUSB.print((int16_t)((data[2] << 8) | data[3]));
  SerialUSB.print(F("   "));
  SerialUSB.println((int16_t)((data[4] << 8) | data[5]));

  I2C1.write();                                              // DMAC write to set MPU6050 register pointer to start of the data
  // Add your concurrent code here...
  while(I2C1.writeBusy);                                     // Wait for synchronization 
  I2C1.read();
  // Add your concurrent code here...
  while(I2C1.readBusy);                                      // Wait for synchronization 
  //SerialUSB.print(F("Gyro 2: "));                            // Acquire raw gyro values from MPU6050 device 2
  SerialUSB.print((int16_t)((data1[0] << 8) | data1[1]));    
  SerialUSB.print(F("   "));
  SerialUSB.print((int16_t)((data1[2] << 8) | data1[3]));
  SerialUSB.print(F("   "));
  SerialUSB.println((int16_t)((data1[4] << 8) | data1[5]));
}

void gyro1Callback()
{
  SerialUSB.print(F("Gyro 1: "));
}

void gyro2Callback()
{
  SerialUSB.print(F("Gyro 2: "));
}

The callbacks functions are optional and are implemented for demonstration purposes only.