I2C (Inter-Integrated Circuit)

Содержание

Введение в I2C (Inter-Integrated Circuit)Архитектурные основы I2CПочему протокол I2C так популярен?Топология "Общей шины": Как несколько устройств общаются всего по двум проводамПринцип "Открытого стока" и подтягивающих резисторовПарадигма "Ведущий-Ведомый" (Master/Slave): Роли устройств на шинеКлючевые сигналы: SDA, SCL и GNDПротокол I2C: Детальный разбор транзакцииВажность подтягивающих резисторов (Pull-Up Resistors)Фундаментальные состояния шины: START и STOPСостояние STARTСостояние STOPПовторное состояние START (Repeated START)Адресация ведомых устройств7-битные адресаБит направления (Read/Write)Механизм подтверждения: Биты ACK (Acknowledge) и NACK (Not Acknowledge)Процесс передачи и приема данных (8 бит + ACK/NACK)Запись данных (Master -> Slave)Чтение данных (Slave -> Master)Полный жизненный цикл транзакции: От START до STOPАппаратная реализация I2C на Repka PiTWI (Two-Wire Interface): I2C-контроллеры в SoC AllwinnerАппаратное ускорение: Как TWI-контроллер разгружает процессорПроблема: Программная эмуляция ("Bit-Banging")Решение: Делегирование задач аппаратному контроллеру1. Аппаратная генерация сигналов: Принцип "Записал и забыл"2. Буферы FIFO (First-In, First-Out): Снижение частоты взаимодействия3. Механизм прерываний: От активного опроса к пассивному ожиданиюДоступные I2C-шины на Repka PiРаспиновка (Pinout): Нахождение физических контактов SDA и SCLРасположение на гребенке для Repka Pi 3Расположение на гребенке для Repka Pi 4Представление в операционной системе: Файлы устройств /dev/i2c-*Архитектура регистров TWI (Низкоуровневый аспект)Обзор ключевых регистров TWI-контроллера в SoC AllwinnerTWI_ADDR (Address Register) - Адресный регистрTWI_DATA (Data Byte Register) - Регистр данныхTWI_CNTR (Control Register) - Регистр управленияTWI_STAT (Status Register) - Регистр статусаРоль драйвера i2c-dev в ядре Linux: Почему прямое управление не требуется

Введение в I2C (Inter-Integrated Circuit)

Inter-Integrated Circuit (I2C) — это синхронный последовательный протокол, разработанный компанией Philips Semiconductors (ныне NXP) для обеспечения связи между интегральными схемами на одной печатной плате. Основной задачей при его создании было упрощение схемотехники и минимизация количества выводов, необходимых для взаимодействия между микроконтроллером и различными периферийными устройствами, такими как датчики, микросхемы памяти EEPROM, часы реального времени (RTC) и другие.

В отличие от UART, который предназначен для связи "точка-точка", I2C изначально спроектирован как шинный интерфейс, что является его ключевой архитектурной особенностью.

Архитектурные основы I2C

В основе протокола лежат три фундаментальных принципа, которые определяют его структуру и функционирование:

  1. Двухпроводная шина (Two-Wire Bus): Вся коммуникация происходит всего по двум сигнальным линиям, не считая обязательной общей земли (GND):

    • SDA (Serial Data): Линия последовательных данных. Эта линия является двунаправленной и используется как для передачи данных от ведущего к ведомому, так и в обратном направлении.
    • SCL (Serial Clock): Линия тактового сигнала. Эта линия всегда управляется ведущим устройством (Master) и синхронизирует передачу каждого бита данных по линии SDA.
  2. Топология общей шины (Shared Bus Topology): К одним и тем же физическим линиям SDA и SCL могут быть параллельно подключены множество устройств. Это кардинально упрощает разводку печатной платы и подключение новых компонентов, так как не требует выделения отдельных портов для каждого устройства.

  3. Парадигма "Ведущий-Ведомый" (Master-Slave): Для организации обмена данными на общей шине протокол I2C вводит строгую иерархию ролей:

    • Ведущий (Master): Это устройство, которое инициирует сеанс связи и генерирует тактовый сигнал на линии SCL. Обычно в этой роли выступает микроконтроллер или процессор, такой как Allwinner H6 на Repka Pi.
    • Ведомый (Slave): Это периферийное устройство (например, датчик), которое "слушает" шину и отвечает только тогда, когда ведущий обращается к нему по его уникальному адресу. Каждое ведомое устройство на шине должно иметь уникальный 7-битный адрес.

Почему протокол I2C так популярен?

Широкое распространение I2C в современной электронике обусловлено рядом значительных преимуществ, вытекающих из его архитектуры:

  • Экономия выводов: Для управления десятками устройств требуется всего два вывода микроконтроллера, что критически важно для систем с ограниченным количеством портов GPIO.
  • Простота разводки платы: Меньшее количество сигнальных линий упрощает проектирование, уменьшает размеры и стоимость печатных плат.
  • Встроенная адресация: Протокол сам по себе содержит механизм адресации, что избавляет от необходимости использовать дополнительные линии выбора ведомого (Chip Select), как, например, в интерфейсе SPI.
  • Гибкость и масштабируемость: Добавление нового устройства на шину сводится к его физическому подключению к линиям SDA/SCL (при условии, что его адрес не конфликтует с уже существующими).
  • Подтверждение доставки: В протокол встроен механизм подтверждения (ACK/NACK), который позволяет ведущему устройству убедиться, что ведомое устройство успешно приняло каждый байт данных, что повышает надежность связи.
  • Стандартизация: I2C является общепринятым промышленным стандартом, что гарантирует наличие огромного экосистемы совместимых датчиков, драйверов и микросхем от разных производителей.

Топология "Общей шины": Как несколько устройств общаются всего по двум проводам

Фундаментальным отличием I2C от интерфейсов типа "точка-точка" (как UART) является его топология общей шины (shared bus). Это означает, что все устройства — один или несколько ведущих и множество ведомых — физически подключаются параллельно к одним и тем же двум сигнальным линиям: SDA и SCL.

Возникает логичный вопрос: как система избегает электрических конфликтов и хаоса в передаче данных, если несколько устройств могут потенциально пытаться управлять одной и той же линией одновременно? Ответ кроется в элегантном электротехническом решении, лежащем в основе протокола.

Принцип "Открытого стока" и подтягивающих резисторов

Все выводы SDA и SCL на I2C-совместимых устройствах реализованы по схеме "открытый сток" (Open-Drain) или "открытый коллектор". Это означает, что устройство аппаратно способно выполнять только одно действие: активно притягивать линию к земле (GND), устанавливая на ней низкий логический уровень (LOW). Устройство не может активно подавать на линию высокое напряжение (HIGH).

Для установления высокого уровня устройство просто "отпускает" линию, переводя свой вывод в высокоимпедансное состояние (практически отключаясь от нее). В этот момент в дело вступают внешние подтягивающие резисторы (pull-up resistors). Это обязательные компоненты любой I2C-шины, которые подключают каждую из линий (SDA и SCL) к линии питания (например, +3.3V).

Таким образом, состояние шины определяется следующим образом:

  • Логический LOW: Устанавливается, когда хотя бы одно из устройств на шине активно притягивает линию к земле.
  • Логический HIGH: Устанавливается только тогда, когда все устройства на шине "отпускают" ее, позволяя подтягивающим резисторам поднять напряжение до высокого уровня.

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

Парадигма "Ведущий-Ведомый" (Master/Slave): Роли устройств на шине

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

  • Ведущий (Master):

    • Роль: Инициатор и координатор связи. В типичной системе с Repka Pi именно она выступает в роли ведущего.
    • Обязанности:
      1. Инициирует транзакцию: Генерирует на шине специальное состояние START.
      2. Генерирует тактовый сигнал: Управляет всеми переключениями на линии SCL, задавая темп передачи данных.
      3. Адресует ведомого: Отправляет на шину уникальный 7-битный адрес того устройства, с которым намерен общаться.
      4. Завершает транзакцию: Генерирует состояние STOP, освобождая шину.
  • Ведомый (Slave):

    • Роль: Пассивный исполнитель. В этой роли выступают периферийные устройства (датчики, память и т.д.).
    • Обязанности:
      1. Имеет уникальный адрес: Адрес обычно жестко задан производителем микросхемы (иногда его можно частично изменить с помощью специальных выводов на плате).
      2. Пассивно прослушивает шину: Постоянно следит за линией SDA в ожидании состояния START.
      3. Распознает свой адрес: После START принимает пакет с адресом и сравнивает его со своим.
      4. Отвечает на обращение: Если адреса совпали, ведомое устройство "отзывается", участвуя в дальнейшем обмене данными до получения состояния STOP. Если адрес чужой, устройство продолжает пассивно слушать шину.

Хотя протокол допускает наличие нескольких ведущих на одной шине (Multi-Master), в подавляющем большинстве проектов с Repka Pi используется конфигурация с одним ведущим и одним или несколькими ведомыми.

Ключевые сигналы: SDA, SCL и GND

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

  • SDA (Serial Data / Линия последовательных данных):

    • Назначение: Транспортировка всех данных. По этой линии передаются стартовые и стоповые условия, 7-битные адреса, биты направления (чтение/запись), 8-битные пакеты данных и биты подтверждения.
    • Характеристика: Двунаправленная (Bi-directional). В зависимости от фазы транзакции, управлять этой линией может как ведущий (при передаче адреса и записи данных), так и ведомый (при передаче данных в режиме чтения).
  • SCL (Serial Clock / Линия тактового сигнала):

    • Назначение: Синхронизация передачи данных. Данные на линии SDA считаются стабильными и валидными только в определенные моменты времени, которые задаются сигналом на линии SCL. Как правило, состояние SDA может меняться только когда SCL находится в состоянии LOW, а считывается, когда SCL в состоянии HIGH.
    • Характеристика: Однонаправленная. В стандартной конфигурации с одним ведущим эта линия всегда управляется ведущим устройством.
  • GND (Ground / Земля):

    • Назначение: Обеспечение общего опорного уровня напряжения (0V) для всех устройств на шине.
    • Характеристика: Обязательное подключение. Без общей земли логические уровни HIGH и LOW одного устройства не будут иметь смысла для другого, что сделает любую коммуникацию невозможной.

Конечно. Мы переходим к самому сердцу протокола — к тем сигналам и правилам, которые управляют всей коммуникацией на шине. Я подготовлю детальное и технически выверенное описание этих концепций.

Протокол I2C: Детальный разбор транзакции

Любой обмен данными по шине I2C — это строго структурированная последовательность событий, называемая транзакцией. Для корректной интерпретации этих событий все устройства на шине должны следовать единому набору правил и понимать фундаментальные состояния шины. Эта глава подробно разбирает эти правила и состояния.

Важность подтягивающих резисторов (Pull-Up Resistors)

Прежде чем рассматривать сам протокол, необходимо понять ключевой электротехнический принцип, который обеспечивает его работу. Как было упомянуто ранее, все устройства на шине I2C имеют выводы типа "открытый сток" (Open-Drain). Это означает, что устройство может либо активно притягивать линию к земле (логический LOW), либо "отпускать" ее, переходя в высокоимпедансное состояние. Устройство не способно самостоятельно генерировать высокий логический уровень (HIGH).

Если бы на шине не было подтягивающих резисторов, то в "отпущенном" состоянии напряжение на линии было бы неопределенным (плавающим).

Подтягивающие резисторы — это обязательные внешние компоненты, которые подключают каждую из линий (SDA и SCL) к линии питания (например, +3.3V для Repka Pi). Их функция заключается в создании состояния по умолчанию.

  • Когда все устройства "отпускают" линию, резистор плавно "подтягивает" ее напряжение до уровня питания, устанавливая на шине стабильный логический HIGH.
  • Когда хотя бы одно устройство активно притягивает линию к земле, оно создает путь с гораздо меньшим сопротивлением, чем у резистора, в результате чего напряжение на линии падает до нуля, устанавливая стабильный логический LOW.

Этот механизм "монтажного И" (Wired-AND) не только обеспечивает стабильные логические уровни, но и является фундаментальным для предотвращения электрических конфликтов на общей шине.

Выбор номинала резистора: Номинал подтягивающих резисторов является компромиссом. Слишком высокое сопротивление (например, 50 кОм) не сможет быстро зарядить емкость шины, что ограничит максимальную скорость передачи. Слишком низкое сопротивление (например, 1 кОм) увеличит энергопотребление и может превысить способность вывода притягивать линию к земле. Типичные значения для работы с 3.3V устройствами лежат в диапазоне от 2.2 кОм до 10 кОм, с наиболее распространенным номиналом в 4.7 кОм.

Фундаментальные состояния шины: START и STOP

Для того чтобы устройства могли отличить служебные сигналы от передачи данных, в протоколе I2C определены два уникальных состояния, которые никогда не могут возникнуть во время обычной передачи битов.

Основное правило передачи данных гласит: состояние линии данных SDA может изменяться только тогда, когда линия тактового сигнала SCL находится в состоянии LOW. Когда SCL переходит в HIGH, данные на SDA должны быть стабильны для считывания.

Состояния START и STOP намеренно нарушают это правило, что и делает их уникальными.

Состояние START

  • Определение: Состояние START — это переход линии SDA с высокого уровня на низкий (HIGH -> LOW) в тот момент, когда линия SCL находится в высоком (HIGH) состоянии.
  • Назначение:
    1. Инициация транзакции: START является сигналом "Внимание!" для всех ведомых устройств на шине. Он сообщает, что сейчас ведущий начнет передачу адреса.
    2. Захват шины: После генерации START ведущий считает шину "занятой" и удерживает контроль над ней до генерации состояния STOP.

Состояние STOP

  • Определение: Состояние STOP — это переход линии SDA с низкого уровня на высокий (LOW -> HIGH) в тот момент, когда линия SCL находится в высоком (HIGH) состоянии.
  • Назначение:
    1. Завершение транзакции: STOP однозначно сигнализирует всем устройствам, что сеанс обмена данными завершен.
    2. Освобождение шины: После генерации STOP шина считается "свободной" (Idle), и любой ведущий (в системах с несколькими ведущими) может инициировать новую транзакцию.

Повторное состояние START (Repeated START)

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

Каждая I2C-транзакция всегда начинается с условия START и заканчивается условием STOP. Все, что происходит между ними, является процессом адресации и обмена данными.

Адресация ведомых устройств

После того как ведущий (Master) инициирует транзакцию, сгенерировав состояние START, его первой задачей является объявление того, с каким именно ведомым (Slave) устройством он намерен общаться. Этот процесс, называемый адресацией, является фундаментальным для работы на общей шине, где могут присутствовать десятки устройств.

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

7-битные адреса

  • Определение: Стандартный протокол I2C использует 7-битную схему адресации. Это означает, что на одной шине теоретически может находиться до 128 (2^7) уникальных ведомых устройств с адресами в диапазоне от 0x00 до 0x7F.
  • Источник адреса: Адрес не является произвольным. Он жестко задается производителем микросхемы (датчика, памяти и т.д.) и указывается в ее технической документации (datasheet).
  • Уникальность на шине: Ключевое требование для стабильной работы — все устройства, подключенные к одной I2C-шине, должны иметь уникальные адреса. Если два устройства будут иметь одинаковый адрес, они оба откликнутся на обращение ведущего, что приведет к конфликту на шине и повреждению данных (bus collision).
  • Конфигурируемые адреса: Многие производители микросхем предусматривают возможность частичного изменения адреса. На плате с датчиком для этого часто присутствуют специальные контакты (обычно обозначаемые как A0, A1, A2). "Притягивая" такой контакт к питанию (VCC) или к земле (GND), можно изменить один или несколько младших битов адреса. Это позволяет, например, подключить к одной шине несколько одинаковых датчиков, присвоив каждому из них свой уникальный адрес.

Пример: Датчик может иметь базовый адрес 0x68. Если его контакт A0 не подключен (или притянут к земле), его адрес будет 0x68. Если же контакт A0 соединить с питанием, его адрес изменится на 0x69.

  • Зарезервированные адреса: Некоторые из 128 адресов зарезервированы для специальных функций (например, адрес 0x00 для "Общего вызова" / General Call) и не должны использоваться для обычных ведомых устройств.

Бит направления (Read/Write)

Ведущий должен не только указать, к кому он обращается, но и что он собирается делать. Для этого используется восьмой бит, следующий сразу за 7-битным адресом. Этот бит называется битом Чтения/Записи (Read/Write bit).

  • R/W bit = 0 (Write / Запись):
    • Назначение: Ведущий сообщает ведомому, что сейчас он будет передавать данные. Ведомое устройство должно подготовиться к приему байтов.
  • R/W bit = 1 (Read / Чтение):
    • Назначение: Ведущий сообщает ведомому, что он хочет получить от него данные. Ведомое устройство должно подготовиться к передаче байтов.

Таким образом, полный "адресный байт", который ведущий отправляет на шину, имеет следующую структуру:

Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0
A6 A5 A4 A3 A2 A1 A0 R/W
--- --- --- --- --- --- --- ---

Практический пример: Предположим, мы хотим обратиться к датчику с 7-битным адресом 0x68.

  • В двоичной системе это 110 1000.

Для операции ЗАПИСИ (WRITE):

  1. Ведущий берет 7-битный адрес: 110 1000.
  2. Добавляет в конец бит R/W со значением 0: 110 10000.
  3. Полученный 8-битный байт (0xD0 в HEX) отправляется на шину.

Для операции ЧТЕНИЯ (READ):

  1. Ведущий берет тот же 7-битный адрес: 110 1000.
  2. Добавляет в конец бит R/W со значением 1: 110 10001.
  3. Полученный 8-битный байт (0xD1 в HEX) отправляется на шину.

Все ведомые устройства на шине принимают этот первый байт, "отрезают" последний бит, а первые семь сравнивают со своим адресом. Только то устройство, у которого адрес совпал, продолжит участвовать в транзакции. Высокоуровневые библиотеки, такие как smbus2, обычно скрывают эту сложность от пользователя: вы указываете 7-битный адрес (0x68), а библиотека сама формирует правильный 8-битный байт в зависимости от вызываемой функции (чтения или записи).

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

Механизм подтверждения: Биты ACK (Acknowledge) и NACK (Not Acknowledge)

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

После того как передатчик отправил 8-й бит байта, он "отпускает" линию SDA (переводит свой выход в высокоимпедансное состояние). Затем ведущий генерирует на линии SCL еще один, девятый по счету, тактовый импульс. В течение этого импульса управление линией SDA переходит к принимающему устройству.

  • ACK (Acknowledge / Подтверждение):

    • Механизм: Если приемник успешно принял предыдущие 8 бит, он активно притягивает линию SDA к земле (LOW) на время девятого тактового импульса.
    • Значение: ACK является для передатчика сигналом "Байт принят, все в порядке, готов к следующему".
  • NACK (Not Acknowledge / Нет подтверждения):

    • Механизм: Если приемник по какой-либо причине не может принять данные, он оставляет линию SDA в "отпущенном" (высокоимпедансном) состоянии. Подтягивающий резистор удержит линию на высоком уровне (HIGH).
    • Значение: NACK является для передатчика сигналом об ошибке или о завершении обмена. Основные причины для NACK:
      1. Устройство не существует: Ведущий отправил адрес, но ни одно ведомое устройство на шине не распознало его. В этом случае никто не притянет линию SDA к земле, и ведущий получит NACK.
      2. Устройство занято: Ведомое устройство получило адрес, но в данный момент занято выполнением внутренней операции (например, измерением) и не готово к обмену данными.
      3. Ошибка передачи: Ведомое устройство обнаружило ошибку в принятом байте.
      4. Завершение чтения (штатная ситуация): Когда ведущий считывает данные с ведомого, он сам генерирует NACK после приема последнего байта, который он хотел получить. Это является для ведомого сигналом "Спасибо, больше данных не нужно, можешь освободить шину".

Механизм ACK/NACK делает I2C гораздо более надежным, чем UART, так как он обеспечивает немедленную обратную связь о статусе доставки каждого байта.

Процесс передачи и приема данных (8 бит + ACK/NACK)

Весь обмен полезной информацией после этапа адресации происходит порциями по 8 бит (один байт), за каждой из которых следует 1 бит подтверждения.

Запись данных (Master -> Slave)

  1. Ведущий выставляет на линию SDA первый бит данных.
  2. Ведущий генерирует импульс на SCL (LOW -> HIGH -> LOW), во время которого ведомый считывает бит.
  3. Шаги 1-2 повторяются 8 раз для всех битов байта (от старшего к младшему).
  4. Ведущий генерирует 9-й тактовый импульс. В это время ведомое устройство управляет линией SDA, отправляя ACK (притягивая к LOW) или NACK (оставляя HIGH).
  5. Ведущий считывает состояние линии SDA и, если получил ACK, может начать передачу следующего байта. Если получен NACK, ведущий должен сгенерировать STOP и завершить транзакцию.

Чтение данных (Slave -> Master)

  1. Ведущий генерирует тактовый импульс на SCL (LOW -> HIGH -> LOW).
  2. Во время этого импульса ведомое устройство выставляет на линию SDA бит данных. Ведущий считывает его.
  3. Шаги 1-2 повторяются 8 раз.
  4. Ведущий генерирует 9-й тактовый импульс. В это время ведущее устройство управляет линией SDA.
    • Если ведущий хочет прочитать еще один байт, он отправляет ACK.
    • Если это был последний байт, который он хотел прочитать, он отправляет NACK, сигнализируя ведомому о завершении чтения.
  5. После отправки NACK ведущий генерирует STOP.

Полный жизненный цикл транзакции: От START до STOP

Теперь объединим все элементы в единую картину. Рассмотрим полную, типичную транзакцию записи двух байт данных в ведомое устройство.

Шаг Действие Ведущего (Master) Действие Ведомого (Slave) Состояние шины
1 Генерирует состояние START. Все устройства "просыпаются". Шина занята.
2 Отправляет 7-битный адрес + бит W (0). Сравнивает адрес со своим. Данные на SDA.
3 Генерирует 9-й импульс SCL. Если адрес совпал, отправляет ACK. SDA = LOW.
4 Получив ACK, отправляет первый байт данных. Принимает 8 бит данных. Данные на SDA.
5 Генерирует 9-й импульс SCL. Успешно приняв байт, отправляет ACK. SDA = LOW.
6 Получив ACK, отправляет второй байт данных. Принимает 8 бит данных. Данные на SDA.
7 Генерирует 9-й импульс SCL. Успешно приняв байт, отправляет ACK. SDA = LOW.
8 Получив ACK и завершив передачу, генерирует STOP. Распознает STOP, переходит в режим ожидания. Шина свободна.

Эта последовательность — START -> [АДРЕС+W] -> [ACK] -> [ДАННЫЕ1] -> [ACK] -> [ДАННЫЕ2] -> [ACK] -> STOP — является каноническим примером I2C-транзакции. Все операции, будь то простое чтение одного байта или сложная запись в EEPROM, строятся из этих фундаментальных блоков.

Аппаратная реализация I2C на Repka Pi

После детального разбора протокола I2C необходимо рассмотреть, как именно он реализован на аппаратном уровне в однокристальной системе (SoC), установленной в Repka Pi. Понимание этой реализации позволяет оценить возможности и ограничения, заложенные в "кремний".

TWI (Two-Wire Interface): I2C-контроллеры в SoC Allwinner

В технической документации на процессоры Allwinner, включая H5 и H6, используемые в Repka Pi, интерфейс, совместимый с I2C, часто упоминается под названием TWI (Two-Wire Interface).

  • Что такое TWI? TWI — это, по сути, альтернативное название для I2C-совместимого аппаратного блока. Название I2C (Inter-Integrated Circuit) является зарегистрированной торговой маркой компании NXP (ранее Philips), и чтобы избежать лицензионных отчислений, многие производители микросхем (включая Atmel/Microchip и Allwinner) используют для своих I2C-совместимых периферийных модулей название TWI.

  • Функциональная эквивалентность: Для конечного пользователя и разработчика TWI и I2C являются функционально идентичными. TWI-контроллер в SoC Allwinner полностью реализует весь протокол I2C, включая:

    • Генерацию состояний START и STOP.
    • Генерацию тактового сигнала SCL.
    • Отправку и прием 7-битных адресов и данных по линии SDA.
    • Обработку битов подтверждения ACK/NACK.
    • Поддержку стандартных скоростей (Standard Mode - 100 кбит/с, Fast Mode - 400 кбит/с).

Как и в случае с UART, TWI-контроллер является независимым аппаратным блоком на кристалле SoC. Его задача — полностью взять на себя всю сложную работу по формированию сигналов на линиях SDA и SCL в строгом соответствии с протоколом I2C. Центральный процессор (ЦП) лишь отдает высокоуровневые команды ("отправить такой-то байт по такому-то адресу") и получает результаты, не отвлекаясь на побитовую генерацию сигналов.

В блок-схеме SoC Allwinner H6 эти контроллеры обозначены как TWI x4 и расположены в секции "Connectivity", что указывает на наличие четырех независимых аппаратных TWI/I2C-контроллеров, каждый из которых может управлять своей собственной шиной I2C. Это обеспечивает значительную гибкость при проектировании сложных систем с большим количеством периферийных устройств.

Конечно. Вы правы, эти механизмы заслуживают более глубокого и детального объяснения. Я расширю этот раздел, превратив его в исчерпывающее описание принципов аппаратного ускорения, чтобы у читателя не осталось ни малейшего сомнения, как и почему это работает.

Аппаратное ускорение: Как TWI-контроллер разгружает процессор

Ключевым преимуществом использования аппаратного TWI-контроллера является его способность автономно выполнять протокольные операции, освобождая центральный процессор (ЦП) от крайне ресурсозатратных задач. Этот раздел подробно рассматривает механизмы, которые лежат в основе этой эффективности.

Проблема: Программная эмуляция ("Bit-Banging")

Чтобы оценить важность аппаратного контроллера, необходимо сначала понять, какой была бы альтернатива — программная эмуляция протокола, известная как "bit-banging". В этом сценарии ЦП пришлось бы взять на себя всю работу по формированию сигналов:

  1. Захват ресурсов: Процессор должен был бы полностью монополизировать два обычных GPIO-пина, настроенных как выходы с "открытым стоком".
  2. Побитовое формирование сигнала: Для передачи всего одного бита процессор должен был бы выполнить строгую последовательность действий:
    • SCL = LOW
    • Выставить нужный бит на SDA (HIGH или LOW)
    • SCL = HIGH (в этот момент ведомый считывает бит)
    • SCL = LOW (завершение тактового импульса)
  3. Соблюдение временных интервалов: Между этими действиями процессор должен был бы выдерживать точнейшие микросекундные задержки, чтобы соответствовать выбранной скорости шины (например, 100 кГц).
  4. Ручная обработка ACK: После отправки 8 бит ЦП должен был бы переключить пин SDA в режим входа, сгенерировать 9-й импульс на SCL и в этот момент считать состояние SDA, чтобы проверить бит подтверждения.

Такой подход не только чрезвычайно сложен в реализации, но и губителен для производительности. Во время всей транзакции процессор был бы полностью парализован, выполняя низкоуровневые, чувствительные ко времени операции. В многозадачной операционной системе, такой как Linux, где процессор постоянно переключается между десятками процессов, обеспечить стабильность таких "ручных" задержек практически невозможно.

Решение: Делегирование задач аппаратному контроллеру

Аппаратный TWI-контроллер — это, по сути, специализированный конечный автомат (Finite State Machine), который аппаратно реализует всю логику протокола I2C. Процессор не управляет линиями напрямую; он общается с этим "черным ящиком" через набор управляющих регистров, делегируя ему всю рутинную работу.

1. Аппаратная генерация сигналов: Принцип "Записал и забыл"

Когда ядро Linux (драйвер I2C) хочет отправить байт данных, оно выполняет всего две простые операции:

  1. Записывает 8-битное значение в регистр данных (TWI_DATA).
  2. Записывает команду "Начать передачу" в регистр управления (TWI_CNTR).

После этого TWI-контроллер автономно выполняет всю сложную последовательность, описанную выше: генерирует 8 тактовых импульсов на SCL, синхронно выставляет биты данных на SDA, генерирует 9-й импульс и считывает бит ACK/NACK. Весь этот процесс происходит на аппаратном уровне, параллельно с работой ЦП, который уже может заниматься другими задачами.

2. Буферы FIFO (First-In, First-Out): Снижение частоты взаимодействия

Даже с аппаратной генерацией, если бы ЦП приходилось загружать каждый байт по отдельности, это все равно требовало бы частого вмешательства. Для решения этой проблемы TWI-контроллер оснащен аппаратными буферами FIFO.

  • Что такое FIFO? Это электронная "очередь", работающая по принципу "первым пришел — первым ушел".
  • Принцип работы: Вместо одного 8-битного регистра данных у контроллера есть небольшая область памяти (например, на 16, 32 или 64 байта) для приема и для передачи.
  • Эффект: Драйвер может "забросить" в передающий (TX) FIFO сразу целую пачку байт. TWI-контроллер будет самостоятельно извлекать их из очереди и отправлять на шину один за другим. Прерывание будет сгенерировано не после каждого байта, а только тогда, когда в TX FIFO освободится достаточно места для следующей пачки данных. Аналогично, при приеме (RX) прерывание генерируется, когда в RX FIFO накопится определенное количество байт.
  • Результат: Количество прерываний, а значит, и переключений контекста для ЦП, кардинально снижается, что повышает общую пропускную способность и эффективность системы.

3. Механизм прерываний: От активного опроса к пассивному ожиданию

Последний и самый важный элемент — это взаимодействие с ЦП через прерывания, а не через опрос.

  • Что такое прерывание? Это аппаратный сигнал от периферийного устройства (TWI-контроллера) к процессору, который говорит: "Я завершил свою задачу, мне нужно твое внимание".
  • Принцип работы:
    1. TWI-контроллер завершает операцию (например, отправил всю пачку байт из TX FIFO).
    2. Он посылает электрический сигнал на общий контроллер прерываний (GIC - Generic Interrupt Controller).
    3. GIC, зная приоритеты всех устройств, немедленно приостанавливает текущее выполнение кода на одном из ядер ЦП.
    4. ЦП переходит к выполнению специальной функции-обработчика, принадлежащей драйверу I2C.
    5. Драйвер определяет, что произошло (например, "передача завершена"), выполняет необходимое действие (например, загружает следующую пачку данных в TX FIFO) и сообщает GIC, что прерывание обработано.
    6. ЦП возвращается к выполнению прерванной задачи.
  • Результат: Процессор не тратит время на постоянную проверку регистра статуса TWI_STAT. Он "просыпается" только тогда, когда его вмешательство действительно необходимо. Это и есть основа эффективной многозадачной работы, позволяющая Repka Pi одновременно общаться по I2C, обрабатывать сетевые пакеты и отрисовывать графический интерфейс без потери производительности.

Доступные I2C-шины на Repka Pi

Хотя процессор Allwinner предоставляет несколько аппаратных TWI/I2C-контроллеров, количество шин, доступных пользователю на 40-контактной гребенке, определяется схемотехникой конкретной модели платы. Инженеры-проектировщики решают, какие из этих контроллеров будут выведены наружу для общего использования.

На платах Repka Pi, как правило, для пользовательских проектов выведены одна или две независимые I2C-шины. Чаще всего они обозначаются как I2C1, I2C2 и так далее. Важно понимать, что каждая такая шина представляет собой пару контактов (SDA и SCL), подключенных к одному аппаратному TWI-контроллеру внутри SoC.

Использование разных шин позволяет:

  • Разрешать конфликты адресов: Если у вас есть два необходимых устройства с одинаковым, жестко заданным I2C-адресом, вы можете подключить их к разным шинам (I2C1 и I2C2), управляя ими как двумя независимыми системами.
  • Оптимизировать производительность: Разделение быстрых и медленных устройств по разным шинам может в некоторых случаях улучшить общую производительность системы.

Распиновка (Pinout): Нахождение физических контактов SDA и SCL

Правильная идентификация физических контактов SDA и SCL на 40-контактной гребенке является критически важным шагом при подключении I2C-устройств. Расположение этих пинов может отличаться в зависимости от модели Repka Pi.

Важное примечание: Как и в любом другом протоколе, для стабильной работы I2C обязательно необходимо соединять общую землю (GND) между Repka Pi и всеми ведомыми устройствами на шине.

Расположение на гребенке для Repka Pi 3

На Repka Pi 3 основной и наиболее часто используемый I2C-порт (I2C1) выведен на следующие физические пины:

  • SDA (I2C1_SDA): Пин 3 (BOARD)
  • SCL (I2C1_SCL): Пин 5 (BOARD)
  • GND: Любой доступный пин земли (например, Пин 6, 9 и т.д.)


Расположение на гребенке для Repka Pi 4

Для сохранения совместимости с периферийными модулями, разработанными для Raspberry Pi и других одноплатных компьютеров, на Repka Pi 4 основной I2C-порт (I2C1) расположен на тех же физических контактах.

  • SDA (I2C1_SDA): Пин 3 (BOARD)
  • SCL (I2C1_SCL): Пин 5 (BOARD)
  • GND: Любой доступный пин земли (например, Пин 6, 9 и т.д.)

На Repka Pi 4 также может быть доступна вторая шина I2C2 на пинах 27 (SDA) и 28 (SCL).



Представление в операционной системе: Файлы устройств /dev/i2c-*

Операционная система Linux предоставляет унифицированный и стандартизированный способ доступа к аппаратным I2C-шинам. После того как ядро инициализирует TWI-контроллеры, оно создает в системной директории /dev специальные файлы устройств.

Каждый такой файл представляет одну физическую I2C-шину. Обычно они именуются последовательно:

  • /dev/i2c-0
  • /dev/i2c-1
  • /dev/i2c-2
  • ...и так далее.

Соответствие между номером шины и файлом: Номер в имени файла (i2c-X) напрямую соответствует номеру I2C-шины, управляемой ядром. Например, шина I2C1 будет представлена файлом /dev/i2c-1.

Роль драйвера i2c-dev: Эти файлы-устройства не являются простым "окном" в аппаратные регистры. Взаимодействие с ними происходит через специальный драйвер ядра — i2c-dev. Когда ваша программа (или библиотека, такая как smbus2) открывает файл /dev/i2c-1 и пытается выполнить операцию чтения или записи, она на самом деле отправляет запрос этому драйверу.

Драйвер i2c-dev берет на себя всю сложность низкоуровневого управления:

  1. Принимает от программы высокоуровневую команду (например, "прочитать 1 байт с устройства по адресу 0x68 из регистра 0xD0").
  2. Транслирует эту команду в последовательность низкоуровневых операций с регистрами TWI-контроллера (генерация START, отправка адреса, отправка номера регистра, генерация Repeated START, отправка адреса с битом чтения, прием данных, генерация STOP).
  3. Управляет прерываниями и, при необходимости, DMA для эффективной передачи данных.
  4. Возвращает результат (прочитанный байт) обратно в программу.

Таким образом, файловый интерфейс /dev/i2c-* является мощной абстракцией, которая предоставляет программам в пользовательском пространстве простой и безопасный доступ к сложной аппаратной шине I2C.

Архитектура регистров TWI (Низкоуровневый аспект)

Любое взаимодействие с TWI/I2C-контроллером на самом низком уровне сводится к чтению и записи в его аппаратные регистры. Хотя в прикладном программировании эти операции полностью скрыты за абстракциями драйвера ядра, понимание назначения ключевых регистров дает полное представление о том, как работает аппаратный конечный автомат I2C.

Обзор ключевых регистров TWI-контроллера в SoC Allwinner

Ниже описаны четыре основных регистра, которые управляют полным циклом I2C-транзакции.

TWI_ADDR (Address Register) - Адресный регистр

  • Назначение: Хранение собственного 7-битного адреса, на который контроллер будет отзываться, если сам выступает в роли ведомого (Slave).
  • Принцип работы: В режиме ведущего (Master), в котором почти всегда работает Repka Pi, этот регистр, как правило, не используется. Однако он становится критически важным, если Repka Pi должна эмулировать I2C-сенсор и отвечать на запросы другого ведущего устройства.

TWI_DATA (Data Byte Register) - Регистр данных

  • Назначение: Это 8-битный "почтовый ящик" для обмена полезной информацией.
  • Принцип работы:
    • При записи (Write): Драйвер помещает в этот регистр байт, который необходимо передать на шину. TWI-контроллер автоматически забирает его и начинает последовательную передачу.
    • При чтении (Read): После успешного приема 8 бит от ведомого устройства, TWI-контроллер собирает их и помещает в этот регистр. Драйвер должен своевременно считать это значение, прежде чем оно будет перезаписано следующим принятым байтом.

TWI_CNTR (Control Register) - Регистр управления

  • Назначение: Это главный "пульт управления" транзакцией. Записью определенных битов в этот регистр драйвер отдает TWI-контроллеру команды.
  • Ключевые управляющие биты:
    • STA (START): Запись 1 в этот бит заставляет контроллер сгенерировать на шине состояние START.
    • STO (STOP): Запись 1 в этот бит заставляет контроллер сгенерировать состояние STOP, завершая транзакцию.
    • ACK (Acknowledge): Управляет генерацией бита подтверждения. Когда драйверу нужно отправить ACK после приема байта, он устанавливает этот бит. Для отправки NACK он его сбрасывает.
    • INT_EN (Interrupt Enable): Бит, который разрешает контроллеру генерировать аппаратные прерывания.
    • BUS_EN (Bus Enable): "Главный рубильник", который активирует или деактивирует сам TWI-контроллер.

