Use DMA-based full speed mixing
This commit is contained in:
parent
c7980567c0
commit
f8b316a340
3 changed files with 560 additions and 238 deletions
|
@ -19,6 +19,7 @@ target_link_libraries(
|
||||||
hardware_divider
|
hardware_divider
|
||||||
hardware_dma
|
hardware_dma
|
||||||
hardware_pio
|
hardware_pio
|
||||||
|
hardware_pwm
|
||||||
hardware_timer
|
hardware_timer
|
||||||
hardware_vreg
|
hardware_vreg
|
||||||
hardware_interp
|
hardware_interp
|
||||||
|
@ -29,8 +30,8 @@ pico_add_extra_outputs(pico_sdr)
|
||||||
set_property(TARGET pico_sdr PROPERTY C_STANDARD 23)
|
set_property(TARGET pico_sdr PROPERTY C_STANDARD 23)
|
||||||
target_compile_options(pico_sdr PRIVATE -Wall -Wextra -Wnull-dereference)
|
target_compile_options(pico_sdr PRIVATE -Wall -Wextra -Wnull-dereference)
|
||||||
target_compile_definitions(pico_sdr PUBLIC PICO_MAX_SHARED_IRQ_HANDLERS=8u)
|
target_compile_definitions(pico_sdr PUBLIC PICO_MAX_SHARED_IRQ_HANDLERS=8u)
|
||||||
target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_ENABLE_CRLF_SUPPORT=1)
|
target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_ENABLE_CRLF_SUPPORT=0)
|
||||||
target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=1)
|
target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=0)
|
||||||
|
|
||||||
target_include_directories(pico_sdr PRIVATE include)
|
target_include_directories(pico_sdr PRIVATE include)
|
||||||
|
|
||||||
|
|
735
src/main.c
735
src/main.c
|
@ -26,9 +26,11 @@
|
||||||
#include <hardware/vreg.h>
|
#include <hardware/vreg.h>
|
||||||
#include <hardware/sync.h>
|
#include <hardware/sync.h>
|
||||||
#include <hardware/pio.h>
|
#include <hardware/pio.h>
|
||||||
|
#include <hardware/pwm.h>
|
||||||
#include <hardware/interp.h>
|
#include <hardware/interp.h>
|
||||||
|
|
||||||
#include <hardware/regs/clocks.h>
|
#include <hardware/regs/clocks.h>
|
||||||
|
#include <hardware/structs/bus_ctrl.h>
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -36,29 +38,47 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define CLK_SYS_HZ (250 * MHZ)
|
#define CLK_SYS_HZ (250 * MHZ)
|
||||||
#define CLKDIV_RF 2
|
#define BANDWIDTH 8000
|
||||||
#define CLK_RF_HZ ((unsigned)(CLK_SYS_HZ / CLKDIV_RF))
|
|
||||||
|
|
||||||
#define EXTRA_BITS 8
|
#define EXTRA_BITS 8
|
||||||
#define NUM_SAMPLES 128
|
#define NUM_SAMPLES 128
|
||||||
#define LPF_SAMPLES 0 /* 4 */
|
#define LPF_SAMPLES 0 /* 4 */
|
||||||
|
|
||||||
|
#define IQ_BLOCK_LEN 32
|
||||||
|
|
||||||
|
#define XOR_ADDR 0x1000
|
||||||
|
#define LO_COS_ACCUMULATOR (&pio1->sm[2].pinctrl)
|
||||||
|
#define LO_SIN_ACCUMULATOR (&pio1->sm[3].pinctrl)
|
||||||
|
|
||||||
#define LO_BITS_DEPTH 13
|
#define LO_BITS_DEPTH 13
|
||||||
#define LO_WORDS (1 << LO_BITS_DEPTH)
|
#define LO_WORDS (1 << LO_BITS_DEPTH)
|
||||||
static uint32_t lo_cos[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4)));
|
static uint32_t lo_cos[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4)));
|
||||||
static uint32_t lo_sin[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4)));
|
static uint32_t lo_sin[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4)));
|
||||||
static uint32_t rx_buf[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4)));
|
|
||||||
|
|
||||||
#define IQ_BLOCK_LEN 60
|
#define RX_BITS_DEPTH 10
|
||||||
static queue_t rx_queue;
|
#define RX_WORDS (1 << RX_BITS_DEPTH)
|
||||||
|
static uint32_t rx_cos[RX_WORDS] __attribute__((__aligned__(RX_WORDS * 4)));
|
||||||
|
static uint32_t rx_sin[RX_WORDS] __attribute__((__aligned__(RX_WORDS * 4)));
|
||||||
|
|
||||||
static int tx_dma = -1;
|
/* rx -> cp -> cos -> sin -> pio_cos -> pio_sin -> rx ... */
|
||||||
static int rx_dma = -1;
|
static int dma_ch_rx = -1;
|
||||||
|
static int dma_ch_cp = -1;
|
||||||
|
static int dma_ch_cos = -1;
|
||||||
|
static int dma_ch_sin = -1;
|
||||||
|
static int dma_ch_pio_cos = -1;
|
||||||
|
static int dma_ch_pio_sin = -1;
|
||||||
|
|
||||||
static volatile struct status {
|
static int dma_ch_samp_trig = -1;
|
||||||
unsigned sample_rate;
|
static int dma_ch_samp_cos = -1;
|
||||||
int gap;
|
static int dma_ch_samp_sin = -1;
|
||||||
} status;
|
|
||||||
|
static int dma_t_samp = -1;
|
||||||
|
|
||||||
|
static int dma_ch_in_cos = -1;
|
||||||
|
static int dma_ch_in_sin = -1;
|
||||||
|
|
||||||
|
static queue_t iq_queue;
|
||||||
|
static int gap = 0;
|
||||||
|
|
||||||
static void bias_init(int in_pin, int out_pin)
|
static void bias_init(int in_pin, int out_pin)
|
||||||
{
|
{
|
||||||
|
@ -71,29 +91,30 @@ static void bias_init(int in_pin, int out_pin)
|
||||||
gpio_set_input_hysteresis_enabled(out_pin, false);
|
gpio_set_input_hysteresis_enabled(out_pin, false);
|
||||||
gpio_set_drive_strength(out_pin, GPIO_DRIVE_STRENGTH_2MA);
|
gpio_set_drive_strength(out_pin, GPIO_DRIVE_STRENGTH_2MA);
|
||||||
|
|
||||||
const uint16_t lm_insn[] = {
|
const uint16_t insn[] = {
|
||||||
pio_encode_mov_not(pio_pins, pio_pins),
|
pio_encode_mov_not(pio_pins, pio_pins),
|
||||||
};
|
};
|
||||||
|
|
||||||
pio_program_t lm_prog = {
|
pio_program_t prog = {
|
||||||
.instructions = lm_insn,
|
.instructions = insn,
|
||||||
.length = 1,
|
.length = 1,
|
||||||
.origin = 0,
|
.origin = 31,
|
||||||
};
|
};
|
||||||
|
|
||||||
pio_sm_set_enabled(pio1, 0, false);
|
pio_sm_set_enabled(pio1, 0, false);
|
||||||
pio_sm_restart(pio1, 0);
|
pio_sm_restart(pio1, 0);
|
||||||
|
pio_sm_clear_fifos(pio1, 0);
|
||||||
|
|
||||||
if (pio_can_add_program(pio1, &lm_prog))
|
if (pio_can_add_program(pio1, &prog))
|
||||||
pio_add_program(pio1, &lm_prog);
|
pio_add_program(pio1, &prog);
|
||||||
|
|
||||||
pio_sm_config pc = pio_get_default_sm_config();
|
pio_sm_config pc = pio_get_default_sm_config();
|
||||||
sm_config_set_in_pins(&pc, in_pin);
|
sm_config_set_in_pins(&pc, in_pin);
|
||||||
sm_config_set_out_pins(&pc, out_pin, 1);
|
sm_config_set_out_pins(&pc, out_pin, 1);
|
||||||
sm_config_set_set_pins(&pc, out_pin, 1);
|
sm_config_set_set_pins(&pc, out_pin, 1);
|
||||||
sm_config_set_wrap(&pc, 0, 0);
|
sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1);
|
||||||
sm_config_set_clkdiv_int_frac(&pc, 3, 127);
|
sm_config_set_clkdiv_int_frac(&pc, 7, 127);
|
||||||
pio_sm_init(pio1, 0, 0, &pc);
|
pio_sm_init(pio1, 0, prog.origin, &pc);
|
||||||
|
|
||||||
pio_sm_set_consecutive_pindirs(pio1, 0, out_pin, 1, GPIO_OUT);
|
pio_sm_set_consecutive_pindirs(pio1, 0, out_pin, 1, GPIO_OUT);
|
||||||
|
|
||||||
|
@ -113,22 +134,23 @@ static void watch_init(int in_pin)
|
||||||
pio_program_t prog = {
|
pio_program_t prog = {
|
||||||
.instructions = insn,
|
.instructions = insn,
|
||||||
.length = 1,
|
.length = 1,
|
||||||
.origin = 1,
|
.origin = 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
pio_sm_set_enabled(pio1, 1, false);
|
pio_sm_set_enabled(pio1, 1, false);
|
||||||
pio_sm_restart(pio1, 1);
|
pio_sm_restart(pio1, 1);
|
||||||
|
pio_sm_clear_fifos(pio1, 1);
|
||||||
|
|
||||||
if (pio_can_add_program(pio1, &prog))
|
if (pio_can_add_program(pio1, &prog))
|
||||||
pio_add_program(pio1, &prog);
|
pio_add_program(pio1, &prog);
|
||||||
|
|
||||||
pio_sm_config pc = pio_get_default_sm_config();
|
pio_sm_config pc = pio_get_default_sm_config();
|
||||||
sm_config_set_in_pins(&pc, in_pin);
|
sm_config_set_in_pins(&pc, in_pin);
|
||||||
sm_config_set_wrap(&pc, 1, 1);
|
sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1);
|
||||||
sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0);
|
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
|
||||||
sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX);
|
sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX);
|
||||||
sm_config_set_in_shift(&pc, false, true, 32);
|
sm_config_set_in_shift(&pc, false, true, 32);
|
||||||
pio_sm_init(pio1, 1, 1, &pc);
|
pio_sm_init(pio1, 1, prog.origin, &pc);
|
||||||
|
|
||||||
pio_sm_set_enabled(pio1, 1, true);
|
pio_sm_set_enabled(pio1, 1, true);
|
||||||
}
|
}
|
||||||
|
@ -147,11 +169,12 @@ static void send_init(int out_pin)
|
||||||
pio_program_t prog = {
|
pio_program_t prog = {
|
||||||
.instructions = insn,
|
.instructions = insn,
|
||||||
.length = 1,
|
.length = 1,
|
||||||
.origin = 2,
|
.origin = 29,
|
||||||
};
|
};
|
||||||
|
|
||||||
pio_sm_set_enabled(pio1, 2, false);
|
pio_sm_set_enabled(pio1, 1, false);
|
||||||
pio_sm_restart(pio1, 2);
|
pio_sm_restart(pio1, 1);
|
||||||
|
pio_sm_clear_fifos(pio1, 1);
|
||||||
|
|
||||||
if (pio_can_add_program(pio1, &prog))
|
if (pio_can_add_program(pio1, &prog))
|
||||||
pio_add_program(pio1, &prog);
|
pio_add_program(pio1, &prog);
|
||||||
|
@ -159,44 +182,87 @@ static void send_init(int out_pin)
|
||||||
pio_sm_config pc = pio_get_default_sm_config();
|
pio_sm_config pc = pio_get_default_sm_config();
|
||||||
sm_config_set_out_pins(&pc, out_pin, 1);
|
sm_config_set_out_pins(&pc, out_pin, 1);
|
||||||
sm_config_set_set_pins(&pc, out_pin, 1);
|
sm_config_set_set_pins(&pc, out_pin, 1);
|
||||||
sm_config_set_wrap(&pc, 2, 2);
|
sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1);
|
||||||
sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0);
|
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
|
||||||
sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_TX);
|
sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_TX);
|
||||||
sm_config_set_out_shift(&pc, false, true, 32);
|
sm_config_set_out_shift(&pc, false, true, 32);
|
||||||
pio_sm_init(pio1, 2, 2, &pc);
|
pio_sm_init(pio1, 1, prog.origin, &pc);
|
||||||
|
|
||||||
pio_sm_set_consecutive_pindirs(pio1, 2, out_pin, 1, GPIO_OUT);
|
pio_sm_set_consecutive_pindirs(pio1, 1, out_pin, 1, GPIO_OUT);
|
||||||
|
|
||||||
pio_sm_set_enabled(pio1, 2, true);
|
pio_sm_set_enabled(pio1, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float lo_freq_init(float req_freq)
|
static void adder_init()
|
||||||
{
|
{
|
||||||
const float step_hz = (float)CLK_RF_HZ / (LO_WORDS * 32);
|
#if 1
|
||||||
float freq = roundf(req_freq / step_hz) * step_hz;
|
const uint16_t insn[] = {
|
||||||
|
pio_encode_jmp_y_dec(1),
|
||||||
|
pio_encode_out(pio_pc, 2),
|
||||||
|
pio_encode_out(pio_pc, 2),
|
||||||
|
pio_encode_jmp_x_dec(2),
|
||||||
|
|
||||||
unsigned step = (float)UINT_MAX / (float)CLK_RF_HZ * freq;
|
/* Avoid Y-- on wrap. */
|
||||||
|
pio_encode_out(pio_pc, 2),
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
/* For debugging. Consumes at full speed and counts at half cycles. */
|
||||||
|
const uint16_t insn[] = {
|
||||||
|
pio_encode_out(pio_null, 2),
|
||||||
|
pio_encode_jmp_x_dec(0),
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
interp0->ctrl[0] = (31 << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) |
|
pio_program_t prog = {
|
||||||
(0 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) |
|
.instructions = insn,
|
||||||
(0 << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB);
|
.length = sizeof(insn) / sizeof(*insn),
|
||||||
interp0->ctrl[1] = interp0->ctrl[0];
|
.origin = 0,
|
||||||
|
};
|
||||||
|
|
||||||
interp0->base[0] = step;
|
pio_sm_set_enabled(pio1, 2, false);
|
||||||
interp0->base[1] = step;
|
pio_sm_set_enabled(pio1, 3, false);
|
||||||
interp0->accum[1] = UINT_MAX / 4;
|
|
||||||
|
pio_sm_restart(pio1, 2);
|
||||||
|
pio_sm_restart(pio1, 3);
|
||||||
|
|
||||||
|
pio_sm_clear_fifos(pio1, 2);
|
||||||
|
pio_sm_clear_fifos(pio1, 3);
|
||||||
|
|
||||||
|
if (pio_can_add_program(pio1, &prog))
|
||||||
|
pio_add_program(pio1, &prog);
|
||||||
|
|
||||||
|
pio_sm_config pc = pio_get_default_sm_config();
|
||||||
|
sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1);
|
||||||
|
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
|
||||||
|
sm_config_set_in_shift(&pc, false, true, 32);
|
||||||
|
sm_config_set_out_shift(&pc, false, true, 32);
|
||||||
|
pio_sm_init(pio1, 2, prog.origin + prog.length - 1, &pc);
|
||||||
|
pio_sm_init(pio1, 3, prog.origin + prog.length - 1, &pc);
|
||||||
|
|
||||||
|
pio_sm_set_enabled(pio1, 2, true);
|
||||||
|
pio_sm_set_enabled(pio1, 3, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float lo_freq_init(double req_freq)
|
||||||
|
{
|
||||||
|
const double step_hz = (double)CLK_SYS_HZ / (LO_WORDS * 32);
|
||||||
|
double freq = round(req_freq / step_hz) * step_hz;
|
||||||
|
|
||||||
|
unsigned step = ((double)UINT_MAX + 1.0) / (double)CLK_SYS_HZ * freq;
|
||||||
|
unsigned asin = 0;
|
||||||
|
unsigned acos = UINT_MAX / 4;
|
||||||
|
|
||||||
for (int i = 0; i < LO_WORDS; i++) {
|
for (int i = 0; i < LO_WORDS; i++) {
|
||||||
unsigned bsin = 0, bcos = 0;
|
unsigned bsin = 0, bcos = 0;
|
||||||
|
|
||||||
for (int j = 0; j < 32; j++) {
|
for (int j = 0; j < 32; j++) {
|
||||||
int sin_bit = interp0->peek[0] >> 31;
|
bsin |= asin >> 31;
|
||||||
bsin |= sin_bit;
|
|
||||||
bsin <<= 1;
|
bsin <<= 1;
|
||||||
|
asin += step;
|
||||||
|
|
||||||
int cos_bit = interp0->pop[1] >> 31;
|
bcos |= acos >> 31;
|
||||||
bcos |= cos_bit;
|
|
||||||
bcos <<= 1;
|
bcos <<= 1;
|
||||||
|
acos += step;
|
||||||
}
|
}
|
||||||
|
|
||||||
lo_sin[i] = bsin;
|
lo_sin[i] = bsin;
|
||||||
|
@ -244,125 +310,282 @@ inline static __unused int cheap_angle_diff(int angle1, int angle2)
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static unsigned popcount(unsigned v)
|
static const uint32_t samp_insn[] __attribute__((__aligned__(16))) = {
|
||||||
|
0x4020, /* IN X, 32 */
|
||||||
|
0x4040, /* IN Y, 32 */
|
||||||
|
0xe020, /* SET X, 0 */
|
||||||
|
0xe040, /* SET Y, 0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t null, one = 1;
|
||||||
|
|
||||||
|
static float rf_rx_start(int rx_pin, int bias_pin, float freq, int frac_num, int frac_denom)
|
||||||
{
|
{
|
||||||
v = v - ((v >> 1) & 0x55555555);
|
dma_ch_rx = dma_claim_unused_channel(true);
|
||||||
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
dma_ch_cp = dma_claim_unused_channel(true);
|
||||||
return (((v + (v >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
|
dma_ch_cos = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_sin = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_pio_cos = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_pio_sin = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
dma_ch_samp_cos = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_samp_sin = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_samp_trig = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
dma_t_samp = dma_claim_unused_timer(true);
|
||||||
|
|
||||||
|
dma_channel_config dma_conf;
|
||||||
|
|
||||||
|
/* Read received word into accumulator I. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_rx);
|
||||||
|
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);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, false));
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_cp);
|
||||||
|
dma_channel_configure(dma_ch_rx, &dma_conf, LO_COS_ACCUMULATOR, &pio1->rxf[1], 1, false);
|
||||||
|
|
||||||
|
/* Copy accumulator I to accumulator Q. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_cp);
|
||||||
|
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);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_cos);
|
||||||
|
dma_channel_configure(dma_ch_cp, &dma_conf, LO_SIN_ACCUMULATOR, LO_COS_ACCUMULATOR, 1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* Read lo_cos into accumulator I with XOR. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_cos);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_sin);
|
||||||
|
dma_channel_configure(dma_ch_cos, &dma_conf, LO_COS_ACCUMULATOR + XOR_ADDR / 4, lo_cos, 1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* Read lo_sin into accumulator Q with XOR. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_sin);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_pio_cos);
|
||||||
|
dma_channel_configure(dma_ch_sin, &dma_conf, LO_SIN_ACCUMULATOR + XOR_ADDR / 4, lo_sin, 1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* Copy mixed I accumulator to PIO adder I. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_pio_cos);
|
||||||
|
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);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true));
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_pio_sin);
|
||||||
|
dma_channel_configure(dma_ch_pio_cos, &dma_conf, &pio1->txf[2], LO_COS_ACCUMULATOR, 1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* Copy mixed Q accumulator to PIO adder Q. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_pio_sin);
|
||||||
|
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);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 3, true));
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_rx);
|
||||||
|
dma_channel_configure(dma_ch_pio_sin, &dma_conf, &pio1->txf[3], LO_SIN_ACCUMULATOR, 1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* Pacing timer for the sampling script trigger channel. */
|
||||||
|
dma_timer_set_fraction(dma_t_samp, frac_num, frac_denom);
|
||||||
|
|
||||||
|
/* Sampling trigger channel. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_samp_trig);
|
||||||
|
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);
|
||||||
|
channel_config_set_dreq(&dma_conf, dma_get_timer_dreq(dma_t_samp));
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_samp_cos);
|
||||||
|
dma_channel_configure(dma_ch_samp_trig, &dma_conf, &null, &one, 1, false);
|
||||||
|
|
||||||
|
/* Trigger I accumulator values push. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_samp_cos);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_ring(&dma_conf, false, 4);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_samp_sin);
|
||||||
|
dma_channel_configure(dma_ch_samp_cos, &dma_conf, &pio1->sm[2].instr, samp_insn, 4, false);
|
||||||
|
|
||||||
|
/* Trigger Q accumulator values push. */
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_samp_sin);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_ring(&dma_conf, false, 4);
|
||||||
|
channel_config_set_chain_to(&dma_conf, dma_ch_samp_trig);
|
||||||
|
dma_channel_configure(dma_ch_samp_sin, &dma_conf, &pio1->sm[3].instr, samp_insn, 4, false);
|
||||||
|
|
||||||
|
bias_init(rx_pin, bias_pin);
|
||||||
|
adder_init();
|
||||||
|
|
||||||
|
float actual = lo_freq_init(freq);
|
||||||
|
|
||||||
|
dma_channel_start(dma_ch_rx);
|
||||||
|
dma_channel_start(dma_ch_samp_trig);
|
||||||
|
watch_init(rx_pin);
|
||||||
|
|
||||||
|
return actual;
|
||||||
}
|
}
|
||||||
|
|
||||||
__noinline static int mix(uint32_t *buf, uint32_t *lo, int len)
|
static void rf_rx_stop(void)
|
||||||
{
|
{
|
||||||
int x = 0;
|
pio_sm_set_enabled(pio1, 0, false);
|
||||||
|
pio_sm_set_enabled(pio1, 1, false);
|
||||||
|
pio_sm_set_enabled(pio1, 2, false);
|
||||||
|
pio_sm_set_enabled(pio1, 3, false);
|
||||||
|
|
||||||
for (int k = 0; k < len; k++)
|
pio_sm_restart(pio1, 0);
|
||||||
x += popcount(buf[k] ^ lo[k]);
|
pio_sm_restart(pio1, 1);
|
||||||
|
pio_sm_restart(pio1, 2);
|
||||||
|
pio_sm_restart(pio1, 3);
|
||||||
|
|
||||||
return x;
|
pio_sm_clear_fifos(pio1, 0);
|
||||||
|
pio_sm_clear_fifos(pio1, 1);
|
||||||
|
pio_sm_clear_fifos(pio1, 2);
|
||||||
|
pio_sm_clear_fifos(pio1, 3);
|
||||||
|
|
||||||
|
sleep_us(10);
|
||||||
|
|
||||||
|
dma_channel_abort(dma_ch_rx);
|
||||||
|
dma_channel_abort(dma_ch_cp);
|
||||||
|
dma_channel_abort(dma_ch_cos);
|
||||||
|
dma_channel_abort(dma_ch_sin);
|
||||||
|
dma_channel_abort(dma_ch_pio_cos);
|
||||||
|
dma_channel_abort(dma_ch_pio_sin);
|
||||||
|
dma_channel_abort(dma_ch_samp_cos);
|
||||||
|
dma_channel_abort(dma_ch_samp_sin);
|
||||||
|
dma_channel_abort(dma_ch_samp_trig);
|
||||||
|
|
||||||
|
dma_channel_cleanup(dma_ch_rx);
|
||||||
|
dma_channel_cleanup(dma_ch_cp);
|
||||||
|
dma_channel_cleanup(dma_ch_cos);
|
||||||
|
dma_channel_cleanup(dma_ch_sin);
|
||||||
|
dma_channel_cleanup(dma_ch_pio_cos);
|
||||||
|
dma_channel_cleanup(dma_ch_pio_sin);
|
||||||
|
dma_channel_cleanup(dma_ch_samp_cos);
|
||||||
|
dma_channel_cleanup(dma_ch_samp_sin);
|
||||||
|
dma_channel_cleanup(dma_ch_samp_trig);
|
||||||
|
|
||||||
|
dma_channel_unclaim(dma_ch_rx);
|
||||||
|
dma_channel_unclaim(dma_ch_cp);
|
||||||
|
dma_channel_unclaim(dma_ch_cos);
|
||||||
|
dma_channel_unclaim(dma_ch_sin);
|
||||||
|
dma_channel_unclaim(dma_ch_pio_cos);
|
||||||
|
dma_channel_unclaim(dma_ch_pio_sin);
|
||||||
|
dma_channel_unclaim(dma_ch_samp_cos);
|
||||||
|
dma_channel_unclaim(dma_ch_samp_sin);
|
||||||
|
dma_channel_unclaim(dma_ch_samp_trig);
|
||||||
|
|
||||||
|
dma_timer_unclaim(dma_t_samp);
|
||||||
|
|
||||||
|
dma_ch_rx = -1;
|
||||||
|
dma_ch_cp = -1;
|
||||||
|
dma_ch_cos = -1;
|
||||||
|
dma_ch_sin = -1;
|
||||||
|
dma_ch_pio_cos = -1;
|
||||||
|
dma_ch_pio_sin = -1;
|
||||||
|
dma_ch_samp_cos = -1;
|
||||||
|
dma_ch_samp_sin = -1;
|
||||||
|
dma_ch_samp_trig = -1;
|
||||||
|
|
||||||
|
dma_t_samp = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rf_tx_start(int tx_pin)
|
||||||
|
{
|
||||||
|
send_init(tx_pin);
|
||||||
|
|
||||||
|
dma_ch_cos = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
dma_channel_config dma_conf = dma_channel_get_default_config(dma_ch_cos);
|
||||||
|
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dma_conf, true);
|
||||||
|
channel_config_set_write_increment(&dma_conf, false);
|
||||||
|
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, true));
|
||||||
|
dma_channel_configure(dma_ch_cos, &dma_conf, &pio1->txf[1], lo_cos, UINT_MAX, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rf_tx_stop()
|
||||||
|
{
|
||||||
|
puts("Stopping TX...");
|
||||||
|
pio_sm_set_enabled(pio1, 1, false);
|
||||||
|
pio_sm_restart(pio1, 1);
|
||||||
|
pio_sm_clear_fifos(pio1, 1);
|
||||||
|
|
||||||
|
puts("Stopping DMA...");
|
||||||
|
dma_channel_abort(dma_ch_cos);
|
||||||
|
dma_channel_cleanup(dma_ch_cos);
|
||||||
|
dma_channel_unclaim(dma_ch_cos);
|
||||||
|
dma_ch_cos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rf_rx(void)
|
static void rf_rx(void)
|
||||||
{
|
{
|
||||||
#if LPF_SAMPLES
|
|
||||||
static int lpIh1[LPF_SAMPLES], lpQh1[LPF_SAMPLES];
|
|
||||||
int lpIavg1 = 0, lpQavg1 = 0;
|
|
||||||
|
|
||||||
static int lpIh2[LPF_SAMPLES], lpQh2[LPF_SAMPLES];
|
|
||||||
int lpIavg2 = 0, lpQavg2 = 0;
|
|
||||||
|
|
||||||
static int lpIh3[LPF_SAMPLES], lpQh3[LPF_SAMPLES];
|
|
||||||
int lpIavg3 = 0, lpQavg3 = 0;
|
|
||||||
|
|
||||||
int lpIidx = 0, lpQidx = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < LPF_SAMPLES; i++) {
|
|
||||||
lpIh1[i] = lpIh2[i] = lpIh3[i] = 0;
|
|
||||||
lpQh1[i] = lpQh2[i] = lpQh3[i] = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned prev_transfers = 0;
|
|
||||||
|
|
||||||
static int16_t block[IQ_BLOCK_LEN];
|
static int16_t block[IQ_BLOCK_LEN];
|
||||||
int block_ptr = 0;
|
uint32_t prev_transfers = dma_hw->ch[dma_ch_in_cos].transfer_count;
|
||||||
|
unsigned pos = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers;
|
if (multicore_fifo_rvalid()) {
|
||||||
int gap = NUM_SAMPLES - delta;
|
multicore_fifo_pop_blocking();
|
||||||
|
multicore_fifo_push_blocking(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (delta < NUM_SAMPLES)
|
int delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count;
|
||||||
delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers;
|
gap = IQ_BLOCK_LEN - delta;
|
||||||
|
|
||||||
if (gap < 0)
|
while (delta < IQ_BLOCK_LEN) {
|
||||||
prev_transfers += NUM_SAMPLES;
|
delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count;
|
||||||
|
sleep_us(100);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned pos = prev_transfers & (LO_WORDS - 1);
|
prev_transfers -= IQ_BLOCK_LEN;
|
||||||
prev_transfers += NUM_SAMPLES;
|
|
||||||
|
|
||||||
int I = mix(rx_buf + pos, lo_cos + pos, NUM_SAMPLES);
|
uint32_t *cos_ptr = rx_cos + pos;
|
||||||
int Q = mix(rx_buf + pos, lo_sin + pos, NUM_SAMPLES);
|
uint32_t *sin_ptr = rx_sin + pos;
|
||||||
|
|
||||||
/* Remove the large DC bias. */
|
pos = (pos + IQ_BLOCK_LEN) & (RX_WORDS - 1);
|
||||||
I -= 16 * NUM_SAMPLES;
|
|
||||||
Q -= 16 * NUM_SAMPLES;
|
|
||||||
|
|
||||||
/* Not sure why, but this removes a small DC bias. */
|
int16_t *blockptr = block;
|
||||||
I -= NUM_SAMPLES / 16;
|
|
||||||
Q -= NUM_SAMPLES / 16;
|
|
||||||
|
|
||||||
/* Normalize to given number of bits. */
|
for (int i = 0; i < IQ_BLOCK_LEN / 2; i++) {
|
||||||
I = (I * ((1 << (7 + EXTRA_BITS)) - 1)) / 16;
|
uint32_t cos_pos = *cos_ptr++;
|
||||||
Q = (Q * ((1 << (7 + EXTRA_BITS)) - 1)) / 16;
|
uint32_t cos_neg = *cos_ptr++;
|
||||||
|
uint32_t sin_pos = *sin_ptr++;
|
||||||
|
uint32_t sin_neg = *sin_ptr++;
|
||||||
|
|
||||||
I /= NUM_SAMPLES;
|
int I = cos_neg - cos_pos;
|
||||||
Q /= NUM_SAMPLES;
|
int Q = sin_neg - sin_pos;
|
||||||
|
|
||||||
#if LPF_SAMPLES
|
*blockptr++ = I;
|
||||||
lpIavg1 += I - lpIh1[lpIidx];
|
*blockptr++ = Q;
|
||||||
lpIh1[lpIidx] = I;
|
}
|
||||||
|
|
||||||
lpIavg2 += lpIavg1 - lpIh2[lpIidx];
|
if (!queue_try_add(&iq_queue, block)) {
|
||||||
lpIh2[lpIidx] = lpIavg1;
|
puts("queue overflow");
|
||||||
|
|
||||||
lpIavg3 += lpIavg2 - lpIh3[lpIidx];
|
|
||||||
lpIh3[lpIidx++] = lpIavg2;
|
|
||||||
|
|
||||||
if (lpIidx >= LPF_SAMPLES)
|
|
||||||
lpIidx = 0;
|
|
||||||
|
|
||||||
I = lpIavg3 / (LPF_SAMPLES * LPF_SAMPLES * LPF_SAMPLES);
|
|
||||||
|
|
||||||
lpQavg1 += Q - lpQh1[lpQidx];
|
|
||||||
lpQh1[lpQidx] = Q;
|
|
||||||
|
|
||||||
lpQavg2 += lpQavg1 - lpQh2[lpQidx];
|
|
||||||
lpQh2[lpQidx] = lpQavg1;
|
|
||||||
|
|
||||||
lpQavg3 += lpQavg2 - lpQh3[lpQidx];
|
|
||||||
lpQh3[lpQidx++] = lpQavg2;
|
|
||||||
|
|
||||||
if (lpQidx >= LPF_SAMPLES)
|
|
||||||
lpQidx = 0;
|
|
||||||
|
|
||||||
Q = lpQavg3 / (LPF_SAMPLES * LPF_SAMPLES * LPF_SAMPLES);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
status.sample_rate = CLK_RF_HZ / (NUM_SAMPLES * 32);
|
|
||||||
status.gap = gap;
|
|
||||||
|
|
||||||
block[block_ptr++] = I;
|
|
||||||
block[block_ptr++] = Q;
|
|
||||||
|
|
||||||
if (block_ptr >= IQ_BLOCK_LEN) {
|
|
||||||
queue_try_add(&rx_queue, block);
|
|
||||||
block_ptr = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static int icopysign(int x, int s)
|
||||||
|
{
|
||||||
|
return s >= 0 ? abs(x) : -abs(x);
|
||||||
|
}
|
||||||
|
|
||||||
static void __unused plot_IQ(int I, int Q)
|
static void __unused plot_IQ(int I, int Q)
|
||||||
{
|
{
|
||||||
int mag = I ? copysign(log2f(abs(I)), I) : 0;
|
int mag = I ? icopysign(32 - __builtin_clz(abs(I)), I) : 0;
|
||||||
|
|
||||||
if (mag < 0) {
|
if (mag < 0) {
|
||||||
for (int l = -mag; l < 16; l++)
|
for (int l = -mag; l < 16; l++)
|
||||||
|
@ -382,7 +605,7 @@ static void __unused plot_IQ(int I, int Q)
|
||||||
putchar(' ');
|
putchar(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
mag = Q ? copysign(log2f(abs(Q)), Q) : 0;
|
mag = Q ? icopysign(32 - __builtin_clz(abs(Q)), Q) : 0;
|
||||||
|
|
||||||
if (mag < 0) {
|
if (mag < 0) {
|
||||||
for (int l = -mag; l < 16; l++)
|
for (int l = -mag; l < 16; l++)
|
||||||
|
@ -403,6 +626,106 @@ static void __unused plot_IQ(int I, int Q)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__unused inline static uint32_t ring_next(uint32_t **ptr, int dma_ch, int us)
|
||||||
|
{
|
||||||
|
const unsigned ring_bits = (dma_hw->ch[dma_ch].al1_ctrl >> 6) & 15;
|
||||||
|
const uint32_t ring_mask = (1 << ring_bits) - 1;
|
||||||
|
uint32_t uptr = (uint32_t)*ptr;
|
||||||
|
|
||||||
|
while (uptr == dma_hw->ch[dma_ch].write_addr)
|
||||||
|
sleep_us(us);
|
||||||
|
|
||||||
|
uptr = (uptr & ~ring_mask) | ((uptr + sizeof(uint32_t)) & ring_mask);
|
||||||
|
*ptr = (uint32_t *)uptr;
|
||||||
|
return *(uint32_t *)uptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_rx(int rx_pin, int bias_pin, float freq, char mode)
|
||||||
|
{
|
||||||
|
float actual = rf_rx_start(rx_pin, bias_pin, freq, 1, CLK_SYS_HZ / BANDWIDTH);
|
||||||
|
sleep_us(100);
|
||||||
|
|
||||||
|
dma_ch_in_cos = dma_claim_unused_channel(true);
|
||||||
|
dma_ch_in_sin = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
dma_channel_config dma_conf;
|
||||||
|
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_in_cos);
|
||||||
|
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, true);
|
||||||
|
channel_config_set_ring(&dma_conf, true, RX_BITS_DEPTH + 2);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, false));
|
||||||
|
dma_channel_configure(dma_ch_in_cos, &dma_conf, rx_cos, &pio1->rxf[2], UINT_MAX, false);
|
||||||
|
|
||||||
|
dma_conf = dma_channel_get_default_config(dma_ch_in_sin);
|
||||||
|
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, true);
|
||||||
|
channel_config_set_ring(&dma_conf, true, RX_BITS_DEPTH + 2);
|
||||||
|
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 3, false));
|
||||||
|
dma_channel_configure(dma_ch_in_sin, &dma_conf, rx_sin, &pio1->rxf[3], UINT_MAX, false);
|
||||||
|
|
||||||
|
dma_start_channel_mask((1 << dma_ch_in_sin) | (1 << dma_ch_in_cos));
|
||||||
|
|
||||||
|
multicore_launch_core1(rf_rx);
|
||||||
|
|
||||||
|
printf("Frequency: %.0f\n", actual);
|
||||||
|
|
||||||
|
static int16_t block[IQ_BLOCK_LEN];
|
||||||
|
|
||||||
|
while (queue_try_remove(&iq_queue, block))
|
||||||
|
/* Flush the queue */;
|
||||||
|
|
||||||
|
if ('b' == mode) {
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
putchar('$');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int c = getchar_timeout_us(0);
|
||||||
|
if ('\r' == c)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (queue_try_remove(&iq_queue, block)) {
|
||||||
|
if ('b' == mode) {
|
||||||
|
fwrite(block, sizeof block, 1, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < IQ_BLOCK_LEN / 2; i += 8) {
|
||||||
|
int I = block[i * 2];
|
||||||
|
int Q = block[i * 2 + 1];
|
||||||
|
printf("%2i %12i %12i ", gap, I, Q);
|
||||||
|
plot_IQ(I, Q);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
putchar('\n');
|
||||||
|
puts("Stopping core1...");
|
||||||
|
multicore_fifo_push_blocking(0);
|
||||||
|
multicore_fifo_pop_blocking();
|
||||||
|
sleep_us(10);
|
||||||
|
multicore_reset_core1();
|
||||||
|
|
||||||
|
puts("Stopping RX...");
|
||||||
|
rf_rx_stop();
|
||||||
|
|
||||||
|
puts("Stopping readout DMAs...");
|
||||||
|
dma_channel_abort(dma_ch_in_cos);
|
||||||
|
dma_channel_abort(dma_ch_in_sin);
|
||||||
|
dma_channel_cleanup(dma_ch_in_cos);
|
||||||
|
dma_channel_cleanup(dma_ch_in_sin);
|
||||||
|
dma_channel_unclaim(dma_ch_in_cos);
|
||||||
|
dma_channel_unclaim(dma_ch_in_sin);
|
||||||
|
dma_ch_in_cos = -1;
|
||||||
|
dma_ch_in_sin = -1;
|
||||||
|
|
||||||
|
puts("Done.");
|
||||||
|
}
|
||||||
|
|
||||||
static void command(const char *cmd)
|
static void command(const char *cmd)
|
||||||
{
|
{
|
||||||
static char tmp[83];
|
static char tmp[83];
|
||||||
|
@ -414,6 +737,7 @@ static void command(const char *cmd)
|
||||||
puts("drive N X - set GPIO pin drive strength");
|
puts("drive N X - set GPIO pin drive strength");
|
||||||
puts("bias I O - output negated I to O");
|
puts("bias I O - output negated I to O");
|
||||||
puts("rx N FREQ - receive on pin N");
|
puts("rx N FREQ - receive on pin N");
|
||||||
|
puts("brx N FREQ - receive on pin N, binary output");
|
||||||
puts("tx N FREQ - transmit on pin N");
|
puts("tx N FREQ - transmit on pin N");
|
||||||
puts("sweep N F G S - sweep from F to G with given step");
|
puts("sweep N F G S - sweep from F to G with given step");
|
||||||
puts("noise N - transmit random noise");
|
puts("noise N - transmit random noise");
|
||||||
|
@ -437,86 +761,38 @@ static void command(const char *cmd)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (3 == sscanf(cmd, " rx %i %f %[\a]", &n, &f, tmp)) {
|
if (4 == sscanf(cmd, " rx %i %i %f %[\a]", &n, &x, &f, tmp)) {
|
||||||
watch_init(n);
|
do_rx(n, x, f, 'a');
|
||||||
float actual = lo_freq_init(f);
|
return;
|
||||||
printf("actual frequency = %10.6f Hz\n", actual / MHZ);
|
}
|
||||||
|
|
||||||
dma_channel_config dma_conf = dma_channel_get_default_config(rx_dma);
|
|
||||||
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, true);
|
|
||||||
channel_config_set_ring(&dma_conf, true, LO_BITS_DEPTH + 2);
|
|
||||||
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, false));
|
|
||||||
dma_channel_configure(rx_dma, &dma_conf, rx_buf, &pio1->rxf[1], UINT_MAX, true);
|
|
||||||
|
|
||||||
multicore_launch_core1(rf_rx);
|
|
||||||
sleep_us(100);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
int c = getchar_timeout_us(0);
|
|
||||||
|
|
||||||
if (13 == c) {
|
|
||||||
multicore_reset_core1();
|
|
||||||
dma_channel_abort(rx_dma);
|
|
||||||
dma_channel_cleanup(rx_dma);
|
|
||||||
puts("stopped");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int16_t block[IQ_BLOCK_LEN];
|
|
||||||
|
|
||||||
while (queue_try_remove(&rx_queue, block)) {
|
|
||||||
putchar('!');
|
|
||||||
|
|
||||||
for (int i = 0; i < IQ_BLOCK_LEN; i++)
|
|
||||||
printf("%04hx", block[i]);
|
|
||||||
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("gap=%i, sr=%i\n", status.gap, status.sample_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (4 == sscanf(cmd, " brx %i %i %f %[\a]", &n, &x, &f, tmp)) {
|
||||||
|
do_rx(n, x, f, 'b');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (3 == sscanf(cmd, " tx %i %f %[\a]", &n, &f, tmp)) {
|
if (3 == sscanf(cmd, " tx %i %f %[\a]", &n, &f, tmp)) {
|
||||||
dma_channel_abort(tx_dma);
|
float actual = lo_freq_init(f);
|
||||||
|
printf("Frequency: %.0f\n", actual);
|
||||||
|
|
||||||
if (!f) {
|
rf_tx_start(n);
|
||||||
gpio_init(n);
|
puts("Transmitting, press ENTER to stop.");
|
||||||
puts("stopped");
|
|
||||||
return;
|
while (true) {
|
||||||
|
int c = getchar_timeout_us(1000);
|
||||||
|
|
||||||
|
if ('\r' == c) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_init(n);
|
rf_tx_stop();
|
||||||
float actual = lo_freq_init(f);
|
puts("Done.");
|
||||||
printf("actual frequency = %10.6f MHz\n", actual / MHZ);
|
|
||||||
|
|
||||||
dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma);
|
|
||||||
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
|
||||||
channel_config_set_read_increment(&dma_conf, true);
|
|
||||||
channel_config_set_write_increment(&dma_conf, false);
|
|
||||||
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
|
||||||
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true));
|
|
||||||
dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true);
|
|
||||||
puts("started");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (5 == sscanf(cmd, " sweep %i %f %f %i %[\a]", &n, &f, &g, &x, tmp)) {
|
if (5 == sscanf(cmd, " sweep %i %f %f %i %[\a]", &n, &f, &g, &x, tmp)) {
|
||||||
dma_channel_abort(tx_dma);
|
const float step_hz = (float)CLK_SYS_HZ / (LO_WORDS * 32);
|
||||||
|
|
||||||
if (!f || !g) {
|
|
||||||
gpio_init(n);
|
|
||||||
puts("stopped");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
send_init(n);
|
|
||||||
|
|
||||||
const float step_hz = (float)CLK_RF_HZ / (LO_WORDS * 32);
|
|
||||||
const float start = roundf(f / step_hz) * step_hz;
|
const float start = roundf(f / step_hz) * step_hz;
|
||||||
const float stop = roundf(g / step_hz) * step_hz;
|
const float stop = roundf(g / step_hz) * step_hz;
|
||||||
|
|
||||||
|
@ -525,53 +801,41 @@ static void command(const char *cmd)
|
||||||
for (int i = 0; i < LO_WORDS; i++)
|
for (int i = 0; i < LO_WORDS; i++)
|
||||||
lo_cos[i] = 0;
|
lo_cos[i] = 0;
|
||||||
|
|
||||||
dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma);
|
rf_tx_start(n);
|
||||||
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
|
||||||
channel_config_set_read_increment(&dma_conf, true);
|
|
||||||
channel_config_set_write_increment(&dma_conf, false);
|
|
||||||
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
|
||||||
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true));
|
|
||||||
dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true);
|
|
||||||
|
|
||||||
for (int i = 0; i < steps; i += x) {
|
for (int i = 0; i < steps; i += x) {
|
||||||
int c = getchar_timeout_us(0);
|
int c = getchar_timeout_us(100);
|
||||||
if ('\n' == c || '\r' == c)
|
if ('\r' == c)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
float actual = lo_freq_init(start + i * step_hz);
|
float actual = lo_freq_init(start + i * step_hz);
|
||||||
printf("frequency = %10.6f MHz\n", actual / MHZ);
|
printf("Frequency: %.0f\n", actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
dma_channel_abort(tx_dma);
|
rf_tx_stop();
|
||||||
|
puts("Done.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (2 == sscanf(cmd, " noise %i %[\a]", &n, tmp)) {
|
if (2 == sscanf(cmd, " noise %i %[\a]", &n, tmp)) {
|
||||||
send_init(n);
|
|
||||||
|
|
||||||
for (int i = 0; i < LO_WORDS; i++)
|
for (int i = 0; i < LO_WORDS; i++)
|
||||||
lo_cos[i] = rand();
|
lo_cos[i] = rand();
|
||||||
|
|
||||||
dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma);
|
rf_tx_start(n);
|
||||||
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
|
|
||||||
channel_config_set_read_increment(&dma_conf, true);
|
|
||||||
channel_config_set_write_increment(&dma_conf, false);
|
|
||||||
channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2);
|
|
||||||
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true));
|
|
||||||
dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true);
|
|
||||||
|
|
||||||
puts("Transmitting noise, press ENTER to stop.");
|
puts("Transmitting noise, press ENTER to stop.");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int c = getchar_timeout_us(0);
|
int c = getchar_timeout_us(100);
|
||||||
if ('\n' == c || '\r' == c)
|
if ('\r' == c)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (int i = 0; i < LO_WORDS; i++)
|
for (int i = 0; i < LO_WORDS; i++)
|
||||||
lo_cos[i] = rand();
|
lo_cos[i] = rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
dma_channel_abort(tx_dma);
|
rf_tx_stop();
|
||||||
|
puts("Done.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +849,8 @@ int main()
|
||||||
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ,
|
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ,
|
||||||
CLK_SYS_HZ);
|
CLK_SYS_HZ);
|
||||||
|
|
||||||
|
bus_ctrl_hw->priority |= BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;
|
||||||
|
|
||||||
stdio_usb_init();
|
stdio_usb_init();
|
||||||
|
|
||||||
for (int i = 0; i < 30; i++) {
|
for (int i = 0; i < 30; i++) {
|
||||||
|
@ -594,14 +860,11 @@ int main()
|
||||||
sleep_ms(100);
|
sleep_ms(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_dma = dma_claim_unused_channel(true);
|
|
||||||
rx_dma = dma_claim_unused_channel(true);
|
|
||||||
|
|
||||||
queue_init(&rx_queue, IQ_BLOCK_LEN * 2, 64);
|
|
||||||
|
|
||||||
printf("\nPuppet Online!\n");
|
printf("\nPuppet Online!\n");
|
||||||
printf("clk_sys = %10.6f MHz\n", (float)clock_get_hz(clk_sys) / MHZ);
|
printf("clk_sys = %10.6f MHz\n", (float)clock_get_hz(clk_sys) / MHZ);
|
||||||
|
|
||||||
|
queue_init(&iq_queue, IQ_BLOCK_LEN * 2, 256);
|
||||||
|
|
||||||
static char cmd[83];
|
static char cmd[83];
|
||||||
int cmdlen = 0;
|
int cmdlen = 0;
|
||||||
|
|
||||||
|
@ -611,7 +874,7 @@ int main()
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getchar_timeout_us(10000)) >= 0) {
|
while ((c = getchar_timeout_us(10000)) >= 0) {
|
||||||
if (13 == c) {
|
if ('\r' == c) {
|
||||||
/* Enter */
|
/* Enter */
|
||||||
} else if ((8 == c) && (cmdlen > 0)) {
|
} else if ((8 == c) && (cmdlen > 0)) {
|
||||||
cmd[--cmdlen] = 0;
|
cmd[--cmdlen] = 0;
|
||||||
|
@ -626,7 +889,7 @@ int main()
|
||||||
putchar(c);
|
putchar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((13 == c) || cmdlen == 80) {
|
if (('\r' == c) || cmdlen == 80) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (cmdlen > 0) {
|
if (cmdlen > 0) {
|
||||||
cmd[cmdlen] = '\a';
|
cmd[cmdlen] = '\a';
|
||||||
|
|
58
util/bridge.py
Executable file
58
util/bridge.py
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import time
|
||||||
|
from codecs import encode
|
||||||
|
from socket import AF_INET, SO_SNDBUF, SOCK_STREAM, SOL_SOCKET, socket
|
||||||
|
|
||||||
|
import click
|
||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"-f", "--frequency", default=40680000, type=int, help="Frequency to tune to"
|
||||||
|
)
|
||||||
|
def bridge(frequency):
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM)
|
||||||
|
sock.setsockopt(SOL_SOCKET, SO_SNDBUF, 1024 * 100)
|
||||||
|
|
||||||
|
with serial.Serial("/dev/ttyACM0", baudrate=10_000_000) as fp:
|
||||||
|
print("Resetting...")
|
||||||
|
fp.write(b"\r\n")
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
while fp.in_waiting:
|
||||||
|
fp.read(fp.in_waiting)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print("Connecting to localhost:1234...")
|
||||||
|
sock.connect(("localhost", 1234))
|
||||||
|
|
||||||
|
print(f"Starting RX 10/11 at {frequency}...")
|
||||||
|
fp.write(f"brx 10 11 {frequency}\r\n".encode("ascii"))
|
||||||
|
fp.read_until(b"$")
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
bstr = fp.read(64)
|
||||||
|
assert len(bstr) == 64
|
||||||
|
|
||||||
|
try:
|
||||||
|
assert len(bstr) == sock.send(bstr)
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except ConnectionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
fp.write(b"\r\n")
|
||||||
|
print("Bye.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
bridge()
|
Loading…
Reference in a new issue