/* * Si5351_Cal_With_Alternate_I2C.ino * * Simple calibration routine for the Si5351 breakout board. * * Copyright 2015 - 2018 Paul Warren * Jason Milldrum * * Modified 2019 - John Price (WA2FZW): * * Allows the use of non-standard I2C pins * * * Uses code from https://github.com/darksidelemm/open_radio_miniconf_2015 * and the old version of the calibration sketch */ #define SI_SDA 26 // Pin assignments for the TTGO #define SI_SCL 27 // ESP32 VFO project #define SI_I2C_ADDR 0x60 // Si5251 I2C address #include // Etherkit si5351 library #include Si5351 si5351 ( SI_I2C_ADDR ); // Create the Si5351 object int32_t cal_factor = 31500UL; // Initial calibration factor int32_t old_cal = 0; // Old calibration factor uint64_t rx_freq; uint64_t target_freq = 1000000000ULL; // 10 MHz, in hundredths of Hertz uint64_t pll_freq = 80000000000ULL; // PLL frequency in hundredths of Hertz (800MHz) uint64_t tempFreq; char freqString[50]; void setup() { Serial.begin ( 115200 ); // Start serial monitor Wire.begin ( SI_SDA, SI_SCL ); // Using ESP32 Wire library! delay ( 1000 ); Serial.println ( "\nSi5351 Calibration Program" ); // On the air! Serial.println ( "Waiting for Si5351 to come online!" ); while ( true ) { if ( si5351.init ( SI5351_CRYSTAL_LOAD_8PF, 0, 0 )) // Set proper crystal load { Serial.println ( "\nSi5351 is online!\n" ); break; } } FmtFreq100 ( Wire.getClock () * 100, freqString ); Serial.print ( "I2C Bus Speed: " ); Serial.println ( freqString ); /* * Start on the target frequency in CLK0: */ // si5351.set_pll ( SI5351_PLL_FIXED, SI5351_PLLA ); // PLL-A Set to 800MHz // delay ( 50 ); // si5351.set_freq ( target_freq, SI5351_CLK0 ); // Set target freq 10 10MHz si5351.set_freq_manual ( target_freq, pll_freq, SI5351_CLK0 ); delay ( 50 ); // si5351.set_correction ( cal_factor, SI5351_PLL_INPUT_XO ); // delay ( 50 ); // si5351.output_enable ( SI5351_CLK0, 1 ); // Turn on the clock // delay ( 50 ); FmtCorrection ( si5351.get_correction ( SI5351_PLL_INPUT_XO ), freqString ); Serial.print ( "Actual correction: " ); Serial.println ( freqString ); FmtFreq100 ( target_freq, freqString ); Serial.print ( "Target Frequency: " ); Serial.println ( freqString ); FmtFreq100 ( si5351.clk_freq[SI5351_CLK0], freqString ); Serial.print ( "Actual clock freq: " ); Serial.println ( freqString ); FmtFreq100 ( si5351.plla_freq, freqString ); Serial.print ( "PLL-A Frequency: " ); Serial.println ( freqString ); FmtFreq100 ( si5351.pllb_freq, freqString ); Serial.print ( "PLL-B Frequency: " ); Serial.println ( freqString ); } void loop() { si5351.update_status (); // Update status? /* * Read the Status Register and print it */ Serial.print ( "\nSYS_INIT: " ); Serial.print ( si5351.dev_status.SYS_INIT) ; Serial.print ( " LOL_A: " ); Serial.print ( si5351.dev_status.LOL_A ); Serial.print ( " LOL_B: " ); Serial.print ( si5351.dev_status.LOL_B ); Serial.print ( " LOS: " ); Serial.print ( si5351.dev_status.LOS ); Serial.print (" REVID: " ); Serial.println ( si5351.dev_status.REVID ); delay ( 1000 ); // while ( 1 ) {} if ( si5351.dev_status.SYS_INIT == 1 ) // Check status { Serial.println ( "Initialising Si5351, you shouldn't see many of these!" ); delay(500); } else { Serial.println ( "\nAdjust until your frequency counter reads as close to 10 MHz as possible." ); Serial.println ( "Press 'q' when complete." ); vfo_interface(); } } static void flush_input ( void ) // Clears any trash from the input { while ( Serial.available() > 0 ) // Something to read? Serial.read(); // Then read it! } static void vfo_interface ( void ) { rx_freq = target_freq; cal_factor = old_cal; Serial.println ( " Up: r t y u i o p" ); Serial.println ( " Down: f g h j k l ;" ); Serial.println ( " Hz: 0.01 0.1 1 10 100 1K 10k" ); while ( true ) { if ( Serial.available() > 0 ) // New command? { char c = Serial.read (); // Read a character switch ( c ) // Figure out what to do about it { case 'q': // Quit flush_input (); // Clear out the input buffer Serial.print( "\nCalibration factor is: " ); Serial.println ( cal_factor ); Serial.println( "Setting calibration factor" ); si5351.set_correction ( cal_factor, SI5351_PLL_INPUT_XO ); si5351.set_pll (SI5351_PLL_FIXED, SI5351_PLLA ); Serial.println ( "Resetting target frequency" ); si5351.set_freq ( target_freq, SI5351_CLK0 ); // si5351.output_enable ( SI5351_CLK0, 1 ); cmd_si5351 ( 177, 0x20 ); // Reset PLLA cmd_si5351 ( 16, 0x4F ); // Enable CLK0? old_cal = cal_factor; return; case 'r': rx_freq += 1; break; case 'f': rx_freq -= 1; break; case 't': rx_freq += 10; break; case 'g': rx_freq -= 10; break; case 'y': rx_freq += 100; break; case 'h': rx_freq -= 100; break; case 'u': rx_freq += 1000; break; case 'j': rx_freq -= 1000; break; case 'i': rx_freq += 10000; break; case 'k': rx_freq -= 10000; break; case 'o': rx_freq += 100000; break; case 'l': rx_freq -= 100000; break; case 'p': rx_freq += 1000000; break; case ';': rx_freq -= 1000000; break; default: // Do nothing continue; } cal_factor = (int32_t) ( rx_freq - target_freq ) + old_cal; // rx & tgt were reversed! si5351.set_correction ( cal_factor, SI5351_PLL_INPUT_XO ); si5351.set_pll ( SI5351_PLL_FIXED, SI5351_PLLA ); si5351.pll_reset ( SI5351_PLLA ); si5351.set_freq ( target_freq, SI5351_CLK0 ); // si5351.output_enable ( SI5351_CLK0, 1 ); FmtCorrection ( si5351.get_correction ( SI5351_PLL_INPUT_XO ), freqString ); Serial.print ( "Actual correction: " ); Serial.println ( freqString ); FmtFreq100 ( si5351.clk_freq[SI5351_CLK0], freqString ); Serial.print ( "Actual clock freq: " ); Serial.println ( freqString ); } } } /* * Format the frequency expressed in hundredths of Hz */ void FmtFreq100 ( int64_t freq, char* buff ) { int decimal; int KHz; int MHz1; int MHz2; decimal = freq % 100; freq = freq / 100; KHz = freq % 1000; freq = freq / 1000; MHz2 = freq % 1000; freq = freq / 1000; MHz1 = freq; if ( MHz1 != 0 ) sprintf ( buff, "%2d,%03d,%03d.%02d Hz", MHz1, MHz2, KHz, decimal ); else if ( MHz2 != 0 ) sprintf ( buff, "%d,%03d.%02d Hz", MHz2, KHz, decimal ); else sprintf ( buff, "%3d.%02d Hz", KHz, decimal ); } void FmtCorrection ( int32_t freq, char* buff ) { int decimal; int Hz; int KHz; int MHz1; int MHz2; char sign = ' '; if ( freq < 0 ) sign = '-'; freq = abs ( freq ); decimal = freq % 100; freq = freq / 100; Hz = freq % 1000; freq = freq / 1000; KHz = freq % 1000; freq = freq / 1000; MHz2 = freq % 1000; freq = freq / 1000; MHz1 = freq; if ( MHz1 != 0 ) sprintf ( buff, "%c,%2d,%03d,%03d,03d,.%02d Hz", sign, MHz1, MHz2, KHz, Hz, decimal ); else if ( MHz2 != 0 ) sprintf ( buff, "%c,%d,%03d,%03d.%02d Hz", sign, MHz2, KHz, Hz, decimal ); else if ( KHz != 0 ) sprintf ( buff, "%c%d,%03d.%02d Hz", sign, KHz, Hz, decimal ); else sprintf ( buff, "%c%d.%02d Hz", sign, Hz, decimal ); } /* * wr_I2C sends a byte of data to the Si5351. The bits are sent in order from * the high order bit (0x80) to the low order bit (0x01). In other words, it * is basically a parallel to serial converter. */ void wr_I2C ( uint8_t d ) { int k; // Loop counter for ( k = 0; k < 8; k++ ) // One bit at a time { if ( d & 0x80 ) // Is the current bit a '1'? digitalWrite ( SI_SDA, HIGH ); // Yes, then send a '1' else digitalWrite ( SI_SDA, LOW ); // Otherwise send a '0' delayMicroseconds ( 1 ); // Timeout digitalWrite ( SI_SCL, HIGH ); // Clock the bit into the Si5351 delayMicroseconds ( 1 ); digitalWrite ( SI_SCL, LOW ); // Pulsing the clock pin delayMicroseconds ( 1 ); digitalWrite ( SI_SDA, LOW ); // Data pin back to a LOW d <<= 1; // Next bit please! } digitalWrite ( SI_SCL, HIGH ); // One final pulse on the clock delayMicroseconds ( 1 ); digitalWrite ( SI_SCL, LOW ); } /* * "cmd_si5351" sends a command sequence to one of the si5551 registers; * typically those that control the factors that go into making it operate * on a specific frequency. */ void cmd_si5351 ( uint8_t reg_No, uint8_t d ) { digitalWrite ( SI_SDA, LOW ); // Start with the data pin LOW delayMicroseconds ( 1 ); digitalWrite ( SI_SCL, LOW ); // and the clock pin LOW delayMicroseconds ( 1 ); wr_I2C ( SI_I2C_ADDR << 1 ); // Send I2C Address wr_I2C ( reg_No ); // Select the proper register wr_I2C ( d ); // Send the command byte delayMicroseconds ( 1 ); digitalWrite ( SI_SCL, HIGH ); // Stop the transaction delayMicroseconds ( 1 ); digitalWrite ( SI_SDA, HIGH ); delayMicroseconds ( 10 ); // Bigger delay here }