class I2C – двухпроводной последовательный протокол

I2C – это двухпроводной протокол для обмена данными между устройствами. На физическом уровне он состоит из 2 проводов: SCL и SDA – линии тактирования и данных соответственно.

Объекты I2C создаются привязанными к конкретной шине. Они могут быть инициализированы при создании или позже.

Вывод объекта I2C на печать даёт информацию о его конфигурации.

Существуют как аппаратная, так и программная реализации I2C через классы I2C и SoftI2C. Аппаратный I2C использует встроенную аппаратную поддержку системы для выполнения чтения/записи и обычно эффективен и быстр, но может иметь ограничения на то, какие выводы можно использовать. Программный I2C реализован через bit-banging и может использоваться на любом выводе, но не так эффективен. Эти классы имеют одинаковый набор методов и отличаются главным образом способом создания.

Примечание

Для работы шины I2C требуются подтягивающие резисторы как на SDA, так и на SCL. Обычно это резисторы номиналом от 1 до 10 кОм, подключённые от каждой линии SDA/SCL к Vcc. Без них поведение не определено и может варьироваться от блокировки и неожиданного сброса по сторожевому таймеру до просто неверных значений. Часто такая подтягивающая схема уже встроена в плату MCU или в платы расширения с датчиками, но это не правило. Поэтому при возникновении проблем, пожалуйста, проверьте это. См. также это отличное руководство от Adafruit о подключении I2C.

Пример использования:

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

Конструкторы

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

Создаёт и возвращает новый объект I2C, используя следующие параметры:

  • id идентифицирует конкретное периферийное устройство I2C. Допустимые значения зависят от конкретного порта/платы

  • scl должен быть объектом вывода, указывающим вывод для использования в качестве SCL.

  • sda должен быть объектом вывода, указывающим вывод для использования в качестве SDA.

  • freq должен быть целым числом, задающим максимальную частоту для SCL.

  • timeout – это максимальное время в микросекундах, отводимое на транзакции I2C. Этот параметр недоступен на некоторых портах.

Обратите внимание, что некоторые порты/платы имеют значения scl и sda по умолчанию, которые можно изменить в этом конструкторе. У других значения scl и sda фиксированы и не могут быть изменены.

Общие методы

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Инициализирует шину I2C с заданными аргументами:

  • scl – это объект вывода для линии SCL

  • sda – это объект вывода для линии SDA

  • freq – это тактовая частота SCL

В случае аппаратного I2C фактическая тактовая частота может быть ниже запрошенной. Это зависит от аппаратной платформы. Фактическую частоту можно определить, выведя объект I2C на печать.

scan() List[int]

Сканирует все адреса I2C от 0x08 до 0x77 включительно и возвращает список тех, что ответили. Устройство отвечает, если оно притягивает линию SDA к низкому уровню после того, как на шину отправлен его адрес (включая бит записи).

Примитивные операции I2C

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

Эти методы доступны только в классе SoftI2C.

start() None

Генерирует условие START на шине (SDA переходит на низкий уровень, пока SCL находится на высоком уровне).

stop() None

Генерирует условие STOP на шине (SDA переходит на высокий уровень, пока SCL находится на высоком уровне).

readinto(buf: bytearray, nack: bool = True, /) None

Читает байты с шины и сохраняет их в buf. Количество прочитанных байтов равно длине buf. После приёма всех байтов, кроме последнего, на шину отправляется ACK. После приёма последнего байта, если nack истинно, отправляется NACK, иначе отправляется ACK (и в этом случае периферийное устройство предполагает, что в последующем вызове будут прочитаны ещё байты).

write(buf: bytes) int

Записывает байты из buf на шину. Проверяет, что после каждого байта получен ACK, и прекращает передачу оставшихся байтов, если получен NACK. Функция возвращает количество полученных ACK.

Стандартные операции шины

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

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

Читает nbytes из периферийного устройства, заданного addr. Если stop истинно, в конце передачи генерируется условие STOP. Возвращает объект bytes с прочитанными данными.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

Читает в buf из периферийного устройства, заданного addr. Количество прочитанных байтов будет равно длине buf. Если stop истинно, в конце передачи генерируется условие STOP.

Метод возвращает None.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Записывает байты из buf в периферийное устройство, заданное addr. Если после записи байта из buf получен NACK, то оставшиеся байты не отправляются. Если stop истинно, в конце передачи генерируется условие STOP, даже если получен NACK. Функция возвращает количество полученных ACK.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Записывает байты, содержащиеся в vector, в периферийное устройство, заданное addr. vector должен быть кортежем или списком объектов с буферным протоколом. addr отправляется один раз, после чего байты из каждого объекта в vector записываются последовательно. Объекты в vector могут иметь нулевую длину, и в этом случае они не вносят вклад в вывод.

