Обмен данными между arduino по i2c. PCF8574P: подключение кнопок к Arduino по шине I2C или Люк Скайуокер наносит удар

Для Windows Phone 14.07.2019
Для Windows Phone

Описание библиотеки Wire

Данная библиотека позволяет вам взаимодействовать с I2C / TWI устройствами. На платах Arduino с компоновкой R3 (распиновка 1.0) SDA (линия данных) и SCL (линия тактового сигнала) находятся на выводах около вывода AREF. Arduino Due имеет два I2C / TWI интерфейса: SDA1 и SCL1 находятся около вывода AREF, а дополнительные линии находятся на выводах 20 и 21.

В таблице ниже показано, где расположены TWI выводы на разных платах Arduino.

Начиная с Arduino 1.0, данная библиотека наследует функции Stream , что делает ее совместимой с другими библиотеками чтения/записи. Из-за этого send() и receive() были заменены на read() и write() .

Примечание

Существуют 7- и 8-битные версии адресов I2C. 7 битов идентифицируют устройство, а восьмой бит определяет, идет запись или чтение. Библиотека Wire использует 7 битные адреса. Если у вас есть техническое описание или пример кода, где используется 8-битный адрес, вам нужно откинуть младший бит (т.е. сдвинуть значение на один бит вправо), получив адрес от 0 до 127. Однако адреса от 0 до 7 не используются, так как зарезервированы, поэтому первым адресом, который может быть использован, является 8. Обратите внимание, что при подключении выводов SDA/SCL необходимы подтягивающие резисторы. Для более подробной информации смотрите примеры. На плате MEGA 2560 есть подтягивающие резисторы на выводах 20 и 21.

Описание методов

Wire.begin()

Описание

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

Синтаксис

Wire.begin(address)

Параметры

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

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

Пример

Примеры для ведомого устройства смотрите в примерах к методам onReceive() и onRequest() . Примеры для ведущего устройства смотрите в примерах к остальным методам. .

Wire.requestFrom()

Описание

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

Если этот аргумент равен true , то requestFrom() после запроса посылает сообщение STOP, освобождая шину I2C.

Если этот аргумент равен false , то requestFrom() после запроса посылает сообщение RESTART. Шина не освобождается, что мешает другому устройству-мастеру влезть между сообщениями. Это позволяет одному ведущему устройству посылать несколько запросов, пока оно контролирует шину.

Синтаксис

Wire.requestFrom(address, quantity)

Wire.requestFrom(address, quantity, stop)

Параметры

  • address: 7-битный адрес устройства, у которого запрашиваются байты;
  • quantity: количество запрашиваемых байтов;
  • stop: boolean . true посылает сообщение STOP после запроса. false посылает сообщение RESTART после запроса, сохраняя соединение активным.
Возвращаемое значение

byte: количество байтов, возвращенных от ведомого устройства.

Пример

Wire.beginTransmission()

Описание

Начинает передачу на ведомое I2C устройство с заданным адресом. После него последовательность байтов для передачи ставится в очередь с помощью функции write() , и их передача с помощью вызова endTransmission() .

Синтаксис

Wire.beginTransmission(address)

Параметры

address: 7-битный адрес устройства, на которое необходимо передать данные.

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

Пример

Wire.endTransmission()

Описание

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

Для совместимости с определенными I2C устройствами, начиная с Arduino 1.0.1, requestFrom() принимает аргумент логического типа данных, меняющий его поведение.

Если этот аргумент равен true , то requestFrom() после передачи посылает сообщение STOP, освобождая шину I2C.

Если этот аргумент равен false , то requestFrom() после передачи посылает сообщение RESTART. Шина не освобождается, что мешает другому устройству-мастеру влезть между сообщениями. Это позволяет одному ведущему устройству посылать несколько передач, пока оно контролирует шину.

По умолчанию этот аргумент равен true .

Синтаксис

Wire.endTransmission()

Wire.endTransmission(stop)

Параметры

stop: boolean . true посылает сообщение STOP после передачи. false посылает сообщение RESTART после передачи, сохраняя соединение активным.

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

byte , который указывает на состояние передачи:

  • 0: успех;
  • 1: данные слишком длинны для заполнения буфера передачи;
  • 2: принят NACK при передаче адреса;
  • 3: принят NACK при передаче данных;
  • 4: остальные ошибки.
Пример

Смотрите пример к методу write() .

Wire.write()

Описание

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

Синтаксис

Wire.write(value)

Wire.write(string)

Wire.write(data, length)

Параметры

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

byte: write() возвращает количество записанных байтов, хотя чтение этого количества не обязательно.

Пример #include byte val = 0; void setup() { Wire.begin(); // подключиться к шине i2c } void loop() { Wire.beginTransmission(44); // передача на устройство #44 (0x2c) // адрес устройства задан в техническом описании Wire.write(val); // отправить байт значения Wire.endTransmission(); // остановить передачу val++; // увеличить значение if(val == 64) // если дошли до 64-го значения (max) { val = 0; // начать с начала } delay(500); }

Wire.available()

Описание

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

Синтаксис

Wire.available()

Параметры

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

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

Пример

Смотрите пример к методу read() .

Wire.read()

Описание

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

Синтаксис

Параметры

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

byte: очередной принятый байт.

Пример #include byte val = 0; void setup() { Wire.begin(); // подключиться к шине i2c (адрес для мастера не обязателен) Serial.begin(9600); // настроить последовательный порт для вывода } void loop() { Wire.requestFrom(2, 6); // запросить 6 байтов от ведомого устройства #2 while(Wire.available()) // ведомое устройство может послать меньше, чем запрошено { char c = Wire.read(); // принять байт как символ Serial.print(c); // напечатать символ } delay(500); }

Wire.setClock()

Описание

Изменяет тактовую частоту для связи по шине I2C. У ведомых I2C устройств нет минимальной рабочей тактовой частоты, однако обычно используется 100 кГц.

Синтаксис

Wire.setClock(clockFrequency)

Параметры

clockFrequency: значение частоты (в герцах) тактового сигнала. Принимаются значения 100000 (стандартный режим) и 400000 (быстрый режим). Некоторые процессоры также поддерживают 10000 (низкоскоростной режим), 1000000 (быстрый режим плюс) и 3400000 (высокоскоростной режим). Чтобы убедиться, что необходимый режим поддерживается, обращайтесь к технической документации на конкретный процессор.

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

Wire.onReceive()

Описание

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

Синтаксис

Wire.onReceive(handler)

Параметры

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

void myHandler(int numBytes)

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

Пример

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

Wire.onRequest()

Описание

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

Синтаксис

Wire.onRequest(handler)

Параметры

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

void myHandler()

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

Пример

Код для платы Arduino, работающей в качестве ведомого устройства:

#include void setup() { Wire.begin(8); // подключиться к i2c шине с адресом #8 Wire.onRequest(requestEvent); // зарегистрировать обработчик события } void loop() { delay(100); } // функция, которая будет выполняться всякий раз, когда мастером будут // запрошены данные // данная функция регистрируется как обработчик события, смотрите setup() void requestEvent() { Wire.write("hello "); // ответить сообщением }

LCD I2C модуль позволить подключить символьный дисплей к плате Arduino всего по двум сигнальным проводам.

Используемые компоненты (купить в Китае):

. Управляющая плата

. Соединительные провода

Основные технические характеристики:

Дисплей: Символьный 16х02 либо 20x04
. Подсветка: Синяя c белыми символами
. Контраст: Настраивается потенциометром
. Напряжение питания: 5В
. Интерфейс: I2C
. I2C адрес: 0x27
. Размеры: 82мм x 35мм x 18мм

Подключение к Arduino

Модуль оборудован четырех-пиновым разъемом стандарта 2.54мм

SCL : последовательная линия тактирования (Serial CLock)

SDA : последовательная линия данных (Serial DAta)

VCC : "+" питания

GND : "-" питания

