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

Организация стабильной работы шины I2C

Добрый вечер!

Имеется микроконтроллер, имеется некоторое периферийное устройство, подключенное к первому по шине I2C. Однажды, при отладке их взаимодействия, я столкнулся с особенностью, на первый взгляд интересной, но в то же время привносящей плачевные последствия. Дело в том, что, в отличие от синхронных интерфейсов, имеющих отдельную физическую линию разрешения (CS, EN, и т.д.), например, SPI, шина I2C таковой не является: только две линии - SDA и SCL. Усугубляется это обстоятельство еще и идеологией сигналов: в зависимости от состояний логических уровней на этих линиях различают старт-бит, повторный старт, стоп бит.

При включении питания микроконтроллер начинает инициализировать свою внутреннюю периферию, в том числе линии портов ввода/вывода, к которым подключена шина I2C. И заранее никто не защищен от переходных процессов на шине в этот момент - там может и всплеск быть короткий, и что угодно. Пагубное влияние таких всплесков налицо - они случайно могут служить условиями старта для I2C. Когда-то так получалось и у меня, пока я не посмотрел осциллограммы сигналов на интерфейсных линиях. Действительно, там был кратковременный всплеск, и I2C-ведомые думали, что микроконтроллер сформировал условие старта. А МК, ничего об этом не подозревая, шлет реальный старт-бит, вводя ведомое устройство в ступор (ну железно зашитый в нем конечный автомат, чего поделать). Далее все просто - ведомый не отправляет бит подтверждения, а в некоторых случаях может сам подтянуть линию SCL к низкому логическому уровню. А что в этот момент делает микроконтроллер? Он опрашивает различные флаги (произошло на линии условие старта или нет, передался ли адрес, принялся ли бит подтверждения и т.д.). И, к сожалению, если делать это так:

while(!(STATUS_I2C_REG & (1 << 5))); // ожидание установки флага

и в этот момент на шине ничего не произойдет для установки этого флага, микроконтроллер тут и повиснет.

Можно сделать на прерывании, но сути не изменится - аппаратная ошибка на шине, которую можно обнаружить в регистрах I2C-интерфейса микроконтроллера, и соответственно отреагировать, но ведомому-то до этого какое дело? Вот произошла ошибка в момент передачи данных, ведомый притянул линию SCL к земле - вот и ничего не поделаешь уже, МК не сможет даже условие стопа сформировать, чтобы потом снова попытаться обратиться к этому ведомому. Я думаю вы поняли к чему я веду: случайная наводка на интерфейсной шине может сформировать определенный сигнал на шине, причем микроконтроллер еще сможет это обнаружить, но ведомый - нет. Он все так же будет ждать следующим битом, например, стоп-бит, а микроконтроллер еще только отправляет последний бит данных... Тут возникает жестокий ступор, и как вывести ведомого из него - как раз мой вопрос.

Ведь по идее делать так:

while(!(STATUS_I2C_REG & (1 << 5))); // ожидание установки флага

нельзя нигде. Мало ли почему флаг не установится, программа зациклится и что-нибудь взорвется :laughing:

Насколько я знаю, для таких целей предусматривают таймауты, по истечению которых обрабатывается произошедший казус. Но на I2C как обработать ошибку? У меня вариант только один - сделать управляемым питание ведомого устройства. Через полевой транзистор, например. Чуть что - перезапустили и дальше работаем.

Благодарю за внимание!

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


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

Имеется микроконтроллер, имеется некоторое периферийное устройство, подключенное к первому по шине I2C.

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

 

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


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

Так в том и проблема, что у ведомого никаких входов сброса и аппаратного контроля, к сожалению.

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


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

На шине I2C пассивное состояние (лог. единица) формируется резисторами подтяжки. И если во время инициализации ваш контроллер каким-то образом умудряется сформировать там случайные нули - исправляйте программу.

 

Существует и стандартная процедура сброса автомата приема ведомого - нужно подать 9 импульсов SCLK не притягивая SDAT к нулю. После этого ведомое будет ожидать старт-условие.

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


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

Ну во-первых надо заменить

 

while(!(STATUS_I2C_REG & (1 << 5)));

 

на

 

i=0;
while(!(STATUS_I2C_REG & (1 << 5)))
{
  i++;
  if(i>100000)
     break;
}
if(i>10000)
{
  //// Обработка тайм-аута
}

 

 

Ну и так далее. Обрабатываться должны абсолютно все исключительные ситуации. Никаких бесконечных циклов без тайм-аута или делений на результат из АЦП без проверки того что АЦП не выдал 0.

Разница между "выдающим ошибки" I2S и "стабильным" SPI лишь в том, что в SPI ошибки происходят в 1000 раз реже, и Вы их не видите, пока отлаживаете. Но это не значит, что не нужно заботится об их обработке. Исходите из того, что в программе на МК любой флаг может не выставиться и любая переменная может принять любое значение. Устройство при этом не должно впасть в каматоз. Последний форпост - WDT таймер. Это называется "сбоеустойчивый код".

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


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

Сергей Борщ,

На шине I2C пассивное состояние (лог. единица) формируется резисторами подтяжки. И если во время инициализации ваш контроллер каким-то образом умудряется сформировать там случайные нули - исправляйте программу.

 

Существует и стандартная процедура сброса автомата приема ведомого - нужно подать 9 импульсов SCLK не притягивая SDAT к нулю. После этого ведомое будет ожидать старт-условие.

 

Инициализация как ни странно - правильная. А артефакты бывают тогда, когда подключаю ведомое двухметровым проводом, но - на минимальной скорости. Про 9 бит надо попробовать, спасибо!

 

MiklPolikov, благодарю за подсказку!

Изменено пользователем Arlleex

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


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

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

 

Само название шины - Inter-IC bus как бы намекает, что она задумана для связи между микросхемами, а не между устройствами.

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


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

Про правильное включение I2C я знаю. Про то, что она для связи микросхем - тоже. Подключаю экранированным двужильным кабелем (марку не помню, к сожалению). Резисторы по 4,7к.

Я проводил эксперименты, где микроконтроллер несколько раз в секунду взаимодействовал с ведомым на низкой скорости. Результаты на самом деле хорошие - произошло 250 000 обращений и связь нарушилась. Я оставлял плату на день и уходил по своим делам, фиксируя все на дисплее.

Следовательно, результаты не плохие, но, нужно уметь избавляться от любых проблем на шине, ведь перезапустив устройства, можно спокойно совершить столько же обращений (ну как повезет - может меньше, а может больше).

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


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

Про правильное включение I2C я знаю. Про то, что она для связи микросхем - тоже.

Если знаете, то почему не сделаете правильно? Пропустите через кабель не I2C, а подходящий для такого использования интерфейс, а на той стороне поставьте простейший контроллер с I2C.

 

Следовательно, результаты не плохие, но, нужно уметь избавляться от любых проблем на шине, ведь перезапустив устройства, можно спокойно совершить столько же обращений (ну как повезет - может меньше, а может больше).

Если Вам просто костыль нужен, то как вариант - на удалённую сторону поставить WatchDog на линию SCLK, сигнал RESET с него завести на вход enable LDO, питающего I2C-слэйвы (раз у них нет входов сброса).

Для сброса ведомых I2C достаточно будет просто приостановить опрос на время более периода таймаута сторожевика.

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


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

Резисторы по 4,7к.
По-хорошему надо бы измерить емкость вашего кабеля и выбрать их номинал по графикам в стандарте исходя из емкости. Но этого никто делать не будет, к гадалке не ходи. Поэтому просто умньшите их до 1.5к.

 

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


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

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

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

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

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

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

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

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

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

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