Files
radar_system/device_firmware/switch_controller/README.md
T
AyzenandClaude Opus 5 c1f8654677 review fixes: shared mock capture window, 2-position controller caveat
Мок-бэкенд ставил каждому трейсу свой capture_end_ns, потому что метка бралась
в момент создания трейса. На реальном железе все комбинации коллекции выходят из
одного синхронизированного цикла и делят одно окно — мок теперь ведёт себя так
же, иначе он вводит в заблуждение при чтении записанных меток.

В README контроллера добавлена оговорка: прошивка рассчитана ровно на два
положения ключа. Хост допускает и 4, но тогда положение перестаёт быть
чередованием одного бита и нужен контроллер на две управляющие линии.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 22:15:50 +03:00

8.9 KiB

Контроллер выходного ключа

Прошивка STM32, которая переключает выходной СВЧ-ключ внутри частотной точки — вторая половина режима, описанного в ../per_point_switching.md. Плата и тулчейн те же, что у пульта тележки: WeAct STM32G431CB, PlatformIO.

Подключение

мастер TRIGGER_OUT ──► PB0   [STM32]   PB1 ──► слейв 1 TRIGGER_IN
                                       PB2 ──► управление обоими СВЧ-ключами
                                       PB5 ──► отладочный выход

Контроллер встаёт в разрыв линии между мастером и первым слейвом, остальная часть кольца (слейв 1 → слейв 2 → мастер) не трогается. Это безопасно: в режиме Mode::Trigger прошивка LibreVNA уводит собственный STM32 прибора в Hi-Z на обеих триггерных линиях, так что и вход слейва, и выход мастера свободны.

Одна нога PB2 управляет обоими ключами (на порту 1 и на порту 2) — они ходят синхронно, и этого достаточно: нужное положение зависит только от номера стадии, а не от того, какой порт сейчас излучает. Если у ключа больше двух состояний, лишние входы замыкаются джампером.

Прошивка рассчитана ровно на два положения ключа (stimulus_switch_positions: 2 на хосте). Хост технически допускает и 4 положения, но тогда положение перестаёт быть простым чередованием одного бита и потребуется контроллер на две управляющие линии — эта прошивка для такого режима не годится.

Если положению A соответствует лог. 1, а не 0 — поменяйте kSwitchActiveLow в src/main.cpp.

Как это работает

Прибор измеряет каждую частоту в 4 стадии: порт 1 в положениях A и B ключа, затем порт 2 в тех же положениях. Положение ключа — это младший бит номера стадии, а период 2 нацело делит 4 стадии, поэтому нужная последовательность — непрерывное чередование A B A B A B …, не прерывающееся на границе точки. Контроллеру достаточно инвертировать ногу на каждый цикл триггера; абсолютный счётчик стадий не нужен.

Переключение делается по спаду триггера. Спад означает «оцифровка стадии закончилась»; сразу за ним прибор уходит в состояние успокоения следующей стадии, и это окно (dwell_us плюс собственная задержка PLL прибора, ~62 мкс при dwell_us = 2) целиком покрывает установление ключа. Переключение по фронту заставило бы удерживать кольцо на время установления и удлинило бы каждую стадию.

Восстановление чётности

Чередование самодостаточно, но один пропущенный или лишний фронт инвертирует чётность навсегда и молча меняет антенны местами. Поэтому контроллер дополнительно распознаёт границу точки по длине паузы:

Пауза Длительность Из чего состоит
между стадиями базовая только время успокоения
между точками базовая + перезагрузка регистров PLL мастера
между развёртками базовая + + ещё и работа хоста между свипами

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

Порог подстраивается сам: межстадийная пауза — это минимальная регулярно повторяющаяся пауза, поэтому за порог берётся минимум по скользящему окну, умноженный на kBoundaryFactorNum / kBoundaryFactorDen (по умолчанию 1.5). Пока минимум не набран, восстановление молчит — чередование корректно и без него.

Множитель намеренно щедрый. Ложное срабатывание посреди точки испортит данные, а несрабатывание всего лишь отключит страховку. Если по отчёту видно, что границы не распознаются вообще (point boundaries близко к нулю при ненулевом trigger cycles), множитель можно снижать — но не ниже реального отношения межточечной паузы к межстадийной, которое видно там же в отчёте.

Задержка

Зеркалирование занимает ~50 нс и на измерения не влияет: мастер стартует оцифровку по своему TRIGGER_IN, который приходит с конца кольца, то есть тоже после контроллера. Задержка общая для всех трёх приборов, взаимные сдвиги мастер/слейвы задаются только межFPGA-передачей, и в отношении «приёмник/опорник» она сокращается. Единственный эффект — развёртка длиннее на ~2 × задержку × число стадий.

Диагностика

PB5 — импульс 2 мкс каждый раз, когда чётность пришлось чинить. В исправной системе эта нога молчит: предсказание чередованием уже даёт нужное положение на границе точки. Постоянные импульсы означают, что фронты теряются или порог распознавания границы занижен.

PB2 — само положение ключа, смотрится осциллографом вместе с триггером: на одной частоте должно быть ровно 4 стадии и два полных периода A B A B.

USB CDC (115200) — отчёт печатается один раз, когда на линии триггера тишина дольше 200 мс, то есть когда развёртка остановлена. Во время работы печати нет: Serial блокирует зеркалирование, а пропущенные фронты сбили бы чётность. Ключевые строки:

parity corrections  : 0        (ok)          <- в норме всегда 0
stages per point    : 4 (ожидается 4), отклонений: 0
inter-stage gap, us : 62
boundary above, us  : 93

stages per point — самопроверка распознавания границ: если оно верное, между двумя границами всегда ровно 4 стадии. Любое другое число означает, что порог подобран неправильно.

Сборка и прошивка

cd device_firmware/switch_controller
pio run                 # сборка
pio run -t upload       # прошить через ST-Link
pio device monitor      # отчёт по USB CDC