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:
parent
0f4ba4766f
commit
cc3a0707a1
20 changed files with 5645 additions and 84 deletions
|
|
@ -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, ®, 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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue