Multicore support

Added multicore support
pull/13/head
ArjanteMarvelde 2021-04-05 21:18:30 +02:00
rodzic 6238c98a3e
commit a060b06065
10 zmienionych plików z 224 dodań i 131 usunięć

Wyświetl plik

@ -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

78
dsp.c
Wyświetl plik

@ -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);
}

10
dsp.h
Wyświetl plik

@ -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

5
lcd.c
Wyświetl plik

@ -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
*

8
lcd.h
Wyświetl plik

@ -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"

92
monitor.c 100644
Wyświetl plik

@ -0,0 +1,92 @@
/*
* monitor.c
*
* Created: Mar 2021
* Author: Arjan te Marvelde
*
* Command shell on stdin/stdout.
* Collects characters and parses commandstring.
*/
#include <stdio.h>
#include <string.h>
#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<NCMD; i++)
if (strcmp(p, shell[i]) == 0) break;
switch(i)
{
case 0:
// Next: p = strtok(NULL, delim); (returns NULL if none left)
for (i=0; i<nreg; i++) si5351_reg[i] = 0xaa;
si_getreg(si5351_reg, (uint8_t)base, (uint8_t)nreg);
for (i=0; i<nreg; i++) printf("%02x ",(int)(si5351_reg[i]));
printf("\n");
break;
case 1:
printf("%s\n", p);
break;
case 2:
printf("%s\n", p);
break;
default:
break;
}
}
void mon_init()
{
/* Initialize IOs */
stdio_init_all();
}
void mon_read(uint32_t timeout)
{
int i = 0;
int c = getchar_timeout_us(timeout);
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<CMD_LEN) i++; // check range and increment
break;
}
}

15
monitor.h 100644
Wyświetl plik

@ -0,0 +1,15 @@
#ifndef __MONITOR_H__
#define __MONITOR_H__
/*
* monitor.h
*
* Created: Mar 2021
* Author: Arjan te Marvelde
*
* See monitor.c for more information
*/
void mon_init();
void mon_read(uint32_t timeout);
#endif

Wyświetl plik

@ -1,8 +1,8 @@
/*
* si5351.c
*
* Created: 12 Jan 2020 21:45:00
* Author: Arjan
* Created: Jan 2020
* Author: Arjan
Si5351 principle:
=================
@ -117,21 +117,6 @@ Control Si5351:
*/
/*
Implicit type conversion precedence:
- long double
- double
- float
- unsigned long int
- long int
- unsigned int
- int
- other
conversion is always to highest type, this is also the result of an operation.
Maximum UL = 4,294,967,295 (0xffffffff)
*/
#include <stdio.h>
#include <math.h>
#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);
}

Wyświetl plik

@ -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 */

102
uSDR.c
Wyświetl plik

@ -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 <stdio.h>
#include <string.h>
#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<NCMD; i++)
if (strcmp(p, shell[i]) == 0) break;
switch(i)
{
case 0:
// Next: p = strtok(NULL, delim); (returns NULL if none left)
for (i=0; i<nreg; i++) si5351_reg[i] = 0xaa;
si_getreg(si5351_reg, (uint8_t)base, (uint8_t)nreg);
for (i=0; i<nreg; i++) printf("%02x ",(int)(si5351_reg[i]));
printf("\n");
break;
case 1:
printf("%s\n", p);
break;
case 2:
printf("%s\n", p);
break;
default:
break;
}
}
int main()
{
/* Initialize IOs */
stdio_init_all();
/* Initialize LED pin output */
gpio_init(PICO_DEFAULT_LED_PIN);
@ -80,51 +51,24 @@ int main()
gpio_put(PICO_DEFAULT_LED_PIN, true); // Set LED on
add_repeating_timer_ms(-1000, led_callback, NULL, &led_timer);
/* Initialize PWM */
dsp_init();
/* Initialize units */
si_init(); // VFO control unit
lcd_init(); // LCD output unit
dsp_init(); // Signal processing unit
/* Initialize Si5351 vfo */
vfo_init();
/* Initialize LCD */
lcd_init();
lcd_ctrl(LCD_GOTO, 0, 0);
sprintf(display1, "A: 7074.0 kHz");
lcd_write(display1,13);
SI_SETFREQ(0, 2*7074000UL); // Set freq to 2*7074 kHz
SI_SETPHASE(0,2); // Set phase to 180deg
/* Initialize monitor terminal */
printf("Pico> ");
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<CMD_LEN) i++; // check range and increment
break;
}
mon_read(100000L); // Wait max 100msec
/* Check whether VFO settings have changed */
vfo_evaluate();
si_evaluate();
}
return 0;