RSS
people

Мои инструменты: Logic Analyzer

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

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

Так вот он, значит, выглядит с наружи:

Логический анализатор - общий вид

В принципе — ничего особенного — 8 проводов с щупами — зацепками (крассные) для снятия сигнала и 9-й — земля (черный).

Подключается этот чудный приборчик к USB порту и распознается как обычный последовательный порт, так как я использовал микросхему фирмы FTDI — FT232BM. Если взглянуть на схему прибора, то мы увидим, что там ничего особенного нету. Один микроконтроллер ATMega 128, и USB контроллер FT232BM.

Светодиоды около USB порта показывают текущее состояние работы прибора.

  • P — Power — Ну тут все ясно — питание есть — уже хоршо.
  • A — Armed — На взводе — ждем нужное событие, что бы продолжить — об этом дальше.
  • T — Triggered — Дождались — нужное событие произошло — пошла запись.
  • S — Send — Шлем посылки — запись закончилась — отсылаем.

Ну и вот так вот прибор выглядит изнутри:

Внутренности логического анализатора - вид сверху
Внутренности логического анализатора - вид снизу

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

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

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

На схеме эта часть выглядит вот так:

Схема логических входов

В принципе — ничего сложного. Многие читатели заметят — что на входах нет никакой защиты. Да — нету. И на это есть у меня пара отговорок. 1 — делалось все побыстрому, хотелось поскорее посмотреть сигнал на одной из платок моих. 2 — я то знаю, что там нету защиты никакой, поэтому не пихаю куда попало.

Так вот — схема простая — и все, что нам надо — считывать порт PORTC в программе. Процесс считывания происходит в функции прерывания таймера.

  1. SIGNAL(SIG_OVERFLOW1)
  2. {
  3.  u08 register bByte;
  4.  outp(AnalyzerConfig.u16TimerCounter.bytes.high, TCNT1H);  // load counter value hi
  5.  outp(AnalyzerConfig.u16TimerCounter.bytes.low,  TCNT1L);  // load counter value lo
  6.  if (!gFlags.bDataHere)
  7.  {
  8.    // PORTB = PINB | 0x02;
  9.    bByte = ANALYZER_PINS;
  10.    if (!gFlags.bTrigger && !gFlags.bStopTriggering)
  11.    {
  12.      if (AnalyzerConfig.bTriggerType != TT_NO_TRIGGER)
  13.      {
  14.        if (AnalyzerConfig.bTriggerType == TT_FALLING_EDGE)
  15.        {
  16.          if (
  17.                 (bOldByte & bTrigMask)
  18.              && ((bByte & bTrigMask) == 0)
  19.             )
  20.          {
  21.             gFlags.bTrigger = 1;
  22.          }
  23.        }
  24.        else
  25.        {
  26.          if (
  27.                  ((bOldByte & bTrigMask) == 0)
  28.               && (bByte & bTrigMask)
  29.             )
  30.          {
  31.             gFlags.bTrigger = 1;
  32.          }
  33.        }
  34.      }
  35.      else
  36.      {
  37.        gFlags.bTrigger = 1;
  38.      }
  39.      bOldByte = bByte;
  40.    }
  41.    bAnalyze[wAnWriteIndex] = bByte;
  42.    if (gFlags.bTrigger && !gFlags.bStopTriggering)
  43.    {
  44.      LED_TRIGGER_ON;
  45.      LED_ARMED_OFF;
  46.      if(wAnWriteIndex < AnalyzerConfig.wMaxBytesToStore - 1)
  47.        wAnWriteIndex++;
  48.      else
  49.      {
  50.        gFlags.bDataHere = 1;
  51.        gFlags.bStopTriggering = 1;
  52.      }
  53.    }
  54.    else
  55.    {
  56.      LED_TRIGGER_OFF;
  57.    }
  58.    // PORTB = PINB & ~0x02;
  59.  }
  60. }

  Вся процедура чтения состоит из двух частей — с 103-й по 136-ю строку определяется событие, надо ли ждать его, если надо, то наступило ли оно или еще нет. И второая часть с 138-й по 153-ю строки — тут происходит, собственно, подсчет индексов массива,в который мы заранее сохранили значение порта на строке 137, которое, в свою очередь, считали еще раньше — в самом начале нашей функции прерывания, на строке 105.

В первой части определяется событие срабатывания, но только в том случае, если мы этого еще не определили. На это нам указывает флаг gFlags.bTrigger. Если он еще не установлен — то мы проверяем условие срабатывания, которым выступает изменение какого — либо бита данных, изменение с 0 в 1 или наоборот — с 1 в 0.

