| 
									
										
										
										
											2017-12-07 10:18:43 +05:30
										 |  |  | // *************  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
 | 
					
						
							| 
									
										
										
										
											2018-01-20 22:05:04 +09:00
										 |  |  |   uint32_t msxp1; | 
					
						
							| 
									
										
										
										
											2017-12-07 10:18:43 +05:30
										 |  |  |   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
 | 
					
						
							| 
									
										
										
										
											2018-01-30 13:20:52 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (cwMode == 0) | 
					
						
							| 
									
										
										
										
											2018-01-30 17:44:15 +09:00
										 |  |  |     si5351bx_setfreq(0, usbCarrier + (isIFShift ? ifShiftValue : 0)); | 
					
						
							| 
									
										
										
										
											2018-01-30 13:20:52 +09:00
										 |  |  |  else | 
					
						
							| 
									
										
										
										
											2018-01-30 17:44:15 +09:00
										 |  |  |     si5351bx_setfreq(0, cwmCarrier + (isIFShift ? ifShiftValue : 0)); | 
					
						
							| 
									
										
										
										
											2017-12-07 10:18:43 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |