Files
templates/c/set-protocol/docs/SIGNAL_GENERATOR.md

147 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.
# Генератор произвольного сигнала WG v1/v2
## Расширение карты v2: F407 до 1 000 000 отсчётов/с
Версия карты в статусе — 2, `set_wave_state.rate` — uint32_t.
Для записи частоты сначала остановить выход, записать старшие 16 бит в
`0x1308`, затем младшие 16 бит в `0x1303`. Вторая запись проверяет диапазон
1…1 000 000 и применяет частоту; обе записи снимают ready. Чтение `0x1308`
возвращает старшие биты применённой частоты, а слово 3 статуса — младшие.
Host ABI: операция 9 — запись старшего слова, 8 — его чтение, 2 — запись
младшего слова (включая ноль). Остальные команды не меняются.
Это версия карты регистров, а не транспорт: SETGUI использует SET v2
через EmulatorSerialPort, с резервным RTU для старой прошивки. Новый клиент
понимает статус карт v1/v2; для v1 предел остаётся 50 000. Старый клиент
отклоняет незнакомую версию 2 до записи. Код F407 и GUI поддерживают 1 МГц;
порт G474 пока сохраняет прежний аппаратный лимит 50 кГц.
Ниже описание исходной карты v1; пределы и регистры частоты заменены этим
расширением для v2. На высоких частотах аналоговое установление зависит
от величины скачка и нагрузки; аппаратная проверка осциллографом обязательна
для оценки точности конкретного сигнала.
Общее C99-ядро строит кривые по точкам, преобразует напряжения в коды ЦАП и
принимает таблицы через RTU поверх USB CDC/COM. Оно не зависит от Qt, HAL или ОС.
Память предоставляет вызывающий; скрытого heap и глобального состояния нет.
```text
SETGUI: точки -> Python/ctypes -> set_signal.c -> график / CSV / C
-> set_wavegen.c -> USB CDC
MCU: USB stream -> set_wave_rtu -> set_wave_port -> TIM6/DMA/DAC
```
| Файл | Зависимости и назначение |
|---|---|
| `include/set_signal.h`, `src/set_signal.c` | C99/math: МНК 1…5, linear, PCHIP, natural cubic spline, 12-bit DAC |
| `include/set_wavegen.h`, `src/set_wavegen.c` | C99 + `set_crc.c`: транзакционная загрузка и wire codec |
| `ports/stm32f407-wavegen` | CMSIS F407: PA4/DAC1, TIM6, DMA1 Stream5 Channel7 |
| `ports/stm32g474-wavegen` | STM32G4 HAL: готовые DAC/TIM/DMA handles из CubeMX |
| `python/set_devices/signal_reconstruction.py`, `waveform.py` | Модели, JSON/экспорт, ctypes; алгоритмов на Python нет |
| `python/set_devices/wavegen_protocol.py`, `qt_ports/wavegen.py` | ctypes-кодек и Qt COM worker с проверкой чтением обратно |
## Интерполяция и таблица
`set_signal_reconstruct` принимает раздельные X/Y и workspace `14*N+128`
элементов double. Сортировка, усреднение одинаковых времён, нормализация,
МНК через QR и интерполяция выполняются в C. Максимум 100000 входных/10000
выходных точек. Переданный workspace может повторно использоваться.
Большие расчёты предназначены для хоста; на MCU можно строить малые таблицы
или принимать готовую таблицу без затрат на интерполяцию.
`endpoint=1` включает последний X (анализ логов); `endpoint=0` строит один
период `[0,T)` без дублированного отсчёта стыка. Частота Fs и период T задают
целое `N=Fs*T`; для WG максимум N=4096, Fs=1…50000 Гц, один канал, циклический
выход. При разных значениях первой и последней точки на стыке будет скачок.
Напряжения 0…Vref округляются к ближайшему коду 0…4095; выход за диапазон
отклоняется целиком. Это относится и к выбросам сплайна. Vref должен
соответствовать фактическому VDDA/VREF+ платы; это не программируемое питание.
Пример хоста (DLL передаётся через `SETPROTOCOL_LIBRARY`):
```python
from set_devices.waveform import generate, c_header
points = [(0, 0), (10, 3.3), (20, 0)]
wave = generate(points, sample_rate=10000, vref=3.3, method="linear")
assert len(wave.codes) == 200
with open("waveform.h", "w", encoding="utf-8") as output:
output.write(c_header(wave))
```
## Контракт порта
`start(context, samples, count, sample_rate)` возвращает 0 при успехе;
`stop(context)` синхронно прекращает DMA и устанавливает нулевой выход.
Память таблицы принадлежит `set_wave_state`, её нельзя менять до stop.
Вызывайте RTU и обслуживание состояния из одного основного потока, не из IRQ.
IRQ USB помещает байты в очередь; USB-пакеты не являются границами RTU.
```c
static uint16_t samples[SET_WAVE_MAX]; /* DMA-accessible SRAM, not CCM */
static set_wave_state wave;
static set_wave_f407 hardware = {72000000};
void application_init(void) {
set_wave_port port = set_wave_f407_port(&hardware);
set_wave_init(&wave, samples, SET_WAVE_MAX, &port);
}
size_t on_rtu(const uint8_t *frame, size_t n, uint8_t reply[256]) {
return set_wave_rtu(&wave, 16, frame, n, reply, 256);
}
```
Для F407 DAC_CH1 — PA4, TIM6 TRGO_UPDATE, DMA1 Stream5/Channel7. Порт использует
только нижние 16 бит DAC_CR и не меняет канал 2. Таблица лежит в SRAM1/2
`0x20000000…0x2001FFFF`, не в CCM. Перед первым отсчётом есть один такт
предзагрузки последнего значения предыдущего периода. Для гарантированной
целой частоты порт принимает только делители входной частоты TIM6; при
72 МГц подходят, например, 100, 1000, 5000, 10000, 20000 Гц.
Частоту таймера передавайте с учётом удвоения при делителе APB1 > 1.
Ошибку DMA/underrun проверяйте через `set_wave_f407_fault()` и останавливайте
состояние. На F407-проекте этот вызов включён в главный цикл.
Для G474 настройте CubeMX: DAC1 channel1 (PA4), output buffer enabled,
TIM6 TRGO_UPDATE, DMA memory-to-peripheral, circular, halfword/halfword,
memory increment, запрос `DMA_REQUEST_DAC1_CHANNEL1`; подключите HAL IRQ
для выбранного канала DMA и DAC. Передайте handles и timer_hz в
`set_wave_g474`. Обработчики ошибок DMA/DAC в приложении должны вызвать stop
и сбросить `wave.running/ready`. Порт проверен компиляцией с CubeG4 1.6.1;
конкретная плата G474 здесь не прошита.
## USB / Modbus RTU
Адрес по умолчанию 16, FC03/FC06, CRC16 Modbus. Максимальный запрос — 8 байт,
ответ — 21 байт; существующий потоковый USB RTU parser F407 подходит без
изменения формата. Все поля строит/проверяет C-кодек, GUI не пакует байты.
| Регистр | Значение |
|---|---|
| `0x1300`, FC03, 8 слов | `0x5747`, версия 1, running, Fs, count, received, ready, capacity |
| `0x1302`, FC06 | 0: stop и нулевой выход; 1: start только после commit |
| `0x1303`, FC06 | Частота 1…50000; сбрасывает ready |
| `0x1304`, FC06 | Начать загрузку, число отсчётов 2…4096; сброс received/ready |
| `0xA000+i`, FC06 | Последовательная загрузка отсчёта 0…4095; повтор того же значения допустим |
| `0xA000+i`, FC03, 1 слово | Чтение загруженного отсчёта для проверки |
| `0x1306`, FC06 | `0xA55A`: commit, только если все отсчёты загружены |
Во время running разрешены чтение и stop; запись/перезагрузка дают исключение
6. Неполная таблица не запускается. Повреждённый CRC и чужой адрес не меняют
состояние. При reset ready/running=0, таблица не сохраняется во Flash.
Профиль и регистры генератора не являются регистратором УМП или протоколом 2812.
Хост выполняет probe → stop → Fs → count → samples → readback каждого
отсчёта → commit → status. Пуск — отдельная пользовательская команда.
После потери USB уже запущенный DAC продолжает автономное воспроизведение;
при таймауте состояние неизвестно до повторного status/stop. Одновременно
используйте одного клиента, не открывайте этот же COM во вкладке подключения.
## Потребители и проверки
- SETGUI: «Логи и графики → Генератор», JSON, CSV и C header.
- `home/407vet6_emul_TMS_Periph`: USB CDC и UART, штатный адрес настройки STM,
отдельный PA4 DAC выход; HAL/CMSIS и настройки платы остаются в проекте.
- G474: переносимый HAL-порт; board init остаётся в целевой прошивке.
- `tests/test_signal_wave.c`: C-векторы интерполяции, DAC, CRC, неполной
загрузки, readback и блокировки running. Общие байты проверены Python
ctypes и фактическим USB stream parser эмулятора.
Аппаратная проверка амплитуды, периода и формы осциллографом не выполнена.