Эти параметры срабатывания задаются нашему устройству управляющей программой с компьютера. Но об этом попозже.

Далее, когда мы определились с условием срабатывания, во второй части функции происходит, собственно, сохранение информации и вычисление следующего элемента массива, куда будем сохранять следующую порцию информации.  И если мы сохранили достаточное количетсво этой самой информации, то вывешивается флаг, что данные собраны, мы закончили:  gFlags.bDataHere = 1; и gFlags.bStopTriggering = 1;

А отправка самих данных происходит в основной функции main ():

  1.   if (gFlags.bDataHere)
  2.   {
  3.     LED_SEND_ON;
  4.     uart_putchar(UART_SYNC_BYTE);
  5.     uart_putchar(0xEF);
  6.     uart_putchar(UART_SYNC_BYTE);
  7.     uart_putchar(0x55);
  8.     uart_putchar(wAnWriteIndex);
  9.     uart_putchar(wAnWriteIndex >> 8);
  10.     u16 i;
  11.     u08 * bData = AnalyzerGetBuffer();
  12.     for (i = 0; i <= wAnWriteIndex && gFlags.bDataHere; i++)
  13.     {
  14.       SWdtReset    = 1;
  15.       uart_putchar(bData[i]);
  16.     }
  17.     wAnWriteIndex = 0;
  18.     gFlags.bDataHere = 0;
  19.     if (AnalyzerConfig.bTriggerType == TT_NO_TRIGGER)
  20.     {
  21.       gFlags.bTrigger = 1;
  22.       gFlags.bStopTriggering = 0;
  23.       LED_ARMED_ON;
  24.     }
  25.     else
  26.     {
  27.       if (AnalyzerConfig.bTriggerRepeat)
  28.       {
  29.         gFlags.bTrigger = 0;
  30.         gFlags.bStopTriggering = 0;
  31.         LED_ARMED_ON;
  32.       }
  33.     }
  34.     LED_SEND_OFF;
  35.   }

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

Вот собственно и вся функциональность самого прибора.

Еще немного можно рассказать про установку параметров от управляющей программы.

Прием данных сделан с использованием прерывания UART.

  1. /* signal handler for receive complete interrupt */
  2. SIGNAL(SIG_UART0_RECV)
  3. {
  4.     uart_rxd_buffer[uart_Write_in_Index] = UDR0;
  5.     if (uart_rxd_buffer[0] == UART_SYNC_BYTE)
  6.     {
  7.       //*uart_rxd_in_ptr = ~(*uart_rxd_in_ptr);
  8.       uart_Write_in_Index++;
  9.       if (uart_Write_in_Index >= sizeof(TCommData))
  10.       {
  11.          LED2_OFF;
  12.          LED3_OFF;
  13.          LED4_OFF;
  14.          ProcessData((TCommData *)uart_rxd_buffer);
  15.          uart_Write_in_Index = 0;
  16.          uart_rxd_buffer[0] = 0;
  17.       }
  18.       if(uart_Write_in_Index >= UART_BUF_SIZE)
  19.        uart_Write_in_Index = 0;
  20.     }
  21.     else
  22.     {
  23.       uart_Write_in_Index = 0;
  24.     }
  25. }

