Getting started with MPU-9250, Arduino Guide – Latest Open Tech From Seeed

Getting started with MPU-9250, Arduino Guide - Latest Open Tech From Seeed Роботы

2подключение 7-сегментного светодиодногоиндикатора к arduino

В качестве индикатора для вывода показаний компаса будем использовать семисегментный индикатор 3361AS-1. Он построен по принципу индикатора с общим катодом.

Светодиодный индикатор с общим катодом – это тип индикатора, состоящий из нескольких светодиодов в одном корпусе, у которых общая земля, а питание на каждый светодиод подаётся отдельно.

Напомню, что 7-сегментным индикатор называется из-за того, что он состоит из 7-ми светодиодов, которые расположены в форме цифры “8”. Зажигая определённые сегменты, можно изображать разные цифры. Это похоже на цифры индекса на почтовом конверте: закрашивая определённые участки, мы пишем разные индексы.

Обозначение сегментов индикатора латинскими буквами
Обозначение сегментов индикатора латинскими буквами

Воспользуемся популярным способом управления 7-сегментным индикатором с помощью драйвера CD4511. Это микросхема двоично-десятичного преобразователя, который переводит двоичный код числа в напряжение на соответствующих цифре сегментах индикатора. Такой преобразователь использует всего 4 ножки Arduino.

Выводы двоично-десятичного преобразователя CD4511
Выводы двоично-десятичного преобразователя CD4511

Отечественными аналогами данного преобразователя являются микросхемы серий ИД1…ИД7.

При подключении двоичного декодера будем руководствоваться следующей таблицей:

Вывод CD4511НазначениеПримечание
A0…A3Входы двоичного преобразователяСоответствуют разрядам двоичного числа.
a…gВыходы на сегменты индикатораПодключаются через токоограничительные резисторы к соответствующим сегментам светодиодного индикатора.
Lamp Test#Тест индикатора (включает все сегменты)Подключим к питанию, не использовать его.
Blanking#Очистка индикатора (отключает все сегменты)Подключим к питанию, чтобы не использовать его.
Latch Enabled#Выход активенБудет подключен к земле, чтобы выход был всегда активен.
VDDПитание микросхемы и индикатораОт 3 до 15 В.
GNDЗемляОбщая у CD4511, Arduino, 7-сегментного индикатора.

Индикатор 3361AS не имеет токоограничительных резисторов, поэтому необходимо озаботиться этим самому, подключая индикатор. При напряжении питания 5 В сопротивление на каждый сегмент должно быть около 200 Ом.

Желательно также подключить керамический конденсатор ёмкостью примерно 1 мкФ между землёй и питанием микросхемы CD4511.

Нам нужно одновременно управлять тремя разрядами десятичного числа, используя только один преобразователь CD4511. Но чисто физически это невозможно. Однако можно добиться иллюзии постоянного свечения всех разрядов светодиодного индикатора. Для этого придётся быстро переключаться между разрядами, постоянно обновляя показание каждого разряда.

Для человеческого глаза такое переключение между разрядами будет незаметно, но если результат снять на видео, то можно увидеть, как мерцают разряды чисел при переключении между разрядами, и даже мерцание отдельных светодиодов.

Скетч для управления трёхразрядным 7-сегментным индикатором (разворачивается)
// Выводы Arduino для управления двоичным конвертером CD4511:
const byte bit0 = 11;
const byte bit1 = 10;
const byte bit2 = 9;
const byte bit3 = 8;

// Выводы Arduino для выбора десятичных разрядов индикатора 3361AS:
const byte B_0 = 5;
const byte B_1 = 6;
const byte B_2 = 7;

#define seconds() (millis()/1000) // макрос определения секунд, прошедших с начала работы скетча

void setup()
{
  pinMode(bit0, OUTPUT);
  pinMode(bit1, OUTPUT);
  pinMode(bit2, OUTPUT);
  pinMode(bit3, OUTPUT);
  
  pinMode(B_0, OUTPUT);
  pinMode(B_1, OUTPUT);
  pinMode(B_2, OUTPUT);

  digitalWrite(B_0, HIGH);
  digitalWrite(B_1, HIGH);
  digitalWrite(B_2, HIGH);
}

void loop()
{
  // Каждую секунду увеличиваем показания индикатора на 1:
  int sec = seconds();
  for (int i=0; i<1000; i  ) {
    while (sec == seconds()) {
      printNumber(i);
    }
    sec = seconds();
  }
}

// Выводит 3-разрядное число на 7-сегментный индикатор.
void printNumber(int n) {
  setDigit(B_0, n/100); // выводим сотни десятичного числа
  setDigit(B_1, n/10 ); // выводим десятки
  setDigit(B_2, n/1  ); // выводим единицы
}

// Выводит заданное число на заданный разряд индикатора.
void setDigit(byte digit, int value) {
  digitalWrite(digit, LOW); // выбираем разряд индикатора 3361AS-1
  setNumber(value); // выводим на этот разряд число
  delay(4);
  digitalWrite(digit, HIGH); // снимаем выбор разряда индикатора
}

// Выставляет двоичный код на входе преобразователя CD4511
void setNumber(int n) {
  static const struct number {
    byte b3;
    byte b2;
    byte b1;
    byte b0;
  }
  
  numbers[] = {
    {0, 0, 0, 0}, // 0 
    {0, 0, 0, 1}, // 1 
    {0, 0, 1, 0}, // 2 
    {0, 0, 1, 1}, // 3 
    {0, 1, 0, 0}, // 4 
    {0, 1, 0, 1}, // 5 
    {0, 1, 1, 0}, // 6 
    {0, 1, 1, 1}, // 7 
    {1, 0, 0, 0}, // 8 
    {1, 0, 0, 1}, // 9 
  };

  digitalWrite(bit0, numbers[n].b0);
  digitalWrite(bit1, numbers[n].b1);
  digitalWrite(bit2, numbers[n].b2);
  digitalWrite(bit3, numbers[n].b3);
}
Управление трёхразрядным семисегментным индикатором с помощью преобразователя CD4511 и Arduino
Управление трёхразрядным семисегментным индикатором с помощью преобразователя CD4511 и Arduino

Итак, теперь мы умеем выводить трёхзначные числа на 7-сегментный индикатор, что нам понадобится для отображения азимута.

3чтение показаний датчика тока acs712с помощью arduino

В скетче будем постоянно читать значение с порта A0 и выводить в монитор последовательных данных. Напомню, АЦП у разных плат Arduino имеет различную разрядность, обычно 10 или 12 бит. Подробнее здесь.

Это означает, что с аналогового порта могут приходить значения от 0 до 210 = 1024 для 10-разрядного АЦП. Будем считать, что у нас датчик тока, диапазон измерений которого от -5 А до 5 А, а чувствительность 185 мВ/А.

Если на 1 А приходится 185 мВ, это соответствует примерно 38 единицам измерения АЦП: 185·1024/5000 = 37.888, (1) где 5000 – это максимальное значение напряжения, которое способен измерить АЦП Arduino, в милливольтах.

На выходе OUT датчика ACS712 при отсутствии измеряемого тока должна быть половина напряжения питания, т.е. 2.5 В. Так как вся шкала АЦП лежит в диапазоне от 0 до 1024, то при отсутствии измеряемого тока мы должны считывать с аналогового порта Arduino число 512.

Это начало шкалы отсчёта. Обозначим его value_zero. Отклонение тока value_adc от нулевого уровня в большую или меньшую сторону и будет показывать силу тока. Следовательно, чтобы посчитать в амперах значение тока с датчика ACS712, необходимо разницу нулевого уровня и измеренного значения с аналогового порта A0 поделить на 38.

Пояснение принципа вычисления силы тока
Пояснение принципа вычисления силы тока

На практике значение на аналоговом выводе A0 не будет равняться точно 512. Поэтому, чтобы определить начало отсчёта, добавим в скетч примитивную калибровку. Калибровка будет заключаться в том, что некоторое количество раз прочитаем значение с аналогового порта A0 при отсутствии тока на датчике ACS712, и усредним его. Естественно, нагрузка на время калибровки должна быть выключена, чтобы ток не протекал через датчик.

Скетч для измерения постоянного тока датчиком ACS712 (разворачивается)
const int acs712_pin = A0;

int zero; // уровень нуля, относительно которого измеряется ток, обычно VCC/2

void setup() {
  Serial.begin(9600);
  calibrate();
}

// определим нуль шкалы (до включения нагрузки)
void calibrate(){
  zero = 0;
  int repeats = 10;
  for (int i=0; i<repeats; i  ){
    zero  = analogRead(acs712_pin);
    delay(100);
  }
  zero /= repeats; // берём среднее арифметическое
  Serial.print("Zero=");
  Serial.println(zero);
}

void loop() {
  int sensorValue = analogRead(acs712_pin); // читаем значение с АЦП и выводим в монитор
  Serial.print(sensorValue); 
  Serial.print(" = ");
  int c = getCurrent(sensorValue); // преобразуем в значение тока и выводим в монитор
  Serial.print(c); 
  Serial.println(" mA");
  delay(100);
}

// рассчитывает ток в мА по значению с АЦП
int getCurrent(int adc) {
  int delta = zero - adc; // отклонение от нуля шкалы
  float scale = 37.888; // сколько единиц АЦП приходится на 1 ампер, по формуле (1)
  int current = (int)delta*1000/scale; // считаем ток в мА и округляем до целых, по формуле (2)
  return current;
}

Загрузим скетч и плавно начнём поднимать напряжение и ток на нагрузке. Какое-то время подождём, а затем начнём уменьшать ток. В результате получим примерно такую картинку:

Вывод тока датчика ACS712 в монитор последовательного порта и его график
Вывод тока датчика ACS712 в монитор последовательного порта и его график

Как видно, аналоговый сигнал постоянно «прыгает». Чтобы этого избежать, следует добавить в скетч сглаживание. Для этого будем проводить подряд несколько измерений, а затем брать среднее арифметическое от них в качестве действительного значения. Заодно совместим начальную калибровку, т.к. она выполняется точно так же. Вот как изменится в результате скетч:

Скетч для измерения постоянного тока датчиком ACS712 со сглаживанием
const int acs712_pin = A0;

int zero; // уровень нуля, относительно которого измеряется ток, обычно VCC/2

void setup() {
  Serial.begin(9600);
  zero = getSmoothedValue(); // определим нуль шкалы (до включения нагрузки)
  Serial.print("Zero=");
  Serial.println(zero);
}

// получает сглаженное значение с АЦП Arduino
int getSmoothedValue(){
  int value;
  int repeats = 10;
  for (int i=0; i<repeats; i  ){ // измеряем значение несколько раз
    value  = analogRead(acs712_pin); // суммируем измеренные значения
    delay(1);
  }
  value /= repeats; // и берём среднее арифметическое
  return value;
}

void loop() {
  int sensorValue = getSmoothedValue(); // читаем значение с АЦП и выводим в монитор
  Serial.print(sensorValue); 
  Serial.print(" = ");
  int c = getCurrent(sensorValue); // преобразуем в значение тока и выводим в монитор
  Serial.print(c); 
  Serial.println(" mA");
  delay(100);
}

// рассчитывает ток в мА по значению с АЦП
int getCurrent(int adc) {
  int delta = zero - adc; // отклонение от нуля шкалы
  float scale = 37.888; // сколько единиц АЦП приходится на 1 ампер
  int current = (int)delta*1000/scale; // считаем ток в мА
  return current;
}

Кстати, желательно вынести чувствительность датичка ACS712 в константу в начале скетча, чтобы можно было быстро изменить скетч для модификации датчика с другим диапазоном измерений.

В результате выполнения данного скетча картинка получается гораздо более приятная:

Сглаженный график тока, измеренного датчиком ACS712
Сглаженный график тока, измеренного датчиком ACS712

Тот же самый принцип заложен в библиотеки для Arduino, которые оперируют с датчиком тока ACS712. Например, вот эта библиотека Troyka Current.

По результату эксперимента получается, что датчик ACS712 очень простой, но при этом довольно не точный. Гораздо точнее датчик тока, который мы рассмотрим в следующем разделе.

4управление 7-сегментным индикатором с помощью драйвера cd4511 и arduino

При подключении двоичного декодера будем руководствоваться следующей таблицей:

Смотрите про коптеры:  Обсуждение xiaomi mi 1c робот пылесос 4pda
Вывод CD4511НазначениеПримечание
A0…A3Входы двоичного преобразователяСоответствуют разрядам двоичного числа.
a…gВыходы на сегменты индикатораПодключаются через токоограничивающие резисторы к соответствующим сегментам светодиодного индикатора.
Lamp Test#Тест индикатора (включает все сегменты)Подключим к питанию, не использовать его.
Blanking#Очистка индикатора (отключает все сегменты)Подключим к питанию, чтобы не использовать его.
Latch Enabled#Выход активенБудет подключен к земле, чтобы выход был всегда активен.
VDDПитание микросхемы и индикатораОт 3 до 15 В.
GNDЗемляОбщая у CD4511, Arduino, 7-сегментного индикатора.

Желательно также подключить керамический конденсатор ёмкостью примерно 1 мкФ между землёй и питанием микросхемы CD4511.

Подключение 7-сегментного индикатора к Arduino с двоичным декодером CD4511B
Подключение 7-сегментного индикатора к Arduino с двоичным декодером CD4511B

Теперь напишем простой скетч, чтобы проверить работоспособность 7-сегментного индикатора 3361AS-1 в связке с двоично-десятичным декодером, а также получить опыт работы с ними. Данный скетч будет поочерёдно перебирать числа от 0 до 9, перемещаясь по циклу от одного разряда индикатора к следующему.

Скетч для управления 7-сегментным индикатором (светится 1 разряд) (разворачивается)
// выводы Arduino для управления двоичным кодом на входе декодера CD4511:
const byte D_0 = 11;
const byte D_1 = 10;
const byte D_2 = 9;
const byte D_3 = 8;

// выводы Arduino для выбора десятичного разряда индикатора:
const byte B_0 = 7;
const byte B_1 = 6;
const byte B_2 = 5;

void setup() {
  pinMode(D_0, OUTPUT);
  pinMode(D_1, OUTPUT);
  pinMode(D_2, OUTPUT);
  pinMode(D_3, OUTPUT);
  pinMode(B_0, OUTPUT);
  pinMode(B_1, OUTPUT);
  pinMode(B_2, OUTPUT);
}

void loop() {
  for (int i=0; i<3; i  ){ // перебираем разряды с 0 по 2-ой
    setDigit(i);
    for (int n=0; n<10; n  ){ // перебираем числа от 0 до 9
      printNumber(n);
      delay(200);
    }    
  }
}

// выбирает разряд десятичного числа на счётчике:
void setDigit(byte b){
  switch (b) {
    case 0:
      digitalWrite(B_0, LOW);
      digitalWrite(B_1, HIGH);
      digitalWrite(B_2, HIGH);
      break;
    case 1:
      digitalWrite(B_0, HIGH);
      digitalWrite(B_1, LOW);
      digitalWrite(B_2, HIGH);
      break;
    case 2:
      digitalWrite(B_0, HIGH);
      digitalWrite(B_1, HIGH);
      digitalWrite(B_2, LOW);
      break;
  }
}

// зажигает заданную цифру 7-сегментного индикатора
void printNumber(byte n){
  switch(n){
    case 0:
      digitalWrite(D_0, LOW);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, LOW);
      break;
    case 1:
      digitalWrite(D_0, HIGH);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, LOW);
      break;
    case 2:
      digitalWrite(D_0, LOW);
      digitalWrite(D_1, HIGH);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, LOW);
      break;
    case 3:
      digitalWrite(D_0, HIGH);
      digitalWrite(D_1, HIGH);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, LOW);
      break;
    case 4:
      digitalWrite(D_0, LOW);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, HIGH);
      digitalWrite(D_3, LOW);
      break;
    case 5:
      digitalWrite(D_0, HIGH);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, HIGH);
      digitalWrite(D_3, LOW);
      break;
    case 6:
      digitalWrite(D_0, LOW);
      digitalWrite(D_1, HIGH);
      digitalWrite(D_2, HIGH);
      digitalWrite(D_3, LOW);
      break;
    case 7:
      digitalWrite(D_0, HIGH);
      digitalWrite(D_1, HIGH);
      digitalWrite(D_2, HIGH);
      digitalWrite(D_3, LOW);
      break;
    case 8:
      digitalWrite(D_0, LOW);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, HIGH);
      break;
    case 9:
      digitalWrite(D_0, HIGH);
      digitalWrite(D_1, LOW);
      digitalWrite(D_2, LOW);
      digitalWrite(D_3, HIGH);
      break;
  }
}

Загрузим скетч в Arduino и посмотрим результат.

В один момент времени светится только один разряд индикатора. Как же задействовать одновременно сразу три разряда индикатора? Это будет немного сложнее. Сложность заключается в том, что нам одновременно нужно управлять тремя разрядами десятичного число, используя только один преобразователь CD4511.

