Comments 25
Проблема всех этих HALов в том, что они часто обеспечивают функционал чисто уровня "помигать лампочкой на ардуино". Яркий пример - вступил в лужу при попытке использовать I2C HAL от STM. Там тоже иногда мастер вываливался в ожидания освобождения шины. А когда покурил букварь на чипс, то в еррате оказалось, что есть такой кейс на баг в этом бите. И всё, что нужно сделать, это реализовать полноценный обработчик ошибок и, желательно, на прерывании. Восстановление простое по сути и не нарушает работу всей шины, но индусы просто "забыли" это реализовать, хотя в их же еррате это прописано. Так что при работе с ифейсом чипа я сначала пытаюсь поискать еррату на этот чипс а уже потом думать что и как сделать. Ато так ведь можно и Бога поверить от таких чудес, когда LA или осциллограф показывает идеальную картинку на физической линии а ошибка всё равно появляется.
но индусы просто "забыли" это реализовать, хотя в их же еррате это прописано.
Тем не менее Индусы посадили свой аппарат Чандраян-3 на Луну, а русский аппарат луна-25 разбился в хлам.
Ну правильно, хорошие индусы-програмисты ушли в индийское НАСА. Не осуждаю, объективные достижения достойны уважения вне зависимости от национальности, расы или чего бы там ни было.
Вполне возможно что не код был неправильный, а забитый молотком разъем вверх ногами, вследствие хронических неплатежей з.п. и прочих оптимизаций менеджеров.
/s Как невесело шутят - надо плату брать с работников за то что вообще ходят на работу. /s
Это стандартное поведение для большинства плохо написанных драйверов I2C. Нет ответа - висим в бесконечном цикле.
В аналогичной ситуации пришлось немного психануть и полностью переписать драйвер. С полной обработкой всех ситуаций и прерываний, включая тайм-аут.
Для нашего конечного автомата входами будут биты в статусных регистрах микроконтроллера.
если у вас только входных сигналов 24, а еще есть текущее состояние, какого же размера у вас получилась таблицу переходов конечного автомата? Получается размер одной стороны превышает 2^24 степени, это возможно?
сигналов 24, а еще есть текущее состояние, какого же размера у вас получилась таблицу переходов конечного автомата?
Хороший вопрос!
Всё много проще. На каждой итерации обрабатывается только один бит. В порядке убывания приоритета этого бита.
граф конечного автомата I2C мастера
на сколько я понимаю что изображено на картинке с кружочками, с такой подписью, у вас не один конечный автомат, а 8 (восемь) конечных автоматов. По моему, каждый кружочек сам по себе, конечный автомат, а еще есть логика перехода из одного конечного автомата в другой. Получается у вас объектная модель в которой базовым объектом является конечный автомат, и логика переходов по списку конечных автоматов. Не пробовали рассмотреть систему с такого ракурса?
Посмотрите на переходы между кружочками, каждый кружочек (конечный автомат) "знает-имеет" список кружочков которым он может передать управление. Конечные автоматы из разных кружочков не могут же паралелльно исполняться? В каждый момент времени работает единственный конечный автомат, но в разные моменты времени это разные конечные автоматы.
При таком рассмотрении вопрос про 2^24 разрешается сам собой. потому что 24 делится на 8 плюс состояние скажем 3 бита = 8 стейт-машин на 2^(3+3) = 2^6 переходов. Конечно + девятая логика переходов между стейт машинами, но она не сложнее одной из этих стейт машин будет. Попробуйте!
Если всё в парадигме ООП заворачивать в классы, объекты, то можно потерять детали.
(тут бы хорошо подошла картинка про бритву Оккама - https://imaginaria.ru/uploads/images/users/105/2023/04/17/c3d5e5.jpg )
Есть состояние конечного автомата, и в этом состоянии есть своя таблица, с блекджеком и стюардессами, откуда и куда идёт выход.
С формальной точки зрения, можно рассмотреть каждое состояние как отдельную машину, но зачем?
Если всё в парадигме ООП заворачивать в классы, объекты, то можно потерять детали.
Я не понимаю что имеется ввиду под словом заворачивать! По моему заворачивать ничего не надо, у вас на картинке четко различимы отдельные кружочки они и стали объектом моего внимания.
С формальной точки зрения, можно рассмотреть каждое состояние как отдельную машину, но зачем?
Получается что вы предлагаете рассматривать эти кружочки как состояния какой то супер стейт машины на 2^24 переходов между этими состояниями. Ну а я просто предложил вам рассмотреть их как стейт машины, это вопрос о том в терминах каких объектов мы рассматриваем систему не более того (состояния это то же объекты - объекты нашего внимания, как говорил классик: "Все объекты!"). Понятно что вы будете рассматривать ее так как вам привычнее, я просто предложил вам альтернативу, которая, понятно, привычной сразу не станет. На нет как говорится и суда нет.
I2C slave микросхемы имеют свойство спонтанно зависать прижимая к земле провод тактирования SCL
Такого в принципе не должно быть для slave-only устройств. Они не имеют электрических цепей способных уронить линию SCL. Эта линия для них чистый вход.
Бывает что они зависают удерживая линию SDA в нуле. Если сделаны по стандарту, то выводятся из этого состояния некоторым количеством импульсов по SCL.
На разных датчиках наблюдал зависание с удержанием линии SDA. Никогда не встречал ситуацию с удержанием SCL.
Бывает другая ситуация, когда контролер I2C не позволяет произвольно сформировать серию импульсов по SCL после зависания slave-устройства. Он фиксирует ошибку на линии, и не желает ничего отправлять, пока SDA не вернется в единицу. И если его просто перезапустить, то он сразу обнаружит линию SDA в состоянии 0, и снова впадет в ступор. В такой ситуации его никак не заставишь послать импульсы по SCL. Тогда контролер нужно выключить, сформировать импульсы по линии SCL через прямое управление GPIO, для сброса зависшего slave-устройства, и только затем инициализировать контролер.
Но как универсальное и надежное средство борьбы с зависанием устройств I2C, ваше предложение - "На чип I2C ставить аналоговый ключ для электронного пере сброса электропитания на этом чипе" - безусловно верное. Если есть возможность, то так и надо делать. Если питание выше 3 вольт, то можно использовать p-mosfet с низким напряжением переключения. В некоторых случаях можно прямо от вывода GPIO запитывать I2C устройства с низким потреблением.
Вполне могут иметь, это называется clock stretching. Используется, если устройство медленное и хочет притормозить трафик на шине, который не успевает обрабатывать.
Ну так SCL даунится на сигнале ACK - как-бы не так сложно это обработать. Да и не все слейвы поддерживают. Да и многие мастера это отслеживают аппаратно. У того-же stm32.
Как оно у китайцев реализовано - очень большой (ну еще и больной) вопрос.
Вполне могут иметь, это называется clock stretching.
Да, я был не прав в том что SCL на всех slave-only устройствах это чистый вход.
MAX41461-MAX41464 (sub-GHz transmitter) "A FIFO overflow is avoided by utilizing the I2C clock stretching mechanism. Clock stretching is done before the ACK bit." Использовал их, и даже не знал об этом свойстве.
BNO055 (gyroscope/accelerometer/magnetic sens.) использует clock stretching. Пишут о проблеме с контроллером I2C у BCM2711 Raspberry Pi 4, которая проявилась при работе с этим акселерометром (https://forums.raspberrypi.com/viewtopic.php?t=329543). Broadcom накосячил с контролером.
Однако в спецификации I2C (UM10204 I2C-bus specification and user manual Rev. 7.0) есть такой текст.
"Clock stretching is optional and in fact, most target devices do not include an SCL driver so they are unable to stretch the clock."
"For a single controller application, the controller’s SCL output can be a push-pull driver design if there are no devices on the bus which would stretch the clock."
Это слегка дезинформирует.
В общем нужно смотреть каждое устройство, опасно оно для SCL или нет.
TMP117 (temperature sensor) "SCL is the input pin, SDA is a bidirectional pin, and ALERT is the output. The TMP117 can operate as a slave receiver or slave transmitter. As a slave device, the TMP117 never drives the SCL line."
TMP1075 (temperature sensor) "The TMP1075 can operate as a receiver or transmitter. As a target device, the TMP1075 never drives the SCL line."
BMP280 (pressure sensor) точно не уронит SCL. Для него производитель даже картинку нарисовал, не поленился.
В документации на многие датчики вопрос про свойства вывода SCL совсем никак не освещается. Зато в документации на LM92 (древний термосенсор) есть описание как он завешивает линию SDA. И это даже не баг.
"An inadvertent 8-bit read from a 16-bit register, with the D7 bit low, can cause the LM92 to stop in a state where the SDA line is held low as shown in Figure 9. This can prevent any further bus communication until at least 9 additional clock cycles have occurred. Alternatively, the master can issue clock cycles until SDA goes high, at which time issuing a “Stop” condition will reset the LM92."
99.99 случаев
Если вспомнить, что I2C обязателен в составе PCIe, то бишь, используется для коммуникации между PCB, то тут явно погорячились
Тут вся статья - один большой арт-объект об оверинжиниринге, но её финализация (после перечисления всех возможных состояний, переходов и т.п.) заявлением об успехе на основании трех тривиальных тестов одной конкретной микросхемы - это вишенка.
Такой тип тестов называется интеграционными, а не модульными. Проверяется работа нескольких абсолютно разных блоков: сам драйвер, микросхема, её состояние (например, возможно, она дает разные ответы в зависимости от внешней среды). Причем, что важно, проверяется не корректность фунционального блока в его полной теоретической формальной постановке, а выполнение системой заранее заданных практических сценариев.
Модульными они были бы, если бы вы вместо реального устройства подключили бы туда, например, FPGA и проэмулировали на ней все случаи из формальной спецификации - ACK, NACK, старт, рестарт, висящая шина, clock stretching, передача длинного буфера, NACK посередине буфера, взаимодействие этого всего с DMA (и/или RTOS) и т.п.
Тогда от статьи действительно был бы какой-то смысл — ведь в интернете и без неё полно баганного кода, способного прочитать один байт из EEPROM в бесконечном цикле без какого-либо намека на таймауты или DMA.
То, что мы сейчас прочитали, - это именно то, что нужно для решения всей проблемы.
Я предлагаю Вам @makkarpov до конца месяца составить публикацию на habr о проекте драйвера.
Чтобы уже в ближайшее время можно было получить работающий драйвер I2C трансивера новым "правильным" способом.
У меня есть проект по модульному тестированию физического уровня USB-стека похожим способом (внешней FPGA с ULPI-трансивером), и по мере наличия свободного времени я его делаю. Как доделаю — обязательно выложу на хабр, только, боюсь, займет это куда больше, чем щедро выделенные вами три недели.
Похожая ситуация, кстати, возникала на STM32 и с I2C во внутреннем проекте. Если не дождаться полного завершения передачи и освобождения модуля (там есть несколько разных флагов на этот счет, взводящихся в разное время) и сразу начать новую — I2C модуль встает в странной ситуации, когда считает, что шина занята, но никаких действий не предпринимает. Причем ваши "модульные тесты" это бы не словили, потому что было нужно, чтобы сразу после записи быстро шло чтение.
Но этот I2C драйвер реализован примерно как и ваш — только те части, что реально нужны для реального устройства, потому о нем статьи на хабре я не пишу и не называю их "Конечный Автомат Аппаратного I2C Трансивера" с претензией на всеобъемность и академичность. Конечный автомат трансивера в STM32 не факт, что сами инженеры из ST нарисовать способны с доступом к исходникам. Он уж точно намного сложнее, чем ваш сферический конь в вакууме — там есть разные режимы передач, разные настройки аппаратных реакций на события, разные режимы адресации (7b/10b), автоматический отсчет переданных байт (с аппаратной реакцией на конец сообщения) и т.п. Как раз в аппаратных реакциях проблема и была — модуль рапортовал завершение передачи данных, но у него еще был аппаратный стоп, который сигнализировался другим флагом.
Потому раз уж пишите статьи — пишите их по делу, а не вводите людей в заблуждение, наваливая кучу теории, рассказывая про зависшие устройства, делая исторический экскурс вплоть до истории открытия электрона и завершая их фразой "я передал один байт и я молодец".
Я апробировал этот драйвер на двух ASICах: NAU8814 и LIS3DH. В каждом случае удавалось успешно читать/писать ячейки и сканировать адреса.
У меня есть проект по модульному тестированию физического уровня USB-стека похожим способом (внешней FPGA с ULPI-трансивером), и по мере наличия свободного времени я его делаю. Как доделаю — обязательно выложу на хабр, только, боюсь, займет это куда больше, чем щедро выделенные вами три недели.
Супер! Будет очень интересно почитать!
На чип I2C ставить аналоговый ключ для электронного пере сброса электропитания на этом чипе по GPIO от MCU. Тогда в случае зависания можно будет пере инициализировать и хоть как-то дальше работать.
Это вообще не гарантия. После отрубания питания чип может оказаться запитанным phantom-питанием и его автомат останется в зависшем состоянии.
чип может оказаться запитанным phantom-питанием
Линии выбора адреса подвязаны к питанию самой микросхемы, SDA и SCL можно прижать через GPIO в любом варианте. Остаются прочие функциональные выводы, если они у данной микросхемы есть, если они имеют защитные диоды соединенные с выводом питания, и если на эти выводы Извне приходит потенциал который нельзя погасить.
Никто не говорит что это решение подойдет для всех ситуаций, и для любых I2C устройств. Если это какой-нибудь расширитель портов, через который управляются ответственные силовые реле, то такой способ решения проблемы может быть непригоден в принципе. Нужно устранять саму возможность сбоя.
Для простых датчиков подойдет, как дополнительная мера реагирования на зависание. Понятно, что если обмен по I2C прерывается из-за зависания устройства, то надо сначала перепроверить реализацию протокола обмена с ним, на предмет ошибок. Но аппаратные меры не помешают. Если это внешний датчик на проводах, то коммутатор по линии питания как правило уже закладывается в схему, вместе с защитой по линии питания.
Конечный Aвтомат Аппаратного I2C-Трансивера