- 2описание оптопары на микросхеме tlp281-4
- 1подключение датчика mpu-9250к arduino
- 2подключение 7-сегментного индикатора непосредственно к arduino
- 2подключение 7-сегментного светодиодногоиндикатора к arduino
- 3чтение показаний датчика тока acs712с помощью arduino
- 4собираем всё вместе:компас на arduino
- 4управление 7-сегментным индикатором с помощью драйвера cd4511 и arduino
- 5подключение датчика тока и напряжения ina219 к arduino
- 6как читать данные сдатчика тока и напряжения ina219
- 7подключение трёхканального датчика тока и напряжения ina3221 к arduino
- List of settings
- Mpu 9250 и arduino
2описание оптопары на микросхеме tlp281-4
Рассмотрим работу оптопары на примере микросхемы TLP281, а точнее её разновидности TLP281-4. Микросхема TLP281-4 имеет 4 канала. То есть у неё есть 4 управляющих ножки и 4 выходных ножки, к которым подключается полезная нагрузка.
Будем использовать для работы модуль HW-399. Выглядит он так, как показано на иллюстрации ниже. Рядом приведена его схема.

Здесь выводы IN1…IN4 – это управляющие входные сигналы от микроконтроллера, например, Arduino, или другого управляющего элемента. На них можно подавать напряжение от 3,3 до 5 вольт. Выводы OUT1…OUT4 – выходы. Ножки HVCC и HGND – питание и земля управляемой части электрической схемы. На ножку питания HVCC можно подавать напряжение до 24 вольт.
Выводы IN1…IN4 соответствуют анодам светодиодов модуля, которые и являются источниками светового сигнала для фотокатодов модуля, которые являются электронными ключами OUT1…OUT4.
Для демонстрации работы оптопары давайте соберём схему, показанную на следующем рисунке. Здесь управлять будем одним каналом IN1 модуля HW-399 с помощью Arduino. К выходу OUT1 модуля подключим светодиод, питание на который будем подавать с отдельного источника питания (хотя можно и с самого Arduino, в данном случае это не принципиально). Подключать светодиод необходимо через токоограничивающий резистор, разумеется.

Как только мы соберём схему и подадим питание на внешнюю цепь (ножка HVCC), светодиод загорится. Это из-за того, что на управляющий пин IN1 ещё не подан управляющий сигнал. При отсутствии напряжения логической единицы на входе IN1 (допустим, он просто «висит» в воздухе или подключён к земле)

Давайте загрузим в Arduino стандартный скетч из примеров – Blink. Этот скетч каждую секунду меняет логический уровень на 13-ой ножке Arduino. Таким образом, мы наглядно увидим, как работает управление оптопарой.

Когда на 13-ом выводе Arduino высокий логический уровень – загорается встроенный светодиод платы Arduino, и отправляется управляющий сигнал на вход IN1 модуля. На выходе OUT1 появляется высокий уровень, и светодиод, подключённый к модулю, гаснет, т.к. нулевая разность потенциалов, и ток не может протекать через светодиод.
Когда на 13-ой ножке Arduino низкий уровень, то встроенный светодиод гаснет, и управляющий сигнал переключается также в низкий уровень. Из-за этого между выходом OUT1 и питанием HVCC модуля возникает разность потенциалов, и подключённый к микросхеме TLP281 светодиод загорается. Таким образом эти два светодиода будут загораться как бы в противофазе.

На приведённой осциллограмме голубой график – управляющий сигнал с пина 13 платы Arduino. А фиолетовый график – напряжение на светодиоде на 1-ом выходе модуля HW-399.
1подключение датчика mpu-9250к arduino
Датчик MPU-9250 – это по сути несколько датчиков, расположенных на одном чипе. Так, он реализует функции 3-осевого гироскопа, 3-осевого акселерометра и 3-осевого магнитометра. В данном проекте мы будем использовать только магнитометр. Остальные датчики можно отключить в целях уменьшения потребляемого тока, т.к. будем делать портативное устройство, питающееся от батареи «Крона».

Модуль имеет 10 выводов. Вот их назначение:
Вывод | Назначение вывода модуля с MPU-9250 (MPU-9255) |
---|---|
VCC | Внешнее питание 3.3 В. |
GND | Общий. |
SCL | Линия тактовых импульсов I2C и SPI. |
SDA | Линия данных для I2C или SPI. |
EDA | Линия данных при подключении внешних датчиков по шине I2C. |
ECL | Линия тактов при подключении внешних датчиков по шине I2C. |
AD0 | Для выставления адреса I2C в режиме I2C. В режиме SPI это линия данных от датчика. |
INT | Линия прерываний. Срабатывание настраивается при конфигурировании датчика MPU-9250. |
NCS | В режиме SPI – линия выбора ведомого (chip select). В режиме I2C не соединяется ни с чем. |
FSYNC | Зависит от конфигурации. |
Прежде чем подключать датчик MPU-9250 к Arduino, проверим его работоспособность с помощью моей любимой платы с микросхемой FT2232H. Для самого простого теста прочитаем содержимое регистра датчика, в котором содержится постоянное значение.
Таким регистром может служить, например, регистр, в котором хранится идентификатор магнитометра, равный 0x48. Подключаться будем по интерфейсу I2C (для сокращения числа проводников). Как и большинство датчиков, MPU-9250 является ведомым на I2C шине.

2подключение 7-сегментного индикатора непосредственно к arduino
Мы можем подключить индикатор прямо к выводам Arduino. Для этого придётся задействовать сразу 7 ножек (или 8, если нужна десятичная точка). Обратим внимание, что индикатор 3361AS не имеет токоограничивающих резисторов. Необходимо обеспечить наличие сопротивления номиналом около 180…220 Ом на каждый вывод индикатора (т.к. питание подаём 5 В от Arduino).

Расположение выводов индикатора показано на иллюстрации:

Подключать индикатор будем в соответствии с таблицей. Будет выбран первый разряд, остальные два пока не будем трогать.
Вывод индикатора 3361AS | Назначение | Вывод Arduino |
---|---|---|
1 | Сегмент E | D6 |
2 | Сегмент D | D5 |
3 | DP | D9 |
4 | Сегмент C | D4 |
5 | Сегмент G | D8 |
7 | Сегмент B | D3 |
8 | Выбор 3-го разряда | 5V |
9 | Выбор 2-го разряда | 5V |
10 | Сегмент F | D7 |
11 | Сегмент A | D2 |
12 | Выбор 1-го разряда | GND |
Напишем скетч, который последовательно выводит числа от 0 до 9 на первом разряде индикатора.
Скетч управления индикатором 3361AS (разворачивается)
const int A = 2; const int B = 3; const int C = 4; const int D = 5; const int E = 6; const int F = 7; const int G = 8; const int DP = 9; void setup() { pinMode(A, OUTPUT); pinMode(B, OUTPUT); pinMode(C, OUTPUT); pinMode(D, OUTPUT); pinMode(E, OUTPUT); pinMode(F, OUTPUT); pinMode(G, OUTPUT); pinMode(DP, OUTPUT); } void loop() { for (int i=0; i<=9; i ){ printNumber(i); delay(1000); } } // зажигает на 7-сегментном индикаторе заданную цифру void printNumber(int num){ int numbers[10][8] = { // многомерный массив, в котором описаны состояния сегментов A…G и DP для цифр от 0 до 9 {1,1,1,1,1,1,0,0}, // 0 {0,1,1,0,0,0,0,0}, // 1 {1,1,0,1,1,0,1,0}, // 2 {1,1,1,1,0,0,1,0}, // 3 {0,1,1,0,0,1,1,0}, // 4 {1,0,1,1,0,1,1,0}, // 5 {1,0,1,1,1,1,1,0}, // 6 {1,1,1,0,0,0,0,0}, // 7 {1,1,1,1,1,1,1,0}, // 8 {1,1,1,1,0,1,1,0} // 9 }; lightSegments(numbers[num]); } // зажигает заданные сегменты void lightSegments(int segments[]){ digitalWrite(A, segments[0]); digitalWrite(B, segments[1]); digitalWrite(C, segments[2]); digitalWrite(D, segments[3]); digitalWrite(E, segments[4]); digitalWrite(F, segments[5]); digitalWrite(G, segments[6]); digitalWrite(DP, segments[7]); }
Небольшое пояснение по поводу массива numbers[] в функции printNumber(). Этот массив состоит из 10-ти подмассивов, каждый из которых определяет одну цифру от 0 до 9. В свою очередь подмассивы состоят из 8-ми элементов, которые задают состояния сегментов от A до G и DP. Например, первый подмассив описан как {1,1,1,1,1,1,0,0} и он отвечает за вывод на индикатор нуля. Это означает, что сегменты A,B,C,D,E,F должны гореть, а сегменты G и DP – нет.
[1|1|1|1|1|1|0|0 ] [A|B|C|D|E|F|G|DP]
В результате получаем примерно следующее:

И вот так в динамике:
Это самый простой способ управления сегментным индикатором, но, как мы видим, он задействует почти все цифровые ножки Arduino. Особенно если мы решим использовать все разряды индикатора. Тогда кроме ножек для управления сегментами придётся дополнительно использовать столько выводов, сколько разрядов у индикатора.
2подключение 7-сегментного светодиодногоиндикатора к arduino
В качестве индикатора для вывода показаний компаса будем использовать семисегментный индикатор 3361AS-1. Он построен по принципу индикатора с общим катодом.
Светодиодный индикатор с общим катодом – это тип индикатора, состоящий из нескольких светодиодов в одном корпусе, у которых общая земля, а питание на каждый светодиод подаётся отдельно.
Напомню, что 7-сегментным индикатор называется из-за того, что он состоит из 7-ми светодиодов, которые расположены в форме цифры «8». Зажигая определённые сегменты, можно изображать разные цифры. Это похоже на цифры индекса на почтовом конверте: закрашивая определённые участки, мы пишем разные индексы.

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

Отечественными аналогами данного преобразователя являются микросхемы серий ИД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); }

Итак, теперь мы умеем выводить трёхзначные числа на 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 со сглаживанием
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 в константу в начале скетча, чтобы можно было быстро изменить скетч для модификации датчика с другим диапазоном измерений.
В результате выполнения данного скетча картинка получается гораздо более приятная:

Тот же самый принцип заложен в библиотеки для Arduino, которые оперируют с датчиком тока ACS712. Например, вот эта библиотека Troyka Current.
По результату эксперимента получается, что датчик ACS712 очень простой, но при этом довольно не точный. Гораздо точнее датчик тока, который мы рассмотрим в следующем разделе.
4собираем всё вместе:компас на arduino
Схема нашего устройства будет такой (нарисована в DipTrace Schematic):

Здесь ARD1 – это Arduino Nano, CD4511 – драйвер управления 7-сегментным дисплеем 3361AS, MPU-9255 – собственно, сам модуль с магнитным датчиком, SW1 – кнопка для запуска и останова отслеживания азимута, BUZ – звуковой извещатель, а PWR – клемма для подачи внешнего питания от батареи «Крона» на устройство.
Монтаж компаса будем производить на печатной плате, которую «разведём» в программе DipTrace PCB Layout.

Закажем печатную плату здесь. На этом предприятии делают всё быстро и качественно. Например, изготовление данной печатной платы заняло около суток от момента заказа до отправки. Единственный минус – придётся долго ждать доставки из Китая (2-4 недели).

Распаяем элементы на плате.

После распайки компонентов плата электронного компаса будет выглядеть так:

Останется только придумать какой-то корпус для платы с компасом.

В процессе экспериментов выяснились несколько деталей, которые потребовали доработки. Во-первых, динамик вносит искажения в показания компаса. Величина искажения зависит от типа динамика и его близости к датчику. Поэтому его желательно отнести подальше от датчика, а не размещать непосредственно на плате.