TWI_STAT (Status Register) - Регистр статуса

  • Назначение: Это "индикаторная панель", которая отражает текущее состояние конечного автомата I2C. Драйвер читает этот регистр, чтобы понять, на каком этапе находится транзакция и что делать дальше.
  • Принцип работы: После каждого ключевого события на шине (например, отправлен START, принят ACK, отправлен байт) аппаратура TWI-контроллера обновляет в этом регистре 8-битный код состояния.
  • Примеры кодов состояния:
    • 0x08: "Состояние START успешно передано".
    • 0x18: "Адрес + бит W переданы, получен ACK".
    • 0x20: "Адрес + бит W переданы, получен NACK".
    • 0x28: "Байт данных передан, получен ACK".
    • 0x50: "Байт данных принят, отправлен ACK".
    • И так далее. Каждый возможный шаг транзакции имеет свой уникальный код.

Полный цикл работы драйвера — это постоянный диалог с регистрами TWI_CNTR и TWI_STAT. Драйвер дает команду через TWI_CNTR (например, "отправить START"), ждет прерывания, читает код результата в TWI_STAT, и на основе этого кода решает, какую следующую команду отправить в TWI_CNTR (например, "отправить байт данных").

Роль драйвера i2c-dev в ядре Linux: Почему прямое управление не требуется

Как видно из описания выше, ручное управление I2C-транзакцией — это сложный, многошаговый процесс, требующий постоянного анализа состояния и точной реакции. Выполнение этого из пользовательской программы было бы крайне неэффективным и опасным.

Именно поэтому драйвер i2c-dev в ядре Linux берет на себя всю эту работу.

  1. Абстракция: Драйвер предоставляет простой и мощный интерфейс для пользовательских программ через файлы /dev/i2c-*. Когда ваша Python-программа с помощью библиотеки smbus2 вызывает функцию read_byte_data(), она, по сути, отправляет ядру одну высокоуровневую команду.

  2. Реализация: Получив эту команду, драйвер i2c-dev запускает всю необходимую последовательность низкоуровневых операций:

    • Записывает бит STA в TWI_CNTR для генерации START.
    • Ждет прерывания, проверяет TWI_STAT.
    • Записывает адрес и бит W в TWI_DATA и дает команду на передачу.
    • Ждет прерывания, проверяет TWI_STAT на наличие ACK.
    • Записывает адрес внутреннего регистра устройства в TWI_DATA.
    • ...и так далее, выполняя десятки операций с регистрами для выполнения вашего одного запроса.
  3. Безопасность и многозадачность: Драйвер гарантирует, что только один процесс может работать с шиной в каждый момент времени, предотвращая конфликты. Он также эффективно использует прерывания, не "замораживая" систему во время ожидания ответа от медленных ведомых устройств.

Таким образом, хотя TWI-контроллер и управляется через регистры, в операционной системе Linux эта сложность полностью инкапсулирована внутри драйвера ядра. Это позволяет разработчикам работать с I2C на высоком уровне абстракции, не беспокоясь о деталях аппаратной реализации.


53 просмотров0 комментариев

Комментарии (0)

Для участия в обсуждении Вы должны быть авторизованным пользователем
Введение в I2C (Inter-Integrated Circuit)Архитектурные основы I2CПочему протокол I2C так популярен?Топология "Общей шины": Как несколько устройств общаются всего по двум проводамПринцип "Открытого стока" и подтягивающих резисторовПарадигма "Ведущий-Ведомый" (Master/Slave): Роли устройств на шинеКлючевые сигналы: SDA, SCL и GNDПротокол I2C: Детальный разбор транзакцииВажность подтягивающих резисторов (Pull-Up Resistors)Фундаментальные состояния шины: START и STOPСостояние STARTСостояние STOPПовторное состояние START (Repeated START)Адресация ведомых устройств7-битные адресаБит направления (Read/Write)Механизм подтверждения: Биты ACK (Acknowledge) и NACK (Not Acknowledge)Процесс передачи и приема данных (8 бит + ACK/NACK)Запись данных (Master -> Slave)Чтение данных (Slave -> Master)Полный жизненный цикл транзакции: От START до STOPАппаратная реализация I2C на Repka PiTWI (Two-Wire Interface): I2C-контроллеры в SoC AllwinnerАппаратное ускорение: Как TWI-контроллер разгружает процессорПроблема: Программная эмуляция ("Bit-Banging")Решение: Делегирование задач аппаратному контроллеру1. Аппаратная генерация сигналов: Принцип "Записал и забыл"2. Буферы FIFO (First-In, First-Out): Снижение частоты взаимодействия3. Механизм прерываний: От активного опроса к пассивному ожиданиюДоступные I2C-шины на Repka PiРаспиновка (Pinout): Нахождение физических контактов SDA и SCLРасположение на гребенке для Repka Pi 3Расположение на гребенке для Repka Pi 4Представление в операционной системе: Файлы устройств /dev/i2c-*Архитектура регистров TWI (Низкоуровневый аспект)Обзор ключевых регистров TWI-контроллера в SoC AllwinnerTWI_ADDR (Address Register) - Адресный регистрTWI_DATA (Data Byte Register) - Регистр данныхTWI_CNTR (Control Register) - Регистр управленияTWI_STAT (Status Register) - Регистр статусаРоль драйвера i2c-dev в ядре Linux: Почему прямое управление не требуется
Разделы

Навигация

ВойтиРегистрация