Освой Arduino играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Интерфейсная шина I²C/Библиотека Wire

Узнать адрес

I²C (IIC, англ. Inter-Integrated Circuit) — последовательная асимметричная шина для связи между интегральными схемами внутри электронных приборов.

Шина I2C также известна как двухпроводной интерфейс (Two Wire Interface, TWI) — простое и удобное устройство, используемое для обмена данными. Сам протокол был предложен фирмой Philips, но во избежание проблем с авторскими правами иногда протокол называют "двухпроводным".

Передача данных между устройствами и Arduino осуществляется по двум линиям, которые называют линией данных (Serial Data Line, SDA) и тактовой линией синхронизации сигнала (Serial Clock Line, SCL). В Arduino Uno вывод SDA находится на A4, а линия SCL — на контакте A5. Некоторые новейшие платы R3 имеют отдельные контакты, соединённые с шиной I2C и расположенные в верхнем левом углу для удобства доступа к ним. При подключении требуется установка подтягивающих резисторов. Обычно используют резисторы номиналом 4.7 кОм.

Будучи подключённой к шине I2C, плата Arduino считается ведущим устройством, а все остальные устройства — ведомыми. Каждое ведомое устройство имеет свой адрес (идентификационный номер) — шестнадцатеричное число, — позволяющий плате Arduino обращаться и взаимодействовать с каждым устройством по отдельности. Обычно устройство имеет на выбор диапазон адресов I2C, который указан в документации к нему. Конкретные доступные адреса определяются подключением контактов IC тем или иным образом.

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

Библиотека Wire

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

В скетче сначала необходимо активировать библиотеку, затем в setup() активировать шину.


#include <Wire.h>

void setup() {
  Wire.begin(); // активируем шину
}

Поскольку как правило плата Arduino действует как ведущее устройство, ей не нужно присваивать адрес. Если бы плата настраивалась на работу в режиме ведомого устройства, нам пришлось бы присвоить адрес в диапазоне от 0 до 127, передав его как параметр, чтобы уникально идентифицировать плату на шине I2C.

Передача данных по шине осуществляется по одному байту. Чтобы послать байт из платы Arduino в устройство на шине, необходимо вызвать три функции:

  • Первая функция инициализирует связь, как показано ниже (где аргумент address — это адрес ведомого устройства на шине в шестнадцатеричном виде, например 0x50):
    
    Wire.beginTransmission(address);
    
  • Вторая функция посылает 1 байт данных из Arduino в устройство с адресом, указанным в предыдущем вызове функции. Здесь аргумент data — это переменная, содержащая 1 байт данных; вы можете послать несколько байтов, но для каждого байта придётся вызвать Wire.write():
    
    Wire.write(data);
    
  • По завершении передачи данных определённому устройству следует разорвать связь:
    
    Wire.endTransmission();
    

Чтобы запросить данные из устройства на шине I2C, инициализируйте связь вызовом Wire.beginTransmission(address) и отправьте запрос Wire.requestFrom(address, x), (где x — количество запрашиваемых байтов данных). Затем с помощью следующей функции нужно сохранить принятый байт в переменной:


incoming = Wire.read(); // incoming - переменная, куда
// сохраняется принятый байт данных

По окончании приёма следует разорвать связь вызовом Wire.endTransmission().

Методы библиотеки

begin()


void begin();
void begin(uint8_t address);
void begin(int address);

Инициализация библиотеки Wire и подключение к шине I2C в качестве ведущего или ведомого устройства. Как правило, вызывается только один раз.

Параметры

address: 7-битный адрес устройства (если работаем в режиме ведомого). Если не указано, то контроллер подключается к шине в роли ведущего (master).

Возвращаемое значение

Не возвращает значений

requestFrom()


uint8_t requestFrom(uint8_t address, uint8_t quantity);

Используется ведущим устройством для запроса байта от ведомого устройства. Байты могут быть получены с помощью методов available() и read().

Параметры

  • address: 7-битный адрес устройства для запроса байтов данных
  • quantity: количество запрошенных байт

Возвращаемое значение

Возвращает число считанных байт.

beginTransmission()


void beginTransmission(uint8_t address);

Начало передачи I2C для ведомого устройства с заданным адресом. Затем, нужно вызвать метод write() для добавления последовательности байт в очередь предназначенных для передачи, и выполнить саму передачу данных методом endTransmission().

Параметры

address: 7-битный адрес устройства для передачи.

Возвращаемое значение

Не возвращает значений

endTransmission()


uint8_t endTransmission(void);

Завершает передачу данных для ведомого устройства, которое было начато beginTransmission() и, фактически, осуществляет передачу байт, которые были поставлены в очередь методом write().

Параметры

Нет

Возвращаемое значение

Возвращает байт, который указывает статус передачи:

  • 0: успех
  • 1: данных слишком много и они не помещается в буфер передачи/размер буфера задаётся определением #define BUFFER_LENGTH 32
  • 2: получили NACK на передачу адреса
  • 3: получили NACK на передачу данных
  • 4: другая ошибка

write()


size_t write(uint8_t data);
size_t write(const uint8_t *data, size_t quantity);

Записывает данные от ведомого устройства в ответ на запрос мастера, или записывает очередь байт для передачи от мастера к ведомому устройству (в промежутках между вызовами beginTransmission() и endTransmission()).

Примеры


Wire.write(value);
Wire.write(string);
Wire.write(data, length);

Параметры

  • value: значение для отправления как единичный байт
  • string: строка для отправления как последовательность байт
  • data: массив байт для отправления
  • length: число байт для передачи

Возвращаемое значение

Возвращает число записанных байт.

available()


int available(void);

Метод available() наследуется от класса Stream. Возвращает количество байт, доступных для получения. Этот метод должно быть вызван на мастере, после вызова requestFrom() или ведомым внутри обработчика onReceive().

Параметры

Нет.

Возвращаемое значение

Число байт, доступных для чтения.

read()


int read(void);

Метод read() наследуется от класса Stream. Считывает байт, который был передан от ведомого устройства к мастеру, после вызова requestFrom() или был передан от мастера к ведомому.

Параметры

Нет.

Возвращаемое значение

Следующий полученный байт.

onReceive()
void onReceive( void (*function)(int) );

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

Параметры

  • function: функция, которая вызывается, когда ведомый получает данные; обработчик должен принимать один параметр — int (число байт, считанных от мастера) и ничего не возвращать. Например:
    void MyHandler (int numBytes);

Возвращаемое значение

Не возвращает значений

onRequest()


void onRequest( void (*function)(void) );

Регистрирует функцию, которая вызывается, когда мастер запрашивает данные из этого ведомого устройства.

Параметры

  • function: функция, которая будет вызываться; не имеет параметров и ничего не возвращает. например:
    void MyHandler();

Возвращаемое значение

Не возвращает значений

Узнать адрес

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

Также можно запустить собственный скетч.


#include <Wire.h>

void setup() {
  Serial.begin (115200);

  // Leonardo: wait for serial port to connect
  while (!Serial) 
  {
  }

  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;
  
  Wire.begin();
  for (byte i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
    {
        Serial.print ("Found address: ");
        Serial.print (i, DEC);
        Serial.print (" (0x");
        Serial.print (i, HEX);
        Serial.println (")");
        count++;
        delay (1);  // maybe unneeded?
    }
  }
  
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (count, DEC);
  Serial.println (" device(s).");
}

void loop() {}

Проверил на ЖК-экране, чей адрес обычно 0х27.

I2C address

Дополнительные материалы

RTC (часы реального времени)

Реклама