КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Урок 26.3 Соединяем две arduino по шине I2C

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

В этом уроке мы научимся соединять две arduino по аппаратной шине I2C.

Преимущества:

  • Реализуется возможность подключения до 126 устройств.
    (не рекомендуется присваивать устройствам адреса 0x00 и 0x7F)
  • Не требуются дополнительные модули.
  • Все устройства одинаково подключаются к шине.
  • Каждое ведомое устройство имеет свой уникальный адрес на шине.

Недостатки:

  • Код программы немного сложнее чем для шины UART.

Нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

  • Библиотека LiquidCrystal_I2C_V112 (для подключения дисплеев LCD1602 по шине I2C).
  • Библиотека iarduino_I2C_connect (для удобства соединения нескольких arduino по шине I2C).

О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE, а о том, как работать с LCD дисплеями, на странице Wiki - Работа с символьными ЖК дисплеями.

Видео:

Схема подключения:

На шине i2С находятся 4 устройства: 3 arduino и 1 LCD дисплей. Все устройства шины I2C соединены через Trema I2C Hub. Для подключения Arduino используются аппаратные выводы шины I2C.

Схема соединения нескольких Arduino по шине I2C

На шине I2C не используются дополнительные подтягивающие резисторы (для линий SDA и SCL), так как они интегрированы в LDC I2C дисплее.

Код программы:

Arduino master:
// Подключаем библиотеки:
#include <Wire.h>                                     // подключаем библиотеку для работы с шиной I2C
#include <LiquidCrystal_I2C.h>                        // подключаем библиотеку для работы с LCD дисплеем
#include <iarduino_I2C_connect.h>                     // подключаем библиотеку для соединения arduino по шине I2C
// Объявляем переменные и константы:
iarduino_I2C_connect I2C2;                            // объявляем переменную для работы c библиотекой iarduino_I2C_connect
LiquidCrystal_I2C    lcd(0x27,16,2);                  // объявляем переменную для работы с LCD дисплеем, указывая параметры дисплея (адрес I2C = 0x27, количество столбцов = 16, количество строк = 2)
const byte           PIN_Button_master = 2;           // объявляем константу с указанием номера цифрового вывода, к которому подключена кнопка
      bool           VAR_Button_master = 0;           // объявляем переменную для чтения состояний собственной кнопки
      bool           VAR_Button_slave  = 0;           // объявляем переменную для чтения состояний кнопки ведомого 0x77
      int            VAR_Potentiometer = 0;           // объявляем переменную для чтения значения с потенциометра ведомого 0x77
void setup(){
//Wire.setClock(400000);                              // устанавливаем скорость передачи данных по шине I2C = 400кБит/с
  Wire.begin();                                       // инициируем подключение к шине I2C в качестве ведущего (master) устройства
  pinMode(PIN_Button_master, INPUT);                  // Устанавливаем режим работы вывода собственной кнопки, как вход
//Выводим данные на LCD дисплей:
  lcd.init();                                         // инициируем LCD дисплей
  lcd.backlight();                                    // включаем подсветку LCD дисплея
  lcd.setCursor(0, 0); lcd.print("iArduino.ru");      // выводим текст "iArduino.ru"
  delay(1000);                                        // ждём 1 секунду
  lcd.setCursor(0, 0); lcd.print("button   = ");      // выводим текст "button   = "
  lcd.setCursor(0, 1); lcd.print("resistor = ");      // выводим текст "resistor = "
}
void loop(){
//Считываем данные:
  VAR_Button_master  = digitalRead(PIN_Button_master);// Считываем состояние собственной кнопки
  VAR_Button_slave   = I2C2.readByte(0x01,0);         // Считываем состояние кнопки ведомого (адрес ведомого 0x01, номер регистра 0)
  VAR_Potentiometer  = I2C2.readByte(0x02,1)<<8;      // Считываем старший байт значения потенциометра ведомого (адрес ведомого 0x02, номер регистра 1), сдвигаем полученный байт на 8 бит влево, т.к. он старший
  VAR_Potentiometer += I2C2.readByte(0x02,2);         // Считываем младший байт значения потенциометра ведомого (адрес ведомого 0x02, номер регистра 2), добавляя его значение к ранее полученному старшему байту
//Отправляем данные:
  I2C2.writeByte(0x02,0,VAR_Button_master);           // Отправляем состояние собственной кнопки ведомому (адрес ведомого 0x02, номер регистра 0, состояние кнопки)
//Выводим данные на LCD дисплей:
  lcd.setCursor(11, 0); lcd.print("    ");            // стираем предыдущее состояние кнопки ведомого 0x01
  lcd.setCursor(11, 0); lcd.print(VAR_Button_slave);  // выводим состояние кнопки ведомого 0x01
  lcd.setCursor(11, 1); lcd.print("    ");            // стираем предыдущее значение потенциометра ведомого 0x02
  lcd.setCursor(11, 1); lcd.print(VAR_Potentiometer); // выводим значение потенциометра ведомого 0x02
  delay(50);                                          // ждём 0.05 секунд, иначе данные на LCD дисплее будут заметно мерцать
}

скачать

Arduino slave 0x01:
// Подключаем библиотеки:
#include <Wire.h>                                     // подключаем библиотеку для работы с шиной I2C
#include <iarduino_I2C_connect.h>                     // подключаем библиотеку для соединения arduino по шине I2C
// Объявляем переменные и константы:
iarduino_I2C_connect I2C2;                            // объявляем переменную для работы c библиотекой iarduino_I2C_connect
const byte           PIN_Button = 2;                  // объявляем константу с указанием номера цифрового вывода, к которому подключена кнопка
      byte           REG_Array[1];                    // объявляем массив, данные которого будут доступны для чтения/записи по шине I2C
void setup(){
//Wire.setClock(400000);                              // устанавливаем скорость передачи данных по шине I2C = 400кБит/с
  Wire.begin(0x01);                                   // инициируем подключение к шине I2C в качестве ведомого (slave) устройства, с указанием своего адреса на шине.
  I2C2.begin(REG_Array);                              // инициируем возможность чтения/записи данных по шине I2C, из/в указываемый массив
  pinMode(PIN_Button, INPUT);                         // Устанавливаем режим работы вывода кнопки, как вход
}
void loop(){
  REG_Array[0] = digitalRead(PIN_Button);             // Сохраняем состояние кнопки в 0 ячейку массива REG_Massive
}

скачать

Arduino slave 0x02:
// Подключаем библиотеки:
#include <Wire.h>                                     // подключаем библиотеку для работы с шиной I2C
#include <iarduino_I2C_connect.h>                     // подключаем библиотеку для соединения arduino по шине I2C
// Объявляем переменные и константы:
iarduino_I2C_connect I2C2;                            // объявляем переменную для работы c библиотекой iarduino_I2C_connect
const byte           PIN_Potentiometer = 0;           // объявляем константу с указанием номера аналогового вывода, к которому подключён потенциометр
const byte           PIN_LED           = 13;          // объявляем константу с указанием номера цифрового вывода, к которому подключен светодиод
      int            VAR_Potentiometer = 0;           // объявляем переменную для чтения значения с потенциометра
      byte           REG_Massive[3];                  // объявляем массив, данные которого будут доступны для чтения/записи по шине I2C
void setup(){
//Wire.setClock(400000);                              // устанавливаем скорость передачи данных по шине I2C = 400кБит/с
  Wire.begin(0x02);                                   // инициируем подключение к шине I2C в качестве ведомого (slave) устройства, с указанием своего адреса на шине.
  I2C2.begin(REG_Massive);                            // инициируем возможность чтения/записи данных по шине I2C, из/в указываемый массив
  pinMode(PIN_LED, OUTPUT);                           // Устанавливаем режим работы вывода светодиода, как выход
}
void loop(){
  VAR_Potentiometer = analogRead(PIN_Potentiometer);  // Считываем значение потенциометра
  REG_Massive[1]    = VAR_Potentiometer>>8;           // Сохраняем старший байт значения потенциометра в  1 ячейку массива REG_Massive
  REG_Massive[2]    = VAR_Potentiometer;              // Сохраняем младший байт значения потенциометра во 2 ячейку массива REG_Massive
  digitalWrite(PIN_LED, REG_Massive[0]);              // вкл/выкл светодиод в соответствии со значением 0 элемента массива REG_Massive
}

скачать

Алгоритм работы:

  • Arduino master проверяет состояние собственной кнопки.
  • Arduino master опрашивает две Arduino Slave.
    • Arduino Slave 0x01 возвращает состояние кнопки.
    • Arduino Slave 0x02 возвращает значение падения напряжения плеча потенциометра.
  • Arduino master мастер отправляет состояние своей кнопки в Arduino Slave 0x02.
    • Arduino Slave 0x02 включает или выключает светодиод, в соответствии с полученными данными.
  • Arduino master отправляет данные о состояниях кнопки Arduino Slave 0x01 и потенциометра Arduino Slave 0x02 на LCD I2C дисплей.

Настройка параметров шины I2C:

Максимальная, аппаратно реализуемая частота передачи данных, может достигать 1/16 от тактовой частоты.

Библиотека Wire позволяет устанавливать скорость передачи данных через функцию setClock(), которую требуется вызвать до функции begin().

Библиотека Wire позволяет аппаратно подключить Arduino к шине I2C с указанием роли Arduino на шине: ведущий или ведомый.

  • Wire.setClock(400000); // скорость передачи данных 400 кБит/с.
  • Wire.begin(); // подключение к шине I2C в роли ведущего.
  • Wire.begin(адрес); // подключение к шине I2C в роли ведомого, с указанием адреса.

Функции библиотеки iarduino_I2C_connect:

В библиотеке iarduino_I2C_connect реализованы 4 функции: 2 для ведущего и 2 для ведомого.

На ведомом устройстве достаточно вызвать функцию begin() с указанием массива, данные которого требуется сделать доступными для мастера. Далее можно работать с этим массивом, «забыв» про шину I2C. Мастер обращаясь к ведомому сможет получать и изменять данные элементов указанного массива (обращаясь к номеру элемента массива как к номеру регистра).

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

На ведущем устройстве доступны функции readByte() и writeByte(). Указывая в качестве параметров функций, адрес ведомого устройства и номер регистра, можно побайтно читать и записывать данные.

Функции для ведомого:

begin():

  • Назначение: указание массива, элементы которого будут доступны для чтения/записи по шине I2C.
  • Синтаксис: begin(массив); // тип данных массива - byte.
  • Возвращаемые значения: Нет.
  • Примечание: Вызывается 1 раз в коде функции Setup().

writeMask():

  • Назначение: указание маскировочного массива, каждый элемент которого является флагом разрешения записи по шине I2C.
  • Синтаксис: writeMask(маскировочный_массив); // тип данных массива - bool.
  • Возвращаемые значения: Нет.
  • Примечание: Вызывается 1 раз в коде функции Setup().
  • Пример:
    #include <Wire.h>
    #include <iarduino_I2C_connect>
    iarduino_I2C_connect ccc;
    byte aaa[5]; // объявляем 5 элементов массива, к которому разрешим доступ.
    bool bbb[5] = {0,1,1,1,0}; // объявляем 5 элементов маскировочного массива.
    Wire.begin(0x33); // подключаемся к шине I2C в роли ведомого с адресом 0x33.
    ccc.begin(aaa); // разрешаем доступ к массиву aaa по шине I2C.
    ccc.wtiteMask(bbb); // маскируем разрешение на запись в массив aaa.
    • В соответствии со значениями массива bbb, мастер сможет записывать данные в ячейки 1,2,3 массива aaa, а попытка записи в 0 и 4 элементы будет проигнорирована.
    • Если массив aaa имеет больше элементов чем массив bbb, то попытка мастера записать данные в «лишние» элементы будет проигнорирована.
    • Если не объявлять маскировочный массив (не вызывать функцию writeMask() вообще), то все элементы массива aaa будут доступны для записи мастером.
Функции для ведущего:

readByte():

  • Назначение: Чтение одного байта данных из устройства на шине I2C.
  • Синтаксис: readByte(адрес_устройства, адрес_регистра);
  • Возвращаемые значения: uint8_t байт - данные считанные из указанного адреса, указанного устройства.
  • Примечание: Если производится чтение по шине I2C из массива объявленного функцией begin(), то адрес регистра соответствует номеру элемента массива.

writeByte():

  • Назначение: Запись одного байта данных в устройство на шине I2C.
  • Синтаксис: writeByte(адрес_устройства, адрес_регистра, байт_данных);
  • Возвращаемые значения: uint8_t байт - результат операции записи, соответствует значению возвращаемому функцией Wire.endTransmission().
    • 0 - успех.
    • 1 - переполнение буфера передачи данных.
    • 2 - получен NACK на переданный адрес устройства.
    • 3 - получен NACK на переданный адрес регистра.
    • 4 - неизвестная ошибка.
  • Примечание: Если производится запись по шине I2C в массив объявленный функцией begin(), то адрес регистра соответствует номеру элемента массива.

Ссылки:




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями