More or less stable version
pull/13/head
ArjanteMarvelde 2021-11-24 13:33:08 +01:00
rodzic 493756488a
commit 82d6dd841b
15 zmienionych plików z 578 dodań i 211 usunięć

Wyświetl plik

@ -19,7 +19,7 @@ pico_sdk_init()
# Add executable. Default name is the project name, version 0.1 # Add executable. Default name is the project name, version 0.1
add_executable(uSDR uSDR.c lcd.c si5351.c dsp.c hmi.c monitor.c) add_executable(uSDR uSDR.c lcd.c si5351.c dsp.c hmi.c monitor.c relay.c)
pico_set_program_name(uSDR "uSDR") pico_set_program_name(uSDR "uSDR")
pico_set_program_version(uSDR "0.1") pico_set_program_version(uSDR "0.1")

BIN
CODEv2.zip 100644

Plik binarny nie jest wyświetlany.

BIN
PCBv2.zip 100644

Plik binarny nie jest wyświetlany.

185
dsp.c
Wyświetl plik

@ -37,6 +37,7 @@
#define ADC0_IRQ_FIFO 22 #define ADC0_IRQ_FIFO 22
#include "dsp.h" #include "dsp.h"
#include "hmi.h"
/* /*
@ -64,10 +65,10 @@
/* /*
* AGC reference level is log2(64) = 6, where 64 is the MSB of half DAC_RANGE * AGC reference level is log2(0x40) = 6, where 0x40 is the MSB of half DAC_RANGE
* 1/AGC_DECAY and 1/AGC_ATTACK are multipliers before agc_gain value integrator * 1/AGC_DECAY and 1/AGC_ATTACK are multipliers before agc_gain value integrator
* These values should ultimately be set by the HMI. * These values should ultimately be set by the HMI.
* The time it takes to effect in a gain change is the ( (Set time)/(signal delta) ) / samplerate * The time it takes to a gain change is the ( (Set time)/(signal delta) ) / samplerate
* So when delta is 1, and attack is 64, the time is 64/15625 = 4msec (fast attack) * So when delta is 1, and attack is 64, the time is 64/15625 = 4msec (fast attack)
* The decay time is about 100x this value * The decay time is about 100x this value
* Slow attack would be about 4096 * Slow attack would be about 4096
@ -76,7 +77,7 @@
#define AGC_DECAY 8192 #define AGC_DECAY 8192
#define AGC_FAST 64 #define AGC_FAST 64
#define AGC_SLOW 4096 #define AGC_SLOW 4096
#define AGC_OFF 65534 #define AGC_OFF 32766
volatile uint16_t agc_decay = AGC_OFF; volatile uint16_t agc_decay = AGC_OFF;
volatile uint16_t agc_attack = AGC_OFF; volatile uint16_t agc_attack = AGC_OFF;
void dsp_setagc(int agc) void dsp_setagc(int agc)
@ -102,7 +103,7 @@ void dsp_setagc(int agc)
* MODE is modulation/demodulation * MODE is modulation/demodulation
* This setting steers the signal processing branch chosen * This setting steers the signal processing branch chosen
*/ */
volatile uint16_t dsp_mode; // For values see hmi.c volatile uint16_t dsp_mode; // For values see hmi.c, assume {USB,LSB,AM,CW}
void dsp_setmode(int mode) void dsp_setmode(int mode)
{ {
dsp_mode = (uint16_t)mode; dsp_mode = (uint16_t)mode;
@ -110,6 +111,39 @@ void dsp_setmode(int mode)
/*
* VOX LINGER is the number of 16us cycles to wait before releasing TX mode
* The level of detection is related to the maximum ADC range.
*/
#define VOX_LINGER 500000/16
#define VOX_HIGH ADC_BIAS/2
#define VOX_MEDIUM ADC_BIAS/4
#define VOX_LOW ADC_BIAS/16
#define VOX_OFF 0
volatile uint16_t vox_count;
volatile uint16_t vox_level = VOX_OFF;
void dsp_setvox(int vox)
{
switch(vox)
{
case 1:
vox_level = VOX_LOW;
break;
case 2:
vox_level = VOX_MEDIUM;
break;
case 3:
vox_level = VOX_HIGH;
break;
default:
vox_level = VOX_OFF;
vox_count = 0;
break;
}
}
/* /*
* Low pass filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/) * Low pass filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/)
* Settings: sample rates 62500, 31250 or 15625 Hz, stopband -40dB, passband ripple 5dB * Settings: sample rates 62500, 31250 or 15625 Hz, stopband -40dB, passband ripple 5dB
@ -161,10 +195,12 @@ void adcfifo_handler(void)
*/ */
volatile int16_t i_s_raw[15], q_s_raw[15]; // Raw I/Q samples minus DC bias volatile int16_t i_s_raw[15], q_s_raw[15]; // Raw I/Q samples minus DC bias
volatile uint16_t peak=0; // Peak detector running value volatile uint16_t peak=0; // Peak detector running value
volatile int16_t agc_gain=0, agc_accu=0; // AGC gain (shift value), log peak level integrator volatile int16_t agc_gain=0; // AGC gain (left-shift value)
volatile int16_t agc_accu=0; // Log peak level integrator
volatile int16_t i_s[15], q_s[15]; // Filtered I/Q samples volatile int16_t i_s[15], q_s[15]; // Filtered I/Q samples
volatile int16_t i_dc, q_dc; // DC bias for I/Q channel volatile int16_t i_dc, q_dc; // DC bias for I/Q channel
volatile int rx_cnt=0; // Decimation counter volatile int rx_cnt=0; // Decimation counter
bool rx(void) bool rx(void)
{ {
int16_t q_sample, i_sample, a_sample; int16_t q_sample, i_sample, a_sample;
@ -175,8 +211,8 @@ bool rx(void)
/*** SAMPLING ***/ /*** SAMPLING ***/
i_sample = adc_result[0]; // Take last ADC 0 result q_sample = adc_result[0]; // Take last ADC 0 result, connected to Q input
q_sample = adc_result[1]; // Take last ADC 1 result i_sample = adc_result[1]; // Take last ADC 1 result, connected to I input
/* /*
* Remove DC and store new sample * Remove DC and store new sample
@ -184,25 +220,26 @@ bool rx(void)
* Amplitude of samples should fit inside [-2048, 2047] * Amplitude of samples should fit inside [-2048, 2047]
*/ */
q_sample = (q_sample&0x0fff) - ADC_BIAS; // Clip to 12 bits and subtract mid-range q_sample = (q_sample&0x0fff) - ADC_BIAS; // Clip to 12 bits and subtract mid-range
q_dc += (q_sample>>7) - (q_dc>>7); // then IIR running average q_dc += q_sample/128 - q_dc/128; // then IIR running average
q_sample -= q_dc; // and subtract DC q_sample -= q_dc; // and subtract DC
i_sample = (i_sample&0x0fff) - ADC_BIAS; // Same for I sample i_sample = (i_sample&0x0fff) - ADC_BIAS; // Same for I sample
i_dc += (i_sample>>7) - (i_dc>>7); i_dc += i_sample/128 - i_dc/128;
i_sample -= i_dc; i_sample -= i_dc;
/* /*
* Shift with AGC feedback from AUDIO GENERATION stage * Shift with AGC feedback from AUDIO GENERATION stage
* Note: bitshift does not work with negative numbers, so need to MPY/DIV
* This behavior in essence is exponential, complementing the logarithmic peak detector * This behavior in essence is exponential, complementing the logarithmic peak detector
*/ */
if (agc_gain > 0) if (agc_gain > 0)
{ {
q_sample <<= agc_gain; q_sample = q_sample * (1<<agc_gain);
i_sample <<= agc_gain; i_sample = i_sample * (1<<agc_gain);
} }
else if (agc_gain < 0) else if (agc_gain < 0)
{ {
q_sample >>= -agc_gain; q_sample = q_sample / (1<<(-agc_gain));
i_sample >>= -agc_gain; i_sample = i_sample / (1<<(-agc_gain));
} }
/* /*
@ -235,8 +272,8 @@ bool rx(void)
q_accu += (int32_t)q_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling q_accu += (int32_t)q_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling
i_accu += (int32_t)i_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling i_accu += (int32_t)i_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling
} }
q_accu >>= 8; q_accu = q_accu/256;
i_accu >>= 8; i_accu = i_accu/256;
q_s[14] = q_accu; q_s[14] = q_accu;
i_s[14] = i_accu; i_s[14] = i_accu;
@ -247,20 +284,20 @@ bool rx(void)
{ {
case 0: //USB case 0: //USB
/* /*
* USB demodulate: I[7] - Qh, * USB demodulate: I[7] - Qh,
* Qh is Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills calculator) * Qh is Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills calculator)
*/ */
q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L + (q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L; q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L + (q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L;
qh = q_accu >> 12; qh = q_accu / 4096L;
a_sample = i_s[7] - qh; a_sample = i_s[7] - qh;
break; break;
case 1: //LSB case 1: //LSB
/* /*
* USB demodulate: I[7] - Qh, * LSB demodulate: I[7] + Qh,
* Qh is Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills calculator) * Qh is Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills calculator)
*/ */
q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L + (q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L; q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L + (q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L;
qh = q_accu >> 12; qh = q_accu / 4096L;
a_sample = i_s[7] + qh; a_sample = i_s[7] + qh;
break; break;
case 2: //AM case 2: //AM
@ -277,9 +314,9 @@ bool rx(void)
/*** AUDIO GENERATION ***/ /*** AUDIO GENERATION ***/
/* /*
* AGC, peak detector * AGC, peak detector
* Sample speed is still 15625 per second * Sample speed is 15625 per second
*/ */
peak = (127*peak + (ABS(a_sample)))>>7; // Running average level detect, a=1/128 peak += (ABS(a_sample))/128 - peak/128; // Running average level detect, a=1/128
k=0; i=peak; // Logarithmic peak detection k=0; i=peak; // Logarithmic peak detection
if (i&0xff00) {k+=8; i>>=8;} // k=log2(peak), find highest bit set if (i&0xff00) {k+=8; i>>=8;} // k=log2(peak), find highest bit set
if (i&0x00f0) {k+=4; i>>=4;} if (i&0x00f0) {k+=4; i>>=4;}
@ -295,7 +332,6 @@ bool rx(void)
agc_gain++; // Increase gain agc_gain++; // Increase gain
agc_accu += agc_decay; // Reset integrator agc_accu += agc_decay; // Reset integrator
} }
/* /*
* Scale and clip output, * Scale and clip output,
@ -315,49 +351,83 @@ bool rx(void)
/* /*
* CORE1: * CORE1:
* Execute TX branch signal processing * The VOX function is called separately every cycle, to check audio level.
* Execute TX branch signal processing when tx enabled
*/ */
volatile int16_t a_s_raw[15]; // Raw samples, minus DC bias volatile int16_t a_s_raw[15]; // Raw samples, minus DC bias
volatile int16_t a_level=0; // Average level of raw sample stream
volatile int16_t a_s[15]; // Filtered and decimated samples volatile int16_t a_s[15]; // Filtered and decimated samples
volatile int16_t a_dc; // DC level volatile int16_t a_dc; // DC level
volatile int tx_cnt=0; // Decimation counter volatile int tx_cnt=0; // Decimation counter
bool vox(void)
{
int16_t a_sample;
int i;
/*
* Get sample and shift into delay line
*/
a_sample = adc_result[2]; // Get latest ADC 2 result
/*
* Remove DC and store new raw sample
* IIR filter: dc = a*sample + (1-a)*dc where a = 1/128
*/
a_sample = (a_sample&0x0fff) - ADC_BIAS; // Clip and subtract mid-range
a_dc += (a_sample - a_dc)/128; // then IIR running average
a_sample -= a_dc; // subtract DC
for (i=0; i<14; i++) // and store in shift register
a_s_raw[i] = a_s_raw[i+1];
a_s_raw[14] = a_sample;
/*
* Detect level of audio signal
* Return true if VOX enabled and:
* - Audio level higher than threshold
* - Linger time sill active
*/
if (a_sample<0) a_sample = -a_sample; // Absolute value
a_level += (a_sample - a_level)/128; // running average, 16usec * 128 = 2msec
if (vox_level != VOX_OFF)
{
if (a_level > vox_level)
{
vox_count = VOX_LINGER;
return(true);
}
if (vox_count>0)
{
vox_count--;
return(true);
}
}
return(false);
}
bool tx(void) bool tx(void)
{ {
static int tx_phase = 0;
int16_t a_sample;
int32_t a_accu; int32_t a_accu;
int16_t qh; int16_t qh;
int i; int i;
/* /*** RAW Audio SAMPLES from VOX function ***/
* Get sample and shift into delay line
*/
a_sample = adc_result[2]; // Take last ADC 2 result
for (i=0; i<14; i++)
a_s_raw[i] = a_s_raw[i+1]; // Audio raw samples shift register
/*
* Remove DC and store new sample
* IIR filter: dc = a*sample + (1-a)*dc where a = 1/128
*/
a_sample = (a_sample&0x0fff) - ADC_BIAS; // Clip and subtract mid-range
a_dc = (a_sample>>7) + a_dc - (a_dc>>7); // then IIR running average
a_s_raw[14] = a_sample - a_dc; // and subtract DC, store in shift register
/* /*
* Low pass filter + decimation * Low pass filter + decimation
*/ */
tx_cnt = (tx_cnt+1)&3; // Calculate only every fourth sample tx_cnt = (tx_cnt+1)&3; // Calculate only every fourth sample
if (tx_cnt>0) return true; if (tx_cnt>0) return true; // So effective sample rate will be 15625Hz
for (i=0; i<14; i++) // Shift decimated samples for (i=0; i<14; i++) // Shift decimated samples
a_s[i] = a_s[i+1]; a_s[i] = a_s[i+1];
a_accu = 0; // Initialize accumulator a_accu = 0; // Initialize accumulator
for (i=0; i<15; i++) // Low pass FIR filter for (i=0; i<15; i++) // Low pass FIR filter, using raw samples
a_accu += (int32_t)a_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz sampling a_accu += (int32_t)a_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz sampling
a_s[14] = a_accu >> 8; a_s[14] = a_accu / 256;
/* /*
* From here things get dependent on transmit mode. * From here things get dependent on transmit mode.
@ -368,14 +438,14 @@ bool tx(void)
* Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills): * Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills):
*/ */
a_accu = (a_s[0]-a_s[14])*315L + (a_s[2]-a_s[12])*440L + (a_s[4]-a_s[10])*734L + (a_s[6]-a_s[ 8])*2202L; a_accu = (a_s[0]-a_s[14])*315L + (a_s[2]-a_s[12])*440L + (a_s[4]-a_s[10])*734L + (a_s[6]-a_s[ 8])*2202L;
qh = (int16_t)(a_accu >> 12); qh = (int16_t)(a_accu / 4096);
/* /*
* Write I and Q to QSE DACs, phase is 7 back. * Write I and Q to QSE DACs, phase is 7 back.
* Need to multiply AC with DAC_RANGE/ADC_RANGE (appr 1/16, but compensate for losses) * Need to multiply AC with DAC_RANGE/ADC_RANGE (appr 1/16, but compensate for losses)
*/ */
pwm_set_chan_level(dac_iq, PWM_CHAN_A, DAC_BIAS + (a_s[7]/4)); pwm_set_chan_level(dac_iq, PWM_CHAN_A, DAC_BIAS + (qh/4));
pwm_set_chan_level(dac_iq, PWM_CHAN_B, DAC_BIAS + (qh/4)); pwm_set_chan_level(dac_iq, PWM_CHAN_B, DAC_BIAS + (a_s[7]/4));
return true; return true;
} }
@ -398,8 +468,8 @@ void dsp_loop()
fifo_incnt++; fifo_incnt++;
/* Initialize DACs */ /* Initialize DACs */
gpio_set_function(20, GPIO_FUNC_PWM); // GP20 is PWM for I DAC (Slice 2, Channel A) gpio_set_function(20, GPIO_FUNC_PWM); // GP20 is PWM for Q DAC (Slice 2, Channel A)
gpio_set_function(21, GPIO_FUNC_PWM); // GP21 is PWM for Q DAC (Slice 2, Channel B) gpio_set_function(21, GPIO_FUNC_PWM); // GP21 is PWM for I DAC (Slice 2, Channel B)
dac_iq = pwm_gpio_to_slice_num(20); // Get PWM slice for GP20 (Same for GP21) dac_iq = pwm_gpio_to_slice_num(20); // Get PWM slice for GP20 (Same for GP21)
pwm_set_clkdiv_int_frac (dac_iq, 1, 0); // clock divide by 1 pwm_set_clkdiv_int_frac (dac_iq, 1, 0); // clock divide by 1
pwm_set_wrap(dac_iq, DAC_RANGE-1); // Set cycle length pwm_set_wrap(dac_iq, DAC_RANGE-1); // Set cycle length
@ -414,8 +484,8 @@ void dsp_loop()
/* Initialize ADCs */ /* Initialize ADCs */
adc_init(); // Initialize ADC to known state adc_init(); // Initialize ADC to known state
adc_set_clkdiv(0); // Fastest clock (500 kSps) adc_set_clkdiv(0); // Fastest clock (500 kSps)
adc_gpio_init(26); // GP26 is ADC 0 for I channel adc_gpio_init(26); // GP26 is ADC 0 for Q channel
adc_gpio_init(27); // GP27 is ADC 1 for Q channel adc_gpio_init(27); // GP27 is ADC 1 for I channel
adc_gpio_init(28); // GP28 is ADC 2 for Audio channel adc_gpio_init(28); // GP28 is ADC 2 for Audio channel
adc_select_input(0); // Start with ADC0 adc_select_input(0); // Start with ADC0
adc_next = 0; adc_next = 0;
@ -434,18 +504,11 @@ void dsp_loop()
while(1) while(1)
{ {
cmd = multicore_fifo_pop_blocking(); // Wait for fifo output cmd = multicore_fifo_pop_blocking(); // Wait for fifo output
if (cmd == DSP_RX) tx_enabled = ptt_active || vox(); // Sample audio and check level
{ if (tx_enabled)
fifo_rx++;
rx();
}
else if (cmd == DSP_TX)
{
fifo_tx++;
tx(); tx();
}
else else
fifo_xx++; rx();
if (multicore_fifo_rvalid()) if (multicore_fifo_rvalid())
fifo_overrun++; // Check for missed events fifo_overrun++; // Check for missed events
} }

1
dsp.h
Wyświetl plik

@ -16,6 +16,7 @@
void dsp_setagc(int agc); void dsp_setagc(int agc);
void dsp_setmode(int mode); void dsp_setmode(int mode);
void dsp_setvox(int vox);
extern volatile bool tx_enabled; extern volatile bool tx_enabled;
#define DSP_SETPTT(on) tx_enabled = (on) #define DSP_SETPTT(on) tx_enabled = (on)

127
hmi.c
Wyświetl plik

@ -37,6 +37,7 @@
#include "hmi.h" #include "hmi.h"
#include "dsp.h" #include "dsp.h"
#include "si5351.h" #include "si5351.h"
#include "relay.h"
/* /*
* GPIO assignments * GPIO assignments
@ -66,11 +67,12 @@
* using Left/Right for digit and ENC for value, Enter to commit change. * using Left/Right for digit and ENC for value, Enter to commit change.
* Press ESC to enter the submenu states (there is only one sub menu level): * Press ESC to enter the submenu states (there is only one sub menu level):
* *
* Submenu Values ENC Enter Escape Left Right * Submenu Values ENC Enter Escape Left Right
* ------------------------------------------------------------------------------------- * -----------------------------------------------------------------------------------------------
* Mode USB, LSB, AM, CW change commit exit prev next * Mode USB, LSB, AM, CW change commit exit prev next
* AGC Fast, Slow, Off change commit exit prev next * AGC Fast, Slow, Off change commit exit prev next
* Pre +10dB, 0, -10dB, -20dB change commit exit prev next * Pre +10dB, 0, -10dB, -20dB, -30dB change commit exit prev next
* Vox NoVOX, Low, Medium, High change commit exit prev next
* *
* --will be extended-- * --will be extended--
*/ */
@ -80,7 +82,9 @@
#define HMI_S_MODE 1 #define HMI_S_MODE 1
#define HMI_S_AGC 2 #define HMI_S_AGC 2
#define HMI_S_PRE 3 #define HMI_S_PRE 3
#define HMI_NSTATES 4 #define HMI_S_VOX 4
#define HMI_S_BPF 5
#define HMI_NSTATES 6
/* Event definitions */ /* Event definitions */
#define HMI_E_NOEVENT 0 #define HMI_E_NOEVENT 0
@ -97,14 +101,18 @@
/* Sub menu option string sets */ /* Sub menu option string sets */
#define HMI_NMODE 4 #define HMI_NMODE 4
#define HMI_NAGC 3 #define HMI_NAGC 3
#define HMI_NPRE 4 #define HMI_NPRE 5
char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC ","Pre "}; // Indexed by hmi_state #define HMI_NVOX 4
char hmi_o_mode[HMI_NMODE][8] = {"USB", "LSB", "AM ", "CW "}; // Indexed by hmi_sub[HMI_S_MODE] #define HMI_NBPF 5
char hmi_o_agc [HMI_NAGC][8] = {"NoGC", "Slow", "Fast"}; // Indexed by hmi_sub[HMI_S_AGC] char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC","Pre","VOX"}; // Indexed by hmi_state
char hmi_o_pre [HMI_NPRE][8] = {"-20dB", "-10dB", "0dB", "+10dB"}; // Indexed by hmi_sub[HMI_S_PRE] char hmi_o_mode[HMI_NMODE][8] = {"USB","LSB","AM","CW"}; // Indexed by hmi_sub[HMI_S_MODE]
char hmi_o_agc [HMI_NAGC][8] = {"NoGC","Slow","Fast"}; // Indexed by hmi_sub[HMI_S_AGC]
char hmi_o_pre [HMI_NPRE][8] = {"-30dB","-20dB","-10dB","0dB","+10dB"}; // Indexed by hmi_sub[HMI_S_PRE]
char hmi_o_vox [HMI_NVOX][8] = {"NoVOX","VOX-L","VOX-M","VOX-H"}; // Indexed by hmi_sub[HMI_S_VOX]
char hmi_o_test[HMI_NBPF][8] = {"<2.5","2-6","5-12","10-24","20-40"};
uint8_t hmi_state, hmi_option; // Current state and option selection uint8_t hmi_state, hmi_option; // Current state and option selection
uint8_t hmi_sub[HMI_NSTATES] = {4,0,0,0}; // Stored option selection per state uint8_t hmi_sub[HMI_NSTATES] = {4,0,0,3,0,0}; // Stored option selection per state
uint32_t hmi_freq; // Frequency from Tune state uint32_t hmi_freq; // Frequency from Tune state
uint32_t hmi_step[6] = {10000000, 1000000, 100000, 10000, 1000, 100}; // Frequency digit increments uint32_t hmi_step[6] = {10000000, 1000000, 100000, 10000, 1000, 100}; // Frequency digit increments
@ -113,6 +121,10 @@ uint32_t hmi_step[6] = {10000000, 1000000, 100000, 10000, 1000, 100}; // Frequen
#define HMI_MULFREQ 1 // Factor between HMI and actual frequency #define HMI_MULFREQ 1 // Factor between HMI and actual frequency
// Set to 2 for certain types of mixer // Set to 2 for certain types of mixer
#define PTT_DEBOUNCE 3 // Nr of cycles for debounce
int ptt_state; // Debounce counter
bool ptt_active; // Resulting state
/* /*
* Some macros * Some macros
*/ */
@ -201,7 +213,11 @@ void hmi_handler(uint8_t event)
case HMI_S_PRE: case HMI_S_PRE:
if (event==HMI_E_ENTER) if (event==HMI_E_ENTER)
{ {
// Set PRE if (hmi_option == 0) relay_setattn(0x03); // {"-30dB","-20dB","-10dB","0dB","+10dB"}
if (hmi_option == 1) relay_setattn(0x01);
if (hmi_option == 2) relay_setattn(0x02);
if (hmi_option == 3) relay_setattn(0x00);
if (hmi_option == 4) relay_setattn(0x04);
hmi_sub[hmi_state] = hmi_option; // Store selected option hmi_sub[hmi_state] = hmi_option; // Store selected option
} }
if (event==HMI_E_INCREMENT) if (event==HMI_E_INCREMENT)
@ -213,6 +229,40 @@ void hmi_handler(uint8_t event)
hmi_option = (hmi_option>0)?hmi_option-1:0; hmi_option = (hmi_option>0)?hmi_option-1:0;
} }
break; break;
case HMI_S_VOX:
if (event==HMI_E_ENTER)
{
dsp_setvox(hmi_option);
hmi_sub[hmi_state] = hmi_option; // Store selected option
}
if (event==HMI_E_INCREMENT)
{
hmi_option = (hmi_option<HMI_NVOX-1)?hmi_option+1:HMI_NVOX-1;
}
if (event==HMI_E_DECREMENT)
{
hmi_option = (hmi_option>0)?hmi_option-1:0;
}
break;
case HMI_S_BPF:
if (event==HMI_E_ENTER)
{
if (hmi_option == 0) relay_setattn(0x01); // {"<2.5","2-6","5-12","10-24","20-40"}
if (hmi_option == 1) relay_setattn(0x02);
if (hmi_option == 2) relay_setattn(0x04);
if (hmi_option == 3) relay_setattn(0x08);
if (hmi_option == 4) relay_setattn(0x10);
hmi_sub[hmi_state] = hmi_option; // Store selected option
}
if (event==HMI_E_INCREMENT)
{
hmi_option = (hmi_option<HMI_NBPF-1)?hmi_option+1:HMI_NBPF-1;
}
if (event==HMI_E_DECREMENT)
{
hmi_option = (hmi_option>0)?hmi_option-1:0;
}
break;
} }
/* General actions for submenus */ /* General actions for submenus */
@ -263,12 +313,16 @@ void hmi_callback(uint gpio, uint32_t events)
if (events&GPIO_IRQ_EDGE_FALL) if (events&GPIO_IRQ_EDGE_FALL)
evt = HMI_E_RIGHT; evt = HMI_E_RIGHT;
break; break;
/*
case GP_PTT: // PTT case GP_PTT: // PTT
if (events&GPIO_IRQ_EDGE_FALL) if (events&GPIO_IRQ_EDGE_FALL)
DSP_SETPTT(true); ptt_active = true;
else else
DSP_SETPTT(false); // This event needs to be detected better, to prevent hanging in TX state
// 10nF also helps suppressing the ripple...
ptt_active = false;
return; return;
*/
default: default:
return; return;
} }
@ -286,7 +340,9 @@ void hmi_init(void)
* The callback handles interrupts for all GPIOs with IRQ enabled. * The callback handles interrupts for all GPIOs with IRQ enabled.
* Level interrupts don't seem to work properly. * Level interrupts don't seem to work properly.
* For debouncing, the GPIO pins should be pulled-up and connected to gnd with 100nF. * For debouncing, the GPIO pins should be pulled-up and connected to gnd with 100nF.
* PTT has separate debouncing logic
*/ */
// Init input GPIOs // Init input GPIOs
gpio_init_mask(GP_MASK_IN); gpio_init_mask(GP_MASK_IN);
@ -305,7 +361,7 @@ void hmi_init(void)
gpio_set_irq_enabled(GP_AUX_1, GPIO_IRQ_EDGE_ALL, true); gpio_set_irq_enabled(GP_AUX_1, GPIO_IRQ_EDGE_ALL, true);
gpio_set_irq_enabled(GP_AUX_2, GPIO_IRQ_EDGE_ALL, true); gpio_set_irq_enabled(GP_AUX_2, GPIO_IRQ_EDGE_ALL, true);
gpio_set_irq_enabled(GP_AUX_3, GPIO_IRQ_EDGE_ALL, true); gpio_set_irq_enabled(GP_AUX_3, GPIO_IRQ_EDGE_ALL, true);
gpio_set_irq_enabled(GP_PTT, GPIO_IRQ_EDGE_ALL, true); //gpio_set_irq_enabled(GP_PTT, GPIO_IRQ_EDGE_ALL, true);
// Set callback, one for all GPIO, not sure about correctness! // Set callback, one for all GPIO, not sure about correctness!
gpio_set_irq_enabled_with_callback(GP_ENC_A, GPIO_IRQ_EDGE_ALL, true, hmi_callback); gpio_set_irq_enabled_with_callback(GP_ENC_A, GPIO_IRQ_EDGE_ALL, true, hmi_callback);
@ -315,8 +371,11 @@ void hmi_init(void)
hmi_option = 4; // Active kHz digit hmi_option = 4; // Active kHz digit
hmi_freq = 7074000UL; // Initial frequency hmi_freq = 7074000UL; // Initial frequency
SI_SETFREQ(0, HMI_MULFREQ*hmi_freq); // Set freq to 7074 kHz SI_SETFREQ(0, HMI_MULFREQ*hmi_freq); // Set freq to 7074 kHz (depends on mixer type)
SI_SETPHASE(0, 1); // Set phase to 90deg SI_SETPHASE(0, 1); // Set phase to 90deg (depends on mixer type)
ptt_state = 0;
ptt_active = false;
} }
/* /*
@ -328,14 +387,14 @@ void hmi_evaluate(void)
char s[32]; char s[32];
// Print top line of display // Print top line of display
sprintf(s, "%s %7.1f %c%3d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, (tx_enabled?'T':'R'),920); sprintf(s, "%s %7.1f %c%3d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, (tx_enabled?0x07:0x06), (tx_enabled?0:920));
lcd_writexy(0,0,s); lcd_writexy(0,0,s);
// Print bottom line of dsiplay, depending on state // Print bottom line of dsiplay, depending on state
switch (hmi_state) switch (hmi_state)
{ {
case HMI_S_TUNE: case HMI_S_TUNE:
sprintf(s, " %s %s", hmi_o_agc[hmi_sub[HMI_S_AGC]], hmi_o_pre[hmi_sub[HMI_S_PRE]]); sprintf(s, "%s %s %s", hmi_o_vox[hmi_sub[HMI_S_VOX]], hmi_o_agc[hmi_sub[HMI_S_AGC]], hmi_o_pre[hmi_sub[HMI_S_PRE]]);
lcd_writexy(0,1,s); lcd_writexy(0,1,s);
lcd_curxy(4+(hmi_option>4?6:hmi_option), 0, true); lcd_curxy(4+(hmi_option>4?6:hmi_option), 0, true);
break; break;
@ -354,10 +413,36 @@ void hmi_evaluate(void)
lcd_writexy(0,1,s); lcd_writexy(0,1,s);
lcd_curxy(8, 1, false); lcd_curxy(8, 1, false);
break; break;
case HMI_S_VOX:
sprintf(s, "Set VOX: %s ", hmi_o_vox[hmi_option]);
lcd_writexy(0,1,s);
lcd_curxy(8, 1, false);
break;
case HMI_S_BPF:
sprintf(s, "Band: %d %s ", hmi_option, hmi_o_test[hmi_option]);
lcd_writexy(0,1,s);
lcd_curxy(8, 1, false);
default: default:
break; break;
} }
SI_SETFREQ(0, hmi_freq); // Set freq to latest /* PTT debouncing */
if (gpio_get(GP_PTT)) // Get PTT level
{
if (ptt_state<PTT_DEBOUNCE) // Increment debounce counter when high
ptt_state++;
}
else
{
if (ptt_state>0) // Decrement debounce counter when low
ptt_state--;
}
if (ptt_state == PTT_DEBOUNCE) // Reset PTT when debonced level high
ptt_active = false;
if (ptt_state == 0) // Set PTT when debounced level low
ptt_active = true;
/* Set freq to latest entered value */
SI_SETFREQ(0, HMI_MULFREQ*hmi_freq);
} }

2
hmi.h
Wyświetl plik

@ -9,6 +9,8 @@
* See hmi.c for more information * See hmi.c for more information
*/ */
extern bool ptt_active;
void hmi_init(void); void hmi_init(void);
void hmi_evaluate(void); void hmi_evaluate(void);

93
lcd.c
Wyświetl plik

@ -83,80 +83,85 @@
#define LCD_DATA 0x40 #define LCD_DATA 0x40
/* I2C address and pins */ /* I2C address and pins */
#define I2C_LCD 0x3E #define I2C_LCD 0x3E
#define I2C0_SDA 16
#define I2C0_SCL 17
/*
uint8_t cgram[65] = // Write CGRAM * User defined characters
{ // 8x8 bytes */
0x80, uint8_t cgram[8][8] = // Write CGRAM
0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x00, {
0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x0b, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00: blank
0x08, 0x10, 0x08, 0x10, 0x08, 0x13, 0x0b, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00}, // 0x01: Level 1
0x08, 0x10, 0x08, 0x10, 0x0b, 0x13, 0x0b, 0x00, {0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x00}, // 0x02: Level 2
0x08, 0x10, 0x08, 0x13, 0x0b, 0x13, 0x0b, 0x00, {0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x03: Level 3
0x08, 0x10, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00, {0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x04: Level 4
0x08, 0x13, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00, {0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x05: Level 5
0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00 {0x00, 0x04, 0x04, 0x04, 0x1f, 0x0e, 0x04, 0x00}, // 0x06: Receive arrow down
{0x04, 0x0e, 0x1f, 0x04, 0x04, 0x04, 0x00, 0x00} // 0x07: Transmit arrow up
}; };
void lcd_init(void) void lcd_init(void)
{ {
uint8_t txdata[8]; uint8_t txdata[10];
uint8_t i;
/* I2C0 initialisation at 400Khz. */
i2c_init(i2c0, 400*1000);
gpio_set_function(I2C0_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C0_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C0_SDA);
gpio_pull_up(I2C0_SCL);
sleep_ms(50); sleep_ms(50);
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
/* Initialize function set (see datasheet fig 23)*/ /* Initialize function set (see datasheet fig 23)*/
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE | LCD_5x8DOTS; txdata[1] = LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE | LCD_5x8DOTS;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(4500); sleep_us(4500);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(100); sleep_us(100);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
/* Initialize display control */ /* Initialize display control */
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYOFF | LCD_CURSOROFF | LCD_BLINKOFF; txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYOFF | LCD_CURSOROFF | LCD_BLINKOFF;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
/* Display clear */ /* Display clear */
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_CLEARDISPLAY; txdata[1] = LCD_CLEARDISPLAY;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(1530); sleep_us(1530);
/* Initialize entry mode set */ /* Initialize entry mode set */
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_ENTRYMODESET | LCD_ENTRYINC | LCD_ENTRYNOSHIFT; txdata[1] = LCD_ENTRYMODESET | LCD_ENTRYINC | LCD_ENTRYNOSHIFT;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
/* Load CGRAM */ /* Load CGRAM */
txdata[1] = 0x40; //Set CGRAM address 0 for (i=0; i<8; i++)
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); {
sleep_us(LCD_DELAY); txdata[0] = LCD_COMMAND;
i2c_write_blocking(i2c0, I2C_LCD, cgram, 65, false); txdata[1] = LCD_SETCGRAMADDR | (i<<3); //Set CGRAM address
sleep_us(LCD_DELAY); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY);
txdata[0] = LCD_DATA;
for (int j=0; j<8; j++) txdata[1+j] = cgram[i][j];
i2c_write_blocking(i2c1, I2C_LCD, txdata, 9, false);
sleep_us(LCD_DELAY);
}
/* Initialize display control */ /* Initialize display control */
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
/* Display clear once more */ /* Display clear once more */
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_CLEARDISPLAY; txdata[1] = LCD_CLEARDISPLAY;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(1530); sleep_us(1530);
} }
@ -166,7 +171,7 @@ void lcd_clear(void)
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
txdata[1] = LCD_CLEARDISPLAY; txdata[1] = LCD_CLEARDISPLAY;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(1530); sleep_us(1530);
} }
@ -178,12 +183,12 @@ void lcd_curxy(uint8_t x, uint8_t y, bool on)
y &= 0x01; y &= 0x01;
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
txdata[1] = x | 0x80 | (y==1?0x40:0x00); txdata[1] = x | 0x80 | (y==1?0x40:0x00);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYON | (on?LCD_CURSORON:LCD_CURSOROFF) | LCD_BLINKOFF; txdata[1] = LCD_DISPLAYCONTROL | LCD_DISPLAYON | (on?LCD_CURSORON:LCD_CURSOROFF) | LCD_BLINKOFF;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
} }
@ -195,12 +200,12 @@ void lcd_putxy(uint8_t x, uint8_t y, uint8_t c)
y &= 0x01; y &= 0x01;
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
txdata[1] = x | 0x80 | (y==1?0x40:0x00); txdata[1] = x | 0x80 | (y==1?0x40:0x00);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
txdata[0] = LCD_DATA; txdata[0] = LCD_DATA;
txdata[1] = c; txdata[1] = c;
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
} }
@ -213,14 +218,14 @@ void lcd_writexy(uint8_t x, uint8_t y, uint8_t *s)
y &= 0x01; y &= 0x01;
txdata[0] = LCD_COMMAND; txdata[0] = LCD_COMMAND;
txdata[1] = x | 0x80 | ((y==1)?0x40:0x00); txdata[1] = x | 0x80 | ((y==1)?0x40:0x00);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
len = strlen(s); len = strlen(s);
len = (len>(16-x))?(16-x):len; len = (len>(16-x))?(16-x):len;
txdata[0] = LCD_DATA; txdata[0] = LCD_DATA;
for(i=0; i<len; i++) txdata[i+1]=s[i]; for(i=0; i<len; i++) txdata[i+1]=s[i];
i2c_write_blocking(i2c0, I2C_LCD, txdata, len+1, false); i2c_write_blocking(i2c1, I2C_LCD, txdata, len+1, false);
sleep_us(LCD_DELAY); sleep_us(LCD_DELAY);
} }

3
lcd.h
Wyświetl plik

@ -9,9 +9,6 @@
* See lcd.c for more information * See lcd.c for more information
*/ */
#include "pico/stdlib.h"
#include "hardware/i2c.h"
void lcd_init(void); void lcd_init(void);
void lcd_clear(void); void lcd_clear(void);
void lcd_curxy(uint8_t x, uint8_t y, bool on); void lcd_curxy(uint8_t x, uint8_t y, bool on);

154
monitor.c
Wyświetl plik

@ -10,46 +10,67 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "lcd.h" #include "lcd.h"
#include "si5351.h" #include "si5351.h"
#include "dsp.h" #include "dsp.h"
#include "relay.h"
#include "monitor.h" #include "monitor.h"
/* Monitor definitions */
#define CR 13 #define CR 13
#define LF 10 #define LF 10
#define CMD_LEN 32 #define SP 32
#define CMD_LEN 80
char mon_cmd[CMD_LEN+1]; #define CMD_ARGS 16
char mon_cmd[CMD_LEN+1]; // Command string buffer
char *argv[CMD_ARGS]; // Argument pointers
int nargs; // Nr of arguments
typedef struct typedef struct
{ {
char *cmdstr; // Command string char *cmdstr; // Command string
int cmdlen; // Command string length int cmdlen; // Command string length
void (*cmd)(char* par); // Command executive void (*cmd)(void); // Command executive
char *cmdsyn; // Command syntax char *cmdsyn; // Command syntax
char *help; // Command help text char *help; // Command help text
} shell_t; } shell_t;
/* ------------------------------------------------------------- */
/* Below the definitions of the shell commands, add where needed */
/* ------------------------------------------------------------- */ /*** Initialisation, called at startup ***/
void mon_init()
{
stdio_init_all(); // Initialize Standard IO
mon_cmd[CMD_LEN] = '\0'; // Termination to be sure
printf("\n");
printf("=============\n");
printf(" uSDR-Pico \n");
printf(" PE1ATM \n");
printf(" 2021, Udjat \n");
printf("=============\n");
printf("Pico> "); // prompt
}
/*** ------------------------------------------------------------- ***/
/*** Below the definitions of the shell commands, add where needed ***/
/*** ------------------------------------------------------------- ***/
/* /*
* Dumps a defined range of Si5351 registers * Dumps a defined range of Si5351 registers
*/ */
uint8_t si5351_reg[200]; uint8_t si5351_reg[200];
void mon_si(char *par) void mon_si(void)
{ {
int base=0, nreg=200, i; int base=0, nreg=200, i;
// Next: p = strtok(NULL, delim); (returns NULL if none left)
for (i=0; i<nreg; i++) si5351_reg[i] = 0xaa; for (i=0; i<nreg; i++) si5351_reg[i] = 0xaa;
si_getreg(si5351_reg, (uint8_t)base, (uint8_t)nreg); si_getreg(si5351_reg, (uint8_t)base, (uint8_t)nreg);
for (i=0; i<nreg; i++) printf("%02x ",(int)(si5351_reg[i])); for (i=0; i<nreg; i++) printf("%02x ",(int)(si5351_reg[i]));
@ -58,9 +79,9 @@ void mon_si(char *par)
/* /*
* Dumps the complete built-in and programmed characterset on the LCD * Dumps the entire built-in and programmed characterset on the LCD
*/ */
void mon_lt(char *par) void mon_lt(void)
{ {
printf("Check LCD..."); printf("Check LCD...");
lcd_test(); lcd_test();
@ -72,7 +93,7 @@ void mon_lt(char *par)
* Checks for inter-core fifo overruns * Checks for inter-core fifo overruns
*/ */
extern volatile uint32_t fifo_overrun, fifo_rx, fifo_tx, fifo_xx, fifo_incnt; extern volatile uint32_t fifo_overrun, fifo_rx, fifo_tx, fifo_xx, fifo_incnt;
void mon_fo(char *par) void mon_fo(void)
{ {
printf("Fifo input: %lu\n", fifo_incnt); printf("Fifo input: %lu\n", fifo_incnt);
printf("Fifo rx: %lu\n", fifo_rx); printf("Fifo rx: %lu\n", fifo_rx);
@ -86,7 +107,7 @@ void mon_fo(char *par)
* Toggles the PTT status, overriding the HW signal * Toggles the PTT status, overriding the HW signal
*/ */
bool ptt = false; bool ptt = false;
void mon_pt(char *par) void mon_pt(void)
{ {
if (ptt) if (ptt)
{ {
@ -101,60 +122,109 @@ void mon_pt(char *par)
tx_enabled = ptt; tx_enabled = ptt;
} }
/*
* Relay read or write
*/
void mon_bp(void)
{
int ret;
if (*argv[1]=='w')
{
if (nargs>=2)
{
ret = atoi(argv[2]);
relay_setband((uint8_t)ret);
}
}
ret = relay_getband();
if (ret<0)
printf ("I2C read error\n");
else
printf("%02x\n", ret);
}
#define NCMD 4 /*
* Relay read or write
*/
void mon_rx(void)
{
int ret;
if (*argv[1]=='w')
{
if (nargs>=2)
{
ret = atoi(argv[2]);
relay_setattn((uint8_t)ret);
}
}
ret = relay_getattn();
if (ret<0)
printf ("I2C read error\n");
else
printf("%02x\n", ret);
}
/*
* Command shell table, organize the command functions above
*/
#define NCMD 6
shell_t shell[NCMD]= shell_t shell[NCMD]=
{ {
{"si", 2, &mon_si, "si <start> <nr of reg>", "Dumps Si5351 registers"}, {"si", 2, &mon_si, "si <start> <nr of reg>", "Dumps Si5351 registers"},
{"lt", 2, &mon_lt, "lt (no parameters)", "LCD test, dumps characterset on LCD"}, {"lt", 2, &mon_lt, "lt (no parameters)", "LCD test, dumps characterset on LCD"},
{"fo", 2, &mon_fo, "fo (no parameters)", "Returns inter core fifo overruns"}, {"fo", 2, &mon_fo, "fo (no parameters)", "Returns inter core fifo overruns"},
{"pt", 2, &mon_pt, "pt (no parameters)", "Toggles PTT status"} {"pt", 2, &mon_pt, "pt (no parameters)", "Toggles PTT status"},
{"bp", 2, &mon_bp, "bp {r|w} <value>", "Read or Write BPF relays"},
{"rx", 2, &mon_rx, "rx {r|w} <value>", "Read or Write RX relays"}
}; };
/*** ---------------------------------------- ***/
/*** Commandstring parser and monitor process ***/
/*** ---------------------------------------- ***/
/*
/* Commandstring parser, checks commandstring and invokes shellcommand */ * Command line parser
char delim[] = " "; */
void mon_parse(char* s) void mon_parse(char* s)
{ {
char *p; char *p;
int i; int i;
p = s; // Get command part of string p = s; // Set to start of string
for (i=0; i<NCMD; i++) nargs = 0;
if (strncmp(p, shell[i].cmdstr, shell[i].cmdlen) == 0) break; while (*p!='\0') // Assume stringlength >0
if (i<NCMD)
(*shell[i].cmd)(p);
else
{ {
for (i=0; i<NCMD; i++) while (*p==' ') p++; // Skip whitespace
if (*p=='\0') break; // String might end in spaces
argv[nargs++] = p; // Store first valid char loc after whitespace
while ((*p!=' ')&&(*p!='\0')) p++; // Skip non-whitespace
}
if (nargs==0) return; // No command or parameter
for (i=0; i<NCMD; i++) // Lookup shell command
if (strncmp(argv[0], shell[i].cmdstr, shell[i].cmdlen) == 0) break;
if (i<NCMD)
(*shell[i].cmd)();
else // Unknown command
{
for (i=0; i<NCMD; i++) // Print help if no match
printf("%s\n %s\n", shell[i].cmdsyn, shell[i].help); printf("%s\n %s\n", shell[i].cmdsyn, shell[i].help);
} }
} }
void mon_init()
{
stdio_init_all(); // Initialize Standard IO
mon_cmd[CMD_LEN] = '\0'; // Termination to be sure
printf("\n");
printf("=============\n");
printf(" uSDR-Pico \n");
printf(" PE1ATM \n");
printf(" 2021, Udjat \n");
printf("=============\n");
printf("Pico> "); // prompt
}
/* /*
* Monitor process
* This function collects characters from stdin until CR * This function collects characters from stdin until CR
* Then the command is send to a parser and executed. * Then the command is send to a parser and executed.
*/ */
void mon_evaluate(uint32_t timeout) void mon_evaluate(void)
{ {
static int i = 0; static int i = 0;
int c = getchar_timeout_us(timeout); // NOTE: this is the only SDK way to read from stdin int c = getchar_timeout_us(10L); // NOTE: this is the only SDK way to read from stdin
if (c==PICO_ERROR_TIMEOUT) return; // Early bail out if (c==PICO_ERROR_TIMEOUT) return; // Early bail out
switch (c) switch (c)

Wyświetl plik

@ -10,6 +10,6 @@
*/ */
void mon_init(); void mon_init();
void mon_evaluate(uint32_t timeout); void mon_evaluate(void);
#endif #endif

77
relay.c 100644
Wyświetl plik

@ -0,0 +1,77 @@
/*
* relay.c
*
* Created: Nov 2021
* Author: Arjan te Marvelde
*
* Two PCF8574 expanders are on the I2C bus, one on the RX and one on the BPF board.
* The RX (0x42) bit assignments:
* 0: Enable -20dB attenuator
* 1: Enable -10dB attenuator
* 2: Enable +10dB pre-amplifier
* The BPF (0x40) bit assignments:
* 0: Enable LPF 2.5 MHz
* 1: Enable BPF 2.0 - 6.0 MHz
* 2: Enable BPF 5.0 -12.0 MHz
* 3: Enable BPF 10.0 -24.0 MHz
* 4: Enable BPF 20.0 -40.0 MHz
*
*/
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "relay.h"
/* I2C address and pins */
#define I2C_RX 0x21
#define I2C_BPF 0x20
void relay_setband(uint8_t val)
{
uint8_t data[2];
data[0] = val&0x1f;
i2c_write_blocking(i2c1, I2C_BPF, data, 1, false);
}
int relay_getband(void)
{
uint8_t data[2];
int ret;
ret = i2c_read_blocking(i2c1, I2C_BPF, data, 1, false);
if (ret>=0)
ret=data[0];
return(ret);
}
void relay_setattn(uint8_t val)
{
uint8_t data[2];
data[0] = val&0x07;
i2c_write_blocking(i2c1, I2C_RX, data, 1, false);
}
int relay_getattn(void)
{
uint8_t data[2];
int ret;
ret = i2c_read_blocking(i2c1, I2C_RX, data, 1, false);
if (ret>=0)
ret=data[0];
return(ret);
}
void relay_init(void)
{
relay_setattn(0);
relay_setband(0);
relay_setattn(REL_PRE_10);
relay_setband(REL_BPF12);
}

29
relay.h 100644
Wyświetl plik

@ -0,0 +1,29 @@
#ifndef __RELAY_H__
#define __RELAY_H__
/*
* relay.h
*
* Created: Nov 2021
* Author: Arjan te Marvelde
*
* See relay.c for more information
*/
#define REL_LPF2 0x01
#define REL_BPF6 0x02
#define REL_BPF12 0x04
#define REL_BPF24 0x08
#define REL_BPF40 0x10
#define REL_ATT_30 0x03
#define REL_ATT_20 0x01
#define REL_ATT_10 0x02
#define REL_PRE_10 0x04
extern void relay_setband(uint8_t val);
extern void relay_setattn(uint8_t val);
extern int relay_getband(void);
extern int relay_getattn(void);
extern void relay_init(void);
#endif

Wyświetl plik

@ -61,8 +61,8 @@ NOTE: Phase offsets only work when Ri = 1, this means minimum Fout is 4.762MHz a
Control Si5351: Control Si5351 (see AN619):
================ ===========================
----+---------+---------+---------+---------+---------+---------+---------+---------+ ----+---------+---------+---------+---------+---------+---------+---------+---------+
@ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | @ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
----+---------+---------+---------+---------+---------+---------+---------+---------+ ----+---------+---------+---------+---------+---------+---------+---------+---------+
@ -166,15 +166,12 @@ Control Si5351:
#define SI_XTAL_FREQ 24998851UL // Replace with measured crystal frequency of XTAL for CL = 10pF (default) #define SI_XTAL_FREQ 25001414UL // Replace with measured crystal frequency of XTAL for CL = 10pF (default)
#define SI_MSN_LO ((0.6e9)/SI_XTAL_FREQ) #define SI_MSN_LO ((0.6e9)/SI_XTAL_FREQ)
#define SI_MSN_HI ((0.9e9)/SI_XTAL_FREQ) #define SI_MSN_HI ((0.9e9)/SI_XTAL_FREQ)
#define SI_PLL_C 1000000UL // Parameter c for PLL-A and -B setting #define SI_PLL_C 1000000UL // Parameter c for PLL-A and -B setting
/* I2C1 pins */
#define I2C1_SDA 18
#define I2C1_SCL 19
vfo_t vfo[2]; // 0: clk0 and clk1 1: clk2 vfo_t vfo[2]; // 0: clk0 and clk1 1: clk2
@ -183,9 +180,9 @@ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len)
{ {
int ret; int ret;
ret = i2c_write_blocking(i2c1, I2C_VFO, &reg, 1, true); ret = i2c_write_blocking(i2c0, I2C_VFO, &reg, 1, true);
if (ret<0) printf ("I2C write error\n"); if (ret<0) printf ("I2C write error\n");
ret = i2c_read_blocking(i2c1, I2C_VFO, data, len, false); ret = i2c_read_blocking(i2c0, I2C_VFO, data, len, false);
if (ret<0) printf ("I2C read error\n"); if (ret<0) printf ("I2C read error\n");
return(len); return(len);
} }
@ -223,7 +220,7 @@ void si_setmsn(uint8_t i)
data[6] = ((SI_PLL_C & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16); data[6] = ((SI_PLL_C & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[7] = (P2 & 0x0000FF00) >> 8; data[7] = (P2 & 0x0000FF00) >> 8;
data[8] = (P2 & 0x000000FF); data[8] = (P2 & 0x000000FF);
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
} }
// Set up registers with MS and R divider for vfo[i], assuming values have been set in vfo[i] // Set up registers with MS and R divider for vfo[i], assuming values have been set in vfo[i]
@ -254,38 +251,38 @@ void si_setmsi(uint8_t i)
data[6] = 0x00; data[6] = 0x00;
data[7] = 0x00; data[7] = 0x00;
data[8] = 0x00; data[8] = 0x00;
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// If vfo[0] also set clk 1 // If vfo[0] also set clk 1
if (i==0) if (i==0)
{ {
data[0] = SI_SYNTH_MS1; // Same data in synthesizer data[0] = SI_SYNTH_MS1; // Same data in synthesizer
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
if (vfo[0].phase&1) // Phase is either 90 or 270 deg? if (vfo[0].phase&1) // Phase is either 90 or 270 deg?
{ {
data[0] = SI_CLK1_PHOFF; data[0] = SI_CLK1_PHOFF;
data[1] = vfo[0].msi; data[1] = vfo[0].msi;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
} }
else // Phase is 0 or 180 deg else // Phase is 0 or 180 deg
{ {
data[0] = SI_CLK1_PHOFF; data[0] = SI_CLK1_PHOFF;
data[1] = 0; data[1] = 0;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
} }
if (vfo[0].phase&2) // Phase is 180 or 270 deg? if (vfo[0].phase&2) // Phase is 180 or 270 deg?
{ {
data[0] = SI_CLK1_CTL; data[0] = SI_CLK1_CTL;
data[1] = 0x5f; // CLK1: INT, PLLA, INV, MS, 8mA data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
} }
} }
// Reset associated PLL // Reset associated PLL
data[0] = SI_PLL_RESET; data[0] = SI_PLL_RESET;
data[1] = (i==1)?0x80:0x20; data[1] = (i==1)?0x80:0x20;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
} }
@ -335,13 +332,7 @@ void si_init(void)
{ {
uint8_t data[16]; // I2C trx buffer uint8_t data[16]; // I2C trx buffer
i2c_init(i2c1, 400*1000); // Hard initialize Synth registers: 7.074MHz, CLK1 90 deg ahead, PLLA for CLK 0&1, PLLB for CLK2
gpio_set_function(I2C1_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C1_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C1_SDA);
gpio_pull_up(I2C1_SCL);
// Hard initialize Synth registers: all 10MHz, CLK1 90 deg ahead, PLLA for CLK 0&1, PLLB for CLK2
// Ri=1, // Ri=1,
// MSi=68, P1=8192, P2=0, P3=1 // MSi=68, P1=8192, P2=0, P3=1
// MSN=27.2 P1=2969, P2=600000, P3=1000000 // MSN=27.2 P1=2969, P2=600000, P3=1000000
@ -368,12 +359,12 @@ void si_init(void)
data[6] = 0xf9; // MSNA_P3[19:16] , MSNA_P2[19:16] data[6] = 0xf9; // MSNA_P3[19:16] , MSNA_P2[19:16]
data[7] = 0x27; // MSNA_P2[15:8] data[7] = 0x27; // MSNA_P2[15:8]
data[8] = 0xc0; // MSNA_P2[7:0] data[8] = 0xc0; // MSNA_P2[7:0]
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// PLLB: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240 // PLLB: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240
data[0] = SI_SYNTH_PLLB; // Same content data[0] = SI_SYNTH_PLLB; // Same content
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// MS0 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 // MS0 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1
data[0] = SI_SYNTH_MS0; data[0] = SI_SYNTH_MS0;
@ -385,43 +376,43 @@ void si_init(void)
data[6] = 0x00; // MS0_P3[19:16] , MS0_P2[19:16] data[6] = 0x00; // MS0_P3[19:16] , MS0_P2[19:16]
data[7] = 0x00; // MS0_P2[15:8] data[7] = 0x00; // MS0_P2[15:8]
data[8] = 0x00; // MS0_P2[7:0] data[8] = 0x00; // MS0_P2[7:0]
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// MS1 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 // MS1 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1
data[0] = SI_SYNTH_MS1; // Same content data[0] = SI_SYNTH_MS1; // Same content
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// MS2 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 // MS2 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1
data[0] = SI_SYNTH_MS2; // Same content data[0] = SI_SYNTH_MS2; // Same content
i2c_write_blocking(i2c1, I2C_VFO, data, 9, false); i2c_write_blocking(i2c0, I2C_VFO, data, 9, false);
// Phase offsets for 3 clocks // Phase offsets for 3 clocks
data[0] = SI_CLK0_PHOFF; data[0] = SI_CLK0_PHOFF;
data[1] = 0x00; // CLK0: phase 0 deg data[1] = 0x00; // CLK0: phase 0 deg
data[2] = 0x44; // CLK1: phase 90 deg (=MSi) data[2] = 0x44; // CLK1: phase 90 deg (=MSi)
data[3] = 0x00; // CLK2: phase 0 deg data[3] = 0x00; // CLK2: phase 0 deg
i2c_write_blocking(i2c1, I2C_VFO, data, 4, false); i2c_write_blocking(i2c0, I2C_VFO, data, 4, false);
// Output port settings for 3 clocks // Output port settings for 3 clocks
data[0] = SI_CLK0_CTL; data[0] = SI_CLK0_CTL;
data[1] = 0x4f; // CLK0: INT, PLLA, nonINV, MS, 8mA data[1] = 0x4d; // CLK0: INT, PLLA, nonINV, MS, 4mA
data[2] = 0x4f; // CLK1: INT, PLLA, nonINV, MS, 8mA data[2] = 0x4d; // CLK1: INT, PLLA, nonINV, MS, 4mA
data[3] = 0x6f; // CLK2: INT, PLLB, nonINV, MS, 8mA data[3] = 0x6f; // CLK2: INT, PLLB, nonINV, MS, 8mA
i2c_write_blocking(i2c1, I2C_VFO, data, 4, false); i2c_write_blocking(i2c0, I2C_VFO, data, 4, false);
// Disable spread spectrum (startup state is undefined) // Disable spread spectrum (startup state is undefined)
data[0] = SI_SS_EN; data[0] = SI_SS_EN;
data[1] = 0x00; data[1] = 0x00;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
// Reset both PLL // Reset both PLL
data[0] = SI_PLL_RESET; data[0] = SI_PLL_RESET;
data[1] = 0xa0; data[1] = 0xa0;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
// Enable all outputs // Enable all outputs
data[0] = SI_CLK_OE; data[0] = SI_CLK_OE;
data[1] = 0x00; data[1] = 0x00;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
} }

53
uSDR.c
Wyświetl plik

@ -16,6 +16,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h" #include "hardware/gpio.h"
#include "hardware/timer.h" #include "hardware/timer.h"
#include "hardware/clocks.h" #include "hardware/clocks.h"
@ -25,7 +27,15 @@
#include "dsp.h" #include "dsp.h"
#include "si5351.h" #include "si5351.h"
#include "monitor.h" #include "monitor.h"
#include "relay.h"
#define LED_MS 1000
#define LOOP_MS 10
#define I2C0_SDA 16
#define I2C0_SCL 17
#define I2C1_SDA 18
#define I2C1_SCL 19
/* /*
* LED TIMER definition and callback routine * LED TIMER definition and callback routine
@ -40,6 +50,18 @@ bool led_callback(struct repeating_timer *t)
return true; return true;
} }
/*
* Scheduler callback function.
* This executes every LOOP_MS.
*/
semaphore_t loop_sem;
struct repeating_timer loop_timer;
bool loop_callback(struct repeating_timer *t)
{
sem_release(&loop_sem);
return(true);
}
int main() int main()
{ {
@ -47,7 +69,27 @@ int main()
gpio_init(PICO_DEFAULT_LED_PIN); gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_LED_PIN, true); // Set LED on gpio_put(PICO_DEFAULT_LED_PIN, true); // Set LED on
add_repeating_timer_ms(-1000, led_callback, NULL, &led_timer); add_repeating_timer_ms(-LED_MS, led_callback, NULL, &led_timer);
/*
* i2c0 is used for the si5351 interface
* i2c1 is used for the LCD and all other interfaces
*/
/* i2c0 initialisation at 400Khz. */
i2c_init(i2c0, 400*1000);
gpio_set_function(I2C0_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C0_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C0_SDA);
gpio_pull_up(I2C0_SCL);
/* i2c1 initialisation at 400Khz. */
i2c_init(i2c1, 400*1000);
gpio_set_function(I2C1_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C1_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C1_SDA);
gpio_pull_up(I2C1_SCL);
/* Initialize units */ /* Initialize units */
mon_init(); // Monitor shell on stdio mon_init(); // Monitor shell on stdio
@ -55,10 +97,15 @@ int main()
dsp_init(); // Signal processing unit dsp_init(); // Signal processing unit
lcd_init(); // LCD output unit lcd_init(); // LCD output unit
hmi_init(); // HMI user inputs hmi_init(); // HMI user inputs
relay_init();
while (1) /* A simple round-robin scheduler */
sem_init(&loop_sem, 1, 1) ;
add_repeating_timer_ms(-LOOP_MS, loop_callback, NULL, &loop_timer);
while (1)
{ {
mon_evaluate(10000L); // Check monitor input, wait max 10000 usec sem_acquire_blocking(&loop_sem); // Wait until timer callback releases sem
mon_evaluate(); // Check monitor input
si_evaluate(); // Refresh VFO settings si_evaluate(); // Refresh VFO settings
hmi_evaluate(); // Refresh HMI hmi_evaluate(); // Refresh HMI
} }