Но чисто физически это невозможно. Однако можно добиться иллюзии постоянного свечения всех разрядов светодиодного индикатора. Для этого придётся быстро переключаться между разрядами, постоянно обновляя показание каждого разряда. Мы будем поочерёдно активировать каждый из разрядов индикатора 3361AS, выставлять на нём с помощью двоичного преобразователя CD4511 нужную цифру, а затем переключаться на следующий разряд.

Для человеческого глаза такое переключение между разрядами будет незаметно, но если результат снять на видео, то его можно увидеть.

Также перепишем функцию setNumber() отправки двоичного кода на вход микросхемы преобразователя CD4511. Вместо использования оператора switch, используем массив массивов.

Скетч для управления трёхразрядным 7-сегментным индикатором (разворачивается)
// Выводы Arduino для управления двоичным конвертером CD4511:
const byte bit0 = 11;
const byte bit1 = 10;
const byte bit2 = 9;
const byte bit3 = 8;

// Выводы Arduino для выбора десятичных разрядов индикатора 3361AS:
const byte B_0 = 5;
const byte B_1 = 6;
const byte B_2 = 7;

#define seconds() (millis()/1000) // макрос определения секунд, прошедших с начала работы скетча

void setup()
{
  pinMode(bit0, OUTPUT);
  pinMode(bit1, OUTPUT);
  pinMode(bit2, OUTPUT);
  pinMode(bit3, OUTPUT);
  
  pinMode(B_0, OUTPUT);
  pinMode(B_1, OUTPUT);
  pinMode(B_2, OUTPUT);

  digitalWrite(B_0, HIGH);
  digitalWrite(B_1, HIGH);
  digitalWrite(B_2, HIGH);
}

void loop()
{
  // Каждую секунду увеличиваем показания индикатора на 1:
  int sec = seconds();
  for (int i=0; i<1000; i  ) {
    while (sec == seconds()) {
      printNumber(i);
    }
    sec = seconds();
  }
}

// Выводит 3-разрядное число на 7-сегментный индикатор.
void printNumber(int n) {
  setDigit(B_0, n/100); // выводим сотни десятичного числа
  setDigit(B_1, n/10 ); // выводим десятки
  setDigit(B_2, n/1  ); // выводим единицы
}

// Выводит заданное число на заданный разряд индикатора.
void setDigit(byte digit, int value) {
  digitalWrite(digit, LOW); // выбираем разряд индикатора 3361AS-1
  setNumber(value); // выводим на этот разряд число
  delay(4);
  digitalWrite(digit, HIGH); // снимаем выбор разряда индикатора
}

// Выставляет двоичный код на входе преобразователя CD4511
void setNumber(int n) {
  static const struct number {
    byte b3;
    byte b2;
    byte b1;
    byte b0;
  }
  
  numbers[] = {
    {0, 0, 0, 0}, // 0 
    {0, 0, 0, 1}, // 1 
    {0, 0, 1, 0}, // 2 
    {0, 0, 1, 1}, // 3 
    {0, 1, 0, 0}, // 4 
    {0, 1, 0, 1}, // 5 
    {0, 1, 1, 0}, // 6 
    {0, 1, 1, 1}, // 7 
    {1, 0, 0, 0}, // 8 
    {1, 0, 0, 1}, // 9 
  };

  digitalWrite(bit0, numbers[n].b0);
  digitalWrite(bit1, numbers[n].b1);
  digitalWrite(bit2, numbers[n].b2);
  digitalWrite(bit3, numbers[n].b3);
}

Получится вот такая картина.

Управление трёхразрядным семисегментным индикатором с помощью преобразователя CD4511 и Arduino
Управление трёхразрядным семисегментным индикатором с помощью преобразователя CD4511 и Arduino

В динамике это выглядит так. Тут как раз временами видны мерцания сегментов светодиодного индикатора.

Можно попробовать поиграть значением задержек в функции setDigit(). Если сделать задержки меньше, то мерцание станет меньше заметно. Но начнут сильнее засвечиваться соседние сегменты на выбранном разряде индикатора. Тут необходимо выбрать какое-то компромиссное решение.

6как читать данные сдатчика тока и напряжения ina219

Если посмотреть на обмен данными по шине I2C, который происходит при работе данного скетча (с помощью логического анализатора, конечно), то увидим следующее.

Осциллограмма чтения регистров датчика INA219
Осциллограмма чтения регистров датчика INA219

Чтобы понять, что здесь происходит, необходимо познакомиться с картой регистров датчика INA219. Датчик содержит всего 6 регистров. Все регистры 16-разрядные.

Карта регистров датчика тока и напряжения INA219
Адрес регистраНазвание регистраНазначение регистраТип
0x00ConfigurationСброс всех регистров, настройка диапазона измерений, усиления PGA, разрешения АЦП и фильтрации.Чтение/Запись
0x01Shunt voltageХранит измеренное значение напряжения на шунтирующем резисторе 0,1 Ом.Чтение
0x02Bus voltageХранит измеренное значение напряжения шины.Чтение
0x03PowerХранит измеренное значение мощности.Чтение
0x04CurrentСодержит значение силы тока, протекающего через шунтирующий резистор.Чтение
0x05CalibrationКалибровочный регистр. Задаёт диапазон измерений и позволяет осуществлять калибровку системы.Чтение/Запись

Для обмена с модулем воспользуемся отладочной платой с микросхемой FT2232H и программой SPI via FTDI. Это будет проще, чем использовать Arduino, т.к. для внесения изменений в целях эксперимента не придётся каждый раз перепрограммировать ПЗУ, а можно будет вносить изменения в передаваемые команды «на лету».

Чтение регистров датчика тока INA219 с помощью FT2232H
Чтение регистров датчика тока INA219 с помощью FT2232H

Запустим программу SPI via FTDI, выберем в меню «Устройство» интерфейс I2C. Подключимся к порту A. Просканируем устройства на шине I2C. Программа найдёт устройство по адресу 64 (0x40), если конечно вы не меняли адрес перемычками A0 и A1.

Чтение регистров датчика тока INA219 с помощью FT2232H и программы "SPI via FTDI"
Чтение регистров датчика тока INA219 с помощью FT2232H и программы “SPI via FTDI”

Как вы уже наверное догадались, команда “0” означает адрес регистра, из которого мы хотим прочитать данные. А число 0x399F – это данные в нулевом регистре (регистр конфигурации). И это соответствует документации, т.к. после включения и загрузки микросхема INA219 имеет именно такую конфигурацию по умолчанию. Вот какую структуру имеет регистр конфигурации.

Структура конфигурационного регистра датчика тока INA219
Структура конфигурационного регистра датчика тока INA219

В регистре конфигурации датчика INA219 присутствуют следующие части:

  • RST (reset) – сброс;
  • BRNG (bus voltage range) – диапазон измерения шины;
  • BADC (bus ADC resolution/averaging) – разрешающая способность АЦП шины;
  • SADC (shunt ADC resolution/averaging) – разрешающая способность АЦП шунта;
  • MODE – режим;
  • PG – усиление и диапазон PGA.

0x399F в двоичном виде это “001_11_0011_0011_111”. Следовательно, значения по умолчанию после включения такие.

  • BRNG равен “1”, что означает диапазон измерений 32 вольта FSR;
  • PG равно “11”: задаёт диапазон ±320 мВ и коэффициент усиления 8;
  • BADC, SADC равны “0011”: максимальная разрешающая способность АЦП – 12 бит;
  • MODE, равное “111”, означает непрерывный режим работы, включены и шунт, и шина.

Для чтения других регистров необходимо сначала так же записать их адрес в поле «Чтение» «Команда», а затем прочитать 2 байта. Или можно записать номер регистра в поле «Запись» «Команда», а затем просто читать (не указывая адрес регистра в команде чтения).

К сожалению, последовательного чтения всех регистров микросхемы INA219 «за один проход» не предусмотрено.

Вернёмся к нашей осциллограмме. Мы видим на ней 6 циклов чтения (каждый начинается с зелёной точки ● и заканчивается тёмно-красной ●). Сначала читаем регистр с напряжением шунта Vшунт. (адрес 0x01), который хранит значение 0x1957.

Далее читаем значение регистра напряжения шины Vшины (0x02), в котором значение 0x19BA. Далее читаем регистр калибровки Cal (0x05) со значением 0x1000. Потом регистр тока шунта Iшунт. (0x04), в котором значение 0x1959.

Вывод монитора порта в момент снятия осциллограммы с датчика INA219
Вывод монитора порта в момент снятия осциллограммы с датчика INA219

Рассмотрим, как привести данные в регистрах «в человеческий вид». Нам интересны не все значения, а только напряжения и ток. Плюс регистр калибровки, который играет роль поправочного коэффициента.

7подключение трёхканального датчика тока и напряжения ina3221 к arduino

Датчик тока INA3221 практически идентичен датчику INA219. Основное отличие состоит в том, что он имеет 3 измерительных канала вместо одного. Показания с них можно снимать независимо друг от друга. Будем использовать вот такую небольшую плату с датчиком:

Смотрите про коптеры:  Как почистить датчик затяжки вейпа
Плата с датчиком INA3221
Плата с датчиком INA3221

Подключается данный модуль к Arduino всего 4-мя проводами: два для питания, и ещё два – шина I2C.

Вывод модуля INA3221Вывод ArduinoНазначение
SDAA4Данные шины I2C
SCLA5Импульсы синхронизации шины I2C
VS 3.3VПитание
GNDGNDОбщий
Подключение датчика INA3221 к Arduino Nano
Подключение датчика INA3221 к Arduino Nano

Назначение остальных выводов модуля показано на приведённом рисунке и в таблице ниже.

Вывод модуля INA3221Назначение
TCЦифровой выход оповещения о сбое таймингов (timing control alert).
WARЦифровой выход оповещения о сбоях измерений (warning).
CRIЦифровой выход оповещения о критических сбоях (critical).
PVЦифровой выход оповещения о валидности питающего напряжения (power valid).
VPUАналоговый вход подтягивающего напряжения для смещения выходных цепей определения валидности питания.
POWАналоговый вход питания измеряемой нагрузки.
CH1, CH2, CH3Порты для подключения измеряемых цепей.

Используем библиотеку для работы с датчиком INA3221. Поместим файлы с расширениями *.cpp и *.h в одну директорию, в ней же создадим файл с расширением *.ino и следующим содержимым:

Скетч для чтения показаний датчика INA3221
#include "Wire.h"
#include "SDL_Arduino_INA3221.h"

SDL_Arduino_INA3221 ina3221; // создаём экземпляр класса датчика

// Три канала измерения датчика INA3221
#define CHANNEL_1 1
#define CHANNEL_2 2
#define CHANNEL_3 3

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Arduino INA3221 test");
  ina3221.begin();

  Serial.print("ID=0x");
  int id = ina3221.getManufID();
  Serial.println(id, HEX);

  Serial.println("Measuring voltage and current with ina3221 ...");
}

