Сниффер/эмулятор I2C и 1-wire

AVR
Этеншн!
Это первая версия устройства, и она не лишена недостатков. В т.ч. иногда девайс зависает по непонятным причинам. Если честно мне лениво было отлавливать этот баг. Зависает не часто и решается перезагрузкой.

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

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

И вот, дабы упростить/ускорить процесс отладки, я решил сделать i2c сниффер. Первоначально задача была такая: прослушивать I2C линию и отправлять лог в компьютер. Когда это было реализовано, выяснилось, что в Tiny2313 осталось еще полно свободного флеша. Поэтому был придуман дополнительный функционал.
Вот, что в итоге получилось:



Вид у него не самый гламурный, да в этом и не было необходимости.

— Обмен данными с компьютером через UART. Сейчас подключается к COM порту, что не очень удобно (приходится отдельно подрубать питание). Во второй версии сделаю подключение по USB на базе FT232.

— В режиме i2c сниффера уверенно работает при скорости обмена (частоте SCL) до 50кГц. В этом режиме он записывает всю активность на линии в лог, который в фоновом режиме выкидывается на комп. Вместе с каждой записью в лог пишется время.

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

Потом я расскажу про мастер-режим. i2c мастер тоже реализован на USI. 1-wire полностью программный, и ничего особо интересного из себя не представляет.

Но сначала, общий экскурс:
За питание отвечает преобразователь на 78L05. Или, как я уже сказал, можно подрубить 5В с отлаживаемого устройства. Вообще, это конечно неудобно. В следующей версии будет USB. На питании стоят защитные диоды, поэтому спалить девайс проблематично.

На связи у нас max232. Моя разводка может показаться слишком мелкой (so16 и 0805 кондеры почти влотную к нему), но по-другому оно туда не влезало.

Кварц 20МГц. Можно было взять и меньше, например на 16. Высокая скорость необходима для быстрой работы конечного автомата i2c сниффера. Корпус кварца я заземлил, так, на «всякий пожарный».

ATTiny2313 правит бал. Во-первых это один из немногих обладателей USI. Во-вторых он может разгонятся до 20МГц. Разъём для программирования я не вывел, поэтому придётся подпаивать проводки.

На линиях SDA и SCL (это PB5 и PB7, соответственно) есть подтягивающие резисторы по 4.7к. Они подключены к ножке PB6, которая, по команде с компа подключается к питанию, обеспечивая внутреннюю подтяжку.

Ещё есть места для четырёх светодиодов, которые, в итоге не пригодились.


Крупнее

i2c сниффер.


i2c сниффер можно было реализовать полностью программно, но на USI это намного удобнее. Главным образом потому, что USI имеет встроенный Start condition detector, да ещё и с отдельным прерыванием.
Вот только проблема в том, что USI сам-по себе бесполезный кусок конечного автомата, и часть логики придется реализовать программно. С одной стороны это не удобно, а с другой — есть возможность замутить, например, сниффер, который на обычном TWI сделать невозможно.

По логике протокола i2c обмен данными, при передаче байта, проходит в двух направлениях. Если мы передаём байт, то нужно передать 8 бит «туда», и прочитать 1 бит «оттуда». Если читаем, то наоборот.
В случае-же сниффера, не существует «туда» и «оттуда». Есть только одно направление — «мимо». Это несколько облегчает задачу. Нам нужно всего-навсего слушать линию и складывать данные в буфер. Из этого буфера они будут передаваться на компьютер.

Вся движуха на линии делится на четыре типа:
0x1 — Старт детектед
0x2 — Стоп детектед
0x4 — Байт + ACK
0xC — Байт + NACK

Запись для каждого события занимает 4 байта. Первый (Старший) байт — это код события (те-самые 0x1,0x2...). Во втором данные. Для первых двух событий данные не нужны, тогда второй байт = 0. Последние два байта — значение таймера. Он запускается, как только будет пойман первый start-condition, и сбрасывается по команде с компьютера. Период одного «тика» таймера — 1мс. Он может считать чуть больше минуты, этого более чем достаточно для отлова всяких повторов и других неведомых вещей, которые иногда творятся на линии.



Если бы мы решили ловить Start condition программными средствами, то пришлось-бы заводить ножку SDA на прерывания, и уже там определять старт это, или просто SDA переключилась. А при помощи USI такая задачка решается просто: включаем прерывание USI START и ждём…

Вот так выглядит обработчик этого прерывания:
procedure USI_Start; org 0xF;
begin
 asm //Очищаем флаг, а счётчик = 15.
 in r16, USISR
 andi r16, %11110000
 ori  r16, %10001111
 out USISR, r16
 end;
 USI_recv_state := 2;

 //Пускаем таймер
 if TCCR0B=0 then
  begin
   Timer := 0;
   TCNT0 := 0;
   PSR10_bit := 1;
   
   TCCR0B := 4; //Предделитель 256
  end;
  
 //Запихиваем данные в буфер
 push_to_buffer(f_start, 0);
end;


Первым делом мы сбрасываем флаг USISIF. Это нужно сделать как можно быстрее, ибо, пока флаг поднят, USI будет зажимать линию SCL. Затем мы запихиваем в счётчик USICNT 0x0F, и пишем в переменную USI_recv_state 2. Это подготовка к приёму данных (Знаю, сейчас похоже на черную магию, но потом станет понятнее). Проверяем, если таймер ещё не запущен, то сбрасываем его и запускаем.

Для опредения stop-condition нам предоставили почти тоже самое, что и для start. Только прерывание пожадничали. Поэтому пришлось добавить в цикл программы проверку флага USIPF.

   if USIPF_bit=1 then
    begin
     USIPF_bit := 1;
     push_to_buffer(f_stop, 0);
    end;

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

Самое интересное — поймать данные. Целых 9 бит. USI у нас настроен так:
USICS1 = 1; USICS0 = 0; USICLK = 0 — это значит, что при каждом положительном фронте на SCL, регистр данных USIDR будет сдвигаться, и в него будет записываться бит с линии SDA. А счётчик USICNT будет тикать на каждый фронт SCL.

А вот и чёрная магия:


При старте мы загружаем в USI_recv_state значение 2, а в счётчик (USICNT) — 0x0F. Таким образом переполнение счётчика (и прерывание USI) произойдёт, как только линия SCL упадёт в 0.

В прерывании (Переполнение счётчика) мы проверяем USI_recv_state — определяем на каком этапе приёма мы сейчас находимся.

Если USI_recv_state = 2, то нужно подготовится к приёму данных. Очищаем счётчик, чтобы он мог протикать все 16 тактов. И не забываем обновить значение USI_recv_state (теперь записываем 0), чтобы в следующий раз корректно обработать прерывание.

Потом, в течении 16 тактов счётчика (по одному такту на каждый перепад линии SCL) USI работает автоматически, не отвлекая программу по каждому пустяку. При этом, на каждый положительный фронт SCL, бит с шины проталкивается в регистр USIDR. Таким образом, к тому моменту, как случится прерывание, в регистре уже соберётся весь байт.

Если USI_recv_state = 0, то можно забирать данные (пока-что во временную переменную), и готовится к чтению девятого бита (ACK). Счётчик переполнился на заднем фронте восьмого импульса SCL, а ACK можно читать только на переднем фронте следующего импульса. Поэтому нужно протупить ещё один такт счётчика — записываем 0x0F. Ну, а в USI_recv_state записываем 1.

Если USI_recv_state = 1, значит можно читать ACK и загонять данные в буфер. А вот следующий фронт нам придётся пропустить. Для этого загоняем в счётчик 0x0F, а в USI_recv_state число 2.

Потом, если по линии побегут ещё данные, цикл повторится сначала.
Отправка данных в компьютер тоже автоматизирована. Как только в буфер попадают данные, запускается конечный автомат на прерывании UDRE (UART Data Register Empty). Как только на линии происходит какое-то событие, в буфер добавляются данные и сдвигается указатель, по которому происходит запись. Параллельно с этим данные из буфера отправляются в UART.


i2c мастер.

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

Обмен данными с отладчиком в этом режиме происходит по такой схеме:
1) Посылаем команду 0x31 — переход в мастер режим.
2) Посылаем 0x34. Эта команда говорит отладчику, что сейчас в UART прилетит куча данных — список действий, который отладчик должен выполнить.
3) Сразу за командой 0x34, посылаем размер (в байтах) этого списка действий.
4) Посылаем весь список.
5) Посылаем команду 0x35 — выполнить список.

После этого отладчик начинает последовательно выполнять команды из списка. На место выполненной команды записывается отчёт. То есть, после выполнения команды «Передать байт 0x38», на её место запишется, например «Передан байт 0x38, получен NACK».
И команда, и отчёт занимают два байта. Логика здесь такая-же, как и в режиме сниффера: первый байт — код, второй — данные.

Команды для i2c мастер-режима могут быть такие:
0x05 — Старт (Или повстарт)
0x02 — Стоп
0x07 — Записать байт. (Во втором байте — данные, которые нужно записать)
0x10 — Прочитать байт, передать ACK
0x11 — Прочитать байт, передать NACK

Коды отчётов:
0x05 — Передан start-condition
0x02 — Передан stop-condition
0x07 — Передали байт, нам ответили ACK
0x08 — Передали байт, в ответ — NACK
0x10 — Прочитали байт, сказали ACK
0x11 — Прочитали байт, сказали NACK

После того, как все команды будут выполнены и на их место будет записан отчёт, список отправляется обратно в компьютер. Когда весь список будет отправлен, устройство переходит в мастер режим. Можно опять загонять команду 0x34 и новый список, или перевести отладчик в другой режим.

1-Wire мастер.

Этот режим, практически, является частью i2c мастера. Точнее, есть просто «Мастер-режим», в котором устройство выполняет команды из списка. Мы можем загнать в этот список команды для работы с i2c или 1-Wire, логика работы, при этом, не меняется.
Для работы с 1-Wire есть 5 команд:
0x01 — Послать RESET импульс, и прочитать PRESENSE.
0x03 — Прочитать байт
0x06 — Передать байт
0x12 — Подключить дополнительное питание на линию Dq
0x13 — Отключить дополнительное питание.

После выполнения списка, отладчик может выдать вот такие коды:
0x01 — Передали RESET, поймали PRESENSE импульс.
0x04 — Передали RESET, но PRESENSE не было.
0x06 — Байт прочитан
0x09 — Байт передан
0x12 — Дополнительное питание подключено
0x13 — Дополнительное питание отключено

Если 1-Wire устройство работает от паразитного питания, то для выполнения некоторых операций ему понадобится дополнительная подпитка. Как раз для этих целей существуют команды 0x12 и 0x13. Первая подключает ножку Dq к питанию, а вторая — переводит в режим Hi-Z. Хотя, команда отключения питания почти не используется, т.к. оно отключается само, как только начнёт выполняться любая другая команда.

Архив с прошивкой и схемой

Архив с управляющей программой

  • +5
  • 16 февраля 2011, 23:17
  • dcoder

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

RSS свернуть / развернуть
Один тэг забыл. Пора бы уже включаться в общую гонку :)
0
А в чем проблема-то? Тыкаем «редактировать», добавляем в теги «конкурсная»… все :)
Это-ж можно сделать в любой момент :)
0
для iWire мастера я добавил бы команду принять/передать 1 тайм- слот- пригодится, например, для чтения режима питания термометров ds1820, или при записи заготовок rw1990.2- там есть команда записи флага, плюс большие промежутки (>10мс) между тайм- слотами во время записи кода
0
  • avatar
  • hexFF
  • 17 февраля 2011, 09:24
Хм… а я режим питания DS18b20 сразу целым байтом читал.
Получалось 00 или FF.
0
ух… так не пробовал, как по даташиту- один тайм-слот, и всё. надо будет посмотреть
0
Ага, а еще, экспериментируя с этим датчиком я выяснил, что если при работе от паразитного питания, ногу Vcc не замыкать на Gnd — он будет думать, что работает от обычного питания.
0
хорошая мысль
0
Очень во время.Сейчас залип на отладке i2c как раз.
0
Кстати нужная вещ… Надо будет собрать, хех жаль только на всё мало времени((
0
А файл платы чем открывать? Не знаю чем открыть можно.
0
Sprint Layout 5.0
0
За неимением Тини2313 поставил Тиню2313V-10 и MAX3232 (кстати по ДШ вывод 2 через конденсатор идет на + питания) Кварц 20Мгц вроде запустился. Включаю питание, запускаю прогу дебаггера -НЕ ПОДКЛЮЧЕН. Снимаю питание со схемы — ПОДКЛЮЧЕН. Что за фокус, мож все же надо Тиню2313-20. Естественно в монтаже ошибок нет. Фузы на внешний кварц. Битрейт только 9600 или возможно поставить другой?
0
  • avatar
  • V11
  • 09 декабря 2011, 02:52
В активный режим не хочет переключаться, в ждущем записывает в лог только 0ms >Старт, все остальное побоку. Если прога виснет перезапуск её не помогает, только рестарт компа. Что делаю не так?
0
Собрал сниффер, прошил МК, проверил работу max232 с com-портом. Подключил к компу, с прогой конектится, подтягивающий резистор по команде подключает. При попытке что-то прочитать с шины i2c кроме «Start(XXXms)» больше ничего не выводит, через несколько попыток виснет. Что может быть?
0
Аналогичная фигня.
В ждущем записывает в лог только 0ms >Старт, и все.
За три минуты работы зависнет 10 раз.

В графе управаление отлодчиком — самопроизвольно и хаотично переключается Мастер/Ждущий/Снифер



dcoder, Не подскажете в чем может быть дело?
0
Только что изготовил и протестировал сниффер (правда сразу с ЮСБ исполнением)
Если кому нужно, то вот мой рабочий вариант печатки с FT232RL
0
Можно немного приглушить скромность и дать устройству довольно удачное имя — dbugger.
0
кто файлы перезальёт?
0
Не качаются файлы! Может есть у кого ранее скачанные, пожалуйста на NL016@yandex.ru
0
0
Vga Спасибо! Уже и не надеялся, думал тема заброшена
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.