kopia lustrzana https://github.com/ArjanteMarvelde/uSDR-pico
rodzic
493756488a
commit
82d6dd841b
|
@ -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")
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
185
dsp.c
185
dsp.c
|
@ -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
1
dsp.h
|
@ -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
127
hmi.c
|
@ -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
2
hmi.h
|
@ -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
93
lcd.c
|
@ -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
3
lcd.h
|
@ -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
154
monitor.c
|
@ -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)
|
||||||
|
|
|
@ -10,6 +10,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void mon_init();
|
void mon_init();
|
||||||
void mon_evaluate(uint32_t timeout);
|
void mon_evaluate(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
61
si5351.c
61
si5351.c
|
@ -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, ®, 1, true);
|
ret = i2c_write_blocking(i2c0, I2C_VFO, ®, 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
53
uSDR.c
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue