Сброс зависшей шины 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 в нуле и считает шину занятой. Сам ведомый тоже не виноват — по протоколу он не может отпустить линию без тактов. Шина на открытом стоке устроена так, что один ноль блокирует всех.
Процедура
- Проверить SDA. Если она в нуле, а SCL в единице, выдавать такты на SCL по одному.
- После каждого такта смотреть, отпущена ли SDA.
- Когда SDA вернулась в единицу — сформировать STOP, чтобы все устройства сбросили конечный автомат.
- Если и после девятого такта 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, застрявшей в нуле, есть только аппаратный сброс. Поэтому у критичных датчиков полезно управлять питанием через транзистор.
Источники
- NXP. UM10204 I2C-bus specification and user manual, Rev. 7.0 (1 October 2021) — разд. 3.1.16 Bus clear: если SCL застряла в низком уровне, предпочтительно сбросить шину аппаратным сигналом сброса, если у устройств есть такой вход, иначе выключить и включить питание, чтобы сработал обязательный внутренний сброс по включению; если в низком уровне SDA, контроллер должен выдать девять тактовых импульсов — устройство, удерживающее шину, должно отпустить её за эти девять тактов; если нет — аппаратный сброс или выключение питания
- 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 в нуле эффективного способа нет, только аппаратный сброс
Нашли ошибку?
Смотрите также
- ACK и NACK в I²CДевятый такт: подтверждение байта и его отсутствие
- Открытый стокВыход только с транзистором к земле, единицу даёт подтяжка
- Растягивание такта I²CВедомое держит SCL в нуле: зачем, ограничения SMBus, совместимость
- Фильтр выбросов I²C (tSP)Входной фильтр SDA и SCL, подавляющий импульсы короче 50 нс
- I²CSDA и SCL, START/STOP, ACK, адреса, скорости, расчёт подтяжек
- Транзакция I²C по байтамБайт адреса, ACK и NACK, повторный START, диаграмма и код