101 применение шлюза UART-to-I2C/SPI/1W (RH-0004). Часть 6. Подключение 3-осевого гироскопа/акселерометра MPU6050 к компьютеру

  1. Часть 1. USB программатор микросхем EPROM с интерфейсом I2C
  2. Часть 2. USB программатор микросхем EPROM с интерфейсом SPI
  3. Часть 3. USB-термометр на DS18B20 с передачей данных по сети
  4. Часть 4. USB программатор микросхем EPROM DS2430
  5. Часть 5. USB-программатор микросхем EPROM DS24B33
  6. Часть 6. Подключение 3-осевого гироскопа/акселерометра MPU6050 к компьютеру

Насмотрелся я в ютубе видосов про всякие гироскопы, ну и захотелось мне тоже с ними поиграться. Одной из самых популярных оказалась платка GY-521 с чипом MPU6050 (фотка справа). Дёшево и сердито. Что из этого в итоге получилось, я сейчас попробую рассказать.

Итак, чип MPU6050 включает в себя:

  • 3-осевой гироскоп c 16-битным АЦП
    настраиваемый диапазон: ±250, ±500, ±1000, ±2000 0/sec (dps)
  • 3-осевой акселерометр с 16-битным АЦП
    настраиваемый диапазон: ±2g, ±4g, ±8g, ±16g
  • температурный сенсор
  • digital motion processor (DMP)
  • буфер FIFO (1024 байта)

схема модуля GY-521 на базе чипа MPU6050

Приятные и полезные «плюшки»:

  • ток потребления в нормальном режиме всего 4 мА (а есть ещё режим пониженного потребления)
  • система прерываний (детектор движения с программируемым порогом срабатывания, переполнение буфера FIFO…)
  • выбор источника тактирования из нескольких вариантов, включая внешний источник и внутренний генератор 8 MHz
  • программируемый низкочастотный фильтр (DLPF)
  • программируемая частота сэмплирования (Sample Rate = Gyroscope Output Rate / (1+ SMPLRT_DIV), где Gyroscope Output Rate = 1 kHz при включенном DLPF и 8 kHz при выключенном)
  • возможность подключить внешний трехосевой магнитометр

Работа с чипом происходит через регистры, посредством интерфейсов SPI или I2C, в зависимости от модификации чипа. Мне достался чип с интерфейсом I2C (что, собственно, без разницы). Документ с картой регистров можно скачать в конце статьи, а схема показана на рисунке справа.

Как правило, модуль GY-521 используют совместно с Arduino, либо с чем-то подобным, имеющим на борту достаточно «толстый» контроллер. «Толстый» контроллер нужен ввиду того, что данные гироскопа и акселерометра (мгновенные скорости вращения и ускорения по трём осям) обычно нафиг никому не нужны. Всем интересно положение тела в пространстве, а для того, чтобы его получить — нужно данные гироскопа и акселерометра интегрировать.

Есть второй путь, — использовать встроенный Digital Motion Processor. Он может сам проводить интегрирование и отдавать нам уже положение тела в пространстве в виде кватерниона (это такая математическая сущность, которой очень удобно описывать всякие вращения, подробнее как-нибудь отдельно про неё напишу).

Я решил использовать DMP. Это позволяло обойтись без сложных вычислений и подключить модуль напрямую к компьютеру с помощью шлюза UART-to-I2C/SPI/1W (шлюз, как всегда, был дополнен преобразователем USB-to-UART, чтобы подключаться к компьютеру по USB).

Запитать плату также можно прямо от шлюза, — для этого надо установить перемычку выбора питания на шлюзе (JP1) в положение +5В. Контакты соединяем следующим образом:

  • SCL <-> Clock (тактирование I2C)
  • SDA <-> Data (данные I2C)
  • VCC <-> Supply (питание)
  • GND <-> COM (общий)

С DMP есть только одна проблема — на него совершенно нет документации. Её в принципе и на сам чип не много, но на DMP вообще нет. Перерыв кучу разных источников, я обнаружил, что все работающие варианты сходятся к одному источнику — библиотеке под AVR Дэвида Джирони (Davide Gironi) от 2012 года, который, в свою очередь, ссылается на библиотеку для ардуино от Джефа Роуберга (Jeff Rowberg).

Как этот DMP работает, какие фильтры использует, — никто толком не знает (кто-то конечно знает, но кто знает — не рассказывает). В DMP просто заливается в виде бинарников эн-цать блоков кода, в процессе чего необходимо сделать несколько включений, выключений, переключений (этот алгоритм я опишу ниже), и всё, — остаётся только читать данные из буфера FIFO.

Размер буфера, как уже отмечалось, 1024 байта. Есть двухбайтный регистр-счётчик, содержащий количество байт в буфере (FIFO_COUNTH, FIFO_COUNTL). Чтобы эти регистры корректно обновлялись нужно обязательно вычитывать оба регистра, причём первым обязательно FIFO_COUNTH. Кроме того, можно настроить прерывания по переполнению буфера и по поступлению в буфер очередной порции данных.

Данные от DMP имеют вид блоков по 42 байта следующей структуры:

структура пакета данных от DMP, встроенного в чип MPU6050

Как видите, размер буфера FIFO не кратен размеру пакета от DMP, то есть надо считывать данные быстрее, чем буфер успеет переполниться, ну или в случае переполнения придётся что-то придумывать для определения начала пакета от DMP. Я решил, что успею сделать это даже по USB, если выбрать частоту сэмплирования поменьше, скажем 200 Гц и ниже.

