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

120 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Контроллер выходного ключа
Прошивка STM32, которая переключает выходной СВЧ-ключ внутри частотной точки —
вторая половина режима, описанного в [`../per_point_switching.md`](../per_point_switching.md).
Плата и тулчейн те же, что у пульта тележки: WeAct STM32G431CB, PlatformIO.
## Подключение
```text
мастер 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`](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` блокирует зеркалирование, а пропущенные фронты сбили бы
чётность. Ключевые строки:
```text
parity corrections : 0 (ok) <- в норме всегда 0
stages per point : 4 (ожидается 4), отклонений: 0
inter-stage gap, us : 62
boundary above, us : 93
```
`stages per point` — самопроверка распознавания границ: если оно верное, между
двумя границами всегда ровно 4 стадии. Любое другое число означает, что порог
подобран неправильно.
## Сборка и прошивка
```bash
cd device_firmware/switch_controller
pio run # сборка
pio run -t upload # прошить через ST-Link
pio device monitor # отчёт по USB CDC
```