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(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_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
#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
* 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)
* The decay time is about 100x this value
* Slow attack would be about 4096
@ -76,7 +77,7 @@
#define AGC_DECAY 8192
#define AGC_FAST 64
#define AGC_SLOW 4096
#define AGC_OFF 65534
#define AGC_OFF 32766
volatile uint16_t agc_decay = AGC_OFF;
volatile uint16_t agc_attack = AGC_OFF;
void dsp_setagc(int agc)
@ -102,7 +103,7 @@ void dsp_setagc(int agc)
* MODE is modulation/demodulation
* 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)
{
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/)
* 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 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_dc, q_dc; // DC bias for I/Q channel
volatile int rx_cnt=0; // Decimation counter
bool rx(void)
{
int16_t q_sample, i_sample, a_sample;
@ -175,8 +211,8 @@ bool rx(void)
/*** SAMPLING ***/
i_sample = adc_result[0]; // Take last ADC 0 result
q_sample = adc_result[1]; // Take last ADC 1 result
q_sample = adc_result[0]; // Take last ADC 0 result, connected to Q input
i_sample = adc_result[1]; // Take last ADC 1 result, connected to I input
/*
* Remove DC and store new sample
@ -184,25 +220,26 @@ bool rx(void)
* Amplitude of samples should fit inside [-2048, 2047]
*/
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
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;
/*
* 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
*/
if (agc_gain > 0)
{
q_sample <<= agc_gain;
i_sample <<= agc_gain;
q_sample = q_sample * (1<<agc_gain);
i_sample = i_sample * (1<<agc_gain);
}
else if (agc_gain < 0)
{
q_sample >>= -agc_gain;
i_sample >>= -agc_gain;
q_sample = q_sample / (1<<(-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
i_accu += (int32_t)i_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling
}
q_accu >>= 8;
i_accu >>= 8;
q_accu = q_accu/256;
i_accu = i_accu/256;
q_s[14] = q_accu;
i_s[14] = i_accu;
@ -247,20 +284,20 @@ bool rx(void)
{
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)
*/
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;
break;
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)
*/
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;
break;
case 2: //AM
@ -277,9 +314,9 @@ bool rx(void)
/*** AUDIO GENERATION ***/
/*
* 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
if (i&0xff00) {k+=8; i>>=8;} // k=log2(peak), find highest bit set
if (i&0x00f0) {k+=4; i>>=4;}
@ -295,7 +332,6 @@ bool rx(void)
agc_gain++; // Increase gain
agc_accu += agc_decay; // Reset integrator
}
/*
* Scale and clip output,
@ -315,49 +351,83 @@ bool rx(void)
/*
* 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_level=0; // Average level of raw sample stream
volatile int16_t a_s[15]; // Filtered and decimated samples
volatile int16_t a_dc; // DC level
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)
{
static int tx_phase = 0;
int16_t a_sample;
int32_t a_accu;
int16_t qh;
int i;
/*
* 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
/*** RAW Audio SAMPLES from VOX function ***/
/*
* Low pass filter + decimation
*/
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
a_s[i] = a_s[i+1];
a_accu = 0; // Initialize accumulator
for (i=0; i<15; i++) // Low pass FIR filter
a_accu += (int32_t)a_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz sampling
a_s[14] = a_accu >> 8;
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_s[14] = a_accu / 256;
/*
* 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):
*/
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.
* 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_B, DAC_BIAS + (qh/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 + (a_s[7]/4));
return true;
}
@ -398,8 +468,8 @@ void dsp_loop()
fifo_incnt++;
/* Initialize DACs */
gpio_set_function(20, GPIO_FUNC_PWM); // GP20 is PWM for I 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(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 I DAC (Slice 2, Channel B)
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_wrap(dac_iq, DAC_RANGE-1); // Set cycle length
@ -414,8 +484,8 @@ void dsp_loop()
/* Initialize ADCs */
adc_init(); // Initialize ADC to known state
adc_set_clkdiv(0); // Fastest clock (500 kSps)
adc_gpio_init(26); // GP26 is ADC 0 for I channel
adc_gpio_init(27); // GP27 is ADC 1 for Q channel
adc_gpio_init(26); // GP26 is ADC 0 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_select_input(0); // Start with ADC0
adc_next = 0;
@ -434,18 +504,11 @@ void dsp_loop()
while(1)
{
cmd = multicore_fifo_pop_blocking(); // Wait for fifo output
if (cmd == DSP_RX)
{
fifo_rx++;
rx();
}
else if (cmd == DSP_TX)
{
fifo_tx++;
tx_enabled = ptt_active || vox(); // Sample audio and check level
if (tx_enabled)
tx();
}
else
fifo_xx++;
rx();
if (multicore_fifo_rvalid())
fifo_overrun++; // Check for missed events
}

1
dsp.h
Wyświetl plik

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

127
hmi.c
Wyświetl plik

@ -37,6 +37,7 @@
#include "hmi.h"
#include "dsp.h"
#include "si5351.h"
#include "relay.h"
/*
* GPIO assignments
@ -66,11 +67,12 @@
* 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):
*
* Submenu Values ENC Enter Escape Left Right
* -------------------------------------------------------------------------------------
* Mode USB, LSB, AM, CW change commit exit prev next
* AGC Fast, Slow, Off change commit exit prev next
* Pre +10dB, 0, -10dB, -20dB change commit exit prev next
* Submenu Values ENC Enter Escape Left Right
* -----------------------------------------------------------------------------------------------
* Mode USB, LSB, AM, CW change commit exit prev next
* AGC Fast, Slow, Off 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--
*/
@ -80,7 +82,9 @@
#define HMI_S_MODE 1
#define HMI_S_AGC 2
#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 */
#define HMI_E_NOEVENT 0
@ -97,14 +101,18 @@
/* Sub menu option string sets */
#define HMI_NMODE 4
#define HMI_NAGC 3
#define HMI_NPRE 4
char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC ","Pre "}; // Indexed by hmi_state
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] = {"-20dB", "-10dB", "0dB", "+10dB"}; // Indexed by hmi_sub[HMI_S_PRE]
#define HMI_NPRE 5
#define HMI_NVOX 4
#define HMI_NBPF 5
char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC","Pre","VOX"}; // Indexed by hmi_state
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_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_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
// 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
*/
@ -201,7 +213,11 @@ void hmi_handler(uint8_t event)
case HMI_S_PRE:
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
}
if (event==HMI_E_INCREMENT)
@ -213,6 +229,40 @@ void hmi_handler(uint8_t event)
hmi_option = (hmi_option>0)?hmi_option-1:0;
}
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 */
@ -263,12 +313,16 @@ void hmi_callback(uint gpio, uint32_t events)
if (events&GPIO_IRQ_EDGE_FALL)
evt = HMI_E_RIGHT;
break;
/*
case GP_PTT: // PTT
if (events&GPIO_IRQ_EDGE_FALL)
DSP_SETPTT(true);
ptt_active = true;
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;
*/
default:
return;
}
@ -286,7 +340,9 @@ void hmi_init(void)
* The callback handles interrupts for all GPIOs with IRQ enabled.
* Level interrupts don't seem to work properly.
* For debouncing, the GPIO pins should be pulled-up and connected to gnd with 100nF.
* PTT has separate debouncing logic
*/
// Init input GPIOs
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_2, 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!
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_freq = 7074000UL; // Initial frequency
SI_SETFREQ(0, HMI_MULFREQ*hmi_freq); // Set freq to 7074 kHz
SI_SETPHASE(0, 1); // Set phase to 90deg
SI_SETFREQ(0, HMI_MULFREQ*hmi_freq); // Set freq to 7074 kHz (depends on mixer type)
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];
// 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);
// Print bottom line of dsiplay, depending on state
switch (hmi_state)
{
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_curxy(4+(hmi_option>4?6:hmi_option), 0, true);
break;
@ -354,10 +413,36 @@ void hmi_evaluate(void)
lcd_writexy(0,1,s);
lcd_curxy(8, 1, false);
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:
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
*/
extern bool ptt_active;
void hmi_init(void);
void hmi_evaluate(void);

93
lcd.c
Wyświetl plik

@ -83,80 +83,85 @@
#define LCD_DATA 0x40
/* I2C address and pins */
#define I2C_LCD 0x3E
#define I2C0_SDA 16
#define I2C0_SCL 17
#define I2C_LCD 0x3E
uint8_t cgram[65] = // Write CGRAM
{ // 8x8 bytes
0x80,
0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x00,
0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x0b, 0x00,
0x08, 0x10, 0x08, 0x10, 0x08, 0x13, 0x0b, 0x00,
0x08, 0x10, 0x08, 0x10, 0x0b, 0x13, 0x0b, 0x00,
0x08, 0x10, 0x08, 0x13, 0x0b, 0x13, 0x0b, 0x00,
0x08, 0x10, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00,
0x08, 0x13, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00,
0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x13, 0x0b, 0x00
/*
* User defined characters
*/
uint8_t cgram[8][8] = // Write CGRAM
{
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00: blank
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00}, // 0x01: Level 1
{0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x00}, // 0x02: Level 2
{0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x03: Level 3
{0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x04: Level 4
{0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00}, // 0x05: Level 5
{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)
{
uint8_t txdata[8];
/* 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);
uint8_t txdata[10];
uint8_t i;
sleep_ms(50);
txdata[0] = LCD_COMMAND;
/* Initialize function set (see datasheet fig 23)*/
txdata[0] = LCD_COMMAND;
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);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false);
i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
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);
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false);
i2c_write_blocking(i2c1, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY);
/* Initialize display control */
txdata[0] = LCD_COMMAND;
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);
/* Display clear */
txdata[0] = LCD_COMMAND;
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);
/* Initialize entry mode set */
txdata[0] = LCD_COMMAND;
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);
/* Load CGRAM */
txdata[1] = 0x40; //Set CGRAM address 0
i2c_write_blocking(i2c0, I2C_LCD, txdata, 2, false);
sleep_us(LCD_DELAY);
i2c_write_blocking(i2c0, I2C_LCD, cgram, 65, false);
sleep_us(LCD_DELAY);
for (i=0; i<8; i++)
{
txdata[0] = LCD_COMMAND;
txdata[1] = LCD_SETCGRAMADDR | (i<<3); //Set CGRAM address
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 */
txdata[0] = LCD_COMMAND;
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);
/* Display clear once more */
txdata[0] = LCD_COMMAND;
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);
}
@ -166,7 +171,7 @@ void lcd_clear(void)
txdata[0] = LCD_COMMAND;
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);
}
@ -178,12 +183,12 @@ void lcd_curxy(uint8_t x, uint8_t y, bool on)
y &= 0x01;
txdata[0] = LCD_COMMAND;
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);
txdata[0] = LCD_COMMAND;
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);
}
@ -195,12 +200,12 @@ void lcd_putxy(uint8_t x, uint8_t y, uint8_t c)
y &= 0x01;
txdata[0] = LCD_COMMAND;
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);
txdata[0] = LCD_DATA;
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);
}
@ -213,14 +218,14 @@ void lcd_writexy(uint8_t x, uint8_t y, uint8_t *s)
y &= 0x01;
txdata[0] = LCD_COMMAND;
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);
len = strlen(s);
len = (len>(16-x))?(16-x):len;
txdata[0] = LCD_DATA;
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);
}

3
lcd.h
Wyświetl plik

@ -9,9 +9,6 @@
* See lcd.c for more information
*/
#include "pico/stdlib.h"
#include "hardware/i2c.h"
void lcd_init(void);
void lcd_clear(void);
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 <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "lcd.h"
#include "si5351.h"
#include "dsp.h"
#include "relay.h"
#include "monitor.h"
/* Monitor definitions */
#define CR 13
#define LF 10
#define CMD_LEN 32
char mon_cmd[CMD_LEN+1];
#define SP 32
#define CMD_LEN 80
#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
{
char *cmdstr; // Command string
int cmdlen; // Command string length
void (*cmd)(char* par); // Command executive
void (*cmd)(void); // Command executive
char *cmdsyn; // Command syntax
char *help; // Command help text
} 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
*/
uint8_t si5351_reg[200];
void mon_si(char *par)
void mon_si(void)
{
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;
si_getreg(si5351_reg, (uint8_t)base, (uint8_t)nreg);
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...");
lcd_test();
@ -72,7 +93,7 @@ void mon_lt(char *par)
* Checks for inter-core fifo overruns
*/
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 rx: %lu\n", fifo_rx);
@ -86,7 +107,7 @@ void mon_fo(char *par)
* Toggles the PTT status, overriding the HW signal
*/
bool ptt = false;
void mon_pt(char *par)
void mon_pt(void)
{
if (ptt)
{
@ -101,60 +122,109 @@ void mon_pt(char *par)
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]=
{
{"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"},
{"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 */
char delim[] = " ";
/*
* Command line parser
*/
void mon_parse(char* s)
{
char *p;
int i;
p = s; // Get command part of string
for (i=0; i<NCMD; i++)
if (strncmp(p, shell[i].cmdstr, shell[i].cmdlen) == 0) break;
if (i<NCMD)
(*shell[i].cmd)(p);
else
p = s; // Set to start of string
nargs = 0;
while (*p!='\0') // Assume stringlength >0
{
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);
}
}
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
* Then the command is send to a parser and executed.
*/
void mon_evaluate(uint32_t timeout)
void mon_evaluate(void)
{
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
switch (c)

Wyświetl plik

@ -10,6 +10,6 @@
*/
void mon_init();
void mon_evaluate(uint32_t timeout);
void mon_evaluate(void);
#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 |
----+---------+---------+---------+---------+---------+---------+---------+---------+
@ -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_HI ((0.9e9)/SI_XTAL_FREQ)
#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
@ -183,9 +180,9 @@ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len)
{
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");
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");
return(len);
}
@ -223,7 +220,7 @@ void si_setmsn(uint8_t i)
data[6] = ((SI_PLL_C & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[7] = (P2 & 0x0000FF00) >> 8;
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]
@ -254,38 +251,38 @@ void si_setmsi(uint8_t i)
data[6] = 0x00;
data[7] = 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 (i==0)
{
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?
{
data[0] = SI_CLK1_PHOFF;
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
{
data[0] = SI_CLK1_PHOFF;
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?
{
data[0] = SI_CLK1_CTL;
data[1] = 0x5f; // CLK1: INT, PLLA, INV, MS, 8mA
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false);
data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
}
}
// Reset associated PLL
data[0] = SI_PLL_RESET;
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
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);
// Hard initialize Synth registers: all 10MHz, CLK1 90 deg ahead, PLLA for CLK 0&1, PLLB for CLK2
// Hard initialize Synth registers: 7.074MHz, CLK1 90 deg ahead, PLLA for CLK 0&1, PLLB for CLK2
// Ri=1,
// MSi=68, P1=8192, P2=0, P3=1
// 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[7] = 0x27; // MSNA_P2[15:8]
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
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
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[7] = 0x00; // MS0_P2[15:8]
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
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
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
data[0] = SI_CLK0_PHOFF;
data[1] = 0x00; // CLK0: phase 0 deg
data[2] = 0x44; // CLK1: phase 90 deg (=MSi)
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
data[0] = SI_CLK0_CTL;
data[1] = 0x4f; // CLK0: INT, PLLA, nonINV, MS, 8mA
data[2] = 0x4f; // CLK1: INT, PLLA, nonINV, MS, 8mA
data[1] = 0x4d; // CLK0: INT, PLLA, nonINV, MS, 4mA
data[2] = 0x4d; // CLK1: INT, PLLA, nonINV, MS, 4mA
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)
data[0] = SI_SS_EN;
data[1] = 0x00;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false);
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
// Reset both PLL
data[0] = SI_PLL_RESET;
data[1] = 0xa0;
i2c_write_blocking(i2c1, I2C_VFO, data, 2, false);
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
// Enable all outputs
data[0] = SI_CLK_OE;
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 <string.h>
#include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
@ -25,7 +27,15 @@
#include "dsp.h"
#include "si5351.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
@ -40,6 +50,18 @@ bool led_callback(struct repeating_timer *t)
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()
{
@ -47,7 +69,27 @@ int main()
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
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 */
mon_init(); // Monitor shell on stdio
@ -55,10 +97,15 @@ int main()
dsp_init(); // Signal processing unit
lcd_init(); // LCD output unit
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
hmi_evaluate(); // Refresh HMI
}