175 lines
7.2 KiB
C++
175 lines
7.2 KiB
C++
/************************************************************************************
|
|
* KD8CEC
|
|
* kd8cec@gmail.com http://www.hamskey.com
|
|
*
|
|
* Merge two SI5351 Librarys
|
|
* KE7ER's fixed vco and variable Clocks Configure values
|
|
* G3ZIL's fixed Clock Configure Value and variable VCO
|
|
* * I have combined the two libraries above. All licenses follow the above library.
|
|
*
|
|
* PLL-A is generated by fixing 850Mhz clock. All output clocks use PLL-A to
|
|
* generate the frequency. This is the method used in QRP radios such as uBITX.
|
|
* When switching to WSPR transmission mode, PLL-B operates for the base frequency to transmit WSPR.
|
|
* The output clock channel that controls the frequency is connected to the PLL-B.
|
|
* The WSPR protocol is generated by changing the clock of the PLL-B.
|
|
************************************************************************************/
|
|
|
|
// ************* 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)
|
|
uint8_t SI5351BX_ADDR; // I2C address of Si5351 (variable from Version 1.097)
|
|
#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();
|
|
}
|
|
|
|
uint8_t si5351Val[8] = {0, 1, 0, 0, 0, 0, 0, 0}; //for reduce program memory size
|
|
|
|
void si5351bx_init() { // Call once at power-up, start PLLA
|
|
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};
|
|
si5351Val[2] = BB2(msxp1);
|
|
si5351Val[3] = BB1(msxp1);
|
|
si5351Val[4] = BB0(msxp1);
|
|
|
|
i2cWriten(26, si5351Val, 8); // Write to 8 PLLA msynth regs
|
|
i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB)
|
|
}
|
|
|
|
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 SetCarrierFreq()
|
|
{
|
|
unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0));
|
|
//si5351bx_setfreq(0, (sdrModeOn ? 0 : appliedCarrier));
|
|
si5351bx_setfreq(0, ((sdrModeOn && (inTx == 0)) ? 0 : appliedCarrier)); //found bug by KG4GEK
|
|
|
|
|
|
/*
|
|
if (cwMode == 0)
|
|
si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0));
|
|
else
|
|
si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0));
|
|
*/
|
|
}
|
|
|
|
void initOscillators(){
|
|
//initialize the SI5351
|
|
si5351bx_init();
|
|
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor
|
|
SetCarrierFreq();
|
|
}
|
|
|
|
//============================================================
|
|
// ADD FUNCTIONS by KD8CEC
|
|
//============================================================
|
|
uint8_t Wspr_Reg1[8] = {0xFF,0xFE, 0x00, 0, 0, 0, 0, 0}; //3, 4, 5, 6, 7
|
|
uint8_t Wspr_Reg2[8] = {0, 1, 0, 0, 0, 0, 0, 0}; //2, 3, 4
|
|
|
|
void Set_WSPR_Param(void)
|
|
{
|
|
i2cWrite(18, 128);
|
|
i2cWriten(34, Wspr_Reg1, 8);
|
|
i2cWriten(58, Wspr_Reg2, 8);
|
|
i2cWrite(177, 128);
|
|
i2cWrite(18, 111);
|
|
|
|
si5351bx_clken &= ~(1 << 2);
|
|
i2cWrite(3, si5351bx_clken);
|
|
}
|
|
|
|
void TXSubFreq(unsigned long P2)
|
|
{
|
|
i2cWrite(40, (P2 & 65280) >> 8);
|
|
i2cWrite(41, P2 & 255);
|
|
}
|
|
|
|
|
|
|