Баузбенд FM100B: - найден прозрачный мост ПК<->модуль (включение с зажатой МЕНЮ), проверен живьём - расшифрована контрольная сумма кадров (one's complement, BE), сверена с ответом рации - инструмент tools/fm100b.py (ping/send/raw/scan) - cmd 0x25 = запрос версии, модуль отвечает V1.2.0.32 (совпало с офиц. образом) - разобран диспетчер входящих кадров: 193 записи, реальный код у 10 команд - cmd 0x59 = индикация приёма, она же управляет гейтом звука PA14 - исправлено: boot-handshake это cmd 0x84, а не 0x64 (research/re/dmr.md) Спектроанализатор REFV DualTachyon (docs/refw-spectrum.md): - вызывается как функция горячей клавиши №22 Analog Spectrum - вход 0x08009CAA -> обычный 0x080139E4 / по зоне 0x08015318 - спектр это экран №11; тик 0x08013BA4 (автомат на 4 состояния), клавиши 0x08013BFC Декомпозиция (docs/firmware-parts.md): - два процессора + внешний SPI = три канала внедрения - карта ресурсов SPI: шрифты, пиньинь, голос, таблица Unicode - найден штатный загрузчик ресурсов FontVoicePicture (шрифты/голос/картинки) - дерево меню целиком: MIC/SPK Gain, RX/TX Limit, SMS Format уже в стоке - аудио двухступенчатое: PA2 питание УНЧ, PA14 гейт от DMR-модуля Прочее: - везде исправлен режим прошивки: тангента PTT вместо клавиши "*" - устаревший Ru-4D_Flasher.exe удалён из репозитория - добавлены инструменты реверса: xref, refs, gpiomap, gpioscan, schem
14 KiB
RT-4D — управление DMR-баузбендом (чтение/запись) и аудио (вкл/выкл)
Источники: реверс стоковой прошивки V3.25 (rt4d_stock_v3.25_abs_0x08002800.bin, база 0x08002800,
ARM Thumb) + принципиальная схема RT4DDLT01 v2.1 (Xiamen Radtel, 2024-06-04).
0. Что даёт схема (ключ к обеим задачам)
MCU — U500, 48 pin (распиновка LQFP48, F1-совместимая ⇒ AT32F403A/407-класс; подтверждается раскладкой регистров GPIO, см. §2).
| Пин | Цепь на схеме | Назначение |
|---|---|---|
| 12 | AF MUTE4898 | вкл/выкл УНЧ → через R522 (0R) на пин 1 (EN) усилителя U502 = LN4898 |
| 16 / 17 | DIG-RXD / DIG-TXD | UART к DMR-баузбенду FM100B (= USART3 0x40004800) |
| 19 | DIG POWER-EN | питание/включение баузбенда |
| 23 | DIG/ANA AF-SW | выбор источника звука: цифра (FM100B) ↔ аналог |
| 21 | DIG/ANA-SW | переключение тракта модуляции |
| 2 / 3 / 4 | SCK1080 / SDA1080 / FM POWER-EN | I²C и питание FM-вещательного приёмника (BK1080) |
| 25–28 | W25Q CS/SCK/DO/DI | SPI-флеш (codeplug/шрифты/калибровка) |
| 32 / 33 | TXD, RXD/PTT | CPS-UART (USART6) и PTT |
Аудио-тракт: DIG-AFOUT(баузбенд) / аналог → ключи BL1555 (U504/U505) → потенциометр громкости
VR500 10K (громкость аналоговая, MCU ею не управляет) → U502 LN4898 → динамик SPK 16R/1W
и гнездо гарнитуры J502/PJ-208B. Часть цепей (U701, Q700, Q502/Q503) помечены NC — не установлены.
1. Аудио: включение/выключение
1.1 Железо
AF MUTE4898 = GPIOA, бит 2 (PA2) → EN усилителя LN4898. Активный уровень — высокий = звук есть.
1.2 Регистры (AT32F43x-раскладка GPIO, подтверждена по коду)
GPIOA = 0x40020000; SCR = +0x18 (установить биты), CLR = +0x28 (сбросить), TOGR = +0x2C.
#define GPIOA_SCR (*(volatile unsigned*)0x40020018)
#define GPIOA_CLR (*(volatile unsigned*)0x40020028)
GPIOA_SCR = 0x4; // усилитель ВКЛ (звук есть)
GPIOA_CLR = 0x4; // усилитель ВЫКЛ (тишина)
1.3 Штатный API прошивки (ref-counted — лучше использовать его)
Счётчик запросов звука — 16-битное слово флагов 0x20000C6E; бит 0x8000 = «звук активен»,
младшие биты — отдельные источники (бип, тон, приём…).
| vaddr | функция | смысл |
|---|---|---|
0x0801B574 |
audio_request(u16 mask) |
flags |= mask | 0x8000 — заявка на звук |
0x0801A348 |
audio_release(u16 mask) |
flags &= ~mask, затем вызывает ↓ |
0x0801A824 |
audio_off_if_idle() |
если flags == 0x8000 → flags = 0; GPIOA_CLR = 4 (выкл) |
| — | включение | там, где заявка первая: if (flags == 0) GPIOA_SCR = 4; (напр. 0x080100DE, 0x08014D42) |
Проверенные места: 0x08014D44 (SCR, тракт тона/бипа), 0x0801A836 (CLR, простой),
0x080100E0 (SCR), 0x08010840 (CLR, последовательность выключения).
Как рулить: для разовых экспериментов достаточно писать в
SCR/CLR. Для «правильного» поведения (чтобы прошивка сама не выключила звук) — использоватьaudio_request()/audio_release().
1.4 Смежное
- Громкость — аналоговый потенциометр
VR500, программно не регулируется. Громкость DMR-голоса задаётся в самом FM100B:atc_set_call_spk_vol(msg0x02) @0x0800760A. - Тон/бип генерирует TIM1 (
0x40010000, CR1.CEN черезset_bit0()@0x08021C4C); функция бипа —0x08014CB8(частота0x1B8ok /0x65Cошибка). - Выбор источника (цифра/аналог) —
DIG/ANA AF-SW, пин 23.
2. Баузбенд FM100B: запись (команды)
Связь — USART3 0x40004800 (пины 16/17), собственный бинарный протокол, полностью независимый
от CPS-протокола (USART6) и от формата codeplug.
2.1 Формат кадра
68 | cmd | b | sub | CKh CKl | lenH lenL | <payload …len> | 10
Полный размер = len + 9.
2.2 Примитивы отправки (блокирующие, сами качают приём)
| vaddr | функция |
|---|---|
0x0801B044 |
fm100b_send1(u8 cmd,u8 b,u8 sub,u8 data,void*resp,u16 timeout) — 1 байт данных |
0x0801B0C4 |
fm100b_send(u8 cmd,u8 b,u8 sub,u16 len,void*resp,const u8*payload,u16 timeout) |
0x08006C9C |
usart3_tx_buf(const u8*,u16) — сырая отправка |
0x08006CB8 |
usart3_tx_byte(u8) |
0x08003050 |
poll_serial() — насос: разбор FM100B + CPS + слив кольца (звать в любом ожидании) |
Таймауты из стока: 0x3E8 (1000) для установки вызова, 0x64 (100) для конфигурации.
2.3 Примеры (из стока)
atc_set_radio_id(id) : fm100b_send(0x49,1,1,4,resp,be32(id),0x64); // 0x080074FA (msg 0x2A вариант)
call setup : fm100b_send(0x06,1,type,5,resp,[type,be32(id)],0x3E8);
radio_apply_channel : 0x0801AE9C — композит: частота/режим/полоса/мощность/CC/шумодав/ID/CTCSS
Полная таблица команд — в subsystems/dmr.md §2 и subsystems/radio.md §9.
3. Баузбенд FM100B: чтение (ответы)
3.1 Путь приёма
USART3 ISR @0x080205B0 → кольцо 4 КБ
fm100b_rx_parse @0x08018CB0 — ищет кадр 0x68…0x10, проверяет CRC
fm100b_on_frame @0x08006348 — разбор:
status = f[3]; cmd = f[1];
*(u8*)(0x20007476 + cmd) = status; // ← ТАБЛИЦА СТАТУСОВ (193 записи)
if (cmd < 0xC1) jump_table[cmd](); // таблица @0x0800636C
3.2 Что читаемо
- Статус любой команды:
status = *(u8*)(0x20007476 + cmd)сразу после возврата изfm100b_send*. - Данные ответа: раскладываются персональными обработчиками
*Cnf/*Indиз jump-таблицы@0x0800636C(адрес обработчика =0x0800636C + word[0x0800636C + cmd*4]). 104 из 193 команд имеют свой обработчик (остальные 89 — заглушка0x08006C26). Наиболее «содержательные» (с реальным телом):cmd 05 @0x0800668C,06 @0x080066AA,07 @0x0800671E,09 @0x08006816,0A @0x08006870; далее идут короткие переходники к общим обработчикам.
3.3 Как получить «сырое» чтение
Штатный парсер сразу диспатчит кадр. Чтобы видеть любой ответ целиком, нужно перехватить
fm100b_on_frame @0x08006348 (или fm100b_rx_parse @0x08018CB0) и скопировать кадр в свой буфер —
это открывает полный read-доступ к баузбенду.
4. Рекомендуемый способ «рулить» баузбендом
Самый практичный вариант для экспериментов — мост FM100B ↔ ПК: инъекция в свободный флеш
(0x08029000+, см. UI-Architecture.md), которая
- перехватывает
fm100b_on_frameи отправляет сырые кадры в CPS-UART (USART6), - принимает из USART6 команды и вызывает
fm100b_send*.
Тогда весь протокол баузбенда доступен из скрипта на ПК — можно перебирать команды, читать ответы и документировать неизвестные (89 «незанятых» cmd — потенциально интересны).
⚠️ Безопасность: не трогать калибровочные NV-элементы FM100B (
SPCali_*) — это заводская трим-настройка VCO/PLL/мощности. И не писать «сырую» мощность в обходradio_apply_channel— риск для PA.
5. Перепроверка
Для независимой сверки доступна REFW beta42 (rt-4d-refw-beta42.bin, 251 904 Б = 0x3D800 — полный
образ приложения) — другая, с нуля написанная реализация того же железа: удобно сверять GPIO и команды.
Открытый CPS: https://github.com/jcalado/rt4d-cps/releases/tag/v0.5.4.
6. 🔑 Мост ПК ↔ FM100B — уже реализован в прошивке
Самое важное открытие (найдено через разбор REFW и подтверждено в стоке): рация умеет работать прозрачным serial-мостом между ПК и DMR-баузбендом. Именно так заводская утилита «DMR Upgrade Tool» шьёт FM100B — через USB рации.
6.1 Как это сделано в РЕФВ (REFW beta42) — эталонный поток
Меню «Update DMR Chip» (0x08008EAC → UI 0x08005070), затем:
GPIOB->CLR = 0x2; // PB1 (DIG POWER-EN, пин 19) = LOW → выключить баузбенд
delay_ms(200);
GPIOB->SCR = 0x2; // PB1 = HIGH → включить (холодный ребут FM100B)
for (;;) { // бесконечный мост
pc_to_dmr(); // 0x0801A0EC
dmr_to_pc(); // 0x0801A11C
}
| Функция REFW | Роль | Кольцо |
|---|---|---|
0x0801A0EC |
ПК → FM100B | 4 КБ @0x200078E4 (head 0x200078E0, tail 0x20004F78) |
0x0801A11C |
FM100B → ПК | 1 КБ @0x200054B4 (head 0x200054AC, tail 0x200054B0) |
0x0801A094 |
байт → USART3 (ждёт TC) | |
0x0801ADA8 |
байт → USART6 (ПК) | |
0x0801A0D4 |
fm100b_tx_buf(buf,len) |
побайтно |
6.2 То же самое в стоке — мост уже встроен
Направление ПК → FM100B реализовано в двух местах, оба под флагами:
// USART6 RX ISR @0x0802061C (байт с ПК)
b = USART6->DR;
ring[0x200082EF + (head & 0xFFF)] = b; // 4 КБ кольцо, head @0x20000C60
if (*(u8*)0x20000B67) // ← ФЛАГ МОСТА
usart3_tx_byte(b); // сразу в баузбенд (0x0802064A)
head++;
// главный цикл @0x080213E8
if (*(u8*)0x20000B66) { // ← ФЛАГ РЕЖИМА (CPS/мост)
cps_framer(); ...
if (tail < head) usart3_tx_byte(ring[...]); // (0x08021406)
}
| Адрес | Смысл |
|---|---|
0x20000B67 |
флаг: пересылать байты ПК→FM100B прямо в ISR (он же loopback-флаг в usart3_tx_byte) |
0x20000B66 |
флаг режима сессии (ветка CPS/мост в главном цикле) |
0x200082EF |
4 КБ кольцо приёма с ПК, head 0x20000C60 |
0x08006CB8 |
usart3_tx_byte() |
Практический вывод: чтобы «рулить бекеном» с компьютера, не нужно писать свой мост —
достаточно перевести рацию в этот режим (пункт меню «Update DMR Chip» / установка флагов),
после чего с ПК можно слать произвольные кадры 68 … и читать ответы обычным скриптом.
6.3 Новые команды FM100B, найденные в REFW
REFW хранит готовые шаблоны кадров в флеше и шлёт их fm100b_tx_buf(buf,len):
| Адрес шаблона | Кадр | Назначение |
|---|---|---|
0x08020072 |
68 3E 01 01 [CK CK] 00 0F ×15 0F 10 (24 Б) |
cmd 0x3E — «Update DMR Chip» (перевод FM100B в режим обновления) |
0x0802008A |
68 42 01 01 [CK CK] 00 01 FF 10 (10 Б) |
cmd 0x42 |
Команд 0x3E и 0x42 не было в нашем разборе стока — это новые, полученные из REFW.
Отправщик cmd 0x3E — 0x0800509C, вызывается из 4 мест (0x080064DA, 0x08008018,
0x08011186, 0x0801129C).
6.4 Проверка версии прошивки DMR (REFW)
0x08008DE0+ : REFW читает версию FM100B в 0x20004F74 и сравнивает с 0x01020027;
если ниже — показывает «Upgrade DMR FW / to v1.2.0.28 / or higher». То есть версия баузбенда
читается программно — это готовый пример «чтения» из FM100B.