I2C или IIC (Inter-Integrated Circuit), читается «Ай-ту-Си» или «и-два-цэ» по-нашенски — последовательная шина разработана фирмой Philips Semiconductors ещё в 80-х годах прошлого века. Задумывалась, как простая 8-битная шина внутренней связи для создания управляющей электроники. Так как право на его использование стоит денег, все пользуют в свое удовольствие, называя только по другому. В Atmel его зовут TWI, но от этого ничего не меняется.
Данные передаются по двум проводам — провод данных и провод тактов. В сети есть хотя бы одно ведущее устройство (Master), которое инициализирует передачу данных и генерирует сигналы синхронизации. В сети также есть ведомые устройства (Slave), такими как гироскопы, акселерометры, датчики давления, EEPROM-память, дисплеи, которые передают данные по запросу ведущего. У каждого ведомого устройства есть уникальный адрес, по которому ведущий и обращается к нему. Адрес устройства указывается в паспорте (datasheet). К одной шине I2C может быть подключено до 127 устройств, в том числе несколько ведущих. К шине можно подключать устройства в процессе работы, т.е. она поддерживает «горячее подключение».
Pi4J предоставляет возможность работы с последовательной шиной I2C/TWI из Java на Raspberry Pi, Banana Pi, Orange Pi, Nano Pi и Odroid. Все классы и интерфейсы для инициализации и работы с шиной находятся в пакете com.pi4j.io.i2c.*;
.
Ниже представлены описания интерфейсов/классов и их методов.
Интерфейс I2CBus
Это абстракция шины I2C. Этот интерфейс позволяет шине возвращать устройство I2C.
getDevice(int)
Возвращает I2C устройство.
I2CDevice getDevice(int address) throws IOException;
Параметры
address
— адрес устройства I2C
Возвращает
I2C устройство
Бросает
IOException
— в случае, если эта шина не может вернуть I2C устройство.
getBusNumber()
int getBusNumber()
Возвращает
номер шины
close()
Закрывает шину I2C. Обычно это означает закрытие основного файла.
void close() throws IOException;
Бросает
IOException
— в случае возникновения проблем с закрытием шины I2C.
Интерфейс I2CDevice
Это абстракция устройства I2C. Он позволяет считывать или записывать данные на устройство.
getAddress()
Возвращает адрес, для которого создан этот экземпляр.
int getAddress();
write(byte)
Этот метод записывает один байт непосредственно на устройство I2C.
void write(byte b) throws IOException;
Параметры
b
— байт, который будет записан
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
write(byte[], int, int)
Этот метод записывает несколько байтов непосредственно на устройство I2C из заданного буфера при заданном смещении.
void write(byte[] buffer, int offset, int size) throws IOException;
Параметры
buffer
— буфер данных, который должен быть записан на устройство I2C за один раз
offset
— смещение в буфере
size
— количество записываемых байтов
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
write(byte[])
Этот метод записывает все байты, включенные в данный буфер, непосредственно на устройство I2C.
void write(byte[] buffer) throws IOException;
Параметры
buffer
— буфер данных, который должен быть записан на устройство I2C за один раз
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
write(int, byte)
Этот метод записывает один байт непосредственно на устройство I2C.
void write(int address, byte b) throws IOException;
Параметры
b
— байт, который будет записан
address
— локальный адрес в устройстве I2C
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
write(int, byte[], int, int)
Этот метод записывает несколько байтов непосредственно на устройство I2C из заданного буфера при заданном смещении.
void write(int address, byte[] buffer, int offset, int size) throws IOException;
Параметры
address
— локальный адрес в устройстве I2C
buffer
— буфер данных, который должен быть записан на устройство I2C за один раз
offset
— смещение в буфере
size
— количество записываемых байтов
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
write(int, byte[])
Этот метод записывает все байты, включенные в данный буфер, непосредственно на адрес регистра на устройстве I2C
void write(int address, byte[] buffer) throws IOException;
Параметры
address
— локальный адрес в устройстве I2C
buffer
— буфер данных, который должен быть записан на устройство I2C за один раз
Бросает
IOException
— в случае, если байт не может быть записан на устройство/шину I2C.
read()
Этот метод считывает один байт с устройства I2C. Результат от 0 до 255, если операция чтения была успешной, в противном случае отрицательное число ошибки.
int read() throws IOException;
Возвращает
значение байта: положительное число от 0 до 255, если чтение было успешным. Отрицательное число, если чтение не удалось.
Бросает
IOException
— в случае, если байты не могут быть прочитан с устройства/шины I2C.
read(byte[], int, int)
Этот метод считывает байты непосредственно из устройства I2C в заданный буфер при запрошенном смещении.
int read(byte[] buffer, int offset, int size) throws IOException;
Параметры
buffer
— буфер данных, который должен считываться с устройство I2C за один раз
offset
— смещение в буфере
size
— количество байт для чтения
Возвращает
количество прочитанных байтов
Бросает
IOException
— в случае, если байт не может быть прочитан с устройства/шины I2C.
read(int)
Этот метод считывает один байт с устройства I2C.
int read(int address) throws IOException;
Параметры
address
— локальный адрес в устройстве I2C
Возвращает
значение байта: положительное число от 0 до 255, если чтение было успешным. Отрицательное число, если чтение не удалось.
Бросает
IOException
— в случае, если байт не может быть прочитан с устройства/шины I2C.
read(int, byte[], int, int)
Этот метод считывает байты, начиная с заданного адреса в устройстве I2C в буфере при запрошенном смещении.
int read(int address, byte[] buffer, int offset, int size) throws IOException;
Параметры
address
— локальный адрес в устройстве I2C
buffer
— буфер данных, который должен считываться с устройство I2C за один раз
offset
— смещение в буфере
size
— количество байт для чтения
Возвращает
количество прочитанных байтов
Бросает
IOException
— в случае, если байт не может быть прочитан с устройства/шины I2C.
read(byte[], int, int, byte[], int, int)
Этот метод записывает и считывает байты в / из устройства I2C одним вызовом метода
int read(byte[] writeBuffer, int writeOffset, int writeSize, byte[] readBuffer, int readOffset, int readSize) throws IOException;
Параметры
writeBuffer
— буфер данных, который должен быть записан на устройство I2C за один раз
writeOffset
— смещение в буфере
writeSize
— количество записываемых байтов
readBuffer
— буфер данных, который должен считываться с устройство I2C за один раз
readOffset
— смещение в буфере
readSize
— количество байт для чтения
Возвращает
количество прочитанных байтов
Бросает
IOException
— в случае, если байт не может быть прочитан/записан с/на устройство/шину I2C .
ioctl(…)
Запускает ioctl на этом устройстве.
void ioctl(long command, int value) throws IOException; void ioctl(long command, ByteBuffer data, IntBuffer offsets) throws IOException;
Класс I2CConstants
Это константы, взяты непосредственно из ядра linux (i2c-dev.h i2c.h). Они должны использоваться с расширенным I2C ioctl.
Класс I2CFactory
I2C factory — он возвращает экземпляры интерфейса I2CBus
.
getInstance(int)
Создаёт новый экземпляр I2CBus
.
Тайм-аут блокировки шины для эксклюзивной связи устанавливается в DEFAULT_LOCKAQUIRE_TIMEOUT
.
public static I2CBus getInstance(int busNumber) throws UnsupportedBusNumberException, IOException
Параметры
busNumber
— номер шины.
Возвращает
новый экземпляр I2CBus
Бросает
UnsupportedBusNumberException
— если данный номер шины не поддерживается базовой системой.
IOException
— если сообщение с шиной I2C не работает.
getInstance(int, long, TimeUnit)
Создаёт новый экземпляр I2CBus
.
public static I2CBus getInstance(int busNumber, long lockAquireTimeout, TimeUnit lockAquireTimeoutUnit) throws UnsupportedBusNumberException, IOException
Параметры
busNumber
— номер шины.
lockAquireTimeout
— таймаут для блокировки шины.
lockAquireTimeoutUnit
— единицы измерения для lockAquireTimeout
.
Возвращает
новый экземпляр I2CBus
Бросает
UnsupportedBusNumberException
— если данный номер шины не поддерживается базовой системой.
IOException
— если сообщение с шиной I2C не работает.
setFactory(I2CFactoryProvider)
Позволяет изменить поставщика для фабрики.
public static void setFactory(I2CFactoryProvider factoryProvider)
Параметры
factoryProvider
— новый поставщик.
getBusIds()
Выдаёт все доступные номера шины I2C из sysfs.
public static int[] getBusIds() throws IOException
Возвращает
Возвращает найденные номера шин I2C или null
Бросает
IOException
— если извлечение из интерфейса sysfs не удалась.
Интерфейс I2CFactoryProvider
I2C factory provider — он возвращает экземпляры интерфейса I2CBus
.
getBus(int, long, TimeUnit)
Создаёт новый экземпляр I2CBus
.
I2CBus getBus(int busNumber, long lockAquireTimeout, TimeUnit lockAquireTimeoutUnit) throws UnsupportedBusNumberException, IOException;
Параметры
busNumber
— номер шины.
lockAquireTimeout
— таймаут для блокировки шины.
lockAquireTimeoutUnit
— единицы измерения для lockAquireTimeout
.
Возвращает
новый экземпляр I2CBus
Бросает
UnsupportedBusNumberException
— если данный номер шины не поддерживается базовой системой.
IOException
— если сообщение с шиной I2Cне работает.
Подключение DS3231 к Orange Pi PC по I2C
Для проверки работоспособности я взял часы реального времени DS3231, так как они первые попались под руку, ещё с этим чипом очень легко работать.
Описание регистров DS3231
Ниже в таблице представлен перечень регистров часов реального времени:
Адрес | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | Функция | Пределы |
---|---|---|---|---|---|---|---|---|---|---|
0x00 | 0 | 10 секунд | Секунды | Секунды | 00-59 | |||||
0x01 | 0 | 10 минут | Минуты | Минуты | 00-59 | |||||
0x02 | 0 | 12/24 | AM/PM | 10 часов | Час | Часы | 1-12 + AM/PM или 00-23 | |||
10 часов | ||||||||||
0x03 | 0 | 0 | 0 | 0 | 0 | День | День недели | 1-7 | ||
0x04 | 0 | 0 | 10 число | Число | Дата | 01-31 | ||||
0x05 | Century | 0 | 0 | 10 месяц | Месяц | Месяцы/век | 01-12 + Век | |||
0x06 | 10 лет | Год | Годы | 00-99 | ||||||
0x07 | A1M1 | 10 секунд | Секунды | Секунды, 1-й будильник | 00-59 | |||||
0x08 | A1M2 | 10 минут | Минуты | Минуты, 1-й будильник | 00-59 | |||||
0x09 | A1M3 | 12/24 | AM/PM | 10 часов | Час | Часы, 1-й будильник | 1-12 + AM/PM или 00-23 | |||
10 часов | ||||||||||
0x0A | A1M4 | DY/DT | 10 число | День | День недели, 1-й будильник | 1-7 | ||||
Число | Дата, 1-й будильник | 01-31 | ||||||||
0x0B | A2M2 | 10 минут | Минуты | Минуты, 2-й будильник | 00-59 | |||||
0x0C | A2M3 | 12/24 | AM/PM | 10 часов | Час | Часы, 2-й будильник | 1-12 + AM/PM или 00-23 | |||
10 часов | ||||||||||
0x0D | A2M4 | DY/DT | 10 число | День | День недели, 2-й будильник | 1-7 | ||||
Число | Дата, 2-й будильник | 01-31 | ||||||||
0x0E | EOSC | BBSQW | CONV | RS2 | RS1 | INTCN | A2IE | A1IE | Регистр настроек (Control) | |
0x0F | OSF | 0 | 0 | 0 | EN32kHz | BSY | A2F | A1F | Регистр статуса (Status) | |
0x10 | SIGN | DATA | DATA | DATA | DATA | DATA | DATA | DATA | Регистр подстройки частоты (Aging Offset) | |
0x11 | SIGN | DATA | DATA | DATA | DATA | DATA | DATA | DATA | Регистр температуры, старший байт | |
0x12 | DATA | DATA | 0 | 0 | 0 | 0 | 0 | 0 | Регистр температуры, младший байт |
Информация о времени хранится в двоично-десятичном формате, то есть каждый разряд десятичного числа (от 0 до 9) представляется группой из 4-х бит. В случае одного байта, младший полубайт отсчитывает единицы, старший десятки и т. д.
Схема подключения
Пример 1 — запись данных
I2C шина является адресной, так что каждое подключаемое устройство имеет свой адрес. Адрес устройства в шестнадцатеричной системе равен 0x68 — это 104 в десятичной. Объявляем константу:
private static final int DS3231_ADDR = 0x68;
Так как данные будут записаны только в регистры даты и времени (от 0x00 до 0x06), объявляем только их:
private static final int DS3231_TIME_SECONDS_ADDR = 0x00; private static final int DS3231_TIME_MINUTES_ADDR = 0x01; private static final int DS3231_TIME_HOURS_ADDR = 0x02; private static final int DS3231_TIME_WEEK_DAY_ADDR = 0x03; private static final int DS3231_TIME_DATE_ADDR = 0x04; private static final int DS3231_TIME_MONTH_CENTURY_ADDR = 0x05; private static final int DS3231_TIME_YEAR_ADDR = 0x06;
Поскольку мы не используем платформу Raspberry Pi, мы должны явно указывать платформу, в моём случае — это Orange Pi.
PlatformManager.setPlatform(Platform.ORANGEPI);
Чтобы работать с I2C устройствами, надо создать экземпляр I2CBus
с помощью метода getInstance
класса I2CFactory
, где параметр — это номер шины.
I2CBus i2c = I2CFactory.getInstance(I2CBus.BUS_0);
Создаём экземпляр I2CDevice
с помощью метода getDevice
, где параметр — это адрес устройства. Таким образом можно создать объект для каждого подключённого устройства.
I2CDevice device = i2c.getDevice(DS3231_ADDR);
Для записи данных на устройство используем методы write
.
device.write(DS3231_TIME_SECONDS_ADDR, decToBcd(now.getSecond()));
Ниже приведён пример программы, которая устанавливает время и дату на часах. Получить время можно с помощью LocalDateTime.now();
.
import java.time.LocalDateTime; import com.pi4j.io.i2c.I2CBus; import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CFactory; import com.pi4j.platform.Platform; import com.pi4j.platform.PlatformManager; public class I2CDS3231Write { private static final int DS3231_ADDR = 0x68; private static final int DS3231_TIME_SECONDS_ADDR = 0x00; private static final int DS3231_TIME_MINUTES_ADDR = 0x01; private static final int DS3231_TIME_HOURS_ADDR = 0x02; private static final int DS3231_TIME_WEEK_DAY_ADDR = 0x03; private static final int DS3231_TIME_DATE_ADDR = 0x04; private static final int DS3231_TIME_MONTH_CENTURY_ADDR = 0x05; private static final int DS3231_TIME_YEAR_ADDR = 0x06; public static void main(String[] args) throws Exception { PlatformManager.setPlatform(Platform.ORANGEPI); I2CBus i2c = I2CFactory.getInstance(I2CBus.BUS_0); I2CDevice device = i2c.getDevice(DS3231_ADDR); LocalDateTime now = LocalDateTime.now(); device.write(DS3231_TIME_SECONDS_ADDR, decToBcd(now.getSecond())); device.write(DS3231_TIME_MINUTES_ADDR, decToBcd(now.getMinute())); device.write(DS3231_TIME_HOURS_ADDR, decToBcd(now.getHour())); device.write(DS3231_TIME_WEEK_DAY_ADDR, decToBcd(now.getDayOfWeek().getValue())); device.write(DS3231_TIME_DATE_ADDR, decToBcd(now.getDayOfMonth())); byte century; int yearShort; if (now.getYear() >= 2000) { century = (byte) 0x80; yearShort = (now.getYear() - 2000); } else { century = 0; yearShort = (now.getYear() - 1900); } device.write(DS3231_TIME_MONTH_CENTURY_ADDR, (byte) (decToBcd(now.getMonthValue()) | century)); device.write(DS3231_TIME_YEAR_ADDR, decToBcd(yearShort)); } static byte decToBcd(int val) { return (byte) ((val / 10 * 16) + (val % 10)); } }
Проверяем код:
- создаём java файл и вставляем код;
nano I2CDS3231Write.java
- компилируем файл;
javac -classpath .:classes:/opt/pi4j/lib/'*' I2CDS3231Write.java
- запускаем программу.
sudo java -classpath .:classes:/opt/pi4j/lib/'*' I2CDS3231Write
Пример 2 — чтение данных
Для чтения данных с устройства используем методы read
.
device.read(DS3231_TIME_SECONDS_ADDR); /*...*/ device.read(DS3231_TIME_SECONDS_ADDR, buffer, 0, buffer.length);
Читаем данные температуры. Текущее значение температуры хранится в регистрах с адресами 0x11 и 0x12, старший и младший байт соответственно, значение температуры в регистрах периодически обновляется. Установлено левое выравнивание, разрешение составляет 10 бит или 0,25°C/LSB, то есть в старшем байте находится целая часть температуры, а 6, 7-й биты в младшем регистры составляют дробную часть. В старшем байте 7-й бит указывает знак температуры, например, значению 00011010 01 соответствует температура +26.25 °C, значению 11111100 10 температура -4.5 °C.
int tempMsb = device.read(DS3231_TEMPERATURE_ADDR_MSB); int tempLsb = device.read(DS3231_TEMPERATURE_ADDR_LSB) >> 6; int nint; if ((tempMsb & 0x80) != 0) { nint = tempMsb | ~((1 << 8) - 1); /* если отрицательное, получаем двоичное дополнение */ } else { nint = tempMsb; } double temperature = 0.25 * tempLsb + nint; System.out.println("t = " + temperature + " °C");
Этот пример программы считывает и выводит в консоль данные даты, времени и температуры.
import java.util.HashMap; import java.util.Map; import com.pi4j.io.i2c.I2CBus; import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CFactory; import com.pi4j.platform.Platform; import com.pi4j.platform.PlatformManager; public class I2CDS3231Read { private static final int DS3231_ADDR = 0x68; private static final int DS3231_TIME_SECONDS_ADDR = 0x00; private static final int DS3231_TIME_MINUTES_ADDR = 0x01; private static final int DS3231_TIME_HOURS_ADDR = 0x02; private static final int DS3231_TIME_WEEK_DAY_ADDR = 0x03; private static final int DS3231_TIME_DATE_ADDR = 0x04; private static final int DS3231_TIME_MONTH_CENTURY_ADDR = 0x05; private static final int DS3231_TIME_YEAR_ADDR = 0x06; private static final int DS3231_TEMPERATURE_ADDR_MSB = 0x11; private static final int DS3231_TEMPERATURE_ADDR_LSB = 0x12; public static Map<Integer, String> weekDay = new HashMap<Integer, String>() { private static final long serialVersionUID = 1193975786754897061L; { put(1, "Понедельник"); put(2, "Вторник"); put(3, "Среда"); put(4, "Четверг"); put(5, "Пятница"); put(6, "Суббота"); put(7, "Воскресенье"); } }; public static void main(String[] args) throws Exception { PlatformManager.setPlatform(Platform.ORANGEPI); I2CBus i2c = I2CFactory.getInstance(I2CBus.BUS_0); I2CDevice device = i2c.getDevice(DS3231_ADDR); int tempMsb = device.read(DS3231_TEMPERATURE_ADDR_MSB); int tempLsb = device.read(DS3231_TEMPERATURE_ADDR_LSB) >> 6; int nint; if ((tempMsb & 0x80) != 0) { nint = tempMsb | ~((1 << 8) - 1); } else { nint = tempMsb; } double temperature = 0.25 * tempLsb + nint; System.out.println("t = " + temperature + " °C"); System.out.println(String.format("%s %s/%s/%s %s:%s:%s", weekDay.get(bcdToDec(device.read(DS3231_TIME_WEEK_DAY_ADDR))), bcdToDec(device.read(DS3231_TIME_DATE_ADDR)), bcdToDec(device.read(DS3231_TIME_MONTH_CENTURY_ADDR) & 0x1F), bcdToDec(device.read(DS3231_TIME_YEAR_ADDR)) + 2000, bcdToDec(device.read(DS3231_TIME_HOURS_ADDR)), bcdToDec(device.read(DS3231_TIME_MINUTES_ADDR)), bcdToDec(device.read(DS3231_TIME_SECONDS_ADDR)))); Thread.sleep(1000); byte[] buffer = new byte[7]; device.read(DS3231_TIME_SECONDS_ADDR, buffer, 0, buffer.length); System.out.println(String.format("%s %s/%s/%s %s:%s:%s", weekDay.get(bcdToDec(buffer[3])), bcdToDec(buffer[4]), bcdToDec(buffer[5] & 0x1F), bcdToDec(buffer[6]) + 2000, bcdToDec(buffer[2]), bcdToDec(buffer[1]), bcdToDec(buffer[0]))); } static int bcdToDec(byte val) { int intVal = val & 0xFF; return ((intVal / 16 * 10) + (intVal % 16)); } static int bcdToDec(int val) { return ((val / 16 * 10) + (val % 16)); } }
Проверяем код:
- создаём java файл и вставляем код;
nano I2CDS3231Read.java
- компилируем файл;
javac -classpath .:classes:/opt/pi4j/lib/'*' I2CDS3231Read.java
- запускаем программу.
sudo java -classpath .:classes:/opt/pi4j/lib/'*' I2CDS3231Read
Результат
Как мы видим, данные успешно получены, так что Pi4J можно использовать для работы с I2C.
маркетплейс для реселлеров продать аккаунт
перепродажа аккаунтов безопасная сделка аккаунтов
Buy Account Purchase Ready-Made Accounts
Buy accounts Marketplace for Ready-Made Accounts
Account Catalog Account Sale
Account Exchange Service Secure Account Purchasing Platform
Verified Accounts for Sale Secure Account Purchasing Platform
Account Trading Verified Accounts for Sale
Social media account marketplace Website for Selling Accounts
sell account secure account sales
account marketplace website for selling accounts
accounts market buy accounts
account buying platform verified accounts for sale
account trading service buy pre-made account
account trading platform account market
account trading platform buy pre-made account
verified accounts for sale sell account
accounts marketplace accounts marketplace
website for selling accounts sell pre-made account
buy account best-social-accounts.org
account market account-buy.org
ready-made accounts for sale accounts marketplace
find accounts for sale verified accounts for sale
database of accounts for sale account selling platform
account sale buy accounts
account trading accounts market
account trading platform https://accounts-offer.org
sell account https://accounts-marketplace.xyz
account acquisition https://social-accounts-marketplaces.live/
database of accounts for sale https://accounts-marketplace.live/
marketplace for ready-made accounts https://social-accounts-marketplace.xyz
buy account buy-accounts.space
ready-made accounts for sale https://buy-accounts-shop.pro/
account market https://accounts-marketplace.art
guaranteed accounts https://social-accounts-marketplace.live
social media account marketplace https://accounts-marketplace-best.pro