ubitx-upr/ubitx_20/ubitx_si5351.ino
Ashhar Farhan e481ea2a24
The ubitx production sktech, wireup and circuit
This is the snap of the circuit, wiring instructions for the ubitx pcb. the sketch may change slightly for factory alignment but the rest will remain the same.
2017-12-07 10:18:43 +05:30

117 lines
5.1 KiB
C++

// ************* SI5315 routines - tks Jerry Gaffke, KE7ER ***********************
// An minimalist standalone set of Si5351 routines.
// VCOA is fixed at 875mhz, VCOB not used.
// The output msynth dividers are used to generate 3 independent clocks
// with 1hz resolution to any frequency between 4khz and 109mhz.
// Usage:
// Call si5351bx_init() once at startup with no args;
// Call si5351bx_setfreq(clknum, freq) each time one of the
// three output CLK pins is to be updated to a new frequency.
// A freq of 0 serves to shut down that output clock.
// The global variable si5351bx_vcoa starts out equal to the nominal VCOA
// frequency of 25mhz*35 = 875000000 Hz. To correct for 25mhz crystal errors,
// the user can adjust this value. The vco frequency will not change but
// the number used for the (a+b/c) output msynth calculations is affected.
// Example: We call for a 5mhz signal, but it measures to be 5.001mhz.
// So the actual vcoa frequency is 875mhz*5.001/5.000 = 875175000 Hz,
// To correct for this error: si5351bx_vcoa=875175000;
// Most users will never need to generate clocks below 500khz.
// But it is possible to do so by loading a value between 0 and 7 into
// the global variable si5351bx_rdiv, be sure to return it to a value of 0
// before setting some other CLK output pin. The affected clock will be
// divided down by a power of two defined by 2**si5351_rdiv
// A value of zero gives a divide factor of 1, a value of 7 divides by 128.
// This lightweight method is a reasonable compromise for a seldom used feature.
#define BB0(x) ((uint8_t)x) // Bust int32 into Bytes
#define BB1(x) ((uint8_t)(x>>8))
#define BB2(x) ((uint8_t)(x>>16))
#define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical)
#define SI5351BX_XTALPF 2 // 1:6pf 2:8pf 3:10pf
// If using 27mhz crystal, set XTAL=27000000, MSA=33. Then vco=891mhz
#define SI5351BX_XTAL 25000000 // Crystal freq in Hz
#define SI5351BX_MSA 35 // VCOA is at 25mhz*35 = 875mhz
// User program may have reason to poke new values into these 3 RAM variables
uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate
uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv)
uint8_t si5351bx_drive[3] = {1, 1, 1}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2
uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off
int32_t calibration = 0;
void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c
Wire.beginTransmission(SI5351BX_ADDR);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array
Wire.beginTransmission(SI5351BX_ADDR);
Wire.write(reg);
while (vcnt--) Wire.write(*vals++);
Wire.endTransmission();
}
void si5351bx_init() { // Call once at power-up, start PLLA
uint8_t reg; uint32_t msxp1;
Wire.begin();
i2cWrite(149, 0); // SpreadSpectrum off
i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers
i2cWrite(183, SI5351BX_XTALPF << 6); // Set 25mhz crystal load capacitance
msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional
uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0};
i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs
i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB)
// for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's
// i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4
}
void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz
uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top;
if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range
si5351bx_clken |= 1 << clknum; // shut down the clock
else {
msa = si5351bx_vcoa / fout; // Integer part of vco/fout
msb = si5351bx_vcoa % fout; // Fractional part of vco/fout
msc = fout; // Divide by 2 till fits in reg
while (msc & 0xfff00000) {
msb = msb >> 1;
msc = msc >> 1;
}
msxp1 = (128 * msa + 128 * msb / msc - 512) | (((uint32_t)si5351bx_rdiv) << 20);
msxp2 = 128 * msb - 128 * msb / msc * msc; // msxp3 == msc;
msxp3p2top = (((msc & 0x0F0000) << 4) | msxp2); // 2 top nibbles
uint8_t vals[8] = { BB1(msc), BB0(msc), BB2(msxp1), BB1(msxp1),
BB0(msxp1), BB2(msxp3p2top), BB1(msxp2), BB0(msxp2)
};
i2cWriten(42 + (clknum * 8), vals, 8); // Write to 8 msynth regs
i2cWrite(16 + clknum, 0x0C | si5351bx_drive[clknum]); // use local msynth
si5351bx_clken &= ~(1 << clknum); // Clear bit to enable clock
}
i2cWrite(3, si5351bx_clken); // Enable/disable clock
}
void si5351_set_calibration(int32_t cal){
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor
si5351bx_setfreq(0, usbCarrier);
}
void initOscillators(){
//initialize the SI5351
si5351bx_init();
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor
si5351bx_setfreq(0, usbCarrier);
}