Files
rt-4d/docs/baseband-and-audio.md
T
viktor 5f4d207aa5 Мост к DMR-модулю, разбор спектра REFV DualTachyon, карта запчастей прошивки
Баузбенд 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
2026-07-22 21:17:20 +09:00

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 == 0x8000flags = 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 (msg 0x02) @0x0800760A.
  • Тон/бип генерирует TIM1 (0x40010000, CR1.CEN через set_bit0() @0x08021C4C); функция бипа — 0x08014CB8 (частота 0x1B8 ok / 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), которая

  1. перехватывает fm100b_on_frame и отправляет сырые кадры в CPS-UART (USART6),
  2. принимает из 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 0x3E0x0800509C, вызывается из 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.