Как работает I2C Communication и как использовать его с Arduino
В этом уроке мы узнаем, как работает протокол связи I2C, а также сделаем практический пример этого с платой Arduino и датчиком, который использует этот протокол. Шина связи I2C очень популярна и широко используется многими электронными устройствами, потому что она может быть легко реализована во многих электронных конструкциях, которые требуют связи между ведущим и несколькими ведомыми устройствами или даже несколькими главными устройствами. Простая реализация обеспечивается тем, что для связи между почти 128 (112) устройствами при использовании 7-битной адресации требуется всего два провода, а при 10-битной адресации - до 1024 (1008) устройств.
Как это возможно, связь между таким количеством устройств только с проводами? Каждое устройство имеет предустановленный идентификатор или уникальный адрес устройства, поэтому мастер может выбирать, с какими устройствами будет обмениваться данными.
Два провода или линии называются последовательными часами (или SCL) и последовательными данными (или SDA). Линия SCL - это тактовый сигнал, который синхронизирует передачу данных между устройствами на шине I2C и генерируется ведущим устройством. Другая строка - это строка SDA, которая переносит данные.
Две линии являются «открытыми», что означает, что к ним необходимо подключить подтягивающие резисторы, чтобы линии были высокими, потому что устройства на шине I2C имеют активный низкий уровень. Обычно используемые значения для резисторов составляют от 2 КБ для более высоких скоростей около 400 кбит / с до 10 К для более низких скоростей около 100 кбит / с.
Сигнал данных передается в последовательностях по 8 бит. Таким образом, после возникновения особого условия запуска наступает первая 8-битовая последовательность, которая указывает адрес подчиненного устройства, на которое отправляются данные. После каждой 8-битной последовательности следует бит, называемый подтверждением. После первого бита подтверждения в большинстве случаев следует другая последовательность адресации, но на этот раз для внутренних регистров подчиненного устройства. После последовательностей адресации следует столько последовательностей данных, сколько данных полностью отправлено, и это заканчивается специальным условием остановки.
Давайте еще внимательнее посмотрим на эти события. Пусковое условие возникает, когда линия данных падает на низком уровне, а линия синхронизации все еще остается высокой. После этого часы запускаются, и каждый бит данных передается в течение каждого тактового импульса.
Последовательность адресации устройства начинается со старшего значащего бита (MSB) и заканчивается наименьшим значащим битом (LSB), и на самом деле он состоит из 7 битов, потому что 8-й бит используется для указания того, будет ли ведущий записывать данные в ведомое устройство (низкий логический уровень) ) или читать из него (логика высокая).
Следующий бит AKC / NACK используется ведомым устройством, чтобы указать, успешно ли оно приняло предыдущую последовательность битов. Таким образом, в это время ведущее устройство передает управление линией SDA подчиненному устройству, и если ведомое устройство успешно получило предыдущую последовательность, оно переводит линию SDA в состояние, называемое подтверждением. Если ведомое устройство не вытягивает линию SDA вниз, условие называется Not Acknowledge, и это означает, что он не получил успешно предыдущую последовательность, что может быть вызвано несколькими причинами. Например, ведомое устройство может быть занято, может не понимать полученные данные или команду, не может получать больше данных и так далее. В таком случае ведущее устройство решает, как оно будет действовать.
Далее идет адресация внутренних регистров. Внутренние регистры - это места в памяти ведомого, содержащие различную информацию или данные. Например, акселерометр ADX345 имеет уникальный адрес устройства и дополнительные адреса внутренних регистров для осей X, Y и Z. Поэтому, если мы хотим прочитать данные оси X, сначала нам нужно отправить адрес устройства, а затем конкретный адрес внутреннего регистра для оси X. Эти адреса можно найти в техническом описании датчика. После адресации последовательности передачи данных начинаются либо с ведущего, либо с ведомого, в зависимости от выбранного режима на бите R / W. После того, как данные полностью отправлены, передача закончится с условием остановки, которое происходит, когда линия SDA переходит от низкого к высокому, в то время как линия SCL высокая.
I2C и Arduino
В качестве примера я буду использовать коммутационную плату GY-80, которая состоит из 5 различных датчиков, и коммутационную плату GY-521, которая состоит из 3 различных датчиков. Таким образом, мы можем получить данные от 8 различных датчиков с помощью всего лишь двух проводов с шиной I2C.
• ADXL345 3-осевой ускоритель
• 2 в 1: 6-осевой гироскоп и акселерометр MPU6050
• 3 в 1: 9-осевой гироскоп ускорения магнитного поля GY-80
• 3 в 1: модуль GY-86 10DOF MS5611 HMC5883L MPU6050
Вот как мы будем соединять платы. Вывод Serial Clock платы Arduino будет подключен к выводам Serial Clock двух плат коммутации, то же самое относится и к выводам Serial Data, и мы будем питать платы с помощью разъема Gnd и 5V от платы Arduino. Обратите внимание, что здесь мы не используем подтягивающие резисторы, потому что разделительные платы уже есть.
Теперь для связи с этими чипами или датчиками нам нужно знать их уникальные адреса. Мы можем найти их из таблиц датчиков. Для платы GY-80 у нас есть следующие 4 адреса: шестнадцатеричный 0x53 для датчика 3-осевого акселерометра, шестнадцатеричный 0x69 для 3-осевого гироскопа, шестнадцатеричный 0x1E для 3-осевого магнитометра и шестнадцатеричный 0x77 для барометра и термометра датчик. Для коммутационной панели GY-521 у нас есть только один адрес, это шестнадцатеричный 0x68. Мы также можем получить или проверить адреса, используя эскиз сканера I2C, который можно найти на официальном сайте Arduino. Поэтому здесь, если мы загрузим и запустим этот эскиз, мы получим адреса подключенных устройств на шине I2C.
После того, как мы нашли адреса устройств, нам также нужно найти адреса их внутренних регистров, чтобы считывать данные с них. Например, если мы хотим считывать данные для оси X с датчика 3-осевого акселерометра платы коммутации GY-80, нам нужно найти адрес внутреннего регистра, в котором хранятся данные оси X. Из таблицы данных датчика видно, что данные для оси X на самом деле хранятся в двух регистрах: DATAX0 с шестнадцатеричным адресом 0x32 и DATAX1 с шестнадцатеричным адресом 0x33.
Исходный код
/*
* How I2C Communication Protocol Works - Example01
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
*/
#include <Wire.h>
int ADXLAddress = 0x53; // Device address in which is also included the 8th bit for selecting the mode, read in this case.
#define X_Axis_Register_DATAX0 0x32 // Hexadecima address for the DATAX0 internal register.
#define X_Axis_Register_DATAX1 0x33 // Hexadecima address for the DATAX1 internal register.
#define Power_Register 0x2D // Power Control Register
int X0,X1,X_out;
void setup() {
Wire.begin(); // Initiate the Wire library
Serial.begin(9600);
delay(100);
// Enable measurement
Wire.beginTransmission(ADXLAddress);
Wire.write(Power_Register);
// Bit D3 High for measuring enable (0000 1000)
Wire.write(8);
Wire.endTransmission();
}
void loop() {
Wire.beginTransmission(ADXLAddress); // Begin transmission to the Sensor
//Ask the particular registers for data
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission(); // Ends the transmission and transmits the data from the two registers
Wire.requestFrom(ADXLAddress,2); // Request the transmitted two bytes from the two registers
if(Wire.available()<=2) { //
X0 = Wire.read(); // Reads the data from the register
X1 = Wire.read();
}
Serial.print("X0= ");
Serial.print(X0);
Serial.print(" X1= ");
Serial.println(X1);
}
Теперь давайте создадим код, который будет получать данные для оси X. Таким образом, мы будем использовать Arduino Wire Library, которая должна быть включена в эскиз. Здесь сначала мы должны определить адрес датчика и два внутренних адреса регистров, которые мы нашли ранее. Функция Wire.begin () запускает библиотеку Wire, а также нам нужно инициировать последовательное соединение, потому что мы будем использовать Serial Monitor для отображения данных с датчика. В цикле () мы начнем с функции Wire.beginTransmission (), которая начнет передачу на конкретный датчик, в нашем случае трехосевой акселерометр. Затем с помощью функции Wire.write () мы запросим конкретные данные из двух регистров оси X. Wire.endTransmission () завершит передачу и передаст данные из регистров. Теперь с помощью функции Wire.requestFrom () мы будем запрашивать переданные данные или два байта из двух регистров. Функция Wire.available () будет возвращать количество байтов, доступных для извлечения, и если это число совпадает с нашими запрошенными байтами, в нашем случае 2 байта, используя функцию Wire.read (), мы будем считывать байты из двух регистров ось X В конце мы распечатаем данные на последовательном мониторе. Вот эти данные, но имейте в виду, что это необработанные данные, и для того, чтобы получить правильные значения оси X, необходимо выполнить некоторые математические операции. Вы можете найти более подробную информацию об этом в моем следующем уроке по использованию акселерометров с платой Arduino, потому что я не хочу перегружать этот урок, потому что его основной целью было объяснить, как работает связь I2C.
В начало обзора