diff --git a/dsp.c b/dsp.c index dd8d5ff..ce0abb1 100644 --- a/dsp.c +++ b/dsp.c @@ -338,13 +338,14 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t) // Crude AGC mechanism **TO BE IMPROVED** if (!tx_enabled) { + // Approximate amplitude, with alpha max + beta min function uint32_t i=adc_level[1],q=adc_level[0]; if (i>q) temp = (MAX(i,((29*i/32) + (61*q/128))))>>LSH; else temp = (MAX(q,((29*q/32) + (61*i/128))))>>LSH; s_rssi = MAX(1,temp); - rx_agc = AGC_TOP/s_rssi; // calculate required AGC factor + rx_agc = AGC_TOP/s_rssi; // calculate scaling factor if (rx_agc==0) rx_agc=1; } @@ -363,7 +364,7 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t) pwm_set_gpio_level(22, A_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output A to DAC } - // When sample buffer is full, move pointer and signal DSP loop + // When sample buffer is full, move pointer to next and signal the DSP loop if (++dsp_tick >= BUFSIZE) // Increment tick and check range { dsp_tick = 0; // Reset counter @@ -505,7 +506,7 @@ void __not_in_flash_func(dsp_loop)() rx(); // Do RX signal processing } -/////// This is a trap, ptt remains active after once asserted +/////// This is a trap, ptt remains active after once asserted: to be checked! tx_enabled = vox_active || ptt_active; // Check RX or TX #if DSP_FFT == 1 diff --git a/si5351.c b/si5351.c index 7248f1a..ed434cc 100644 --- a/si5351.c +++ b/si5351.c @@ -9,30 +9,35 @@ Si5351 principle of operation: ============================== -Crystal frequency (usually 25MHz) is multiplied in a PLL by to obtain . -PLL A and B have independent MSN values, the on channel can be derived from either. - must be between 600MHz and 900MHz, but the spec is more relaxed in reality. - is divided by and to obtain the output frequency . - and are selected to be in the ballpark of desired frequency range. - is then used for tuning . -Only certain values of and are allowed when quadrature output is required. +Crystal frequency Fxtal (usually 25MHz) is multiplied in a PLL by MSN to obtain Fvco. +PLLs A and B have independent MSN values, leading to two Fvco frequencies. +The output Fout on each channel (i) can be derived from either Fvco. +Fvco must be between 600MHz and 900MHz, but the spec is more relaxed in reality. +In the second stage, an Fvco is divided by MSi and Ri to obtain the output frequency Fout. +Only certain values of MSi and Ri are allowed when quadrature output is required. + +Tuning stragtegy: +MSi and Ri are selected to be in the ballpark of desired frequency range. +MSN is then used for tuning Fout. - +-------+ +-------+ +------+ - - Fxtal --> | * MSN | -- Fvco --> | / MSi | --> | / Ri | -- Fout --> - +-------+ +-------+ +------+ - Details: ======== - ---Derivation of Fout--- + + +-------+ +-------+ +------+ + Fxtal --> | * MSN | -- Fvco --> | / MSi | --> | / Ri | -- Fout --> + +-------+ +-------+ +------+ + MSN determines: Fvco = Fxtal * (MSN) , where MSN = a + b/c - MSi and Ri determine: Fout = Fvco / (Ri*MSi) , where MSi = a + b/c (different a, b and c) + MSi and Ri determine: Fout = Fvco / (Ri*MSi) , where MSi = a + b/c (note: different a, b and c) + ---Derivation of the register values, that determine MSN and MSi--- - P1 = 128*a + Floor(128*b/c) - 512 - P2 = 128*b - c*Floor(128*b/c) (P2 = 0 for MSi integer mode, or calculated for MSN tuning) - P3 = c (P3 = 1 for MSi integer mode, or P3 = 1000000 for MSN tuning) + P1 = 128*a + Floor(128*b/c) - 512 (P1 = calculated for MSN tuning, P1 = 750MHz/Fout for MSi integer mode) + P2 = 128*b - c*Floor(128*b/c) (P2 = calculated for MSN tuning, P2 = 0 for MSi integer mode) + P3 = c (P3 = c = 1000000 for MSN tuning, P3 = 1 for MSi integer mode) - This VFO implementation assumes PLLA is used for VFO0 (clk0 and clk1), and PLLB is used for VFO1 (clk2) + + This VFO implementation assumes PLLA is used for VFO 0 (clk0 and clk1), and PLLB is used for VFO 1 (clk2) The algorithm to get from required Fout to synthesizer settings: | calculate new from the desired , based on the current and @@ -46,28 +51,35 @@ Details: | reset PLL (this all assumes that the current settings are consistent, i.e. must be initialized at startup) -Some boundary values: -Ri MSi Lo MHz Hi MHz -1 4 150.000000 225.000000 -1 126 4.761905 7.142857 -32 4 4.687500 7.031250 -32 126 0.148810 0.223214 -128 4 1.171875 1.757813 -128 126 0.037202 0.055804 +Calculate MSi: + MSi = 750MHz/(Fout*Ri) // Target for mid-band, i.e. Fvco=750MHz + MSi &= 0xfe // Make it even, minimum is 4 in case of integer mode -MSi: target for mid-band, i.e. Fvco=750MHz - MSi = 750MHz/(Fout*Ri) - MSi &= 0xfe // Make it even +Calculate MSN: + MSN = MSi*Ri*Fout/Fxtal (spec mandates between 24 and 36, but could be stretched) + + +Some boundary values, assuming 600M < Fvco < 900M. +With low end 400M, Low MHz is multiplied with 2/3. +------------+--------------+-----------------------+ +Ri MSi : a b c | Low MHz High MHz +1 4 : 4 0 1 | 150.000000 225.000000 +1 126 : 126 0 1 | 4.761905 7.142857 +32 4 : 4 0 1 | 4.687500 7.031250 +32 126 : 126 0 1 | 0.148810 0.223214 +128 4 : 4 0 1 | 1.171875 1.757813 +128 126 : 126 0 1 | 0.037202 0.055804 -MSN = MSi*Ri*Fout/Fxtal (should be between 24 and 36) +NOTE: Phase offsets +The PHOFF is given as a multiple of 1/(4*Fvco), so when MSi == PHOFF the shift will be 90deg. +It also implies that MSi must be an integer, and Ri == 1. -Only use MSi even-integers, i.e. d=[4, 6, 8..126], e=0 and f=100000, and set INT bits in reg 22, 23. +Only use MSi even-integers, i.e. a=[4, 6, 8..126], b=0 and c=100000, and set INT bits in reg 22, 23. Quadrature Phase offsets (i.e. delay): -- Offset for MS0 (reg 165) must be 0 (cosine), -- Offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sine), +- Phase offset for MS0 (reg 165) must be 0 (cos: delay = 0), +- Phase offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sin: delay = MSi / 4*Fvco), - Set INV bit (reg 17) to add 180 deg. -NOTE: Phase offsets only work when Ri = 1, This implies that minimum Fout is 4.762MHz at Fvco = 600MHz. Additional flip/flop dividers are needed to get down to 80m band frequencies, or Fvco must be tuned below spec. @@ -180,7 +192,7 @@ Control Si5351 (see AN619): #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.4e9)/SI_XTAL_FREQ) // Should be 600M, but 400MHz works too #define SI_MSN_HI ((0.9e9)/SI_XTAL_FREQ) #define SI_PLL_C 1000000UL // Parameter c for PLL-A and -B setting @@ -190,7 +202,7 @@ typedef struct uint32_t freq; // type can hold up to 4GHz uint8_t flag; // flag != 0 when update needed uint8_t phase; // in quarter waves (0, 1, 2, 3) - uint8_t ri; // Ri (1 .. 128) + uint8_t ri; // Ri (1 .. 128), but should be 1 for VFO 0 uint8_t msi; // MSi parameter a (4, 6, 8 .. 126) double msn; // MSN (24.0 .. 35.9999) } vfo_t; @@ -230,11 +242,11 @@ void si_enable(int i, bool en) data[0] = SI_CLK_OE; if (i==0) { - data[1] = en ? data[1]&~SI_VFO0_DISABLE : data[1]|SI_VFO0_DISABLE; + data[1] = en ? data[1]&~SI_VFO0_DISABLE : data[1]|SI_VFO0_DISABLE; // clk0 and clk1 } else { - data[1] = en ? data[1]&~SI_VFO1_DISABLE : data[1]|SI_VFO1_DISABLE; + data[1] = en ? data[1]&~SI_VFO1_DISABLE : data[1]|SI_VFO1_DISABLE; // clk2 } i2c_write_blocking(i2c0, I2C_VFO, &data[0], 2, false); } @@ -258,19 +270,26 @@ int si_getreg(uint8_t *data, uint8_t reg, uint8_t len) * Set up MSN PLL divider for vfo[i], assuming MSN has been set in vfo[i] * Optimize for speed, this may be called with short intervals * See also SiLabs AN619 section 3.2 + * VFO 0 refers to PLL a, VFO 1 refers to PLL B + + MSN = a + b/c + c = 1000000 (Fxtal/c step size) + P1 = 128*a + Floor(128*b/c) - 512 + P2 = 128*b - c*Floor(128*b/c) + P3 = c + */ void si_setmsn(int i) { uint8_t data[16]; // I2C trx buffer - uint32_t P1, P2; // MSN parameters - uint32_t A; - uint32_t B; + uint32_t P1, P2; // MSN parameters, P3 is SI_PLL_C + uint32_t A, B; if ((i<0)||(i>1)) return; // Check VFO range A = (uint32_t)(floor(vfo[i].msn)); // A is integer part of MSN B = (uint32_t)((vfo[i].msn - (double)A) * SI_PLL_C); // B is C * fraction part of MSN (C is a constant) - P2 = (uint32_t)(floor((double)(128 * B) / (double)SI_PLL_C)); + P2 = (uint32_t)(floor((double)(128 * B) / (double)SI_PLL_C)); // use P2 for intermediate result.. P1 = (uint32_t)(128 * A + P2 - 512); P2 = (uint32_t)(128 * B - SI_PLL_C * P2); @@ -293,7 +312,13 @@ void si_setmsn(int i) /* * Set up registers with MS and R divider for vfo[i], assuming values have been set in vfo[i] * In this implementation we only use integer mode, i.e. b=0 and P3=1 - * See also SiLabs AN619 section 4.1 + + MSi = a + b/c + c = 1, b=0 + P1 = 128*a - 512 + P2 = 0 + P3 = c + */ void si_setmsi(uint8_t i) { @@ -301,7 +326,7 @@ void si_setmsi(uint8_t i) uint32_t P1; uint8_t R; - i=(i>0?1:0); + if ((i<0)||(i>1)) return; // Check VFO range P1 = (uint32_t)(128*(uint32_t)floor(vfo[i].msi) - 512); R = vfo[i].ri; @@ -321,13 +346,13 @@ void si_setmsi(uint8_t i) data[8] = 0x00; i2c_write_blocking(i2c0, I2C_VFO, data, 9, false); - // If vfo[0] also set clk 1 + // If vfo[0] also set clk 1 and phase offset, (integer mode and high drive current for low phase noise). if (i==0) { data[0] = SI_SYNTH_MS1; // Same data in synthesizer 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&(PH000|PH270)) // Phase is either 90 or 270 deg? { data[0] = SI_CLK1_PHOFF; data[1] = vfo[0].msi; // offset == MSi for 90deg @@ -339,12 +364,24 @@ void si_setmsi(uint8_t i) data[1] = 0; // offset == 0 for 0deg i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); } - if (vfo[0].phase&2) // Phase is 180 or 270 deg? + if (vfo[0].phase&(PH180|PH270)) // Phase is 180 or 270 deg? { - data[0] = SI_CLK1_CTL; // Then set the invert flag - data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA + data[0] = SI_CLK1_CTL; // set the invert flag + data[1] = 0x1f; // CLK1: nonINT, PLLA, INV, MS, 8mA i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); } + else + { + data[0] = SI_CLK1_CTL; // clear the invert flag + data[1] = 0x0f; // CLK1: nonINT, PLLA, nonINV, MS, 8mA + i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); + } + } + else + { + data[0] = SI_CLK2_CTL; // set the invert flag + data[1] = 0x2f; // CLK2: nonINT, PLLB, nonINV, MS, 8mA + i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); } // Reset associated PLL @@ -355,10 +392,13 @@ void si_setmsi(uint8_t i) /* + * This function needs to be invoked at regular intervals, e.g. 10x per sec. See hmi.c * For each vfo, calculate required MSN setting, MSN = MSi*Ri*Fout/Fxtal - * If in range, just set MSN registers - * If not in range, recalculate MSi and Ri and also MSN - * Set MSN, MSi and Ri registers (implicitly resets PLL) + * If still in range, + * then just set MSN registers + * else, + * recalculate MSi and Ri as well + * set MSN, MSi and Ri registers (implicitly resets PLL) */ void si_evaluate(void) { @@ -369,20 +409,22 @@ void si_evaluate(void) msn = (double)(vfo[0].msi); // Re-calculate MSN msn = msn * (double)(vfo[0].ri); msn = msn * (double)(vfo[0].freq) / SI_XTAL_FREQ; - if ((msn>=SI_MSN_LO)&&(msn=SI_MSN_LO)&&(msn3000000) + vfo[0].ri = 1; + else if (vfo[0].freq>1000000) vfo[0].ri = 32; else - vfo[0].ri = 1; + vfo[0].ri = 128; // Set MSi if ((vfo[0].freq >= 3000000)&&(vfo[0].freq < 6000000)) // Handle Low end of Ri=1 range @@ -402,6 +444,40 @@ void si_evaluate(void) } if (vfo[1].flag) { + msn = (double)(vfo[1].msi); // Re-calculate MSN + msn = msn * (double)(vfo[1].ri); + msn = msn * (double)(vfo[1].freq) / SI_XTAL_FREQ; + + if ((msn>=SI_MSN_LO)&&(msn3000000) + vfo[1].ri = 1; + else if (vfo[1].freq>1000000) + vfo[1].ri = 32; + else + vfo[1].ri = 128; + + // Set MSi + if ((vfo[1].freq >= 3000000)&&(vfo[1].freq < 6000000)) // Handle Low end of Ri=1 range + vfo[1].msi = (uint8_t)126; // Maximum MSi on Fvco=(4x126)MHz + else // Or calculate MSi on Fvco=750MHz + vfo[1].msi = (uint8_t)(750000000UL / (vfo[1].freq * vfo[1].ri)) & 0x000000fe; + + msn = (double)(vfo[1].msi); // Re-calculate MSN + msn = msn * (double)(vfo[1].ri); + msn = msn * (double)(vfo[1].freq) / SI_XTAL_FREQ; + vfo[1].msn = msn; + + si_setmsn(1); + si_setmsi(1); + } vfo[1].flag = 0; } } @@ -409,92 +485,39 @@ void si_evaluate(void) /* * Initialize the Si5351 VFO registers - * Hard initialize Synth registers to: 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 */ void si_init(void) { uint8_t data[16]; // I2C trx buffer - vfo[0].freq = 10000000; // Check this, should be 7074000? - vfo[0].flag = 0; - vfo[0].phase = 1; - vfo[0].ri = 1; - vfo[0].msi = 68; - vfo[0].msn = 27.2; - vfo[1].freq = 10000000; // Check this, should be 7074000? - vfo[1].flag = 0; - vfo[1].phase = 0; - vfo[1].ri = 1; - vfo[1].msi = 68; - vfo[1].msn = 27.2; - - // PLLA: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240 - data[0] = SI_SYNTH_PLLA; - data[1] = 0x42; // MSNA_P3[15:8] - data[2] = 0x40; // MSNA_P3[7:0] - data[3] = 0x00; // 0b000000 , MSNA_P1[17:16] - data[4] = 0x0b; // MSNA_P1[15:8] - data[5] = 0x99; // MSNA_P1[7:0] - 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(i2c0, I2C_VFO, data, 9, false); - - - // PLLB: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240 - data[0] = SI_SYNTH_PLLB; // Same content - i2c_write_blocking(i2c0, I2C_VFO, data, 9, false); - - // MS0 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 - data[0] = SI_SYNTH_MS0; - data[1] = 0x00; // MS0_P3[15:8] - data[2] = 0x01; // MS0_P3[7:0] - data[3] = 0x00; // 0b0, R0_DIV[2:0] , MS0_DIVBY4[1:0] , MS0_P1[17:16] - data[4] = 0x20; // MS0_P1[15:8] - data[5] = 0x00; // MS0_P1[7:0] - 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(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(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(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(i2c0, I2C_VFO, data, 4, false); - - // Output port settings for 3 clocks - data[0] = SI_CLK0_CTL; - 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(i2c0, I2C_VFO, data, 4, false); - // Disable spread spectrum (startup state is undefined) data[0] = SI_SS_EN; data[1] = 0x00; i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); - // Reset both PLL - data[0] = SI_PLL_RESET; - data[1] = 0xa0; - i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); + // Initialize VFO values + vfo[0].freq = 7074000; + vfo[0].flag = 0; + vfo[0].phase = PH090; + vfo[0].ri = 1; + vfo[0].msi = 106; + vfo[0].msn = ((double)vfo[0].freq*vfo[0].msi)/(double)SI_XTAL_FREQ; + + vfo[1].freq = 10000000; + vfo[1].flag = 0; + vfo[1].phase = PH000; + vfo[1].ri = 1; + vfo[1].msi = 76; + vfo[1].msn = ((double)vfo[1].freq*vfo[1].msi)/(double)SI_XTAL_FREQ; + + // Commit settings + si_setmsn(0); + si_setmsi(0); + si_setmsn(1); + si_setmsi(1); - // Enable only VFO0 outputs - data[0] = SI_CLK_OE; - data[1] = ~SI_VFO0_DISABLE; - i2c_write_blocking(i2c0, I2C_VFO, data, 2, false); + // Enable only VFO 0 outputs + si_enable(0, true); + si_enable(1, false); } diff --git a/si5351.h b/si5351.h index a557451..78a5720 100644 --- a/si5351.h +++ b/si5351.h @@ -10,13 +10,18 @@ * VFO 0 is a quadrature clock on outputs 0 and 1, * VFO 1 is a regular clock on output 2. * - * The quadrature clock allows to set shared frequency and phase offsets of 0, 90, 180 and 270 deg - * The regular clock just allows to set frequency, phase is ignored + * VFO 0 allows to set frequency and phase offsets of 0-90-180-270 deg (delay clk1 wrt clk0) + * VFO 1 just allows to set frequency, phase is ignored * + * Use the 'set' functions to change VFO settings. + * Make regular calls to the 'evaluate' function to commit the changes (if any). + * To get smooth tuning, a suggested interval is 0.1sec, e.g. from a timer loop + * * See si5351.c for more information * */ +// Phase definitions for si_setphase() #define PH000 0 #define PH090 1 #define PH180 2