Подключение и программирование устройства через I2C к плате Arduino

  1. Подключаем устройство к пинам SDA, SCL, GND, VCC. Если необходимо ставим подтягивающий резистор к питанию. https://www.youtube.com/watch?v=6IAkYpmA1DQ (5:52)
  2. Ищем datasheet на датчик с адресами устройства и адресами внутренних регистров для считывания конкретной информации.
    Как вариант можно проверить адреса с помощью i2c_scanner скетча. https://playground.arduino.cc/Main/I2cScanner/

Сканер устройств на шине i2c

#include <Wire.h>
 
void setup(){
    Wire.begin();
 
    Serial.begin(9600);
    while (!Serial);
    Serial.println("\nI2C Scanner");
} 

void loop(){
    byte error, address;
    int nDevices;
 
    Serial.println("Scanning...");
 
    nDevices = 0;
    for(address = 8; address < 127; address++ ){
        Wire.beginTransmission(address);
        error = Wire.endTransmission();
 
        if (error == 0){
            Serial.print("I2C device found at address 0x");
            if (address<16)
                Serial.print("0");
            Serial.print(address,HEX);
            Serial.println(" !");
 
            nDevices++;
        }
        else if (error==4) {
            Serial.print("Unknow error at address 0x");
            if (address<16)
                Serial.print("0");
            Serial.println(address,HEX);
        } 
    }
    if (nDevices == 0)
        Serial.println("No I2C devices found\n");
    else
        Serial.println("done\n");
 
    delay(5000);
}

Пример кода для работы через I2C протокол

#include <Wire.h>

int ADXLAddress = 0x53; // адрес устройства

#define X_Axis_Register_DATAX0 0x32 // адрес регистра DATAX0
#define X_Axis_Register_DATAX1 0x33 // адрес регистра DATAX1
#define Power_Register 0x2D // адрес ргеистра управления питанием

int X0,X1,X_out;

void setup() {
  Wire.begin(); // инициализируем библиотеку Wire
  Serial.begin(9600);
  delay(100);
  // Enable measurement
  Wire.beginTransmission(ADXLAddress);
  Wire.write(Power_Register);
  // Bit D3 High for measuring enable (0000 1000)
  Wire.write(8);  
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(ADXLAddress); // начинаем обмен данными к датчику
  //Опрашиваем конкретные регистры 
  Wire.write(X_Axis_Register_DATAX0);
  Wire.write(X_Axis_Register_DATAX1);
  
  Wire.endTransmission(); // заканчиваем общение и получаем данные их 2 регистров
  
  Wire.requestFrom(ADXLAddress,2); // запоашиваем переданные 2 байта из 2 регистров
  
  if(Wire.available()<=2) {  // 
    X0 = Wire.read(); // Читаем значения из регистра
    X1 = Wire.read();   
  }
  
  Serial.print("X0= ");
  Serial.print(X0);
  Serial.print("   X1= ");
  Serial.println(X1);
}

Скетч для считывания данных гироскопа / акселерометра MPU 9250

#include <Wire.h>
#include <TimerOne.h>
 
#define MPU9250_ADDRESS 0x68
#define MAG_ADDRESS 0x0C
 
#define GYRO_FULL_SCALE_250_DPS 0x00 
#define GYRO_FULL_SCALE_500_DPS 0x08
#define GYRO_FULL_SCALE_1000_DPS 0x10
#define GYRO_FULL_SCALE_2000_DPS 0x18
 
#define ACC_FULL_SCALE_2_G 0x00 
#define ACC_FULL_SCALE_4_G 0x08
#define ACC_FULL_SCALE_8_G 0x10
#define ACC_FULL_SCALE_16_G 0x18
 
// This function read Nbytes bytes from I2C device at address Address. 
// Put read bytes starting at register Register in the Data array. 
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
    {
      // Set register address
      Wire.beginTransmission(Address);
      Wire.write(Register);
      Wire.endTransmission();
 
      // Read Nbytes
      Wire.requestFrom(Address, Nbytes); 
      uint8_t index=0;
      while (Wire.available())
      Data[index++]=Wire.read();
    }
 
// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
    {
      // Set register address
      Wire.beginTransmission(Address);
      Wire.write(Register);
      Wire.write(Data);
      Wire.endTransmission();
    }
 
// Initial time
long int ti;
volatile bool intFlag=false;
 
// Initializations
void setup()
    {
      // Arduino initializations
      Wire.begin();
      Serial.begin(115200);
 
      // Set accelerometers low pass filter at 5Hz
      I2CwriteByte(MPU9250_ADDRESS,29,0x06);
      // Set gyroscope low pass filter at 5Hz
      I2CwriteByte(MPU9250_ADDRESS,26,0x06);
 
      // Configure gyroscope range
      I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_1000_DPS);
      // Configure accelerometers range
      I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_4_G);
      // Set by pass mode for the magnetometers
      I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);
 
      // Request continuous magnetometer measurements in 16 bits
      I2CwriteByte(MAG_ADDRESS,0x0A,0x16);
 
      pinMode(13, OUTPUT);
      Timer1.initialize(10000); // initialize timer1, and set a 1/2 second period
      Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
 
      // Store initial time
      ti=millis();
    } 
 
// Counter
long int cpt=0;
 
void callback()
    { 
      intFlag=true;
      digitalWrite(13, digitalRead(13) ^ 1);
    }
 
