147 lines
11 KiB
Markdown
147 lines
11 KiB
Markdown
# Генератор произвольного сигнала 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 эмулятора.
|
||
|
||
Аппаратная проверка амплитуды, периода и формы осциллографом не выполнена.
|