Итак, мне оставалось просто портировать нужные функции из C для AVR в С++ Builder, что и было в итоге сделано. Задача сильно облегчалась тем, что я, в своё время, написал специальную динамически подключаемую библиотеку (rh0004.dll) для работы со шлюзом и теперь для общения по I2C пользовался стандартными функциями из этой библиотеки. Функции я портировал следующие:

  • mpu6050_readBytes — прочитать указанное число байт, начиная с определённого адреса
  • mpu6050_readByte — прочитать один байт по указанному адресу
  • mpu6050_writeBytes — записать указанное число байт, начиная с определённого адреса
  • mpu6050_writeByte — записать один байт по указанному адресу
  • mpu6050_readBits — прочитать указанные биты из байта по определённому адресу
  • mpu6050_readBit — прочитать один бит из байта по определённому адресу
  • mpu6050_writeBits — записать указанные биты в байт по определённому адресу
  • mpu6050_writeBit — записать один бит в байт по определённому адресу
  • mpu6050_setMemoryBank — выбор банка памяти
  • mpu6050_setMemoryStartAddress — установка указателя адреса в выбранном банке памяти
  • mpu6050_readMemoryBlock — чтение блока памяти
  • mpu6050_writeMemoryBlock — запись блока памяти
  • mpu6050_writeDMPConfigurationSet — запись блока конфигурации DMP
  • mpu6050_getFIFOCount — чтение количества байт, находящихся в буфере FIFO
  • mpu6050_getFIFOBytes — чтение заданного количества байт из буфера FIFO
  • mpu6050_getIntStatus — чтение регистра INT_STATUS (статус прерываний)
  • mpu6050_resetFIFO — очистить буфер FIFO
  • mpu6050_getXGyroOffset
  • mpu6050_setXGyroOffset
  • mpu6050_getYGyroOffset
  • mpu6050_setYGyroOffset
  • mpu6050_getZGyroOffset
  • mpu6050_setZGyroOffset
  • mpu6050_setSleepDisabled — выйти из спящего режима
  • mpu6050_setSleepEnabled — перейти в спящий режим
  • mpu6050_testConnection — прочитать регистр MPU6050_RA_WHO_AM_I
  • mpu6050_init — инициализация акселерометра и гироскопа
  • mpu6050_dmpInitialize — инициализация DMP
  • mpu6050_dmpEnable — включение DMP
  • mpu6050_dmpDisable — отключение DMP

Как вы наверное заметили, часть функций (такие как get(set)X(Y,Z)GyroOffset) не описана вообще, а часть описана непонятно (скажем, про блоки и банки памяти). Это спасибо документации, в ней вообще отсутствуют описания регистров с нулевого по 12-й, а так же со 109-го по 113-й (судя по препарируемой библиотеке, они имеют отношение к начальной калибровке и DMP). Эту часть пришлось тупо скопировать, без какого-либо понимания почему, зачем и как.

Переходим к самому интересному. Алгоритм инициализации акселерометра и гироскопа (функция mpu6050_init) выглядит следующим образом:

  1. выходим из спящего режима, — функция mpu6050_setSleepDisabled() сбрасывает бит MPU6050_PWR1_SLEEP_BIT в регистре MPU6050_RA_PWR_MGMT_1
  2. выбираем источник тактирования, — я выбрал XGYRO (прописываем соответствующие биты в регистр MPU6050_RA_PWR_MGT_1)
  3. настраиваем низкочастотный фильтр (прописываем соответствующие биты в регистр MPU6050_RA_CONFIG), я настроил фильтр на 42 Hz
  4. настраиваем частоту сэмплирования (прописываем соответствующие биты в регистр MPU6050_RA_SMPLRT_DIV), я использовал частоту 200 Hz
  5. настраиваем диапазон шкалы гироскопа (прописываем соответствующие биты в регистр MPU6050_RA_GYRO_CONFIG)
  6. настраиваем диапазон шкалы акселерометра (прописываем соответствующие биты в регистр MPU6050_RA_ACCEL_CONFIG)

Собственно, после описанных выше манипуляций чип заработает и можно будет читать с него «живые» данные гироскопа и акселерометра по всем трём осям. Жаль, что эти данные без обработки, как мы уже говорили, никому не нужны. Для обработки придётся включить и настроить ещё и DMP. Алгоритм инициализации и настройки DMP несколько длиннее, поэтому здесь я его приводить не буду. Этот алгоритм можно посмотреть в логах демонстрационной программы.

Демонстрационная программа для компьютера, позволяющая вычитывать из чипа данные от DMP и визуализировать угловое положение чипа в пространстве, была написана на C++ Builder-е. Для визуализации использовалась библиотека OpenGL. Программу с исходниками, документацию на чип MPU6050 и ссылку на видео в youtube, демонстрирующее работу программы, можно найти ниже.

Программа MPU6050_Demo.zip
Исходники MPU6050_Demo_sources.zip
Документация на чип даташит
карта регистров
Демонстрационное видео MPU6050 Demo
  1. Часть 1. USB программатор микросхем EPROM с интерфейсом I2C
  2. Часть 2. USB программатор микросхем EPROM с интерфейсом SPI
  3. Часть 3. USB-термометр на DS18B20 с передачей данных по сети
  4. Часть 4. USB программатор микросхем EPROM DS2430
  5. Часть 5. USB-программатор микросхем EPROM DS24B33
  6. Часть 6. Подключение 3-осевого гироскопа/акселерометра MPU6050 к компьютеру

Добавить комментарий