Add DiSEqC motor control, QO-100 DATV reception, and carrier survey

Firmware v3.03.0: DiSEqC Manchester encoder (cmd 0x8D extended),
parameterized spectrum sweep (0xBA), adaptive blind scan (0xBB),
error code reporting (0xBC). All new function locals moved to XDATA
to fit within FX2LP 256-byte internal RAM constraint.

Motor control: DiSEqC 1.2 positioner with USALS GotoX, stored
positions, interactive keyboard jog, 30-second safety auto-halt.

QO-100 DATV: Es'hail-2 wideband transponder tools — LNB IF
calculator, narrowband scan, tune, and TS-to-video pipe (ffplay/mpv).

Carrier survey: six-stage pipeline (coarse sweep → peak detection →
fine sweep → blind scan → TS sample → catalog). JSON catalog with
differential analysis, QO-100 optimized mode, CSV/text export.

TUI: F9 Motor screen (3-column layout with signal gauge), F10 Survey
screen (Full Band + QO-100 tabs). Bridge, demo, and theme updated.

Docs: motor.mdx, survey.mdx, qo100-datv.mdx guide, tui.mdx updated
for 10 screens. Site builds 41 pages, all links valid.
This commit is contained in:
Ryan Malloy 2026-02-15 17:01:11 -07:00
parent 0f4ba4766f
commit cc3a0707a1
20 changed files with 5645 additions and 84 deletions

View file

