minor spur/phase noise improvements

This commit is contained in:
Jan Käberich 2021-03-22 21:28:03 +01:00
parent ac1e9fcec6
commit 06a7365a0c
5 changed files with 44 additions and 28 deletions

View File

@ -119,6 +119,29 @@ bool Si5351C::SetCLK(uint8_t clknum, uint32_t frequency, PLL source, DriveStreng
return WriteClkConfig(c, clknum);
}
bool Si5351C::SetBypass(uint8_t clknum, PLLSource source, DriveStrength strength) {
// compile CLKControl register
uint8_t clkcontrol = 0x00;
if (source == PLLSource::CLKIN) {
clkcontrol |= 0x04;
}
switch (strength) {
case DriveStrength::mA2:
break;
case DriveStrength::mA4:
clkcontrol |= 0x01;
break;
case DriveStrength::mA6:
clkcontrol |= 0x02;
break;
case DriveStrength::mA8:
clkcontrol |= 0x03;
break;
}
Reg reg = (Reg) ((int) Reg::CLK0Control + clknum);
return WriteRegister(reg, clkcontrol);
}
bool Si5351C::SetCLKtoXTAL(uint8_t clknum) {
Reg reg = (Reg) ((int) Reg::CLK0Control + clknum);
LOG_INFO("Connecting CLK%d to XTAL", clknum);
@ -163,8 +186,15 @@ bool Si5351C::WritePLLConfig(PLLConfig config, PLL pll) {
PllData[5] = ((config.P3 >> 12) & 0xF0) | ((config.P2 >> 16) & 0x0F);
PllData[6] = (config.P2 >> 8) & 0xFF;
PllData[7] = config.P2 & 0xFF;
// Check if integer mode is applicable
Reg reg = pll == PLL::A ? Reg::CLK6Control : Reg::CLK7Control;
if (config.P2 == 0 && config.P1 % 128 == 0) {
SetBits(reg, 0x40);
} else {
ClearBits(reg, 0x40);
}
bool success = true;
Reg reg = pll == PLL::A ? Reg::MSNA_CONFIG : Reg::MSNB_CONFIG;
reg = pll == PLL::A ? Reg::MSNA_CONFIG : Reg::MSNB_CONFIG;
success &= WriteRegisterRange(reg, PllData, sizeof(PllData));
reg = pll == PLL::A ? Reg::CLK6Control : Reg::CLK7Control;
if (config.IntegerMode) {
@ -229,6 +259,11 @@ bool Si5351C::WriteClkConfig(ClkConfig config, uint8_t clknum) {
clkcontrol |= 0x03;
break;
}
// Check if integer mode is applicable
if(config.P2 == 0 && config.P1%128 == 0) {
// Use integer mode
clkcontrol |= 0x40;
}
Reg reg = (Reg) ((int) Reg::CLK0Control + clknum);
success &= WriteRegister(reg, clkcontrol);
if (clknum <= 5) {
@ -242,6 +277,7 @@ bool Si5351C::WriteClkConfig(ClkConfig config, uint8_t clknum) {
ClkData[5] = ((config.P3 >> 12) & 0xF0) | ((config.P2 >> 16) & 0x0F);
ClkData[6] = (config.P2 >> 8) & 0xFF;
ClkData[7] = config.P2 & 0xFF;
// Calculate address of register control block
reg = (Reg) ((int) Reg::MS0_CONFIG + 8 * clknum);
success &= WriteRegisterRange(reg, ClkData, sizeof(ClkData));
@ -318,28 +354,6 @@ void Si5351C::FindOptimalDivider(uint32_t f_pll, uint32_t f, uint32_t &P1,
// always using the highest modulus divider results in less than 1Hz deviation for all frequencies, that is good enough
uint32_t best_c = (1UL << 20) - 1;
uint32_t best_b = (uint64_t) f_rem * best_c / f;
// uint32_t best_deviation = UINT32_MAX;
// for (uint32_t c = (1UL << 20) - 1; c >= (1UL << 19); c--) {
// uint32_t guess_b = (uint64_t) f_rem * c / f;
// for (uint32_t b = guess_b; b <= guess_b + 1; b++) {
// int32_t f_div = (uint64_t) f * b / c;
// uint32_t deviation = abs(f_rem - f_div);
// if (deviation < best_deviation) {
// best_b = b;
// best_c = c;
// best_deviation = deviation;
// if (deviation <= 3) {
// break;
// }
// }
// }
// if (best_deviation <= 3) {
// break;
// }
// }
// LOG_DEBUG(
// "Optimal divider for %luHz/%luHz is: a=%lu, b=%lu, c=%lu (%luHz deviation)",
// f_pll, f, a, best_b, best_c, best_deviation);
// convert to Si5351C parameters
uint32_t floor = 128 * best_b / best_c;
P1 = 128 * a + floor - 512;
@ -360,4 +374,3 @@ bool Si5351C::ReadRawCLKConfig(uint8_t clknum, uint8_t *config) {
return ReadRegisterRange(reg, config, 8);
}

View File

@ -33,6 +33,7 @@ public:
bool ConfigureCLKIn(uint32_t clkin_freq);
bool SetPLL(PLL pll, uint32_t frequency, PLLSource src);
bool SetCLK(uint8_t clknum, uint32_t frequency, PLL source, DriveStrength strength = DriveStrength::mA2, uint32_t PLLFreqOverride = 0);
bool SetBypass(uint8_t clknum, PLLSource source, DriveStrength strength = DriveStrength::mA2);
bool SetCLKtoXTAL(uint8_t clknum);
bool SetCLKToCLKIN(uint8_t clknum);
bool Enable(uint8_t clknum);

View File

@ -42,7 +42,7 @@ bool MAX2871::Init(uint32_t f_ref, bool doubler, uint16_t r, bool div2) {
// recommended phase setting
regs[1] |= (1UL << 15);
SetMode(Mode::LowSpur2);
SetMode(Mode::LowNoise);
// for all other CP modes the PLL reports unlock condition (output signal appears to be locked)
SetCPMode(CPMode::CP20);
SetCPCurrent(15);

View File

@ -92,10 +92,10 @@ bool HW::Init() {
Si5351.Init();
// Use Si5351 to generate reference frequencies for other PLLs and ADC
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::XTAL);
Si5351.SetPLL(Si5351C::PLL::A, 832000000, Si5351C::PLLSource::XTAL);
while(!Si5351.Locked(Si5351C::PLL::A));
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::XTAL);
Si5351.SetPLL(Si5351C::PLL::B, 832000000, Si5351C::PLLSource::XTAL);
while(!Si5351.Locked(Si5351C::PLL::B));
extRefInUse = 0;
@ -103,8 +103,10 @@ bool HW::Init() {
Si5351.Disable(SiChannel::ReferenceOut);
// Both MAX2871 get a 100MHz reference
// Si5351.SetBypass(SiChannel::Source, Si5351C::PLLSource::XTAL);
Si5351.SetCLK(SiChannel::Source, HW::PLLRef, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
Si5351.Enable(SiChannel::Source);
// Si5351.SetBypass(SiChannel::LO1, Si5351C::PLLSource::XTAL);
Si5351.SetCLK(SiChannel::LO1, HW::PLLRef, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
Si5351.Enable(SiChannel::LO1);
// 16MHz FPGA clock

View File

@ -34,7 +34,7 @@ static constexpr uint32_t IF2 = 250000;
static constexpr uint32_t LO1_minFreq = 25000000;
static constexpr uint32_t MaxSamples = 130944;
static constexpr uint32_t MinSamples = 16;
static constexpr uint32_t PLLRef = 100000000;
static constexpr uint32_t PLLRef = 104000000;
static constexpr uint32_t BandSwitchFrequency = 25000000;
static constexpr uint8_t ADCprescaler = FPGA::Clockrate / ADCSamplerate;