Files
radar_system/device_firmware/switch_controller/README.md
T
AyzenandClaude Opus 5 2412aedc58 switch controller: shorten the gap-minimum window to adapt to dwell changes
Окно минимума было 4096 отсчётов, и это давало опасную асимметрию: при
УМЕНЬШЕНИИ межстадийной паузы минимум падает мгновенно, а при УВЕЛИЧЕНИИ
(подняли dwell_us или задержку PLL прибора) устаревшее маленькое значение живёт
тысячи стадий. Порог оказывался ниже реальной межстадийной паузы, детектор
границы срабатывал на каждой стадии и залипал ключ в положении A на пару
развёрток.

Окно сокращено до 16 отсчётов. Длинное окно не давало точности: пауза задаётся
счётчиком в FPGA и детерминирована, а межстадийная — самая частая (три из
четырёх при четырёх стадиях), так что минимум по десятку отсчётов уже равен ей.
Зато теперь смена настроек подхватывается за считанные точки, и случайная
короткая пауза от помехи вымывается так же быстро.

IFBW на порог не влияет и влиять не может: он меняет длительность оцифровки
(фронт-спад), а меряется пауза (спад-фронт). Плюс ключ переключается по спаду,
когда оцифровка закончена, а детектор границы во время оцифровки неактивен —
boundaryPending_ сбрасывается на фронте. Разобрано в README.

Прошито и проверено: после сброса ядро в loop, Verified OK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:37:31 +03:00

143 lines
11 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). Пока
минимум не набран, восстановление молчит — чередование корректно и без него.
### Надо ли перекалибровывать паузу под каждую настройку
Нет, порог подстраивается сам, и вот что на него влияет, а что нет.
**IFBW не влияет.** Он меняет длительность оцифровки, то есть интервал от фронта
до спада, а контроллер меряет паузу от спада до следующего фронта. Это разные
интервалы. Плюс переключение делается по спаду, когда оцифровка уже закончилась,
а детектор границы во время оцифровки вообще неактивен — так что сдвинуть ключ
посреди измерения он не может ни при каком IFBW.
**Число точек, полоса, мощность не влияют** — паузы от них не зависят.
**Влияют `dwell_us` и задержка PLL прибора**: они и есть время успокоения, то
есть сама пауза. Но подстройка на это и рассчитана — окно минимума короткое (16
отсчётов), поэтому новое значение подхватывается за считанные точки. Ручной
калибровки не требуется.
**Смена диапазона внутри развёртки безопасна.** На точках нижнего диапазона
прибор останавливает развёртку и работает с Si5351 по I2C — такие паузы намного
длиннее обычных. Логика от этого не ломается: остановка всегда происходит на
границе точки, перед стадией 0, а значит «длинная пауза → следующая стадия
нулевая → положение A» остаётся верным.
Множитель намеренно щедрый. Ложное срабатывание **посреди** точки испортит
данные, а несрабатывание всего лишь отключит страховку. Если по отчёту видно,
что границы не распознаются вообще (`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
```