kopia lustrzana https://github.com/ArjanteMarvelde/uSDR-pico
rodzic
6238c98a3e
commit
a060b06065
|
@ -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
78
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);
|
||||
}
|
||||
|
||||
|
|
10
dsp.h
10
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
|
||||
|
|
5
lcd.c
5
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
|
||||
*
|
||||
|
|
8
lcd.h
8
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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
27
si5351.c
27
si5351.c
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
11
si5351.h
11
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 */
|
102
uSDR.c
102
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 <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;
|
||||
|
|
Ładowanie…
Reference in New Issue