switch controller firmware + per-point switching spec

Прошивка STM32 (WeAct G431CB, тот же тулчейн, что у пульта тележки), которая
стоит в разрыве триггерной петли между мастером и первым слейвом и переключает
выходной ключ внутри частотной точки.

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

Один потерянный фронт инвертировал бы чётность навсегда и молча поменял антенны
местами, поэтому контроллер распознаёт границу точки по длине паузы (между
точками к успокоению добавляется перезагрузка регистров PLL) и принудительно
ставит положение A — чётность чинится на каждой точке, а не раз на развёртку.
Порог берётся как минимум паузы по скользящему окну с запасом, то есть
подстраивается сам; отдельный провод-маркер начала развёртки не нужен.

Задержку зеркалирования проверил по Sweep.vhd: мастер стартует оцифровку по
своему TRIGGER_IN с конца кольца, то есть тоже после контроллера. Задержка
общая для всех приборов и сокращается в отношении «приёмник/опорник» — фазовой
ошибки не вносит, только чуть удлиняет развёртку.

Собирается: RAM 13.9%, Flash 20.0%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ayzen
2026-09-09 21:45:50 +03:00
co-authored by Claude Opus 5
parent 6693ece0aa
commit 0db3dbe295
5 changed files with 561 additions and 24 deletions
+28 -24
View File
@@ -3,7 +3,7 @@
Режим, в котором одна развёртка даёт матрицу 4×4 вместо 2×4: каждый порт мастера
измеряется на **каждой** частоте дважды — с двумя положениями выходного ключа.
```
```text
4 выхода = 2 порта мастера × 2 положения ключа
4 входа = 2 слейва × 2 порта (входной ключ не участвует)
```
@@ -29,7 +29,7 @@
стадии**, а период 2 нацело делит 4 стадии. Значит через границу точки чередование
продолжается без разрыва, и контроллеру достаточно инвертировать ногу на каждый
цикл триггера — абсолютный счётчик стадий не нужен, нужна только правильная
чётность, и та только один раз на старте развёртки.
чётность, а её контроллер восстанавливает на каждой точке по длине паузы.
## Контракт по проводу
@@ -137,32 +137,36 @@ FPGA::SetupSweep(s.stages, s.port1Stage, s.port2Stage, s.port3Stage,
## Контроллер ключа (отдельный STM32)
Врезается в триггерную петлю между `TRIGGER_OUT` мастера и `TRIGGER_IN` слейва.
Электрически это чисто: в режиме `Mode::Trigger` прошивка LibreVNA уводит STM32
прибора в Hi-Z на обеих линиях (`GPIO_MODE_INPUT`, `NOPULL`, EXTI снят), так что
обе линии свободны.
Реализован: [`switch_controller/`](switch_controller/) — WeAct STM32G431CB,
PlatformIO, тот же тулчейн, что у пульта тележки. Подробности подключения и
диагностики в [его README](switch_controller/README.md), здесь только суть.
Три требования:
Врезается в триггерную петлю между `TRIGGER_OUT` мастера и `TRIGGER_IN` первого
слейва. Электрически чисто: в режиме `Mode::Trigger` прошивка LibreVNA уводит
STM32 прибора в Hi-Z на обеих линиях (`GPIO_MODE_INPUT`, `NOPULL`, EXTI снят).
1. **Прозрачный повторитель обоих фронтов.** Задержка суб-микросекундная
(bare-metal EXTI), иначе она добавляется к каждой из 4 стадий каждой точки.
2. **Ключ переключается по СПАДУ**, а не по фронту. Спад означает «оцифровка
стадии закончилась»; дальше идёт `SamplingDone` → `Settling` следующей стадии,
то есть окно успокоения. При `dwell_us = 2` и штатной задержке PLL это окно
~62 мкс — запас огромный. Переключение по фронту заставило бы удерживать
кольцо на время установления ключа.
3. **Одна нога на два ключа.** Ключи на порту 1 и на порту 2 ходят синхронно;
нужное положение — просто инверсия на каждом цикле триггера.
Логика сводится к трём правилам:
Чётность привязывается к началу развёртки. Между развёртками FPGA держится в
сбросе (`StartSweep` = снять и поднять AUX3), а хост успевает получить пакет
статуса, поэтому пауза на линии триггера на порядок длиннее межстадийной
(~100 мкс против ≥1 мс — померить осциллографом и взять порог посередине). Если
есть физический доступ к AUX3 мастера (нога FPGA P74) — лучше завести её на
контроллер как точный маркер вместо порога по паузе.
1. **Зеркалирование обоих фронтов.** Задержка не влияет на измерения: мастер
стартует оцифровку по своему `TRIGGER_IN`, который приходит с конца кольца,
то есть тоже после контроллера. Она общая для всех трёх приборов и в
отношении «приёмник/опорник» сокращается — только чуть удлиняет развёртку.
2. **Инверсия ноги по СПАДУ триггера.** Положение ключа — младший бит номера
стадии, а период 2 нацело делит 4 стадии, поэтому чередование не рвётся и на
границе точки. Спад означает «оцифровка закончилась», дальше идёт окно
успокоения следующей стадии (~62 мкс при `dwell_us = 2`), которого ключу
хватает с огромным запасом.
3. **Одна нога на оба ключа.** Нужное положение зависит только от номера стадии,
а не от того, какой порт излучает, поэтому ключи на порту 1 и порту 2 ходят
синхронно.
Сбой чётности меняет местами антенны 0↔2 и 1↔3 **молча**, поэтому стоит вывести
текущее положение ключа на отладочную ногу для сверки осциллографом.
Страховка от сбоя чётности: один потерянный фронт инвертирует чередование
навсегда и молча меняет антенны местами. Контроллер распознаёт границу точки по
длине паузы (между точками к времени успокоения добавляется перезагрузка
регистров PLL, между развёртками — ещё и работа хоста) и принудительно ставит
положение A. Так чётность чинится на **каждой** точке. Порог берётся как минимум
паузы по скользящему окну с запасом, то есть подстраивается сам под любые
настройки развёртки; отдельный провод для маркера начала развёртки не нужен.
## Хост