HR_C7000 as a DMR modem
How the baseband inside the HR_C7000 SoC is driven as a DMR Tier II layer-2 modem and as an FM modem: registers,
buffers, interrupts, what to write when, and what the chip does by itself. Board-level facts (pins, RF chip, PA
timer, calibration source) are on the board's hardware page (feature-matrix.md lists the
boards); the stack-side contract is in dmr-modem.md; the HR_C6000 equivalent is in modem-hrc6000.md.
| Contents | |
|---|---|
| 1 | System view: memory map, buffers |
| 2 | Register map: mode, sync words, CRC masks, interrupts, layer 2, BS mode, physical layer transmit |
| 3 | Interrupts and TX timing |
| 4, 5 | Initialization, entering DMR, receive |
| 6, 7 | Transmit sequences, CRC and FEC |
| 8 | FM mode: registers, tones, DTMF, prompt FIFO, receive audio |
| 9, 10 | Driver requirements, open questions |
Sources: the HR_C7000 chip user guide v2.7 (2016; "the datasheet" below) and measurements on a radio or the SDR bench
(marked "measured"). Values without a mark are the ones the driver writes and that work on air (simplex voice both
ways and repeater wake are checked on the HR_C7000 radios, docs/feature-matrix.md). "(unverified)" marks what has
not been confirmed on a radio.
1. The modem's view of the system
| Item | Value |
|---|---|
| Register block | 0x11000000, 32-bit registers (names below are the datasheet's) |
| TX RAM (layer-2 send RAM) | 0x16000000, 1200 bytes, byte wide |
| RX RAM (layer-2 receive RAM) | 0x160004B0, 1200 bytes |
| AMBE voice RAM | 0x16000960–0x1600097A, 27 bytes: one burst's three 72-bit AMBE+2 frames, both TX and RX |
| Prompt (beep) FIFO | 0x16000980 |
| Codec registers | 0x160009C0 (byte wide) |
| PCM buffers (system SRAM) | mic PCM 0x18000000–0x1800009F (hardware writes), speaker PCM 0x180000A0–0x1800013F (CPU writes) |
| Vocoder | none in hardware: the CPU runs the AMBE+2 codec on the PCM buffers and moves 27-byte bursts through the voice RAM (codec-placement.md) |
| Symbol / sample clocks | 4800 baud 4FSK; 30 ms timeslots; internal 38.4 kHz sampling (AK2401 note) |
The modem runs DMR at layer 2: the CPU builds every burst's information bits (LC, CSBK, data blocks, AMBE) and the chip adds the FEC (BPTC, Golay slot type, RS(12,9), QR EMB, CRC with masks), sync, CACH handling and timeslot timing. Layer 3 (the chip's own call control, SMS, BS activation) exists but is not used by any working firmware.
2. Register map used for DMR
Offsets from 0x11000000. Values are the ones the driver writes unless noted.
2.1 Mode and RF interface
| Offset | Register | Fields | Value used | Why |
|---|---|---|---|---|
| 0x100 | WORK_MODE | b8 digital_analog_en; b7 modulator_mode (0 DMR, 1 FM); b6 Tier II; b5 timeslot mode; b4:3 layer (0 PHY, 1 L2, 2 L3); b2 is_repeater; b1 is_aligned; b0 work_slot_sel | 0x80 FM, 0x6E DMR (Tier II, timeslot, layer 2, repeater bit, aligned, slot 1) | Only these two values are ever written. Timeslot choice is done in software with LAYER2_TXRX_CTRL, not with work_slot_sel |
| 0x104 | RF_MODE | b25 phase_avg_en; b24 af_mode; b23:16 AF gain (n/16); b15 RX IQ invert; b14:13 RX mode; b12 dynamic sync threshold; b11 use_dual_phy (second timeslot receive channel, 2.1a); b9 soft ramp; b7 TX IQ invert / deviation mapping; b6:5 TX mode (3 = two-point) | 0x03269860: AF receive on with averaging, gain 0x26 (2.375), RX IQ inverted, dynamic sync threshold, b11 set (2.1a), two-point modulation | Written once at init and left in both modes. Do not switch to 0x03269060 (b11 clear) in DMR |
| 0x108 | SIG_CENTER | b31:24 TX I bias, b23:16 TX Q bias, b15:8 / b7:0 RX I/Q bias | (cal << 24) or (cal << 16) | TX center from factory calibration (board-specific) |
| 0x10C | TX_IF_FREQ | b23:0 | 0x0BB800 (reset value) | |
| 0x110 | RF_CONTROL | b20:16 tx_pre_on (100 us steps); b13:8 rf_pre_on_tx (RF TX interrupt advance, 100 us); b5:0 rf_pre_on_rx | 0x00041F1A: tx_pre_on 0.4 ms, RF TX interrupt 3.1 ms before the TX burst, RF RX interrupt 2.6 ms before RX | 0x00040A0A (1.0 ms) gave bad timeslots at low power. b13:8 is per board (hrc7000_t.rf_tx_lead): a board whose 3.1 ms clipped the inbound burst before ours uses 0x14 (2.0 ms) |
| 0x114 | RF_MOD_BIAS_CTRL | b25:16 two_point_bias; b15:8 sig_reduce (Mod2 amplitude); b7:0 phase_reduce (Mod1 amplitude) | ((cal + 384) << 16) or modcal (0x4D4D before a frequency is set) | Modulation balance from calibration |
| 0x118 | DEV_LIMITER | b15:0 | 0xFF00 (reset value) | |
| 0x11C | RSSI_BIAS_VALUE | read-only per the datasheet: rssi b31:16; b15:8 / b7:0 the measured DC bias of the ADC's I / Q inputs | 0x1381 is written (no effect expected) | |
| 0x128 | PHASE_OUT | read-only: b29:16 / b13:0 the largest and smallest 14-bit AF phase in AF receive mode | not written | A live check that AF reaches the ADC: a board receiving DMR reads about +1216 / -1149 (the datasheet expects about ±1300 at -47 dBm); a value that stays within a few counts means no AF arrives. One board had this when its audio-select GPIO was left high, which takes the RF chip's audio off the ADC |
| 0x120 | THRESHOLD_VALUE | b28:24 sync ratio; b23:16 sync low threshold; b15:8 sync high threshold; b7:0 signal detect | 0x0978786F | |
| 0x164 | RECV_INFO_COUNT | b31:24 sync errors; b8 select FSK error/EVM; b7:0 FSK error | read for BER/EVM only | |
| 0x168 | (undocumented, "REG_X06") | 0x14 | ||
| 0x1B0 | RX_IF_FREQ | b23:0 | 0x0BB800 | |
| 0x1B4 | RF_RECV_DELAY | b18:0, 100 ns steps | 0x36B0 (1.4 ms). the datasheet says 2000 (0.2 ms) for AF mode; the working value is 0x36B0 |
AF receive input (datasheet, document 2). In AF mode the RF chip's demodulated audio goes to the AF ADC's P input, and a DAC output biases its N input; on some boards that is a board DAC output. Document 1 says the AF bias is set through SIG_CENTER instead. A DC step on either input moves the PHASE_OUT center, which the DMR receiver reads as a frequency offset. AF mode has no frequency offset compensation: RX_IF_FREQ applies to IF mode only.
Reference oscillator control (MOD1, datasheet). In receive the MOD1 level is RF_MOD_BIAS_CTRL b25:16 over 0-3.3 V, which holds only while DAC_CONTROL keeps that DAC powered. In TX it is SIG_CENTER b31:24, ±422 mV around 1.65 V in 3.3 mV steps; RF_MODE b9 sets where the ramp starts (0 = 1.65 V). With cal + 384 in b25:16 both states give about the same voltage, within one step (measured).
2.1a RF_MODE bit 11: the DCDM receive switch
The HR_C7000 datasheet names bit 11 use_dual_phy. It turns on a second receive channel, "for repeater chip receive
mode and DCDM mode" (datasheets/C7000/HR_C7000_Document_2.pdf, register table at p. 98; our translation from the
Chinese). DCDM is dual-timeslot direct mode: two conversations on the two DMR timeslots of one frequency, with no
repeater to supply sync. The community name is "dual-capacity direct mode"; the datasheet doesn't expand it. Its DCDM
set-up (section 18.3.5.1, p. 410) is three steps: turn off MS and BS sync receive, enable TDMA1 and TDMA2 sync
detection, and set RF_MODE[11]. Receive stays enabled all the time.
- Channel mode. The bit can follow the channel mode: simplex channels (RX frequency equal to TX frequency) set it and enable TDMA1 and TDMA2 sync; repeater channels clear it and enable only MS and BS sync. That is the datasheet's DCDM set-up on simplex. The datasheet also lists "repeater chip receive mode" as a user of bit 11, which a repeater channel with the bit clear suggests is a different mode. Not resolved.
- RadioFoundry: writes 0x03269860 (bit 11 set) and enables the TDMA1 and TDMA2 sync detection on every channel (
hrc7000.c, the bench note of 2026-10-16), so RadioFoundry runs the DCDM receive set-up on repeater channels too. Whether that matters on a repeater is not measured. - Not a second RF chip. The RF chip is unchanged. The second channel is a second modem receive path. Multi-chip dual watch is a separate feature (RadioFoundry issue #96).
- Not known: whether a simplex receiver with bit 11 on hears a second-timeslot call that it misses with the bit off. No on-air test has compared the two (section 10, item 7).
2.2 Sync words
24-bit halves of the 48-bit ETSI sync patterns, high half first (all written once at init):
| Offset | Register | Value | ETSI pattern |
|---|---|---|---|
| 0x12C / 0x130 | SEND_DATA_SYNC_H / L | 0xD5D7F7 / 0x7FD757 | MS sourced data |
| 0x134 / 0x138 | SEND_RC_SYNC_H / L | 0x77D55F / 0x7DFD77 | RC |
| 0x13C / 0x140 | RECV_MS_SYNC_H / L | 0xD5D7F7 / 0x7FD757, then set bit 31 of H | MS data (receive) |
| 0x144 / 0x148 | RECV_BS_SYNC_H / L | 0xDFF57D / 0x75DF5D, then set bit 31 of H | BS data (receive) |
| 0x14C / 0x150 | RECV_TDMA1_SYNC_H / L | 0xF7FDD5 / 0xDDFD55 | TDMA direct slot 1 data |
| 0x154 / 0x158 | RECV_TDMA2_SYNC_H / L | 0xD7557F / 0x5FF7F5 | TDMA direct slot 2 data |
| 0x15C / 0x160 | RECV_RC_SYNC_H / L | 0x77D55F / 0x7DFD77 | RC |
Voice syncs are not programmed; the chip derives them (they are the bitwise complements of the data syncs; unverified). Bit 31 of the RECV_MS / RECV_BS high halves is an undocumented enable. The RECV_TDMA1 / RECV_TDMA2 high halves need it too: without it the chip never detects the direct mode syncs (bench, 2026-10-16: an SFR's TS1 inbound was not heard until it was set).
SEND_DATA_SYNC is the only send pattern; a voice burst goes out with its complement. RadioFoundry rewrites it before a burst
that asks for another sync family (dmr_tx_burst_t.sync_override: an SFR's TS2 direct mode outbound) and back to MS
for the next burst without one. On air the burst then carries that family (bench, 2026-10-16: TS2 sync with the
repeated LC).
2.3 CRC, RS and color code
| Offset | Register | Value | Meaning |
|---|---|---|---|
| 0x350 | PI_CRC_MASK | 0x6969 | PI header CRC-16 mask (init value 0 in b31:16) |
| 0x354 / 0x358 | RS_H_MASK / RS_H_INIT | 0x969696 / 0 | RS(12,9) mask for the voice LC header |
| 0x35C / 0x360 | RS_T_MASK / RS_T_INIT | 0x999999 / 0 | RS(12,9) mask for terminators (the datasheet's reset value 0x969696 is wrong for ETSI; write 0x999999) |
| 0x364 | CSBK_CRC_MASK | 0xA5A5 | CSBK CRC-16 mask |
| 0x368 | MBCH_CRC_MASK | 0xAAAA | MBC header CRC-16 mask (the datasheet's reset 0xA5A5 is wrong for ETSI) |
| 0x36C | DATAH_CRC_MASK | 0xCCCC | data header CRC-16 mask (the datasheet's reset 0xA5A5 is wrong for ETSI) |
| 0x370–0x37C | (undocumented) | 0xF0, 0x1FF, 0x10F, 0x00 | |
| 0x380 | CRC8_INIT | 0 | short LC CRC-8 init |
| 0x384 | LOCAL_CC | color code in b3:0; b9:8 cc_opt = 0 (deframe only on matching CC); b16 encryption type | Set per channel; when cc_opt = 1 RF_MODE b12 must be 0 |
| 0x388 | VOICE_EMB_CTRL | 0 at init, 2 when entering DMR receive | b1 = burst F EMB from the chip's own RC encoder; b0 = PI bit for bursts B–E |
| 0x38C | SCRAMBLE_REG | 0 | built-in simple voice scrambler keys (RX in b31:16, TX in b15:0); unused |
2.4 Interrupt control
| Offset | Register | Value / use |
|---|---|---|
| 0x398 | SYS_INTERP_LIST | system interrupt causes (read) |
| 0x39C | SYS_INTERP_MASK | 1 = enabled (inverted sense). 0x1187F: bits 0–6, 11, 12, 16 |
| 0x3A0 | SYS_INTERP_CLEAR | write the bits read from the list; repeat until the list reads 0 |
| 0x3A4 / 0x3A8 / 0x3AC | LAYER3_INTERP_LIST / MASK / CLEAR | layer-3 causes; unused |
| 0x3B0 | INTERP_CLEAR | write-1-to-clear: b0 TX timeslot, b1 RX timeslot, b2 RF TX, b3 RF RX, b4 PCM write (mic buffer full), b5 PCM read (speaker buffer consumed) |
SYS_INTERP_LIST bits:
| Bit | Name | Meaning in DMR layer 2 |
|---|---|---|
| 0 | dll_rdy_interp_tx | layer-2 TX processing point (ahead of a TX slot) |
| 1 | dll_rdy_interp_rx | layer-2 RX processing point |
| 2 | dll_latelc_rx_interp | voice late entry: embedded LC assembled from a superframe |
| 3 | dll_frame_rx_interp | a burst was deframed: read RX_TYPE_INFO and the RX RAM |
| 4 | dll_shortlc_rx_interp | short LC (CACH) received |
| 5 / 6 | dll_tx/rx_slot_abnormal | layer-2 TX / RX timeslot boundary abnormal |
| 7 / 8 | gps_lost / gps_slot_abnormal | simulcast only |
| 9 / 10 | ccl_tx / ccl_rx | layer 3 |
| 11 | phy_rx_interp | PHY test reception |
| 12 | phy_rdy_interp | PHY slot early interrupt |
| 13 / 14 / 15 | fm_sig_rx / fm_rx / fm_tx | FM signaling detect (CTCSS/CDCSS/DTMF/selcall) / FM RX data / FM TX data |
| 16 | phy_recv_slot_abnormal | PHY slot boundary abnormal |
| 17 | rdy_next_short_lc | repeater CACH |
2.5 Layer-2 control
| Offset | Register | Fields |
|---|---|---|
| 0x400 | LAYER2_CONTROL | b7 txen, b6 rxen, b5 layer2slot, b4 txtmp_master_mode, b3 rx_master_auto_update, b2 tx_slave_auto_mode, b1 tx_master_mode (active TX timing), b0 rx_master_mode |
| 0x404 | LAYER2_SLOTON | write: b0 dll_tx_slot_on, b2 dll_rx_slot_on (establish TX / RX slot in active mode), plus self-clearing open/close strobes b5–b13 (b5 dll_rx_open_tx: build the TX slot from the receive slot, with tx_slave_auto_mode 0 and b0 set; datasheet 10.2.2.17); read: b31 TX slot on, b29 RX slot on |
| 0x408 | LAYER2_TXRX_CTRL | b31 tx_complete_cpu (continuous mode only), b23 syncfail_layer2_rx (drop and re-acquire RX slot sync), b22 syncfail_layer2_tx, b15 begin_v_layer2 (voice access success), b7 txnextsloten (transmit in the next slot), b6 rxnextsloten (receive in the next slot), b0 autotest |
| 0x40C | LAYER2_SLOT_CNT | active timing presets: b31 / b30 RX / TX slot number, b29 + b28:20 TX temporary slot counter, b19 + b18:10 RX slot counter, b9 + b8:0 TX slot counter (each enable bit write-clear). In the physical layer mode a TX counter preset (in bits or in symbols) left the slot timing where it was (bench 2026-10-21, 2.10) |
| 0x410 | LAYER2_MAX_CNT | slot length trim: b15 sign (1 shorter) and b11:8 RX, b7 sign and b3:0 TX, n × 100 us. No effect on the physical layer's slot timer (2.10 finding 13) |
| 0x414 | LAYER2_SLOT_UPDATE | slot re-sync strobes (unused) |
| 0x418 | LAYER2_SEND_TYPE | b31:22 codec type for custom data types 11–15; b12 RC in data sync; b11 data EMB RC source; b10 / b9 BS forwarding; b8 crc_calc_sel (0 = chip computes CRCs); b7:4 data type / voice burst index; b3 1 = voice (or RC) burst, 0 = data/control burst; b2 second data header; b1:0 LCSS |
| 0x41C | LAYER2_STATUS | b31 TX slot number, b30 RX slot number, b17:9 RX slot bit count, b8:0 TX slot bit count |
| 0x424 | LAYER2_SLOT_PRE | b23:16 0x55 (do not change), b15:8 RX processing-interrupt advance, b7:0 TX advance. Value 0x551010 |
| 0x428 | LAYER2_BS_MODE | b11:8 bs_idle_type (reset 9 = Idle), b5:4 gps_update_rate, b3 gps_pps_en, b2 bs_tx_mode, b1 bs_rx_mode, b0 bs_single_freq (0 dual frequency, 1 single frequency). Reset type H: a chip reset does not clear it. Section 2.9 |
| 0x430 | BS_SECOND_REG | b5:0 GPS second, simulcast only |
| 0x434 | BS_STATUS | read only: b29:28 CACH LCSS counter, b27:19 and b18:8 simulcast slot and frame counters, b5:0 locked second |
| 0x438 | BS_CACH | b1 bs_cach_at, b0 bs_cach_tc. Does not control the CACH on air as a static value (2.9) |
| 0x12C, 0x130 | SEND_DATA_SYNC_H / _L | TX data sync word, reset D5D7F7 7FD757 (MS data). Writing the BS data sync (DFF57D 75DF5D) makes the chip send BS-sourced data frames (2.9) |
Values written to LAYER2_CONTROL: 0xC0 = TX and RX enabled, passive timing (receive, follow the other side's
slots); 0xC3 = TX and RX enabled, active TX and RX timing (we set the slot clock); 0xCB = 0xC3 plus b3
rx_master_auto_update, for tx_listen. The datasheet (10.2.2.16) says b0 rx_master_mode should normally be 0, and that
in active receive mode the receive slot edges move with the received sync only when b3 is set. With 0xC3 alone an SFR
heard almost nothing in its listening slot while it sent (measured, 2026-10-16: L18/1), so tx_listen sets b3 for
a transmission on our own timing.
An aligned start (DMR_TIMING_ALIGNED with a burst received, an SFR's repeat) writes no LAYER2_CONTROL value: it stays
0xC0 and LAYER2_SLOTON = 0x25 (0x05 plus b5 dll_rx_open_tx) builds the TX slot from the receive slot. Any
LAYER2_CONTROL write at the start left the receiver without slot sync for about 1 s, deaf to the call being repeated:
0xC3 (TX slots 37.7-41.6 ms after the caller's instead of 30), 0xC4 (b2, TX slots following the receiver: 28.6-30.1 ms),
even 0xC4 written over 0xC4. With b0 rx_master_mode set (0xC5, 0xCD) the receiver kept its sync but no TX slot started.
Without a CACH (an SFR's repeat, a direct-mode answer) the first slot after the start is the one the received bursts
came in; the driver sends in the slot whose number (LAYER2_STATUS b31) is the received bursts' chip slot for the same
TS, the other one for another TS. Measured on the SDR bench, 2026-10-16: our bursts 30.1 ms after the caller's, the
whole call heard and repeated.
Through a repeater (BS-sourced bursts; two boards on the SDR bench, issue 72, 6 of 6 runs on the right slot and on the grid) the slot comes from the CACH TC alone (5.3): the TC lock, then a TC estimate that toggles at every timeslot interrupt and gives the TC of the slot starting at that interrupt. The receiver runs in LAYER2_CONTROL 0xC4 (TX slots follow the receiver) from every receive restart on a repeater channel, and that restart releases both slot syncs (TXRX b23 and b22) and is also the end of every transmission. A start waits in receive for the lock, then writes only TXRX (no LAYER2_CONTROL or SLOTON write at the key-up); the first TX tick sends in the next slot when the estimate is the other TS's TC. A first TX-vector interrupt less than half a slot after the last receive one is the same slot and does not toggle the estimate. Without the lock in 1 s the start is refused (TX_REJECTED, nothing sent).
Bench findings behind this (two boards on the SDR bench, 2026-10-18):
| Tried | Result |
|---|---|
| Slot from the last burst's chip slot (RX_TYPE_INFO b19 against LAYER2_STATUS b31) | the other slot about half the time: b31 read 0 at every TX start, and the first burst after the receiver synced carried TC 0 whatever its slot, with b31 set |
| SLOTON 0x25 (TX slot built from the receive slot) at the start | on the repeater's slot grid only when strobed at one parity of receive interrupt, and then always TS2; a skipped first slot put the bursts 8-20 ms off the grid |
| 0xC4 written only at the receive restart, only b23 released | TX slots at the phase they had then (the wake-up burst's, sent on our own timing): 0-30 ms off the grid from run to run |
| 0xC4 rewritten at the key-up | the same phase problem on one radio, right on the other: the write is not what sets the phase |
| First TX-vector interrupt counted as a new slot | about 5 ms after the last receive one in some runs: the estimate one slot off |
| b22 released with b23 at every receive restart, full reset after every TX, nothing written at the key-up | TS1 and on the grid, 6 of 6: the first TX tick comes 29.9-30.0 ms after the last receive tick |
Timing rules the chip follows (what the bench showed; the data sheet doesn't say):
- The TX slot phase is latched, not derived. In 0xC4 the TX slots follow the receiver, but only from the moment the TX slot sync was last released and re-acquired. A TX sync left from an earlier transmission, including a burst sent on our own timing (the wake-up), keeps its phase and the next call goes out off the repeater's grid by whatever the difference was. Every receive restart must release b22 as well as b23.
- Check the phase, not the slot. The measure of "on the grid" is the first TX-vector interrupt 30 ms (one slot)
after the last receive one (
txh uin the diagnostics: 299-300 in the passing runs, 90-180 in the failing ones). It is cheaper to read than the air capture and gives the same verdict. - LAYER2_STATUS b31 is not a usable slot number at the start of a transmission (always 0), and RX_TYPE_INFO b19 is one burst's chip slot, not a reference. Neither decides a TX slot on a repeater.
- Writing LAYER2_CONTROL drops the receiver's slot sync for about 1 s, in every value tried. Set it at a receive restart and leave it.
- A TX-vector tick right after an RX-vector one is the same slot. The two interrupts of one slot came about 5 ms apart in some runs; counting both as slots put the estimate one slot off.
- A deframe near a tick belongs to the previous slot. A burst is deframed late in its slot, around the next timeslot interrupt: in the first half of a slot count it to the one before.
- A slot estimate needs a lock first. The first bursts after the receiver syncs can carry a wrong TC; the driver waits for four alternating values.
SFR (two boards on the SDR bench, 2026-10-18, each run after a key-up on our own timing): the repeat's
TX slot comes 30.1 ms and 30.7 ms after the inbound one, with the TX slot sync released only
on repeater channels (6 runs) and on every channel (10 runs) alike: the SLOTON b5 start takes the receiver's phase
whatever the TX sync held, so the stale phase that broke the repeater path does not reach an SFR. Every receive
restart releases both syncs. Read at an SFR's start, LAYER2_STATUS b31 picked the right slot
in every run (txh e1: b31 set, the received chip slot next, ours after it). The first TX-vector interrupt comes two
slots after the last receive one (txh u 599-600), not one as through a repeater: the slot right after the SLOTON
strobe has no TX tick, so a tick count across an SFR's start is one short. The phase check is a whole number of
slots. A direct mode start can take the slot from the TDMA1 / TDMA2 sync type in RX_TYPE_INFO b3:2 plus
LAYER2_STATUS b29 against an offset captured at that moment, never from b31 or b19.
Values written to LAYER2_TXRX_CTRL: 0x00 nothing next slot; 0x40 receive next slot; 0x80 transmit next slot; 0x8040 receive next slot with the voice-access flag; 0x800000 release RX slot sync.
2.6 LAYER2_SEND_TYPE codes
| Code | Burst | Source of payload in TX RAM |
|---|---|---|
| 0x10 | Voice LC header (data type 1) | LC bytes 0–8 at 0x16000000; bytes 9–11 may be left 0 (chip adds RS(12,9) with RS_H_MASK) |
| 0x20 | Terminator with LC (data type 2) | LC bytes 0–8 |
| 0x30 | CSBK (data type 3) | 10 bytes; chip adds CRC with CSBK_CRC_MASK |
| 0x40 | MBC header (4) | 10 bytes, chip CRC |
| 0x50 | MBC continuation (5) | 12 bytes |
| 0x60 | Data header (6) | 10 bytes, chip CRC with DATAH_CRC_MASK |
| 0x70 | Rate ½ data block (7) | 12 bytes (CRC-9 / CRC-32 by the CPU) |
| 0x80 | Rate ¾ data block (8) | 18 bytes |
| 0xA0 | Rate 1 data block (10) | 24 bytes |
| 0x08 + (n << 4) + LCSS | Voice burst n = 0..5 (A..F) | AMBE 27 bytes to the voice RAM 0x16000960; for burst A also the call's LC (bytes 0–8) at 0x16000000 for the embedded LC |
Datasheet example for one superframe: A = 0x08, B = 0x19, C = 0x2B, D = 0x3B, E = 0x4A, F = 0x50, i.e. LCSS 0 on A, 1 (first fragment) on B, 3 (continuation) on C and D, 2 (last) on E, 0 on F. The driver writes LCSS = 0 on every voice burst (0x08, 0x18, 0x28, 0x38, 0x48, 0x58). Whether the chip then still sends the embedded LC fragments correctly is unverified; follow the datasheet.
2.7 RX_TYPE_INFO (0x390, read after a deframe interrupt)
| Bits | Name | Meaning |
|---|---|---|
| 30 | crc8_error_flag | short LC CRC-8 bad |
| 29 | golay_check_flag | slot type Golay bad |
| 28 | qr_check_flag | voice EMB QR bad |
| 27 | cs_check_flag | late-entry checksum bad |
| 26 | crc16_error_flag | CRC-16 bad (CSBK, headers) |
| 25 | rs24_error_flag | RS(12,9) bad (LC header, terminator) |
| 24 | data_block_error | CRC-9 / CRC-32 result for data blocks |
| 23 | cach_at | CACH AT bit |
| 22 | cach_tc | CACH TC bit: the timeslot number of the received burst |
| 21:20 | cach_lcss | CACH LCSS |
| 19 | current_rx_slot | slot number |
| 18 | save_soft_finish | soft information stored |
| 17 | pi | privacy indicator of the received voice burst |
| 16:15 | lcss | voice burst LCSS |
| 14:12 | current_voice_burst | voice burst number, 1–6 = A–F (0 when not voice) |
| 11:8 | data_type | slot type data type |
| 7:4 | cc | received color code |
| 3:2 | recv_sync_type | 0 MS, 1 BS, 2 TDMA slot 1, 3 TDMA slot 2 |
| 1:0 | sync_type | 0 none (EMB-only burst), 1 voice, 2 data, 3 RC |
"CRC good" for a deframed burst = bits 30:24 all zero. Data blocks have no CRC-16, so do not drop them on these flags; check CRC-9/CRC-32 in software. RX_INFO (0x394): b24:16 trunking multiframe number, b10:0 received RC.
2.8 Buffer layouts
TX RAM (0x16000000), datasheet table 52: 0x00–0x0B the current control burst (PI header 10 + 2 CRC, LC header / terminator 9 + 3 RS, CSBK / MBC header / data header 10 + 2 CRC, MBC block 12, rate ½ 12, rate ¾ 18, rate 1 24); 0x00–0x09 also embedded LC (72 bits + 5-bit checksum in byte 9, "prepared when voice burst A is about to be sent"); 0x29–0x2C burst F EMB filler (or key ID / algorithm ID for privacy); 0x30–0x4A voice burst when voice comes from TX RAM (layer-2 BS mode, AUDIO_CONTROL b0 = 1); 0x4B–0x50 embedded RC; 0x495–0x4AF send voice key (27 bytes).
RX RAM (0x160004B0), datasheet table 53: the deframed control burst at 0x00 (LC header / terminator 9 + 3, CSBK / headers 10 + 2, data blocks 12 / 18 / 24, idle 12, C_RC 7 + 2). Embedded LC after late entry also appears at 0x00–0x08; the chip keeps the previous call's LC there until a new one is decoded (measured).
Voice RAM (0x16000960): write 27 bytes before arming a voice burst; read 27 bytes after a voice deframe interrupt Burst numbering for privacy keystream purposes is RX_TYPE_INFO's 1–6.
2.9 BS (repeater) mode: bench results 2026-10-20
Datasheet source: the Chinese original (HR_C7000_Document_2, 10.2.2.17, 10.2.2.18, 10.2.2.22, 10.2.2.25-10.2.2.28).
The registers, their bits and what the chip does with them were tried on one board (RadioFoundry build with a scratch memory
poke command, SDR bench capture on 461.4625 MHz, lowest power, sdrmon.py analyze --cach). (measured)
Datasheet summary:
| Item | Content |
|---|---|
| BS_MODE b2 / b1 / b0 | BS transmit mode, BS receive mode, dual (0) or single (1) frequency repeater |
| LAYER2_SLOTON b3 / b4 (0x404) | bs_tx_slot_on / bs_rx_slot_on: establish the BS TX / RX slot. Status: b28 o_bs_tx_slot_on, b27 o_bs_rx_slot_on |
| LAYER2_TXRX_CTRL b4 (0x408) | bs_tx_sync_rx_en: BS TX slot follows the BS RX slot |
| LAYER2_SEND_TYPE b10 bs_tx_data_rdy (0x418) | 0: the chip frames and sends Idle (type from BS_MODE b11:8); 1: forwarded data is ready (self-clears at the slot edge) |
| LAYER2_SEND_TYPE b9 bs_tx_data_source | 0: the chip encodes; 1: send 196 information bits without coding |
| AUDIO tx_voice_source = 1 (4.6.5.3) | voice comes from MCU written RAM (the layer 2 BS TX path) |
| BS_CACH b1:0 | AT and TC "for BS transmit mode" |
| RDY_NEXT_SHORT_LC interrupt (bit 17) | the chip asks for the next CACH short LC |
| BS_INTER_IN / BS_INTER_OUT pins (P12 / R12) | baseband BS interrupt in and out, apparently to link two chips |
| BS_SECOND_REG, BS_STATUS, GPS bits | same-frequency simulcast from a GPS pulse (2.9a). Not needed here |
Findings:
| # | Finding | Evidence |
|---|---|---|
| 1 | The BS registers exist and read the datasheet's reset values: BS_MODE 0x00000900, the others 0 | read at idle |
| 2 | Writable bits match the datasheet: BS_MODE b11:8, b5:0; BS_CACH b1:0; BS_SECOND b5:0. Reserved bits read 0. BS_STATUS ignores writes. 0x42C is not implemented (reads 0, ignores writes) | write and read back |
| 3 | BS_MODE survives a chip reset (stubtool --attach --boot reset). A radio left in BS mode stays there until written back or hardware reset |
read after reset |
| 4 | With BS_MODE b2 set, RadioFoundry's normal call start fails: the TX path never arms (dmr.tx_error=stalled, TX denied rejected). BS TX needs its own start sequence |
ptt with b2 set |
| 5 | With b2 set, then LAYER2_SLOTON b3 set and b0 cleared during a call, the chip reports o_bs_tx_slot_on (SLOTON reads 0x1000000C) and BS_STATUS b29:28 becomes non-zero (one read: 0x10000000, so the CACH LCSS counter runs) | read back, 0x434 = 0x10000000 |
| 6 | The chip then sends its own Idle frames (data type 9 from bs_idle_type, color code from LOCAL_CC) with no MCU data, every 60 ms in one slot, with the same 30 ms bursts keyed and unkeyed (the RF keying is still RadioFoundry's). The first frame comes about 0.8 s after the switch | SDR: 54 IDLE bursts, 60.0 ms apart |
| 7 | The sync word on those frames comes from SEND_DATA_SYNC. At its reset value they carry MS data sync. Written with the BS data sync they decode as BS-sourced (bs-d) |
SDR family column |
| 8 | Each BS frame is preceded by a CACH with valid TACT parity (the analyzer counts it tact ok), but AT, TC, LCSS and the short LC payload are not controlled by BS_CACH: with BS_CACH held at 0, 2 and 3, AT and TC showed all four combinations about equally often and the short LC fragments were noise. TC did not alternate |
SDR: 54 CACHs per run, three runs |
Unresolved: where the chip takes the CACH bits from. The likely source is the same as on the HR_C6000, per slot values
the MCU supplies at the RDY_NEXT_SHORT_LC interrupt (BS_CACH written every slot, short LC in a TX RAM window), which a
static poke cannot reproduce. Not tried: the real start sequence (arming BS TX without RadioFoundry's call), bs_tx_data_rdy = 1
with MCU data in both slots, tx_voice_source, bs_rx_mode (receive BS, label slots), bs_single_freq, the BS_INTER
pins, and two chips. The test was a register switch in the middle of a RadioFoundry call, so the one-slot cadence in 6 may come
from RadioFoundry's own TX loop and not the chip.
2.9a gps_pps_en: the simulcast slot lock (datasheet only)
LAYER2_BS_MODE b3 gps_pps_en enables what the datasheet calls same-frequency simulcast (datasheet 10.2.2.25-10.2.2.27).
It is a BS mode feature: the chip disciplines its BS slot timing to a GPS second pulse, so several repeaters on one
frequency send each slot at the same instant. Datasheet only; nothing below has been tried.
| Item | Content |
|---|---|
BS_MODE b5:4 gps_update_rate |
how often the slot timing is corrected from the pulse: every 6 s, 3 s or 1 s |
| BS_SECOND_REG b5:0 | the GPS second (0-59), written by the MCU from the receiver's time messages |
| BS_STATUS | gps_lcss_cnt (the CACH LCSS phase), gps_bit_cnt (a 30 ms slot counter run from the pulse), bs_slot_num (the 60 ms frame number in the 60 s cycle), bs_second_lock |
System interrupt b8 gps_slot_abnormal, b7 gps_lost_interp |
the pulse's slot timing jumped; the pulse was lost |
Answers for the split duplex TX half (split-duplex.md 3.8, INFO capability bit 5):
- It needs a pulse on a chip pin. The datasheet names none for it; the only candidate is BS_INTER_IN (P12, "BS sync
interrupt input"). Where an HR_C7000 radio in
boards/has a GPS module, it is wired to an MCU UART only, and no pulse line to the chip or to the MCU is documented. So no radio here can use it. - It locks the chip's BS mode slot timing, which the TX half doesn't use (it sends in the physical layer mode, 2.10). Whether it also moves the physical layer's slot timer is unknown.
- The host-side grid (ALIGN with the host's clock on NTP or a GPS-disciplined source) gives the same slot numbering without a pulse, to the clock sync's error bound. A PPS input report stays a follow-up for a board that wires one.
2.10 Physical layer transmit: the split duplex TX half
The split duplex TX half (split-duplex.md 5.4) needs a CACH whose AT, TC and short LC it controls, which the BS mode
of 2.9 doesn't give. It uses the physical layer mode instead (datasheet 9.5), as the HR_C6000 does (modem-hrc6000.md 9.1):
the chip sends the CPU's bits without coding, and the core builds every slot (bsOutboundRawSlot: the CACH, then the
burst with BS sync, slot type and coded payload). Driver: drivers/modem/hrc7000/hrc7000_bs.c. Datasheet only until the bench
results below say otherwise.
| Item | Content (datasheet 9.2, 9.4.1, 9.5) |
|---|---|
| WORK_MODE | 0x22: physical layer, timeslot mode (b5 set, layermode 0, b1 aligned); 0x02: physical layer, continuous mode |
| RF_MODE | two-point TX (b6:5 = 3), as the 0x03269860 of 2.1 |
| LAYER2_CONTROL | b7 txen, b6 rxen, b1 tx_master_mode, b0 rx_master_mode. 0x82: transmit with our own slot timing |
| LAYER2_TXRX_CTRL | b7 txnextsloten: send in the coming slot; b6 rxnextsloten; b0 autotest. The datasheet's set-up writes 0x81 |
| TX RAM 0x00-0x23 / 0x24-0x47 | SEND_DATA0 / SEND_DATA1: one 36-byte (288-bit) frame each, sent alternately. Two bits per symbol, 00 +1, 01 +3, 10 -1, 11 -3 (the ETSI dibit mapping), first bit in b7 of the first byte |
| System interrupt b12 | phy_rdy_interp: the physical layer's slot interrupt |
The datasheet's example frame for a bit error test carries the MS data sync at bits 120-167. In a burst the sync starts at
bit 108, so that frame is 12 bits of guard, the 264-bit burst and 12 bits of guard: an MS slot, aligned to the slot
edge. A BS outbound has no guard: its slots follow each other without a gap, each CACH centered on the slot edge. The
driver writes the core's slots (CACH first) back to back, so the bit stream on air is the continuous outbound, and the
CACH half that falls before the chip's slot edge is only a constant 1.25 ms offset of the slot clock (the RX half's
phase tracking absorbs it, split-duplex.md 6). Whether the chip ramps the PA between two slots of the timeslot mode
(it would cut into the CACH) is what the continuous mode variant is for.
The driver, while the BS outbound runs (it takes the slot engine's place, as hrc6000_bs.c does):
- Start: TXRX 0, SLOTON 0, WORK_MODE 0x80 then 0x22 (or 0x02), LAYER2_CONTROL 0x82, codec buffers cleared. A new slot clock.
- Slot clock: a boundary at the first of the TX timeslot, RX timeslot or PHY_RDY interrupts in a slot (one more within 15 ms is the same slot). Unkeyed the clock runs and nothing is sent.
- Keyed: at each boundary a slot's 36 bytes go to TX RAM and TXRX = 0x80. The chip sends them in the slot that starts about 5 ms after the boundary's interrupt, not in the one after it (finding 11), so the slot clock names that slot. The key-up starts on an outbound TS1 slot. Each slot plays its own queue, an empty one an idle burst.
- The first RF TX interrupt keys the RF chip and starts the PA one-shot; the PA then stays up. Unkeyed: no more slots, the PA off one slot later. No boundary for 200 ms: PA off, unkeyed.
- Stop (and leaving DMR): TXRX 0, PA off, WORK_MODE 0x80 then 0x6E, LAYER2_CONTROL passive, both slot syncs released, then the normal receive restart (5.2). LAYER2_BS_MODE is never written; the power-on set-up writes its reset value 0x900 (2.9: it survives a chip reset).
Bench variants (dmr_bs_config_t.variant, the debug link's SPLIT op 1, split-duplex.md 10.1), a bit field: b0 the
continuous mode, b1 the autotest bit with the send bit (0x81), b3:2 the TX RAM halves (0 the slot to both, 1 alternate
starting with SEND_DATA0, 2 alternate starting with SEND_DATA1). The default 0 is timeslot mode, 0x80, both halves.
Bench results, 2026-10-20 (TX half keyed 8 s per variant at the lowest power on 461.4625 MHz; no SDR capture: the RTL-SDR was in use elsewhere. A second radio on the same frequency was the receiver). (measured)
| # | Finding | Evidence |
|---|---|---|
| 1 | PHY_RDY (system interrupt b12) comes first in every slot and makes the slot clock; the TX and RX timeslot interrupts follow in the same slot (two per slot) | bs lines: 366 PHY_RDY, 734 timeslot interrupts, 1 boundary from a timeslot vector; longest gap 30.0 ms, no late slot |
| 2 | Variants 0, 1, 4 and 8 give one RF TX interrupt per key-up: the chip stays in TX from slot to slot. With the autotest bit (variant 2) it comes every 60 ms: the chip leaves TX between slots | bs.rftx_ints 1 against 141 in 8 s |
| 3 | Every slot is set up while keyed (about 280 in 8.4 s), no underrun, no stall | bs.bursts, STATUS idle |
| 4 | The outbound decodes: the second radio deframed one idle burst per 60 ms with BS sync and color code 2, no CRC errors, labeled TS1 from the CACH TC (variant 8: TS2) | its I, cc2 BS ts1 lines |
| 5 | After the role ends (also by lease expiry) a normal call goes out whole | dmr.tx_error=ok; the second radio heard 3 headers, 42 voice bursts, the terminator |
Second board on the SDR bench, 2026-10-21 (lowest power, 461.4625 MHz, tests/hil/test_split_duplex.py). (measured)
| # | Finding | Evidence |
|---|---|---|
| 6 | Variant 0: the outbound is whole. BS data sync on both slots (189 / 188 idle bursts in 10 s), a burst every 30.0 ms (same slot 60.000 ms), one key-up edge, every CACH of valid TACT parity, TC alternating on every CACH, AT following SET_AT, the short LC decoding (93 ok, 0 bad), no underrun | test_split_tx_half_off_air: 377 BS bursts, tact ok 377, TC alternating 376/376 |
| 7 | Variant 1 (continuous mode) gives the same outbound: the timeslot mode keeps the carrier and every CACH between slots too | 358 BS bursts, tact ok 358, TC 357/357 |
| 8 | A host-fed voice call goes out whole in its slot | test_split_tx_half_voice |
| 9 | Keyed 5 minutes: carrier offset +58 Hz at key-up, +56 Hz at the end, no underrun, no stall (10 015 slots) | captures across the key-up and the unkey (sdrmon.py analyze needs carrier-free air in a capture to find its noise floor) |
| 10 | The RX half labels a bench call, residual 0-1 us after centering, and the label flips with the epoch | test_split_rx_half_labels |
Variant 0 stays the default. Writing the slot to both TX RAM halves works, so which half the chip sends in each slot isn't needed. Not measured: PA temperature (no sensor on the diagnostics page), receiver desense with the RX half beside it.
The first board gives the same results on the SDR bench (2026-10-21, variant 0): 352 BS bursts, 176 per slot, tact ok 352, TC alternating 351/351, AT following SET_AT, short LC 87 ok and 0 bad, no underrun; the voice call whole. Its RX half's labels pass too (residual 1 us).
The slot clock against the air, SDR bench, 2026-10-21 (TX half; each GET_EPOCH mapped to wall-clock time through the host's time sync, compared with the SDR's timestamps of the outbound's bursts, whose slot comes from their CACH TC). (measured)
| # | Finding | Evidence |
|---|---|---|
| 11 | The slot set up at a boundary is on air from about 5 ms after the boundary's interrupt. The first driver took it to go out one slot later and named the boundary after the slot before: the epoch was one slot early, and a subscriber's TS2 call through a pair went up labeled TS1 and was repeated on TS1 | before the fix TS2 bursts started 5.0 ms after the epoch's "TS1" and TS1 bursts 25.0 ms before it; after it TS1 bursts start 4.6 ms after the epoch (six epochs each). Another board's TX half measures +2.2 ms the same way |
| 12 | The slot timer restarts when own TX timing is enabled: LAYER2_CONTROL 0xC0 (passive), then 0x82 at a chosen time. The first boundary then comes about 1.2 ms before a whole slot after the write; with that learned, the boundaries land 9-120 us from the host's grid (ALIGN, split-duplex.md 3.8; the host's clock sync bound was 3 ms). Passive timing keeps counting while the driver waits, so it takes no boundary then. Redoing WORK_MODE through FM with LAYER2_CONTROL left at 0x82, or a LAYER2_SLOT_CNT TX counter preset, doesn't move it |
test_split_align: 15 ALIGNs in five runs, all aligned; the learned restart delay -1167 us |
Slewing (split-duplex.md 3.8, capability bit 2), 2026-10-22, no RF. (measured)
| # | Finding | Evidence |
|---|---|---|
| 13 | LAYER2_MAX_CNT (2.5, datasheet 10.2.2.20: b7 sign and b3:0 n × 100 µs for TX slots, b15 and b11:8 for RX) does not move the physical layer's slot timer. Tried: the TX trim for one slot (written at a boundary, cleared at the next), the TX and RX trims together, and both held set. The boundaries stayed where they were: the HR_C7000 driver offers no bs_slew, so a keyed TX half reports its drift and re-aligns at the next unkey, as the HR_C6000 does |
test_split_slew with a scratch driver: 35 trims of +100 µs asked in 5 s, the phase unchanged (-1535, -1696, -1464 µs in the three runs) |
3. Interrupts
| Vector (PIC source) | Name | Fires | What it means / what to do | Clear |
|---|---|---|---|---|
| 61 (29) | System_inter | after the chip deframes a burst (bit 3), on late entry (bit 2), slot abnormalities, FM signaling | read SYS_INTERP_LIST, act, write it back to SYS_INTERP_CLEAR until 0 | SYS_INTERP_CLEAR |
| 62 (30) | Time_slot_inter_tx | every 30 ms on the TX slot clock | decide the next slot: write TX RAM / voice RAM, SEND_TYPE, then TXRX_CTRL (0x80 = send in the coming slot, 0x00 / 0x40 otherwise). Start the PA one-shot whenever 0x80 is written | INTERP_CLEAR b0 and b1 |
| 63 (31) | Time_slot_inter_rx | every 30 ms on the RX slot clock | same decision while receiving (and for the last repeater-wake wait state) | INTERP_CLEAR b0 and b1 |
| 64 (32) | Rf_tx_inter | rf_pre_on_tx × 100 us (3.1 ms with RF_CONTROL 0x41F1A) before each TX burst | put the RF chip into TX (first burst: full TX set-up; later bursts: idle → TX); note the time | INTERP_CLEAR b2 (first thing) |
| 65 (33) | Rf_rx_inter | rf_pre_on_rx before an RX slot | not used; TX is turned off from the next timeslot interrupt once the burst has run ≥ 20 ms | INTERP_CLEAR b3 |
| 59 (27) | PCM_rd_data_interp | every 10 ms while AUDIO_CONTROL b5 is set | speaker buffer consumed: write the next 160 bytes of PCM to 0x180000A0 | INTERP_CLEAR b5 |
| 60 (28) | PCM_wr_data_interp | every 10 ms while AUDIO_CONTROL b4 is set | 160 bytes of mic PCM ready at 0x18000000 | INTERP_CLEAR b4 |
Both timeslot interrupts are handled by one routine; it decides by the current activity (transmitting states on the TX vector, receiving states on the RX vector). DMR TX does not start unless vectors 62 and 64 are hooked.
3.1 TX timing within a burst (inferred from the register values)
| t (ms) relative to the TX timeslot interrupt in which the burst is armed | Event |
|---|---|
| 0 | TX timeslot interrupt: payload and SEND_TYPE written, TXRX_CTRL = 0x80, PA one-shot started |
| ≈ 26.9 | RF TX interrupt (3.1 ms ahead): RF chip idle → TX |
| 28 (default "DMR PA" setting, 1–35 ms) | PA one-shot: band PA select on, PA drive DAC to the calibrated value |
| ≈ 29.6 | tx_pre_on (0.4 ms) |
| 30 | burst on air (27.5 ms of symbols plus guard) |
| next timeslot interrupt with ≥ 20 ms of TX elapsed | PA drive 0, PA selects off, RF chip back to RX |
3.2 Masking the modem interrupts
PIC_MASK bits 29, 30, 31 and PIC_MASK_1 bit 0 (vectors 61–64) (datasheet mapping). Mask them around multi-register updates from task level.
4. Initialization (power-on, once)
Ordered; (register, value, why). The whole DMR block is written once here and not at every mode change.
| # | Register | Value | Why |
|---|---|---|---|
| 1 | ADC controller | reset and idle (board-specific) | the baseband shares the analog front end |
| 2 | IO_DIPLEX2 (0x03C) | clear bit 28 | DAC channel B pin to analog (PA drive) |
| 3 | DAC_PD_CTRL / PD_MODE_EN / DATA_A / B / C | 0x01 / 0x01 / 0 / 0 / 0x06E2 | DAC B (PA drive) and C on, A off; PA drive 0 |
| 4 | FM_BANDWIDTH (0x500) | 0x5F | FM wideband defaults |
| 5 | FM_DEV_COEF (0x504) | 0x33500 | FM deviation defaults (overwritten per TX/RX) |
| 6 | SYS_SOFT_RSTN (0x000) | clear bit 4, wait 2 us | codec reset pulse |
| 7 | codec AICR_DAC, AICR_ADC, CR_MIC1, CR_MIC2, CR_DAC, CR_ADC, CR_VIC, CR_CK, GCR_DACL, GCR_MIC1, GCR_ADCL, CR_DAC_AGC, CR_ADC_AGC, DR_ADC_AGC | 0xC0, 0xC0, 0x00, 0x70, 0x20, 0x20, 0x0C, 0x40, 0x80, 0x00, 0x8B, 0, 0, 0x34 | audio codec on, mic input 2, gains (board-specific) |
| 8 | LINEOUT_CTRL (0x088) | 0x01 | speaker line out on |
| 9 | SIG_CENTER | (cal << 24) or (cal << 16) | TX I/Q center |
| 10 | RF_MOD_BIAS_CTRL | ((cal + 384) << 16) or 0x4D4D | modulation balance until a TX frequency is known |
| 11 | RF_MODE | 0x03269860 | two-point TX, AF RX, b11 set (2.1a) |
| 12 | DEV_LIMITER | 0x0000FF00 | |
| 13 | SYS_SOFT_RSTN | 0x190 | see the board's reset-bit notes |
| 14 | DAC_CONTROL (0x070) | 0x8000001B | baseband DAC set-up: b31 offset code; b5 / b4 power down DAC A / B; b2 / b1 low power DAC A / B (1 = off or low power per the datasheet). So DAC A on at full power, DAC B powered down and low power |
| 15 | ADC_CONTROL (0x074) | = 0x41C3 | |
| 16 | AUDIO_CONTROL (0x080) | 0x02 | I2S slave (reset default), all audio paths off |
| 17 | AUDIO_BUFFER_CLR (0x084) | 0x03 | clear vocoder encode and decode buffers |
| 18 | sync words (2.2) | as listed | DMR syncs |
| 19 | THRESHOLD_VALUE | 0x0978786F | sync detection |
| 20 | PI_CRC_MASK, RS_H_MASK, RS_H_INIT, RS_T_MASK, RS_T_INIT, CSBK_CRC_MASK, MBCH_CRC_MASK, DATAH_CRC_MASK | 0x6969, 0x969696, 0, 0x999999, 0, 0xA5A5, 0xAAAA, 0xCCCC | ETSI masks; the chip then computes those CRCs/RS itself |
| 21 | 0x370, 0x374, 0x378, 0x37C | 0xF0, 0x1FF, 0x10F, 0 | undocumented |
| 22 | CRC8_INIT, VOICE_EMB_CTRL, SCRAMBLE_REG | 0, 0, 0 | |
| 23 | SYS_INTERP_MASK | 0x1187F | enable layer-2 TX/RX processing, late entry, deframe, short LC, slot abnormal, PHY, PHY slot abnormal |
| 24 | LAYER2_SLOT_PRE | 0x551010 | processing-interrupt advance |
| 25 | 0x168 | 0x14 | undocumented |
| 26 | RF_RECV_DELAY | 0x36B0 | 1.4 ms receive path delay |
| 27 | RX_IF_FREQ, TX_IF_FREQ | 0x0BB800 each | |
| 28 | RECV_MS_SYNC_H, RECV_BS_SYNC_H | set bit 31 | undocumented enable |
| 29 | RF_CONTROL | 0x00041F1A | RF TX interrupt 3.1 ms early |
| 30 | WORK_MODE | 0x80 | start in FM (idle) |
| 31 | FM_TONE1..4 | 0x05930629, 0x06D10787, 0x09AC0AB0, 0x0BD10D10 | DTMF tone steps (section 8) |
| 32 | GCR_ADCL | mic gain or 0x80 | DMR mic gain setting |
5. Entering DMR and starting receive
5.1 Enter DMR (from FM or idle; also at every DMR TX start)
| # | Register | Value | Why |
|---|---|---|---|
| 1 | RF_MOD_BIAS_CTRL | ((cal + 384) << 16) or modcal(f_TX) | balance for this TX frequency |
| 2 | GCR_ADCL | DMR mic gain or 0x80 | |
| 3 | DAC_PD_CTRL | clear bit 1 | PA drive DAC back on (FM receive powers it down; without this, DMR after FM has no PA drive) |
| 4 | WORK_MODE | 0x80 | pass through FM: resets the DMR state machine |
| 5 | WORK_MODE | 0x6E | DMR, Tier II, timeslot, layer 2 |
AUDIO_CONTROL is not rewritten here; after FM it ends up 0x00 once FM audio is switched off.
5.2 Start / restart receive
Used at DMR entry, after TX, and wherever reception must be (re)acquired:
| # | Register | Value | Why |
|---|---|---|---|
| 1 | (enter DMR, 5.1) | ||
| 2 | VOICE_EMB_CTRL | 0x02 | burst F EMB from the chip's encoder |
| 3 | LAYER2_CONTROL | 0xC0 | TX/RX enabled, passive timing |
| 4 | LAYER2_SLOTON | 0x00 | drop slot establishment |
| 5 | LAYER2_TXRX_CTRL | 0x800000 | release RX slot sync |
| 6 | LAYER2_TXRX_CTRL | 0x40 | receive in the next slot |
| 7 | LAYER2_SLOTON | 0x05 | dll_rx_slot_on and dll_tx_slot_on |
A sequence of TXRX 0xC00000, 0, 0xC00000, 0x8040 with no SLOTON writes, or a busy wait in the color code probe, does not acquire sync reliably. Use this one everywhere.
5.3 While receiving
| Event | Action |
|---|---|
| System interrupt, bit 3 (deframe) while idle, good CRC, wanted CC | start following: TXRX_CTRL = 0x40 for the next slot (0x8040 here does not work) |
| Each TX/RX timeslot interrupt while following one slot | alternate: our slot → TXRX = 0x8040 (receive, voice-access flag) for the next; other slot → 0x00 |
| Voice deframe | read RX_TYPE_INFO, 27 bytes from the voice RAM; burst number from bits 14:12; LC header / terminator / CSBK / data from the RX RAM |
| BS sync received (recv_sync_type 1) | the other side is a repeater: track the TC bit (RX_TYPE_INFO b22) to find which slot is ours. Locked after 4 bursts in a row, one per slot, with TC alternating; held while a burst came in the last 390 ms. RX_TYPE_INFO b19 and LAYER2_STATUS b30/b31 are not usable for this |
| Color-code scan | set LOCAL_CC to the received CC, then LAYER2_CONTROL = 0xC0 and, in repeater mode, the 5.2 restart steps 4–7 (no busy wait) |
| Late entry (bit 2) | embedded LC is in the RX RAM; voice reception should start at the next burst A |
| End | terminator (data type 2) or loss of bursts; TXRX_CTRL = 0 |
| Reception over (nothing taken for 390 ms) | the 5.2 restart: the chip drops the sender's slot timing and its timeslot interrupts stop |
| Slot timing with nothing usable for 600 ms | the 5.2 restart, again every 600 ms while it lasts. Without it the chip kept a departed sender's timing, reported only unusable and abnormal slots and took no later call (measured) |
6. Transmit sequences
All transmit actions happen in the timeslot interrupts: the CPU arms one slot at a time with TXRX_CTRL = 0x80 and
leaves the other slot unused (0x00, or 0x40 / 0x8040 to listen: tx_listen, an SFR's inbound slot), giving one burst per 60 ms in its timeslot (datasheet figures 99–100). Every arming also starts the PA one-shot (a board with a PA one-shot timer). The RF TX interrupt that follows keys the RF
chip.
6.1 Start of transmission
| # | When | Register writes |
|---|---|---|
| 1 | task level, PTT | load the LC (9 bytes, then 3 zero bytes) into TX RAM 0x16000000; codec reset; LAYER2_CONTROL = 0xC3 (active timing, we own the slot clock); TXRX_CTRL = 0 |
| 2 | next timeslot interrupt | TXRX_CTRL = 0x80, SEND_TYPE = 0x10 (LC header) |
| 3 | next | TXRX_CTRL = 0 |
| 4 | next | TXRX_CTRL = 0x80, SEND_TYPE = 0x10 (second header) |
| 5 | next | TXRX_CTRL = 0 |
| 6 | next | TXRX_CTRL = 0x80, SEND_TYPE = 0x10 (third header) |
TXRX_CTRL bit 31 (tx_complete_cpu) belongs to continuous mode and must not be set at step 2. For a direct-mode start after receiving on a repeater channel the same LAYER2_CONTROL = 0xC3 applies.
6.2 Voice
Alternating slots: in the inactive slot TXRX_CTRL = 0; in the active slot:
- For burst A (n = 0): rewrite the call LC (or talker alias LC) into TX RAM bytes 0–8 for the embedded LC.
- Write 27 bytes of AMBE (three 9-byte frames) to the voice RAM 0x16000960, or the AMBE silence pattern when no encoded audio is ready.
- TXRX_CTRL = 0x80, SEND_TYPE = 0x08 + (n << 4) (+ LCSS, see 2.6); n = n + 1 modulo 6.
Encoded audio must be produced continuously during TX: encode one 20 ms block per tick whenever two PCM buffers are waiting.
6.3 Terminator and back to receive
| # | Slot | Writes |
|---|---|---|
| 1 | active | voice RAM = silence; TX RAM = LC; TXRX_CTRL = 0x80; SEND_TYPE = 0x20 |
| 2 | next | direct mode: done → idle. Repeater mode: LAYER2_CONTROL = 0xC0, TXRX_CTRL = 0 |
| 3 | next (repeater mode) | TXRX_CTRL = 0x40 (receive next slot) → receiving |
6.4 Data and CSBK
Per burst, in an active slot: write the payload to TX RAM (10 bytes for CSBK / MBC header / data header: the chip appends the CRC-16 with the programmed mask because SEND_TYPE b8 = 0; 12 / 18 / 24 bytes for rate ½ / ¾ / 1 blocks, CRC-9 and CRC-32 computed by the CPU), SEND_TYPE = data type << 4, TXRX_CTRL = 0x80, start the PA one-shot. Inactive slot: TXRX_CTRL = 0 (or 0x40 to listen for a response). Sequence for a packet: data header (0x60), blocks (0x70/0x80/0xA0), optional terminator. CSBK signaling: optional preamble CSBKs then the function CSBK, all 0x30 (datasheet appendices B, C). Open RX afterwards to catch the response.
6.5 BS activation (repeater wake)
No layer-3 BS activation: LAYER2_BS_MODE, BS_* registers and WORK_MODE values 0x73/0x76/0x77 are not used. What they do when written is in 2.9 (measured).
| # | When | Writes |
|---|---|---|
| 1 | PTT on a repeater channel with no repeater heard | codec reset; LAYER2_CONTROL = 0xC3 |
| 2 | timeslot interrupt | TX RAM bytes 0–11: 0xB8 (last block, CSBKO 0x38 BS_Dwn_Act), FID 0, bytes 2–6 = 0 (BS address 0), bytes 7–9 = own ID big endian, bytes 10–11 = 0; SEND_TYPE = 0x30; TXRX_CTRL = 0x80; start the PA one-shot; red LED |
| 3 | next timeslot interrupt | SEND_TYPE = 0x30, TXRX_CTRL = 0x80 again: the same CSBK in the immediately following slot; wake response timeout 600 ms |
| 4 | next | go to receive (5.2), count TC toggles |
| 5 | while waiting | when the received TC has toggled correctly 4 times → repeater awake, start 6.1; otherwise retry from step 2 after 600 ms, give up after a few tries |
One CSBK without keying the PA gives only an unmodulated carrier. The PA one-shot and the second consecutive CSBK make repeater wake work (which of the two matters is not known).
7. CRC and FEC the chip does
| Burst | Chip adds | CPU supplies |
|---|---|---|
| LC header, terminator | RS(12,9) with RS_H_MASK / RS_T_MASK, BPTC(196,96), slot type Golay | 9 LC bytes |
| CSBK, MBC header, data header, PI header | CRC-16 with the programmed mask (when SEND_TYPE b8 = 0), BPTC, slot type | 10 bytes |
| Rate ½ / ¾ / 1 data | BPTC / trellis ¾ / none, slot type | 12 / 18 / 24 bytes including CRC-9 and the final CRC-32 |
| Voice | sync / EMB (QR), embedded LC fragments (5-bit checksum from TX RAM byte 9 or computed), burst F EMB from the RC encoder when VOICE_EMB_CTRL b1 = 1 | 27 AMBE bytes; LC for burst A |
| Short LC (CACH) | CRC-8 (CRC8_INIT) | repeater mode only |
On receive the chip checks the same codes and reports them in RX_TYPE_INFO b30:24; it decodes FEC and leaves only information bits in the RX RAM.
8. FM mode
8.1 Registers
| Offset | Register | Fields | Values used |
|---|---|---|---|
| 0x500 | FM_BANDWIDTH | b6 band-pass on; b4 pre-emphasis; b3 TX 25 kHz; b2 adjacent-channel filter 25 kHz; b1 RX 25 kHz; b0 CTCSS RX 25 kHz | TX: 0x5F (25 kHz), 0x50 (12.5 kHz). RX: 0x4F (25 kHz), 0x40 (12.5 kHz) |
| 0x504 | FM_DEV_COEF | b23:16 TX limiter (tr_sig_lim, reset 0x0F), b15:8 RX deviation coefficient, b7:0 TX deviation coefficient |
0x40A20 (25 kHz), 0x40A25 (12.5 kHz); the limiter is the board's (hrc7000_t.fm_tx_limit, 0 = 4) |
| 0x508 | FM_SQL | b15:0 noise measure: higher = less signal | squelch threshold in software |
| 0x50C | SUBVOICE_COEF | b27:16 detector sample depth (default 0x7D0), b15:8 sub-audio / DTMF deviation, b7:0 detection threshold | DTMF deviation (8.3) |
| 0x510 | FM_MOD | b23:16 repeat-symbol threshold; b15:14 XTCSS length; b13:12 CTCSS reverse-burst mode (01 = 240°, 10 = 180°); b11 CDCSS TX invert; b10 CDCSS RX invert; b9 CDCSS turn-off code; b8 XTCSS EOM; b4 selcall; b3 CTCSS; b2 CDCSS; b1 DTMF; b0 XTCSS | |
| 0x518 | CTCSS_ADDR | b15:8 RX tone address, b7:0 TX tone address (table 8.2) | |
| 0x51C | USER_DEFINE_CTC | b31 enable, b20:0 step = round(f × 65.536) | non-standard tones |
| 0x520 | CDCSS_ADDR | b24:16 RX code, b8:0 TX code (the 9-bit DCS code as binary, e.g. 114 octal = 001 001 100). The TX code is taken when FM_PTT goes on; later writes don't change the code on air (measured: written after FM_PTT, the radio sent the previous code) | |
| 0x524 | SELCALL_TONE | 2/5-tone frequency | |
| 0x528 | TONE_SEND_CTRL | b9 selcall send; b8 selcall finish; b7 dtmf_send; b6 dtmf_finish (0 mutes voice during signaling); b3:0 DTMF digit | |
| 0x550–0x55C | FM_TONE1..4 | two 16-bit DTMF tone steps each | 8.3 |
| 0x560 | FM_PTT | b0 FM transmit | 1 in FM TX, 0 in FM RX |
| 0x564 / 0x568 | FM_DECODE_FLAG1 / 2 | FLAG2: b11:8 DTMF digit received; b1 CDCSS detected; b0 CTCSS detected; XTCSS flags | sub-audio squelch |
| 0x530–0x54C | FM_FUNCTION_COEF1..8 | detector coefficients, 2·cos(2πf / fs)·2^22 (fs 32 kHz for DTMF, 8 kHz for custom CTCSS) | |
| 0x080 | AUDIO_CONTROL | b31 prompt FIFO full; b7 prompt path; b6 FM audio to speaker; b5 CPU-fed speaker PCM; b4 mic PCM to CPU; b3 sidetone; b2 I2S select; b1 I2S slave (reset 1); b0 voice source (0 codec, 1 TX RAM), FM transmit included (8.4) | |
| 0x56C | FM_ADDR_SW | 1 = low half, 2 = high half of the TX / RX RAM audio buffer in use (8.4) |
8.2 FM entry sequences
| Mode | Writes |
|---|---|
| FM receive | FM_BANDWIDTH and FM_DEV_COEF for the bandwidth; FM_PTT = 0; AUDIO_CONTROL = 0x40; GCR_DACL = 0x80 (0 dB); DAC_PD_CTRL bit 1 set (PA drive DAC off); WORK_MODE = 0x80 |
| FM transmit | FM_BANDWIDTH and FM_DEV_COEF for the bandwidth; DAC_PD_CTRL bit 1 cleared; WORK_MODE = 0x80; FM_PTT = 1. Board: mic switch to analog, RF chip to TX, PA select, DAC B drive |
| FM audio on / off in DMR context | AUDIO_CONTROL b6 set and WORK_MODE = 0x80 / b6 cleared and WORK_MODE = 0x6E |
| Mute mic / line out | codec CR_ADC b7 / CR_DAC b7 = 1 |
Standard CTCSS addresses (datasheet table 59; RX uses the same addresses in CTCSS_ADDR b15:8):
| Addr | Hz | Addr | Hz | Addr | Hz | Addr | Hz |
|---|---|---|---|---|---|---|---|
| 0x01 | 67.0 | 0x0E | 107.2 | 0x1B | 167.9 | 0x28 | 62.5 |
| 0x02 | 71.9 | 0x0F | 110.9 | 0x1C | 173.8 | 0x29 | 159.8 |
| 0x03 | 74.4 | 0x10 | 114.8 | 0x1D | 179.9 | 0x2A | 165.5 |
| 0x04 | 77.0 | 0x11 | 118.8 | 0x1E | 186.2 | 0x2B | 171.3 |
| 0x05 | 79.7 | 0x12 | 123.0 | 0x1F | 192.8 | 0x2C | 177.3 |
| 0x06 | 82.5 | 0x13 | 127.3 | 0x20 | 203.5 | 0x2D | 183.5 |
| 0x07 | 85.4 | 0x14 | 131.8 | 0x21 | 210.7 | 0x2E | 189.9 |
| 0x08 | 88.5 | 0x15 | 136.5 | 0x22 | 218.1 | 0x2F | 196.6 |
| 0x09 | 91.5 | 0x16 | 141.3 | 0x23 | 225.7 | 0x30 | 199.5 |
| 0x0A | 94.8 | 0x17 | 146.2 | 0x24 | 233.6 | 0x31 | 206.5 |
| 0x0B | 97.4 | 0x18 | 151.4 | 0x25 | 241.8 | 0x32 | 229.1 |
| 0x0C | 100.0 | 0x19 | 156.7 | 0x26 | 250.3 | 0x33 | 254.1 |
| 0x0D | 103.5 | 0x1A | 162.2 | 0x27 | 69.3 |
The address order is not ascending; map from frequency, not from a list index. Other tones: USER_DEFINE_CTC. TX
sub-audio deviation via SUBVOICE_COEF b15:8 (typical 350 Hz narrow, 600 Hz wide). The FM_BANDWIDTH bits don't scale
it: the reset value 0x28 gives 300 Hz CTCSS at both bandwidths, and RadioFoundry writes 0x50 at 25 kHz (600 Hz) (measured, UHF). DCS gets a third of the deviation from the same value (0x28: 100 Hz), and reads the field as signed:
0xF0 sent the code inverted at about 40 Hz. RadioFoundry writes three times the CTCSS value for DCS, capped at 0x7F: 0x78 at
12.5 kHz (305 Hz) and 0x7F at 25 kHz (323 Hz) (measured, UHF). The boot stub's restart doesn't reset the modem, so the field
keeps the last transmission's value; RadioFoundry's init writes 0x28 rather than reading it back (one board read 0xFF there
after a 25 kHz transmission and sent CTCSS at 1.9 kHz deviation, measured). When FM_PTT drops, the chip sends
the reverse burst / turn-off code before stopping. FM_MOD b9 (reset 1) enables the CDCSS turn-off: a 134.4 Hz
tone. RadioFoundry sets b9 with DCS and, for the core's tail (radio-features.md 3.3a), drops FM_PTT while the RF stays
keyed; FM_PTT on again resumes the code. On the SDR bench: 134.4 Hz for 0.20 s, then the carrier without
modulation until the core unkeys (measured). Unkeying together with FM_PTT, as before, put none of it on air. On a detected tone the FM signaling system interrupt fires
(SYS_INTERP_LIST b13) and FM_DECODE_FLAG2 b0 (CTCSS) or b1 (CDCSS) goes 1, back to 0 at the end. CDCSS tail
detection uses FM_FUNCTION_COEF1 = 0x7F499D (134.4 Hz).
8.3 DTMF and single tones
| Item | Value |
|---|---|
| Tone step | round(f × 2.048) (= f × 65536 / 32000) |
| Default tone registers | FM_TONE1 = 697 / 770 Hz (0x0593 / 0x0629), FM_TONE2 = 852 / 941 (0x06D1 / 0x0787), FM_TONE3 = 1209 / 1336 (0x09AC / 0x0AB0), FM_TONE4 = 1477 / 1633 (0x0BD1 / 0x0D10); high half = first tone |
| DTMF deviation (SUBVOICE_COEF b15:8) | Measured (UHF, 1750 Hz single tone): about 5.8 Hz per step, 0x60 = 559 Hz, 0xC0 = 1117 Hz; a value of 0x12 gives about 100 Hz. RadioFoundry uses 0xC0 at 12.5 kHz and 0xFF (the most, about 1.5 kHz) at 25 kHz, on both bands. VHF is unmeasured (a VHF value about 3.25 times the UHF one would match the ratio of the VHF to the UHF modulation calibration bytes, unverified; RadioFoundry applies those bytes per band and keeps the UHF values) |
| Send sequence | FM_MOD b1 = 1; FM_PTT = 1; TONE_SEND_CTRL b6 = 0 (mute voice); AUDIO_CONTROL b3 = 1 for sidetone; TONE_SEND_CTRL b3:0 = digit; b7 = 1 to start the digit, b7 = 0 to end it; repeat per digit; finally b6 = 1 to restore voice |
| Arbitrary single tone | write the tone step into the high halves of FM_TONE1 and FM_TONE3 and send DTMF digit "1" (697 + 1209 pair), restore the defaults after. On air at 1750 Hz (measured): TONE_SEND_CTRL = 0x81 (digit 1 on, voice muted) with FM_MOD b1 set, 0 afterwards (the reset value) |
RadioFoundry's DTMF (hrc7000_tx_dtmf, radio-features.md 3.7) |
the arbitrary-tone sequence with the low tone's step in FM_TONE1's high half and the high tone's in FM_TONE3's, digit "1" sent: one sequence for every digit and for single tones, without the digit codes of figure 95. The deviation is the single tone's (SUBVOICE_COEF b15:8, hrc7000_dtmf_dev); the chip attenuates the low tone 2.5 dB; on air the high tone is 5.0 dB over the low one for every digit on two boards (measured), so the datasheet's figure is likely 10 log of the amplitude ratio. That forward twist is the chip's design and isn't corrected: after a receiver's de-emphasis it is +0.4 to +2.1 dB (radio-features.md 3.7) |
| Receive | FM_MOD b1 = 1; SUBVOICE_COEF b27:16 = 1056 (33 ms at 32 kHz), b7:0 threshold; FM_FUNCTION_COEF1..8 = 0x7ECD9D, 0x7E8A34, 0x7E368C, 0x7DD245, 0x7C690D, 0x7B9F03, 0x7AA7A8, 0x7979F5; read FM_DECODE_FLAG2 b11:8 on each signaling interrupt; FM_MOD b23:16 sets the repeat-symbol window |
8.4 Prompt / beep FIFO
Set AUDIO_CONTROL b7, then while AUDIO_CONTROL b31 is 0 write 64 bytes (32 × 16-bit samples, low byte first, 8 kHz) to 0x16000980..0x160009BF; clear b7 when done (measured). High byte first (16 samples per push) sounds distorted. Flushing stale audio: fill with 0x7F bytes.
RadioFoundry plays all speaker PCM through this FIFO (platform/hrc7000/audio_pcm.c). The CPU-fed speaker buffer at
0x180000A0 (AUDIO_CONTROL b5, section 3) gave rough, choppy beeps on one board with every sample layout tried,
with no queue underruns (measured).
The prompt path also feeds the TX modulator: with AUDIO_CONTROL b7 set during a DMR transmission, the beep's audio rides on the 4FSK. On one board the talk permit tone overlapped the second and third voice LC headers, which then carried no decodable sync on the SDR bench and the bench modem took the call by late entry; with b7 held clear while transmitting, all three headers decoded in five runs (measured). Keep b7 clear during any transmission; a beep that starts then plays silently, and b7 comes back on at TX end if the beep is still running.
APRS transmit uses the same path on purpose (platform/hrc7000/hrc_afsk.c): during an FM transmission, with b7 set
and the microphone muted by the board, the FIFO's samples are the modulation. A timer tops the FIFO up every 2 ms;
speaker PCM is held off the FIFO meanwhile (hrc_audio_pcm_hold), since its chunks would land between the tones.
Bell 202 decoded off air from this path with a valid FCS (measured, aprs.md 13). The DTMF tone
generator (8.3) needs no PCM, but its deviation field tops out near 1.5 kHz, short of the 3 kHz APRS wants.
The datasheet doesn't document this route. It names two FM transmit audio sources, selected by AUDIO_CONTROL b0 (datasheet 7.2.2.1): the codec ADC (0) or the TX RAM (1). As a source, the TX RAM is 0x16000030-0x1600042F, two 512-byte halves of 256 samples (32 ms at 8 kHz), and FM_ADDR_SW (0x56C) names the half in use (1 low, 2 high); SYS_INTERP b15 is the FM TX interrupt. The prompt FIFO is documented only as playback to the codec DAC (datasheet 7.3). LINEOUT_CTRL (0x088) b1 is line out 1 and b0 line out 2, the same PWM waveform on both (RadioFoundry sets 0x01). Two-point modulation: SIG_CENTER b31:24 biases MOD1 (the VC-TCXO) and b23:16 MOD2 (the VCO), 3.3 mV a step; RF_MOD_BIAS b7:0 and b15:8 are the MOD1 and MOD2 amplitudes; RF_MODE b10 sends a 40 Hz test square. The prompt FIFO path decodes on some boards but not on others, which take the mixer route below.
The FM transmit limiter (FM_DEV_COEF b23:16) caps the deviation of audio that enters through the codec ADC, the microphone and the mixer route below. The prompt FIFO's ring play is not capped by it: AFSK through ring play reached 5.1 kHz peak with the limiter at 4. Through the ADC path the ceiling scales with limiter x TX coefficient (measured, 25 kHz, Bell 202 tones):
| Limiter | TX coefficient | Peak deviation |
|---|---|---|
| 4 | 0x20 | 1.65 kHz (clipped at every level tried) |
| 4 | 0x30 | 2.48 kHz (clipped) |
| 8 | 0x20 | 3.18 kHz (clipped) |
| 0x0F | 0x40 | 4.18 kHz |
| 0x0F | 0x20 | 4.57 kHz, max 4.84 kHz (not clipped) |
So a limiter of 4 holds speech to about 1.65 kHz at 25 kHz. A board sets fm_tx_limit for each bandwidth.
Where ring play doesn't reach the modulator on its own, the codec's digital mixer (datasheet 6.5.2.10-11) carries the DAC's
audio into the ADC's record path, which is the FM transmit input. CR_MIX (codec 0x10: b7 mix_en, b6 load, b5:0 the
MIX_n address) and DR_MIX (0x11) reach MIX_0..3. MIX_2 b0 (mix_rec) makes the ADC's output ADC + DAC, and MIX_3 b7:4
= 0xF zeroes the ADC's own input in that sum. hrc7000_codec_dac_to_tx sets MIX_3 and then MIX_2, clears them in the
reverse order and ends with CR_MIX = 0. The codec mic mute (CR_ADC b7) must stay off on this route: it mutes the mix
too. Through this route Bell 202 decoded off air with a valid FCS (measured, a board where ring play alone sent a bare
carrier).
8.5 FM receive audio to the CPU
In FM (WORK_MODE 0x80) the modem copies its demodulated receive audio into the RX RAM as well as to the codec (the datasheet's "analog receive recording", on by default in FM):
| Item | Value |
|---|---|
| Buffer | RX RAM 0x160004E0..0x160008DF: two halves of 512 bytes (256 samples), used in turn |
| Sample format | 16-bit, high byte at the lower address |
| Rate | 8 kHz: the halves alternate every 32.0 ms (measured over 95 changes) |
| Half to read | FM_ADDR_SW (0x56C) b1:0: 1 = the first half, 2 = the second |
| Interrupt | SYS_INTERP bit 14 (fm_rx_interp) as each half fills; its mask bit (SYS_INTERP_MASK b14) is off in the power-on mask |
| Input | the modem's AF input. On a board with a separate FM RF chip that is its audio output, routed by a board GPIO |
The audio is not squelched: it carries noise while no signal is present, so a demodulator can work below the squelch. Receiver noise reaches full scale (peak 32767) with the reset AF gain (measured).
RadioFoundry reads it in the system interrupt (platform/hrc7000/fm_rx_audio.c): the DMR glue owns that interrupt and calls
an FM hook for bit 14, in FM too, and the hook copies each half into a 128 ms ring for the APRS demodulator.
9. Driver requirements
What a working DMR driver does that a first attempt tends to miss. Details are in the sections named.
| # | Area | Requirement |
|---|---|---|
| 1 | Vectors | hook the TX timeslot (62) and RF TX (64) vectors as well as 61 and 63; TX states run on 62, 64 keys the RF chip (3) |
| 2 | Register set-up | write the DMR block once at init, not at every DMR entry (4) |
| 3 | RF_MODE | 0x03269860 always, b11 set (2.1a) |
| 4 | RF_CONTROL | 0x00041F1A, RF TX interrupt 3.1 ms early; 0x00040A0A gives bad timeslots at low power |
| 5 | DMR entry | calibration, mic gain, DAC_PD_CTRL bit 1 cleared, WORK_MODE 0x80 then 0x6E (5.1) |
| 6 | RX restart | SLOTON 0, TXRX 0x800000, 0x40, SLOTON 5, VOICE_EMB_CTRL 2 (5.2) |
| 7 | RX start and after TX | TXRX 0x40; 0x8040 only in the steady receive alternation |
| 8 | CC probe | no busy wait |
| 9 | TX start | LAYER2_CONTROL 0xC3, TXRX bit 31 not set (6.1) |
| 10 | PA keying | one-shot timer from each arming, "DMR PA" delay default 28 ms |
| 11 | RF chip | idle to TX at TX start and at every RF TX interrupt |
| 12 | Mic switch | set the board's mic switch to analog at each TX start so DMR audio reaches the codec |
| 13 | Repeater wake | CSBK in two consecutive slots, PA keyed, extra wait state (6.5) |
| 14 | TX audio | encode in the tick when 2 or more PCM buffers wait (6.2) |
| 15 | Timeslot handler | run it from RAM so flash busy time adds no jitter |
| 16 | Calibration | SIG_CENTER and RF_MOD_BIAS_CTRL from factory data |
| 17 | Interrupt masking | PIC_MASK bits 29-31 and PIC_MASK_1 bit 0 (3.2) |
10. Open questions
- LCSS bits on voice bursts B–E (2.6): the datasheet sets them, the driver does not. Does late entry work on other radios receiving this modem's transmissions?
- Whether the 0x370–0x37C, 0x168 and bit-31 sync-enable writes matter.
- RF_RECV_DELAY 0x36B0 vs the datasheet's 2000 for AF mode.
- The real timing of the TX timeslot interrupt relative to the slot boundary (section 3.1 is inferred).
- Data and CSBK transmission on the C7000 have only been checked against the datasheet and shared host tests, not on air.
- FM sub-audio: the C7000's own CTCSS/CDCSS engine (section 8) is not used on a board with a separate FM RF chip; the driver routes TX CTCSS to that chip instead.
- RF_MODE b11 (2.1a) on simplex receive: does a radio with the bit set hear a call on the second timeslot that it misses with the bit clear? No on-air test has compared the two. A test: a call on one timeslot while the radio receives the other, with b11 on and then off. Also: RadioFoundry sets b11 on repeater channels. Does that change repeater reception?
- BS mode (2.9): where the chip's CACH AT, TC and short LC come from (likely BS_CACH and a short LC written at every RDY_NEXT_SHORT_LC). The split duplex TX half doesn't need it: it uses the physical layer (2.10).
- A slot length trim for the physical layer mode (2.10 finding 13): LAYER2_MAX_CNT has none. Whether the trim works in the layer 2 timing (LAYER2_CONTROL b5 layer2slot), and any other register that does, is untested.