void loop(void)
{
  Serial.println("---------------------------------------------");
  Serial.println("Channel:tt(1)t(2)t(3)t"); // "t" - это символ табуляции

  // Вывод напряжений по трём каналам:
  Serial.print("Bus voltage, V: t");
  float busvoltage1 = ina3221.getBusVoltage_V(CHANNEL_1);
  float busvoltage2 = ina3221.getBusVoltage_V(CHANNEL_2);
  float busvoltage3 = ina3221.getBusVoltage_V(CHANNEL_3);
  Serial.print(busvoltage1); Serial.print("t");
  Serial.print(busvoltage2); Serial.print("t");
  Serial.print(busvoltage3); Serial.println("t");

  // Вывод напряжений на шунте по трём каналам:
  Serial.print("Shunt voltage, mV: t");
  float shuntvoltage1 = ina3221.getShuntVoltage_mV(CHANNEL_1);
  float shuntvoltage2 = ina3221.getShuntVoltage_mV(CHANNEL_2);
  float shuntvoltage3 = ina3221.getShuntVoltage_mV(CHANNEL_3);
  Serial.print(shuntvoltage1); Serial.print("t");
  Serial.print(shuntvoltage2); Serial.print("t");
  Serial.print(shuntvoltage3); Serial.println("t");

  // Вывод напряжений нагрузки по трём каналам:
  Serial.print("Load voltage, V: t");
  float loadvoltage1 = busvoltage1   (shuntvoltage1 / 1000);
  float loadvoltage2 = busvoltage2   (shuntvoltage2 / 1000);
  float loadvoltage3 = busvoltage3   (shuntvoltage3 / 1000);
  Serial.print(loadvoltage1); Serial.print("t");
  Serial.print(loadvoltage2); Serial.print("t");
  Serial.print(loadvoltage3); Serial.println("t");

  // Вывод тока по трём каналам:
  Serial.print("Current, mA: tt");
  float current_mA1 = ina3221.getCurrent_mA(CHANNEL_1);
  float current_mA2 = ina3221.getCurrent_mA(CHANNEL_2);
  float current_mA3 = ina3221.getCurrent_mA(CHANNEL_3);
  Serial.print(current_mA1); Serial.print("t");
  Serial.print(current_mA2); Serial.print("t");
  Serial.print(current_mA3); Serial.println("t");

  delay(2000);
}

Загрузим данный скетч в память Arduino. Перед тем как подключать нагрузку, необходимо подать с источника питания напряжение на контакты POW и GND, расположенные с одного из краёв модуля. Это напряжение будет подаваться на нагрузку и оно в данном модуле общее для всех трёх измерительных каналов. Допустимый диапазон напряжений от 0 до 26 вольт. Я сейчас подам 5 В.

Удобно в места подключения нагрузки и питания впаять клеммники для быстрого монтажа.

Теперь можно подключать нагрузку. Давайте нагрузим выходы модуля и посмотрим, что будет выводиться в монитор последовательного порта. Я подключу на канал 1 два параллельных резистора номиналом 4,3 кОм, что в сумме даст сопротивление 2,15 кОм. А на канал 3 – один резистор 4,3 кОм.

Датчик тока INA3221 с нагрузкой
Датчик тока INA3221 с нагрузкой

В мониторе последовательного порта видно, как меняются показания датчика INA3221 при изменении нагрузки. На иллюстрации для примера показаны три состояния: показания датчика без нагрузки, с нагрузкой на одном канале и с разной нагрузкой на двух каналах.

Показания датчика тока INA3221 в мониторе COM-порта
Показания датчика тока INA3221 в мониторе COM-порта

Если мы подключим в измеряемую цепь амперметр, то убедимся, что показания цифрового датчика INA3221 довольно точно совпадают с показаниями амперметра.

Показания датчика тока INA3221 в сравнении с амперметром
Показания датчика тока INA3221 в сравнении с амперметром

Github – jjcordova/mpu-9250: arduino sketch for mpu-9250 9dof with ahrs sensor fusion

Arduino sketch for MPU-9250 9 DoF sensor with AHRS sensor fusion

Most modern and correct version is: MPU9250_MS5637_AHRS_t3.ino, all require quaternionFilters.ino in the IDE folder also to use the Madgwick and/or Mahony sensor fusion algorithms.

Demonstrate MPU-9250 basic functionality including parameterizing the register addresses, initializing the sensor,
getting properly scaled accelerometer, gyroscope, and magnetometer data out, calibration and self-test of sensors.
Added display functions to allow display to on-breadboard monitor. Addition of 9 DoF sensor fusion using open source Madgwick and Mahony filter algorithms. Sketch runs on the 3.3 V 8 MHz Pro Mini and the Teensy 3.1.

A discussion of the use and limitations of this sensor and sensor fusion in general is found ![here.]
(https://radiocopter.ru/kriswiner/MPU-6050/wiki/Affordable-9-DoF-Sensor-Fusion)

I have also added a program to allow sensor fusion using the MPU-9250 9-axis motion sensor with the STM32F401 Nucleo board using the mbed compiler. The STM32F401 achieves a sensor fusion filter update rate using the Madgwick MARG fusion filter of 4800 Hz running the M4 Cortex ARM processor at 84 MHz; compare to the sensor fusion update rate of 2120 Hz achieved using the same filter with the Teensy 3.1 running its M4 Cortex ARM processor at 96 MHz.

One reason for this difference is the single-precision floating point engine embedded in the STM32F401 core. While both ARM processors achieve impressive rates of filtering, really more than necessary for most applications, the factor of two difference translates into much lower power consumption for the same sensor fusion performance. If adequate sensor fusion filtering, say, 1000 Hz, can be achieved at much lower processor clock speed, then over all power consumption will be reduced. This really matters for wearable and other portable motion sensing and control applications.

I added a version of the basic sketch that uses the i2c_t3.h ‘Wire’ library specifically designed for Teensy 3.1. It allows easy access to Teensy-specific capabilities such as specification of which set of hardware i2c pins will be used, the bus speed (up to 1 MHz!) and also allows master and/or slave designation to handle multiplexing between i2c devices. See www.pjrc.com/teensy and http://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3 for details.

I added another version of the sketch intended specifically for the MPU9250_MS5637 Mini Add-On shield for the Teensy 3.1.

Getting started with MPU-9250, Arduino Guide - Latest Open Tech From SeeedGetting started with MPU-9250, Arduino Guide - Latest Open Tech From SeeedGetting started with MPU-9250, Arduino Guide - Latest Open Tech From Seeed

MPU9250 MS5637 Micro (left) and Mini (right) add-on shields, which solder onto the bottom pads 23-34 or pins 8 -17 on the Teensy 3.1, respectively.

It can be simply modified to work with the corresponding micro shield as well. It uses SDA/SCL on pins 17/16, respectively, and it uses the Teensy 3.1-specific Wire library i2c_t3.h. The MS5637 is a simple but high resolution pressure sensor, which can be used in its high resolution mode with power consumption of 20 microAmp, or in a lower resolution mode with power consumption of only 1 microAmp. The choice will depend on the application. The sketch calculates and outputs temperature in degrees Centigrade, pressure in millibar, and altitude in feet. In high resolution mode, the pressure is accurate to within 10 Pa or 0.1 millibar, and the height discrimination is about 13 cm. This is much better performance than achievable from the venerable MPL3115A2 and the MS5637 is in a very small package perfect for the small micro and mini add-on Teensy 3.1 shields.

For a discussion of the relative merits of modern board-mounted pressure sensors, see here.

I added sketches for the various new Mini add-on shields for Teensy 3.1 with the MPU9250 9-axis motion sensor and either the MPL3115A2 or the newer LPS25H pressure sensor/altimeter. Now there are three flavors of 10 DoF Mini add-on boards specially designed for the Teensy 3.1 with state-of-the-art 20-bit (MPL3115A2) and 24-bit (MS5637 and LPS25H) altimeters. The LPS25H has a 32-byte FIFO and sophisticated hardware filtering which allows very low power operation while maintaining 0.01 millibar resolution. This is really quite a feat; this sensor deserves serious consideration for any airborne application you might have in mind.

Methods

Mpu9250() Default constructor, requires calling the Config method to setup the I2C or SPI bus and I2C address or SPI chip select pin.

Mpu9250(i2c_t3 *bus, I2cAddr addr) Creates a Mpu9250 object. This constructor is used for the I2C communication interface. A pointer to the I2C bus object is passed along with the I2C address of the sensor. The address will be I2C_ADDR_PRIM (0x68) if the AD0 pin is grounded and I2C_ADDR_SEC (0x69) if the AD0 pin is pulled high.

Mpu9250(SPIClass *bus, uint8_t cs) Creates a Mpu9250 object. This constructor is used for the SPI communication interface. A pointer to the SPI bus object is passed along with the chip select pin of the sensor. Any pin capable of digital I/O can be used as a chip select pin.

void Config(TwoWire *bus, const I2cAddr addr) This is required when using the default constructor and sets up the I2C bus and I2C address. The address will be I2C_ADDR_PRIM (0x68) if the AD0 pin is grounded and I2C_ADDR_SEC (0x69) if the AD0 pin is pulled high.

void Config(SPIClass *spi, const uint8_t cs) This is required when using the default constructor and sets up the SPI bus and chip select pin.

Смотрите про коптеры:  Neatsvor x600 прошивка

bool Begin() Initializes communication with the sensor and configures the default sensor ranges, sampling rates, and low pass filter settings. The default accelerometer range is /- 16g and the default gyro range is /- 2,000 deg/s. The default sampling rate is 1000 Hz and the low-pass filter is set to a cutoff frequency of 184 Hz.

True is returned if communication is able to be established with the sensor and configuration completes successfully, otherwise, false is returned. The communication bus is not initialized within this library and must be initialized seperately; this enhances compatibility with other sensors that may on the same bus.

bool EnableDrdyInt() Enables the data ready interrupt. A 50 us interrupt will be triggered on the MPU-9250 INT pin when IMU data is ready. This interrupt is active high. This method returns true if the interrupt is successfully enabled, otherwise, false is returned.

bool DisableDrdyInt() Disables the data ready interrupt. This method returns true if the interrupt is successfully disabled, otherwise, false is returned.

bool ConfigAccelRange(const AccelRange range) Sets the accelerometer full scale range. Options are:

RangeEnum Value
/- 2gACCEL_RANGE_2G
/- 4gACCEL_RANGE_4G
/- 8gACCEL_RANGE_8G
/- 16gACCEL_RANGE_16G

True is returned on succesfully setting the accelerometer range, otherwise, false is returned. The default range is /-16g.

AccelRange accel_range() Returns the current accelerometer range.

AccelRange range = mpu9250.accel_range();

bool ConfigGyroRange(const GyroRange range) Sets the gyro full scale range. Options are:

RangeEnum Value
/- 250 deg/sGYRO_RANGE_250DPS
/- 500 deg/sGYRO_RANGE_500DPS
/- 1000 deg/sGYRO_RANGE_1000DPS
/- 2000 deg/sGYRO_RANGE_2000DPS

True is returned on succesfully setting the gyro range, otherwise, false is returned. The default range is /-2000 deg/s.

GyroRange gyro_range() Returns the current gyro range.

GyroRange range = mpu9250.gyro_range();

bool ConfigSrd(const uint8_t srd) Sets the sensor sample rate divider. The MPU-9250 samples the accelerometer and gyro at a rate, in Hz, defined by:

A srd setting of 0 means the MPU-9250 samples the accelerometer and gyro at 1000 Hz. A srd setting of 4 would set the sampling at 200 Hz. The IMU data ready interrupt is tied to the rate defined by the sample rate divider. The magnetometer is sampled at 100 Hz for sample rate divider values corresponding to 100 Hz or greater. Otherwise, the magnetometer is sampled at 8 Hz.

True is returned on succesfully setting the sample rate divider, otherwise, false is returned. The default sample rate divider value is 0, resulting in a 1000 Hz sample rate.

uint8_t srd() Returns the current sample rate divider value.

bool ConfigDlpfBandwidth(const DlpfBandwidth dlpf) Sets the cutoff frequency of the digital low pass filter for the accelerometer, gyro, and temperature sensor. Available bandwidths are:

DLPF BandwidthEnum Value
184 HzDLPF_BANDWIDTH_184HZ
92 HzDLPF_BANDWIDTH_92HZ
41 HzDLPF_BANDWIDTH_41HZ
20 HzDLPF_BANDWIDTH_20HZ
10 HzDLPF_BANDWIDTH_10HZ
5 HzDLPF_BANDWIDTH_5HZ

True is returned on succesfully setting the digital low pass filters, otherwise, false is returned. The default bandwidth is 184 Hz.

DlpfBandwidth dlpf_bandwidth() Returns the current digital low pass filter bandwidth setting.

DlpfBandwidth dlpf = mpu9250.dlpf_bandwidth();

bool EnableWom(int16_t threshold_mg, const WomRate wom_rate) Enables the Wake-On-Motion interrupt. It places the MPU-9250 into a low power state, waking up at an interval determined by the WomRate. If the accelerometer detects motion in excess of the threshold, threshold_mg, it generates a 50us pulse from the MPU-9250 interrupt pin. The following enumerated WOM rates are supported:

WOM Sample RateEnum Value
0.24 HzWOM_RATE_0_24HZ
0.49 HzWOM_RATE_0_49HZ
0.98 HzWOM_RATE_0_98HZ
1.95 HzWOM_RATE_1_95HZ
3.91 HzWOM_RATE_3_91HZ
7.81 HzWOM_RATE_7_81HZ
15.63 HzWOM_RATE_15_63HZ
31.25 HzWOM_RATE_31_25HZ
62.50 HzWOM_RATE_62_50HZ
125 HzWOM_RATE_125HZ
250 HzWOM_RATE_250HZ
500 HzWOM_RATE_500HZ

The motion threshold is given as a value between 4 and 1020 mg, which is internally mapped to a single byte, 1-255 value. This function returns true on successfully enabling Wake On Motion, otherwise returns false. Please see the wom_i2c example. The following is an example of enabling the wake on motion with a 40 mg threshold and a ODR of 31.25 Hz.

bool EnableFifo() Enables the MPU-9250 512 byte FIFO buffer and configures the buffer to store accelerometer and gyro data. This 512 byte buffer samples data at the data output rate set by the SRD and enables the microcontroller to bulk read the data, reducing microcontroller workload for certain applications. True is returned on successfully enabling the FIFO, otherwise false is returned.

bool DisableFifo() Disables the FIFO buffer. True is returned on successfully disabling the buffer, otherwise false is returned.

static constexpr int8_t FIFO_MAX_SIZE() This constant defines the maximum number of frames that the FIFO buffer will store. If you plan on using FIFO data and would like to read the data into an array, this define helps set the array size.

void Reset() Resets the MPU-9250.

bool Read() Reads data from the MPU-9250 and stores the data in the Mpu9250 object. Returns true if data is successfully read, otherwise, returns false.

bool new_imu_data() Returns true if new data was returned from the accelerometer and gyro.

bool new_mag_data() Returns true if new data was returned from the magnetometer. For MPU-9250 sample rates of 100 Hz and higher, the magnetometer is sampled at 100 Hz. For MPU-9250 sample rates less than 100 Hz, the magnetometer is sampled at 8 Hz, so it is not uncommon to receive new IMU data, but not new magnetometer data.

float accel_x_mps2() Returns the x accelerometer data from the Mpu9250 object in units of m/s/s. Similar methods exist for the y and z axis data.

float gyro_x_radps() Returns the x gyro data from the Mpu9250 object in units of rad/s. Similar methods exist for the y and z axis data.

float mag_x_ut() Returns the x magnetometer data from the Mpu9250 object in units of uT. Similar methods exist for the y and z axis data.

Eigen::Vector3f accel_mps2() Returns the accelerometer data from the Mpu9250 object as an Eigen::Vector3 in units of m/s/s.

Eigen::Vector3f gyro_radps() Returns the gyro data from the Mpu9250 object as an Eigen::Vector3 in units of rad/s.

Eigen::Vector3f mag_ut() Returns the magnetometer data from the Mpu9250 object as an Eigen::Vector3 in units of uT.

float die_temp_c() Returns the die temperature of the sensor in units of C.

int8_t ReadFifo() Reads data from the MPU-920 FIFO buffer and stores the data in the Mpu9250 object. On success, returns the number of FIFO frames collected from the buffer. One frame of data is considered as one set of 3-axis accelerometer data and 3-axis gyro data.

If you’re using the IMU data ready interrupt to trigger the FIFO buffer, as in the fifo_spi example, note that the current frame of data will not be in the buffer, it will be the first frame in the next buffer. If you need the current frame of data, you can use the Read method to get the current data and the ReadFifo method to get the FIFO data.

int8_t fifo_accel_x_mps2(float * data, const std::size_t len) Copies the x-axis accelerometer data from the last ReadFifo to the array pointed to by the data pointer. The array length given by len is used to ensure that the MPU-9250 object doesn’t overflow the data array.

The number of samples copied is returned on success or -1 on failure. Similar methods are available for the y-axis and z-axis accelerometer. Note that this is not a circular buffer, so multiple partial reads won’t work, it simply copys the data up to either the number of frames of data available or the array length, whichever is smaller.

int8_t fifo_gyro_x_radps(float * data, const std::size_t len) Copies the x-axis gyro data from the last ReadFifo to the array pointed to by the data pointer. The array length given by len is used to ensure that the MPU-9250 object doesn’t overflow the data array.

The number of samples copied is returned on success or -1 on failure. Similar methods are available for the y-axis and z-axis gyro. Note that this is not a circular buffer, so multiple partial reads won’t work, it simply copys the data up to either the number of frames of data available or the array length, whichever is smaller.

bool fifo_overflow() Returns whether the FIFO buffer has overflowed. Note that this requires using the Read method to update the overflow status.

Оцените статью
Радиокоптер.ру
Добавить комментарий