Add custom FX2 firmware and RAM loader for open-source development
Custom firmware (SDCC + fx2lib) implements all stock vendor commands (0x80-0x94) plus new commands for spectrum sweep (0xB0), raw BCM4500 register access (0xB1/0xB2), and blind scan (0xB3). Compiles to 6.3KB of code with healthy RAM margins. RAM loader (fw_load.py) uses the FX2 0xA0 vendor request to load firmware into RAM without touching EEPROM -- power cycle restores factory firmware. Supports Intel HEX and raw binary formats.
This commit is contained in:
parent
c7b5932cc0
commit
5710584267
5 changed files with 1729 additions and 0 deletions
864
firmware/skywalker1.c
Normal file
864
firmware/skywalker1.c
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
/*
|
||||
* Genpix SkyWalker-1 Custom Firmware
|
||||
* For Cypress CY7C68013A (FX2LP) + Broadcom BCM4500 demodulator
|
||||
*
|
||||
* Stock-compatible vendor commands (0x80-0x94) plus new
|
||||
* spectrum sweep, raw demod access, and blind scan commands (0xB0-0xB3).
|
||||
*
|
||||
* SDCC + fx2lib toolchain. Loaded into FX2 RAM for testing.
|
||||
*/
|
||||
|
||||
#include <fx2regs.h>
|
||||
#include <fx2macros.h>
|
||||
#include <delay.h>
|
||||
#include <autovector.h>
|
||||
#include <setupdat.h>
|
||||
#include <eputils.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#define SYNCDELAY SYNCDELAY4
|
||||
|
||||
/* BCM4500 I2C address (7-bit) */
|
||||
#define BCM4500_ADDR 0x10
|
||||
|
||||
/* BCM4500 indirect register protocol registers */
|
||||
#define BCM_REG_PAGE 0xA6
|
||||
#define BCM_REG_DATA 0xA7
|
||||
#define BCM_REG_CMD 0xA8
|
||||
|
||||
/* BCM4500 status registers */
|
||||
#define BCM_REG_STATUS 0xA2
|
||||
#define BCM_REG_LOCK 0xA4
|
||||
|
||||
/* BCM commands */
|
||||
#define BCM_CMD_READ 0x01
|
||||
#define BCM_CMD_WRITE 0x03
|
||||
|
||||
/* vendor command IDs */
|
||||
#define GET_8PSK_CONFIG 0x80
|
||||
#define TUNE_8PSK 0x86
|
||||
#define GET_SIGNAL_STRENGTH 0x87
|
||||
#define BOOT_8PSK 0x89
|
||||
#define START_INTERSIL 0x8A
|
||||
#define SET_LNB_VOLTAGE 0x8B
|
||||
#define SET_22KHZ_TONE 0x8C
|
||||
#define SEND_DISEQC 0x8D
|
||||
#define ARM_TRANSFER 0x85
|
||||
#define GET_SIGNAL_LOCK 0x90
|
||||
#define GET_FW_VERS 0x92
|
||||
#define USE_EXTRA_VOLT 0x94
|
||||
|
||||
/* custom vendor commands */
|
||||
#define SPECTRUM_SWEEP 0xB0
|
||||
#define RAW_DEMOD_READ 0xB1
|
||||
#define RAW_DEMOD_WRITE 0xB2
|
||||
#define BLIND_SCAN 0xB3
|
||||
|
||||
/* configuration status byte bits */
|
||||
#define BM_STARTED 0x01
|
||||
#define BM_FW_LOADED 0x02
|
||||
#define BM_INTERSIL 0x04
|
||||
#define BM_DVB_MODE 0x08
|
||||
#define BM_22KHZ 0x10
|
||||
#define BM_SEL18V 0x20
|
||||
#define BM_DC_TUNED 0x40
|
||||
#define BM_ARMED 0x80
|
||||
|
||||
/* GPIO pin definitions for v2.06 hardware */
|
||||
#define PIN_22KHZ 0x08 /* P0.3 */
|
||||
#define PIN_LNB_VOLT 0x10 /* P0.4 */
|
||||
#define PIN_DISEQC 0x80 /* P0.7 */
|
||||
|
||||
/* configuration status byte -- stored in ordinary variable */
|
||||
static volatile BYTE config_status;
|
||||
|
||||
/* ISR flag */
|
||||
volatile __bit got_sud;
|
||||
|
||||
/* I2C scratch buffers in xdata */
|
||||
static __xdata BYTE i2c_buf[8];
|
||||
static __xdata BYTE i2c_rd[8];
|
||||
|
||||
/* ---------- BCM4500 I2C helpers ---------- */
|
||||
|
||||
/*
|
||||
* Write one byte to a BCM4500 direct I2C register (subaddr).
|
||||
* This writes to the I2C register directly, not through the
|
||||
* indirect protocol.
|
||||
*/
|
||||
static BOOL bcm_direct_write(BYTE reg, BYTE val) {
|
||||
i2c_buf[0] = val;
|
||||
return i2c_write(BCM4500_ADDR, 1, ®, 1, i2c_buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read one byte from a BCM4500 direct I2C register.
|
||||
*/
|
||||
static BOOL bcm_direct_read(BYTE reg, BYTE *val) {
|
||||
BYTE r = reg;
|
||||
if (!i2c_write(BCM4500_ADDR, 1, &r, 0, NULL))
|
||||
return FALSE;
|
||||
if (!i2c_read(BCM4500_ADDR, 1, val))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a value to a BCM4500 indirect register.
|
||||
* Protocol:
|
||||
* 1. Write page/register to 0xA6
|
||||
* 2. Write data to 0xA7
|
||||
* 3. Write 0x03 to 0xA8 (execute write)
|
||||
*/
|
||||
static BOOL bcm_indirect_write(BYTE reg, BYTE val) {
|
||||
if (!bcm_direct_write(BCM_REG_PAGE, reg))
|
||||
return FALSE;
|
||||
if (!bcm_direct_write(BCM_REG_DATA, val))
|
||||
return FALSE;
|
||||
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a value from a BCM4500 indirect register.
|
||||
* Protocol:
|
||||
* 1. Write page/register to 0xA6
|
||||
* 2. Write 0x01 to 0xA8 (execute read)
|
||||
* 3. Read data from 0xA7
|
||||
*/
|
||||
static BOOL bcm_indirect_read(BYTE reg, BYTE *val) {
|
||||
if (!bcm_direct_write(BCM_REG_PAGE, reg))
|
||||
return FALSE;
|
||||
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_READ))
|
||||
return FALSE;
|
||||
if (!bcm_direct_read(BCM_REG_DATA, val))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a multi-byte block to BCM4500 via indirect protocol.
|
||||
* Page select, then N data bytes to 0xA7, then commit with 0x03.
|
||||
*/
|
||||
static BOOL bcm_indirect_write_block(BYTE page, __xdata BYTE *data, BYTE len) {
|
||||
BYTE reg;
|
||||
|
||||
reg = BCM_REG_PAGE;
|
||||
i2c_buf[0] = page;
|
||||
if (!i2c_write(BCM4500_ADDR, 1, ®, 1, i2c_buf))
|
||||
return FALSE;
|
||||
|
||||
reg = BCM_REG_DATA;
|
||||
if (!i2c_write(BCM4500_ADDR, 1, ®, len, data))
|
||||
return FALSE;
|
||||
|
||||
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll BCM4500 for readiness. Reads status registers and waits
|
||||
* for the command register to indicate idle.
|
||||
*/
|
||||
static BOOL bcm_poll_ready(void) {
|
||||
BYTE i, val;
|
||||
for (i = 0; i < 20; i++) {
|
||||
if (bcm_direct_read(BCM_REG_CMD, &val)) {
|
||||
if (!(val & 0x01))
|
||||
return TRUE;
|
||||
}
|
||||
delay(5);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------- GPIF streaming ---------- */
|
||||
|
||||
static void gpif_start(void) {
|
||||
if (config_status & BM_ARMED)
|
||||
return;
|
||||
|
||||
config_status |= BM_ARMED;
|
||||
|
||||
/* IFCONFIG: internal 48MHz, GPIF master, async, clock output */
|
||||
IFCONFIG = 0xEE;
|
||||
SYNCDELAY;
|
||||
|
||||
/* EP2FIFOCFG: AUTOIN, ZEROLENIN, 8-bit */
|
||||
EP2FIFOCFG = 0x0C;
|
||||
SYNCDELAY;
|
||||
|
||||
/* FLOWSTATE: enable flow state + FS[3] */
|
||||
FLOWSTATE |= 0x09;
|
||||
|
||||
/* Set transaction count large (effectively infinite) */
|
||||
GPIFTCB3 = 0x80;
|
||||
SYNCDELAY;
|
||||
GPIFTCB2 = 0x00;
|
||||
SYNCDELAY;
|
||||
GPIFTCB1 = 0x00;
|
||||
SYNCDELAY;
|
||||
GPIFTCB0 = 0x00;
|
||||
SYNCDELAY;
|
||||
|
||||
/* Assert P3.5 low (BCM4500 TS enable) briefly */
|
||||
IOD &= ~0x20;
|
||||
|
||||
/* Wait for GPIF idle */
|
||||
while (!(GPIFTRIG & 0x80))
|
||||
;
|
||||
|
||||
IOD |= 0x20;
|
||||
|
||||
/* Trigger continuous GPIF read into EP2 */
|
||||
GPIFTRIG = 0x04;
|
||||
|
||||
/* P0.7 low = streaming indicator */
|
||||
IOA &= ~0x80;
|
||||
}
|
||||
|
||||
static void gpif_stop(void) {
|
||||
if (!(config_status & BM_ARMED))
|
||||
return;
|
||||
|
||||
/* P0.7 high = streaming stopped */
|
||||
IOA |= 0x80;
|
||||
|
||||
/* Force-flush current FIFO buffer */
|
||||
EP2FIFOBCH = 0xFF;
|
||||
SYNCDELAY;
|
||||
|
||||
/* Wait for GPIF idle */
|
||||
while (!(GPIFTRIG & 0x80))
|
||||
;
|
||||
|
||||
/* Skip/discard partial EP2 packet */
|
||||
OUTPKTEND = 0x82;
|
||||
SYNCDELAY;
|
||||
|
||||
config_status &= ~BM_ARMED;
|
||||
|
||||
/* De-assert all BCM4500 control lines on P3 */
|
||||
IOD |= 0xE0;
|
||||
}
|
||||
|
||||
/* ---------- DiSEqC tone burst ---------- */
|
||||
|
||||
/*
|
||||
* Send a tone burst (mini DiSEqC). This is the simpler variant.
|
||||
* Tone burst A: unmodulated 22kHz for 12.5ms
|
||||
* Tone burst B: modulated (not implemented yet)
|
||||
*
|
||||
* Uses Timer2 for timing as the stock firmware does.
|
||||
*/
|
||||
static void diseqc_tone_burst(BYTE sat_b) {
|
||||
BYTE i;
|
||||
|
||||
(void)sat_b; /* both A and B send 22kHz burst for now */
|
||||
|
||||
/* Configure Timer2 auto-reload */
|
||||
/* CKCON.T2M = 0 -> Timer2 clk = 48MHz/12 = 4MHz */
|
||||
CKCON &= ~0x20;
|
||||
T2CON = 0x04; /* auto-reload, running */
|
||||
RCAP2H = 0xF8;
|
||||
RCAP2L = 0x2F; /* reload = 63535 -> ~500us tick */
|
||||
TL2 = 0xFF;
|
||||
TH2 = 0xFF; /* force immediate overflow */
|
||||
|
||||
/* Pre-burst settling: 15 ticks (~7.5ms) with carrier off */
|
||||
IOA &= ~PIN_22KHZ;
|
||||
TF2 = 0;
|
||||
for (i = 0; i < 15; i++) {
|
||||
while (!TF2)
|
||||
;
|
||||
TF2 = 0;
|
||||
}
|
||||
|
||||
/* Burst: 25 ticks (~12.5ms) with carrier on */
|
||||
IOA |= PIN_22KHZ;
|
||||
for (i = 0; i < 25; i++) {
|
||||
while (!TF2)
|
||||
;
|
||||
TF2 = 0;
|
||||
}
|
||||
|
||||
/* Carrier off */
|
||||
IOA &= ~PIN_22KHZ;
|
||||
|
||||
/* Post-burst settling: 5 ticks (~2.5ms) */
|
||||
for (i = 0; i < 5; i++) {
|
||||
while (!TF2)
|
||||
;
|
||||
TF2 = 0;
|
||||
}
|
||||
|
||||
/* Stop Timer2 */
|
||||
TR2 = 0;
|
||||
}
|
||||
|
||||
/* ---------- Spectrum sweep (0xB0) ---------- */
|
||||
|
||||
/*
|
||||
* Step through frequencies from start to stop, reading signal strength
|
||||
* at each step. Results are packed as u16 LE power values into EP2 bulk.
|
||||
*
|
||||
* The host sends a 10-byte payload via EP0:
|
||||
* [0..3] start_freq (u32 LE, kHz)
|
||||
* [4..7] stop_freq (u32 LE, kHz)
|
||||
* [8..9] step_khz (u16 LE)
|
||||
*
|
||||
* We tune to each frequency with a fixed symbol rate (e.g. 20000 sps, DVB-S
|
||||
* QPSK auto-FEC), read the SNR register, and pack u16 results into EP2.
|
||||
*
|
||||
* The sweep uses a simple approach: program freq via BCM4500 indirect write
|
||||
* at each step, wait briefly, and read the signal energy register.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* Parse the 10-byte EP0 payload */
|
||||
start_freq = (DWORD)EP0BUF[0] |
|
||||
((DWORD)EP0BUF[1] << 8) |
|
||||
((DWORD)EP0BUF[2] << 16) |
|
||||
((DWORD)EP0BUF[3] << 24);
|
||||
stop_freq = (DWORD)EP0BUF[4] |
|
||||
((DWORD)EP0BUF[5] << 8) |
|
||||
((DWORD)EP0BUF[6] << 16) |
|
||||
((DWORD)EP0BUF[7] << 24);
|
||||
step_khz = (WORD)EP0BUF[8] | ((WORD)EP0BUF[9] << 8);
|
||||
|
||||
if (step_khz == 0)
|
||||
step_khz = 1000;
|
||||
|
||||
buf_idx = 0;
|
||||
cur_freq = start_freq;
|
||||
|
||||
while (cur_freq <= stop_freq) {
|
||||
/*
|
||||
* Program frequency into BCM4500 via indirect write.
|
||||
* The BCM4500 expects big-endian frequency bytes at page 0.
|
||||
* We write 4 freq bytes (BE) to the data register.
|
||||
*/
|
||||
i2c_buf[0] = (BYTE)(cur_freq >> 24);
|
||||
i2c_buf[1] = (BYTE)(cur_freq >> 16);
|
||||
i2c_buf[2] = (BYTE)(cur_freq >> 8);
|
||||
i2c_buf[3] = (BYTE)(cur_freq);
|
||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
||||
|
||||
/* Wait for demod to settle */
|
||||
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);
|
||||
|
||||
/* Store u16 LE into EP2 FIFO buffer */
|
||||
if (buf_idx < 1024 - 1) {
|
||||
EP2FIFOBUF[buf_idx++] = snr_lo;
|
||||
EP2FIFOBUF[buf_idx++] = snr_hi;
|
||||
}
|
||||
|
||||
/* If buffer is nearly full, commit this chunk */
|
||||
if (buf_idx >= 512) {
|
||||
EP2BCH = MSB(buf_idx);
|
||||
SYNCDELAY;
|
||||
EP2BCL = LSB(buf_idx);
|
||||
SYNCDELAY;
|
||||
buf_idx = 0;
|
||||
|
||||
/* Wait for the buffer to be taken by host */
|
||||
while (EP2CS & bmEPFULL)
|
||||
;
|
||||
}
|
||||
|
||||
cur_freq += step_khz;
|
||||
}
|
||||
|
||||
/* Commit any remaining data */
|
||||
if (buf_idx > 0) {
|
||||
EP2BCH = MSB(buf_idx);
|
||||
SYNCDELAY;
|
||||
EP2BCL = LSB(buf_idx);
|
||||
SYNCDELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Blind scan (0xB3) ---------- */
|
||||
|
||||
/*
|
||||
* Try symbol rates from sr_min to sr_max in sr_step increments
|
||||
* at a given frequency, looking for signal lock.
|
||||
*
|
||||
* EP0 payload (16 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)
|
||||
*
|
||||
* Returns via EP0: 8 bytes on lock [freq_khz(4) + sr_locked(4)]
|
||||
* or 1 byte 0x00 if no lock found.
|
||||
*/
|
||||
static BOOL do_blind_scan(void) {
|
||||
static __xdata DWORD freq_khz, sr_min, sr_max, sr_step, sr_cur;
|
||||
BYTE lock_val;
|
||||
|
||||
freq_khz = (DWORD)EP0BUF[0] |
|
||||
((DWORD)EP0BUF[1] << 8) |
|
||||
((DWORD)EP0BUF[2] << 16) |
|
||||
((DWORD)EP0BUF[3] << 24);
|
||||
sr_min = (DWORD)EP0BUF[4] |
|
||||
((DWORD)EP0BUF[5] << 8) |
|
||||
((DWORD)EP0BUF[6] << 16) |
|
||||
((DWORD)EP0BUF[7] << 24);
|
||||
sr_max = (DWORD)EP0BUF[8] |
|
||||
((DWORD)EP0BUF[9] << 8) |
|
||||
((DWORD)EP0BUF[10] << 16) |
|
||||
((DWORD)EP0BUF[11] << 24);
|
||||
sr_step = (DWORD)EP0BUF[12] |
|
||||
((DWORD)EP0BUF[13] << 8) |
|
||||
((DWORD)EP0BUF[14] << 16) |
|
||||
((DWORD)EP0BUF[15] << 24);
|
||||
|
||||
if (sr_step == 0)
|
||||
sr_step = 1000000;
|
||||
|
||||
sr_cur = sr_min;
|
||||
while (sr_cur <= sr_max) {
|
||||
/*
|
||||
* Program frequency (BE) and symbol rate (BE) into BCM4500.
|
||||
* We write both in a single block: 4 bytes SR + 4 bytes freq.
|
||||
*/
|
||||
i2c_buf[0] = (BYTE)(sr_cur >> 24);
|
||||
i2c_buf[1] = (BYTE)(sr_cur >> 16);
|
||||
i2c_buf[2] = (BYTE)(sr_cur >> 8);
|
||||
i2c_buf[3] = (BYTE)(sr_cur);
|
||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
||||
|
||||
i2c_buf[0] = (BYTE)(freq_khz >> 24);
|
||||
i2c_buf[1] = (BYTE)(freq_khz >> 16);
|
||||
i2c_buf[2] = (BYTE)(freq_khz >> 8);
|
||||
i2c_buf[3] = (BYTE)(freq_khz);
|
||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
||||
|
||||
/* Issue tune command */
|
||||
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
|
||||
|
||||
/* Wait for acquisition attempt */
|
||||
delay(100);
|
||||
|
||||
/* Check lock */
|
||||
lock_val = 0;
|
||||
bcm_direct_read(BCM_REG_LOCK, &lock_val);
|
||||
if (lock_val & 0x20) {
|
||||
/* Locked -- report back via EP0 */
|
||||
EP0BUF[0] = (BYTE)(freq_khz);
|
||||
EP0BUF[1] = (BYTE)(freq_khz >> 8);
|
||||
EP0BUF[2] = (BYTE)(freq_khz >> 16);
|
||||
EP0BUF[3] = (BYTE)(freq_khz >> 24);
|
||||
EP0BUF[4] = (BYTE)(sr_cur);
|
||||
EP0BUF[5] = (BYTE)(sr_cur >> 8);
|
||||
EP0BUF[6] = (BYTE)(sr_cur >> 16);
|
||||
EP0BUF[7] = (BYTE)(sr_cur >> 24);
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 8;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
sr_cur += sr_step;
|
||||
}
|
||||
|
||||
/* No lock found */
|
||||
EP0BUF[0] = 0x00;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------- TUNE_8PSK (0x86) handler ---------- */
|
||||
|
||||
/*
|
||||
* Parse 10-byte EP0 payload, program BCM4500 via I2C indirect registers.
|
||||
* Follows the stock firmware's protocol:
|
||||
* EP0BUF[0..3] = symbol_rate (LE u32, sps)
|
||||
* EP0BUF[4..7] = freq_khz (LE u32, kHz)
|
||||
* EP0BUF[8] = modulation index (0-9)
|
||||
* EP0BUF[9] = FEC index
|
||||
*/
|
||||
static void do_tune(void) {
|
||||
BYTE i;
|
||||
__xdata BYTE tune_data[12];
|
||||
|
||||
if (!(config_status & BM_STARTED))
|
||||
return;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
|
||||
/* Modulation type and FEC rate */
|
||||
tune_data[8] = EP0BUF[8];
|
||||
tune_data[9] = EP0BUF[9];
|
||||
|
||||
/* Demod mode: default standard (0x10) */
|
||||
tune_data[10] = 0x10;
|
||||
|
||||
/* Turbo flag: 0x00 for DVB-S, 0x01 for turbo modes */
|
||||
tune_data[11] = 0x00;
|
||||
if (EP0BUF[8] >= 1 && EP0BUF[8] <= 3)
|
||||
tune_data[11] = 0x01;
|
||||
|
||||
/* Set demod mode for DCII variants */
|
||||
switch (EP0BUF[8]) {
|
||||
case 5: tune_data[10] = 0x12; break; /* DCII I-stream */
|
||||
case 6: tune_data[10] = 0x16; break; /* DCII Q-stream */
|
||||
case 7: tune_data[10] = 0x11; break; /* DCII Offset QPSK */
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* Poll BCM4500 for readiness */
|
||||
bcm_poll_ready();
|
||||
|
||||
/* Write page 0 */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Execute indirect write */
|
||||
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
|
||||
|
||||
/* Wait for command completion */
|
||||
bcm_poll_ready();
|
||||
}
|
||||
|
||||
/* ---------- Vendor command handler ---------- */
|
||||
|
||||
BOOL handle_vendorcommand(BYTE cmd) {
|
||||
WORD wval;
|
||||
BYTE val;
|
||||
|
||||
wval = SETUP_VALUE();
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
/* 0x80: GET_8PSK_CONFIG -- return config status byte */
|
||||
case GET_8PSK_CONFIG:
|
||||
EP0BUF[0] = config_status;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return TRUE;
|
||||
|
||||
/* 0x85: ARM_TRANSFER -- start/stop MPEG-2 streaming */
|
||||
case ARM_TRANSFER:
|
||||
if (wval)
|
||||
gpif_start();
|
||||
else
|
||||
gpif_stop();
|
||||
return TRUE;
|
||||
|
||||
/* 0x86: TUNE_8PSK -- 10-byte tuning payload */
|
||||
case TUNE_8PSK:
|
||||
/* EP0 data phase: wait for 10 bytes from host */
|
||||
EP0BCL = 0;
|
||||
SYNCDELAY;
|
||||
while (EP0CS & bmEPBUSY)
|
||||
;
|
||||
do_tune();
|
||||
return TRUE;
|
||||
|
||||
/* 0x87: GET_SIGNAL_STRENGTH -- read 6 bytes from BCM4500 */
|
||||
case GET_SIGNAL_STRENGTH:
|
||||
if (!(config_status & BM_STARTED)) {
|
||||
EP0BUF[0] = 0; EP0BUF[1] = 0;
|
||||
EP0BUF[2] = 0; EP0BUF[3] = 0;
|
||||
EP0BUF[4] = 0; EP0BUF[5] = 0;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 6;
|
||||
return TRUE;
|
||||
}
|
||||
/* Read signal quality via indirect registers */
|
||||
bcm_indirect_read(0x00, &EP0BUF[0]);
|
||||
bcm_indirect_read(0x01, &EP0BUF[1]);
|
||||
bcm_indirect_read(0x02, &EP0BUF[2]);
|
||||
bcm_indirect_read(0x03, &EP0BUF[3]);
|
||||
bcm_indirect_read(0x04, &EP0BUF[4]);
|
||||
bcm_indirect_read(0x05, &EP0BUF[5]);
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 6;
|
||||
return TRUE;
|
||||
|
||||
/* 0x89: BOOT_8PSK -- initialize BCM4500 demodulator */
|
||||
case BOOT_8PSK:
|
||||
if (wval) {
|
||||
/* Power on: scan for BCM4500 at address 0x10 */
|
||||
val = 0;
|
||||
if (bcm_direct_read(BCM_REG_STATUS, &val)) {
|
||||
config_status |= BM_STARTED;
|
||||
config_status |= BM_FW_LOADED;
|
||||
}
|
||||
} else {
|
||||
config_status &= ~BM_STARTED;
|
||||
}
|
||||
EP0BUF[0] = config_status;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return TRUE;
|
||||
|
||||
/* 0x8A: START_INTERSIL -- enable LNB power supply */
|
||||
case START_INTERSIL:
|
||||
if (wval) {
|
||||
/* Enable LNB power */
|
||||
OEA |= (PIN_22KHZ | PIN_LNB_VOLT | PIN_DISEQC);
|
||||
config_status |= BM_INTERSIL;
|
||||
} else {
|
||||
config_status &= ~BM_INTERSIL;
|
||||
}
|
||||
EP0BUF[0] = config_status;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return TRUE;
|
||||
|
||||
/* 0x8B: SET_LNB_VOLTAGE -- 13V (wval=0) or 18V (wval=1) */
|
||||
case SET_LNB_VOLTAGE:
|
||||
if (wval) {
|
||||
IOA |= PIN_LNB_VOLT;
|
||||
config_status |= BM_SEL18V;
|
||||
} else {
|
||||
IOA &= ~PIN_LNB_VOLT;
|
||||
config_status &= ~BM_SEL18V;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
/* 0x8C: SET_22KHZ_TONE -- on (wval=1) or off (wval=0) */
|
||||
case SET_22KHZ_TONE:
|
||||
if (wval) {
|
||||
IOA |= PIN_22KHZ;
|
||||
config_status |= BM_22KHZ;
|
||||
} else {
|
||||
IOA &= ~PIN_22KHZ;
|
||||
config_status &= ~BM_22KHZ;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
/* 0x8D: SEND_DISEQC -- tone burst or DiSEqC message */
|
||||
case SEND_DISEQC: {
|
||||
WORD wlen;
|
||||
wlen = SETUP_LENGTH();
|
||||
if (wlen == 0) {
|
||||
/* Tone burst: A if wval==0, B if wval!=0 */
|
||||
diseqc_tone_burst((BYTE)wval);
|
||||
}
|
||||
/* Full DiSEqC message: future implementation */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* 0x90: GET_SIGNAL_LOCK -- read BCM4500 lock register */
|
||||
case GET_SIGNAL_LOCK:
|
||||
val = 0;
|
||||
if (config_status & BM_STARTED) {
|
||||
bcm_direct_read(BCM_REG_LOCK, &val);
|
||||
}
|
||||
EP0BUF[0] = val;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return TRUE;
|
||||
|
||||
/* 0x92: GET_FW_VERS -- return firmware version and build date */
|
||||
case GET_FW_VERS:
|
||||
EP0BUF[0] = 0x01; /* patch -> version 3.00.1 */
|
||||
EP0BUF[1] = 0x00; /* minor */
|
||||
EP0BUF[2] = 0x03; /* major */
|
||||
EP0BUF[3] = 0x0B; /* day = 11 */
|
||||
EP0BUF[4] = 0x02; /* month = 2 */
|
||||
EP0BUF[5] = 0x1A; /* year - 2000 = 26 */
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 6;
|
||||
return TRUE;
|
||||
|
||||
/* 0x94: USE_EXTRA_VOLT -- enable +1V LNB boost */
|
||||
case USE_EXTRA_VOLT:
|
||||
/* This would write to the LNB regulator; no-op for now */
|
||||
return TRUE;
|
||||
|
||||
/* --- Custom commands --- */
|
||||
|
||||
/* 0xB0: SPECTRUM_SWEEP */
|
||||
case SPECTRUM_SWEEP:
|
||||
/* EP0 data phase: wait for 10 bytes from host */
|
||||
EP0BCL = 0;
|
||||
SYNCDELAY;
|
||||
while (EP0CS & bmEPBUSY)
|
||||
;
|
||||
do_spectrum_sweep();
|
||||
return TRUE;
|
||||
|
||||
/* 0xB1: RAW_DEMOD_READ -- read BCM4500 register */
|
||||
case RAW_DEMOD_READ:
|
||||
val = 0;
|
||||
bcm_indirect_read((BYTE)wval, &val);
|
||||
EP0BUF[0] = val;
|
||||
EP0BCH = 0;
|
||||
EP0BCL = 1;
|
||||
return TRUE;
|
||||
|
||||
/* 0xB2: RAW_DEMOD_WRITE -- write BCM4500 register */
|
||||
case RAW_DEMOD_WRITE: {
|
||||
WORD widx;
|
||||
widx = SETUP_INDEX();
|
||||
bcm_indirect_write((BYTE)wval, (BYTE)widx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* 0xB3: BLIND_SCAN */
|
||||
case BLIND_SCAN:
|
||||
/* EP0 data phase: wait for 16 bytes from host */
|
||||
EP0BCL = 0;
|
||||
SYNCDELAY;
|
||||
while (EP0CS & bmEPBUSY)
|
||||
;
|
||||
do_blind_scan();
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Required fx2lib callbacks ---------- */
|
||||
|
||||
BOOL handle_get_descriptor(void) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL handle_get_interface(BYTE ifc, BYTE *alt_ifc) {
|
||||
if (ifc == 0) {
|
||||
*alt_ifc = 0;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL handle_set_interface(BYTE ifc, BYTE alt_ifc) {
|
||||
if (ifc == 0 && alt_ifc == 0) {
|
||||
RESETTOGGLE(0x82);
|
||||
RESETFIFO(0x02);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BYTE handle_get_configuration(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL handle_set_configuration(BYTE cfg) {
|
||||
return cfg == 1 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* ---------- USB interrupt handlers ---------- */
|
||||
|
||||
void sudav_isr(void) __interrupt (SUDAV_ISR) {
|
||||
got_sud = TRUE;
|
||||
CLEAR_SUDAV();
|
||||
}
|
||||
|
||||
void usbreset_isr(void) __interrupt (USBRESET_ISR) {
|
||||
handle_hispeed(FALSE);
|
||||
CLEAR_USBRESET();
|
||||
}
|
||||
|
||||
void hispeed_isr(void) __interrupt (HISPEED_ISR) {
|
||||
handle_hispeed(TRUE);
|
||||
CLEAR_HISPEED();
|
||||
}
|
||||
|
||||
/* ---------- Main ---------- */
|
||||
|
||||
void main(void) {
|
||||
|
||||
config_status = 0;
|
||||
got_sud = FALSE;
|
||||
|
||||
REVCTL = 0x03; /* NOAUTOARM + SKIPCOMMIT */
|
||||
SYNCDELAY;
|
||||
|
||||
RENUMERATE_UNCOND();
|
||||
|
||||
SETCPUFREQ(CLK_48M);
|
||||
SETIF48MHZ();
|
||||
|
||||
USE_USB_INTS();
|
||||
ENABLE_SUDAV();
|
||||
ENABLE_HISPEED();
|
||||
ENABLE_USBRESET();
|
||||
|
||||
/* Configure I2C: 400kHz */
|
||||
I2CTL = bm400KHZ;
|
||||
|
||||
/* Configure GPIO output enables for LNB/tone/DiSEqC (v2.06 pin map) */
|
||||
OEA |= (PIN_22KHZ | PIN_LNB_VOLT | PIN_DISEQC); /* P0.3, P0.4, P0.7 output */
|
||||
|
||||
/* Initial GPIO state: LNB off, tone off, DiSEqC idle */
|
||||
IOA = 0x84; /* P0.7=1 (idle), P0.2=1 (BCM4500 control) */
|
||||
IOD = 0xE1; /* P3.7:5=1 (controls idle), P3.0=1 */
|
||||
|
||||
/* EP2 is bulk IN (0x82), 512 byte, double-buffered */
|
||||
EP2CFG = 0xE2; /* valid, IN, bulk, 512, double */
|
||||
SYNCDELAY;
|
||||
|
||||
/* Disable unused endpoints */
|
||||
EP1INCFG &= ~bmVALID;
|
||||
SYNCDELAY;
|
||||
EP1OUTCFG &= ~bmVALID;
|
||||
SYNCDELAY;
|
||||
EP4CFG &= ~bmVALID;
|
||||
SYNCDELAY;
|
||||
EP6CFG &= ~bmVALID;
|
||||
SYNCDELAY;
|
||||
EP8CFG &= ~bmVALID;
|
||||
SYNCDELAY;
|
||||
|
||||
/* Reset all FIFOs */
|
||||
RESETFIFOS();
|
||||
|
||||
/* IFCONFIG: internal 48MHz, GPIF master, async */
|
||||
IFCONFIG = 0xEE;
|
||||
SYNCDELAY;
|
||||
|
||||
/* EP2FIFOCFG: AUTOIN, ZEROLENIN, 8-bit */
|
||||
EP2FIFOCFG = 0x0C;
|
||||
SYNCDELAY;
|
||||
|
||||
/* Disable other FIFO configs */
|
||||
EP4FIFOCFG = 0;
|
||||
SYNCDELAY;
|
||||
EP6FIFOCFG = 0;
|
||||
SYNCDELAY;
|
||||
EP8FIFOCFG = 0;
|
||||
SYNCDELAY;
|
||||
|
||||
EA = 1; /* global interrupt enable */
|
||||
|
||||
while (TRUE) {
|
||||
if (got_sud) {
|
||||
handle_setupdata();
|
||||
got_sud = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue