Перейти к содержанию

Сброс зависшей шины I²C (bus clear)

Микроконтроллер перезагрузился посреди чтения датчика, и шина I²C больше не работает: SDA прижата к земле. Датчик не виноват — он ждёт тактов, чтобы дослать байт. Спецификация I²C описывает простой способ его отпустить — до девяти тактов SCL. Разбираем, почему это работает.

Обновлено Редакция GAW

Сброс зависшей шины I²C (bus clear) — процедура восстановления шины, когда одно из устройств удерживает линию SDA в низком уровне. Спецификация I²C (NXP UM10204, раздел 3.1.16) предписывает контроллеру выдать девять тактов SCL: устройство, удерживающее шину, должно отпустить её за это время.

Откуда берётся зависание

Типичный сценарий: ведущий читает байт с датчика и в этот момент перезагружается. Ведомый успел выставить на SDA очередной бит, равный нулю, и ждёт такта, чтобы продолжить. Ведущий после перезагрузки видит SDA в нуле и считает шину занятой. Сам ведомый тоже не виноват — по протоколу он не может отпустить линию без тактов. Шина на открытом стоке устроена так, что один ноль блокирует всех.

Процедура

  1. Проверить SDA. Если она в нуле, а SCL в единице, выдавать такты на SCL по одному.
  2. После каждого такта смотреть, отпущена ли SDA.
  3. Когда SDA вернулась в единицу — сформировать STOP, чтобы все устройства сбросили конечный автомат.
  4. Если и после девятого такта SDA в нуле — нужен аппаратный сброс устройств или выключение их питания.

Девять тактов — это восемь битов байта и бит подтверждения. RP2040 выполняет эту процедуру аппаратно и в примерах показывает восстановление за 9 и за 6 тактов — в зависимости от того, сколько битов оставалось ведомому.

Если в нуле SCL

Тогда такты подать нельзя. Это может быть растяжение такта медленным ведомым, а может — зависшее устройство или замыкание. UM10204 и RP2040 сходятся: если SCL застряла в нуле, остаётся аппаратный сброс устройств или выключение их питания.

Профилактика

  • Делать сброс шины при старте программы, до первой транзакции.
  • Ограничивать время ожидания в драйвере I²C, чтобы зависание не блокировало программу.
  • Если датчик важен и не имеет входа сброса, питать его через ключ, чтобы можно было перезапустить.

Последовательность транзакции можно проверить в конструкторе транзакций I²C. Подробнее о шине — в статье I²C.

Частые вопросы

Почему для сброса шины I²C нужно именно девять тактов?

Байт на шине I²C — это восемь битов данных и бит подтверждения, всего девять тактов. Ведомый, который передавал байт, когда ведущий перезагрузился, ждёт тактов, чтобы закончить. В худшем случае ему осталось передать все девять, поэтому девяти тактов достаточно, чтобы он дошёл до конца байта и отпустил SDA. RP2040 делает именно так.

Как сбросить шину I²C, если микроконтроллер не умеет делать это аппаратно?

Временно переключить вывод SCL в режим GPIO с открытым стоком и программно выдать до девяти импульсов, проверяя после каждого, отпущена ли SDA. Когда SDA в единице, сформировать STOP: SDA в ноль при высоком SCL, затем SDA в единицу. После этого вернуть выводы модулю I²C и повторить транзакцию.

Что делать, если в нуле застряла линия SCL?

Такты здесь не помогут. UM10204 советует сбросить устройства аппаратным сигналом сброса, если у них есть такой вход, или выключить и включить им питание. RP2040 тоже указывает, что для SCL, застрявшей в нуле, есть только аппаратный сброс. Поэтому у критичных датчиков полезно управлять питанием через транзистор.

Источники

  1. NXP. UM10204 I2C-bus specification and user manual, Rev. 7.0 (1 October 2021) — разд. 3.1.16 Bus clear: если SCL застряла в низком уровне, предпочтительно сбросить шину аппаратным сигналом сброса, если у устройств есть такой вход, иначе выключить и включить питание, чтобы сработал обязательный внутренний сброс по включению; если в низком уровне SDA, контроллер должен выдать девять тактовых импульсов — устройство, удерживающее шину, должно отпустить её за эти девять тактов; если нет — аппаратный сброс или выключение питания
  2. Raspberry Pi. RP2040 Datasheet (build-date 2025-02-20) — разд. 4.3.13 Bus Clear Feature: контроллер I²C (DW_apb_i2c) поддерживает восстановление, когда SDA или SCL застряли в низком уровне; при SDA в нуле ведущий выдаёт не больше девяти тактов, число зависит от того, сколько битов осталось передать ведомому (максимум девять); ведущий пытается выставить 1 на SDA и проверяет, отпущена ли линия; если SDA освободилась — ведущий выдаёт STOP; если нет и после девятого такта — нужен аппаратный сброс; рис. 84–85 — восстановление за 9 и за 6 тактов; при SCL в нуле эффективного способа нет, только аппаратный сброс

Нашли ошибку?