- Что такое протокол связи I2C?
- Как работает связь I2C?
- Где использовать связь I2C?
- I2C в Arduino
- Необходимые компоненты
- Принципиальная электрическая схема
- Рабочее объяснение
- Программирование I2C в Arduino
- Объяснение программирования Master Arduino
- Объяснение программирования подчиненного устройства Arduino
В нашем предыдущем руководстве мы узнали о связи SPI в Arduino. Сегодня мы узнаем о другом протоколе последовательной связи: I2C (Inter Integrated Circuits). Сравнивая I2C с SPI, I2C имеет только два провода, в то время как SPI использует четыре, а I2C может иметь несколько ведущего и ведомого устройства, в то время как SPI может иметь только одно ведущее и несколько ведомых устройств. Таким образом, в проекте есть несколько микроконтроллеров, которые должны быть мастерами, тогда используется I2C. Связь I2C обычно используется для связи с гироскопом, акселерометром, датчиками атмосферного давления, светодиодными дисплеями и т. Д.
В этом руководстве по Arduino I2C мы будем использовать связь I2C между двумя платами Arduino и отправлять друг другу значения (от 0 до 127) с помощью потенциометра. Значения будут отображаться на ЖК-дисплее 16x2, подключенном к каждому из Arduino. Здесь один Arduino будет действовать как Master, а другой - как Slave. Итак, начнем с введения в коммуникацию I2C.
Что такое протокол связи I2C?
Термин IIC расшифровывается как « Inter Integrated Circuits ». Обычно он обозначается как I2C, или я в квадрате C, или даже как протокол 2-проводного интерфейса (TWI) в некоторых местах, но все это означает то же самое. I2C - это протокол синхронной связи, означающий, что оба устройства, которые обмениваются информацией, должны использовать общий тактовый сигнал. У него есть только два провода для обмена информацией, один из которых используется для сигнала петли, а другой - для отправки и приема данных.
Как работает связь I2C?
Связь I2C была впервые представлена Филлипсом. Как было сказано ранее, у него два провода, эти два провода будут подключены к двум устройствам. Здесь одно устройство называется ведущим, а другое - ведомым. Связь должна и всегда будет происходить между двумя ведущим и ведомым. Преимущество связи I2C в том, что к ведущему можно подключить более одного ведомого устройства.
Полная связь осуществляется через эти два провода, а именно последовательные часы (SCL) и последовательные данные (SDA).
Последовательные часы (SCL): разделяет тактовый сигнал, генерируемый ведущим устройством, с ведомым устройством
Последовательные данные (SDA): отправляет данные между ведущим и ведомым.
В любой момент времени только мастер может инициировать обмен данными. Поскольку на шине более одного ведомого устройства, ведущее устройство должно обращаться к каждому ведомому устройству, используя разные адреса. При обращении только ведомое устройство с этим конкретным адресом ответит с информацией, в то время как остальные продолжат выход. Таким образом, мы можем использовать одну и ту же шину для связи с несколькими устройствами.
На уровнях напряжения I2C не предопределены. Связь I2C является гибкой, это означает, что устройство, которое питается от 5 В, может использовать 5 В для I2C, а устройства 3,3 В могут использовать 3 В для связи I2C. Но что, если двум устройствам, которые работают от разных напряжений, необходимо обмениваться данными с помощью I2C? Шина I2C 5V не может быть связана с 3.3V устройства. В этом случае используются переключатели напряжения для согласования уровней напряжения между двумя шинами I2C.
Есть некоторый набор условий, которые определяют транзакцию. Инициализация передачи начинается с заднего фронта SDA, который определяется как состояние «START» на диаграмме ниже, когда ведущий оставляет SCL высоким, а SDA устанавливает низкий.
Как показано на приведенной ниже диаграмме, Задний фронт SDA - это аппаратный триггер для условия START. После этого все устройства на одной шине переходят в режим прослушивания.
Таким же образом нарастающий фронт SDA останавливает передачу, что показано как состояние «STOP» на диаграмме выше, когда ведущее устройство оставляет SCL на высоком уровне, а также отпускает SDA для перехода в HIGH. Таким образом, нарастающий фронт SDA останавливает передачу.
Бит R / W указывает направление передачи следующих байтов, если он HIGH, значит, ведомый будет передавать, а если он низкий, значит, что мастер будет передавать.
Каждый бит передается в каждом тактовом цикле, поэтому для передачи байта требуется 8 тактовых циклов. После каждого отправленного или полученного байта удерживается девятый тактовый цикл для ACK / NACK (подтвержден / не подтвержден). Этот бит ACK генерируется ведомым или ведущим устройством в зависимости от ситуации. Для бита ACK, SDA устанавливается на низкий уровень ведущим или ведомым устройством в 9- м тактовом цикле. Таким образом, он считается ACK, в противном случае - NACK.
Где использовать связь I2C?
Связь I2C используется только для связи на короткие расстояния. Он определенно надежен до некоторой степени, так как он имеет синхронизированный тактовый импульс, что делает его умным. Этот протокол в основном используется для связи с датчиком или другими устройствами, которые должны отправлять информацию мастеру. Это очень удобно, когда микроконтроллер должен взаимодействовать со многими другими подчиненными модулями, используя как минимум только провода. Если вы ищете связь на большие расстояния, вам следует попробовать RS232, а если вы ищете более надежную связь, вам следует попробовать протокол SPI.
I2C в Arduino
На изображении ниже показаны контакты I2C, присутствующие в Arduino UNO.
Линия I2C | Пин в Arduino |
ПДД | A4 |
SCL | A5 |
Прежде чем мы начнем программировать I2C с использованием двух Arduino. Нам нужно узнать о библиотеке Wire, используемой в Arduino IDE.
библиотека
1. Wire.begin (адрес):
Использование: эта библиотека используется для связи с устройствами I2C. Это инициирует библиотеку Wire и присоединяется к шине I2C как ведущее или ведомое устройство.
Адрес: 7-битный адрес ведомого устройства является необязательным, и если адрес не указан, он присоединяется к шине как ведущий, как это.
2. Wire.read ():
Использование: Эта функция используется для чтения байта, который был получен от ведущего или ведомого устройства, либо который был передан с ведомого устройства на ведущее устройство после вызова requestFrom (), либо был передан от ведущего устройства к ведомому.
3. Wire.write ():
Использование: эта функция используется для записи данных на ведомое или ведущее устройство.
От ведомого к мастеру: ведомое устройство записывает данные на ведущее устройство, когда в мастере используется Wire.RequestFrom () .
Master to Slave: для передачи от главного к подчиненному устройству Wire.write () используется между вызовами Wire.beginTransmission () и Wire.endTransmission ().
Wire.write () можно записать как:
- Wire.write (значение)
значение: значение для отправки в виде одного байта.
- Wire.write (строка):
строка: строка для отправки в виде серии байтов.
- Wire.write (данные, длина):
data: массив данных для отправки в байтах
длина: количество байтов для передачи.
4. Wire.beginTransmission (адрес):
Использование: Эта функция используется для начала передачи на устройство I2C с заданным адресом подчиненного устройства. Затем создайте очередь байтов для передачи с помощью функции write (), а затем передайте их, вызвав функцию endTransmission () . Передается 7-битный адрес устройства.
5. Wire.endTransmission ();
Использование: эта функция используется для завершения передачи на ведомое устройство, которая была начата функцией beginTransmission (), и передает байты, которые были поставлены в очередь Wire.write ().
6. Wire.onRequest ();
Использование: эта функция вызывается, когда мастер запрашивает данные с помощью Wire.requestFrom () с ведомого устройства. Здесь мы можем включить функцию Wire.write () для отправки данных мастеру.
7. Wire.onReceive ();Использование: эта функция вызывается, когда ведомое устройство получает данные от ведущего. Здесь мы можем включить Wire.read (); функция для чтения данных, отправленных от мастера.
8. Wire.requestFrom (адрес, количество);
Использование: эта функция используется ведущим устройством для запроса байтов от ведомого устройства. Функция Wire.read () используется для чтения данных, отправленных с ведомого устройства.
адрес: 7-битный адрес устройства для запроса байтов
количество: количество байтов для запроса
Необходимые компоненты
- Arduino Uno (2 шт.)
- Модуль ЖК-дисплея 16X2
- Потенциометр 10K (4 шт.)
- Макетная плата
- Подключение проводов
Принципиальная электрическая схема
Рабочее объяснение
Здесь для демонстрации связи I2C в Arduino мы используем два Arduino UNO с двумя ЖК-дисплеями 16X2, прикрепленными друг к другу, и используем два потенциометра на обоих Arduino для определения отправляемых значений (от 0 до 127) от ведущего к ведомому и ведомого к ведущему, варьируя потенциометр.
Мы берем входное аналоговое значение на выводе A0 Arduino от (0 до 5 В) с помощью потенциометра и преобразуем его в аналоговое значение в цифровое (от 0 до 1023). Затем эти значения АЦП преобразуются в (от 0 до 127), поскольку мы можем отправлять только 7-битные данные через связь I2C. Связь I2C осуществляется через два провода на контактах A4 и A5 обоих Arduino.
Значения на ЖК-дисплее ведомого Arduino будут изменены путем изменения POT на ведущей стороне и наоборот.
Программирование I2C в Arduino
В этом руководстве есть две программы: одна для ведущего Arduino, а другая - для ведомого Arduino. Полные программы для обеих сторон представлены в конце этого проекта с демонстрационным видео.
Объяснение программирования Master Arduino
1. Прежде всего, нам нужно включить библиотеку Wire для использования функций связи I2C и библиотеку LCD для использования функций LCD. Также определите контакты LCD для LCD 16x2. Узнайте больше о взаимодействии ЖК-дисплея с Arduino здесь.
#включают
2. В void setup ()
- Мы запускаем последовательную связь со скоростью 9600 бод.
Serial.begin (9600);
- Затем мы запускаем связь I2C на контакте (A4, A5)
Wire.begin (); // Начинает связь I2C на контакте (A4, A5)
- Затем мы инициализируем модуль ЖК-дисплея в режиме 16X2, отображаем приветственное сообщение и исчезаем через пять секунд.
lcd.begin (16,2); // Инициализировать ЖК-дисплей lcd.setCursor (0,0); // Устанавливает курсор в первой строке дисплея lcd.print ("Circuit Digest"); // Выводит Дайджест ЦЕПИ на ЖК-дисплей lcd.setCursor (0,1); // Устанавливает курсор во второй строке дисплея lcd.print ("I2C 2 ARDUINO"); // Выводит I2C ARDUINO в LCD delay (5000); // Задержка на 5 секунд lcd.clear (); // Очищает ЖК-дисплей
3. В пустом цикле ()
- Сначала нам нужно получить данные от ведомого устройства, поэтому мы используем requestFrom () с адресом ведомого устройства 8 и запрашиваем один байт
Wire.requestFrom (8,1);
Полученное значение считывается с помощью Wire.read ()
байт MasterReceive = Wire.read ();
- Затем нам нужно прочитать аналоговое значение с главного Arduino POT, подключенного к выводу A0.
int potvalue = analogRead (A0);
Мы преобразовываем это значение в один байт от 0 до 127.
byte MasterSend = map (potvalue, 0,1023,0,127);
- Затем нам нужно отправить эти преобразованные значения, поэтому мы начинаем передачу с ведомого Arduino с 8 адресами.
Wire.beginTransmission (8); Wire.write (MasterSend); Wire.endTransmission ();
- Затем мы отображаем полученные значения от подчиненного Arduino с задержкой в 500 микросекунд, и мы постоянно получаем и отображаем эти значения.
lcd.setCursor (0,0); // Устанавливает Currsor в первой строке LCD lcd.print (">> Master <<"); // Печатает >> Master << на ЖК-дисплее lcd.setCursor (0,1); // Устанавливает курсор во второй строке ЖК-дисплея lcd.print ("SlaveVal:"); // Выводит SlaveVal: на ЖК-дисплее lcd.print (MasterReceive); // Выводит MasterReceive на ЖК-дисплей, полученный от Slave Serial.println ("Master Received From Slave"); // Печать в последовательном мониторе Serial.println (MasterReceive); задержка (500); lcd.clear ();
Объяснение программирования подчиненного устройства Arduino
1. Как и в случае с мастером, прежде всего нам необходимо включить библиотеку Wire для использования функций связи I2C и библиотеку LCD для использования функций LCD. Также определите контакты LCD для LCD 16x2.
#включают
2. В void setup ()
- Мы запускаем последовательную связь со скоростью 9600 бод.
Serial.begin (9600);
- Затем мы запускаем связь I2C на выводе (A4, A5) с адресом ведомого как 8. Здесь важно указать адрес ведомого.
Wire.begin (8);
Затем нам нужно вызвать функцию, когда Slave получает значение от мастера и когда значение запроса Master от Slave
Wire.onReceive (receiveEvent); Wire.onRequest (requestEvent);
- Затем мы инициализируем модуль ЖК-дисплея в режиме 16X2, отображаем приветственное сообщение и исчезаем через пять секунд.
lcd.begin (16,2); // Инициализировать ЖК-дисплей lcd.setCursor (0,0); // Устанавливает курсор в первой строке дисплея lcd.print ("Circuit Digest"); // Выводит Дайджест ЦЕПИ на ЖК-дисплей lcd.setCursor (0,1); // Устанавливает курсор во второй строке дисплея lcd.print ("I2C 2 ARDUINO"); // Выводит I2C ARDUINO в LCD delay (5000); // Задержка на 5 секунд lcd.clear (); // Очищает ЖК-дисплей
3. Далее у нас есть две функции: одна для события запроса и одна для события получения.
По запросу Событие
Эта функция будет выполнена при запросе Мастером значения от ведомого. Эта функция принимает входное значение от Slave POT, преобразует его в 7-битный формат и отправляет это значение мастеру.
void requestEvent () { int potvalue = analogRead (A0); byte SlaveSend = карта (potvalue, 0,1023,0,127); Wire.write (SlaveSend); }
Для получения события
Когда мастер отправляет данные на подчиненное устройство с адресом подчиненного устройства (8), эта функция будет выполняться. Эта функция считывает полученное значение от мастера и сохраняет его в переменной типа byte .
void receiveEvent (int howMany { SlaveReceived = Wire.read (); })
4. В цикле Void ():
Мы постоянно отображаем полученное значение от мастера в модуле ЖК-дисплея.
недействительный цикл (пустота) { lcd.setCursor (0,0); // Устанавливает Currsor в первой строке LCD lcd.print (">> Slave <<"); // Выводит >> Slave << на ЖК-дисплей lcd.setCursor (0,1); // Устанавливает курсор во второй строке ЖК-дисплея lcd.print ("MasterVal:"); // Выводит MasterVal: на ЖК-дисплее lcd.print (SlaveReceived); // Выводит значение SlaveReceived на ЖК-дисплей, полученное от мастера Serial.println ("Slave Received from Master:"); // Печать в последовательном мониторе Serial.println (SlaveReceived); задержка (500); lcd.clear (); }
При вращении потенциометра с одной стороны, вы можете увидеть различные значения на ЖК - дисплее на другой стороне:
Вот как происходит связь I2C в Arduino, здесь мы использовали два Arduino, чтобы продемонстрировать не только отправку данных, но и получение данных с использованием связи I2C. Итак, теперь вы можете подключить любой датчик I2C к Arduino.
Полное кодирование для Master и Slave Arduino приведено ниже с демонстрационным видео.