Implement full-blown NCO using DMA sniffer
This commit is contained in:
parent
f866c97fcb
commit
49cf85006d
140
src/main.c
140
src/main.c
|
@ -24,6 +24,10 @@
|
||||||
#define VREG_VOLTAGE VREG_VOLTAGE_1_20
|
#define VREG_VOLTAGE VREG_VOLTAGE_1_20
|
||||||
#define CLK_SYS_HZ (288 * MHZ)
|
#define CLK_SYS_HZ (288 * MHZ)
|
||||||
|
|
||||||
|
#define INIT_SAMPLE_RATE 100000
|
||||||
|
#define INIT_FREQ 94600000
|
||||||
|
#define INIT_GAIN 127
|
||||||
|
|
||||||
#define LO_PIN 9
|
#define LO_PIN 9
|
||||||
#define RX_PIN 13
|
#define RX_PIN 13
|
||||||
#define FB_PIN 5
|
#define FB_PIN 5
|
||||||
|
@ -39,19 +43,23 @@
|
||||||
#define IQ_BLOCK_LEN (2 * IQ_SAMPLES)
|
#define IQ_BLOCK_LEN (2 * IQ_SAMPLES)
|
||||||
#define IQ_QUEUE_LEN 8
|
#define IQ_QUEUE_LEN 8
|
||||||
|
|
||||||
#define LO_NUM_PHASES (1 << 6)
|
/*
|
||||||
#define LO_PHASE_BITS 10
|
* NOTE: Must have 256 phases with 256 bytes each.
|
||||||
#define LO_PHASE_WORDS (1 << (LO_PHASE_BITS - 2))
|
* Otherwise the DMA 1-byte write trick wouldn't work.
|
||||||
#define LO_COS_PHASES (1 << 14)
|
*/
|
||||||
#define LO_EFFECTIVE_BITS (32 * LO_PHASE_WORDS * LO_COS_PHASES)
|
|
||||||
|
|
||||||
const double step_hz = (double)CLK_SYS_HZ / (LO_EFFECTIVE_BITS / 2.0);
|
#define LO_NUM_PHASES 256
|
||||||
|
#define LO_PHASE_BITS 8
|
||||||
|
#define LO_PHASE_WORDS (1 << (LO_PHASE_BITS - 2))
|
||||||
|
#define STEP_BASE ((UINT_MAX + 1.0) / CLK_SYS_HZ)
|
||||||
|
|
||||||
|
static uint32_t nco_step = (uint32_t)(STEP_BASE * INIT_FREQ) * 32 * LO_PHASE_WORDS;
|
||||||
|
static uint32_t nco_null = 0;
|
||||||
|
|
||||||
static uint32_t lo_phase[LO_NUM_PHASES][LO_PHASE_WORDS]
|
static uint32_t lo_phase[LO_NUM_PHASES][LO_PHASE_WORDS]
|
||||||
__attribute__((__aligned__(LO_NUM_PHASES * 4 * LO_PHASE_WORDS)));
|
__attribute__((__aligned__(LO_NUM_PHASES * 4 * LO_PHASE_WORDS)));
|
||||||
|
|
||||||
static const uint32_t *lo_cos_phases[LO_COS_PHASES]
|
static uint32_t nco_addr = (uint32_t)lo_phase;
|
||||||
__attribute__((__aligned__(1 << LO_PHASE_BITS)));
|
|
||||||
|
|
||||||
#define DECIMATE 16
|
#define DECIMATE 16
|
||||||
#define RX_BITS_DEPTH 10
|
#define RX_BITS_DEPTH 10
|
||||||
|
@ -62,10 +70,6 @@ static uint32_t rx_cos[RX_WORDS] __attribute__((__aligned__(1 << RX_BITS_DEPTH))
|
||||||
static const uint32_t *rx_start = rx_cos;
|
static const uint32_t *rx_start = rx_cos;
|
||||||
static const uint32_t *rx_end = rx_cos + RX_WORDS - 1;
|
static const uint32_t *rx_end = rx_cos + RX_WORDS - 1;
|
||||||
|
|
||||||
#define INIT_SAMPLE_RATE 100000
|
|
||||||
#define INIT_FREQ 94600000
|
|
||||||
#define INIT_GAIN 127
|
|
||||||
|
|
||||||
#define NUM_GAINS 29
|
#define NUM_GAINS 29
|
||||||
static int gains[NUM_GAINS] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157,
|
static int gains[NUM_GAINS] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157,
|
||||||
166, 197, 207, 229, 254, 280, 297, 328, 338, 364,
|
166, 197, 207, 229, 254, 280, 297, 328, 338, 364,
|
||||||
|
@ -78,8 +82,10 @@ static int frequency = INIT_FREQ;
|
||||||
static int dma_ch_rx1 = -1;
|
static int dma_ch_rx1 = -1;
|
||||||
static int dma_ch_rx2 = -1;
|
static int dma_ch_rx2 = -1;
|
||||||
|
|
||||||
static int dma_ch_mix1 = -1;
|
static int dma_ch_nco1 = -1;
|
||||||
static int dma_ch_mix2 = -1;
|
static int dma_ch_nco2 = -1;
|
||||||
|
static int dma_ch_nco3 = -1;
|
||||||
|
static int dma_ch_mix = -1;
|
||||||
|
|
||||||
static int dma_ch_samp_cos = -1;
|
static int dma_ch_samp_cos = -1;
|
||||||
|
|
||||||
|
@ -224,6 +230,8 @@ static void init_rx()
|
||||||
pio_sm_set_consecutive_pindirs(PIO, SM_RX, RX_PIN, 1, GPIO_IN);
|
pio_sm_set_consecutive_pindirs(PIO, SM_RX, RX_PIN, 1, GPIO_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const uint32_t samp_insn = 16;
|
||||||
|
|
||||||
static void init_ad()
|
static void init_ad()
|
||||||
{
|
{
|
||||||
const uint16_t insn[] = {
|
const uint16_t insn[] = {
|
||||||
|
@ -277,8 +285,6 @@ static void init_ad()
|
||||||
pio_sm_init(PIO, SM_AD, origin_ad, &pc);
|
pio_sm_init(PIO, SM_AD, origin_ad, &pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STEP_BASE ((UINT_MAX + 1.0) / CLK_SYS_HZ)
|
|
||||||
|
|
||||||
static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t phase)
|
static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t phase)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
@ -294,37 +300,25 @@ static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rx_lo_init(double req_freq, bool align)
|
static void rx_lo_init(double freq)
|
||||||
{
|
{
|
||||||
double freq = req_freq;
|
|
||||||
|
|
||||||
if (align)
|
|
||||||
freq = round(freq / step_hz) * step_hz;
|
|
||||||
|
|
||||||
uint32_t step = STEP_BASE * freq;
|
uint32_t step = STEP_BASE * freq;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < LO_NUM_PHASES; i++)
|
for (uint32_t i = 0; i < LO_NUM_PHASES; i++)
|
||||||
lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step,
|
lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step, i << 24);
|
||||||
i << (__builtin_clz(LO_NUM_PHASES) + 1));
|
|
||||||
|
|
||||||
uint32_t phase_step = step * 32 * LO_PHASE_WORDS;
|
nco_step = step * 32 * LO_PHASE_WORDS;
|
||||||
uint32_t phase = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < LO_COS_PHASES; i++) {
|
|
||||||
lo_cos_phases[i] = lo_phase[phase >> (__builtin_clz(LO_NUM_PHASES) + 1)];
|
|
||||||
phase += phase_step;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t samp_insn = 16;
|
|
||||||
|
|
||||||
static void rf_rx_start()
|
static void rf_rx_start()
|
||||||
{
|
{
|
||||||
dma_ch_rx1 = dma_claim_unused_channel(true);
|
dma_ch_rx1 = dma_claim_unused_channel(true);
|
||||||
dma_ch_rx2 = dma_claim_unused_channel(true);
|
dma_ch_rx2 = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
dma_ch_mix1 = dma_claim_unused_channel(true);
|
dma_ch_nco1 = dma_claim_unused_channel(true);
|
||||||
dma_ch_mix2 = dma_claim_unused_channel(true);
|
dma_ch_nco2 = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_nco3 = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_mix = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
dma_ch_samp_cos = dma_claim_unused_channel(true);
|
dma_ch_samp_cos = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
@ -349,22 +343,42 @@ static void rf_rx_start()
|
||||||
dma_channel_configure(dma_ch_rx2, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX,
|
dma_channel_configure(dma_ch_rx2, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
/* Drive the LO capacitor. */
|
/* Step the NCO. */
|
||||||
dma_conf = dma_channel_get_default_config(dma_ch_mix1);
|
dma_conf = dma_channel_get_default_config(dma_ch_nco1);
|
||||||
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
channel_config_set_read_increment(&dma_conf, true);
|
channel_config_set_read_increment(&dma_conf, false);
|
||||||
channel_config_set_write_increment(&dma_conf, false);
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
channel_config_set_ring(&dma_conf, GPIO_IN, LO_PHASE_BITS);
|
channel_config_set_chain_to(&dma_conf, dma_ch_nco2);
|
||||||
dma_channel_configure(dma_ch_mix1, &dma_conf, &dma_hw->ch[dma_ch_mix2].al3_read_addr_trig,
|
dma_channel_configure(dma_ch_nco1, &dma_conf, &nco_null, &nco_step, 1, false);
|
||||||
lo_cos_phases, 1, false);
|
|
||||||
|
|
||||||
dma_conf = dma_channel_get_default_config(dma_ch_mix2);
|
/* DMA above will increment the phase accumulator. */
|
||||||
|
dma_sniffer_enable(dma_ch_nco1, DMA_SNIFF_CTRL_CALC_VALUE_SUM, true);
|
||||||
|
|
||||||
|
/* Prepare the phase address. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_nco2);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_8);
|
||||||
|
channel_config_set_read_increment(&dma_conf, false);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_nco3);
|
||||||
|
dma_channel_configure(dma_ch_nco2, &dma_conf, (uint8_t *)(&nco_addr) + 1,
|
||||||
|
((uint8_t *)&dma_hw->sniff_data) + 3, 1, false);
|
||||||
|
|
||||||
|
/* Trigger LO using the address. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_nco3);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, false);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
dma_channel_configure(dma_ch_nco3, &dma_conf, &dma_hw->ch[dma_ch_mix].al3_read_addr_trig,
|
||||||
|
&nco_addr, 1, false);
|
||||||
|
|
||||||
|
/* Drive the LO capacitor. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_mix);
|
||||||
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
channel_config_set_read_increment(&dma_conf, true);
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
channel_config_set_write_increment(&dma_conf, false);
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_LO, GPIO_OUT));
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_LO, GPIO_OUT));
|
||||||
channel_config_set_chain_to(&dma_conf, dma_ch_mix1);
|
channel_config_set_chain_to(&dma_conf, dma_ch_nco1);
|
||||||
dma_channel_configure(dma_ch_mix2, &dma_conf, &PIO->txf[SM_LO], NULL, LO_PHASE_WORDS,
|
dma_channel_configure(dma_ch_mix, &dma_conf, &PIO->txf[SM_LO], lo_phase, LO_PHASE_WORDS,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
/* Trigger accumulator values push. */
|
/* Trigger accumulator values push. */
|
||||||
|
@ -383,7 +397,7 @@ static void rf_rx_start()
|
||||||
init_rx();
|
init_rx();
|
||||||
|
|
||||||
dma_channel_start(dma_ch_rx1);
|
dma_channel_start(dma_ch_rx1);
|
||||||
dma_channel_start(dma_ch_mix1);
|
dma_channel_start(dma_ch_nco1);
|
||||||
dma_channel_start(dma_ch_samp_cos);
|
dma_channel_start(dma_ch_samp_cos);
|
||||||
|
|
||||||
pio_set_sm_mask_enabled(PIO, 0x0f, true);
|
pio_set_sm_mask_enabled(PIO, 0x0f, true);
|
||||||
|
@ -397,32 +411,42 @@ static void rf_rx_stop(void)
|
||||||
|
|
||||||
dma_channel_clear_chain_to(dma_ch_rx1);
|
dma_channel_clear_chain_to(dma_ch_rx1);
|
||||||
dma_channel_clear_chain_to(dma_ch_rx2);
|
dma_channel_clear_chain_to(dma_ch_rx2);
|
||||||
dma_channel_clear_chain_to(dma_ch_mix1);
|
dma_channel_clear_chain_to(dma_ch_nco1);
|
||||||
dma_channel_clear_chain_to(dma_ch_mix2);
|
dma_channel_clear_chain_to(dma_ch_nco2);
|
||||||
|
dma_channel_clear_chain_to(dma_ch_nco3);
|
||||||
|
dma_channel_clear_chain_to(dma_ch_mix);
|
||||||
dma_channel_clear_chain_to(dma_ch_samp_cos);
|
dma_channel_clear_chain_to(dma_ch_samp_cos);
|
||||||
|
|
||||||
dma_channel_abort(dma_ch_rx1);
|
dma_channel_abort(dma_ch_rx1);
|
||||||
dma_channel_abort(dma_ch_rx2);
|
dma_channel_abort(dma_ch_rx2);
|
||||||
dma_channel_abort(dma_ch_mix1);
|
dma_channel_abort(dma_ch_nco1);
|
||||||
dma_channel_abort(dma_ch_mix2);
|
dma_channel_abort(dma_ch_nco2);
|
||||||
|
dma_channel_abort(dma_ch_nco3);
|
||||||
|
dma_channel_abort(dma_ch_mix);
|
||||||
dma_channel_abort(dma_ch_samp_cos);
|
dma_channel_abort(dma_ch_samp_cos);
|
||||||
|
|
||||||
dma_channel_cleanup(dma_ch_rx1);
|
dma_channel_cleanup(dma_ch_rx1);
|
||||||
dma_channel_cleanup(dma_ch_rx2);
|
dma_channel_cleanup(dma_ch_rx2);
|
||||||
dma_channel_cleanup(dma_ch_mix1);
|
dma_channel_cleanup(dma_ch_nco1);
|
||||||
dma_channel_cleanup(dma_ch_mix2);
|
dma_channel_cleanup(dma_ch_nco2);
|
||||||
|
dma_channel_cleanup(dma_ch_nco3);
|
||||||
|
dma_channel_cleanup(dma_ch_mix);
|
||||||
dma_channel_cleanup(dma_ch_samp_cos);
|
dma_channel_cleanup(dma_ch_samp_cos);
|
||||||
|
|
||||||
dma_channel_unclaim(dma_ch_rx1);
|
dma_channel_unclaim(dma_ch_rx1);
|
||||||
dma_channel_unclaim(dma_ch_rx2);
|
dma_channel_unclaim(dma_ch_rx2);
|
||||||
dma_channel_unclaim(dma_ch_mix1);
|
dma_channel_unclaim(dma_ch_nco1);
|
||||||
dma_channel_unclaim(dma_ch_mix2);
|
dma_channel_unclaim(dma_ch_nco2);
|
||||||
|
dma_channel_unclaim(dma_ch_nco3);
|
||||||
|
dma_channel_unclaim(dma_ch_mix);
|
||||||
dma_channel_unclaim(dma_ch_samp_cos);
|
dma_channel_unclaim(dma_ch_samp_cos);
|
||||||
|
|
||||||
dma_ch_rx1 = -1;
|
dma_ch_rx1 = -1;
|
||||||
dma_ch_rx2 = -1;
|
dma_ch_rx2 = -1;
|
||||||
dma_ch_mix1 = -1;
|
dma_ch_nco1 = -1;
|
||||||
dma_ch_mix2 = -1;
|
dma_ch_nco2 = -1;
|
||||||
|
dma_ch_nco3 = -1;
|
||||||
|
dma_ch_mix = -1;
|
||||||
dma_ch_samp_cos = -1;
|
dma_ch_samp_cos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,13 +596,13 @@ static void run_command(uint8_t cmd, uint32_t arg)
|
||||||
if (0x01 == cmd) {
|
if (0x01 == cmd) {
|
||||||
/* Tune to a new center frequency */
|
/* Tune to a new center frequency */
|
||||||
frequency = arg;
|
frequency = arg;
|
||||||
rx_lo_init(frequency + sample_rate, true);
|
rx_lo_init(frequency + sample_rate);
|
||||||
} else if (0x02 == cmd) {
|
} else if (0x02 == cmd) {
|
||||||
/* Set the rate at which IQ sample pairs are sent */
|
/* Set the rate at which IQ sample pairs are sent */
|
||||||
sample_rate = arg;
|
sample_rate = arg;
|
||||||
dc_level = CLK_SYS_HZ / sample_rate / 2;
|
dc_level = CLK_SYS_HZ / sample_rate / 2;
|
||||||
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
|
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
|
||||||
rx_lo_init(frequency + sample_rate, true);
|
rx_lo_init(frequency + sample_rate);
|
||||||
} else if (0x04 == cmd) {
|
} else if (0x04 == cmd) {
|
||||||
/* Set the tuner gain level */
|
/* Set the tuner gain level */
|
||||||
gain = INIT_GAIN * powf(10.0f, arg / 200.0f);
|
gain = INIT_GAIN * powf(10.0f, arg / 200.0f);
|
||||||
|
@ -692,7 +716,7 @@ int main()
|
||||||
|
|
||||||
queue_init(&iq_queue, sizeof(uint8_t *), IQ_QUEUE_LEN);
|
queue_init(&iq_queue, sizeof(uint8_t *), IQ_QUEUE_LEN);
|
||||||
|
|
||||||
rx_lo_init(frequency + sample_rate, true);
|
rx_lo_init(frequency + sample_rate);
|
||||||
|
|
||||||
dma_t_samp = dma_claim_unused_timer(true);
|
dma_t_samp = dma_claim_unused_timer(true);
|
||||||
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
|
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
|
||||||
|
|
Loading…
Reference in a new issue