feat: improve frequency accuracy

This commit is contained in:
TT 2019-12-09 08:05:59 +09:00
parent 75ea464c91
commit 6a88f8ed8f

124
si5351.c
View file

@ -222,55 +222,66 @@ si5351_setupMultisynth(uint8_t output,
si5351_write(clkctrl[output], dat); si5351_write(clkctrl[output], dat);
} }
static uint32_t
gcd(uint32_t x, uint32_t y)
{
uint32_t z;
while (y != 0) {
z = x % y;
x = y;
y = z;
}
return x;
}
#define XTALFREQ 26000000L #define XTALFREQ 26000000L
#define PLL_N 32 #define PLL_N 32
#define PLLFREQ (XTALFREQ * PLL_N) #define PLLFREQ (XTALFREQ * PLL_N)
void void
si5351_set_frequency_fixedpll(int channel, int pll, int pllfreq, int freq, si5351_set_frequency_fixedpll(int channel, int pll, int pllfreq, int freq,
uint32_t rdiv, uint8_t drive_strength) int rdiv, uint8_t drive_strength, int mul)
{ {
int32_t div = pllfreq / freq; // range: 8 ~ 1800 int denom = freq;
int32_t num = pllfreq - freq * div; int div = (pllfreq * mul) / denom; // range: 8 ~ 1800
int32_t denom = freq; int num = (pllfreq * mul) - denom * div;
//int32_t k = freq / (1<<20) + 1;
int32_t k = gcd(num, denom); // cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
num /= k; int max_denominator = (1 << 20) - 1;
denom /= k; if (denom > max_denominator) {
while (denom >= (1<<20)) { int p0 = 0, q0 = 1, p1 = 1, q1 = 0;
num >>= 1; while (denom != 0) {
denom >>= 1; int a = num / denom;
int q2 = q0 + a*q1;
if (q2 > max_denominator)
break;
int p2 = p0 + a*p1;
p0 = p1; q0 = q1; p1 = p2; q1 = q2;
int new_denom = num - a * denom;
num = denom; denom = new_denom;
}
num = p1;
denom = q1;
} }
si5351_setupMultisynth(channel, pll, div, num, denom, rdiv, drive_strength); si5351_setupMultisynth(channel, pll, div, num, denom, rdiv, drive_strength);
} }
void void
si5351_set_frequency_fixeddiv(int channel, int pll, int freq, int div, si5351_set_frequency_fixeddiv(int channel, int pll, int freq, int div,
uint8_t drive_strength) uint8_t drive_strength, int mul)
{ {
int32_t pllfreq = freq * div; int denom = XTALFREQ * mul;
int32_t multi = pllfreq / XTALFREQ; int64_t pllfreq = (int64_t)freq * div;
int32_t num = pllfreq - multi * XTALFREQ; int multi = pllfreq / denom;
int32_t denom = XTALFREQ; int num = pllfreq - denom * multi;
int32_t k = gcd(num, denom);
num /= k; // cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
denom /= k; int max_denominator = (1 << 20) - 1;
while (denom >= (1<<20)) { if (denom > max_denominator) {
num >>= 1; int p0 = 0, q0 = 1, p1 = 1, q1 = 0;
denom >>= 1; while (denom != 0) {
int a = num / denom;
int q2 = q0 + a*q1;
if (q2 > max_denominator)
break;
int p2 = p0 + a*p1;
p0 = p1; q0 = q1; p1 = p2; q1 = q2;
int new_denom = num - a * denom;
num = denom; denom = new_denom;
}
num = p1;
denom = q1;
} }
si5351_setupPLL(pll, multi, num, denom); si5351_setupPLL(pll, multi, num, denom);
si5351_setupMultisynth(channel, pll, div, 0, 1, SI5351_R_DIV_1, drive_strength); si5351_setupMultisynth(channel, pll, div, 0, 1, SI5351_R_DIV_1, drive_strength);
} }
@ -285,11 +296,11 @@ si5351_set_frequency(int channel, int freq, uint8_t drive_strength)
{ {
if (freq <= 100000000) { if (freq <= 100000000) {
si5351_setupPLL(SI5351_PLL_B, 32, 0, 1); si5351_setupPLL(SI5351_PLL_B, 32, 0, 1);
si5351_set_frequency_fixedpll(channel, SI5351_PLL_B, PLLFREQ, freq, SI5351_R_DIV_1, drive_strength); si5351_set_frequency_fixedpll(channel, SI5351_PLL_B, PLLFREQ, freq, SI5351_R_DIV_1, drive_strength, 1);
} else if (freq < 150000000) { } else if (freq < 150000000) {
si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 6, drive_strength); si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 6, drive_strength, 1);
} else { } else {
si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 4, drive_strength); si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 4, drive_strength, 1);
} }
} }
@ -313,17 +324,18 @@ si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_streng
int band; int band;
int delay = DELAY_NORMAL; int delay = DELAY_NORMAL;
uint32_t ofreq = freq + offset; uint32_t ofreq = freq + offset;
int mul = 1, omul = 1;
uint32_t rdiv = SI5351_R_DIV_1; uint32_t rdiv = SI5351_R_DIV_1;
if (freq >= config.harmonic_freq_threshold * 3) { if (freq >= config.harmonic_freq_threshold * 3) {
freq /= 5; mul = 5;
ofreq /= 7; omul = 7;
} else if (freq >= config.harmonic_freq_threshold) { } else if (freq >= config.harmonic_freq_threshold) {
freq /= 3; mul = 3;
ofreq /= 5; omul = 5;
} }
if (freq <= 100000000) { if ((freq / mul) < 100000000) {
band = 0; band = 0;
} else if (freq < 150000000) { } else if ((freq / mul) < 150000000) {
band = 1; band = 1;
} else { } else {
band = 2; band = 2;
@ -357,34 +369,34 @@ si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_streng
} }
si5351_set_frequency_fixedpll(0, SI5351_PLL_A, PLLFREQ, ofreq, si5351_set_frequency_fixedpll(0, SI5351_PLL_A, PLLFREQ, ofreq,
rdiv, drive_strength); rdiv, drive_strength, omul);
si5351_set_frequency_fixedpll(1, SI5351_PLL_A, PLLFREQ, freq, si5351_set_frequency_fixedpll(1, SI5351_PLL_A, PLLFREQ, freq,
rdiv, drive_strength); rdiv, drive_strength, mul);
//if (current_band != 0) //if (current_band != 0)
si5351_set_frequency_fixedpll(2, SI5351_PLL_A, PLLFREQ, CLK2_FREQUENCY, si5351_set_frequency_fixedpll(2, SI5351_PLL_A, PLLFREQ, CLK2_FREQUENCY,
SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA, 1);
break; break;
case 1: case 1:
// Set PLL twice on changing from band 2 // Set PLL twice on changing from band 2
if (current_band == 2) { if (current_band == 2) {
si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength); si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength, omul);
si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength); si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength, mul);
} }
// div by 6 mode. both PLL A and B are dedicated for CLK0, CLK1 // div by 6 mode. both PLL A and B are dedicated for CLK0, CLK1
si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength); si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 6, drive_strength, omul);
si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength); si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 6, drive_strength, mul);
si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq * 6, CLK2_FREQUENCY, si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq / mul * 6, CLK2_FREQUENCY,
SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA, 1);
break; break;
case 2: case 2:
// div by 4 mode. both PLL A and B are dedicated for CLK0, CLK1 // div by 4 mode. both PLL A and B are dedicated for CLK0, CLK1
si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 4, drive_strength); si5351_set_frequency_fixeddiv(0, SI5351_PLL_A, ofreq, 4, drive_strength, omul);
si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 4, drive_strength); si5351_set_frequency_fixeddiv(1, SI5351_PLL_B, freq, 4, drive_strength, mul);
si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq * 4, CLK2_FREQUENCY, si5351_set_frequency_fixedpll(2, SI5351_PLL_B, freq / mul * 4, CLK2_FREQUENCY,
SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA); SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA, 1);
break; break;
} }