diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f9002b..7edd5d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/CODEv2.zip b/CODEv2.zip new file mode 100644 index 0000000..0439d29 Binary files /dev/null and b/CODEv2.zip differ diff --git a/PCBv2.zip b/PCBv2.zip new file mode 100644 index 0000000..1e79b3f Binary files /dev/null and b/PCBv2.zip differ diff --git a/dsp.c b/dsp.c index e7a4825..56f64e5 100644 --- a/dsp.c +++ b/dsp.c @@ -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 >>= -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 } diff --git a/dsp.h b/dsp.h index 55bba95..69a35ea 100644 --- a/dsp.h +++ b/dsp.h @@ -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) diff --git a/hmi.c b/hmi.c index 15b1412..6f8e523 100644 --- a/hmi.c +++ b/hmi.c @@ -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_option0)?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_option0)?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_state0) // 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); } diff --git a/hmi.h b/hmi.h index 8aeccb5..de0f5d0 100644 --- a/hmi.h +++ b/hmi.h @@ -9,6 +9,8 @@ * See hmi.c for more information */ +extern bool ptt_active; + void hmi_init(void); void hmi_evaluate(void); diff --git a/lcd.c b/lcd.c index 38c3b00..d89a868 100644 --- a/lcd.c +++ b/lcd.c @@ -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 +#include #include #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=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 ", "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} ", "Read or Write BPF relays"}, + {"rx", 2, &mon_rx, "rx {r|w} ", "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; i0 { - for (i=0; i "); // 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) diff --git a/monitor.h b/monitor.h index 6dacb50..b15b68b 100644 --- a/monitor.h +++ b/monitor.h @@ -10,6 +10,6 @@ */ void mon_init(); -void mon_evaluate(uint32_t timeout); +void mon_evaluate(void); #endif \ No newline at end of file diff --git a/relay.c b/relay.c new file mode 100644 index 0000000..95780e6 --- /dev/null +++ b/relay.c @@ -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 +#include +#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); +} \ No newline at end of file diff --git a/relay.h b/relay.h new file mode 100644 index 0000000..96efb60 --- /dev/null +++ b/relay.h @@ -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 diff --git a/si5351.c b/si5351.c index d232159..43f4530 100644 --- a/si5351.c +++ b/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 | ----+---------+---------+---------+---------+---------+---------+---------+---------+ @@ -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, ®, 1, true); + ret = i2c_write_blocking(i2c0, I2C_VFO, ®, 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); } diff --git a/uSDR.c b/uSDR.c index e3dc102..672ee1f 100644 --- a/uSDR.c +++ b/uSDR.c @@ -16,6 +16,8 @@ #include #include #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 }