Files
SDRPlusPlus/rx888_source/src/CyAPI/Si5351.cpp
2020-11-12 00:53:38 +01:00

257 lines
8.9 KiB
C++

#include "Si5351.h"
#include "openFX3.h"
#include <spdlog/spdlog.h>
#define DbgPrintf spdlog::info
#define SI_CLK0_CONTROL 16 // Registers
#define SI_CLK1_CONTROL 17
#define SI_CLK2_CONTROL 18
#define SI_SYNTH_PLL_A 26
#define SI_SYNTH_PLL_B 34
#define SI_SYNTH_MS_0 42
#define SI_SYNTH_MS_1 50
#define SI_SYNTH_MS_2 58
#define SI_PLL_RESET (0x177)
#define SI_R_DIV_1 (0) // 0b00000000 /
#define SI_R_DIV_2 (0x10) // 0b00010000
#define SI_R_DIV_4 (0x20) // 0b00100000
#define SI_R_DIV_8 (0x30) // 0b00110000
#define SI_R_DIV_16 (0x40) // 0b01000000
#define SI_R_DIV_32 (0x50) // 0b01010000
#define SI_R_DIV_64 (0x60) // 0b01100000
#define SI_R_DIV_128 (0x70) // 0b01110000
#define SI_CLK_SRC_PLL_A 0b00000000
#define SI_CLK_SRC_PLL_B 0b00100000
#define SI5351_FREQ 27000000 // Crystal frequency
#define SI5351_PLL_FIXED 80000000000ULL
#define SI5351_ADDR (0x60 << 1 )
#define SI5351_CRYSTAL_LOAD 183
#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
#define SI5351_CRYSTAL_LOAD_0PF (0<<6)
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
#define SI5351_PLL_INPUT_SOURCE 15
#define SI5351_CLKIN_DIV_MASK (3<<6)
#define SI5351_CLKIN_DIV_1 (0<<6)
#define SI5351_CLKIN_DIV_2 (1<<6)
#define SI5351_CLKIN_DIV_4 (2<<6)
#define SI5351_CLKIN_DIV_8 (3<<6)
#define SI5351_PLLB_SOURCE (1<<3)
#define SI5351_PLLA_SOURCE (1<<2)
void Si5351init()
{
// Set crystal load capacitance
fx3SendI2cbyte(SI5351_ADDR, SI5351_CRYSTAL_LOAD, SI5351_CRYSTAL_LOAD_6PF | 0b00010010);
fx3SendI2cbyte(SI5351_ADDR, SI_CLK0_CONTROL, 0x80); // clocks off
fx3SendI2cbyte(SI5351_ADDR, SI_CLK1_CONTROL, 0x80);
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x80);
// si5351aSetFrequency(ADC_FREQ, R820T_ZERO);
DbgPrintf("Si5351_init()\n");
}
#define _PLLDEBUG_
//
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
//
static void setupPLL(UINT8 pll, UINT8 mult, UINT32 num, UINT32 denom)
{
UINT32 P1; // PLL config register P1
UINT32 P2; // PLL config register P2
UINT32 P3; // PLL config register P3
UINT8 data[8];
// the actual multiplier is mult + num / denom
#ifdef _PLLDEBUG_
double fa;
double x = 27000000.0;
fa = x * ((double)mult + (double)num / (double)denom);
DbgPrintf((char *) "pll = %d , mult = %d , num = %d , denom = %d\n", pll, mult, num, denom);
DbgPrintf((char *) "pll target %e \n", fa);
#endif
P1 = (UINT32)(128 * ((float)num / (float)denom));
P1 = (UINT32)(128 * (UINT32)(mult)+P1 - 512);
P2 = (UINT32)(128 * ((float)num / (float)denom));
P2 = (UINT32)(128 * num - denom * P2);
P3 = denom;
data[0] = (P3 & 0x0000FF00) >> 8;
data[1] = (P3 & 0x000000FF);
data[2] = (P1 & 0x00030000) >> 16;
data[3] = (P1 & 0x0000FF00) >> 8;
data[4] = (P1 & 0x000000FF);
data[5] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[6] = (P2 & 0x0000FF00) >> 8;
data[7] = (P2 & 0x000000FF);
fx3SendI2cbytes(SI5351_ADDR, pll, data, sizeof(data));
}
//
// Set up MultiSynth with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate register, it is a #define in si5351a.h
//
static void setupMultisynth(UINT8 synth, UINT32 divider, UINT8 rDiv)
{
UINT32 P1; // Synth config register P1
UINT32 P2; // Synth config register P2
UINT32 P3; // Synth config register P3
UINT8 data[8];
#ifdef _PLLDEBUG_
DbgPrintf("setupMultisynth synth = %d divider = %d rDiv= %d \n", synth, divider, rDiv);
//DbgPrintf("output expected f = %e\n", fa / divider);
#endif
P1 = 128 * divider - 512;
P2 = 0; // P2 = 0, P3 = 1 forces an integer value for the divider
P3 = 1;
data[0] = (P3 & 0x0000FF00) >> 8;
data[1] = (P3 & 0x000000FF);
data[2] = ((P1 & 0x00030000) >> 16) | rDiv;
data[3] = (P1 & 0x0000FF00) >> 8;
data[4] = (P1 & 0x000000FF);
data[5] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[6] = (P2 & 0x0000FF00) >> 8;
data[7] = (P2 & 0x000000FF);
fx3SendI2cbytes(SI5351_ADDR, synth, data, sizeof(data));
}
//
// Switches off Si5351a output
// Example: si5351aOutputOff(SI_CLK0_CONTROL);
// will switch off output CLK0
//
void si5351aOutputOff(UINT8 clk)
{
fx3SendI2cbyte(SI5351_ADDR, clk, 0x80); // Refer to SiLabs AN619 to see bit values - 0x80 turns off the output stage
}
void si5351aSetFrequency(UINT32 freq, UINT32 freq2)
{
UINT32 frequency;
UINT32 pllFreq;
UINT32 xtalFreq = SI5351_FREQ;
UINT32 l;
double f;
UINT8 mult;
UINT32 num;
UINT32 denom;
UINT32 divider;
UINT32 rdiv;
double corr = 0.9999314;
//double corr = 1.0;
DbgPrintf("BBRF103 si5351 SetFreq ADC sampling:%d R820T reference:%d\n", freq, freq2);
rdiv = (UINT32)SI_R_DIV_1;
frequency = (UINT32)((double)freq * corr);
while (frequency < 1000000)
{
frequency = frequency * 2;
rdiv += SI_R_DIV_2;
}
#ifdef _PLLDEBUG_
DbgPrintf((char *) "\nCLK0 frequency %d \n", frequency);
#endif
divider = 900000000UL / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency: the divider * desired output frequency
#ifdef _PLLDEBUG_
DbgPrintf((char *) "pllA Freq %d \n", pllFreq);
#endif
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = (double)l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = (UINT32)f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL A with the calculated multiplication ratio
setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// represented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(SI_SYNTH_MS_0, divider, rdiv);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
fx3SendI2cbyte((UINT8)SI5351_ADDR, (UINT8)SI_PLL_RESET, (UINT8)0x20); //pllA
// Finally switch on the CLK0 output (0x4F)
// and set the MultiSynth0 input to be PLL A
fx3SendI2cbyte(SI5351_ADDR, SI_CLK0_CONTROL, 0x4F | SI_CLK_SRC_PLL_A);
if (freq2 > 0)
{
// calculate clk2
frequency = (UINT32)((double)freq2 * corr);
rdiv = SI_R_DIV_1;
xtalFreq = SI5351_FREQ;
while (frequency <= 1000000)
{
frequency = frequency * 2;
rdiv += SI_R_DIV_2;
}
#ifdef _PLLDEBUG_
DbgPrintf((char *) "\nCLK2 frequency %d \n", frequency);
#endif
divider = 900000000UL / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency: the divider * desired output frequency
#ifdef _PLLDEBUG_
DbgPrintf((char *) "pllB Freq %d \n", pllFreq);
#endif
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = (double)l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = (UINT32)f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL B with the calculated multiplication ratio
setupPLL(SI_SYNTH_PLL_B, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// represented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(SI_SYNTH_MS_2, divider, rdiv);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
fx3SendI2cbyte((UINT8)SI5351_ADDR, (UINT8)SI_PLL_RESET, (UINT8)0x80); //pllB
// Finally switch on the CLK2 output (0x4C)
// and set the MultiSynth0 input to be PLL A
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x4C | SI_CLK_SRC_PLL_B); // select PLLB
// calculate clk2
}
else
{
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x80);
}
}