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]:
- 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:
- static void MX_I2C1_Init(void)
- {
- /* USER CODE BEGIN I2C1_Init 0 */
- /* USER CODE END I2C1_Init 0 */
- /* USER CODE BEGIN I2C1_Init 1 */
- /* USER CODE END I2C1_Init 1 */
- hi2c1.Instance = I2C1;
- hi2c1.Init.ClockSpeed = 100000;
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
- hi2c1.Init.OwnAddress1 = 230;
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
- if (HAL_I2C_Init(&hi2c1) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN I2C1_Init 2 */
- /* USER CODE END I2C1_Init 2 */
- }
Rozpoczęcie pracy z układem można wykonać wykorzystując jedną z dwóch funkcji:
- uint8_t MPU6050_Init_Default(void) {
- HAL_StatusTypeDef opStatSelectClock = MPU6050_SelectClockSource(MPU6050_CLOCK_PLL_XGYRO);
- if(opStatSelectClock != HAL_OK) { return 1; }
- HAL_StatusTypeDef opStatSetFullScaleGyro = MPU6050_SetFullScaleGyroRange(MPU6050_GYRO_FS_250);
- if(opStatSetFullScaleGyro != HAL_OK) { return 2; }
- HAL_StatusTypeDef opStatSetFullScaleaccel = MPU6050_SetFullScaleAccelRange(MPU6050_ACCEL_FS_2);
- if(opStatSetFullScaleaccel != HAL_OK) { return 3; }
- HAL_StatusTypeDef opStatSetSleepEnabled = MPU6050_SetSleepEnabled(0);
- if(opStatSetSleepEnabled != HAL_OK) { return 4; }
- return 0;
- }
- uint8_t MPU6050_InitCustomConf(const enum mpu6050_ClockSource_e clockSource, const enum gyro_FullScale_e gyroRange, const enum accel_FullScale_e accelRange) {
- HAL_StatusTypeDef opStatSelectClock = MPU6050_SelectClockSource((uint8_t)clockSource);
- if(opStatSelectClock != HAL_OK) { return 1; }
- HAL_StatusTypeDef opStatSetFullScaleGyro = MPU6050_SetFullScaleGyroRange((uint8_t)gyroRange);
- if(opStatSetFullScaleGyro != HAL_OK) { return 2; }
- HAL_StatusTypeDef opStatSetFullScaleaccel = MPU6050_SetFullScaleAccelRange((uint8_t)accelRange);
- if(opStatSetFullScaleaccel != HAL_OK) { return 3; }
- HAL_StatusTypeDef opStatSetSleepEnabled = MPU6050_SetSleepEnabled(0);
- if(opStatSetSleepEnabled != HAL_OK) { return 4; }
- return 0;
- }
Weryfikacja podłączonego układu należy wykonać funkcją sprawdzającą id:
- uint8_t MPU6050_ReadAndCheckDeviceId(void)
- {
- uint8_t deviceId = MPU6050_ReadDeviceID();
- if((deviceId == 0x34) || (deviceId == 0xC)) {
- return 1;
- }
- return 0;
- }
Po podstawowej inicjalizacji układu można przejść do podstawowego odczytu wyników. Do tego celu służą następujące funkcję:
- HAL_StatusTypeDef MPU6050_GetMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz);
- HAL_StatusTypeDef MPU6050_GetAcceleration(int16_t* x, int16_t* y, int16_t* z);
- int16_t MPU6050_GetAccelerationX(void);
- int16_t MPU6050_GetAccelerationY(void);
- int16_t MPU6050_GetAccelerationZ(void);
- int16_t MPU6050_GetTemperature(void);
- HAL_StatusTypeDef MPU6050_GetRotation(int16_t* x, int16_t* y, int16_t* z);
- int16_t MPU6050_GetRotationX(void);
- int16_t MPU6050_GetRotationY(void);
- 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.
- int16_t MPU6050_GetRawTemperature(void);
- float MPU6050_ConvertTemperature(int16_t readedTempVal);