Если после записи байта из одного из объектов в vector получен NACK, то оставшиеся байты и любые оставшиеся объекты не отправляются. Если stop истинно, в конце передачи генерируется условие STOP, даже если получен NACK. Функция возвращает количество полученных ACK.

Операции с памятью

Некоторые устройства I2C работают как устройство памяти (или набор регистров), из которого можно читать и в которое можно записывать. В этом случае с транзакцией I2C связаны два адреса: адрес периферийного устройства и адрес памяти. Следующие методы являются удобными функциями для обмена данными с такими устройствами.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

Читает nbytes из периферийного устройства, заданного addr, начиная с адреса памяти, заданного memaddr. Аргумент addrsize задаёт размер адреса в битах. Возвращает объект bytes с прочитанными данными.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Читает в buf из периферийного устройства, заданного addr, начиная с адреса памяти, заданного memaddr. Количество прочитанных байтов равно длине buf. Аргумент addrsize задаёт размер адреса в битах.

Метод возвращает None.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Записывает buf в периферийное устройство, заданное addr, начиная с адреса памяти, заданного memaddr. Аргумент addrsize задаёт размер адреса в битах.

Метод возвращает None.

class SoftI2C – программно-эмулируемая шина I2C

Класс SoftI2C реализует I2C посредством bit-banging произвольных выводов GPIO. Он предоставляет тот же набор методов, что и I2C, плюс низкоуровневые примитивные операции шины (start(), stop(), readinto(), write()) для вызывающего кода, которому нужно собрать нестандартные транзакции. Используйте его, когда нужные вам выводы не подключены к аппаратному блоку I2C, когда вам нужно больше шин, чем предоставляет аппаратура, или для общения с устройствами, требующими необычных последовательностей (дополнительные такты, повторные старты после записи и т. д.).

Конструкторы

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

Создаёт программную шину I2C, управляемую scl / sda.

freq – это целевая тактовая частота SCL в Гц (фактическая частота обычно ниже из-за накладных расходов цикла bit-banging).

timeout – это максимальное время в микросекундах ожидания растягивания тактового сигнала (когда SCL удерживается на низком уровне другим устройством на шине); по истечении вызывается OSError(ETIMEDOUT).

Общие методы

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Повторно инициализирует программную шину I2C с заданными выводами и частотой. Эквивалентно созданию нового SoftI2C на том же объекте.

scan() List[int]

Сканирует все адреса I2C от 0x08 до 0x77 включительно и возвращает список тех, что ответили.

Примитивные операции I2C

Следующие методы реализуют примитивные операции контроллера шины I2C и могут комбинироваться для составления любой транзакции I2C. Они доступны только в SoftI2C – аппаратный класс I2C их не предоставляет.

start() None

Генерирует условие START на шине (SDA переходит на низкий уровень, пока SCL находится на высоком уровне).

stop() None

Генерирует условие STOP на шине (SDA переходит на высокий уровень, пока SCL находится на высоком уровне).

readinto(buf: bytearray, nack: bool = True, /) None

Читает байты с шины в buf. Читается len(buf) байтов; после каждого байта, кроме последнего, отправляется ACK. После последнего байта nack=True (по умолчанию) отправляет NACK для завершения передачи; nack=False отправляет ACK, чтобы устройство оставалось выбранным для последующего readinto().

write(buf: bytes) int

Записывает buf на шину, проверяя ACK после каждого байта. Передача прекращается при первом NACK. Возвращает количество полученных ACK.

Стандартные операции шины

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

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

Читает nbytes из устройства с 7-битным адресом addr. Если stop истинно, в конце передачи генерируется условие STOP.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

Читает len(buf) байтов из устройства с адресом addr в buf. Если stop истинно, в конце передачи генерируется условие STOP.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Записывает buf в устройство с адресом addr. Передача прекращается при первом NACK. Если stop истинно, в конце передачи всегда генерируется условие STOP (даже при раннем NACK). Возвращает количество полученных ACK.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Записывает конкатенацию буферов из vector в устройство с адресом addr в виде одной транзакции. Пустые буферы игнорируются. В отношении семантики stop и возвращаемого значения ведёт себя как writeto().

Операции с памятью

Некоторые устройства I2C работают как устройство памяти (или набор регистров), из которого можно читать и в которое можно записывать. В этом случае с транзакцией I2C связаны два адреса: адрес периферийного устройства и адрес памяти. Следующие методы являются удобными вспомогательными средствами для общения с такими устройствами.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

Читает nbytes из устройства с адресом addr, начиная с регистра memaddr. addrsize – это ширина адреса регистра в битах (обычно 8 или 16).

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Читает в buf из устройства с адресом addr, начиная с регистра memaddr.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Записывает buf в устройство с адресом addr, начиная с регистра memaddr.