RF Power Meter V5.0 — открытая прошивка и приложение управления
Проект для китайского USB RF-измерителя мощности RF_POWER_METER V5.0 (100 кГц - 10 ГГц, -60…0 дБм, детектор AD8317 на STM32F401CC): установка открытой прошивки со SCPI-интерфейсом взамен заводской и написанное с нуля приложение на Python3 + PyQt5 для управления прибором.
Состав репозитория
usb_powermeter_firmware/— форк прошивки iamaiy/usb_powermeter_firmware (Arduino/STM32duino) с доработками: рабочая калибровка и три исправленных бага (подробности ниже). Собственный README форка описывает происхождение и железо.app/— приложение управления измерителем (PyQt5, интерфейс на русском).NOTES.md— свободные заметки о ходе работы: что делали, что нашли, какие решения почему приняли.
Что изменено в прошивке относительно апстрима
-
Калибровка заработала. В апстриме
CALibration:FREQuencyбыл пустой заглушкой. Добавлена одноточечная калибровка смещения (offset) для дБм/мВт, действует только на сессию (не пишется во флеш, сбрасывается при перезагрузке):CALibration:FREQuency <Гц>/?— информационная метка частоты калибровочной точки (на саму формулу не влияет).CALibration:POWer <дБм>— подать на вход известную опорную мощность и по текущему свежему отсчёту вычислить смещение; отвечает вычисленным смещением в дБ.CALibration:OFFSet?— текущее применяемое смещение (дБ).CALibration:CLEar— сбросить калибровку.CALibration:STATe?— активна ли калибровка (0/1).- Смещение прибавляется только к дБм/мВт; RAW и Volt остаются «сырыми».
-
Баг напряжения/температуры MCU (
SYStem:VOLTage?, было отмечено как “buggy” в апстриме). Вsetup()строкаanalogReadResolution(12);была закомментирована. STM32duino по умолчанию отдаётanalogRead()в 10-битной шкале (0-1023), а формула пересчёта опорного напряжения жёстко делит на 4096 (12 бит) — отсюда 4-кратное завышение (реальные 3.3В читались как ~13В). На основной канал детектора (PA1) это не влияло — он настраивается отдельно, явно на 12 бит. Раскомментировано. -
TRIGger:SINGLEне останавливал измерение. Команда только выставляла флагcontinuous=0, но аппаратный таймер АЦП продолжал свободно тикать — то есть «одиночный» режим ничем не отличался от непрерывного. ТеперьTRIGger:SINGLEреально останавливает таймер, аTRIGger:IMMediateзапускает его ровно на один (пост-усреднённый) отсчёт и снова останавливает. -
DATA:UNITтеперь принимает и имя единицы, и число. В апстриме документация обещала строки (RAW/dBm/mW/Volt), а код по факту понимал только числовой индексunits_t(0-3) — строка молча парсилась как0(.toInt()на нечисловой строке). Теперь работает оба варианта.
Сборка и прошивка
Инструменты: arduino-cli (Arduino IDE 2.2.1) и библиотека Vrekrer_scpi_parser.
ACLI=arduino-ide_2.2.1_Linux_64bit/resources/app/lib/backend/resources/arduino-cli
CFG=.arduinoIDE/arduino-cli.yaml
FQBN="STMicroelectronics:stm32:GenF4:pnum=BLACKPILL_F401CC,xserial=generic,usb=CDCgen,xusb=FS,upload_method=swdMethod"
cd usb_powermeter_firmware
$ACLI --config-file $CFG compile --fqbn "$FQBN" --export-binaries .
Прошивка вручную через dfu-util (без проприетарного STM32CubeProgrammer, который используется в инструкции апстрима):
- Замкнуть BOOT0 на MCU (на этой плате штатной кнопки/перемычки нет — нужен проводок с 5В на BOOT0-пин, см. README форка прошивки, раздел Bootloader) и подать питание/сбросить — прибор должен определиться как
0483:df11вlsusb. dfu-util -l— убедиться, чтоalt=0это@Internal Flash /0x08000000/....-
Прошить:
dfu-util -a 0 -s 0x08000000:leave -D build/STMicroelectronics.stm32.GenF4/usb_powermeter_firmware.ino.bin - Отсоединить перемычку BOOT0 и переподключить USB — иначе плата снова уйдёт в DFU вместо загрузки прошивки.
Приложение (app/)
Зависимости: PyQt5, pyserial, pyqtgraph (см. app/requirements.txt).
cd app
python3 main.py
Возможности:
- Подключение по последовательному порту (авто-поиск портов, USB CDC ACM).
- Идентификация и служебные данные (
*IDN?, температура, опорное напряжение, автообновление). - Настройка АЦП: интервал/частота дискретизации, число сэмплов пост-усреднения (NAVG).
- Управление запуском измерения: непрерывно / одиночно / принудительный пуск / стоп.
- Единицы измерения RAW/дБм/мВт/В, разовое считывание и потоковая передача (
DATA:PUSH). - Быстрое переключение RAW/дБм и кнопка «Непрерывный сбор и график» (включает continuous + push в один клик).
- Панель калибровки (см. выше).
- Живой график (pyqtgraph) с фильтрацией сигнала на стороне приложения — три алгоритма на выбор:
- Скользящее среднее — простое сглаживание шума, задержка растёт с размером окна.
- Экспоненциальное (EMA) — быстрее реагирует при том же уровне сглаживания, параметр — коэффициент α.
- Медианный фильтр — давит одиночные выбросы/помехи, не размывая реальные перепады сигнала.
- Консоль SCPI для ручной отправки произвольных команд и просмотра сырого потока.
Справочник команд SCPI
*IDN? - идентификация прибора
SYStem:TEMPerature? - температура MCU
SYStem:VOLTage? - опорное напряжение MCU (после фикса; было "buggy")
ACQuire:INTerval [мкс] / ? - интервал дискретизации АЦП
ACQuire:FREQuency [Гц] / ? - частота дискретизации АЦП (жёстко ограничена 50000 Гц прошивкой;
это НЕ частота измеряемого ВЧ-сигнала, а скорость опроса детектора)
ACQuire:NAVG [N] / ? - число сэмплов пост-усреднения
CALibration:FREQuency [Гц] / ? - метка частоты калибровочной точки (информационная)
CALibration:POWer <дБм> - калибровка по опорной мощности; отвечает вычисленным смещением
CALibration:OFFSet? - текущее смещение (дБ)
CALibration:CLEar - сбросить калибровку
CALibration:STATe? - активна ли калибровка (0/1)
TRIGger:CONTinuous [0|1] / ? - непрерывный запуск
TRIGger:SINGLE / ? - одиночный режим (арм)
TRIGger:IMMediate - пуск (в одиночном режиме — ровно один отсчёт)
TRIGger:STOP - стоп
DATA:UNIT [RAW|dBm|mW|Volt|0-3] / ? - единицы (принимает имя и число)
DATA:FETCH? - разовое считывание ("P: <знач> T: <мкс>" либо "NREADY: n/N")
DATA:PUSH <0|1> - потоковая передача отсчётов по мере готовности
Известные особенности
- Калибровка не сохраняется между перезагрузками устройства (сознательное решение — проще и без риска износа флеш-памяти).
- После
TRIGger:IMMediateпервый отсчёт иногда приходит не мгновенно (до ~0.3 с) — особенность родного ADC-драйвера производителя (my_analog.cpp), не трогали. - Абсолютная точность дБм/мВт зависит от констант
slope/interceptвAD8317_format(), подобранных автором прошивки без калибровки под конкретный экземпляр платы и частоту — используйтеCALibration:POWerдля компенсации смещения на нужной частоте.