I2C  интерфейс в Arduino

I2C в ардуино

I2C (Inter-Integrated Circuit) — протокол подключения интерфейса последовательной шины. По другому его называют TWI (двухпроводным интерфейсом), так как обмен данными происходит  по двум проводам. Один из проводов называется — SDA (последовательные данные), другой — SCL (последовательные часы).

I2C — это протокол связи, основанный на подтверждении, т.е. передатчик проверяет наличие подтверждения получения пакета от приемника после того как передатчик отправит данные, чтобы узнать, успешно ли получены данные приемником.

I2C работает в двух режимах, а именно:

  • Режим мастера (Master)
  • Режим ведомого (Slave)

Провод SDA (данные) используется для приема и передачи данных  мастером  и ведомым устройством.

SCL (синхронизация) используются для синхронных часов между мастером (Master)  и ведомым устройством (Slave).

Устройство Master устанавливает связь с устройством Slave. Для того, что бы начать передачу данных, мастеру необходимо знать адрес ведомого устройства. Ведомое устройство отвечает мастеру, когда он к нему обращается.

Устройство I2C имеет 7-битный или 10-битный уникальный адрес. Таким образом, чтобы получить доступ к этим устройствам, мастеру необходимо обратиться к ведомому устройству по его адресу.

Обмен данными между устройствами через шину I2C на практике применяется во многих проектах Arduino. Чтение RTC (часы реального времени, про библиотеку можно узнать здесь), чтение различных датчиков, получение доступа к внешней памяти EEPROM. Чтение данных от сенсорных модулей, таких как гироскоп, магнитометр и т. д.

Протокол I2C использует для связи 2 линии:

  • Serial Clock (SCL): это тактовый сигнал. Данные будут отправлены на другие устройства по событию такта часов. Только ведущее устройство имеет контроль над этой линией SCL.
  • Последовательные данные (SDA): это линия последовательных данных, которая используется для обмена данными между Мастером и Slave устройством.
Общая схема подключения устройств по I2C
Рисунок 1. Общая схема подключения устройств по I2C

Шина I2C представляет собой конфигурацию с открытым стоком, что означает, что они могут тянуть соответствующую сигнальную линию с низким уровнем, но не могут повышать ее. Следовательно, линия перейдет в неизвестное состояние. Чтобы этого избежать, необходимо подключить подтягивающие резисторы к контактам SCL и SDA.

Контакты I2C в Arduino UNO

Плата Arduino Uno имеет контакты I2C, показанный на рисунке ниже.

Контакты SDA и SCL на плате arduino uno
Рисунок 2. Контакты SDA и SCL на плате ардуино уно

Плата Arduino Uno имеет только один модуль I2C, но обеспечивает линии SDA и SCL в двух разных местах.

Примечание


При обмене данными с устройствами, использующими протокол связи I2C, следует использовать подтягивающие резисторы. Значение подтягивающих резисторов может варьироваться в зависимости от используемых устройств.

Функции I2C в Arduino

Для того, что бы использовать интерфейс I2C в arduino, необходимо подключить библиотеку Wire.h. Эта библиотека содержит все функции по инициализации, приему и передаче данных по шине.


Wire.write(data)
Данная функция используется для записи (передачи) данных на ведущее или ведомое устройство.

Параметр

data
это однобайтовое значение, строка, массив данных.

Возвращает

Количество записанных байтов

Пример

Wire.write(7); // отправляем байт данных
Wire.write("i2c"); //отправляем строку на ведомое устройство
Wire.write(a, 6); // здесь а массив

Wire.available()
Функция применяется мастером или ведомым устройством для проверки наличия запрошенных данных. Она возвращает количество байт, которые можно считать.

Wire.read()
Данная функция предназначена для чтения данных, запрошенных мастером с ведомого, или чтения данных, переданных от ведущего к мастеру.

Примечание


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


Wire.begin ()
Функция инициализирует библиотеку Wire.h и подключает устройство к шине I2C в качестве мастера.

Wire.beginTransmission(slave address)
Эта функция начинает передачу с ведомого устройства I2C, имеющего свой уникальный адрес.

Параметр

slave address
7-битный адрес устройства, с которым мы хотим установить связь

Пример

Wire.beginTransmission (50) //начать передачу с ведомого устройства с адресом 50.

Wire.requestFrom(address, quantity)

ИЛИ

Wire.requestFrom(address, quantity, stop)
Эта функция используется мастером для запроса или получения данных от ведомого устройства. Запрошенные данные можно прочитать с помощью

Wire.read()

.

Параметры

address
адрес устройства, с которым мы хотим общаться
quantity
количество запрашиваемых байт
stop
значение true или false. true — отправлять стоп-сообщение после запроса, освобождая шину. false — постоянно отправлять перезагрузку после запроса, сохраняя соединение активным

Возвращает

Число байт, возвращенных ведомым устройством

Пример

Wire.requestFrom(50, 4)           // запрос 4-х байт от ведомого устройства с адресом 50
Wire.requestFrom(50, 4, true) //перестанет получать данные после 4 байт, освобождая  шину.

Wire.endTransmission()
Функция завершает передачу на ведомое устройство, начатую функцией

beginTransmission()

, и передает байты, поставленные в очередь функцией

write()

.

Возвращает

Байт, который указывает статус передачи.


Wire.begin(address)
Функция инициирует библиотеку Wire и подключается к шине I2C в качестве подчиненного устройства с указанным адресом.

Параметр

address
7-битный адрес ведомого устройства, если он не указан, присоединяйтесь к шине в качестве ведущего.

Wire.onReceive(handler)
Функция обработчика, которая вызывается, когда ведомое устройство получает переданные данные от ведущего.

Пример

void setup() {
  Wire.begin(8);                //инициализируем I2C шину с адресов #8
  Wire.onReceive(receiveEvent); // регистрируем событие обработчика
  Serial.begin(9600);           // инициализируем UART
}
void receiveEvent (int howmany){
  while (1 < Wire.available()) { // в цикле проверяем поступили ли на шину I2C данные
    char c = Wire.read();        // если да, читаем байт
       Serial.print(c);             // отправляем его на UART
  }
}    

Wire.onRequest(handler)
Функция-обработчик, которая вызывается, когда мастер запрашивает у ведомого устройства, не принимает параметров и ничего не возвращает.

Пример

void setup() {
  Wire.begin(8);                //инициализируем I2C шину с адресов #8
  Wire.onRequest(requestEvent); // регестрируем событие 
}
void loop() {
  delay(100);
}

// функция, которая выполняется всякий раз, когда мастер запрашивает данные
// эта функция регистрируется как событие
void requestEvent() {
  Wire.write("hello "); // оправляем сообщение 6 байт
  // как и ожидал мастер
}

Подключение по I2C двух плат Arduino

Подключение по I2C двух плат ардуино уно
Рисунок 3. Две платы Arduino UNO по интерфейсу I2C

Рассмотрим  передачу данных с ведущего на ведомое устройство по шине I2C с использованием двух плат Arduino. Одна в качестве ведущего, а другая в качестве ведомого устройства.

Будем использовать встроенный пример библиотеки Wire, предоставленный Arduino вместе с их IDE для  работы с I2C.

Из библиотеки Wire мы используем здесь master_writer для Arduino в качестве ведущего и slave_receiver для Arduino в качестве ведомого. В этом примере номер передается от ведущего к ведомому, а ведомый отображает его на последовательном порту.

Вы можете использовать примеры из этой библиотеки:

ФайлПримерыWiremaster_writer

ФайлПримерыWiremaster_receiver

Скетч для Arduino Master

#include <Wire.h>
void setup() {
  Wire.begin(); // инициализация мастера I2C
}
byte x = 0;
void loop() {
  Wire.beginTransmission(8);  // передача на устройство с адресом 8
  Wire.write("x is ");        // отправка 5 байт
  Wire.write(x);              // отправка 1 байта
  Wire.endTransmission();     // остановка передачи
  x++;
  delay(500);
}

Скетч для Arduino Slave

#include <Wire.h>
void setup() {
  Wire.begin(8);                // инициализация Slave  I2C с адресом  8
  Wire.onReceive(receiveEvent); // регистрация обработчика
  Serial.begin(9600);           // инициализация UART
}
void loop() {
  delay(100);
}
// функция, которая выполняется всякий раз, когда мастер запрашивает данные
// эта функция регистрируется как событие
void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read();        // прием одного символа
    Serial.print(c);             // передача символа в UART
  }
  int x = Wire.read();           // примем числа
  Serial.println(x);             // Отправка числа в UART
}

Результат работы программы:

Результат обмена данными по интерфейсу I2C
Рисунок 4. Результат обмена данными по интерфейсу I2C между двумя ардуино

Двусторонняя связь между двумя arduino по шине I2C

Давайте напишем программу, в которой мы будем отправлять приветственное сообщение подчиненному устройству, а подчиненное устройство будет отвечать на полученное сообщение приветствием. Две платы Arduino Uno используются как ведущий и ведомый устройства.

Скетч для мастера

#include <Wire.h>
void setup() {
 Serial.begin(9600); /* инициализация UART. */
 Wire.begin();       /* инициализация I2C Master */
 Serial.println("I am I2C Master");
}
void loop() {
 Wire.beginTransmission(8); /* инициализация передачи по адресу 8 */
 Wire.write("Hello Slave"); /* Отправка приветственной строки */
 Wire.endTransmission();    /* остановка передачи */
 Wire.requestFrom(8, 9);    /* событие на чтение 9 байт */
 while(Wire.available()){
    char c = Wire.read();   /* чтение побайтно данных от ведомого устройства */
  Serial.print(c);
 }
 Serial.println();
 delay(1000);
}

Скетч для ведомого

#include <Wire.h>
void setup() {
 Wire.begin(8);                /* инициализация I2C на адрес 8 */
 Wire.onReceive(receiveEvent); /* регистрация события на прием */
 Wire.onRequest(requestEvent); /* регистация события на передачу */
 Serial.begin(9600);           /* инициализация UART. */
 Serial.println("I am I2C Slave");
}
void loop() {
 delay(100);
}
// функция читает данные от мастера
void receiveEvent(int howMany) {
 while (0 <Wire.available()) {
    char c = Wire.read();      
    Serial.print(c);           
  }
 Serial.println();             
}
// функция отправляет мастеру сообщение из 9 байт
void requestEvent() {
 Wire.write("Hi Master");  }

Монитор порта Мастера:

Монитор порта мастера после загрузки примера
Рисунок 5. Монитор порта мастера

Монитор порта Ведомого:

Монитор порта ведомого устройства
Рисунок 6. Монитор порта ведомого устройства
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии