Corrected some errors in si5351 driver
pull/13/head
ArjanteMarvelde 2022-08-03 12:06:30 +02:00
rodzic 53e7c155f5
commit caaf154a13
3 zmienionych plików z 167 dodań i 138 usunięć

7
dsp.c
Wyświetl plik

@ -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

289
si5351.c
Wyświetl plik

@ -9,30 +9,35 @@
Si5351 principle of operation:
==============================
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.
<Fvco> must be between 600MHz and 900MHz, but the spec is more relaxed in reality.
<Fvco> is divided by <MSi> and <Ri> to obtain the output frequency <Fout>.
<MSi> and <Ri> are selected to be in the ballpark of desired frequency range.
<MSN> is then used for tuning <Fout>.
Only certain values of <MSi> and <Ri> 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 <MSN> from the desired <Fout>, based on the current <Ri> and <MSi>
@ -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_HI))
if ((msn>=SI_MSN_LO)&&(msn<SI_MSN_HI)) // Check MSN range
{
vfo[0].msn = msn;
si_setmsn(0);
}
else
{
// Pre-scale Ri, stretch down Ri=1 range
if (vfo[0].freq<1000000)
vfo[0].ri = 128;
else if (vfo[0].freq<3000000)
// Pre-scale Ri, stretch down Ri=1 range to 3MHz
// Otherwise use just 32 and 128
if (vfo[0].freq>3000000)
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)&&(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;
}
}
@ -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);
}

Wyświetl plik

@ -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