клас I2C – двопровідний послідовний протокол

I2C – це двопровідний протокол для зв’язку між пристроями. На фізичному рівні він складається з 2 дротів: SCL та SDA – лінії тактування і даних відповідно.

Об’єкти I2C створюються прив’язаними до конкретної шини. Їх можна ініціалізувати під час створення або пізніше.

Виведення об’єкта I2C відображає інформацію про його конфігурацію.

Існують апаратні та програмні реалізації I2C через класи I2C та SoftI2C. Апаратний I2C використовує підтримку апаратного забезпечення системи для виконання читання/запису і зазвичай є ефективним і швидким, але може накладати обмеження на те, які виводи можна використовувати. Програмний I2C реалізовано через побітне управління і може використовуватися на будь-якому виводі, але є менш ефективним. Обидва класи мають однакові доступні методи і відрізняються насамперед способом побудови.

Примітка

Шина I2C вимагає підтягуючих резисторів на лініях SDA та SCL для роботи. Зазвичай це резистори в діапазоні від 1 до 10 кОм, підключені від кожного SDA/SCL до Vcc. Без них поведінка невизначена і може варіюватися від блокування, непередбачуваного скидання за сторожовим таймером до просто неправильних значень. Часто ці підтягуючі схеми вже вбудовані в плату мікроконтролера або плати розширення датчиків, але жодного правила щодо цього не існує. Тому, будь ласка, перевіряйте у разі виникнення проблем. Дивіться також чудовий посібник з навчання від 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 має значення true, надсилається NACK; в іншому випадку надсилається ACK (і в цьому випадку периферійний пристрій очікує, що в подальшому виклику буде зчитано більше байтів).

write(buf: bytes) int

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

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

Наступні методи реалізують стандартні операції читання та запису контролера I2C, спрямовані на заданий периферійний пристрій.

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

Прочитати nbytes з периферійного пристрою за адресою addr. Якщо stop має значення true, в кінці передачі генерується умова STOP. Повертає об’єкт bytes з прочитаними даними.

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

Зчитати в buf з периферійного пристрою за адресою addr. Кількість прочитаних байтів дорівнює довжині buf. Якщо stop має значення true, в кінці передачі генерується умова STOP.

Метод повертає None.

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

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

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

Записати байти, що містяться у vector, на периферійний пристрій за адресою addr. vector має бути кортежем або списком об’єктів з буферним протоколом. addr надсилається один раз, а потім байти з кожного об’єкта у vector записуються послідовно. Об’єкти у vector можуть мати нульову довжину в байтах, і в цьому випадку вони не вносять внесок у вихідний потік.

Якщо після запису байта з одного з об’єктів у vector отримується NACK, решта байтів і будь-які решта об’єктів не надсилаються. Якщо stop має значення true, в кінці передачі генерується умова 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.

клас SoftI2C – програмна емуляція шини I2C

Клас SoftI2C реалізує I2C через побітне управління довільними виводами 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 в Гц (фактична частота зазвичай нижча через накладні витрати циклу побітного управління).

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 має значення true, в кінці передачі генерується умова STOP.

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

Прочитати len(buf) байтів з пристрою за адресою addr у buf. Якщо stop має значення true, в кінці передачі генерується умова STOP.

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

Записати buf на пристрій за адресою addr. Передача зупиняється при першому NACK. Якщо stop має значення true, умова STOP завжди генерується в кінці передачі (навіть при ранньому NACK). Повертає кількість отриманих ACK.

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

Записати конкатенацію буферів у vector на пристрій за адресою addr як одну транзакцію. Порожні буфери ігноруються. Поводиться як writeto() щодо семантики stop і значення, що повертається.

Операції з пам’яттю

Деякі пристрої 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.