kopia lustrzana https://github.com/ArjanteMarvelde/uSDR-pico
rodzic
53e7c155f5
commit
caaf154a13
7
dsp.c
7
dsp.c
|
@ -338,13 +338,14 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
|
||||||
// Crude AGC mechanism **TO BE IMPROVED**
|
// Crude AGC mechanism **TO BE IMPROVED**
|
||||||
if (!tx_enabled)
|
if (!tx_enabled)
|
||||||
{
|
{
|
||||||
|
// Approximate amplitude, with alpha max + beta min function
|
||||||
uint32_t i=adc_level[1],q=adc_level[0];
|
uint32_t i=adc_level[1],q=adc_level[0];
|
||||||
if (i>q)
|
if (i>q)
|
||||||
temp = (MAX(i,((29*i/32) + (61*q/128))))>>LSH;
|
temp = (MAX(i,((29*i/32) + (61*q/128))))>>LSH;
|
||||||
else
|
else
|
||||||
temp = (MAX(q,((29*q/32) + (61*i/128))))>>LSH;
|
temp = (MAX(q,((29*q/32) + (61*i/128))))>>LSH;
|
||||||
s_rssi = MAX(1,temp);
|
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;
|
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
|
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
|
if (++dsp_tick >= BUFSIZE) // Increment tick and check range
|
||||||
{
|
{
|
||||||
dsp_tick = 0; // Reset counter
|
dsp_tick = 0; // Reset counter
|
||||||
|
@ -505,7 +506,7 @@ void __not_in_flash_func(dsp_loop)()
|
||||||
rx(); // Do RX signal processing
|
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
|
tx_enabled = vox_active || ptt_active; // Check RX or TX
|
||||||
|
|
||||||
#if DSP_FFT == 1
|
#if DSP_FFT == 1
|
||||||
|
|
289
si5351.c
289
si5351.c
|
@ -9,30 +9,35 @@
|
||||||
|
|
||||||
Si5351 principle of operation:
|
Si5351 principle of operation:
|
||||||
==============================
|
==============================
|
||||||
Crystal frequency <Fxtal> (usually 25MHz) is multiplied in a PLL by <MSN> to obtain <Fvco>.
|
Crystal frequency Fxtal (usually 25MHz) is multiplied in a PLL by MSN to obtain Fvco.
|
||||||
PLL A and B have independent MSN values, the <Fout> on channel <i> can be derived from either.
|
PLLs A and B have independent MSN values, leading to two Fvco frequencies.
|
||||||
<Fvco> must be between 600MHz and 900MHz, but the spec is more relaxed in reality.
|
The output Fout on each channel (i) can be derived from either Fvco.
|
||||||
<Fvco> is divided by <MSi> and <Ri> to obtain the output frequency <Fout>.
|
Fvco must be between 600MHz and 900MHz, but the spec is more relaxed in reality.
|
||||||
<MSi> and <Ri> are selected to be in the ballpark of desired frequency range.
|
In the second stage, an Fvco is divided by MSi and Ri to obtain the output frequency Fout.
|
||||||
<MSN> is then used for tuning <Fout>.
|
Only certain values of MSi and Ri are allowed when quadrature output is required.
|
||||||
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:
|
Details:
|
||||||
========
|
========
|
||||||
---Derivation of Fout---
|
|
||||||
|
+-------+ +-------+ +------+
|
||||||
|
Fxtal --> | * MSN | -- Fvco --> | / MSi | --> | / Ri | -- Fout -->
|
||||||
|
+-------+ +-------+ +------+
|
||||||
|
|
||||||
MSN determines: Fvco = Fxtal * (MSN) , where MSN = a + b/c
|
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---
|
---Derivation of the register values, that determine MSN and MSi---
|
||||||
P1 = 128*a + Floor(128*b/c) - 512
|
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 = 0 for MSi integer mode, or calculated for MSN tuning)
|
P2 = 128*b - c*Floor(128*b/c) (P2 = calculated for MSN tuning, P2 = 0 for MSi integer mode)
|
||||||
P3 = c (P3 = 1 for MSi integer mode, or P3 = 1000000 for MSN tuning)
|
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:
|
The algorithm to get from required Fout to synthesizer settings:
|
||||||
| calculate new <MSN> from the desired <Fout>, based on the current <Ri> and <MSi>
|
| calculate new <MSN> from the desired <Fout>, based on the current <Ri> and <MSi>
|
||||||
|
@ -46,28 +51,35 @@ Details:
|
||||||
| reset PLL
|
| reset PLL
|
||||||
(this all assumes that the current settings are consistent, i.e. must be initialized at startup)
|
(this all assumes that the current settings are consistent, i.e. must be initialized at startup)
|
||||||
|
|
||||||
Some boundary values:
|
Calculate MSi:
|
||||||
Ri MSi Lo MHz Hi MHz
|
MSi = 750MHz/(Fout*Ri) // Target for mid-band, i.e. Fvco=750MHz
|
||||||
1 4 150.000000 225.000000
|
MSi &= 0xfe // Make it even, minimum is 4 in case of integer mode
|
||||||
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
|
|
||||||
|
|
||||||
MSi: target for mid-band, i.e. Fvco=750MHz
|
Calculate MSN:
|
||||||
MSi = 750MHz/(Fout*Ri)
|
MSN = MSi*Ri*Fout/Fxtal (spec mandates between 24 and 36, but could be stretched)
|
||||||
MSi &= 0xfe // Make it even
|
|
||||||
|
|
||||||
|
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):
|
Quadrature Phase offsets (i.e. delay):
|
||||||
- Offset for MS0 (reg 165) must be 0 (cosine),
|
- Phase offset for MS0 (reg 165) must be 0 (cos: delay = 0),
|
||||||
- Offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sine),
|
- 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.
|
- 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.
|
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.
|
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_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_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
|
||||||
|
|
||||||
|
@ -190,7 +202,7 @@ typedef struct
|
||||||
uint32_t freq; // type can hold up to 4GHz
|
uint32_t freq; // type can hold up to 4GHz
|
||||||
uint8_t flag; // flag != 0 when update needed
|
uint8_t flag; // flag != 0 when update needed
|
||||||
uint8_t phase; // in quarter waves (0, 1, 2, 3)
|
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)
|
uint8_t msi; // MSi parameter a (4, 6, 8 .. 126)
|
||||||
double msn; // MSN (24.0 .. 35.9999)
|
double msn; // MSN (24.0 .. 35.9999)
|
||||||
} vfo_t;
|
} vfo_t;
|
||||||
|
@ -230,11 +242,11 @@ void si_enable(int i, bool en)
|
||||||
data[0] = SI_CLK_OE;
|
data[0] = SI_CLK_OE;
|
||||||
if (i==0)
|
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
|
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);
|
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]
|
* 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
|
* Optimize for speed, this may be called with short intervals
|
||||||
* See also SiLabs AN619 section 3.2
|
* 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)
|
void si_setmsn(int i)
|
||||||
{
|
{
|
||||||
uint8_t data[16]; // I2C trx buffer
|
uint8_t data[16]; // I2C trx buffer
|
||||||
uint32_t P1, P2; // MSN parameters
|
uint32_t P1, P2; // MSN parameters, P3 is SI_PLL_C
|
||||||
uint32_t A;
|
uint32_t A, B;
|
||||||
uint32_t B;
|
|
||||||
|
|
||||||
if ((i<0)||(i>1)) return; // Check VFO range
|
if ((i<0)||(i>1)) return; // Check VFO range
|
||||||
|
|
||||||
A = (uint32_t)(floor(vfo[i].msn)); // A is integer part of MSN
|
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)
|
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);
|
P1 = (uint32_t)(128 * A + P2 - 512);
|
||||||
P2 = (uint32_t)(128 * B - SI_PLL_C * P2);
|
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]
|
* 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
|
* 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)
|
void si_setmsi(uint8_t i)
|
||||||
{
|
{
|
||||||
|
@ -301,7 +326,7 @@ void si_setmsi(uint8_t i)
|
||||||
uint32_t P1;
|
uint32_t P1;
|
||||||
uint8_t R;
|
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);
|
P1 = (uint32_t)(128*(uint32_t)floor(vfo[i].msi) - 512);
|
||||||
R = vfo[i].ri;
|
R = vfo[i].ri;
|
||||||
|
@ -321,13 +346,13 @@ void si_setmsi(uint8_t i)
|
||||||
data[8] = 0x00;
|
data[8] = 0x00;
|
||||||
i2c_write_blocking(i2c0, 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 and phase offset, (integer mode and high drive current for low phase noise).
|
||||||
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(i2c0, 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&(PH000|PH270)) // Phase is either 90 or 270 deg?
|
||||||
{
|
{
|
||||||
data[0] = SI_CLK1_PHOFF;
|
data[0] = SI_CLK1_PHOFF;
|
||||||
data[1] = vfo[0].msi; // offset == MSi for 90deg
|
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
|
data[1] = 0; // offset == 0 for 0deg
|
||||||
i2c_write_blocking(i2c0, 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&(PH180|PH270)) // Phase is 180 or 270 deg?
|
||||||
{
|
{
|
||||||
data[0] = SI_CLK1_CTL; // Then set the invert flag
|
data[0] = SI_CLK1_CTL; // set the invert flag
|
||||||
data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA
|
data[1] = 0x1f; // CLK1: nonINT, PLLA, INV, MS, 8mA
|
||||||
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
|
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
|
// 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
|
* For each vfo, calculate required MSN setting, MSN = MSi*Ri*Fout/Fxtal
|
||||||
* If in range, just set MSN registers
|
* If still in range,
|
||||||
* If not in range, recalculate MSi and Ri and also MSN
|
* then just set MSN registers
|
||||||
* Set MSN, MSi and Ri registers (implicitly resets PLL)
|
* else,
|
||||||
|
* recalculate MSi and Ri as well
|
||||||
|
* set MSN, MSi and Ri registers (implicitly resets PLL)
|
||||||
*/
|
*/
|
||||||
void si_evaluate(void)
|
void si_evaluate(void)
|
||||||
{
|
{
|
||||||
|
@ -369,20 +409,22 @@ void si_evaluate(void)
|
||||||
msn = (double)(vfo[0].msi); // Re-calculate MSN
|
msn = (double)(vfo[0].msi); // Re-calculate MSN
|
||||||
msn = msn * (double)(vfo[0].ri);
|
msn = msn * (double)(vfo[0].ri);
|
||||||
msn = msn * (double)(vfo[0].freq) / SI_XTAL_FREQ;
|
msn = msn * (double)(vfo[0].freq) / SI_XTAL_FREQ;
|
||||||
if ((msn>=SI_MSN_LO)&&(msn<SI_MSN_HI))
|
|
||||||
|
if ((msn>=SI_MSN_LO)&&(msn<SI_MSN_HI)) // Check MSN range
|
||||||
{
|
{
|
||||||
vfo[0].msn = msn;
|
vfo[0].msn = msn;
|
||||||
si_setmsn(0);
|
si_setmsn(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Pre-scale Ri, stretch down Ri=1 range
|
// Pre-scale Ri, stretch down Ri=1 range to 3MHz
|
||||||
if (vfo[0].freq<1000000)
|
// Otherwise use just 32 and 128
|
||||||
vfo[0].ri = 128;
|
if (vfo[0].freq>3000000)
|
||||||
else if (vfo[0].freq<3000000)
|
vfo[0].ri = 1;
|
||||||
|
else if (vfo[0].freq>1000000)
|
||||||
vfo[0].ri = 32;
|
vfo[0].ri = 32;
|
||||||
else
|
else
|
||||||
vfo[0].ri = 1;
|
vfo[0].ri = 128;
|
||||||
|
|
||||||
// Set MSi
|
// Set MSi
|
||||||
if ((vfo[0].freq >= 3000000)&&(vfo[0].freq < 6000000)) // Handle Low end of Ri=1 range
|
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)
|
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)&&(msn<SI_MSN_HI)) // Check MSN range
|
||||||
|
{
|
||||||
|
vfo[1].msn = msn;
|
||||||
|
si_setmsn(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pre-scale Ri, stretch down Ri=1 range to 3MHz
|
||||||
|
// Otherwise use just 32 and 128
|
||||||
|
if (vfo[1].freq>3000000)
|
||||||
|
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;
|
vfo[1].flag = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,92 +485,39 @@ void si_evaluate(void)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the Si5351 VFO registers
|
* 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)
|
void si_init(void)
|
||||||
{
|
{
|
||||||
uint8_t data[16]; // I2C trx buffer
|
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)
|
// 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(i2c0, I2C_VFO, data, 2, false);
|
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
|
||||||
|
|
||||||
// Reset both PLL
|
// Initialize VFO values
|
||||||
data[0] = SI_PLL_RESET;
|
vfo[0].freq = 7074000;
|
||||||
data[1] = 0xa0;
|
vfo[0].flag = 0;
|
||||||
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
|
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
|
// Enable only VFO 0 outputs
|
||||||
data[0] = SI_CLK_OE;
|
si_enable(0, true);
|
||||||
data[1] = ~SI_VFO0_DISABLE;
|
si_enable(1, false);
|
||||||
i2c_write_blocking(i2c0, I2C_VFO, data, 2, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
si5351.h
9
si5351.h
|
@ -10,13 +10,18 @@
|
||||||
* VFO 0 is a quadrature clock on outputs 0 and 1,
|
* VFO 0 is a quadrature clock on outputs 0 and 1,
|
||||||
* VFO 1 is a regular clock on output 2.
|
* 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
|
* VFO 0 allows to set frequency and phase offsets of 0-90-180-270 deg (delay clk1 wrt clk0)
|
||||||
* The regular clock just allows to set frequency, phase is ignored
|
* 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
|
* See si5351.c for more information
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Phase definitions for si_setphase()
|
||||||
#define PH000 0
|
#define PH000 0
|
||||||
#define PH090 1
|
#define PH090 1
|
||||||
#define PH180 2
|
#define PH180 2
|
||||||
|
|
Ładowanie…
Reference in New Issue