Выводы отвечающие за интерфейс I2C на платах Arduino на базе различных контроллеров разнятся

Для работы с данным модулем необходимо установить библиотеку LiquidCrystal_I2C1602V1

Скачиваем, распаковываем и закидываем в папку libraries в папке Arduino. В случае, если на момент добавления библиотеки, Arduino IDE была открытой, перезагружаем среду.

Переходим непосредственно к скетчу. В данном примере выведем стандартный "Hello, world!" и для адрес нашего сообщества.

пример программного кода:

#include #include LiquidCrystal_I2C lcd(0x27,16,2); /* Задаем адрес и размерность дисплея. При использовании LCD I2C модуля с дисплеем 20х04 ничего в коде изменять не требуется, cледует только задать правильную размерность */ void setup () { lcd.init(); // Инициализация lcd lcd.backlight(); // Включаем подсветку // Курсор находится в начале 1 строки lcd.print ("Hello, world!" ); // Выводим текст lcd.setCursor (0, 1); // Устанавливаем курсор в начало 2 строки lcd.print ("сайт" ); // Выводим текст } void loop () { }

Создание собственных символов

С выводом текста разобрались, буквы английского алфавита зашиты в память контроллера внутри дисплея и с ними проблем нет. А вот что делать если нужного символа в памяти контроллера нет?

Не беда, требуемый символ можно сделать вручную. Данный способ частично, ограничение в 7 символов, поможет решить проблему вывода.

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

В ниже приведенном примере нарисуем смайлик.

пример программного кода:

//Тестировалось на Arduino IDE 1.0.5 // Добавляем необходимые библиотеки #include #include // Битовая маска символа улыбки byte smile = { B00010, B00001, B11001, B00001, B11001, B00001, B00010, }; LiquidCrystal_I2C lcd(0x27,20,4); // Задаем адрес и размерность дисплея. void setup () { lcd.init(); // Инициализация lcd lcd.backlight(); // Включаем подсветку // Создаем символ под номером 0 lcd.createChar (1, smile); lcd.setCursor (0, 0); // Устанавливаем курсор в начало 1 строки lcd.print ("\1" ); // Выводим смайлик (символ под номером 1) - "\1" } void loop () { }

Программка для легкого создания символов

В комментариях участник сообщества скинул ссылку на генератор символов

Протокол обмена данными I2C в свое время был разработан компанией Philips. Название I2C произошло от английского Iner-IC управления или по другому межмикросхемного управления, Inter-IC,IIC (I2C) -название одного и того же протокола.

Данный протокол или интерфейс обеспечивате качественный прием и передачу информации (данных) от нескольких различных устройств, к примеру можно измерять температуру и одновременно управлять цифровым потенциометром. Общение происходит програмно, алгоритм общения с датчиком по протоколу I2С записывается в программу Arduino (скетч).

Существуют специальные переходники которые позволяют подключать другие устройства, к примеру имея переходник можно по двум проводом подключить arduino дисплей 1602 (16x2) LCD по протоколу i2c. По запросу LCD i2c на просторах интернета куча информации, вот пример того как должен выглядить переходник под дисплей http://www.ebay.com/itm/310565362720

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

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

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

Пример подключения датчиков по протоколу I2C

Подключение осуществляется по двум проводам: SCL- сигнала тактирования или тактового сигнала и SDA - сигнала данных. При этом к шине I2C можно подключать любое количество различных датчиков (ведомых устройств), имеющие свои уникальные id.

Знакомство начинается со специально написанной для этих целей библиотеки, имя которой Wire. Перед началом работы ее необходимо импортировать в проект,она имеет специальные команды или методы для "общения" с устройствами.

Для обмена данными с устройствами нужно знать их id. Различные устройства могут иметь разную длину адреса (id) 8 или 7 бит.В библиотеке Wire используется 7-ми битная адресация.

Подключение ведомых устройств осуществляется к выводам на плате Arduino. Каждая версия Arduino имеет свои выводы I2C

  • UNO - A4(SDA), A5(SCL);
  • Mega - 20(SDA), 21(SCL);
  • Leonardo- 2(SDA), 3(SCL);
  • Due - 20(SDA), 21(SCL),SDA1,SCL1;

Пример код программы для управления цифровым потенциометром при помощи библиотеки Wire

Данный пример показывает как устанавливать определенное значение сопротивление в цифровом потенциометре при помощи библиотеки Wire. Установка определенного значения осуществляется при помощи переменной val.

#include void setup() { Wire.begin(); // join i2c bus (address optional for master) } byte val = 0; void loop() { Wire.beginTransmission(44); // transmit to device #44 (0x2c) // device address is specified in datasheet Wire.write(byte(0x00)); // sends instruction byte Wire.write(val); // sends potentiometer value byte Wire.endTransmission(); // stop transmitting val++; // increment value if (val == 64) // if reached 64th position (max) { val = 0; // start over from lowest value } delay(500); }

Видео работы I2C и Arduino. Часть 1

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

На шине I2C может сидеть до 128 устройств. Адреса от 0 до 127.

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

О скорости передачи на сайте производителя не пишут. Но по общей документации на протокол она должна составлять как минимум 100 кбит/с


Теперь хотелось бы потестировать на сколько действительно хороша шина I2C, и на сколько сложно по ней обмениваться данными между несколькими контроллерами Arduino

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

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

Ведущий контроллер будет последовательно опрашивать второго и третьего контроллера. Принятые данные первый контроллер должен выводить на индикатор. Опрос ведомых Arduino Nano будет проводиться с частотой 1 раз/сек.

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

Четыре провода от каждого из 4-х устройств нужно соединить параллельно. Вывод А4 платы Arduino Nano - это шина SDA протокола I2C, а А5 - это SCL.

Я буду использовать монтажные шилды под контроллеры Nano для удобства соединений.

Питание будет подаваться просто на один из контроллеров через mini USB вход.

У LCD адрес в сети I2C по умолчанию 27. У второго контроллера установим адрес 2 и у третьего 3. У ведущего первого контроллера адрес не нужен.

Программа контроллера - мастера.

#include #include // Set the LCD address to 0x27 for a 16 chars and 2 line display LiquidCrystal_I2C lcd(0x27, 16, 2); int nano1=0; int nano2; int nano3; void setup() { Serial.begin(9600); // initialize the LCD lcd.begin(); // Turn on the blacklight and print a message. lcd.backlight(); } void loop() { lcd.setCursor(0, 0); lcd.print(nano1); Wire.requestFrom(2, 2); // request 6 bytes from slave device #8 int i=0;nano2=0; while (Wire.available()) { // slave may send less than requested byte c = Wire.read(); // receive a byte as character Serial.print(c); if (i==0) nano2 = ((c & 0xff) << 8); else nano2 = nano2 | c; i++; } Serial.println(""); Serial.println(nano2); lcd.setCursor(9, 0); lcd.print(nano2); delay(100); Wire.requestFrom(3, 2); // request 6 bytes from slave device #8 i=0;nano3=0; while (Wire.available()) { // slave may send less than requested byte c = Wire.read(); // receive a byte as character Serial.print(c); if (i==0) nano3 = ((c & 0xff) << 8); else nano3 = nano3 | c; i++; } lcd.setCursor(0, 1); lcd.print(nano3); delay(100); nano1++; delay(800); }

Первый контроллер изменяет свою переменную типа integer и выводит ее значение на индикатор. Так же он поочереди опрашивает слейв со 2-м и 3-м адресом. Запрашивает у них два байта информации, преобразовывает их в переменную integer. В результате в первом контроллере крутятся три переменные с трёх Nano и он может вывести их на индикатор.

Программа второго контроллера

#include int nano2=0; byte high; void setup() { Wire.begin(2); // join i2c bus with address #8 Wire.onRequest(requestEvent); // register event } void loop() { delay(1000); nano2--; } // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() { high = (nano2 >>

Программа третьего Arduino Nano

#include int nano2=0; byte high; void setup() { Wire.begin(3); // join i2c bus with address #8 Wire.onRequest(requestEvent); // register event } void loop() { delay(1500); nano2--; } // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() { high = (nano2 >> 8); high = (nano2 & 0xff); Wire.write(high); // respond with message of 2 bytes Wire.write(high); }

Отличаются последние две программы просто адресом в функции Wire.begin(3); и частотой изменения переменной.

Эти программы постоянно изменяют переменную integer и ожидают запроса от мастера. При запросе эта переменная раскладывается на два байта и отправляется как ответ на запрос ведущему контроллеру.

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

Выводы

Прекрасно все работает - цифры на дисплее меняются. Я попробовал удлиннять шлейф проводов между вторым и третьим контроллерами Arduino. Проверил работу шины связи при длине 3 м - без притензий. Длиннее не пробовал, но многие мне утверждали, что I2C не работает дальше 0,5 ... 2 м и меня воодушевила длина 3 м.

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

Я здесь ещё не попробовал передачу данных от мастера слейву. Если попробуете - отпишитесь.

Недостатков здесь на небольших расстояниях ощутимо меньше чем превосходств.

Как расширить функциональность разрабатываемой системы на основе микроконтроллера? Да, этот вопрос интересует многих схемотехников, работающими над прототипами электронных устройств. Удивительно, но добавить к системе новые блоки, не изменяя схемы, позволит шина, разработанная инженерами Philips более 30 лет назад.

Благодаря интерфейсу I2C можно превратить микроконтроллер в простой конструктор, к которому можно подключить несколько сотен микросхем. Сразу стоит отметить, что их количество ограничивается емкостью шины в 400 пФ, но это один из немногих недостатков I2C.

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

Именно эта шина устанавливается для связи с внешним миром дисплеев, камер, сотовых телефонов. Количество периферических устройств, подключаемых к устройствам с помощью I2C, вообще не поддается учету. В чем же преимущества интерфейса?

Основные достоинства и недостатки I2C

I2C – последовательная асимметричная шина для связи между интегральными схемами внутри электронных приборов. Использует две двунаправленные линии связи (SDA и SCL).

Шина представляет собой два проводника, а для управления интерфейсом достаточно одного микроконтроллера. Удивительно, но подобная простота позволяет производить отключение микросхем в процессе работы. Специальный встроенный фильтр способен справляться с всплесками, гарантируя сохранность обрабатываемой информации.

Среди недостатков I2C, кроме ограниченной емкости, сложность программирования и трудность с определением неисправности в ситуации с состоянием низкого уровня.

Изначально скорость шины была всего 100 кбит, а подключить к ней было можно всего 120 устройств. В 90-х годах стандарты изменились и скорость передачи данных увеличилась в 4 раза и появилась возможность подключения до 1000 микросхем.

Однако большинство производителей интерфейса зациклились на 400 кбит с подключением 120 устройств.

Принцип подключения и работы

Проводники шины подсоединены к плюсу резисторами 1-10к, один из проводников является шиной данных, другой – тактирование. Работает такая схема просто: на линии есть одно ведущее устройство (микроконтроллер) и несколько периферийных устройств. Так как линии запитаны на плюсе, то подключенному слейву (ведомому элементу) достаточно прижать провод к земле и передать тем самым 0.

Когда периферическое устройство отпускает провод, по проводнику передается 1. Все элементарно, но если при совместной работе один из слейвов выдал 0, то остальным подключенным к шине устройствам придется подождать. Осуществляет тактирование и передачу микроконтроллер, предварительно уточнив, свободна ли линия. Для этого передается 1 на SCL и SDA, после чего создается старт-условие – прижимается линия SDA при значении SCL равном 1.

Следующим этапом работы является передача адреса того устройства, к которому нужно обратиться.

При этом нужно помнить, что считывание данных осуществляется при SCL =1, а передача идет вперед старшим битом.

Первые 7 бит – адрес устройства, 8 – команда записать (0) или читать (1).

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

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

Режим работы multi-master

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

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



Рекомендуем почитать

Наверх