// Main loop, read and display data
void loop()
    {
      while (!intFlag);
      intFlag=false;
 
      // Display time
      //Serial.print (millis()-ti,DEC);
      //Serial.print ("t");
 
      // _______________
      // ::: Counter :::
 
      // Display data counter
      // Serial.print (cpt++,DEC);
      // Serial.print ("t");
 
      // ____________________________________
      // ::: accelerometer and gyroscope :::
 
      // Read accelerometer and gyroscope
      uint8_t Buf[14];
      I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);
 
      // Create 16 bits values from 8 bits data
 
      // Accelerometer
      int16_t ax=-(Buf[0]<<8 | Buf[1]);
      int16_t ay=-(Buf[2]<<8 | Buf[3]);
      int16_t az=Buf[4]<<8 | Buf[5];
 
      // Gyroscope
      int16_t gx=-(Buf[8]<<8 | Buf[9]);
      int16_t gy=-(Buf[10]<<8 | Buf[11]);
      int16_t gz=Buf[12]<<8 | Buf[13];
 
      // Display values
 
     // Accelerometer
     /*Serial.print (ax,DEC); 
     Serial.print ("-ax");
     Serial.print (ay,DEC);
     Serial.print ("-ay");
     Serial.print (az,DEC); 
     Serial.print ("-az");
     */ 
     // Gyroscope
     Serial.print (gx,DEC); 
     Serial.print ("-gx ");
     Serial.print (gy,DEC);
     Serial.print ("-gy ");
     Serial.print (gz,DEC); 
     Serial.println ("-gz ");
 

     delay(100); 
   }

Код для гироскопа с комментариями

#include <Wire.h> // подключаем библиотеку для работы с I2C

//--- Адреса регистров гироскопа
#define Gyro_gX0 0x28  
#define Gyro_gX1 0x29
#define Gyro_gY0 0x2A
#define Gyro_gY1 0x2B
#define Gyro_gZ0 0x2C  
#define Gyro_gZ1 0x2D

int Gyro = 0x69; //Адрес самого устройства

//--- Создаем переменные для хранения значений с датчика и рассчетов результирующих
int gX0, gX1, gX_out;
int gY0, gY1, gY_out;
int gZ0, gZ1, gZ_out;
float Xg,Yg,Zg;
float angleX,angleY,angleZ,angleXc,angleYc,angleZc;


unsigned long start, finished, elapsed;
float dt=0.015;

void setup()
{
  Wire.begin();                
  Serial.begin(9600);    
  delay(100);
  
  Wire.beginTransmission(Gyro); // начинаем общение с устройством
  Wire.write(0x20); // ? CTRL_REG1 - Power Mode
  Wire.write(15);   // ? Normal mode: 15d - 00001111b   
  Wire.endTransmission();
  
  Wire.beginTransmission(Gyro);
  Wire.write(0x23); // определяем чувствительность и шкалу которую будем использовать CTRL_REG4 - Sensitivity, Scale Selection
  Wire.write(48);   // ? 2000dps: 48d - 00110000b
  Wire.endTransmission();
}

void loop()
{
  start=millis();
  //---- X-Axis
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gX0);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gX0 = Wire.read();
  }
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gX1);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gX1 = Wire.read();
  }

  //---- Y-Axis
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gY0);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gY0 = Wire.read();
  }
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gY1);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gY1 = Wire.read();
  }
  
  //---- Z-Axis
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gZ0);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gZ0 = Wire.read();
  }
  Wire.beginTransmission(Gyro); // transmit to device
  Wire.write(Gyro_gZ1);
  Wire.endTransmission();
  Wire.requestFrom(Gyro,1); 
  if(Wire.available()<=1)   
  {
    gZ1 = Wire.read();
  }
  
  //---------- X - Axis
  
  // Raw Data
  gX1=gX1<<8;
  gX_out =gX0+gX1;
  
  // From the datasheet: 70 mdps/digit
  Xg=gX_out*0.07; // Angular rate
  // Angular_rate * dt = angle
  angleXc = Xg*dt;
  angleX = angleX + angleXc;

  //---------- Y - Axis
  gY1=gY1<<8;
  gY_out =gY0+gY1;
  Yg=gY_out*0.07;
  angleYc = Yg*dt;
  angleY = angleY + angleYc;
  
  //---------- Z - Axis
  gZ1=gZ1<<8;
  gZ_out =gZ0+gZ1;
  Zg=gZ_out*0.07;
  angleZc = Zg*dt;
  angleZ = angleZ + angleZc;

  
  // Prints the data on the Serial Monitor
  Serial.print("angleX= ");
  Serial.print(angleX);
  Serial.print("   angleY= ");
  Serial.print(angleY);
  Serial.print("   angleZ= ");
  Serial.println(angleZ);
  
  delay(10);
  // Calculating dt
  finished=millis();
  elapsed=finished-start;
  dt=elapsed/1000.0;
  start = elapsed = 0;
  
}

Документы по микросхеме MPU 9250 (гироскоп, акеселерометр, барометр)

  1. Спецификация (технические характеристики) микросхемы
  2. Карта регистров микросхемы
  3. Готовая библиотека для MPU 9250

Полезные ссылки

  1. Достаточно полезное видео об устройстве модуля MPU 9250 , который мы используем https://www.youtube.com/watch?v=slzeiFhV6i0