Пролог

Накануне я столкнулся с ситуацией, когда берешь uHAL драйвер от вендора микроконтроллера, собираешь его как в примерах и код не работает. Никогда такого не было, и вот опять...

На этот раз осечка в драйвере I2C трансивера. При попытке прочитать 16-битный регистр родным драйвером I2C шина за стороне slave просто берёт и зависает. Микроконтроллер показывает постоянный bus busy флаг, re-init I2C трансивера ничего не меняет. Выход один, перезагрузка по питанию всей PCB. Только это позволяет вновь хоть как-то достучаться до регистров ASIC(а).

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

Надежный I2C нужен всегда и везде. Вот лишь малый список ASIC(ов), которые управляются по I2C: at24cxx, lis3dh, bh1750, bmp180, nau8814, sa51034, ds3231, fda801, ltr390, max9860, si4703, ssd1306, wm8731 и прочие.

В связи с этим пришлось разрабатывать собственный полноценный драйвер I2C, буквально на регистрах I2C трансивера.

Постановка задачи

Написать на Си драйвер I2C в котором будут следующие функции:

  1. Проверка присутствия микросхемы по её 7-ми битному I2C адресу

bool i2c_check_addr(uint8_t num, 
                    uint8_t chip_addr);
  1. Чтение 16-битного регистра по адресу ячейки внутри ASIC(a)

bool i2c_read_word(uint8_t num, 
                   uint8_t chip_addr, 
                   uint8_t reg_addr, 
                   uint16_t* const word);
  1. Запись 16-битного слова в ASIC по его адресу на шине I2C

bool i2c_write_word(uint8_t num, 
                    uint8_t chip_addr, 
                    uint16_t word);

и тому подобное

Что надо из оборудования?

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

#

Оборудование

1

Логический анализатор Saleae Logic Pro 16 для чтения сырых сигналов на цифровой шине I2C

2

Электронная плата с микроконтроллером, который поддерживает аппаратный I2C трансивер. В моем случае это электронная плата с MCU AT32F435ZM

3

Какой-нибудь ASIC c поддержкой I2C трансивера. В моём случае это аудиокодек NAU8814

Что надо из софтвера?

Софтвер

Назначение

1

GCC ToolChain

Для сборки прошивки

2

Процессор электронных таблиц

Для составления документации по конечному автомату

3

Векторный графический редактор. Например inkscape

Чтобы нарисовать граф конечного автомата

4

Поддержка в прошивке UART-CLI

Для управления прошивкой и отладке кода.

В прошивке должен быть реализован интерфейс командной строки (TUI). Чтобы была возможность в run time запускать на исполнение произвольном порядке команды драйвера I2C трансивера,

запускать модульные тесты ассоциированные с драйвером I2C и управлять уровнями логирования, чтобы смотреть и анализировать log(и) трассировки ветвей алгоритмов внутри микроконтроллера.

Теоретический минимум

I2C - это последовательный, синхронный, цифровой, полудуплексный, двухпроводной , много-мастерный физический интерфейс с топологией общая шина. Плюс протокол канального уровня модели OSI-7. На I2C чем-то похож интерфейс MDIO, SWD. Там тоже два провода и один-тактирование. По моим наблюдениям в каждом микроконтроллере присутствует аппаратный I2C трансивер. Да не один.

Несколько фактов про I2C:

1--Биты передаются старшим битом вперед (MSB).

2--Байты передаются в формате big-endian

3--В шине I2C есть мастер и ведомый устройства. Большинство I2C ASICов это slave устройства.

4--I2C интерфейс схемотехнически это общий коллектор. Это значит, что провода должны быть подтянуты к питанию. А узлы просто по очереди прижимают шину к земле. Получается кто угодно на шине может взять и прижать шину к GND. В таком случае обмен данными будет невозможен. Ноль это доминантный бит. Такое можно встретить также в интерфейсах LIN и 1-Wire.

5--В 90% случаев I2C применяется для обмена данными между микросхемами в пределах одной электронной платы PCB. То есть это интерфейс для обмена данными между микросхемами. Нынче почти все микросхемы это программно-управляемые устройства, которые работают согласно своей конфигурации в ячейках RAM памяти.

Хотя, справедливости ради, надо отметить, что ещё I2C интерфейс присутствует в HDMI шине, которая соединяет материнскую плату и монитор.

6--I2C это полудуплексный интерфейс. То есть в один момент времени на шине может происходить либо передача, либо приём битов, но никак не одновременно.

7--I2C это протокол канального уровня, так как есть структура пакета. Протокол оперирует 7 битными адресами. Это значит, что на шине может быть до 128 микросхем. По факту каждое новое I2C устройство добавляет в шину паразитную ёмкость и общая битовая скорость из-за этого понижается.

8--Бит данных захватывается по положительному перепаду на проводе SCL. То есть в момент перехода от 0V в 3.3V.

9--I2C последовательный интерфейс. Это значит что биты передаются один за другим. Как в UART, SPI, CAN, SWD, 1-wire.

10--I2C это синхронный интерфейс. Значит, что есть тактирование как в SPI, MDIO и SWD.

11--I2C это двухпроводной интерфейс. Прям как CAN, UART.

12--I2C это цифровая общая шина.

На низком уровне I2C оперирует сигналами. Вот полный реестр сигналов I2C.

Терминология

Слово (Word)- целое неотрицательное 16-битное число. От нуля до 0xFFFF=65536

Вот шпаргалка по I2C

Вот типичная структура пакета для чтения 16-битного слова по I2C.

Реализация

Конечно-автоматная методика разработки ПО хороша тем, что она универсальна и очень хорошо формализована. Есть учебники по этой теме, которые уходят корнями в 195х-е года. Любая разработка FSM на состоит из восьми шагов.

номер

фаза

1

Перечислить состояния конечного автомата

2

Перечислить входы конечного автомата

3

Перечислить выходы конечного автомата

4

Построить таблицу переходов конечного автомата

5

Построить таблицу выходов конечного автомата

6

Нарисовать граф переходов конечного автомата

7

Написать код

8

прогнать модульные тесты

В прочем, обо всем по-порядку...

Фаза 1: Перечислить состояния

Фаза 2: Перечислить входные воздействия

Для нашего конечного автомата входами будут биты в статусных регистрах микроконтроллера. Как правило там такой список битов.

Тут сразу стоит отметить, что не все флаги статуса обладают одинаковым приоритетом. Достаточно часто будет происходить такая ситуация, что на данной итерации FSM произошло сразу два события! В этом случае надо выбирать в обработку то событие, у которого выше приоритет. Получается что на каждой итерации обрабатывается только один бит. В порядке убывания приоритета этого бита.

Иначе бы пришлось обрабатывать 2^24=16 777 216 входных состояний, что конечно же, перебор...

Фаза 3: Перечислить выходы конечного автомата

То есть сказать, что должен делать конечный автомат в ответ на стимулы извне. Перечислить легальные действия

Фаза 4 и 5: Построить таблицы переходов.

Таблицы переходов это двухмерные матрицы которые показывают в какое состояние надо переходить в зависимости от нынешнего состояния и входного сигнала. Для данного конечного автомата таблица будет весьма громоздкая, поэтому я составлю её в Google Spreadsheets.

фрагмент таблицы переходов
фрагмент таблицы переходов

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

Фаза 6: Изобразить граф конечного автомата

Как говорит пословица: "Картинка стоит тысячи слов." В самом упрощенном виде граф конечного автомата I2C мастера может выглядеть так.

граф конечного автомата I2C мастера
граф конечного автомата I2C мастера

Я нарисовал его в программе inkscape.

Фаза 7: Написать код

В целом, обладая перечисленной документацией код уже может написать абсолютно любой программист-микроконтроллеров. Исходники это не код на Си. Исходники - это документация: таблицы, формулы и схемы.

Достоинства реализации драйвера I2C на основе конечного автомата:

1++ У вас один код цикла для всех функций: чтение, запись, сканирование. Изменение в одном месте автоматически раскатывается на все операции. Нет дублирования кода как такового. В результате реализация получается лаконичная.

2++ Разработка через FSM хороша тем, что алгоритм сам составляется по мере отладки. Даже если изначально решение абсолютно неочевидное. При использовании методологии конечных автоматов алгоритм уточняется по мере получения красных модульных тестов.

3++ Обрабатываются все варианты использования алгоритма, как штатные, так и не штатные ситуации. FSM алгоритмы самые надежные. Безотказные, как автомат Калашникова.

4++ Для всех комбинаций возможных входных воздействий и нынешних состояний есть алгоритм для работы. Нет никаких исключительных ситуаций.

5++ Когда драйвер I2C реализован в виде конечного автомата то ему даже прерывания не нужны. Всё можно реализовать в одном супер цикле с выходом по таймауту.

6++ Код написанный на конечно автоматной методологии очень легко отлаживать. Достаточно только включить повыше уровень логирования и проанализировать цветной лог. Даже нет нужды в пошаговой отладке как таковой.

7++ Код написанный по FSM очень легко масштабировать. Достаточно добавить новые состояния и появится новый функционал.

Отладка автомата I2C

Отлаживать код - это то же, что работать детективом. Тут надо тщательнейшим образом анализировать логи в UART-CLI. Как водится, первым делом просканируем шину.

  1. Сканирование шины I2C это фундаментальная операция. Она показывает что плата пропаяна, что есть питание на микросхемах, что есть подтягивающие резисторы. Сканирование это smoke тест. Опа! Видим знакомый символ 0x19. Это как раз LIS3DH.

    А тут видим знакомый символ 0x1a. Это как раз NAU8814. Второе число это то же самое что 0x1a. Дело в том, что адрес семи битный, а алгоритм перебирает 8-битные числа. Вот старший бит и отбрасывается.

Это лог сканирования для существующего адреса 0x19.

  1. Чтение слова. Это осциллограмма для чтения 16 бит регистра по I2C.

Лог операции чтения слова:

  1. Запись 9-битного регистра по 7-ми битному адресу. По сути запись 16бит слова по I2C.

Лог записи регистра в виде отправки слова по шине I2C

Все операции работают как часы. Модульные тесты проходят.

Я апробировал этот драйвер на двух ASICах: NAU8814 и LIS3DH. В каждом случае удавалось успешно читать/писать ячейки и сканировать адреса.

Тут надо отметить, что я написал работающий драйвер I2C трансивера и даже ни разу не запускал пошаговую GDB отладку по SWD. Всё, что надо я получал от UART-CLI в виде логов после активации более глубокого логирования от разных программных компонентов: I2C, I2C_FSM, GPIO, TEST и прочих. Как оказалось, Shell очень удружил при разработке HAL.

Пару слов про опыт работы с I2C интерфейс

Как можно заметить, драйвер интерфейса I2C это весьма сложная вещь. Надо следить за тем, чтобы шина не зависла. Работать с I2C надо предельно аккуратно. Программировать I2C трансивер - это как ходить двум параллельно натянутым канатам. Шаг влево или шаг вправо и происходит bus error или bus busy. И привет... Именно по этому I2C - самый ненадёжный проводной интерфейс!

I2C slave микросхемы имеют свойство спонтанно зависать прижимая к земле провод тактирования SCL!

В таких случаях на своей стороне мастер ничего не может сделать, чтобы хоть как-то распетлять ситуацию. Происходить окончательная потеря link(а)... Вот такие вот пирожки с капустой... Понимаете?...

В связи с этим, в особенно ответственных электронных устройствах (automotive, aerospace, military области) я бы рекомендовал следующие схемотехнические меры:

1- Вообще не использовать I2C. Как правило, каждый I2C совместимый чип помимо I2C поддерживает ещё и SPI режим. Примерами таких ASICов являются LIS3DH и NAU8814. В таких случаях надо отдавать предпочтение именно SPI интерфейсу. Легче будет поддерживать этот прибор. В SPI в принципе нечему ломаться.

2- На чип I2C ставить аналоговый ключ для электронного пере сброса электропитания на этом чипе по GPIO от MCU. Тогда в случае зависания можно будет пере инициализировать и хоть как-то дальше работать.

Итоги

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

Конечный автомат также подойдет для программной реализации любого другого протокола канального уровня, где есть ярко выраженная структура пакета: 1-Wire, LIN.

Применяйте конечные автоматы в разработке system software для микроконтроллеров. В этом нет абсолютно ничего сложного.

Надеюсь, что этот текст поможет и другим программистам микроконтроллеров в покорении I2C интерфейса на разных микроконтроллерах.

Словарь

Акроним

Расшифровка

PCB

Printed circuit board

ASIC

Application-specific integrated circuit

I2C

Inter-Integrated Circuit

FSM

Finite-state machine

GDB

GNU DeBugger

GNU

"GNU's Not Unix!

Ссылки

Конечные автоматы - это настолько универсальный шаблон проектирования программного обеспечения, что коллекция его применения в моём опыте насчитывает уже более 9-ти случаев. FSM - золотой молоток!

Гиперссылка

URL

1

Аналитика по I2C

https://docs.google.com/spreadsheets/d/1g7YgtQlS0IxCyJhToEgJ9Bsoc-LIWk6CnQUHOfSLMq4/edit?gid=647651430#gid=647651430

3

Saleae Logic Pro 16

https://www.saleae.com/products/saleae-logic-pro-16

2

Создаем I2C Master Controller на Verilog. Логический уровень

https://habr.com/ru/companies/timeweb/articles/753076/

4

Запуск I2S Трансивера на Artery [часть 2] (DMA, FSM, PipeLine)

https://habr.com/ru/articles/834304/

5

Cross-Detect для Проверки Качества Пайки в Электронных Цепях

https://habr.com/ru/articles/762142/

6

Синтаксический разбор CSV строчек

https://habr.com/ru/articles/765066/

7

Принцип Определения Дальности Между UWB Трансиверами (Конечный Автомат Для DS-TWR)

https://habr.com/ru/articles/723822/

8

H-мост: Load Detect (или как выявлять вандализм)

https://habr.com/ru/articles/709374/

9

Техникум: Конечный Aвтомат Обработки Сигнала с Кнопки

https://habr.com/ru/articles/760088/

10

Техникум: Распознавание Вещественного Числа из Строчки

https://habr.com/ru/articles/757122/

11

Load-Detect для Проверки Качества Пайки

https://habr.com/ru/articles/756572/

12

Задача про две ёмкости для жидкости

https://habr.com/ru/articles/662561/

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


  1. HardWrMan
    09.11.2024 05:38

    Проблема всех этих HALов в том, что они часто обеспечивают функционал чисто уровня "помигать лампочкой на ардуино". Яркий пример - вступил в лужу при попытке использовать I2C HAL от STM. Там тоже иногда мастер вываливался в ожидания освобождения шины. А когда покурил букварь на чипс, то в еррате оказалось, что есть такой кейс на баг в этом бите. И всё, что нужно сделать, это реализовать полноценный обработчик ошибок и, желательно, на прерывании. Восстановление простое по сути и не нарушает работу всей шины, но индусы просто "забыли" это реализовать, хотя в их же еррате это прописано. Так что при работе с ифейсом чипа я сначала пытаюсь поискать еррату на этот чипс а уже потом думать что и как сделать. Ато так ведь можно и Бога поверить от таких чудес, когда LA или осциллограф показывает идеальную картинку на физической линии а ошибка всё равно появляется.


    1. aabzel Автор
      09.11.2024 05:38

      но индусы просто "забыли" это реализовать, хотя в их же еррате это прописано.

      Тем не менее Индусы посадили свой аппарат Чандраян-3 на Луну, а русский аппарат луна-25 разбился в хлам.


      1. HardWrMan
        09.11.2024 05:38

        Ну правильно, хорошие индусы-програмисты ушли в индийское НАСА. Не осуждаю, объективные достижения достойны уважения вне зависимости от национальности, расы или чего бы там ни было.


      1. strvv
        09.11.2024 05:38

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


  1. DanilinS
    09.11.2024 05:38

    Это стандартное поведение для большинства плохо написанных драйверов I2C. Нет ответа - висим в бесконечном цикле.

    В аналогичной ситуации пришлось немного психануть и полностью переписать драйвер. С полной обработкой всех ситуаций и прерываний, включая тайм-аут.


    1. megalloid
      09.11.2024 05:38

      Если есть возможность и желание - поделитесь ссылкой на код :)


  1. rukhi7
    09.11.2024 05:38

    Для нашего конечного автомата входами будут биты в статусных регистрах микроконтроллера.

    если у вас только входных сигналов 24, а еще есть текущее состояние, какого же размера у вас получилась таблицу переходов конечного автомата? Получается размер одной стороны превышает 2^24 степени, это возможно?


    1. aabzel Автор
      09.11.2024 05:38

       сигналов 24, а еще есть текущее состояние, какого же размера у вас получилась таблицу переходов конечного автомата?

      Хороший вопрос!

      Всё много проще. На каждой итерации обрабатывается только один бит. В порядке убывания приоритета этого бита.


      1. rukhi7
        09.11.2024 05:38

        граф конечного автомата I2C мастера

        на сколько я понимаю что изображено на картинке с кружочками, с такой подписью, у вас не один конечный автомат, а 8 (восемь) конечных автоматов. По моему, каждый кружочек сам по себе, конечный автомат, а еще есть логика перехода из одного конечного автомата в другой. Получается у вас объектная модель в которой базовым объектом является конечный автомат, и логика переходов по списку конечных автоматов. Не пробовали рассмотреть систему с такого ракурса?

        Посмотрите на переходы между кружочками, каждый кружочек (конечный автомат) "знает-имеет" список кружочков которым он может передать управление. Конечные автоматы из разных кружочков не могут же паралелльно исполняться? В каждый момент времени работает единственный конечный автомат, но в разные моменты времени это разные конечные автоматы.

        При таком рассмотрении вопрос про 2^24 разрешается сам собой. потому что 24 делится на 8 плюс состояние скажем 3 бита = 8 стейт-машин на 2^(3+3) = 2^6 переходов. Конечно + девятая логика переходов между стейт машинами, но она не сложнее одной из этих стейт машин будет. Попробуйте!


        1. strvv
          09.11.2024 05:38

          Если всё в парадигме ООП заворачивать в классы, объекты, то можно потерять детали.
          (тут бы хорошо подошла картинка про бритву Оккама - https://imaginaria.ru/uploads/images/users/105/2023/04/17/c3d5e5.jpg )
          Есть состояние конечного автомата, и в этом состоянии есть своя таблица, с блекджеком и стюардессами, откуда и куда идёт выход.
          С формальной точки зрения, можно рассмотреть каждое состояние как отдельную машину, но зачем?


          1. rukhi7
            09.11.2024 05:38

            Если всё в парадигме ООП заворачивать в классы, объекты, то можно потерять детали.

            Я не понимаю что имеется ввиду под словом заворачивать! По моему заворачивать ничего не надо, у вас на картинке четко различимы отдельные кружочки они и стали объектом моего внимания.

            С формальной точки зрения, можно рассмотреть каждое состояние как отдельную машину, но зачем?

            Получается что вы предлагаете рассматривать эти кружочки как состояния какой то супер стейт машины на 2^24 переходов между этими состояниями. Ну а я просто предложил вам рассмотреть их как стейт машины, это вопрос о том в терминах каких объектов мы рассматриваем систему не более того (состояния это то же объекты - объекты нашего внимания, как говорил классик: "Все объекты!"). Понятно что вы будете рассматривать ее так как вам привычнее, я просто предложил вам альтернативу, которая, понятно, привычной сразу не станет. На нет как говорится и суда нет.


  1. LinkToOS
    09.11.2024 05:38

    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 устройства с низким потреблением.


    1. makkarpov
      09.11.2024 05:38

      Вполне могут иметь, это называется clock stretching. Используется, если устройство медленное и хочет притормозить трафик на шине, который не успевает обрабатывать.


      1. Yuri0128
        09.11.2024 05:38

        Ну так SCL даунится на сигнале ACK - как-бы не так сложно это обработать. Да и не все слейвы поддерживают. Да и многие мастера это отслеживают аппаратно. У того-же stm32.

        Как оно у китайцев реализовано - очень большой (ну еще и больной) вопрос.


      1. LinkToOS
        09.11.2024 05:38

        Вполне могут иметь, это называется 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."


  1. ptr128
    09.11.2024 05:38

    99.99 случаев

    Если вспомнить, что I2C обязателен в составе PCIe, то бишь, используется для коммуникации между PCB, то тут явно погорячились


    1. aabzel Автор
      09.11.2024 05:38

      Еще I2C есть в HDMI шине.


  1. makkarpov
    09.11.2024 05:38

    Тут вся статья - один большой арт-объект об оверинжиниринге, но её финализация (после перечисления всех возможных состояний, переходов и т.п.) заявлением об успехе на основании трех тривиальных тестов одной конкретной микросхемы - это вишенка.

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

    Модульными они были бы, если бы вы вместо реального устройства подключили бы туда, например, FPGA и проэмулировали на ней все случаи из формальной спецификации - ACK, NACK, старт, рестарт, висящая шина, clock stretching, передача длинного буфера, NACK посередине буфера, взаимодействие этого всего с DMA (и/или RTOS) и т.п.

    Тогда от статьи действительно был бы какой-то смысл — ведь в интернете и без неё полно баганного кода, способного прочитать один байт из EEPROM в бесконечном цикле без какого-либо намека на таймауты или DMA.


    1. aabzel Автор
      09.11.2024 05:38

      То, что мы сейчас прочитали, - это именно то, что нужно для решения всей проблемы.

      Я предлагаю Вам @makkarpov до конца месяца составить публикацию на habr о проекте драйвера.

      Чтобы уже в ближайшее время можно было получить работающий драйвер I2C трансивера новым "правильным" способом.


      1. makkarpov
        09.11.2024 05:38

        У меня есть проект по модульному тестированию физического уровня USB-стека похожим способом (внешней FPGA с ULPI-трансивером), и по мере наличия свободного времени я его делаю. Как доделаю — обязательно выложу на хабр, только, боюсь, займет это куда больше, чем щедро выделенные вами три недели.

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

        Но этот I2C драйвер реализован примерно как и ваш — только те части, что реально нужны для реального устройства, потому о нем статьи на хабре я не пишу и не называю их "Конечный Автомат Аппаратного I2C Трансивера" с претензией на всеобъемность и академичность. Конечный автомат трансивера в STM32 не факт, что сами инженеры из ST нарисовать способны с доступом к исходникам. Он уж точно намного сложнее, чем ваш сферический конь в вакууме — там есть разные режимы передач, разные настройки аппаратных реакций на события, разные режимы адресации (7b/10b), автоматический отсчет переданных байт (с аппаратной реакцией на конец сообщения) и т.п. Как раз в аппаратных реакциях проблема и была — модуль рапортовал завершение передачи данных, но у него еще был аппаратный стоп, который сигнализировался другим флагом.

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


        1. aabzel Автор
          09.11.2024 05:38

          Я апробировал этот драйвер на двух ASICах: NAU8814 и LIS3DH. В каждом случае удавалось успешно читать/писать ячейки и сканировать адреса.


  1. mpa4b
    09.11.2024 05:38

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

    Это вообще не гарантия. После отрубания питания чип может оказаться запитанным phantom-питанием и его автомат  I^2C останется в зависшем состоянии.