Сегодня мы поговорим об инструментах, которые могут нам пригодиться в столь интересном и увлекательном деле, как изобретение чего-нибудь интересного и увлекательного, и в данный момент речь пойдет об одном из моих самых свежих инструментов — логическом анализаторе.
Старая добрая Википедия говорит нам, что логический анализатор — это такое устройство, которе предназначено для записи и анализа цифровых последовательностей. Вот, собственно, этим мой анализатор и занимается — записывает цифровые последовательности.
Так вот он, значит, выглядит с наружи:
В принципе — ничего особенного — 8 проводов с щупами — зацепками (крассные) для снятия сигнала и 9-й — земля (черный).
Подключается этот чудный приборчик к USB порту и распознается как обычный последовательный порт, так как я использовал микросхему фирмы FTDI — FT232BM. Если взглянуть на схему прибора, то мы увидим, что там ничего особенного нету. Один микроконтроллер ATMega 128, и USB контроллер FT232BM.
Светодиоды около USB порта показывают текущее состояние работы прибора.
- P — Power — Ну тут все ясно — питание есть — уже хоршо.
- A — Armed — На взводе — ждем нужное событие, что бы продолжить — об этом дальше.
- T — Triggered — Дождались — нужное событие произошло — пошла запись.
- S — Send — Шлем посылки — запись закончилась — отсылаем.
Ну и вот так вот прибор выглядит изнутри:
Естественно, что для работы данного устройства необходима прошивка — программа, которая для нас будет записывать состояния сигналов и отправлять их нам.
Писал я эту программку, что называется, на коленке, по этому там довольно мало комментариев, но на основным моментах мы сейчас остановимся.
Основная часть программы, которой вы скорее всего заинтересуетесь, это сама процедура записи сигнала с пробников.
На схеме эта часть выглядит вот так:
В принципе — ничего сложного. Многие читатели заметят — что на входах нет никакой защиты. Да — нету. И на это есть у меня пара отговорок. 1 — делалось все побыстрому, хотелось поскорее посмотреть сигнал на одной из платок моих. 2 — я то знаю, что там нету защиты никакой, поэтому не пихаю куда попало.
Так вот — схема простая — и все, что нам надо — считывать порт PORTC в программе. Процесс считывания происходит в функции прерывания таймера.
- SIGNAL(SIG_OVERFLOW1)
- {
- u08 register bByte;
- outp(AnalyzerConfig.u16TimerCounter.bytes.high, TCNT1H); // load counter value hi
- outp(AnalyzerConfig.u16TimerCounter.bytes.low, TCNT1L); // load counter value lo
- if (!gFlags.bDataHere)
- {
- // PORTB = PINB | 0x02;
- bByte = ANALYZER_PINS;
- if (!gFlags.bTrigger && !gFlags.bStopTriggering)
- {
- if (AnalyzerConfig.bTriggerType != TT_NO_TRIGGER)
- {
- if (AnalyzerConfig.bTriggerType == TT_FALLING_EDGE)
- {
- if (
- (bOldByte & bTrigMask)
- && ((bByte & bTrigMask) == 0)
- )
- {
- gFlags.bTrigger = 1;
- }
- }
- else
- {
- if (
- ((bOldByte & bTrigMask) == 0)
- && (bByte & bTrigMask)
- )
- {
- gFlags.bTrigger = 1;
- }
- }
- }
- else
- {
- gFlags.bTrigger = 1;
- }
- bOldByte = bByte;
- }
- bAnalyze[wAnWriteIndex] = bByte;
- if (gFlags.bTrigger && !gFlags.bStopTriggering)
- {
- LED_TRIGGER_ON;
- LED_ARMED_OFF;
- if(wAnWriteIndex < AnalyzerConfig.wMaxBytesToStore - 1)
- wAnWriteIndex++;
- else
- {
- gFlags.bDataHere = 1;
- gFlags.bStopTriggering = 1;
- }
- }
- else
- {
- LED_TRIGGER_OFF;
- }
- // PORTB = PINB & ~0x02;
- }
- }
Вся процедура чтения состоит из двух частей — с 103-й по 136-ю строку определяется событие, надо ли ждать его, если надо, то наступило ли оно или еще нет. И второая часть с 138-й по 153-ю строки — тут происходит, собственно, подсчет индексов массива,в который мы заранее сохранили значение порта на строке 137, которое, в свою очередь, считали еще раньше — в самом начале нашей функции прерывания, на строке 105.
В первой части определяется событие срабатывания, но только в том случае, если мы этого еще не определили. На это нам указывает флаг gFlags.bTrigger. Если он еще не установлен — то мы проверяем условие срабатывания, которым выступает изменение какого — либо бита данных, изменение с 0 в 1 или наоборот — с 1 в 0.
Эти параметры срабатывания задаются нашему устройству управляющей программой с компьютера. Но об этом попозже.
Далее, когда мы определились с условием срабатывания, во второй части функции происходит, собственно, сохранение информации и вычисление следующего элемента массива, куда будем сохранять следующую порцию информации. И если мы сохранили достаточное количетсво этой самой информации, то вывешивается флаг, что данные собраны, мы закончили: gFlags.bDataHere = 1; и gFlags.bStopTriggering = 1;
А отправка самих данных происходит в основной функции main ():
- if (gFlags.bDataHere)
- {
- LED_SEND_ON;
- uart_putchar(UART_SYNC_BYTE);
- uart_putchar(0xEF);
- uart_putchar(UART_SYNC_BYTE);
- uart_putchar(0x55);
- uart_putchar(wAnWriteIndex);
- uart_putchar(wAnWriteIndex >> 8);
- u16 i;
- u08 * bData = AnalyzerGetBuffer();
- for (i = 0; i <= wAnWriteIndex && gFlags.bDataHere; i++)
- {
- SWdtReset = 1;
- uart_putchar(bData[i]);
- }
- wAnWriteIndex = 0;
- gFlags.bDataHere = 0;
- if (AnalyzerConfig.bTriggerType == TT_NO_TRIGGER)
- {
- gFlags.bTrigger = 1;
- gFlags.bStopTriggering = 0;
- LED_ARMED_ON;
- }
- else
- {
- if (AnalyzerConfig.bTriggerRepeat)
- {
- gFlags.bTrigger = 0;
- gFlags.bStopTriggering = 0;
- LED_ARMED_ON;
- }
- }
- LED_SEND_OFF;
- }
В самом начале мы отсылаем пару байт синхронизации, которые говорят управляющей программе, что сейчас пойдут данные, а не просто текст или что-либо еще. Затем мы так же отсылаем количество байт, чтобы на той стороне знали — сколько байт ждать. И затем, в цикле, отсылаем сами данные.
Вот собственно и вся функциональность самого прибора.
Еще немного можно рассказать про установку параметров от управляющей программы.
Прием данных сделан с использованием прерывания UART.
- /* signal handler for receive complete interrupt */
- SIGNAL(SIG_UART0_RECV)
- {
- uart_rxd_buffer[uart_Write_in_Index] = UDR0;
- if (uart_rxd_buffer[0] == UART_SYNC_BYTE)
- {
- //*uart_rxd_in_ptr = ~(*uart_rxd_in_ptr);
- uart_Write_in_Index++;
- if (uart_Write_in_Index >= sizeof(TCommData))
- {
- LED2_OFF;
- LED3_OFF;
- LED4_OFF;
- ProcessData((TCommData *)uart_rxd_buffer);
- uart_Write_in_Index = 0;
- uart_rxd_buffer[0] = 0;
- }
- if(uart_Write_in_Index >= UART_BUF_SIZE)
- uart_Write_in_Index = 0;
- }
- else
- {
- uart_Write_in_Index = 0;
- }
- }
Сдесь мы принимаем данные до тех пор, пока не забъется весь приемный буфер. Причем, прием начнется только тогда, когда мы примем байт синхронизации. Т.е. так мы отсекаем всякий мусор. Когда буфер набъется необходимым количеством байтиков — проводим разбор этих самых байтиков:
- void ProcessData(TCommData * CommData)
- {
- if (CommData->bSyncByte == UART_SYNC_BYTE)
- {
- switch (CommData->bCmd)
- {
- case UART_TRIGGER_RESET:
- break;
- case UART_TRIGGER_SET:
- AnalyzerConfig.bTriggerBit = CommData->AnCfg.bTriggerBit;
- AnalyzerConfig.bTriggerType = CommData->AnCfg.bTriggerType;
- AnalyzerConfig.bTriggerRepeat = CommData->AnCfg.bTriggerRepeat;
- break;
- case UART_TIMER_SET:
- AnalyzerConfig.bTimerPrescaler = CommData->AnCfg.bTimerPrescaler;
- AnalyzerConfig.u16TimerCounter.value = CommData->AnCfg.u16TimerCounter.value;
- break;
- case UART_SET_WIDTH:
- if (CommData->AnCfg.wMaxBytesToStore <= AN_MAX_ANALYZES_BUFFER)
- {
- AnalyzerConfig.wMaxBytesToStore = CommData->AnCfg.wMaxBytesToStore;
- }
- break;
- case UART_SET_ALL:
- AnalyzerConfig.bTimerPrescaler = CommData->AnCfg.bTimerPrescaler;
- AnalyzerConfig.u16TimerCounter.value = CommData->AnCfg.u16TimerCounter.value;
- AnalyzerConfig.bTriggerBit = CommData->AnCfg.bTriggerBit;
- AnalyzerConfig.bTriggerType = CommData->AnCfg.bTriggerType;
- AnalyzerConfig.bTriggerRepeat = CommData->AnCfg.bTriggerRepeat;
- if (CommData->AnCfg.wMaxBytesToStore <= AN_MAX_ANALYZES_BUFFER)
- {
- AnalyzerConfig.wMaxBytesToStore = CommData->AnCfg.wMaxBytesToStore;
- }
- break;
- }
- AnalyserSetupTimer();
- }
- }
Ну тут особого ничего нету — обычное присваивание с одной структуры в другу.
Но это все происходит в нашей маленькой черненькой коробочке и не так захватывающе, потому, что нашему глазу мало чего видать. А теперь поговорим об управляющей программе, 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, и может так получиться, что без некоторых библиотек не запустится, поэтому рекомнедую их скачать тоже.
Ну и как обычно — список файлов, описанных в этом выпуске:
- Схема прибора -logicanalyser_schematic.pdf
- Прошивка микроконтроллера прибора — simplelogicanalyzer.rar
- Программа Логический анализатор В. -1.0 — logicanalyser_v_1_0.rar
- Некоторые библиотеки от Borland -bcblibs.rar
- Описание микроконтроллера Atmel AVR ATMega 128 — atmega128-fullerrata.pdf
- Описание USB — Serial конвертера FTDI FT232BM — ft232bm.pdf
15 комментариев к “Мои инструменты: Logic Analyzer”
Оставить комментарий или два
Пожалуйста, зарегистрируйтесь для комментирования.
18th February 2009 в 16:42
Какая частота опроса?
18th February 2009 в 16:50
Там в тексте — в описании программы самой есть :
«Следующая группа Sampling устанавливает параметры сэмплирования — частоты чтения пробников. Sample time — это время задержки, с которой будет происходить счетние сигналов. Его можно установить грубо с помощью ползунка, что чуть ниже, и более точно с помощью кнопочек вверх/вниз, что справа. Диапазон значений от 5 микросекунд до 10 секунд.»
Т.е. Максимальная частота опроса — 200 kHz (1 / 5 микросекунд). Минимальная — 100 Hz (1 / 10 секунд).
28th October 2009 в 3:49
Добрый день Алексей, можно ли спаять на АТмега 64 даную конструкцию? Есть ли ограничение по количеству отчетов для записи в программе? На чем написан исходник AVR на С?
28th October 2009 в 19:55
Думаю, что конструкцию спаять можно. Надо будет только подправить исходники на другой процессор. Возможно, что имена регистров не будут совпадать со 128й атмегой. Особенно это касается USART.
Ограничение по количеству отчетов есть — 3072 байта. Это число я взял примерно, посмотрев, сколько памяти надо для моих внутренних переменных и стека и все оставшееся отдал под массив считываемых данных. Так как у 64й атмеги тоже 4 кб РАМ, то этот параметр можно не менять.
Исходники на GNU WinAVR — С.
31st October 2009 в 14:01
Добрый день, Алексей. Спаял по вашей схеме анализатор, только не могу запустить. При включении загорается светодиод на 15 ноге Меги, на 17 ноге мигает с частотой 10 раз в секунду, Я припаял к конвертеру USB-COM два светодиода RX, TX и по ним наблюдаю обмен данными. прибор постоянно передает посылки данных но программа их не принимает. Передача настроек с программы проходит (вижу по светодиоду). Но светодиоды А и Т не горят вообще. Частота генерации кварца Меги 8 Мгц (проверял мультиметром). Может-быть вспомните какие биты прописаны в Меге? Или знаете в чем проблема. Прибор очень необходим. Жду ответа. Заранее благодарен
31st October 2009 в 14:28
Здравствуйте, Gre-kir!
Тут в схеме есть небольшой косячек, но лечится относительно легко. Косяк в том, что на схеме указана частота кварца — 8 Мгц. А софт расчитан на кварц 16 Мгц. Просто я, когда разрабатывал это все — то просто скопировал кусок схемы с одного из своих проектов, и забыл подправить номиналы. А заметил только когда уже выставил на сайт.
К сожалению, придется менять именно кварц, так как если софт переделывать под эту частоту, то прошивку то переделать дело 3х минут и будет работать, а вот GUI уже будет неправильно считать параметры таймера опроса. Там этот параметр пока что железно зашит в программу.
31st October 2009 в 14:30
Еще попутно вопрос — вы сделали анализатор на базе именно меги 128 или все же мегу 64ю взяли?
3rd November 2009 в 6:42
Решил не искушать судьбу, поставил 128, сегодна кварц моменяю, сообщу о резулультатах...
6th November 2009 в 4:43
Заменил кварц- заработало. Хорошая штука. Только нет возможности записать снятые данные- я по крайней мере не смог. Как скинуть график ??
6th November 2009 в 8:35
Рад, что понравилась вещь.
Про записывание графика — пока никак. У меня пока что не было такой необходимости. Может как нить доработаю, как время на это найду. Если есть желание — могу скинуть исходники проги именно вам на доработку. Пока что у меня по времени небольшой напряг.
16th November 2009 в 17:05
Спасибо за конструкцию,отличная вещь. Давно хотел подобное сварганить да только вот программирование для «большого брата» останавливало. Максимум что могу так это на VBA под SQL «морду» сделать. Обидно что не прочитал сразу отзывы а развел плату без кварца (посмотрел что 8мгц на схеме) и включил внутренний генератор. Пришлось внешний генератор подключать ))
То что на 128 меге построено — отлично. Не задумывались добавить и считывание АЦП ? Удобственно выглядело бы еще и видеть например напряжение или еще что либо )) Удачи. Еще раз спасибо за конструкцию.
16th November 2009 в 23:30
Классно, что железка оказалась полезной.
Про АЦП — хм. Пока не думал, так как все же АЦП — это уже немного другой прибор, другая схемотехника должна быть и т.д. Вот чего я сейчас с этим анализатором делаю — так это SPI докручиваю. Теперь он у меня будет как SPI логгер, тестер. Т.е. можно будет либо послать пару тройку байт в SPI либо считать их. Все, что нужно будет — это добавить проводок до PB1 (SS) пина, да и то, это если нужна возможность работы в режиме Slave.
Ну и перепрошить контроллер. GUI тоже изменится немного, но не сильно. Так что ждите, скоро будет!
16th November 2009 в 23:49
ЫЫЫЫ )))
Видать не зря я при разводке платы вывел SPI разьемчик ))) Не зряяя)))
5th February 2010 в 16:43
[...] Ну я не долго думал и малость усовершенствовал свой Логический анализатор и добавил к нему новую [...]
4th January 2012 в 12:51
[...] обработки данных. Но пока часть анализатора для старого железа уже работает более менее стабильно, что не может не [...]