Добрый день! Как-то возникла на работе проблема — имеется устройство, работающее по I2С и протокол которого необходимо было понять. Следовательно, нужен сниффер под интерфейс I2С, который бы выводил все, что приходит-уходит по I2C, на порт UART и далее через преобразователь на COM-порт компьютера.

Начало


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

Было 2 варианта — включать сниффер параллельно, либо в разрыв цепи. Очевидно, что первый вариант выглядит гораздо проще, что на деле оказалось совсем не так. Но обо всем по порядку.

Вкратце, о самом интерфейсе. В I2C (TWI по-атмеловски) используется два провода — SCL и SDA. Первый отвечает за тактирование сигнала, второй за передачу непосредственно информации. Также интерфейс располагает состояниями СТАРТ и СТОП.

Так вот, первая моя мысль была — взять щуп и с одной стороны подключить его к ноге внешнего прерывания на atmega8 с другой на линию SDA и ловить передний фронт, и по прошедшему времени определять 0 или 1. Очевидно, что работать это должно было очень плохо, так как не отрабатывался корректно сигнал СТОП.

Вторая мысль была сделать все то же, но прерывание ловить на линии SCL, и по прерыванию читать линию SDA, подключенную на обычную цифровую ногу. Здесь все выглядело уже более жизнеспособно, кроме того же состояния СТОП, но я решил попробовать собрать на макетке и посмотреть что же будет.

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

Код обработчика прерывания выглядел следующим образом:

ISR(INT0_vect)
{
	cli();
	if (bitIsHigh(PINB, 0))
		uart_send_char('1');
	else
		uart_send_char('0');
	sei();
}

В порт потекли нули и единички, но сразу стало очевидно, что данные некорректные — их было гораздо меньше ожидаемых и они менялись при повторении одного и того же запроса. В процессе выяснения причин все свелось к тому, что данные теряли из-за обращения к uart интерфейсу, который, к слову, работал на максимальной стабильной скорости 38 кбит/с, при этом сам I2C работал на 100 кбит/с. Поднимать скорость работы UART не представлялось возможным ввиду отсутствия кристалла необходимой частоты, чтобы перевести uart на допустимую скорость. Следовательно нужно было убирать работу с uart из прерывания. Получил что-то следующее:

static uint8_t data = 0;
static uint8_t idx = 7;

ISR(INT0_vect)
{
	cli();
	data |= bitIsHigh(PINB, 0) << (idx--);
	if (!idx)
	{
		uart_send_char(data);
		data = 0;
		idx = 7;
	}
	sei();
}

Работать все стало стабильнее, вот только данные все равно не несли никакого смысла. После нескольких часов проработки алгоритма, включения обработки СТОП и пр. все же решено было пойти другим путем.

На верном пути


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

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

В итоге получился следующий код:

ISR(TWI_vect)
{
	cli();
	
	uint8_t status = TWSR;
	uint8_t b;
	char s[4];
	s[3] = 0;
	_delay_ms(1);

	switch (status & I2C_STATUS_MASK)
	{
		case I2C_STATUS_SR_RX_ADR_ACK:/* case I2C_STATUS_SR_RX_ADR_NACK:*/
			uart_send_str("-AW:");
			uart_send_int( TWDR );
			i2csoft_start();
			i2csoft_open_write(I2C_ADDRESS);
			break;
		
		case I2C_STATUS_SR_RX_DATA_ACK:/* case I2C_STATUS_SR_RX_DATA_NACK:*/
			b = TWDR;
			sprintf(s, " %.2X", b);
			uart_send_str(s);
			i2csoft_write_byte(b);
			break;

		case I2C_STATUS_SR_RX_STOP_RESTART:
			uart_send_str("E\n");
			_delay_ms(10);
			do
			{
				_delay_us(5);
				i2csoft_start();
			}
			while (!i2csoft_open_read(I2C_ADDRESS));
			break;
			
		case I2C_STATUS_BUS_ERROR:
			uart_send_str("B\n");
			break;

		case TW_ST_SLA_ACK:	
			uart_send_str("-AR:");
			uart_send_int( TWDR );
			b = i2csoft_read_byte();
			sprintf(s, " %.2X", b);
			uart_send_str(s);
			TWDR = b;
			break;
			
        case TW_ST_DATA_ACK: 
			b = i2csoft_read_byte();
			sprintf(s, " %.2X", b);
			uart_send_str(s);
			TWDR = b;
            break;
 
        case TW_ST_DATA_NACK: 
        case TW_ST_LAST_DATA:
			b = i2csoft_read_byte(false);
			uart_send_str("L\n");
			break;
			
		default:
			uart_send_char('U');
			uart_send_int(status);
			uart_send_char(' ');
			break;
	}
	TWCR |= (1<<TWINT);
	sei();
}

Мастер устройство подключается на аппаратный I2C атмеги, исполняющее устройство подключается на любые 2 цифровые ноги, одна из которых работает в режиме SCL, другая — SDA. Все, что делает приведенный выше код — это получает по I2C прерывание и вызывает аналогичное состояние на программном интерфейсе, при этом в uart записывается служебная информация, помогающая понять что происходит. Выставленные задержки подбирались для конкретного устройства и могут несколько отличаться для других. В итоге получаем вполне добротный сниффер.

Если кому-нибудь будет интересно, то исходники можно взять с гитхаба.
Поделиться с друзьями
-->

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


  1. IronHead
    03.04.2017 16:01
    +8

    1. Alexeyslav
      03.04.2017 16:49
      +1

      Счастья не будет… одно только разочарование. Всё это сказки сплошные, кроме того что оно МОЖЕТ захватывать.
      Но из-за маленького размера буфера, которого едва хватает на один фрейм USB малейшая задержка или непонятный чипсет, и анализатор становится бесполезным и его хочется сначала об стенку потом в мусорку.
      И во вторых, никакой наглядности процесса — пока не закончится захват ничего он на экране не покажет… а как понять когда хватит? Или вообще когда начинать захват? По понятным причинам, визуализировать захваченное сразу же в реальном времени нельзя — чуть что, контроллер USB задумался и захват сорван. Мышку пошевелил на том же контроллере где и лог.анализатор и привет — захват сорван.


      1. IronHead
        03.04.2017 16:57
        +6

        В статье вопрос про i2c. Сюда же можно отнести:spi, uart, различные протоколы параллельных шин, да что там говорить — я таким анализатором TDM поток 8Mbit смотрел.

        Если же вы хотите USB (2.0/3.0 и прочее) — берите лог анализатор по круче да подороже, кто мешает?
        На гиктаймс 99% аудитории будет достаточно данного анализатора, а остальные 1% я думаю сами в состоянии понять и выбрать — что им купить в соответствии с их запросами.


        1. Alexeyslav
          03.04.2017 23:16

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


          1. IronHead
            04.04.2017 00:18
            +1

            Ну я и говорю, остальные 1% сами в состоянии понять и выбрать — что им купить в соответствии с их запросами.


  1. PKav
    03.04.2017 16:03

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

    А в разрыв как-то не совсем правильно. Лучше уж тогда использовать процессор помощнее, типа STM32. Ну, или подключить параллельно на шину сдвиговые регистры. Вход на SDA, строб на SCL, а 8 выходов целиком на порт контроллера, и можно быстро забирать байты прямо из регистра IDR порта.


    1. Mogwaika
      03.04.2017 16:19
      +3

      А вообще идеально ПЛИС поставить…


      1. 32bit_me
        03.04.2017 18:09

        Плюсую плис.


      1. Alfishe
        04.04.2017 00:54
        +1

        Железки на FPGA то уже есть и аппаратно весьма неплохо сделанные. Например DSLogic Pro (Spartan-6 + Cypress USB 2.0). Но вот софт оставляет желать лучшего


        1. Mogwaika
          04.04.2017 01:02

          Для сбора данных по медленному такту и отправки их в uart никакого софта и сложных железок не надо.


          1. Alfishe
            04.04.2017 01:49
            +1

            тогда BusPirate


            1. Mogwaika
              04.04.2017 01:55

              Это всё ещё дороже китайской платы со Спартаном, к которому можно повесить ChipScope.
              А потом запрогать его ещё для чего-нибудь.
              Но ОК, варианты явно лучше, для всего есть свои задачи, всё лучше чем над ардуиной издеваться.


            1. teleghost
              04.04.2017 23:07

              мы с Krey пробовали Bus Pirate для разбора SMBus (точнее, Krey колол PMBus-реализацию блоков питания Corsair). Bus Pirate с I2C/SMbus работает хреново, мусора летит много, и что-то, наоборот, не видно. Но что-то есть вполне осмысленное. Т.е., простите, ни в п&&ду, ни в Красную Армию такие устройства. Может, мы чего не доглядели?


              1. Alfishe
                05.04.2017 00:27
                +1

                Лично мне из всего BusPirate понравился только макрос сканирования I2C шины на наличие устройств.
                А в целом — ну как можно на маломощном МК с подключенным UART на 115200 бод поймать хоть что-то внятное идущее на гораздо более высоких скоростях. Как помощь при обучении или простенькие одиночные посылки поймать-послать — вполне. Но для чего-то более серьезного такие устройства не подходят. Ну и работа в консоли — на любителя


                1. teleghost
                  05.04.2017 00:51

                  А, ну раз там 115200, тогда понятно… участвовать в I2C-обмене — да (на 100k), но в *перехвате* BusPirate хватило только на то, чтобы увидеть адрес slave-устройства и убедиться, что блок питания действительно использует PMBus. Половина транзакций битая. Остальное выясняли уже подключившись другим master-устройством и прогнав софт через ".NET-дизасемблер". Хотя с этими ООП-фреймворками реверсная инженерия превращается в изучение оформленного по всем правилам исходного кода, разве что только комментариев разработчика нет:)


    1. 32bit_me
      03.04.2017 18:09

      > существуют логические анализаторы
      И осциллографы, которые декодируют кучу протоколов.


      1. dernuss
        03.04.2017 18:29

        Мне нравятся? пикоскопы;)


        1. 32bit_me
          03.04.2017 19:19

          Это автомобильные, что ли?
          Я имел в виду настоящие dso, типа такого:


          1. dernuss
            03.04.2017 19:32
            +1

            вот офсайт https://www.picotech.com
            ПО у них хорошее.


    1. Zolg
      03.04.2017 18:47
      +1

      существуют логические анализаторы, которые не только захватят, но и разберут и красиво покажут весь обмен данными по любым более-менее популярным протоколам
      sigrok: разбирает кучу протоколов.
      FOSS.
      В качестве аппаратных интерфейсов поддерживает в том числе копеешные железяки из коммента выше.


    1. Alfishe
      04.04.2017 00:57

      Недавно зарелизило очень интересную разработку Analyzer2Go — PC клиент + прошивка для дешевых STM Evaluation Boards типа Nucleo и Discovery. Проект в самом начале пути, но качество (от русскоязычного, кстати) разработчика радует.


  1. slog2
    03.04.2017 16:21
    +1

    ISR(INT0_vect)
    {
    cli();

    uart_send_char(data);

    sei();

    Потеряете все события на входе внешнего прерывания кроме первого, пока не выполнится uart_send_char(data). А это довольно долго. Вложенные прерывания запрещать не надо и правильная uart_send_char(data) тоже должна работать по прерываниям.


    1. VT100
      04.04.2017 00:02
      +1

      Плюсую. С таким подходом автора — и ARM можно утопить ожиданиями флагов аппаратуры в прерываниях.
      Думаю, что надо было просто раскурить как следует мануал на блок TWI, а не возиться с «bit-bang'ом навыворот».


      1. slog2
        04.04.2017 10:08

        I2c это как правило 100кгц такты. UART в разы медленнее, и типичная uart_send_char(data) ждёт пока весь байт + стопы не уйдут. За это время на i2c несколько байт пролетит мимо. Но в принципе работало бы, если правильно всё организовать.


        1. VT100
          04.04.2017 10:25

          Ну почему в разы? Стандартными средствами ОСей до 115200 кБод (что теоретически позволяет успевать за стандартным I2C на 100 кГц — 100/9 к 115,2/10), немного покопавшись — можно и больше. Плюс — наверняка в передачах I2C будут разрывы на обработку данных в системе и кольцевую буферизацию в снифере сделать с флажком «извини, хозяин, — я не успел».


          1. slog2
            04.04.2017 15:08
            -2

            115200кБод это 57,6кГц же. Ну пусть не в разы, сравнимы скорости. Хотя авр и быстрее может. На 460800 обмен с компом у меня работал. В любом случае заработает снифер только если всё правильно написать с грамотным использованием прерываний и с буферами. Что автор не осилил.


            1. VT100
              04.04.2017 23:43
              +1

              115200кБод это 57,6кГц же.

              Ну как-же так? 115.2 кГц… вот прям щас на осциллографе посмотрел.


              1. slog2
                11.04.2017 12:50
                +1

                Это же асинхронный uart. За один период частоты передаётся 2 бита. Настроив uart на 115200 бод на выходе данных вы получите частоту максимум в половину от скорости, это 57600Гц. А если бы был тактовый сигнал, то он был бы 115200гц. но тут, в отличие от i2c его нет. Вот же

                image


                1. VT100
                  12.04.2017 21:23
                  +1

                  Пожалуй — да.
                  «В каждой части света есть свои, не менее замечательные, меньшие части.»


  1. Leerooooy
    03.04.2017 17:00
    +1

    Однако велосипед… Почему не сделать проще и лучше: берем MAX10, цепляемся к нише и заполняем FIFO, постепенно сливая данные в юарт через терминал?
    Если хочется писать большое количество данных, то цепляем гиг ОЗУ (у альтеры есть готовое IP ядро для новичков) и пишем в них очень и очень долго. Можно сразу и в юарт сливать. Использование FIFO в ОЗУ позволит и записать много данных и не потерять ничего.


    1. ukt
      03.04.2017 18:10

      Макс10 пока ещё дороговат )


      1. Mogwaika
        03.04.2017 19:37

        Плата с 3-им Спартаном ~20$ с 6-м ~25$ это дорого?


      1. Leerooooy
        03.04.2017 20:33

        Это где же она дорогая? 7.5$, что собственно дешевле циклона. И стоит помнить, что в MAX10 есть внутри ПЗУ для хранения конфигурации, что экономит еще 7-10$. (Цены с digikey).


        1. Alfishe
          04.04.2017 01:00

          спартан без хорошего USB чипа для стриминга мало что сможет, только триггер и захват куда-то в буфер. Плюс нужна сама буферная память, то се…
          В идеале, конечно, чтобы декодеры протоколов были тоже аппаратные. Но про такое слышал только в контексте MegaZoom IV ASIC для Keysight осциллографов…


          1. Mogwaika
            04.04.2017 01:09

            Я думаю для большинства i2c хватит uart стандартных скоростей.
            Это же не АЦП данные, по одному битику в спартан xc6slx9 можно полмиллиона отсчётов запихать, т.е. секунду обмена.


            1. Alfishe
              05.04.2017 00:29

              А если посчитать? минимально стандартизированная скорость I2C — 100кбит. Как без компрессии впихнуть всю информацию о посылках I2C (информация о фронтах, длительность, декодированная информация) в 115.2кбит? никак. 400кбит, 1мбит и выше — тем более


              1. Mogwaika
                05.04.2017 12:52

                Скажем про 1Мбит я не знал, во вторых сведения о задержках пакетов и прочую телеметрию я пока не предлагал передавать, я естественно рассматривал вариант 100кбит.
                Во вторых я пока мало знаком с i2c, только bme280 завёл на оранжпай, так, диванные рассуждения.
                В третьих я слышал про более быстрые уарты, х.з. как с поддержкой скоростей компами, но на плисе можно любую скорость реализовать.


  1. ukt
    03.04.2017 18:10

    Когда заходит в прерывания, прерывания(другие) уже запрещены.
    Поэтому не нужно в них писать cli(), sei();


  1. nerudo
    03.04.2017 22:55
    +1

    Хм. Правильно я понял, что включить параллельно на gpio не удалось из-за недостаточной скорости обработки, а задействовать параллельно аппаратный порт из-за того, что он обязан отвечать (выставлять Ack на каждый байт), тем самым вмешиваясь в обмен?
    Вообще идея вставать в разрыв не самая удачная, т.к. I2C поддерживает мультимастеринг на шине, и в неудачном случае можно с удивлением обнаружить транзакции приходящие с обеих сторон ;)


    1. Alfishe
      04.04.2017 01:02
      +1

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


  1. sim-dev
    04.04.2017 08:05

    Если память мне не изменяет, протокол I2C допускает торможение обмена путем принудительного придавливания SCL в ноль… Не уверен, что все «мастеры» это поддерживают, но по идее попробовать можно было бы: прижали SCL на время передачи в UART, потом отпустили и ловим следующий фрейм/байт.


    1. WebConn
      04.04.2017 09:54
      +1

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

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


  1. safari2012
    04.04.2017 18:00
    -3

    я бы взял Esp8266 вместо AVR/MEGA. Там два аппаратных i2c. Ну и вообще, железяка помощнее и будет, при том, скорее всего скетчи для arduino подойдут с мин.танцами.


  1. teleghost
    05.04.2017 12:36
    +1

    Автор, спасибо за тему, конечно, но после заголовков «Начало» и «На верном пути» не хватает заголовка «Решение» с соответствующим описанием победы. Т.е. я ценю Ваши усилия по созданию анализатора, но не вижу подтверждения наличия готового продукта, с помощью которого (внимание!) была решена конкретная задача. Прототип от продукта отличается тем, что в последнем учтены все нюансы, без которых невозможно решать реальные задачи в адекватные сроки. В комментариях выше я даже упомянул изделие, которое считается продуктом, но по факту не работает (таких примеров не просто много, а очень много). А что в итоге есть у Вас — идея с исходниками, готовый прототип или полностью работающий продукт?


    1. Neomer
      06.04.2017 16:24
      -1

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


  1. FGV
    06.04.2017 16:22
    +1

    uart интерфейсу, который, к слову, работал на максимальной стабильной скорости 38 кбит/с

    хм, а тактовая частота кристалла какая? и что за мега? 8,16...?


    1. Neomer
      06.04.2017 16:22
      +1

      atmega8 от внутренней RC-цепочки.


  1. mark_ablov
    11.04.2017 15:22

    мк в разрыве не давал побочных задержек?