diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e03693..c1b29b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ pico_sdk_init() # Add executable. Default name is the project name, version 0.1 -add_executable(uSDR uSDR.c lcd.c si5351.c dsp.c) +add_executable(uSDR uSDR.c lcd.c si5351.c dsp.c monitor.c) pico_set_program_name(uSDR "uSDR") pico_set_program_version(uSDR "0.1") @@ -31,9 +31,12 @@ target_link_libraries(uSDR pico_stdlib) pico_enable_stdio_uart(uSDR 0) pico_enable_stdio_usb(uSDR 1) + # Add any user requested libraries target_link_libraries(uSDR - hardware_i2c + pico_stdlib + pico_multicore + hardware_i2c hardware_pwm hardware_pio hardware_timer diff --git a/dsp.c b/dsp.c index fc040cd..ac66e1b 100644 --- a/dsp.c +++ b/dsp.c @@ -2,11 +2,12 @@ * dsp.c * * Created: Mar 2021 - * Author: Arjan + * Author: Arjan te Marvelde * - * Signal processing of RX and TX branch - * Each branch has a dedicated timer routine that runs on set times. - * The callback period is set with RX_US and TX_US, e.g. 16 means every 16 usec, or 62.5 kHz. + * Signal processing of RX and TX branch, to be run on the second processor core. + * Each branch has a dedicated routine that must run on set times. + * The period is determined by reads from the inter-core fifo, by the dsp_loop() routine. + * This fifo is written from core0 from a 16us timer callback routine (i.e. 62.5kHz) * * The RX branch: * - Sample I and Q QSD channels intermittently, and shift into I and Q delay line (31.25 kHz per channel) @@ -19,7 +20,7 @@ * * The TX branch: * - Sample the Audio input channel (62.5 kHz) - * - Low pass filter: Fc=4kHz + * - Low pass filter: Fc=3kHz * - Eight rate (7.8125 kHz) to improve low F behavior of Hilbert transform * - Generate Q samples by doing a Hilbert transform * - Push I and Q to QSE output DACs @@ -27,8 +28,12 @@ */ #include "pico/stdlib.h" +#include "pico/multicore.h" #include "hardware/pwm.h" #include "hardware/adc.h" +#include "hardware/irq.h" +#include "hardware/timer.h" +#include "hardware/clocks.h" #include "dsp.h" @@ -65,8 +70,10 @@ uint16_t wave4[64] = * Exact time is obtained by passing the value negative. * Here we use 16us (62.5 kHz == PWM freq/4 [or 8]) */ -#define RX_US 16 -#define TX_US 16 +#define DSP_US 16 +#define DSP_TX 1 +#define DSP_RX 2 + /* * Low pass filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/) @@ -84,19 +91,16 @@ volatile uint16_t dac_iq, dac_audio; volatile bool tx_enabled; -/* RX TIMER and callback */ +/* CORE1: RX branch */ volatile int16_t i_s[15], q_s[15], i_dc, q_dc, i_prev; -struct repeating_timer rx_timer; -bool rx_callback(struct repeating_timer *t) +bool rx(void) { static bool q_phase; int16_t sample; int32_t accu; int16_t qh; int i; - - if (tx_enabled) return(true); // Early bailout when TX-ing - + if (q_phase) { adc_select_input(1); // Q channel ADC @@ -170,19 +174,16 @@ bool rx_callback(struct repeating_timer *t) } -/* TX TIMER and callback */ +/* CORE1: TX branch */ volatile int16_t a_s_pre[15], a_s[15], a_dc; -struct repeating_timer tx_timer; -bool tx_callback(struct repeating_timer *t) +bool tx(void) { static int tx_phase = 0; int16_t sample; int32_t accu; int16_t qh; int i; - - if (!tx_enabled) return(true); - + /* * Get sample and shift into delay line */ @@ -233,7 +234,37 @@ bool tx_callback(struct repeating_timer *t) } -int dsp_init() +/* CORE1: Timing loop, triggered through inter-core fifo */ +void dsp_loop() +{ + uint32_t cmd; + + while(1) + { + cmd = multicore_fifo_pop_blocking(); // Wait for fifo output + if (cmd == DSP_TX) + tx(); + else + rx(); + } +} + + +/* CORE0: Timer callback, triggers core1 through inter-core fifo */ +struct repeating_timer dsp_timer; +bool dsp_callback(struct repeating_timer *t) +{ + //if (tx_enabled) + multicore_fifo_push_blocking(DSP_TX); // Write TX in fifo to core 1 + //else + multicore_fifo_push_blocking(DSP_RX); // Write RX in fifo to core 1 + + return true; +} + + +/* CORE0: Initialize dsp context and spawn core1 process */ +void dsp_init() { uint16_t slice_num; @@ -259,9 +290,8 @@ int dsp_init() adc_select_input(0); // Select ADC 0 tx_enabled = false; // RX mode - - //add_repeating_timer_us(-TX_US, tx_callback, NULL, &tx_timer); - add_repeating_timer_us(-RX_US, rx_callback, NULL, &rx_timer); - return 0; + multicore_launch_core1(dsp_loop); // Start processing on core1 + add_repeating_timer_us(-DSP_US, dsp_callback, NULL, &dsp_timer); } + diff --git a/dsp.h b/dsp.h index 263b5a1..613c7e1 100644 --- a/dsp.h +++ b/dsp.h @@ -1,11 +1,19 @@ #ifndef __DSP_H__ #define __DSP_H__ +/* + * dsp.h + * + * Created: Mar 2021 + * Author: Arjan te Marvelde + * + * See dsp.c for more information + */ #include "pico/stdlib.h" #include "hardware/adc.h" #include "hardware/pwm.h" -int dsp_init(); +void dsp_init(); #endif diff --git a/lcd.c b/lcd.c index 6522686..55c44b0 100644 --- a/lcd.c +++ b/lcd.c @@ -1,4 +1,9 @@ /* + * lcd.c + * + * Created: Mar 2021 + * Author: Arjan te Marvelde + * * Grove 16x2 LCD, HD44780 chip with JHD1804 I2C interface * Display RAM addresses 0x00-0x1f for top row and 0x40-0x5f for bottom row * diff --git a/lcd.h b/lcd.h index 4c834fc..1f7bd01 100644 --- a/lcd.h +++ b/lcd.h @@ -1,5 +1,13 @@ #ifndef __LCD_H__ #define __LCD_H__ +/* + * lcd.h + * + * Created: Mar 2021 + * Author: Arjan te Marvelde + * + * See lcd.c for more information + */ #include "pico/stdlib.h" #include "hardware/i2c.h" diff --git a/monitor.c b/monitor.c new file mode 100644 index 0000000..04797df --- /dev/null +++ b/monitor.c @@ -0,0 +1,92 @@ +/* + * monitor.c + * + * Created: Mar 2021 + * Author: Arjan te Marvelde + * + * Command shell on stdin/stdout. + * Collects characters and parses commandstring. + */ + +#include +#include +#include "pico/stdlib.h" + +#include "si5351.h" +#include "monitor.h" + +/* Monitor definitions */ +#define ENDSTDIN 255 +#define CR 13 +#define LF 10 +#define CMD_LEN 32 + + +char mon_cmd[CMD_LEN+1]; + + +uint8_t si5351_reg[200]; + +/* Commandstring parser */ +char delim[] = " "; +#define NCMD 3 +char *shell[NCMD] = {"si", "fa", "fb"}; +void mon_parse(char* s) +{ + char *p; + int base, nreg, i; + + p = strtok(s, delim); // Get command part of string + for (i=0; i "); // prompt + break; + default: + if ((c<32)||(c>=128)) break; // Alfanumeric? + putchar((char)c); // echo character + mon_cmd[i] = (char)c; // store in command string + if (i #include #include "pico/stdlib.h" @@ -190,6 +175,7 @@ Control Si5351: vfo_t vfo[2]; // 0: clk0 and clk1 1: clk2 +/* read contents of SI5351 registers, from reg to reg+len-1, output in data array */ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len) { int ret; @@ -304,7 +290,7 @@ void si_setmsi(uint8_t i) // If in range, just set MSN registers // If not in range, recalculate MSi and Ri and also MSN // Set MSN, MSi and Ri registers (implicitly resets PLL) -void vfo_evaluate(void) +void si_evaluate(void) { float msn; @@ -342,7 +328,7 @@ void vfo_evaluate(void) // Initialize the Si5351 VFO registers -void vfo_init(void) +void si_init(void) { uint8_t data[16]; // I2C trx buffer @@ -358,7 +344,7 @@ void vfo_init(void) // MSN=27.2 P1=2969, P2=600000, P3=1000000 vfo[0].freq = 10000000; vfo[0].flag = 0; - vfo[0].phase = 2; + vfo[0].phase = 1; vfo[0].ri = 1; vfo[0].msi = 68; vfo[0].msn = 27.2; @@ -434,6 +420,5 @@ void vfo_init(void) data[0] = SI_CLK_OE; data[1] = 0x00; i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); - } diff --git a/si5351.h b/si5351.h index f83679f..2778418 100644 --- a/si5351.h +++ b/si5351.h @@ -3,8 +3,11 @@ /* * si5351.h * - * Created: 13 March 2021 + * Created: March 2021 * Author: Arjan + * + * See si5351.c for more information + * */ @@ -21,13 +24,13 @@ typedef struct extern vfo_t vfo[2]; // Table contains all control data for three clk outputs, but 0 and 1 are coupled in vfo[0] int si_getreg(uint8_t *data, uint8_t reg, uint8_t len); -void vfo_init(void); -void vfo_evaluate(void); - +void si_init(void); +void si_evaluate(void); #define SI_GETFREQ(i) ((((i)>=0)&&((i)<2))?vfo[(i)].freq:0) #define SI_INCFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)<(150000000-(d)))) { vfo[(i)].freq += (d); vfo[(i)].flag = 1;} #define SI_DECFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)>(d))) { (vfo[(i)].freq) -= (d); vfo[(i)].flag = 1;} #define SI_SETFREQ(i, f) if ((((i)>=0)&&((i)<2))&&((f)<150000000)) { vfo[(i)].freq = (f); vfo[(i)].flag = 1;} +#define SI_SETPHASE(i, p) if (((i)>=0)&&((i)<2)) {vfo[(i)].phase = ((uint8_t)p)&3; vfo[(i)].flag = 1;} #endif /* _SI5351_H */ \ No newline at end of file diff --git a/uSDR.c b/uSDR.c index f7b2dd2..32944c0 100644 --- a/uSDR.c +++ b/uSDR.c @@ -1,7 +1,21 @@ +/* + * uSDR.c + * + * Created: Mar 2021 + * Author: Arjan te Marvelde + * + * The main loop of the application. + * This initializes the units that do the actual work, and then loops in the background. + * Other units are: + * - dsp.c, containing all signal processing in RX and TX branches. This part runs on the second processor core. + * - si5351.c, containing all controls for setting up the si5351 clock generator. + * - lcd.c, contains all functions to put something on the LCD + * - hmi.c, contains all functions that handle user inputs + */ + #include #include #include "pico/stdlib.h" -#include "hardware/pwm.h" #include "hardware/pio.h" #include "hardware/timer.h" #include "hardware/clocks.h" @@ -9,20 +23,13 @@ #include "lcd.h" #include "dsp.h" #include "si5351.h" +#include "monitor.h" - -/* Monitor definitions */ -#define ENDSTDIN 255 -#define CR 13 -#define LF 10 -#define CMD_LEN 32 - uint8_t display1[16]; uint8_t display2[16]; - /* LED TIMER definition and callback */ struct repeating_timer led_timer; bool led_callback(struct repeating_timer *t) @@ -35,44 +42,8 @@ bool led_callback(struct repeating_timer *t) } -uint8_t si5351_reg[200]; -char delim[] = " "; -#define NCMD 3 -char *shell[NCMD] = {"si", "fa", "fb"}; -void mon_parse(char* s) -{ - char *p; - int base, nreg, i; - - p = strtok(s, delim); // Get command part of string - for (i=0; i "); - int c, i=0; - char mon_cmd[CMD_LEN+1]; while (1) { /* Check for monitor input */ - c = getchar_timeout_us(100000); // 1 try per 100 msec - switch (c) - { - case PICO_ERROR_TIMEOUT: // just go-on - break; - case CR: // CR or LF: - case LF: // need to parse command string - putchar((char)c); // echo character - if (i==0) break; // already did a parse, only do it once - mon_cmd[i] = 0; // terminate command string - i=0; // reset index - mon_parse(mon_cmd); // process command - printf("Pico> "); // prompt - break; - default: - if ((c<32)||(c>=128)) break; // Alfanumeric? - putchar((char)c); // echo character - mon_cmd[i] = (char)c; // store in command string - if (i