@ -4,7 +4,9 @@
*
* Stock-compatible vendor commands (0x80-0x94) plus custom
* spectrum sweep, raw demod access, blind scan (0xB0-0xB3),
* hardware diagnostics (0xB4-0xB6), and signal monitoring (0xB7-0xB9).
* hardware diagnostics (0xB4-0xB6), signal monitoring (0xB7-0xB9),
* and advanced commands: parameterized sweep (0xBA), adaptive
* blind scan (0xBB), error codes (0xBC), DiSEqC messaging (0x8D).
*
* SDCC + fx2lib toolchain. Loaded into FX2 RAM for testing.
*/
@ -57,6 +59,17 @@
#define SIGNAL_MONITOR 0xB7
#define TUNE_MONITOR 0xB8
#define MULTI_REG_READ 0xB9
#define PARAM_SWEEP 0xBA
#define ADAPTIVE_BLIND_SCAN 0xBB
#define GET_LAST_ERROR 0xBC
/* error codes (set by I2C helpers, read via 0xBC) */
#define ERR_OK 0x00
#define ERR_I2C_TIMEOUT 0x01
#define ERR_I2C_NAK 0x02
#define ERR_I2C_ARB_LOST 0x03
#define ERR_BCM_NOT_READY 0x04
#define ERR_BCM_TIMEOUT 0x05
/* configuration status byte bits */
#define BM_STARTED 0x01
@ -92,6 +105,15 @@ static __xdata BYTE i2c_rd[8];
/* TUNE_MONITOR result buffer: filled by OUT phase, returned by IN phase */
static __xdata BYTE tm_result[10];
/* DiSEqC message buffer (3-6 bytes) for full message transmission */
static __xdata BYTE diseqc_msg[6];
/* last error code for diagnostic reads via 0xBC */
static __xdata BYTE last_error;
/* Shared scratch buffer for vendor command case blocks (saves DSEG) */
static __xdata BYTE vc_diag[8];
/*
* BCM4500 register initialization data extracted from stock v2.06 firmware.
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
@ -123,8 +145,10 @@ static const __code BYTE bcm_init_block2[] = {
static BOOL i2c_wait_done(void) {
WORD timeout = I2C_TIMEOUT;
while (!(I2CS & bmDONE)) {
if (--timeout == 0)
if (--timeout == 0) {
last_error = ERR_I2C_TIMEOUT;
return FALSE;
}
}
return TRUE;
}
@ -154,23 +178,29 @@ static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
I2DAT = addr << 1;
if (!i2c_wait_done())
goto fail;
if (!(I2CS & bmACK))
if (!(I2CS & bmACK)) {
last_error = ERR_I2C_NAK;
goto fail;
}
/* Write register address */
I2DAT = reg;
if (!i2c_wait_done())
goto fail;
if (!(I2CS & bmACK))
if (!(I2CS & bmACK)) {
last_error = ERR_I2C_NAK;
goto fail;
}
/* REPEATED START + read address */
I2CS |= bmSTART;
I2DAT = (addr << 1) | 1;
if (!i2c_wait_done())
goto fail;
if (!(I2CS & bmACK))
if (!(I2CS & bmACK)) {
last_error = ERR_I2C_NAK;
goto fail;
}
/* For single byte, set LASTRD before dummy read */
if (len == 1)
@ -208,12 +238,12 @@ static BOOL i2c_write_timeout(BYTE addr, BYTE reg, BYTE val) {
I2CS |= bmSTART;
I2DAT = addr << 1;
if (!i2c_wait_done()) goto fail;
if (!(I2CS & bmACK)) goto fail;
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
/* Register address */
I2DAT = reg;
if (!i2c_wait_done()) goto fail;
if (!(I2CS & bmACK)) goto fail;
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
/* Data byte */
I2DAT = val;
@ -241,11 +271,11 @@ static BOOL i2c_write_multi_timeout(BYTE addr, BYTE reg, BYTE len,
I2CS |= bmSTART;
I2DAT = addr << 1;
if (!i2c_wait_done()) goto fail;
if (!(I2CS & bmACK)) goto fail;
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
I2DAT = reg;
if (!i2c_wait_done()) goto fail;
if (!(I2CS & bmACK)) goto fail;
if (!(I2CS & bmACK)) { last_error = ERR_I2C_NAK; goto fail; }
for (i = 0; i < len; i++) {
I2DAT = data[i];
@ -333,9 +363,12 @@ static BOOL bcm_poll_ready(void) {
if (bcm_direct_read(BCM_REG_CMD, &val)) {
if (!(val & 0x01))
return TRUE;
} else {
return FALSE; /* I2C error, last_error already set */
}
delay(2);
}
last_error = ERR_BCM_TIMEOUT;
return FALSE;
}
@ -581,6 +614,330 @@ static void diseqc_tone_burst(BYTE sat_b) {
TR2 = 0;
}
/* ---------- DiSEqC Manchester encoder ---------- */
/*
* DiSEqC uses Manchester encoding over a 22 kHz carrier.
* The external oscillator generates 22 kHz continuously when P0.3 is HIGH;
* gating P0.3 LOW silences the carrier. Timer2 provides ~500.25 us ticks.
*
* Timing (EN 50494 / DiSEqC bus spec):
* Bit '1': 1 tick tone + 2 ticks silence = ~1.5 ms
* Bit '0': 2 ticks tone + 1 tick silence = ~1.5 ms
* Preamble: 30 ticks continuous tone (~15 ms)
* Start gap: 3 ticks silence (~1.5 ms)
* Inter-byte gap: 12 ticks silence (~6 ms)
* Post-message: 12 ticks silence (~6 ms)
*/
static void diseqc_wait_ticks(BYTE count) {
static __xdata BYTE dt_i;
for (dt_i = 0; dt_i < count; dt_i++) {
while (!TF2)
;
TF2 = 0;
}
}
static BYTE diseqc_parity(BYTE val) {
/* Compute odd parity: returns 1 if even number of set bits */
BYTE p = val;
p ^= (p >> 4);
p ^= (p >> 2);
p ^= (p >> 1);
return (~p) & 0x01;
}
static void diseqc_send_bit(BYTE bit) {
if (bit) {
/* '1': 1 tick tone ON, 2 ticks silence */
IOA |= PIN_22KHZ;
diseqc_wait_ticks(1);
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(2);
} else {
/* '0': 2 ticks tone ON, 1 tick silence */
IOA |= PIN_22KHZ;
diseqc_wait_ticks(2);
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(1);
}
}
static void diseqc_send_byte(BYTE val) {
static __xdata BYTE db_i, db_parity;
/* 8 data bits, MSB first */
for (db_i = 0; db_i < 8; db_i++) {
diseqc_send_bit((val >> (7 - db_i)) & 0x01);
}
/* Odd parity bit */
db_parity = diseqc_parity(val);
diseqc_send_bit(db_parity);
}
static void diseqc_send_message(BYTE len) {
static __xdata BYTE dm_i, dm_saved_tone;
if (len < 3 || len > 6)
return;
/* Save current 22 kHz tone state */
dm_saved_tone = IOA & PIN_22KHZ;
/* Configure Timer2 for ~500 us ticks (same as tone burst) */
CKCON &= ~0x20; /* T2M=0: Timer2 clk = 48MHz/12 = 4MHz */
T2CON = 0x04; /* auto-reload, running */
RCAP2H = 0xF8;
RCAP2L = 0x2F; /* reload = 63535 -> ~500 us tick */
TL2 = 0xFF;
TH2 = 0xFF; /* force immediate overflow */
TF2 = 0;
/* Pre-message gap: 6 ticks silence (~3 ms) */
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(6);
/* Preamble: 30 ticks continuous tone (~15 ms) */
IOA |= PIN_22KHZ;
diseqc_wait_ticks(30);
/* Start gap: 3 ticks silence (~1.5 ms) */
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(3);
/* Transmit bytes */
for (dm_i = 0; dm_i < len; dm_i++) {
diseqc_send_byte(diseqc_msg[dm_i]);
/* Inter-byte gap after each byte except the last */
if (dm_i < len - 1) {
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(12);
}
}
/* Post-message gap: 12 ticks silence (~6 ms) */
IOA &= ~PIN_22KHZ;
diseqc_wait_ticks(12);
/* Stop Timer2 */
TR2 = 0;
/* Restore 22 kHz tone state */
if (dm_saved_tone)
IOA |= PIN_22KHZ;
else
IOA &= ~PIN_22KHZ;
}
/* ---------- Parameterized sweep (0xBA) ---------- */
/*
* Like SPECTRUM_SWEEP (0xB0) but host controls SR, modulation, and FEC.
* 16-byte EP0 payload:
* [0..3] start_freq_khz (u32 LE)
* [4..7] stop_freq_khz (u32 LE)
* [8..9] step_khz (u16 LE)
* [10..13] symbol_rate_sps (u32 LE)
* [14] mod_index
* [15] fec_index
*
* At each step: tune (program SR/mod/FEC via do_tune), dwell for AGC
* settling, read SNR registers, output u16 LE power to EP2.
*/
static void do_param_sweep(void) {
static __xdata DWORD ps_start, ps_stop, ps_cur, ps_sr;
static __xdata WORD ps_step, ps_buf_idx;
static __xdata BYTE ps_snr_lo, ps_snr_hi;
static __xdata BYTE ps_mod, ps_fec;
ps_start = (DWORD)EP0BUF[0] |
((DWORD)EP0BUF[1] << 8) |
((DWORD)EP0BUF[2] << 16) |
((DWORD)EP0BUF[3] << 24);
ps_stop = (DWORD)EP0BUF[4] |
((DWORD)EP0BUF[5] << 8) |
((DWORD)EP0BUF[6] << 16) |
((DWORD)EP0BUF[7] << 24);
ps_step = (WORD)EP0BUF[8] | ((WORD)EP0BUF[9] << 8);
ps_sr = (DWORD)EP0BUF[10] |
((DWORD)EP0BUF[11] << 8) |
((DWORD)EP0BUF[12] << 16) |
((DWORD)EP0BUF[13] << 24);
ps_mod = EP0BUF[14];
ps_fec = EP0BUF[15];
if (ps_step == 0)
ps_step = 1000;
ps_buf_idx = 0;
ps_cur = ps_start;
while (ps_cur <= ps_stop) {
/*
* Set up a tune payload in EP0BUF for do_tune():
* [0..3] = symbol_rate (LE), [4..7] = freq (LE), [8] = mod, [9] = fec
*/
EP0BUF[0] = (BYTE)(ps_sr);
EP0BUF[1] = (BYTE)(ps_sr >> 8);
EP0BUF[2] = (BYTE)(ps_sr >> 16);
EP0BUF[3] = (BYTE)(ps_sr >> 24);
EP0BUF[4] = (BYTE)(ps_cur);
EP0BUF[5] = (BYTE)(ps_cur >> 8);
EP0BUF[6] = (BYTE)(ps_cur >> 16);
EP0BUF[7] = (BYTE)(ps_cur >> 24);
EP0BUF[8] = ps_mod;
EP0BUF[9] = ps_fec;
do_tune();
/* Dwell for AGC settling */
delay(10);
/* Read signal strength via indirect register */
ps_snr_lo = 0;
ps_snr_hi = 0;
bcm_indirect_read(0x00, &ps_snr_lo);
bcm_indirect_read(0x01, &ps_snr_hi);
/* Store u16 LE into EP2 FIFO buffer */
if (ps_buf_idx < 1024 - 1) {
EP2FIFOBUF[ps_buf_idx++] = ps_snr_lo;
EP2FIFOBUF[ps_buf_idx++] = ps_snr_hi;
}
/* Commit chunk when buffer is half full */
if (ps_buf_idx >= 512) {
EP2BCH = MSB(ps_buf_idx);
SYNCDELAY;
EP2BCL = LSB(ps_buf_idx);
SYNCDELAY;
ps_buf_idx = 0;
while (EP2CS & bmEPFULL)
;
}
ps_cur += ps_step;
}
/* Commit remaining data */
if (ps_buf_idx > 0) {
EP2BCH = MSB(ps_buf_idx);
SYNCDELAY;
EP2BCL = LSB(ps_buf_idx);
SYNCDELAY;
}
}
/* ---------- Adaptive blind scan (0xBB) ---------- */
/*
* Enhanced blind scan with quick AGC pre-check.
* EP0 payload (18 bytes):
* [0..3] freq_khz (u32 LE)
* [4..7] sr_min (u32 LE, sps)
* [8..11] sr_max (u32 LE, sps)
* [12..15] sr_step (u32 LE, sps)
* [16..17] quick_dwell_ms (u16 LE, 0=disabled)
*
* When quick_dwell_ms > 0: at each SR step, first do a quick AGC read.
* If AGC indicates no energy (below threshold), skip the full 100ms dwell.
* Cuts survey time ~80% on empty frequencies.
*/
static BOOL do_adaptive_blind_scan(void) {
static __xdata DWORD abs_freq, abs_sr_min, abs_sr_max, abs_sr_step, abs_sr_cur;
static __xdata WORD abs_quick_dwell, abs_agc_val;
static __xdata BYTE abs_lock_val, abs_agc_lo, abs_agc_hi;
abs_freq = (DWORD)EP0BUF[0] |
((DWORD)EP0BUF[1] << 8) |
((DWORD)EP0BUF[2] << 16) |
((DWORD)EP0BUF[3] << 24);
abs_sr_min = (DWORD)EP0BUF[4] |
((DWORD)EP0BUF[5] << 8) |
((DWORD)EP0BUF[6] << 16) |
((DWORD)EP0BUF[7] << 24);
abs_sr_max = (DWORD)EP0BUF[8] |
((DWORD)EP0BUF[9] << 8) |
((DWORD)EP0BUF[10] << 16) |
((DWORD)EP0BUF[11] << 24);
abs_sr_step = (DWORD)EP0BUF[12] |
((DWORD)EP0BUF[13] << 8) |
((DWORD)EP0BUF[14] << 16) |
((DWORD)EP0BUF[15] << 24);
abs_quick_dwell = (WORD)EP0BUF[16] | ((WORD)EP0BUF[17] << 8);
if (abs_sr_step == 0)
abs_sr_step = 1000000;
abs_sr_cur = abs_sr_min;
while (abs_sr_cur <= abs_sr_max) {
/* Program SR and frequency into BCM4500 */
i2c_buf[0] = (BYTE)(abs_sr_cur >> 24);
i2c_buf[1] = (BYTE)(abs_sr_cur >> 16);
i2c_buf[2] = (BYTE)(abs_sr_cur >> 8);
i2c_buf[3] = (BYTE)(abs_sr_cur);
bcm_indirect_write_block(0x00, i2c_buf, 4);
i2c_buf[0] = (BYTE)(abs_freq >> 24);
i2c_buf[1] = (BYTE)(abs_freq >> 16);
i2c_buf[2] = (BYTE)(abs_freq >> 8);
i2c_buf[3] = (BYTE)(abs_freq);
bcm_indirect_write_block(0x00, i2c_buf, 4);
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
/* Quick AGC pre-check if enabled */
if (abs_quick_dwell > 0) {
delay((BYTE)(abs_quick_dwell > 255 ? 255 : abs_quick_dwell));
/* Read AGC registers for energy detection */
abs_agc_lo = 0;
abs_agc_hi = 0;
bcm_indirect_read(0x02, &abs_agc_lo);
bcm_indirect_read(0x03, &abs_agc_hi);
abs_agc_val = ((WORD)abs_agc_hi << 8) | abs_agc_lo;
/* High AGC = weak signal. Threshold: ~60000 means no energy.
* Skip full dwell if no energy detected. */
if (abs_agc_val > 60000) {
abs_sr_cur += abs_sr_step;
continue;
}
}
/* Full acquisition dwell */
delay(100);
/* Check lock */
abs_lock_val = 0;
bcm_direct_read(BCM_REG_LOCK, &abs_lock_val);
if (abs_lock_val & 0x20) {
EP0BUF[0] = (BYTE)(abs_freq);
EP0BUF[1] = (BYTE)(abs_freq >> 8);
EP0BUF[2] = (BYTE)(abs_freq >> 16);
EP0BUF[3] = (BYTE)(abs_freq >> 24);
EP0BUF[4] = (BYTE)(abs_sr_cur);
EP0BUF[5] = (BYTE)(abs_sr_cur >> 8);
EP0BUF[6] = (BYTE)(abs_sr_cur >> 16);
EP0BUF[7] = (BYTE)(abs_sr_cur >> 24);
EP0BCH = 0;
EP0BCL = 8;
return TRUE;
}
abs_sr_cur += abs_sr_step;
}
/* No lock found */
EP0BUF[0] = 0x00;
EP0BCH = 0;
EP0BCL = 1;
return FALSE;
}
/* ---------- Spectrum sweep (0xB0) ---------- */
/*
@ -600,9 +957,8 @@ static void diseqc_tone_burst(BYTE sat_b) {
*/
static void do_spectrum_sweep(void) {
static __xdata DWORD start_freq, stop_freq, cur_freq;
static __xdata WORD step_khz;
WORD buf_idx;
BYTE snr_lo, snr_hi;
static __xdata WORD step_khz, ss_buf_idx;
static __xdata BYTE ss_snr_lo, ss_snr_hi;
/* Parse the 10-byte EP0 payload */
start_freq = (DWORD)EP0BUF[0] |
@ -618,7 +974,7 @@ static void do_spectrum_sweep(void) {
if (step_khz == 0)
step_khz = 1000;
buf_idx = 0;
ss_buf_idx = 0;
cur_freq = start_freq;
while (cur_freq <= stop_freq) {
@ -637,24 +993,24 @@ static void do_spectrum_sweep(void) {
delay(10);
/* Read signal strength via indirect register */
snr_lo = 0;
snr_hi = 0;
bcm_indirect_read(0x00, &snr_lo);
bcm_indirect_read(0x01, &snr_hi);
ss_snr_lo = 0;
ss_snr_hi = 0;
bcm_indirect_read(0x00, &ss_snr_lo);
bcm_indirect_read(0x01, &ss_snr_hi);
/* Store u16 LE into EP2 FIFO buffer */
if (buf_idx < 1024 - 1) {
EP2FIFOBUF[buf_idx++] = snr_lo;
EP2FIFOBUF[buf_idx++] = snr_hi;
if (ss_buf_idx < 1024 - 1) {
EP2FIFOBUF[ss_buf_idx++] = ss_snr_lo;
EP2FIFOBUF[ss_buf_idx++] = ss_snr_hi;
}
/* If buffer is nearly full, commit this chunk */
if (buf_idx >= 512) {
EP2BCH = MSB(buf_idx);
if (ss_buf_idx >= 512) {
EP2BCH = MSB(ss_buf_idx);
SYNCDELAY;
EP2BCL = LSB(buf_idx);
EP2BCL = LSB(ss_buf_idx);
SYNCDELAY;
buf_idx = 0;
ss_buf_idx = 0;
/* Wait for the buffer to be taken by host */
while (EP2CS & bmEPFULL)
@ -665,10 +1021,10 @@ static void do_spectrum_sweep(void) {
}
/* Commit any remaining data */
if (buf_idx > 0) {
EP2BCH = MSB(buf_idx);
if (ss_buf_idx > 0) {
EP2BCH = MSB(ss_buf_idx);
SYNCDELAY;
EP2BCL = LSB(buf_idx);
EP2BCL = LSB(ss_buf_idx);
SYNCDELAY;
}
}
@ -690,7 +1046,7 @@ static void do_spectrum_sweep(void) {
*/
static BOOL do_blind_scan(void) {
static __xdata DWORD freq_khz, sr_min, sr_max, sr_step, sr_cur;
BYTE lock_val;
static __xdata BYTE bs_lock_val;
freq_khz = (DWORD)EP0BUF[0] |
((DWORD)EP0BUF[1] << 8) |
@ -737,9 +1093,9 @@ static BOOL do_blind_scan(void) {
delay(100);
/* Check lock */
lock_val = 0;
bcm_direct_read(BCM_REG_LOCK, &lock_val);
if (lock_val & 0x20) {
bs_lock_val = 0;
bcm_direct_read(BCM_REG_LOCK, &bs_lock_val);
if (bs_lock_val & 0x20) {
/* Locked -- report back via EP0 */
EP0BUF[0] = (BYTE)(freq_khz);
EP0BUF[1] = (BYTE)(freq_khz >> 8);
@ -775,8 +1131,8 @@ static BOOL do_blind_scan(void) {
* EP0BUF[9] = FEC index
*/
static void do_tune(void) {
BYTE i;
__xdata BYTE tune_data[12];
static __xdata BYTE tune_i;
static __xdata BYTE tune_data[13]; /* 12 data + 1 scratch for reg addr */
if (!(config_status & BM_STARTED))
return;
@ -785,9 +1141,9 @@ static void do_tune(void) {
* Byte-reverse symbol rate (LE->BE) into tune_data[0..3]
* and frequency (LE->BE) into tune_data[4..7]
*/
for (i = 0; i < 4; i++) {
tune_data[i] = EP0BUF[3 - i]; /* SR BE */
tune_data[4 + i] = EP0BUF[7 - i]; /* Freq BE */
for (tune_i = 0; tune_i < 4; tune_i++) {
tune_data[tune_i] = EP0BUF[3 - tune_i]; /* SR BE */
tune_data[4 + tune_i] = EP0BUF[7 - tune_i]; /* Freq BE */
}
/* Modulation type and FEC rate */
@ -817,10 +1173,8 @@ static void do_tune(void) {
bcm_direct_write(BCM_REG_PAGE, 0x00);
/* Write all configuration data to BCM4500 data register */
{
BYTE reg = BCM_REG_DATA;
i2c_write(BCM4500_ADDR, 1, &reg, 12, tune_data);
}
tune_data[12] = BCM_REG_DATA; /* borrow byte past data (safe: 13 bytes in xdata) */
i2c_write(BCM4500_ADDR, 1, &tune_data[12], 12, tune_data);
/* Execute indirect write */
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
@ -1044,8 +1398,25 @@ BOOL handle_vendorcommand(BYTE cmd) {
if (wlen == 0) {
/* Tone burst: A if wval==0, B if wval!=0 */
diseqc_tone_burst((BYTE)wval);
} else if (wlen >= 3 && wlen <= 6) {
/* Full DiSEqC message: reject if streaming */
if (config_status & BM_ARMED) {
last_error = ERR_BCM_NOT_READY;
return TRUE;
}
/* EP0 data phase: receive message bytes */
EP0BCL = 0;
SYNCDELAY;
while (EP0CS & bmEPBUSY)
;
/* Copy message from EP0BUF to diseqc_msg buffer */
{
BYTE di;
for (di = 0; di < (BYTE)wlen; di++)
diseqc_msg[di] = EP0BUF[di];
}
diseqc_send_message((BYTE)wlen);
}
/* Full DiSEqC message: future implementation */
return TRUE;
}
@ -1062,10 +1433,10 @@ BOOL handle_vendorcommand(BYTE cmd) {
/* 0x92: GET_FW_VERS -- return firmware version and build date */
case GET_FW_VERS:
EP0BUF[0] = 0x00; /* patch -> version 3.02.0 */
EP0BUF[1] = 0x02; /* minor */
EP0BUF[0] = 0x00; /* patch -> version 3.03.0 */
EP0BUF[1] = 0x03; /* minor */
EP0BUF[2] = 0x03; /* major */
EP0BUF[3] = 0x0C; /* day = 12 */
EP0BUF[3] = 0x0F; /* day = 15 */
EP0BUF[4] = 0x02; /* month = 2 */
EP0BUF[5] = 0x1A; /* year - 2000 = 26 */
EP0BCH = 0;
@ -1171,53 +1542,51 @@ BOOL handle_vendorcommand(BYTE cmd) {
* Returns 8 bytes: [write_A6_ok, readback_A6, write_A8_ok, readback_A8,
* readback_A7, direct_read_A6, direct_read_A7, direct_read_A8] */
case 0xB6: {
BYTE target_reg = (BYTE)wval;
BYTE diag[8];
BYTE rb;
/* Use shared xdata diag buffer to save DSEG */
vc_diag[0] = (BYTE)wval; /* target_reg */
/* Step 1: Write target register to page select (0xA6) */
diag[0] = bcm_direct_write(BCM_REG_PAGE, target_reg) ? 0x01 : 0x00;
vc_diag[1] = bcm_direct_write(BCM_REG_PAGE, vc_diag[0]) ? 0x01 : 0x00;
/* Step 2: Read back 0xA6 to verify write */
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &rb);
diag[1] = rb;
vc_diag[2] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[2]);
/* Step 3: Write read command (0x01) to 0xA8 */
diag[2] = bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ) ? 0x01 : 0x00;
vc_diag[3] = bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ) ? 0x01 : 0x00;
/* Step 4: Read back 0xA8 to check command status */
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &rb);
diag[3] = rb;
vc_diag[4] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[4]);
/* Step 5: Small delay for command execution */
delay(2);
/* Step 6: Read 0xA7 (data register) — this is the result */
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &rb);
diag[4] = rb;
vc_diag[5] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[5]);
/* Step 7: Read back all three control regs for final state */
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &rb);
diag[5] = rb;
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &rb);
diag[6] = rb;
rb = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &rb);
diag[7] = rb;
vc_diag[6] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_PAGE, 1, &vc_diag[6]);
vc_diag[7] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[7]);
EP0BUF[0] = vc_diag[1]; /* write_A6_ok */
EP0BUF[1] = vc_diag[2]; /* readback_A6 */
EP0BUF[2] = vc_diag[3]; /* write_A8_ok */
EP0BUF[3] = vc_diag[4]; /* readback_A8 */
EP0BUF[4] = vc_diag[5]; /* readback_A7 */
EP0BUF[5] = vc_diag[6]; /* direct_read_A6 */
/* Read remaining registers directly into EP0BUF */
vc_diag[6] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_DATA, 1, &vc_diag[6]);
EP0BUF[6] = vc_diag[6]; /* direct_read_A7 */
vc_diag[7] = 0xEE;
i2c_combined_read(BCM4500_ADDR, BCM_REG_CMD, 1, &vc_diag[7]);
EP0BUF[7] = vc_diag[7]; /* direct_read_A8 */
EP0BUF[0] = diag[0];
EP0BUF[1] = diag[1];
EP0BUF[2] = diag[2];
EP0BUF[3] = diag[3];
EP0BUF[4] = diag[4];
EP0BUF[5] = diag[5];
EP0BUF[6] = diag[6];
EP0BUF[7] = diag[7];
EP0BCH = 0;
EP0BCL = 8;
return TRUE;
@ -1307,6 +1676,31 @@ BOOL handle_vendorcommand(BYTE cmd) {
return TRUE;
}
/* 0xBA: PARAM_SWEEP -- parameterized spectrum sweep */
case PARAM_SWEEP:
EP0BCL = 0;
SYNCDELAY;
while (EP0CS & bmEPBUSY)
;
do_param_sweep();
return TRUE;
/* 0xBB: ADAPTIVE_BLIND_SCAN -- blind scan with AGC pre-check */
case ADAPTIVE_BLIND_SCAN:
EP0BCL = 0;
SYNCDELAY;
while (EP0CS & bmEPBUSY)
;
do_adaptive_blind_scan();
return TRUE;
/* 0xBC: GET_LAST_ERROR -- return diagnostic error code */
case GET_LAST_ERROR:
EP0BUF[0] = last_error;
EP0BCH = 0;
EP0BCL = 1;
return TRUE;
default:
return FALSE;
}
@ -1365,6 +1759,7 @@ void hispeed_isr(void) __interrupt (HISPEED_ISR) {
void main(void) {
config_status = 0;
last_error = ERR_OK;
got_sud = FALSE;
REVCTL = 0x03; /* NOAUTOARM + SKIPCOMMIT */