poniedziałek, 25 lipca 2022

[50] STM32F4 - MPU-6050

W tym poście chciałbym opisać sposób obsługi 3 osiowego akcelerometru oraz żyroskopu MPU-6050 komunikującego się po I2C. 


MPU-6050 składa się z trzy osiowego żyroskopu, oraz trzy osiowego akcelerometru. Układ można rozszerzyć 

Schemat podobnego modułu przedstawiłem poniżej [link]:


Opis wyprowadzeń jest następujący:

  • VCC - Zasilanie 3V3
  • GND - Masa układu
  • SCL - I2C linia zegara
  • SDA - I2C linia danych
  • XDA - I2C linia danych do podłączenia zewnętrznych modułów.
  • XCL - I2C linia zegara do podłączenia zewnętrznych modułów.
  • ADO - Ustalenie adresu urządzenia. Domyślnie linia podciągnięta do masy przez rezystor R6.
  • INT - Wyzwolenie przerwania.

Diagram blokowy czujnika pobrany z dokumentacji producenta:


Jak można zaobserwować powyżej pomiaru można dokonać z osi X,Y, Z akcelerometru, osi X, Y, Z żyroskopu oraz czujnika temperatury. Dane z żyroskopu oraz akcelerometru przerabiane są przez 16 bitowe przetwornika ADC. DSP zajmuje się obróbką otrzymanych wyników od czujników wewnętrznych oraz ewentualnych czujników zewnętrznych podłączonych po liniach AUX_xx. Następnie dane mogą być odczytane bezpośrednio z rejestrów lub z FIFO. 

Orientacja osi czujnika jest następująca:


Inicjalizacja układu interfejsu została wykonana w programie CubeMx:


Układ komunikuje się za pomocą interfejsu I2C. Inicjalizacja interfejsu:

  1. static void MX_I2C1_Init(void)
  2. {
  3.   /* USER CODE BEGIN I2C1_Init 0 */
  4.   /* USER CODE END I2C1_Init 0 */
  5.   /* USER CODE BEGIN I2C1_Init 1 */
  6.   /* USER CODE END I2C1_Init 1 */
  7.   hi2c1.Instance = I2C1;
  8.   hi2c1.Init.ClockSpeed = 100000;
  9.   hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  10.   hi2c1.Init.OwnAddress1 = 230;
  11.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  12.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  13.   hi2c1.Init.OwnAddress2 = 0;
  14.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  15.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  16.   if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  17.   {
  18.     Error_Handler();
  19.   }
  20.   /* USER CODE BEGIN I2C1_Init 2 */
  21.   /* USER CODE END I2C1_Init 2 */
  22. }

Rozpoczęcie pracy z układem można wykonać wykorzystując jedną z dwóch funkcji:

  1. uint8_t MPU6050_Init_Default(void) {
  2.     HAL_StatusTypeDef opStatSelectClock = MPU6050_SelectClockSource(MPU6050_CLOCK_PLL_XGYRO);
  3.     if(opStatSelectClock != HAL_OK) { return 1; }
  4.  
  5.     HAL_StatusTypeDef opStatSetFullScaleGyro = MPU6050_SetFullScaleGyroRange(MPU6050_GYRO_FS_250);
  6.     if(opStatSetFullScaleGyro != HAL_OK) { return 2; }
  7.  
  8.     HAL_StatusTypeDef opStatSetFullScaleaccel = MPU6050_SetFullScaleAccelRange(MPU6050_ACCEL_FS_2);
  9.     if(opStatSetFullScaleaccel != HAL_OK) { return 3; }
  10.  
  11.     HAL_StatusTypeDef opStatSetSleepEnabled = MPU6050_SetSleepEnabled(0);
  12.     if(opStatSetSleepEnabled != HAL_OK) { return 4; }
  13.  
  14.     return 0;
  15. }
  16.  
  17. uint8_t MPU6050_InitCustomConf(const enum mpu6050_ClockSource_e clockSource, const enum gyro_FullScale_e gyroRange, const enum accel_FullScale_e accelRange) {
  18.     HAL_StatusTypeDef opStatSelectClock = MPU6050_SelectClockSource((uint8_t)clockSource);
  19.     if(opStatSelectClock != HAL_OK) { return 1; }
  20.  
  21.     HAL_StatusTypeDef opStatSetFullScaleGyro = MPU6050_SetFullScaleGyroRange((uint8_t)gyroRange);
  22.     if(opStatSetFullScaleGyro != HAL_OK) { return 2; }
  23.  
  24.     HAL_StatusTypeDef opStatSetFullScaleaccel = MPU6050_SetFullScaleAccelRange((uint8_t)accelRange);
  25.     if(opStatSetFullScaleaccel != HAL_OK) { return 3; }
  26.  
  27.     HAL_StatusTypeDef opStatSetSleepEnabled = MPU6050_SetSleepEnabled(0);
  28.     if(opStatSetSleepEnabled != HAL_OK) { return 4; }
  29.  
  30.     return 0;
  31. }

Weryfikacja podłączonego układu należy wykonać funkcją sprawdzającą id:

  1. uint8_t MPU6050_ReadAndCheckDeviceId(void)
  2. {
  3.     uint8_t deviceId = MPU6050_ReadDeviceID();
  4.  
  5.     if((deviceId == 0x34) || (deviceId == 0xC)) {
  6.         return 1;
  7.     }
  8.     return 0;
  9. }

Po podstawowej inicjalizacji układu można przejść do podstawowego odczytu wyników. Do tego celu służą następujące funkcję:

  1. HAL_StatusTypeDef MPU6050_GetMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz);
  2. HAL_StatusTypeDef MPU6050_GetAcceleration(int16_t* x, int16_t* y, int16_t* z);
  3. int16_t MPU6050_GetAccelerationX(void);
  4. int16_t MPU6050_GetAccelerationY(void);
  5. int16_t MPU6050_GetAccelerationZ(void);
  6. int16_t MPU6050_GetTemperature(void);
  7. HAL_StatusTypeDef MPU6050_GetRotation(int16_t* x, int16_t* y, int16_t* z);
  8. int16_t MPU6050_GetRotationX(void);
  9. int16_t MPU6050_GetRotationY(void);
  10. int16_t MPU6050_GetRotationZ(void);

Dodatkowo z urządzenia można odczytać temperaturę. Jej pomiar jest bardziej wykorzystywany w celu kalibracji układu, niż faktycznego jego użycia do mierzenia temperatury otoczenia.

  1. int16_t MPU6050_GetRawTemperature(void);
  2. float MPU6050_ConvertTemperature(int16_t readedTempVal);