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

Софт-реализация I2C-мастера: учесть все и не сойти с ума

Приветствую!

А почему бы, собственно, и да.

Короче, есть в каком-то обозримом будущем вероятность того, что ногодрыгом придется поднимать I2C-мастера. Глянул что вообще имеется из готового - расстроился. Часто вижу какие-то нелепые задержки на NOP-ах процессора и другой костыльный бред:smile: А даже если и так - все слишком упрощено и работает только у того, кто сие изобрел. Чуть шаг влево - например, слейв требует обязательного СТОП-СТАРТ- вместо РЕСТАРТ-условия - все, не работает. Мастер - штука довольно простая (хотя и со своими тараканами), поэтому моя идея заключается в следующем. Прикладное ПО оперирует функциями I2CSend() и I2CRecv(). Ему по-барабану на внутреннее устройство этих функций. Но в реализации софт-I2C эти функции как конструктор собирают нужную транзакцию из более мелких "простейших" операций на шине. Сами операции потактно формируются либо в прерывании таймера, либо в каком-то периодическом процессе. Предлагаю их рассмотреть. Цифрами 1, 2 ... 5 обозначаю "тики" (минимальные интервалы) таймера, штрих-линиями - границу операции, точками - проверку на обязательное соответствие логических уровней на SCL/SDA уровням, показанным на рисунке.

1. СТАРТ-условие

image.png.68c2abf9178dfad6a97a5bb4397e240e.png

СТАРТ-условие всегда должно начинаться с проверки, что SCL/SDA в лог. 1, иначе это будет что угодно, но не СТАРТ-условие.

2. РЕСТАРТ-условие

image.png.91ef41f646091b0159981f01aa76652a.png

РЕСТАРТ-условие всегда должно начинаться с проверки, что SCL в лог. 0, иначе это будет что угодно, но не РЕСТАРТ-условие.
В этом случае переключаем SDA в лог. 1 вне зависимости от того, какой уровень был на ней до этого. Как видно, здесь требуется уже 4 такта.

3. БИТ

image.png.1b3504089210f87af4d1afc5dccb2673.png

Это интервал передачи/приема бита. Этот же паттерн отражает и ACK/NACK.
Вначале проверяется, что SCL в лог. 0. Потом выставляем данные и "клокаем".

4. СТОП-условие

image.png.ff4ab4e4c4726ecf0d02986e0225e00a.png

СТОП-условие всегда должно начинаться с проверки, что SCL в лог. 0.
Если это так, SDA опускается в лог. 0, а затем формируется СТОП-условие.

5. ЗАЩИТНОЕ СТОП-условие

image.png.b405d740228c8355ad3b9f77aa74fb12.png

В отличие от простого СТОП-условия, формирует СТОП-условие из любого состояния SDA/SCL.
ЗАЩИТНОЕ СТОП-условие предназначено для вывода некоторых слейвов из коматозного состояния.
Хотя спецификация говорит о минимум 9 циклах на SCL, однажды мы выяснили, что лучше подавать СТОП-условия.
Обычные СТОП-ы "как есть" сформировать не получится друг за другом, поэтому нужна диаграмма именно ЗАЩИТНОГО СТОП-а.

Любое несоответствие на диаграммах выше (например, SCL в лог. 0 в начале СТАРТ-услвоия) - ошибка транзакции.

Пример диаграммы передачи

image.thumb.png.6496e1a0c5aba2377999004bf9fd9e7e.png 

Как видно, СТАРТ можно сформировать не из любого места, а только после СТОП-а или ЗАЩИТНОГО СТОП-а.
БИТ можно сформировать только между СТАРТ-ом и СТОП-ом или ЗАЩИТНЫМ СТОП-ом. ЗАЩИТНЫЕ СТОП-ы можно формировать где угодно.

Собственно, прикладное ПО может сформировать последовательность из таких отрезков, чтобы получить нужную транзакцию. Однако, некоторые операции можно сократить, например, передачу/прием байта не нужно разбивать на 8 (+1 ACK) отдельных подкоманд формирования битов; простейшую транзакцию СТАРТ-передача-СТОП тоже желательно обозначить одной операцией. Т.е. не до маразма доходить. Прикладное ПО, когда хочет совершить транзакцию на шине, добавляет в очередь транзакций специального вида дескрипторы (и данные на передачу при необходимости) и запускает таймер. Таймерное прерывание потихоньку выгребает "заявки" и рисует диаграммы, шевеля ногами SCL/SDA. С приемом разберемся чуть позже, а пока рассмотрим структуру дескриптора (и данных, если прикладной код хочет что-то записать в слейв)
 image.thumb.png.be59018df77f8e46f6b61c75183d1ed6.png

Bus Operation ID - это некий идентификатор, по которому можно сгруппировать части одной транзакции. Например, для EEPROM же нужно сначала записать внутренний адрес ячейки памяти, и лишь потом делать транзакции чтения. Общая транзакция разбивается на 2 - первая СТАРТ + запись адреса ведомого + запись внутреннего адреса, вторая - РЕСТАРТ + запись адреса ведомого + БИТ-ы чтения (с выдачей ACK-ов) + СТОП на конце. Технически для EEPROM это одна операция, но для нас это будет 2, но под одним Bus Operation ID.

Rx/Tx Byte Quantity - если это транзакция передачи (см. позже описание Bus Operation Control), то в этой же очереди следом будут идти байты, которые нужно отправить ведомому. Если это прием - данных нет, и принимаемые данные будут помещаться в очередь приема (со своими статусными сообщениями в дескрипторе) - мол, успешно или нет.

Bus Operation Control - самое интересное в дескрипторе. Бит 0 указывает, что за транзакция совершается (прием или передача). В битах 2:1 указывается, нужно ли формировать СТАРТ- или РЕСТАРТ- перед "телом" транзакции. Кроме того, можно здесь указать, что шину нужно "прочистить" (подав 10 ЗАЩИТНЫХ СТОП-ов), и лишь потом выдать СТАРТ-условие. Биты 4:3 указывают, нужно ли в конце "тела" транзакции делать СТОП-условие или его разновидности. Бит 5 нужен только для транзакции приема, он указывает, нужно ли отвечать NACK-ом на последнем принимаемом байте, или ACK-ом (обычно, все-таки, должен быть NACK, но пусть будет универсально). В битах 7:6 отражается необходимость "рапортовать" о результатах транзакции (в очередь приемных сообщений) - например, при передаче сообщать, что слейв не может сожрать какой-то байт, или не получилось СТАРТ-условие сформировать и т.д. Можно настроить, что рапортация будет только при ошибках, или наоборот (только когда транзакция завершена успешно) - это удобно для реализации функции поиска девайсов на шине с заранее не известными физическими адресами. Бит 8 позволяет "прочистить" очередь запросов для транзакций с одинковыми ID по списку (если EEPROM не ответила ACK-ом на свой адрес - зачем ей передавать данные?). Бит 9 позволяет автомат "попросить" сформировать последовательность "прочистки" шины в случае, если транзакция по какой-то причине запорота.

В итоге - формируем нужную последовательность, записываем в очередь передачи - она отрабатывается и рапортует в очередь приема.

Что не учел? Мультимастер?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Arlleex said:

нелепые задержки на NOP-ах процессора и другой костыльный бред:smile:

Нелепо прерываться с частотой 100+кГц, а отнюдь не nop'ы в задержках иметь.

 

1 hour ago, Arlleex said:

Что не учел?

Clock stretching?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Arlleex сказал:

показанным на рисунке.

Вам бы статьи на вики о работе интерфейсов писать.  :wink:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, aaarrr сказал:

Нелепо прерываться с частотой 100+кГц, а отнюдь не nop'ы в задержках иметь.

ИМХО, нужно всегда отдавать себе отчет, что если хочется полноценные I2C-скорости иметь, то нужно как-то на аппаратный I2C сильнее засматриваться... Но когда такой возможности нет, с более медленными скоростями смириться еще можно. С другой стороны, можно подумать еще о связке TIM + DMA -> GPIO, если такое можно провернуть в используемом МК. Или другие механизмы ногодрыга активно использовать - в LPC43xx (вроде) есть некий SGPIO, который, насколько я знаю, много чего может наногодрыжить. А на NOP-ах тоже далеко не уедешь - 100кбит/с при тактовой CPU в 32МГц (у меня сейчас так) - это уже 320 тактов в пустую. Мне скорость не особо важна, главное, что обмены со слейвом - штука регулярная.

Цитата

Clock stretching?

Точно, спасибо:good3: Я подумаю, как.
 

2 часа назад, jcxz сказал:

Вам бы статьи на вики о работе интерфейсов писать...

Я обычно для своих каких-то программных механизмов всегда рисую похожие картинки, так потом проще вспомнить основную суть, не открывая исходников:smile:
 

3 часа назад, gerber сказал:

Софт-реализация I2C Master - это халява, по сравнению с софт-реализацией I2C Slave

Слейв даже не хочу думать пока что... Нет надобности.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, Arlleex сказал:

В отличие от простого СТОП-условия, формирует СТОП-условие из любого состояния SDA/SCL.
ЗАЩИТНОЕ СТОП-условие предназначено для вывода некоторых слейвов из коматозного состояния.
Хотя спецификация говорит о минимум 9 циклах на SCL, однажды мы выяснили, что лучше подавать СТОП-условия.
Обычные СТОП-ы "как есть" сформировать не получится друг за другом, поэтому нужна диаграмма именно ЗАЩИТНОГО СТОП-а.

9 циклов SCL полезнее в тех случаях, когда STOP используется для запуска какого-то внутреннего процесса в ведомой микросхеме I2C. Например STOP запускает запись сектора в I2C-флешь. Поэтому в таких случаях, возможно, лучше использовать 9 тактов SCL, а не STOP.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 минуты назад, controller_m30 сказал:

9 циклов SCL полезнее в тех случаях, когда STOP используется для запуска...

Сама транзакция будет "собираться" по кирпичикам так, как указано в спецификации на конкретную микросхему слейва. 9 циклов SCL - это "не о том" песня. Это когда между мастером и слейвом произошел рассинхрон и шина "залипла" в силу смещенного положения ACK. В микросхемах с внутренним автоинкрементом адреса чтения/записи такое "проклокивание" может не прокатить. И лучше подавать СТОП-ы (вместо 9 клоков 9 СТОП-ов).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Наверное Защитный Стоп полезен - я просто не знаю, к каким микросхемам он может быть применён (не всё можно знать). Но мне интересно уточнить для стандартной флешь, например 24C32, что Вы планируете применять - Защитный Стоп, или 9 тактов SCL при SDA=1?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В случае, если обнаружено залипание шины - думаю, защитные стопы. В обычных транзакция - обычные стопы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тогда я позволю себе уточнить для случая 24C32, что в ситуации, если ведомая микросхема перед сбросом МК что-то передавала на шину, то сигнал Защитный Стоп (повторенный 9 раз) будет ею интерпретирован как ACK от МК, и она вместо того чтобы освободить шину, продолжит передавать биты дальше.

 

Ну а в ситуации, если перед сбросом МК шла запись в буфер флешь, то STOP запустит запись содержимого буфера в память (в неизвестные адреса, кстати), и при этом некоторое время флешь не будет подтверждать обращения к ней. Поэтому остальные сигналы Защитного Стоп (кроме первого) скорее всего пропадут втуне.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Arlleex said:

А на NOP-ах тоже далеко не уедешь

Если строить универсальное решение, лучше предусмотреть разные источники времени. Где-то нужна регулярность, где-то скорость.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 минут назад, controller_m30 сказал:

Ну а в ситуации, если перед сбросом МК шла запись в буфер флешь, то STOP запустит запись содержимого буфера в память (в неизвестные адреса, кстати)

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

А то что при старте ПО, чипы флешь памяти могут некоторое время быть заняты (завершают предыдущую операцию записи/стирания), так корректно написанное ПО это должно учитывать. само собой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

24 минуты назад, jcxz сказал:

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

Ведомая микросхема конечно знает куда переписать содержимое своего буфера. А вот знает-ли об этом программа МК?

Если произошла перезагрузка МК и он пытается инициализировать шину I2C, "забыв" что в буфере 24C32 лежат готовые к записи данные (отправляй только STOP и понеслась!). Тогда запустив серию из 9 "Защитных Стоп" - МК запустит запись неизвестных ему данных по неизвестному ему (МК) адресу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, controller_m30 сказал:

МК запустит запись неизвестных ему данных по неизвестному ему (МК) адресу.

И что? А если МК успеет запустить запись данных (закончит I2C-транзакцию) и тут же перезагрузится - что от этого изменится? Вы этого не боитесь?

Будет то же самое.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В случае 24C32 запись может производиться пакетами по 64 байта (размер буфера флешь). Если МК планировал записать 64 байта, а успел записать только 3 (такое количество для примера), после чего перезагрузился - тогда Защитный Стоп запустит запись такого, возможно некорректного, блока данных. Полагаю что любая запись должна производиться только под "осознанным" контролем программы МК. А случайно инициировать запись во флешь, в надежде что если там что-то и запишется, то это 100% корректные данные - это легкомыслие. 

 

В целом я поддерживаю идею Arlleex по созданию конечного программного автомата I2C_Master. И уточняю момент с 9 тактами SCL или 9 тактами Guard STOP - только с целью доведения этой идеи до блеска :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...