Сдесь мы принимаем данные до тех пор, пока не забъется весь приемный буфер. Причем, прием начнется только тогда, когда мы примем байт синхронизации. Т.е. так мы отсекаем всякий мусор. Когда буфер набъется необходимым количеством байтиков — проводим разбор этих самых байтиков:

  1. void ProcessData(TCommData * CommData)
  2. {
  3.   if (CommData->bSyncByte == UART_SYNC_BYTE)
  4.   {
  5.     switch (CommData->bCmd)
  6.     {
  7.       case UART_TRIGGER_RESET:
  8.  
  9.       break;
  10.       case UART_TRIGGER_SET:
  11.         AnalyzerConfig.bTriggerBit           = CommData->AnCfg.bTriggerBit;
  12.         AnalyzerConfig.bTriggerType          = CommData->AnCfg.bTriggerType;
  13.         AnalyzerConfig.bTriggerRepeat        = CommData->AnCfg.bTriggerRepeat;
  14.       break;
  15.       case UART_TIMER_SET:
  16.         AnalyzerConfig.bTimerPrescaler       = CommData->AnCfg.bTimerPrescaler;
  17.         AnalyzerConfig.u16TimerCounter.value = CommData->AnCfg.u16TimerCounter.value;
  18.       break;
  19.       case UART_SET_WIDTH:
  20.         if (CommData->AnCfg.wMaxBytesToStore <= AN_MAX_ANALYZES_BUFFER)
  21.         {
  22.           AnalyzerConfig.wMaxBytesToStore      = CommData->AnCfg.wMaxBytesToStore;
  23.         }
  24.       break;
  25.       case UART_SET_ALL:
  26.         AnalyzerConfig.bTimerPrescaler       = CommData->AnCfg.bTimerPrescaler;
  27.         AnalyzerConfig.u16TimerCounter.value = CommData->AnCfg.u16TimerCounter.value;
  28.  
  29.         AnalyzerConfig.bTriggerBit           = CommData->AnCfg.bTriggerBit;
  30.         AnalyzerConfig.bTriggerType          = CommData->AnCfg.bTriggerType;
  31.         AnalyzerConfig.bTriggerRepeat        = CommData->AnCfg.bTriggerRepeat;
  32.  
  33.         if (CommData->AnCfg.wMaxBytesToStore <= AN_MAX_ANALYZES_BUFFER)
  34.         {
  35.           AnalyzerConfig.wMaxBytesToStore      = CommData->AnCfg.wMaxBytesToStore;
  36.         }
  37.       break;
  38.     }
  39.     AnalyserSetupTimer();
  40.   }
  41. }

Ну тут особого ничего нету — обычное присваивание с одной структуры в другу.

Но это все происходит в нашей маленькой черненькой коробочке и не так захватывающе, потому, что нашему глазу мало чего видать. А теперь поговорим об управляющей программе, Front end, так сказать.

Вот так выглядит основное окошко программы:

Главное окно программы

В принципе — все довольно просто. Слева внизу выставляется количество каналов, которые будем смотреть. Сама программа расчитана на максимум 16 каналов, но наша маленькая коробочка пока что шлет нам всего 8.

Чуть ниже можно увеличить или уменьшить представление сигналов. Правее идет группа установки события — Trigger. В ней можно выбрать номер канала, на котором ждать события, выбрать вид события — с 0 в 1 — Rising edge, с 1 в 0 — Falling edge, или вообще не ловить события. Галочка Repeat triggering говорит программе, что неплохо было бы за одним пойманным событием сразу ждать следующего.

Кнопочка Set в этой группе посылает команду прибору на установку только этого параметра.

Следующая группа Sampling устанавливает параметры сэмплирования — частоты чтения пробников. Sample time — это время задержки, с которой будет происходить счетние сигналов. Его можно установить грубо с помощью ползунка, что чуть ниже, и более точно с помощью кнопочек вверх/вниз, что справа. Диапазон значений от 5 микросекунд до 10 секунд.

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

Samples — количество чтений в одной посылке данных, которое будет прочитано, прежде чем данные будут посланы прибором в программу. Диапазон значений от 1 до 3072.

Значения можно вбить с клавиатуры или выставить кнопочками вверх/вниз.

Ну и большая кнопка, мимо которой просто не промахнуться — Set All. Позволяет установить все параметры сразу одним махом.

Чуть правее находится информационное поле Time/Div, в котором выводится текущее разрешение экрана каналов, т.е. цена одного деления в секундах.

И еще чуть правее — просто различная отладочная информация, что бы знать, где искать ашипки.

В меню ничего особенного нету, разве что только настройки — Communication Settings:

Настройки последовательного порта

Ну тут все понятно. Для нашего прибора надо выбрать сам порт. Его номер узнаем, когда, открыв в «менеджере устройств» компьютера ветку Com ports, воткнем наш прибор в USB порт. Он должен распознаться и появиться в этой ветке. Вот его номер и заносим сюда. Скорость — 128000. 1 стоповый бит, без четности. 8 бит на байт. Вот собственно и все.

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

Программа написана на Borland C++ Builder 5, и может так получиться, что без некоторых библиотек не запустится, поэтому рекомнедую их скачать тоже.

Ну и как обычно — список файлов, описанных в этом выпуске:

15 комментариев к “Мои инструменты: Logic Analyzer”

  1. di-halt.livejournal.com/ пишет:

    Какая частота опроса?

  2. MasterAlexei пишет:

    Там в тексте — в описании программы самой есть :

    «Следующая группа Sampling устанавливает параметры сэмплирования — частоты чтения пробников. Sample time — это время задержки, с которой будет происходить счетние сигналов. Его можно установить грубо с помощью ползунка, что чуть ниже, и более точно с помощью кнопочек вверх/вниз, что справа. Диапазон значений от 5 микросекунд до 10 секунд.»

    Т.е. Максимальная частота опроса — 200 kHz (1 / 5 микросекунд). Минимальная — 100 Hz (1 / 10 секунд).

  3. Gre-kir пишет:

    Добрый день Алексей, можно ли спаять на АТмега 64 даную конструкцию? Есть ли ограничение по количеству отчетов для записи в программе? На чем написан исходник AVR на С?

  4. MasterAlexei пишет:

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

    Ограничение по количеству отчетов есть — 3072 байта. Это число я взял примерно, посмотрев, сколько памяти надо для моих внутренних переменных и стека и все оставшееся отдал под массив считываемых данных. Так как у 64й атмеги тоже 4 кб РАМ, то этот параметр можно не менять.

    Исходники на GNU WinAVR — С.

  5. Gre-kir пишет:

    Добрый день, Алексей. Спаял по вашей схеме анализатор, только не могу запустить. При включении загорается светодиод на 15 ноге Меги, на 17 ноге мигает с частотой 10 раз в секунду, Я припаял к конвертеру USB-COM два светодиода RX, TX и по ним наблюдаю обмен данными. прибор постоянно передает посылки данных но программа их не принимает. Передача настроек с программы проходит (вижу по светодиоду). Но светодиоды А и Т не горят вообще. Частота генерации кварца Меги 8 Мгц (проверял мультиметром). Может-быть вспомните какие биты прописаны в Меге? Или знаете в чем проблема. Прибор очень необходим. Жду ответа. Заранее благодарен

  6. MasterAlexei пишет:

    Здравствуйте, Gre-kir!

    Тут в схеме есть небольшой косячек, но лечится относительно легко. Косяк в том, что на схеме указана частота кварца — 8 Мгц. А софт расчитан на кварц 16 Мгц. Просто я, когда разрабатывал это все — то просто скопировал кусок схемы с одного из своих проектов, и забыл подправить номиналы. А заметил только когда уже выставил на сайт.

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

  7. MasterAlexei пишет:

    Еще попутно вопрос — вы сделали анализатор на базе именно меги 128 или все же мегу 64ю взяли?

  8. Gre-kir пишет:

    Решил не искушать судьбу, поставил 128, сегодна кварц моменяю, сообщу о резулультатах...

  9. Gre-kir пишет:

    Заменил кварц- заработало. Хорошая штука. Только нет возможности записать снятые данные- я по крайней мере не смог. Как скинуть график ??

  10. MasterAlexei пишет:

    Рад, что понравилась вещь. :)

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

  11. vesel пишет:

    Спасибо за конструкцию,отличная вещь. Давно хотел подобное сварганить да только вот программирование для «большого брата» останавливало. Максимум что могу так это на VBA под SQL «морду» сделать. Обидно что не прочитал сразу отзывы а развел плату без кварца (посмотрел что 8мгц на схеме) и включил внутренний генератор. Пришлось внешний генератор подключать ))

    То что на 128 меге построено — отлично. Не задумывались добавить и считывание АЦП ? Удобственно выглядело бы еще и видеть например напряжение или еще что либо )) Удачи. Еще раз спасибо за конструкцию.

  12. MasterAlexei пишет:

    Классно, что железка оказалась полезной.

    Про АЦП — хм. Пока не думал, так как все же АЦП — это уже немного другой прибор, другая схемотехника должна быть и т.д. Вот чего я сейчас с этим анализатором делаю — так это SPI докручиваю. Теперь он у меня будет как SPI логгер, тестер. Т.е. можно будет либо послать пару тройку байт в SPI либо считать их. Все, что нужно будет — это добавить проводок до PB1 (SS) пина, да и то, это если нужна возможность работы в режиме Slave.

    Ну и перепрошить контроллер. GUI тоже изменится немного, но не сильно. Так что ждите, скоро будет!

  13. vesel пишет:

    ЫЫЫЫ )))

    Видать не зря я при разводке платы вывел SPI разьемчик ))) Не зряяя)))

  14. Мои инструменты: Logic Analyzer V. 2.0 | Fun Electronic пишет:

    [...] Ну я не долго думал и малость усовершенствовал свой Логический анализатор и добавил к нему новую [...]

  15. Новая версия Логического Анализатора, с возможностью добавления плагинов | Fun Electronic пишет:

    [...] обработки данных. Но пока часть анализатора для старого железа уже работает более менее стабильно, что не может не [...]

Оставить комментарий или два

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