36ж. Осциллограф, часть 4: декодирование шин, маски, сегментированная память, сохранение и управление по SCPI
Первые три части (36г, 36д, 36е) учили прибор видеть: как сигнал попадает в память, как поймать нужный момент и как снять с записи число. Эта часть — о том, что прибор делает с записью потом. Он может прочитать в ней байты шины и записать их таблицей, сравнить каждую запись с допуском и посчитать брак, сложить тысячи коротких записей подряд вместо одной длинной, найти в длинной записи редкое событие, сохранить всё в файл — и отдать управление программе на компьютере.
Каждая такая функция экономит часы, но у каждой есть условие, при котором она молча врёт. Декодер не видит шину, если в памяти меньше нескольких выборок на бит. Маска бракует исправную плату, если допуск меньше шума. Поиск находит не всё, если ищет не там. Поэтому для каждого пункта — что это, зачем, что видно, типичная ошибка и случай из работы со встраиваемой платой.
Примеры — на приборе автора, RIGOL DHO900: названия пунктов меню даны по-английски, как на экране, цифры — из его документов. Общие вещи (сколько выборок на бит нужно декодеру, почему узкая маска бракует шум) верны для любого цифрового осциллографа; то, что относится только к DHO900, так и помечено.
Опоры: RIGOL DHO900 User Guide (Books/DHO900_UserGuide_EN.pdf;
страницы здесь и дальше — по PDF-файлу): с. 31 и 177–180 — цифровые
каналы, с. 202–204 — опорные осциллограммы, с. 205–209 — Pass/Fail,
с. 210–233 — декодирование, с. 234–235 — окна, с. 236–240 — запись
осциллограмм, с. 241–246 — поиск и навигация, с. 250–261 и 270–271 —
сохранение, с. 263–268 — сеть и выход AUX, с. 273–275 — удалённое
управление; RIGOL DHO900 Datasheet (Books/dho900-datasheet.pdf):
с. 9 — сводка, с. 11–12 — цифровые каналы, с. 15 — поиск, с. 17 —
запись и Pass/Fail, с. 18–19 — декодирование, с. 23 — разъёмы, с. 26 —
форматы файлов; RIGOL DHO800/DHO900 Programming Guide
(Books/DHO800900_ProgrammingGuide_EN.pdf, 2024): с. 25–35 — синтаксис,
ожидание и корневые команды, с. 42, 93–98, 134–135, 148–151, 167–168,
217–218, 290, 314, 322–329, 425–436 — команды из 36ж.15.
Пороги входов STM32F103 — таблица 35 даташита
(через 36а.6). Все расчётные числа — прогон _verify/scope36zh/model36zh.py
(Python 3.11 и numpy в контейнере stm-lab:1), листинги на Python —
файлы той же папки, проверенные в том же прогоне.
Как это называется в документации
Оригинал Перевод protocol decoding, Decode 1…4, Bus Status декодирование шины, четыре декодера, включение декодера Bus Type: Parallel, RS232, I2C, SPI, CAN, LIN тип шины Threshold, SCL Thre, SDA Thre порог: выше — единица, ниже — ноль Baud, Polarity, Endian (LSB, MSB), Parity скорость, полярность, порядок бит, чётность Copy Trig взять настройки из запуска Event Table, Export таблица событий, выгрузка в CSV logic analyzer (LA), digital channels D0–D15 логический анализатор, цифровые каналы Pass/Fail test, X Mask, Y Mask, Err Action проверка по маске, допуск по времени и по напряжению, действие при браке AUX OUT выход на задней панели: запуск или результат проверки Waveform Recording, Frames, Interval, Play запись осциллограмм, кадры, интервал, воспроизведение segmented memory, UltraAcquire, Frame Segment сегментированная память: запись на каждый запуск Search, MarkTable, Navigation поиск, таблица найденного, переход по записи Reference waveform (Ref) опорная осциллограмма Save Image / Save Wave / Save Setup; .bin, .csv, *.stp снимок экрана, данные, настройки SCPI, IEEE 488.2 common commands язык команд прибора, общие команды стандарта VISA address, USB-TMC, LXI, Web Control адрес прибора, класс USB, сетевой стандарт, управление из браузера :WAVeform:PREamble?, TMC block header #N описание записи: шаги и начала по осям; заголовок двоичного блока
36ж.1 Декодер: порог, часы и что он читает
Что это. Декодер (Decode) берёт из памяти выборки одного-двух-четырёх каналов, сравнивает каждую с порогом (Threshold) — выше порога единица, ниже ноль — и по правилам выбранной шины собирает из единиц и нулей байты, адреса и ошибки. Результат рисуется полосой под осциллограммой и пишется в таблицу событий (Event Table).
У DHO900 четыре независимых декодера — Decode 1…Decode 4, работают одновременно (руководство с. 210; даташит с. 18). Типы шин: Parallel, RS232/UART, I2C, SPI, CAN и LIN. Источником может быть любой аналоговый канал CH1–CH4 или, с логическим щупом, цифровой D0–D15 (руководство с. 211, 217).
Общие пункты у всех шин (руководство с. 211–219):
- Bus Status — включить декодер;
- Threshold — порог для аналогового канала; пока его крутят, на экране видна пунктирная линия уровня, через 2 с она гаснет;
- Copy Trig — перенести в декодер настройки запуска по той же шине (36д.19): не надо дважды вводить скорость и полярность;
- Format — Hex, Dec, Bin или ASCII, для полосы и таблицы;
- Label — подпись типа шины над полосой;
- Event Table и Export — таблица событий и выгрузка её в CSV.
Зачем. Чтобы читать обмен, а не считать биты курсором. Пакет из 64 байт UART — 640 бит; вручную их не разобрать, а декодер выдаёт строку за долю секунды.
Что видно и чего не видно. Полоса декодера показывает то, что помещается на экране: у DHO900 при смене развёртки число строк таблицы меняется, а подробности на полосе появляются только при достаточно крупной развёртке (руководство с. 215). Чтобы прочитать длинную посылку целиком — таблица событий; чтобы рассмотреть байт — лупа (36г.12).
Где декодер выключен. У DHO900 декодирования нет в режиме самописца Roll (руководство с. 74) и в UltraAcquire (руководство с. 70; 36г.17). Выгрузка таблицы — только в состоянии STOP (руководство с. 215).
Ошибка. Порог по умолчанию не там, где середина перепада. Линия 3,3 В с порогом у самого нуля декодируется «почти правильно»: помеха на нуле превращается в лишний бит, и ошибка появляется раз в сотню кадров. Правило руководства DHO900 для логического щупа подходит и тут: порог — в середину размаха сигнала (руководство с. 31).
36ж.2 Сколько выборок на бит: запись решает всё
Что это. Декодер читает только то, что лежит в памяти. Частота выборки — глубина памяти, делённая на окно экрана (10 делений), но не больше предела АЦП (36г.14). Значит, число выборок на бит шины зависит от трёх ручек сразу: развёртки, глубины памяти и числа включённых каналов.
Модель. Три типичные посылки, развёртка выбрана так, чтобы вся посылка встала в окно (шаги 1-2-5):
| шина | посылка | длится | развёртка | 1 тыс. точек | 10 тыс. (по умолчанию) | наибольшая память |
|---|---|---|---|---|---|---|
| UART 115200, 1 канал | 64 байта 8N1 | 5,56 мс | 1 мс/дел | 0,868 на бит | 8,68 на бит | 10851 на бит |
| I2C 400 кГц, 2 канала | чтение 128 байт, 1179 тактов | 2,95 мс | 500 мкс/дел | 0,5 на такт | 5 на такт | 1563 на такт |
| SPI 8 МГц, 4 канала | 4 КБ из флеш-памяти, 32800 тактов | 4,10 мс | 500 мкс/дел | 0,025 на такт | 0,25 на такт | 39 на такт |
Наибольшая память у DHO900 — 50 млн точек на один канал, 25 млн на два и 10 млн на три-четыре (36г.1). При четырёх каналах SPI упирается уже не в память, а в АЦП: 312,5 Мвыб/с.
Как это сказывается на разборе. Простой декодер модели (ищет стартовый перепад и читает середины бит) на той же посылке UART, 20 разных фаз выборки, передатчик с точной скоростью и на 2 % быстрее:
| память | частота выборки | верных байт из 64 (худший случай) |
|---|---|---|
| 1 тыс. | 100 квыб/с | 0 |
| 10 тыс. | 1 Мвыб/с | 64 |
| 100 тыс. | 10 Мвыб/с | 64 |
I2C тем же способом (10 фаз; декодер ловит старт, стоп и читает SDA по фронту SCL): при 1 тыс. точек верно 1 событие из 136, при 10 тыс. и 100 тыс. — все 136.
С этим и без этого. На 1 тыс. точек I2C хуже, чем «мало точек»: 200 квыб/с ровно кратны такту 400 кГц, и все выборки попадают в одну фазу — SCL выглядит постоянной единицей. Это наложение частот (36г.15) на шине: прибор показывает уверенную, гладкую и ложную картинку.
Правило. Не меньше четырёх-пяти выборок на бит, лучше десять — так же, как у логического анализатора (36а.6). Для SPI на 8 МГц с четырьмя каналами по умолчанию (10 тыс.) — четверть выборки на такт: декодер не прочитает ничего. Нужны 10 млн точек (39 на такт) или цифровые каналы (36ж.9).
Ошибка. Растянуть развёртку, чтобы влезла вся посылка, и не поднять глубину памяти. Глубина по умолчанию у DHO900 — 10 тыс. точек (36г.14): для одного байта хватает, для пакета — уже нет.
Случай. Плата отвечает по UART через раз. На 200 мкс/дел и памяти 10 тыс. декодер читает всё; растянули на 10 мс/дел, чтобы увидеть весь обмен, — и пошли ошибки кадра, которых на проводе нет. Прибор перешёл на 100 квыб/с. Ошибки «исчезают», если поднять память до 1 млн точек.
36ж.3 UART и RS232
Что это. Асинхронная линия: каждый байт начинается стартовым битом (ноль), за ним 5–9 бит данных, бит чётности (если есть) и стоповые биты (единица). Часов на проводе нет — приёмник отсчитывает биты по скорости (Baud). У DHO900 до 20 Мбит/с (даташит с. 18).
Пункты меню DHO900 (руководство с. 216–219):
- Tx и Rx — два источника, у каждого свой порог; оба выключить нельзя;
- Polarity — Positive (высокий уровень — единица, как на выводе микроконтроллера) или Negative (как на линии RS-232 после преобразователя уровней, где логика перевёрнута);
- Baud — из списка или User со своим числом;
- Data — 5, 6, 7, 8 или 9 бит;
- Parity — None, Even или Odd; Stop Bit — 1, 1.5 или 2;
- Endian — LSB (младший бит первым, так работает UART) или MSB.
Что видно. Таблица событий UART у DHO900 — столбцы Line No., Time,
Tx/Rx, Data, Error (руководство с. 219). Модель: строка ADC=1234
с CR и LF, 8E1 (чётность Even), 115200, развёртка 200 мкс/дел, память
10 тыс. — 5 Мвыб/с, 43,40 выборки на бит; в пятом байте на линии
испорчен один бит. Таблица, которую строит декодер модели:
Line Time Tx Data Error
1 34.70 us 'A' 0x41
2 130.10 us 'D' 0x44
3 225.70 us 'C' 0x43
4 321.10 us '=' 0x3D
5 416.70 us '5' 0x35 Parity
6 512.10 us '2' 0x32
7 607.50 us '3' 0x33
8 703.10 us '4' 0x34
9 798.50 us '.' 0x0D
10 894.10 us '.' 0x0A
Вместо 1 пришло 0x35 — и только чётность говорит, что это ошибка,
а не данные. Без чётности (None) строка была бы ADC=5234, и никакой
пометки.
Порядок бит и полярность. Тот же байт 0x41 (A) при Endian MSB
читается как 0x82. Перепутанная полярность хуже: стартовый бит
ищется не там. В модели та же строка с Negative вместо Positive дала 9
«байт», из них ни одного верного и 2 ошибки кадра.
Ошибка. Скорость «примерно та». Предел расхождения скоростей и почему ошибка копится к концу кадра — 36д.19; там же запуск по ошибке кадра (Error) и чётности (Check Error).
Случай. Модуль GPS или модем шлёт строки, а прошивка их не понимает. Декодер на Tx модуля и Rx микроконтроллера (два канала, один декодер) показывает обе стороны с временем: видно, что ответ приходит раньше, чем прошивка включила приёмник.
36ж.4 I2C
Что это. Двухпроводная шина: SCL — такты от ведущего, SDA — данные в обе стороны. Посылка начинается условием START (SDA падает при высоком SCL), затем адрес и бит направления R/W, после каждого байта — бит подтверждения ACK (ноль) или NACK (единица), в конце STOP.
Пункты меню DHO900 (руководство с. 220–223): CLK и SDA — источники, SCL Thre и SDA Thre — у каждого свой порог; Exchange — поменять местами SCL и SDA, если щупы перепутаны; R/W — With или Without: показывать адрес с битом направления или без него.
R/W With и Without. Память с адресом 0x50 при Without показывается как 0x50, при With — 0xA0 на запись и 0xA1 на чтение. Это один и тот же адрес; в документации на разные микросхемы встречаются обе записи. Сверяя адрес по даташиту, выставьте тот же вид.
Что видно. Таблица событий I2C: Line No., Time, Address, R/W, Data, ACK (руководство с. 222). NACK после записи прибор выделяет красным (руководство с. 223) — это главный ответ на вопрос «почему датчик молчит»: адрес не тот, ведомый не запитан или занят.
Модель. Чтение 128 байт из памяти 0x50 (адрес ячейки — два байта, повторный старт, 128 байт данных, последний — с NACK, как требует шина) на 400 кГц: 136 событий; при 10 тыс. точек на 500 мкс/дел — все верны (36ж.2).
Ошибка. Порог SCL близко к верхнему уровню. Подтяжки на I2C медленные (фронт — RC подтяжки и ёмкости шины, 36д.9): высокий порог сдвигает момент «фронта» SCL позже, и SDA читается не в том месте. Порог — посередине размаха.
Случай. Датчик отвечает при отладке и молчит после холодного старта. Запуск по I2C MissedAck (36д.19), декодер с таблицей: первая транзакция идёт сразу после включения, когда датчик ещё не готов, — NACK. Дело в задержке старта в прошивке, а не в адресе.
36ж.5 SPI
Что это. Синхронная шина: такт CLK от ведущего, данные MOSI (от ведущего) и MISO (к ведущему), выбор ведомого CS. Бит читается по фронту такта — по нарастающему или спадающему, в зависимости от режима.
Пункты меню DHO900 (руководство с. 224–226):
- CLK с порогом и Slope — Rising или Falling: по какому фронту читать;
- MISO и MOSI — каждый со своим порогом, оба выключить нельзя;
- Mode — CS (начало слова — активный CS, своя полярность CS Polarity) или Timeout (начало слова — пауза такта длиннее заданной: от 8 нс до 10 с, по умолчанию 1 мкс; должна быть больше половины периода такта);
- Endian — LSB или MSB; Polarity данных; Width — длина слова от 4 до 32 бит, по умолчанию 8.
Timeout в числах. SPI на 8 МГц: половина периода — 62,5 нс. Пауза должна быть больше этого и меньше промежутка между словами. 1 мкс по умолчанию годится, если ведущий делает паузы между байтами больше 1 мкс; DMA шлёт байты подряд — тогда только CS.
Что видно с неверным фронтом. Режим 0: данные меняются по спаду, читаются по нарастанию. Модель: ведомый отвечает 0x33; с Slope Rising декодер читает 0x33, с Falling — 0x66: каждый бит взят на такт позже, байт сдвинут на разряд. Такой сдвиг — подпись перепутанного режима (разделы 18 и 36д.17).
Width. Команда 0x8F и пустой байт при Width 16 покажутся одним словом 0x8F00 — удобно для 16-битных регистров, неудобно для байтового протокола.
Ошибка. Четыре аналоговых канала на длинную посылку: память делится, частота выборки падает (36ж.2). SPI с CS удобнее снимать цифровыми каналами.
Случай. Дисплей показывает мусор. Декодер MOSI с CS — команды верные; MISO не нужен. Включили второй декодер на тот же CLK с Slope Falling — данные совпали: дисплей настроен на другой режим.
36ж.6 CAN
Что это. Дифференциальная шина автомобилей и станков. Бит передаётся разностью CAN_H и CAN_L; каждый узел слышит всех. В кадре — идентификатор, длина (DLC), данные, контрольная сумма CRC и бит подтверждения ACK. Чтобы приёмники не теряли синхронизацию, после пяти одинаковых бит передатчик вставляет противоположный (bit stuffing).
Пункты меню DHO900 (руководство с. 231–232): Source с порогом; Signal — CAN_H, CAN_L, Rx, Tx или Diff; Baud — из списка или User; Sample Position — где внутри бита читать уровень, от 10 % до 90 %. До 5 Мбит/с (даташит с. 19).
Signal. Rx и Tx — ножки трансивера со стороны микроконтроллера, обычный щуп. CAN_H или CAN_L — один провод шины. Diff — разность дифференциальным щупом: плюс на CAN_H, минус на CAN_L (руководство с. 231). Почему не «два канала и математика A−B» — 36е.17 и 36з.
Модель кадра. Стандартный кадр, ID 0x123, 8 байт данных 11 22 33 44 55 66 77 88: до вставки 98 бит от начала кадра до CRC, вставлен 1 бит, CRC 0x4237; весь кадр с полем подтверждения, концом кадра и паузой — 112 бит, на 500 кбит/с — 224 мкс. Развёртка 50 мкс/дел, память 10 тыс.: 40 выборок на бит — хватает с запасом.
Sample Position. Бит на 500 кбит/с — 2 мкс; при 75 % прибор читает уровень через 1,5 мкс от начала бита. Ставят так же, как точка выборки в настройках контроллера сети: где читает узел, там должен читать и прибор, иначе на длинной шине с отражениями они увидят разное.
Что видно. Таблица: Line No., Time, Frame ID, DLC, Data, CRC, ACK (руководство с. 233). ACK, равный единице (никто не подтвердил), и ошибка вставки бит (Stuff) выделяются.
Ошибка. Узел один на шине — некому подтвердить кадр, и передатчик шлёт его снова и снова. Декодер покажет ровную серию кадров с ошибкой ACK: дело не в прошивке, а во втором узле или терминаторе.
Случай. Плата на STM32 с bxCAN не видит станок. Декодер на Rx трансивера: кадры станка читаются без ошибок, только если поставить в декодере другую скорость, чем выставлена в прошивке, — значит, ошибка в делителе bxCAN, а не в проводах.
36ж.7 LIN
Что это. Однопроводная медленная шина кузовной электроники: ведущий шлёт заголовок (длинный ноль Break, байт синхронизации 0x55, защищённый идентификатор PID), ведомый — данные и контрольную сумму.
Пункты меню DHO900 (руководство с. 228–229): Source с порогом; Baud Rate — из списка или свой; Parity bit — With или Without; Version — 1.X, 2.X или Both. Таблица: Line No., Time, Frame ID, Data, Checksum, Error (руководство с. 229); ошибка контрольной суммы — красным (руководство с. 230).
Version — это контрольная сумма. В LIN 1.X сумма считается только по данным («классическая»), в 2.X — по PID и данным («расширенная»). Модель: ID 0x10 даёт PID 0x50, данные 01 02 03 04 — классическая сумма 0xF5, расширенная 0xA5. Не та версия — каждая посылка красная, хотя шина исправна.
Длина кадра. Break 13 бит, разделитель, три байта заголовка и 4 байта данных — 84 бита, на 19200 бит/с — 4,38 мс.
Ошибка. Both, когда на шине узлы обеих версий, — удобно; Both по привычке — и красная сумма у своего узла незаметна среди «чужих».
36ж.8 Параллельная шина (Parallel)
Что это. Несколько линий данных и, обычно, такт. Прибор читает все линии разом по фронту такта: Rising, Falling или Both (руководство с. 210–211). Если такта нет (CLK = OFF), читает при каждом изменении данных.
Пункты меню DHO900 (руководство с. 212–214): BUS — готовые сочетания каналов (D7-D0, D15-D8, D15-D0 и обратные) или User со своей шириной и назначением каждого бита; с аналоговыми каналами — до 4 бит (даташит с. 18); Endian и Polarity.
Зачем. Параллельный дисплей, шина ЦАП или АЦП, состояние автомата на выводах-метках (36а.7): четыре вывода отладки — 16 состояний на одной полосе вместо четырёх линий.
Ошибка. CLK = OFF на шине без такта: линии переключаются не строго вместе, и между состояниями появляются короткие «промежуточные» значения — у DHO900 перекос между цифровыми каналами ±5 нс (даташит с. 12). Читают по такту или по линии-стробу.
36ж.9 Шестнадцать цифровых каналов и логический анализатор
Что это. У DHO900 есть 16 цифровых каналов D0–D15 — встроенный логический анализатор (LA). Каждый канал сравнивает напряжение с порогом и хранит один бит. Нужен активный логический щуп PLA2216, он покупается отдельно (даташит с. 9; руководство с. 177).
Характеристики DHO900 (даташит с. 11–12; руководство с. 178–180):
| пункт | значение |
|---|---|
| порог | своё значение для D0–D7 и для D8–D15; ±15 В шагом 10 мВ |
| готовые пороги | TTL 1,4 В, CMOS3.3 1,65 В, CMOS2.5 1,25 В, CMOS1.8 0,9 В и другие |
| точность порога | ±(100 мВ + 3 % от порога) |
| наименьший размах сигнала | 500 мВ |
| вход | около 101 кОм и около 8 пФ |
| наибольшее напряжение | ±40 В |
| самый короткий импульс | 5 нс |
| перекос между каналами | ±5 нс |
Порог в числах. Порог CMOS3.3 1,65 В при точности ±149,5 мВ. Вход F103 гарантированно видит ноль ниже 1,155 В и единицу выше 2,145 В (36а.6) — порог с запасом в обе стороны. Для 1,8-вольтовой логики — свой порог: у DHO900 он задаётся на группу из восьми каналов, поэтому линии 3,3 В и 1,8 В разводят по разным группам (в даташите есть и порог User для отдельного канала, в руководстве он не описан).
Нагрузка. 8 пФ на частоте 10 МГц — это 1989 Ом: на быстрой линии щуп заметен. Длинный общий провод земли — та же беда, что у аналогового щупа (36а.2); руководство советует землю у каждого сигнала (с. 31).
Пункты меню (руководство с. 178–180): включение по одному или группой D0–D7, D8–D15; размер Small, Medium, Large (Large — если включено не больше 8 каналов); порядок D0-D15 или D15-D0; подписи из готового списка (CLK, CS, MISO, MOSI, RX, TX, DATA, ADDR и другие) или свои; калибровка щупа при первом подключении и при смене температуры больше чем на 5 °C.
Что нельзя одновременно. У DHO900 цифровые каналы несовместимы с режимом Average (руководство с. 69), не работают в медленной развёртке и в Roll (руководство с. 74). Щуп нельзя вставлять и вынимать при включённом приборе (руководство с. 177).
Сравнение с приставкой-анализатором (36а.6). Дешёвая приставка — 8 каналов, 24 Мвыб/с, запись идёт в компьютер и длится сколько угодно: на UART 115200 это 208 выборок на бит хоть час подряд. Осциллограф держит запись в своей памяти: на тех же 10 выборках на бит DHO900 запишет одним аналоговым каналом (50 млн точек) 43,4 с, четырьмя (10 млн) — 8,68 с. Частоту выборки и глубину памяти цифровых каналов документы DHO900 прямо не называют (в сноске к перекосу упомянуты 625 Мвыб/с — даташит с. 26).
| что нужно | лучше |
|---|---|
| форма фронта, звон, уровни | аналоговые каналы осциллографа |
| шина и аналог вместе: байт SPI и ток двигателя | цифровые каналы + аналоговые одного прибора |
| час обмена, редкий сбой в протоколе | приставка-анализатор с записью в компьютер |
| SPI 8 МГц и выше, много линий | цифровые каналы или быстрый анализатор |
Ошибка. Поверить цифровому каналу в вопросе уровня. Он скажет «1» и на 1,7 В, и на 3,3 В — плохую единицу видно только на аналоговом канале.
Случай. Шаговый двигатель пропускает шаги. Четыре аналоговых канала — токи и напряжение питания, цифровые D0–D3 — STEP, DIR, ENABLE и вывод-метка прерывания: видно, что пропуск совпадает с провалом питания, а не с ошибкой в счёте шагов.
36ж.10 Маски (Pass/Fail)
Что это. Проверка «годен — не годен» по каждой записи. Вокруг эталонной осциллограммы строится коридор: X Mask — допуск по времени, Y Mask — по напряжению (в делениях); всё, что вне коридора, закрашено. Если запись задела закрашенное — брак (руководство с. 206, 208).
Пункты меню DHO900 (руководство с. 205–209):
- Source — CH1–CH4;
- X Mask, Y Mask, Create — построить маску; Save и Load — файл *.pf;
- Aux Output, Output Event (Pass или Fail), Polarity, Pulse — импульс на разъём [AUX OUT] задней панели от 100 нс до 10 мс, по умолчанию 1 мкс;
- Err Action — Stop (остановиться на браке), Beeper (пискнуть) или Screenshot (снимок на флешку или во внутреннюю память; сбор при этом останавливается и потом продолжается);
- статистика: Failed, Passed, Total Frames; Reset Stat — сбросить.
Во время проверки нельзя менять источник и маску (руководство с. 208); в UltraAcquire и в Roll проверки нет (руководство с. 70, 74).
Модель: маска против шума. Сигнал на 0,5 В/дел с шумом 20 мВ; маска сравнивается в условных 1000 точках записи:
| Y Mask | допуск в шумах | выброс за допуск в одной точке | запись забракована |
|---|---|---|---|
| 0,1 дел (50 мВ) | 2,5 σ | 0,0124 | 1 — все 2000 из 2000 в розыгрыше |
| 0,3 дел (150 мВ) | 7,5 σ | 6,38e−14 | 6,38e−11 — 0 из 2000 |
Узкая маска бракует исправную плату: шум хоть в одной из тысячи точек выйдет за 2,5 σ почти наверняка. С допуском 0,3 дел шум не мешает, а настоящий дефект — пропущенный импульс в среднем раз на тысячу записей — пойман весь: из 10000 записей бракованных было 11, маска нашла 11.
X Mask и дрожание. Фронт дрожит со стандартным отклонением 2 нс, развёртка 10 нс/дел. X Mask 0,2 дел (2 нс) — 31,7 % записей в браке; 0,5 дел (5 нс) — 1,24 %. Допуск по времени выбирают по разбросу фронта, измеренному статистикой или гистограммой (36е.14, 36е.15).
Зачем. Испытание на ночь: прибор смотрит на выход, пищит или снимает экран при браке и считает долю брака. На производстве — импульс AUX OUT на счётчик или в контроллер стенда.
Ошибка. Строить маску по одной случайной записи, где шум сложился удачно, и с допуском «в обтяжку».
Случай. Плата управления клапаном должна выдавать импульс заданной длины с заданной задержкой после команды. Маска на выходе, запуск по команде (36д), Err Action Screenshot, ночь работы — утром несколько снимков, и во всех задержка совпала с записью во флеш-память.
36ж.11 Запись осциллограмм и сегментированная память
Что это. Вместо одной длинной записи прибор пишет много коротких — по одной на каждый запуск — и хранит их рядом. Между запусками память не тратится. У DHO900 так устроены две функции.
Waveform Recording (руководство с. 236–240; даташит с. 9, 17). До 500000 кадров (даташит с. 9). Пункты: Interval — промежуток между кадрами, от 10 нс до 1 с; Frames — сколько записать; Max Frames — сколько влезет: чем больше глубина памяти, тем меньше кадров (руководство с. 238); Beeper — сигнал в конце. Воспроизведение: по одному кадру или подряд, вперёд или назад, Interval показа от 1 мс до 1 с, начальный и конечный кадр. У каждого кадра — ΔT, время от первого кадра. Воспроизведённые кадры можно мерить и декодировать (даташит с. 17). В UltraAcquire запись недоступна (руководство с. 236).
UltraAcquire (36г.17) — тоже сегменты, но ради скорости: до 1 000 000 осциллограмм/с, просмотр через навигацию Frame Segment (36ж.12). Декодирования в нём нет.
Модель: зачем сегменты. Датчик отдаёт по I2C 400 кГц 20 байт раз в 100 мс; посылка — 0,5175 мс, то есть 0,517 % времени. Нужно посмотреть 1000 посылок подряд.
- Одной записью: 100 с — это 10 с/дел; даже 50 млн точек дают 1,25 выборки на такт. Декодер не прочитает ничего (36ж.2), а на такой развёртке прибор вообще уходит в Roll, где декодера нет.
- Сегментами: каждая посылка — кадр на 100 мкс/дел, память 10 тыс.: 25 выборок на такт. 1000 кадров — 10 млн точек полезных данных.
ΔT — бесплатный журнал времени. Модель: 1000 кадров задачи, которая должна приходить раз в 100 мс, разброс 0,30 мс. Наибольший промежуток — 200,0 мс у кадра 618: один пакет пропущен. Найти это глазами на одной длинной записи нельзя, по столбцу ΔT — сразу.
Сколько смотреть. Воспроизведение 500000 кадров даже с наименьшим интервалом 1 мс — 500 с, 8,3 минуты. Длинные записи не смотрят подряд — их обходят поиском и навигацией (36ж.12).
Ошибка. Запись без настроенного запуска (36д): кадры ложатся куда попало, и большая часть из них — пауза между пакетами. Сегменты имеют смысл только с запуском, который срабатывает на начале нужного события.
Случай. Раз в несколько минут подвисает обмен с датчиком. Запуск по START I2C, запись тысяч кадров, утром — кадры с NACK в воспроизведении и их ΔT: сбои идут через равные промежутки, в одно время с записью журнала на SD-карту.
36ж.12 Поиск и навигация
Что это. Поиск (Search) отмечает в уже снятой записи все места, где выполнено условие, и даёт по ним ходить. У DHO900 условия — Edge (фронт) и Pulse (ширина импульса), источник — аналоговый канал (руководство с. 241–242; даташит с. 15). Найденное отмечается треугольниками над сеткой и собирается в таблицу MarkTable; её можно сохранить в CSV (руководство с. 243). Включение поиска само включает лупу (руководство с. 241). To Trigger и From Trigger переносят условие между поиском и запуском (руководство с. 242).
Навигация (руководство с. 243–246; даташит с. 15) — только в состоянии STOP:
- Time — прокрутка записи во времени, со своей скоростью;
- Search Event — к предыдущему, следующему, первому, последнему найденному;
- Frame Segment — по сегментам UltraAcquire страницами: начальный 3 и конечный 9 — 7 сегментов на странице.
Модель: почему глазами не найти. Запись 50 млн точек на 1,25 Гвыб/с — 40 мс. Экран у DHO900 — 1024 точки по ширине (даташит с. 9), а сетка ещё уже, так что на один столбец экрана приходится не меньше 48828 выборок. Помеха 20 нс — 25 выборок, не больше 0,0512 % столбца. Её либо не видно, либо видно как черту без формы. Поиск Pulse «короче 50 нс» по той же записи (ШИМ 20 кГц и три помехи в разных местах): из 802 импульсов найдены ровно 3 — на 5,6320, 18,7740 и 32,8400 мс.
Ошибка. Искать в записи, которой нет: поиск работает по памяти, а не по экрану. Короткая помеха, проскочившая между выборками (36г.16), не найдётся — сначала глубина памяти или Peak.
Случай. ШИМ двигателя изредка даёт «иглу». Single на 4 мс/дел с 50 млн точек, Search Pulse короче 50 нс, таблица MarkTable: три иглы, все — сразу после смены скважности. Ошибка в обновлении регистра сравнения таймера без предзагрузки (раздел 13).
36ж.13 Опорные осциллограммы (Ref)
Что это. Копия осциллограммы, которая остаётся на экране, пока идёт новая запись. У DHO900 источник — CH1–CH4, D0–D15 или Math, на экране до 10 опорных (руководство с. 202; даташит с. 26); пять цветов, своя шкала и смещение, подпись (руководство с. 202–203).
Зачем. Сравнить «до» и «после» правки: фронт до и после последовательного резистора, пульсации до и после второго конденсатора.
Главная ловушка. SaveToRef кладёт копию в память, которая стирается при выключении и сбросе настроек (руководство с. 202). Чтобы сохранить — Export в .ref, .bin или .csv; загрузить обратно можно только .ref (руководство с. 203–204).
Ошибка. Сравнивать опорную и живую при разном масштабе: VScale опорной меняется отдельно. Reset возвращает её на место исходного канала.
36ж.14 Сохранение: снимки, данные, настройки — и чтение в Python
Что это. Меню Storage → Save (руководство с. 250–255):
| что | форматы | пункты |
|---|---|---|
| Save Image — снимок экрана | .png, .bmp, *.jpg | Invert, Color или Gray, Header (модель и дата в шапке) |
| Save Wave — данные | Screen: .bin, .csv; Memory: .bin, .csv, *.wfm | Data Source: экран или вся память |
| Save Setup — настройки | *.stp | загружаются через Load |
Путь — внутренний диск или флешка (по умолчанию флешка, если вставлена, руководство с. 252; FAT32 или NTFS — с. 261); Overlay — перезаписать файл с тем же именем. Кнопка быстрого действия на панели сохраняет одним нажатием снимок, данные, настройки или всё сразу — Save Group (руководство с. 270–271). Внутренняя память — 8 ГБ (даташит с. 26).
Screen или Memory. Screen — то, что нарисовано; Memory — вся запись. Для обработки на компьютере — только Memory: экранная копия прорежена.
BIN, а не CSV. Формат *.bin описан в руководстве DHO900
(с. 255–258): заголовок файла 16 байт (метка RG, версия, размер
файла, число осциллограмм), на каждый канал — заголовок осциллограммы
140 байт и заголовок данных 16 байт, затем точки float32. Размер
файла: 1 млн точек одного канала — 4000172 байт; 10 млн на четыре
канала — 160000640 байт. Текст на ту же точку в нашем формате
«время,значение» занял 26 байт — в 6,6 раза больше, и читается в разы
дольше.
Чего нет в описании. Поля заголовка осциллограммы в таблице руководства дают 128 байт из 140; 12 байт не описаны. Порядок байт тоже не назван. Поэтому читатель берёт размер заголовка из самого файла, а не из константы:
# read_bin.py — чтение файла *.bin осциллографа DHO900 (раскладка из руководства)
import struct, sys
import numpy as np
def read_bin(path):
raw = open(path, "rb").read()
cookie, ver, size, nwav = struct.unpack_from("<2sHqi", raw, 0)
assert cookie == b"RG" and size == len(raw), "не тот файл или обрезан"
pos, out = 16, {}
for _ in range(nwav):
hsize, wtype, nbuf, npts = struct.unpack_from("<iiii", raw, pos)
dt, x0 = struct.unpack_from("<dd", raw, pos + 32)
name = raw[pos + 112:pos + 128].split(b"\0")[0].decode()
pos += hsize # размер заголовка — из файла, не 140
dsize, btype, bpp, nbytes = struct.unpack_from("<ihhq", raw, pos)
pos += dsize
y = np.frombuffer(raw, "<f4", nbytes // 4, pos)
pos += nbytes
out[name] = (x0 + dt * np.arange(npts), y)
return out
if __name__ == "__main__":
for name, (t, y) in read_bin(sys.argv[1]).items():
print("%s: %d точек, шаг %.3g с, от %.3g до %.3g В" % (name, len(y), t[1] - t[0], y.min(), y.max()))
В прогоне модели этот файл читает собранный по описанию руководства файл на два канала по 1000 точек (8328 байт) и возвращает точки байт в байт:
CH1: 1000 точек, шаг 1e-06 с, от 0 до 3.3 В
CH2: 1000 точек, шаг 1e-06 с, от -0.5 до 0.5 В
Прочитать CSV проще — numpy.loadtxt(path, delimiter=",", skiprows=…),
но число строк шапки и имена столбцов CSV в руководстве DHO900 не
описаны: сначала откройте файл текстовым редактором. Формат *.wfm не
описан вовсе. Таблицы событий декодера и найденного поиском — тоже CSV.
Ошибка. Save Wave с Data Source = Screen, а потом удивляться, что в файле тысяча точек вместо миллиона. И второе — снимок экрана как единственный документ испытания: по картинке не пересчитать ни одного числа. Снимок для отчёта, BIN — для повторной обработки, STP — чтобы через месяц выставить прибор так же.
Случай. Испытание блока питания на пуск: Save Group на кнопке — снимок, данные и настройки одним нажатием после каждого прогона. Скрипт из 115в собирает файлы и строит сводку по всем пускам.
36ж.15 Управление по SCPI: команды DHO900 из Python
Что это. SCPI — текстовый язык команд измерительных приборов: программа шлёт строку, прибор отвечает строкой. У DHO900 два пути (руководство с. 273–275; даташит с. 23):
- USB DEVICE на задней панели — прибор виден как USB-TMC;
- LAN 10/100 Мбит/с — по сети; прибор соответствует LXI.
За командами руководство пользователя отправляет в отдельный
документ — Programming Guide (руководство с. 273). Это DHO800/DHO900 Programming Guide
(Books/DHO800900_ProgrammingGuide_EN.pdf, дальше — «программирование»,
страницы по PDF-файлу). Другой файл в Books/, DHO_programming_manual.pdf, —
для серий DHO1000/DHO4000; его команды к DHO900 не применяют.
Адрес прибора. VISA-адрес написан в меню Utility → IO (руководство
с. 263); по сети его же отдаёт запрос :LAN:VISA? — с параметром USB,
LXI или SOCKet, без параметра возвращается адрес LXI (программирование
с. 167–168). В том же меню Utility → IO — DHCP, Auto IP или Static IP и имя для mDNS
(руководство с. 264–265). Номер TCP-порта для голого сокета числом
в руководствах не назван: он — часть адреса, который вернёт :LAN:VISA? SOCKet.
Как писать команды (программирование с. 25–27, 33):
- уровни через двоеточие:
:TRIGger:EDGE:LEVel; запрос кончается?; - регистр не важен, сокращение — заглавные буквы:
:WAV:DATA?то же, что:WAVeform:DATA?; - единицы не пишутся: прибор понимает только число в единицах по
умолчанию (вольты, секунды) —
:CHANnel1:SCALe 1, а не1V.
Минимальный набор для стенда (страницы — программирование):
| задача | команды | с. |
|---|---|---|
| кто ты, сброс, ошибки | *IDN?, *RST, *OPC?, *CLS, :SYSTem:ERRor? |
148–151, 290 |
| канал | :CHANnel1:DISPlay, :SCALe, :OFFSet, :COUPling, :PROBe |
93–98 |
| развёртка и память | :TIMebase:MAIN:SCALe, :ACQuire:MDEPth |
314, 42 |
| запуск | :TRIGger:MODE EDGE, :TRIGger:EDGE:SOURce, :SLOPe, :LEVel, :TRIGger:SWEep |
322–329 |
| сбор | :SINGle, :RUN, :STOP, :TFORce, :TRIGger:STATus? |
33–35, 324 |
| чтение памяти | :WAVeform:SOURce, :MODE, :FORMat, :STARt, :STOP, :PREamble?, :DATA? |
426–436 |
| измерения | :MEASure:ITEM, :MEASure:ITEM? |
217–218 |
| снимок экрана | :DISPlay:DATA? |
134–135 |
Ожидание конца. Из всех команд только *RST выполняется
параллельно со следующими (таблица на с. 32), поэтому после сброса
ждут *OPC?. На с. 31 сказано, что *OPC? ждёт и отвечает 1, на
с. 151 — что до конца операции он отвечает 0; надёжно — спрашивать,
пока не придёт 1. :SINGle переводит запуск в режим Single (то же,
что :TRIGger:SWEep SINGle, с. 35); конец одиночного сбора видно по
:TRIGger:STATus? — он отвечает TD, WAIT, RUN, AUTO или STOP (с. 324).
Режимы чтения (программирование с. 427–429):
- NORMal — то, что на экране: не больше 1000 точек, шаг XINCrement — развёртка/100; на 500 мкс/дел это 5 мкс;
- RAW — вся память, только в состоянии STOP; шаг — 1/частота выборки: на 500 мкс/дел и 1 млн точек — 5 нс, в 1000 раз подробнее;
- MAXimum — в RUN как NORMal, в STOP как RAW.
Формат — BYTE (точка — байт, по умолчанию), WORD (два байта) или ASCii
(числа в вольтах через запятую). Диапазон точек — :WAVeform:STARt
и :WAVeform:STOP; руководство говорит, что память можно читать
частями, и каждая часть приходит своим блоком (с. 426).
Преамбула :WAVeform:PREamble? — 10 чисел через запятую (с. 436):
| поле | что значит |
|---|---|
| format | 0 — BYTE, 1 — WORD, 2 — ASCii |
| type | 0 — NORMal, 1 — MAXimum, 2 — RAW |
| points | число точек, до 50 000 000 |
| count | число усреднений в режиме Average, иначе 1 |
| xincrement | шаг по времени, с |
| xorigin | время первой точки, с |
| xreference | опорная точка по времени (первая точка, 0) |
| yincrement | шаг по напряжению, В на код |
| yorigin | смещение в кодах относительно опорного уровня |
| yreference | опорный уровень в кодах |
Код точки переводят в вольты так: (код − YORigin − YREFerence) ×
YINCrement (с. 426); время точки — XORigin плюс её номер × XINCrement.
Пример преамбулы из руководства — 0,0,1000,1,1.000000E-8,-5.000000E-6,0.000000E-12,4.000000E-03,0,128:
1000 точек по 10 нс — окно 10 мкс, YINCrement 4 мВ — это 0,1 В/дел
(в NORMal YINCrement = масштаб/25, с. 432). Байт 0x8E в этом примере —
0,056 В.
Двоичный блок. :WAVeform:DATA? в BYTE и WORD и :DISPlay:DATA?
отвечают блоком #N<длина><данные> и завершающим символом: N — сколько
цифр в длине, дальше сама длина в байтах. Пример руководства —
#9000001000…: 9 цифр, 1000 байт данных (с. 425); у снимка экрана —
#9000387356, 387356 байт (с. 135). Заголовок блока с длиной в 9 цифр —
11 байт.
Листинг: одна осциллограмма в вольтах. Сброс, канал CH1 со щупом
1:10, 1 В/дел и смещением −1,6 В, 500 мкс/дел, 1 млн точек, запуск по
фронту 1,6 В, Single, ожидание STOP, чтение памяти частями по 250000
точек, перевод в вольты по преамбуле, своя частота и :MEASure для
сравнения:
# scpi_capture.py — одна осциллограмма с DHO900 по SCPI: настройка, Single, память, вольты
import sys, time
import numpy as np
import pyvisa
ADDR = sys.argv[1] # VISA-адрес: Utility → IO или ответ :LAN:VISA?
CHUNK = 250000 # точек за одно чтение :WAVeform:DATA?
scope = pyvisa.ResourceManager().open_resource(ADDR)
scope.timeout = 10000 # мс
scope.read_termination = "\n"
scope.write_termination = "\n"
def cmd(s):
"""Команда и сразу проверка очереди ошибок."""
scope.write(s)
err = scope.query(":SYSTem:ERRor?").strip()
if int(err.split(",")[0]) != 0:
raise RuntimeError(s + " -> " + err)
print(scope.query("*IDN?").strip())
scope.write("*RST")
while scope.query("*OPC?").strip() != "1": # *RST идёт параллельно с другими командами
time.sleep(0.1)
cmd(":CHANnel1:DISPlay ON")
cmd(":CHANnel1:PROBe 10")
cmd(":CHANnel1:SCALe 1") # В/дел, единицы не пишутся
cmd(":CHANnel1:OFFSet -1.6") # В
cmd(":CHANnel1:COUPling DC")
cmd(":TIMebase:MAIN:SCALe 0.0005") # 500 мкс/дел
cmd(":ACQuire:MDEPth 1M")
cmd(":TRIGger:MODE EDGE")
cmd(":TRIGger:EDGE:SOURce CHANnel1")
cmd(":TRIGger:EDGE:SLOPe POSitive")
cmd(":TRIGger:EDGE:LEVel 1.6")
cmd(":SINGle")
t0 = time.time()
while scope.query(":TRIGger:STATus?").strip() != "STOP":
if time.time() - t0 > 5:
raise TimeoutError("нет запуска за 5 с")
time.sleep(0.05)
cmd(":WAVeform:SOURce CHANnel1")
cmd(":WAVeform:MODE RAW") # вся память, только в STOP
cmd(":WAVeform:FORMat BYTE")
pre = scope.query(":WAVeform:PREamble?").split(",")
npts = int(pre[2])
xinc, xorg, xref = float(pre[4]), float(pre[5]), float(pre[6])
yinc, yorg, yref = float(pre[7]), float(pre[8]), float(pre[9])
raw = b""
for start in range(1, npts + 1, CHUNK):
cmd(":WAVeform:STOP %d" % min(start + CHUNK - 1, npts))
cmd(":WAVeform:STARt %d" % start)
raw += scope.query_binary_values(":WAVeform:DATA?", datatype="B", container=bytes)
code = np.frombuffer(raw, np.uint8).astype(float)
volt = (code - yorg - yref) * yinc # формула руководства по программированию
t = xorg + (np.arange(npts) - xref) * xinc
mid = (volt.max() + volt.min()) / 2
up = np.flatnonzero((volt[:-1] < mid) & (volt[1:] >= mid))
f_own = (len(up) - 1) / (t[up[-1]] - t[up[0]])
cmd(":MEASure:ITEM FREQuency,CHANnel1")
cmd(":MEASure:ITEM VPP,CHANnel1")
f_meas = float(scope.query(":MEASure:ITEM? FREQuency,CHANnel1"))
vpp = float(scope.query(":MEASure:ITEM? VPP,CHANnel1"))
scope.close()
print("точек %d, шаг %.3g с, от %.3g с; %.2f…%.2f В" % (npts, xinc, xorg, volt.min(), volt.max()))
print("частота: по данным %.1f Гц, :MEASure %.1f Гц; размах %.2f В" % (f_own, f_meas, vpp))
Против подставного прибора (scpi_test.py: он знает только команды
из руководства и отвечает в его форматах; на CH1 — ШИМ 1 кГц 0…3,3 В
с шумом 10 мВ) листинг выдал:
RIGOL TECHNOLOGIES,DHO924,DHO9A000000000,00.02.01
точек 1000000, шаг 5e-09 с, от -0.0025 с; -0.04…3.36 В
частота: по данным 1000.0 Гц, :MEASure 1000.0 Гц; размах 3.40 В
Память прочитана за 4 запроса :WAVeform:DATA?. При смещении −1,6 В
и 1 В/дел YINCrement — 0,04 В, YORigin — −40 кодов: 3,3 В — код 170,
0 В — код 88, а весь байт от 0 до 255 охватывает от −3,52 до 6,68 В.
Снимок экрана голым сокетом — без VISA, только стандартная
библиотека; здесь виден разбор блока #N вручную:
# scpi_sock.py — снимок экрана DHO900 через голый сокет: двоичный блок #N
import socket, sys
HOST, PORT, OUT = sys.argv[1], int(sys.argv[2]), sys.argv[3] # порт — из :LAN:VISA? SOCKet
def read_exact(s, n):
buf = b""
while len(buf) < n:
part = s.recv(min(65536, n - len(buf)))
if not part:
raise ConnectionError("прибор закрыл соединение")
buf += part
return buf
def read_block(s):
if read_exact(s, 1) != b"#":
raise ValueError("нет заголовка блока #")
ndig = int(read_exact(s, 1)) # N — сколько цифр в длине
size = int(read_exact(s, ndig)) # длина данных, байт
data = read_exact(s, size)
read_exact(s, 1) # завершающий перевод строки
return data
with socket.create_connection((HOST, PORT), timeout=10) as s:
s.sendall(b":DISPlay:DATA? PNG\n")
png = read_block(s)
open(OUT, "wb").write(png)
print("снимок: %d байт, подпись PNG: %s" % (len(png), png[:8] == b"\x89PNG\r\n\x1a\n"))
снимок: 69 байт, подпись PNG: True
Ошибки — в очереди. Неверная команда не ломает связь: прибор
кладёт ошибку в очередь, и :SYSTem:ERRor? возвращает её как
<номер>,"<текст>", например -113,"Undefined header; command cannot be found"
(с. 290). Функция cmd() в листинге спрашивает очередь после каждой
команды: так опечатка в имени команды видна сразу, а не через десять
строк пустыми данными. В прогоне все 27 разных команд листингов
приняты, ошибок — 0.
Сколько стоит выгрузка. 10 млн точек по LAN 100 Мбит/с — не меньше 0,8 с в BYTE и 3,2 с, если бы на точку шло четыре байта; по USB 2.0 (480 Мбит/с) — 0,167 и 0,667 с. Это нижние пределы по скорости провода; настоящая скорость — опыт.
Ошибка. Читать RAW во время RUN: память пишется заново, и
руководство разрешает RAW только в STOP (с. 427–428). Вторая — забыть
терминатор \n: прибор ждёт конца команды, программа — ответа, обе
стоят до тайм-аута. Третья — слать команды, пока человек крутит
ручки, — настройки сменятся у него под руками.
36ж.16 Скрипт-испытатель: прошить, поймать, измерить, записать
Зачем. Ручная проверка платы после каждой правки прошивки — пять минут и шанс забыть шаг. Скрипт делает то же за секунды и оставляет журнал: какой прибор, когда, что намерил, годна ли плата. Устройство такого скрипта — 115в; здесь в роли «прибора на другом конце» — осциллограф.
Сценарий. Прошивка выдаёт на выводе ШИМ 1 кГц со скважностью 25 % (импульс 250 мкс). Скрипт:
- прошивает плату внешней командой (программатор задаётся строкой — любой, который работает из командной строки);
- настраивает развёртку и запуск по фронту на CH1, включает
измерения ширины и частоты, даёт
:SINGle; - ждёт STOP не дольше 3 с — нет фронта, значит прошивка не запустилась, и это тоже результат;
- читает
:MEASure:ITEM? PWIDth,CHANnel1иFREQuency, сравнивает с ожидаемым (допуск 1 %); - дописывает строку в CSV-журнал и возвращает код выхода 0 или 1 — его поймает CI или make.
# bench_pwm.py — испытатель: прошить плату, поймать ШИМ на CH1, сравнить, записать в журнал
import csv, shlex, subprocess, sys, time
import pyvisa
ADDR, FLASH, LOG = sys.argv[1], sys.argv[2], sys.argv[3]
WANT_W, WANT_F, TOL = 250e-6, 1000.0, 0.01 # ширина, частота, допуск 1 %
subprocess.run(shlex.split(FLASH), check=True, timeout=120) # прошивка платы
scope = pyvisa.ResourceManager().open_resource(ADDR)
scope.timeout = 10000
scope.read_termination = scope.write_termination = "\n"
for c in (":TIMebase:MAIN:SCALe 0.0005", ":TRIGger:MODE EDGE",
":TRIGger:EDGE:SOURce CHANnel1", ":TRIGger:EDGE:SLOPe POSitive",
":TRIGger:EDGE:LEVel 1.6", ":MEASure:ITEM PWIDth,CHANnel1",
":MEASure:ITEM FREQuency,CHANnel1", ":SINGle"):
scope.write(c)
t0 = time.time()
while scope.query(":TRIGger:STATus?").strip() != "STOP":
if time.time() - t0 > 3:
sys.exit("FAIL: нет фронта на CH1 за 3 с")
time.sleep(0.05)
width = float(scope.query(":MEASure:ITEM? PWIDth,CHANnel1"))
freq = float(scope.query(":MEASure:ITEM? FREQuency,CHANnel1"))
idn = scope.query("*IDN?").strip()
scope.close()
ok = abs(width - WANT_W) <= TOL * WANT_W and abs(freq - WANT_F) <= TOL * WANT_F
with open(LOG, "a", newline="") as f:
csv.writer(f).writerow([time.strftime("%Y-%m-%d %H:%M:%S"), idn,
"%.4e" % width, "%.2f" % freq, "PASS" if ok else "FAIL"])
print("%s: ширина %.1f мкс (нужно %.0f ± %.1f), частота %.1f Гц" % (
"PASS" if ok else "FAIL", width * 1e6, WANT_W * 1e6, TOL * WANT_W * 1e6, freq))
sys.exit(0 if ok else 1)
Два прогона против подставного прибора — исправная плата и плата со скважностью 30 %:
PASS: ширина 250.0 мкс (нужно 250 ± 2.5), частота 1000.0 Гц
FAIL: ширина 300.0 мкс (нужно 250 ± 2.5), частота 1000.0 Гц
В журнале — 2 строки: PASS и FAIL, с *IDN? прибора в каждой.
Ошибка. Сравнивать без допуска: 250,0 и 249,9 мкс — одно и то же
для ШИМ от кварца, но == скажет «брак». И не проверять, что запуск
вообще был: без тайм-аута на STOP скрипт ждёт вечно, а без запуска
:MEASure вернёт измерение старой картинки.
Случай. После смены делителя таймера в общей библиотеке скрипт на трёх платах разом пишет FAIL с частотой ровно вдвое меньше — ошибка найдена до того, как прошивка ушла на сборку.
36ж.17 Веб-управление, LXI и второй экран
Web Control (руководство с. 273; даташит с. 23). IP-адрес прибора в браузере — и на компьютере, планшете или телефоне видно живой экран прибора, которым можно управлять; кнопка SCPI Panel Control там же шлёт команды из 36ж.15 прямо из браузера (программирование с. 28). Страница LXI с моделью, серийным номером, MAC и IP открывается по тому же адресу (руководство с. 275); версия модуля веб-управления — в Utility → About (руководство с. 268).
Зачем. Прибор стоит у стенда под напряжением (36з), а смотреть удобнее с безопасного места. Показать картинку коллеге без снимков. Обновление прошивки по сети — там же, через LAN (руководство с. 260).
Безопасность. У веб-управления есть заводские имя и пароль, они напечатаны в руководстве (с. 273) — то есть известны всем. Смена пароля в руководстве не описана. Поэтому прибор держат в отдельной сети стенда, а не в общей сети офиса и тем более не открывают в интернет: через веб-страницу можно не только смотреть, но и менять настройки во время чужого испытания.
HDMI (даташит с. 23; руководство с. 41) — второй экран: проектор на разборе или большой монитор над стендом. Экран прибора при этом тоже работает.
Окна (руководство с. 234–235). Не управление, но рядом: Windows → Result Table → Decode выводит таблицу декодера отдельным окном — удобно на большом экране по HDMI.
Ошибка. Подключать кабели связи к включённому прибору: руководство просит выключать его перед подключением (с. 273).
36ж.18 Грабли
Декодер врёт на длинной посылке. Развёртку растянули, память оставили 10 тыс.: на SPI 8 МГц — 0,25 выборки на такт, на I2C на 1 тыс. точек SCL выглядит постоянной единицей (36ж.2).
Ошибка чётности есть, а данных «правильных» нет. Испорченный бит
без Parity даёт уверенно неверный байт: 1 → 5 (36ж.3).
I2C-адрес «не тот». 0x50 и 0xA0 — один адрес с битом R/W и без него; переключатель R/W (36ж.4).
SPI-байты сдвинуты на разряд. Slope не тот: 0x33 читается как 0x66 (36ж.5).
LIN — каждая сумма красная. Version 1.X против 2.X: 0xF5 и 0xA5 на одних данных (36ж.7).
Маска бракует всё. Y Mask 0,1 дел при шуме 20 мВ на 0,5 В/дел — брак в каждой записи (36ж.10).
Длинная запись вместо сегментов. 1000 посылок по 0,5175 мс за 100 с — 1,25 выборки на такт; сегментами — 25 (36ж.11).
Опорная осциллограмма пропала. SaveToRef не переживает выключения; Export в *.ref (36ж.13).
В файле тысяча точек. Data Source = Screen (36ж.14).
Скрипт висит. Нет \n в конце команды или ответа (36ж.15).
Память читается мусором. RAW во время RUN — читать только после STOP; вольты считать по преамбуле, а не «код × масштаб» (36ж.15).
Испытатель ждёт вечно. Нет тайм-аута на :TRIGger:STATus? — плата
не запустилась, а скрипт об этом не узнал (36ж.16).
Веб-управление с заводским паролем в общей сети (36ж.17).
36ж.19 Итоги
- Декодер читает память, а не провод: выборок на бит не меньше четырёх-пяти, лучше десять. Глубина памяти по умолчанию (10 тыс.) годится для байта, но не для пакета.
- Порог — в середину размаха; полярность, порядок бит, R/W, фронт SPI, версия LIN — частые причины «неверных» данных на исправной шине.
- Цифровые каналы DHO900: 16 линий, порог на группу из восьми, ±(100 мВ + 3 %); не с Average, не в Roll. Уровни — только аналогом; час обмена — приставкой-анализатором.
- Маска шире шума: 7,5 σ не бракуют исправное и ловят настоящий дефект.
- Сегменты (Recording, UltraAcquire) хранят только нужное и дают ΔT каждого запуска.
- Поиск находит в 50 млн точек то, что на экране занимает 0,0512 % столбца, — если оно есть в памяти.
- Для обработки — BIN из Memory; размер заголовка брать из файла.
- SCPI DHO900:
:SINGleи ожидание STOP,:WAVeform:MODE RAWв STOP, преамбула из 10 полей и формула кода в вольты, блок#N; ошибки — через:SYSTem:ERRor?. Испытатель на стенде — прошивка, Single,:MEASure, допуск и журнал; веб-управление — в отдельной сети.
Дальше: раздел 36з «Гальваническая развязка при измерениях: земля осциллографа, изолированные каналы, дифференциальный щуп» — почему нельзя отрезать землю осциллографа и как смотреть сигналы относительно разных точек, в том числе в сетевых цепях.