Во-вторых, изначальный скетч определения азимута выводит довольно приблизительные и нестабильные измерения. Поэтому в части работы компаса всё было переделано. Я взял за основу скетч, представленный в этой статье. Он отличается тем, что используются показания акселерометра для коррекции наклона датчика, а также вводятся дополнительные коррекции, связанные с индивидуальными особенностями датчика (в частности, чувствительность ASAX, ASAY, ASAZ). Для нормальной работы этого скетча необходимо сделать следующее.
4управление 7-сегментным индикатором с помощью драйвера cd4511 и arduino
При подключении двоичного декодера будем руководствоваться следующей таблицей:
Вывод CD4511 | Назначение | Примечание |
---|---|---|
A0…A3 | Входы двоичного преобразователя | Соответствуют разрядам двоичного числа. |
a…g | Выходы на сегменты индикатора | Подключаются через токоограничивающие резисторы к соответствующим сегментам светодиодного индикатора. |
Lamp Test# | Тест индикатора (включает все сегменты) | Подключим к питанию, не использовать его. |
Blanking# | Очистка индикатора (отключает все сегменты) | Подключим к питанию, чтобы не использовать его. |
Latch Enabled# | Выход активен | Будет подключен к земле, чтобы выход был всегда активен. |
VDD | Питание микросхемы и индикатора | От 3 до 15 В. |
GND | Земля | Общая у CD4511, Arduino, 7-сегментного индикатора. |
Желательно также подключить керамический конденсатор ёмкостью примерно 1 мкФ между землёй и питанием микросхемы CD4511.

Теперь напишем простой скетч, чтобы проверить работоспособность 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); }
Получится вот такая картина.

В динамике это выглядит так. Тут как раз временами видны мерцания сегментов светодиодного индикатора.
Можно попробовать поиграть значением задержек в функции setDigit(). Если сделать задержки меньше, то мерцание станет меньше заметно. Но начнут сильнее засвечиваться соседние сегменты на выбранном разряде индикатора. Тут необходимо выбрать какое-то компромиссное решение.
5подключение датчика тока и напряжения ina219 к arduino
Для начала пойдём простым путём: скачаем готовую библиотеку, загрузим в Arduino и посмотрим на результат. Существует несколько библиотек для работы с нашим датчиком. Предлагаю воспользоваться вот этой популярной библиотекой для INA219 от Adafruit. Скачаем её, установим стандартным образом и загрузим в Arduino скетч из примеров getcurrent.
Если скетч не компилируется, а в сообщениях об ошибках присутствуют какие-то недостающие компоненты (например, Adafruit_I2CDevice.h или Adafruit_BusIO_Register.h, то необходимо доустановить их. Проще всего это сделать так. Для этого способа требуется подключение к интернету на компьютере, где запущена среда разработки.
Открыть в среде Arduino IDE менеджер библиотек: в меню ToolsManage Libraries…. Откроется окно Library Manager. В поле поиска следует ввести adafruit busio. Когда библиотека будет обнаружена и покажется в списке, нажать кнопку Install.

Подключим модуль GY-219 к Arduino по следующей схеме. SDA и SCL датчика можно подключить как к аналоговым входам A4 и A5 Arduino, так и к специально выделенным портам SDA и SCL (если они есть на вашей плате).

В качестве нагрузки может быть любой источник, например, электромотор, лампа или просто мощный резистор. У меня это 5 соединённых параллельно 5-ваттных 16-омных резисторов. В качестве источника питания также может выступать любой из имеющихся у вас источников. Я буду использовать лабораторный источник питания.

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

Отлично! Всё работает! Как говорится, бери – и пользуйся.
Данная библиотека позволяет также проводить калибровку датчика INA219 при необходимости. Подробности – в описании библиотеки и в самих исходниках (в файле Adafruit_INA219.cpp библиотеки даётся большое число пояснений).
6как читать данные сдатчика тока и напряжения ina219
Если посмотреть на обмен данными по шине I2C, который происходит при работе данного скетча (с помощью логического анализатора, конечно), то увидим следующее.

Чтобы понять, что здесь происходит, необходимо познакомиться с картой регистров датчика INA219. Датчик содержит всего 6 регистров. Все регистры 16-разрядные.
Адрес регистра | Название регистра | Назначение регистра | Тип |
---|---|---|---|
0x00 | Configuration | Сброс всех регистров, настройка диапазона измерений, усиления PGA, разрешения АЦП и фильтрации. | Чтение/Запись |
0x01 | Shunt voltage | Хранит измеренное значение напряжения на шунтирующем резисторе 0,1 Ом. | Чтение |
0x02 | Bus voltage | Хранит измеренное значение напряжения шины. | Чтение |
0x03 | Power | Хранит измеренное значение мощности. | Чтение |
0x04 | Current | Содержит значение силы тока, протекающего через шунтирующий резистор. | Чтение |
0x05 | Calibration | Калибровочный регистр. Задаёт диапазон измерений и позволяет осуществлять калибровку системы. | Чтение/Запись |
Для обмена с модулем воспользуемся отладочной платой с микросхемой FT2232H и программой SPI via FTDI. Это будет проще, чем использовать Arduino, т.к. для внесения изменений в целях эксперимента не придётся каждый раз перепрограммировать ПЗУ, а можно будет вносить изменения в передаваемые команды «на лету».

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

Как вы уже наверное догадались, команда «0» означает адрес регистра, из которого мы хотим прочитать данные. А число 0x399F – это данные в нулевом регистре (регистр конфигурации). И это соответствует документации, т.к. после включения и загрузки микросхема 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.

Рассмотрим, как привести данные в регистрах «в человеческий вид». Нам интересны не все значения, а только напряжения и ток. Плюс регистр калибровки, который играет роль поправочного коэффициента.
7подключение трёхканального датчика тока и напряжения ina3221 к arduino
Датчик тока INA3221 практически идентичен датчику INA219. Основное отличие состоит в том, что он имеет 3 измерительных канала вместо одного. Показания с них можно снимать независимо друг от друга. Будем использовать вот такую небольшую плату с датчиком:

Подключается данный модуль к Arduino всего 4-мя проводами: два для питания, и ещё два – шина I2C.
Вывод модуля INA3221 | Вывод Arduino | Назначение |
---|---|---|
SDA | A4 | Данные шины I2C |
SCL | A5 | Импульсы синхронизации шины I2C |
VS | 3.3V | Питание |
GND | GND | Общий |

Назначение остальных выводов модуля показано на приведённом рисунке и в таблице ниже.
Вывод модуля 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 довольно точно совпадают с показаниями амперметра.

List of settings
enumclassACCEL_FS_SEL { A2G, A4G, A8G, A16G };
enumclassGYRO_FS_SEL { G250DPS, G500DPS, G1000DPS, G2000DPS };
enumclassMAG_OUTPUT_BITS { M14BITS, M16BITS };
enumclassFIFO_SAMPLE_RATE : uint8_t {
SMPL_1000HZ,
SMPL_500HZ,
SMPL_333HZ,
SMPL_250HZ,
SMPL_200HZ,
SMPL_167HZ,
SMPL_143HZ,
SMPL_125HZ,
};
enumclassGYRO_DLPF_CFG : uint8_t {
DLPF_250HZ,
DLPF_184HZ,
DLPF_92HZ,
DLPF_41HZ,
DLPF_20HZ,
DLPF_10HZ,
DLPF_5HZ,
DLPF_3600HZ,
};
enumclassACCEL_DLPF_CFG : uint8_t {
DLPF_218HZ_0,
DLPF_218HZ_1,
DLPF_99HZ,
DLPF_45HZ,
DLPF_21HZ,
DLPF_10HZ,
DLPF_5HZ,
DLPF_420HZ,
};
structMPU9250Setting {
ACCEL_FS_SEL accel_fs_sel {ACCEL_FS_SEL::A16G};
GYRO_FS_SEL gyro_fs_sel {GYRO_FS_SEL::G2000DPS};
MAG_OUTPUT_BITS mag_output_bits {MAG_OUTPUT_BITS::M16BITS};
FIFO_SAMPLE_RATE fifo_sample_rate {FIFO_SAMPLE_RATE::SMPL_200HZ};
uint8_t gyro_fchoice {0x03};
GYRO_DLPF_CFG gyro_dlpf_cfg {GYRO_DLPF_CFG::DLPF_41HZ};
uint8_t accel_fchoice {0x01};
ACCEL_DLPF_CFG accel_dlpf_cfg {ACCEL_DLPF_CFG::DLPF_45HZ};
};
Mpu 9250 и arduino
Сегодня мы хотим познакомить вас с 9-осевым датчиком 3D положения MPU 9250. Этот модуль выбран нами для обзора совсем не случайно, а по веским причинам — из-за функциональности, практичности и миниатюрности. Его можно использовать в различных спортивных диагностирующих приборах, манипуляторах, роботах, 3D-контроллерах и аппаратах автомобильной электроники, устройствах такого плана как квадрокоптеры ∕ дроны, а также подключать к планшетам ∕ смартфонам (например, в качестве компаса или инструмента навигации).
Да и в целом любителям робототехники и проектирования подобный девайс с функцией 3 в 1 (акселерометр, гироскоп, магнитометр) точно не помешает, ведь такие устройства наравне с датчиками движения широко востребованы в среде любителей «самоделок». А раз так – нам необходимо детальнее познакомиться с его техническими характеристиками и схемами подключения к Arduino.
Начнем с первого пункта – параметры:
- питание: 3.5 – 5 V (имеется внутренний стабилизатор);
- интерфейс: шина I2C (400кГц), SPI (1 МГц);
- рабочие диапазоны гироскопа: ± 250 /500 /1000 /2000 ° / сек;
- — акселерометра: ± 2 /± /4 ±/ 8 ± /16 г;
- диапазон измерения магнитометра: ± 4800uT;
- габаритные размеры: 15 x 25 мм.
Кроме того, в приборе продуманы дополнительное выводы — AUX I2C.
Теперь разберемся с главным вопросом – как подключить MPU 9250 к Ардуино. Для этой цели нам понадобятся: плата расширения Arduino Uno, непосредственно IMU датчик, макетная плата, соединительные провода (для подключения к персональному компьютеру — USB кабель). Схема подключения сенсора выглядит следующим образом (через I2C):Для считывания данных одновременно с трех датчиков предлагаем написать такой скетч:
#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 ("t");
Serial.print (ay,DEC);
Serial.print ("t");
Serial.print (az,DEC);
Serial.print ("t");
// Gyroscope
Serial.print (gx,DEC);
Serial.print ("t");
Serial.print (gy,DEC);
Serial.print ("t");
Serial.print (gz,DEC);
Serial.print ("t");
// _____________________
// ::: Magnetometer :::
// Read register Status 1 and wait for the DRDY: Data Ready
uint8_t ST1;
do
{
I2Cread(MAG_ADDRESS,0x02,1,&ST1);
}
while (!(ST1&0x01));
// Read magnetometer data
uint8_t Mag[7];
I2Cread(MAG_ADDRESS,0x03,7,Mag);
// Create 16 bits values from 8 bits data
// Magnetometer
int16_t mx=-(Mag[3]<<8 | Mag[2]);
int16_t my=-(Mag[1]<<8 | Mag[0]);
int16_t mz=-(Mag[5]<<8 | Mag[4]);
// Magnetometer
Serial.print (mx 200,DEC);
Serial.print ("t");
Serial.print (my-70,DEC);
Serial.print ("t");
Serial.print (mz-700,DEC);
Serial.print ("t");
// End of line
Serial.println("");
// delay(100);
}
В этой прошивке используется специализированное ПО — библиотека Wire. Ее можно отыскать и импортировать в Arduino IDE (менеджер библиотек), а вот вторую библиотеку — TimerOne — придется отыскать и скачать в Интернете (архив распаковываем в директории libraries).
На этом пока всё. Хороших вам проектов!