Silicon Laboratories Inc. Si5351A-B-GT I2C-PROGRAMMABLE ANY-FREQUENCY CMOS CLOCK GENERATOR

Dependents:   clockGenerator Check_Si5351A_Clock_generator t2d Thing2Do ... more

Test program:
/users/kenjiArai/code/Check_Si5351A_Clock_generator/

Revision:
0:47b9bfa03730
Child:
1:a2309757c450
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/si5351a.cpp	Sun Jan 01 01:21:34 2017 +0000
@@ -0,0 +1,1948 @@
+/*
+ * mbed Library / Silicon Laboratories Inc. Si5351A-B-GT
+ *      I2C-PROGRAMMABLE ANY-FREQUENCY CMOS CLOCK GENERATOR
+ *      https://www.silabs.com/products/
+ *          timing/clock-generator/si535x/pages/Si5351A-B-GM.aspx
+ *
+ *  Checked on Nucleo-F411RE & F401RE mbed board
+ *
+ *  Original & Reference program:
+ *  1)
+ *      https://github.com/adafruit/Adafruit_Si5351_Library
+ *      see original source (bottom part of si5351a.cpp file)
+ *         Software License Agreement (BSD License)
+ *         Copyright (c) 2014, Adafruit Industries  All rights reserved.
+ *  2)
+ *      https://gist.github.com/edy555/f1ee7ef44fe4f5c6f7618ac4cbbe66fb
+ *      made by TT@Hokkaido-san (edy555)
+ *      http://ttrftech.tumblr.com/
+ *      http://ttrftech.tumblr.com/post/150247113216/
+ *          si5351a-configuration-how-to-and-signal-quality
+ *
+ *  Modified by Kenji Arai / JH1PJL
+ *      http://www.page.sannet.ne.jp/kenjia/index.html
+ *      http://mbed.org/users/kenjiArai/
+ *
+ *      Started:  December 24th, 2016
+ *      Revised:  January   1st, 2017
+ *
+ */
+
+#include "mbed.h"
+#include "si5351a.h"
+
+#if 0               // Debug mode = 1
+#define DEBUG
+#endif
+
+#if defined(DEBUG)
+#define DBG(...)     printf(__VA_ARGS__)
+#else
+#define DBG(...)     {;}
+#endif
+
+#define PLL_N 32
+
+SI5351A::SI5351A (PinName p_sda, PinName p_scl,
+                    uint32_t base_clk_freq,
+                    uint8_t xtal_cap,
+                    uint8_t drive_current
+)
+        : _i2c(p_sda, p_scl)
+{
+    base_freq = base_clk_freq;
+    x_cap = xtal_cap;
+    drv_current = drive_current;
+    si5351_init();
+}
+
+SI5351A::SI5351A (I2C& p_i2c,
+                    uint32_t base_clk_freq,
+                    uint8_t xtal_cap,
+                    uint8_t drive_current
+)
+        : _i2c(p_i2c)
+{
+    base_freq = base_clk_freq;
+    x_cap = xtal_cap;
+    drv_current = drive_current;   
+    si5351_init();
+}
+
+/*
+ * 1~100MHz fixed PLL (XTAL * PLL_N)MHz, fractional divider
+ * 100~150MHz fractional PLL 600-900MHz, fixed divider 6
+ * 150~200MHz fractional PLL 600-900MHz, fixed divider 4
+ */
+uint32_t SI5351A::set_frequency(uint8_t channel, uint32_t freq)
+{
+    uint8_t pll;
+    double f;
+    uint8_t state;
+    if (channel == SI5351_CLK0){
+        pll = SI5351_PLL_A;
+    } else {    // SI5351_CLK1 & SI5351_CLK2
+        pll = SI5351_PLL_B;
+    }
+    si5351_disable_output();
+    if (freq <= FREQ_100MHZ){
+        if((channel == SI5351_CLK1) && (clk2_state == CLK_OUT_FIXEDDIV)){
+                DBG("DBG: Error CLK2 uses as over 100MHz!!\r\n");
+                return 0;
+        } else if((channel == SI5351_CLK2) && (clk1_state == CLK_OUT_FIXEDDIV)){
+                DBG("DBG: Error CLK1 uses as over 100MHz!!\r\n");
+                return 0;
+        }
+        DBG("DBG: Passed condition\r\n");
+        if (freq > FREQ_450KHZ){ // over 450KHz
+            si5351_setupPLL(pll, PLL_N, 0, 1);
+            f = si5351_set_frequency_fixedpll(channel, pll, pll_freq, freq, 0);
+        } else if (freq > FREQ_75KHZ){
+            si5351_setupPLL(pll, PLL_N, 0, 1);
+            f = si5351_set_frequency_fixedpll(
+                    channel, pll, pll_freq, freq * 8, SI5351_R_DIV_8);
+            f /= 8.0f;
+        } else if (freq > FREQ_20KHZ){
+            si5351_setupPLL(pll, PLL_N, 0, 1);
+            f = si5351_set_frequency_fixedpll(
+                    channel, pll, pll_freq, freq * 32, SI5351_R_DIV_32);
+            f /= 32.0f;
+        } else {
+            si5351_setupPLL(pll, PLL_N, 0, 1);
+            f = si5351_set_frequency_fixedpll(
+                    channel, pll, pll_freq, freq * 128, SI5351_R_DIV_128);
+            f /= 128.0f;
+        }
+        state = CLK_OUT_FIXEDPLL;
+    } else {
+        DBG("DBG: Set over 100MHz clock\r\n");
+        if((channel == SI5351_CLK1) && (clk2_state == CLK_OUT_FIXEDPLL)){
+                DBG("DBG: Error CLK2 uses as under 100MHz!!\r\n");
+                return 0;
+        } else if((channel == SI5351_CLK2) && (clk1_state == CLK_OUT_FIXEDPLL)){
+                DBG("DBG: Error CLK1 uses as under 100MHz!!\r\n");
+                return 0;
+        }
+        DBG("DBG: Passed condition\r\n");
+        if (freq < FREQ_150MHZ) {
+            DBG("DBG: Set 100MHz to 150MHz clock\r\n");
+            f = si5351_set_frequency_fixeddiv(channel, pll, freq, 6);
+        } else {
+            DBG("DBG: Set over 150MHz clock\r\n");
+            f = si5351_set_frequency_fixeddiv(channel, pll, freq, 4);
+        }
+        state = CLK_OUT_FIXEDDIV;
+    }
+    si5351_enable_output();
+    if (channel == SI5351_CLK0){
+        clk0_state = state;
+        clk0_freq = f;
+    } else if (channel == SI5351_CLK1){
+        clk1_state = state;
+        clk1_freq = f;
+    } else {
+        clk2_state = state;
+        clk2_freq = f;
+    }
+    DBG("DBG: freq./ Target=%u,Set=%0.1f,diff=%.0f\r\n",
+            freq, f, (double)freq - f);
+    return (uint32_t)f;
+}
+
+uint32_t SI5351A::shift_freq(uint8_t channel, int32_t diff)
+{
+    double f;
+    uint32_t f_err;
+    // Check current status
+    if (channel == SI5351_CLK0){
+        f = clk0_freq;
+        f_err = (uint32_t)f;
+        if ((clk0_state == CLK_OUT_NOT_USED)
+            || (clk0_state == CLK_OUT_FIXEDDIV)){
+                DBG("DBG: error over 100MHz\r\n");
+                return f_err; // return current frequency
+        }
+    } else if (channel == SI5351_CLK1){
+        f = clk1_freq;
+        f_err = (uint32_t)f;
+        if ((clk1_state == CLK_OUT_NOT_USED)
+            || (clk1_state == CLK_OUT_FIXEDDIV)){
+                DBG("DBG: error over 100MHz\r\n");
+                return f_err; // return current frequency
+        }
+    } else if (channel == SI5351_CLK2){
+        f = clk2_freq;
+        f_err = (uint32_t)f;
+        if ((clk2_state == CLK_OUT_NOT_USED)
+            || (clk2_state == CLK_OUT_FIXEDDIV)){
+                DBG("DBG: error over 100MHz\r\n");
+                return f_err; // return current frequency
+        }
+    } else {
+        return 0;
+    }
+    // set new frequency
+    double f_tatget = f + (double)diff;
+    uint32_t freq = (uint32_t)f_tatget;
+    DBG("DBG: Target F=%u\r\n", freq);
+    // only range between 1MHz to 100MHz
+    if ((freq > FREQ_100MHZ) || (freq < FREQ_450KHZ)){// between 450KHz-100MHz
+        DBG("DBG: Out of range\r\n");
+        return f_err; // return current frequency
+    }
+    uint32_t div = (floor)((double)pll_freq / (double)freq);
+    uint32_t num = pll_freq - freq * div;
+    uint32_t denom = freq;
+    uint32_t k = gcd(num, denom);
+    num /= k;
+    denom /= k;
+    while (denom >= (1<<20)) {
+      num >>= 1;
+      denom >>= 1;
+    }
+    if (denom == 0){            // Avoid divide by zero
+        DBG("DBG: error demon=0\r\n");
+        return f_err; // return current frequency
+    }
+    if (num >=0x100000){        // 20-bit limit
+        DBG("DBG: error num over 20bit\r\n");
+        return f_err; // return current frequency
+    }    
+    if (denom >=0x100000){      // 20-bit limit
+        DBG("DBG: error denom over 20bit\r\n");
+        return f_err; // return current frequency
+    }
+    uint32_t P1, P2, P3;
+    uint32_t multisynth_vale;
+    double   multisynth_double;
+    const uint8_t msreg_base[] = {
+        SI5351_REG_42_MULTISYNTH0,
+        SI5351_REG_50_MULTISYNTH1,
+        SI5351_REG_58_MULTISYNTH2,
+    };
+    uint8_t baseaddr = msreg_base[channel];
+    // Fractional mode
+    multisynth_vale = (floor)((double)num /(double)denom);
+    P1 = 128 * div + 128 * multisynth_vale - 512;
+    P2 = 128 * num - denom * 128 * multisynth_vale;
+    P3 = denom;
+    multisynth_double = div + (double)num /(double)denom;
+    // a + b/c between 6..1800
+    if ((multisynth_double < 6.0f) || (multisynth_double > 1800.0f)){
+        DBG("DBG: error multisynth less 6 or over 1800\r\n");
+        return f_err; // return current frequency
+    }
+    DBG("DBG: CLK%u: PLL=%u,div=%u,num=%u,denom=%u\r\n",
+            channel, pll_freq, div, num, denom);
+    // Set the MSx config registers
+    si5351_write(baseaddr,   (P3 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+1, (P3 & 0x000000ff));
+    si5351_write(baseaddr+2, (P1 & 0x00030000) >> 16);
+    si5351_write(baseaddr+3, (P1 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+4, (P1 & 0x000000ff));
+    si5351_write(baseaddr+5,
+                ((P3 & 0x000f0000) >> 12) | ((P2 & 0x000f0000) >> 16));
+    si5351_write(baseaddr+6, (P2 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+7, (P2 & 0x000000ff));
+    f = (double)pll_freq / multisynth_double; 
+    if (channel == SI5351_CLK0){
+        clk0_freq = f;
+    } else if (channel == SI5351_CLK1){
+        clk1_freq = f;
+    } else {    // SI5351_CLK2
+        clk2_freq = f;
+    }
+    return (uint32_t)f;
+}
+
+uint32_t SI5351A::read_freq(uint8_t channel)
+{
+    if (channel == SI5351_CLK0){
+        return (uint32_t)clk0_freq;
+    } else if (channel == SI5351_CLK1){
+        return (uint32_t)clk1_freq;
+    } else {    // SI5351_CLK2
+        return (uint32_t)clk2_freq;
+    }
+}
+
+static const uint8_t reg_table[] = {
+ 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170
+};
+
+void SI5351A::all_reset(void)
+{
+    plla_freq  = 0;
+    pllb_freq  = 0;
+    clk0_freq  = 0;
+    clk1_freq  = 0;
+    clk2_freq  = 0;
+    clk0_state = 0;
+    clk1_state = 0;
+    clk2_state = 0;
+    /* Disable all outputs setting CLKx_DIS high */
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
+    for (uint16_t i = 0; i < sizeof(reg_table); i++){
+        si5351_write(reg_table[i], 0);
+    }
+    /* Apply soft reset */
+    si5351_write(SI5351_REG_177_PLL_RESET, 0xac);     
+}
+
+////////////// Configration for Initialization /////////////////////////////////
+// length, register addr, data, ...
+const uint8_t si5351_configs[] = {
+    // 0xff = all outputs are disabled.
+    2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff,
+    // CLK0,CLK1(REG_17),CLK2(REG_18) -> Power down mode
+    4, SI5351_REG_16_CLK0_CONTROL,
+     SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN,
+    // Dummy data for PLL
+    9, SI5351_REG_26_PLL_A, /*P3*/0, 0, /*P1*/0, 0, 0, /*P3/P2*/0, 0, 0,
+    // RESET PLL (Both PLLA & PLLB)
+    2, SI5351_REG_177_PLL_RESET, (SI5351_PLL_RESET_A | SI5351_PLL_RESET_B),
+    // Dummy data for MULTISYNTH
+    9, SI5351_REG_58_MULTISYNTH2, /*P3*/0, 0, /*P1*/0, 0, 0, /*P2|P3*/0, 0, 0,
+    // 0 = enable / CLK0,1,2 (bit 0,1,2 = 0) 
+    2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xf8,
+    0 // sentinel
+};
+
+void SI5351A::si5351_init(void)
+{
+    addr = SI5351_I2C_ADDR;
+    pll_freq = base_freq * PLL_N;
+    clk0_freq = 0;
+    clk1_freq = 0;
+    clk2_freq = 0;
+    clk0_state = CLK_OUT_NOT_USED;
+    clk1_state = CLK_OUT_NOT_USED;
+    clk2_state = CLK_OUT_NOT_USED;
+    si5351_reset_pll();
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
+    // Total load capacitance less than or equal to 12 pF (See AN551)
+    si5351_write(SI5351_REG_183_CRYSTAL_LOAD, x_cap);
+    const uint8_t *p = si5351_configs;
+    while (*p) {
+        uint8_t len = *p++;
+        si5351_bulk_write(p, len);
+        p += len;
+    }
+    // CONTROL for CLK0,1,2
+    uint8_t dt = (drv_current | \
+                  SI5351_CLK_INPUT_MULTISYNTH_N | \
+                  SI5351_CLK_INTEGER_MODE);
+    si5351_write(SI5351_REG_16_CLK0_CONTROL, dt);
+    si5351_write(SI5351_REG_17_CLK1_CONTROL, dt);
+    si5351_write(SI5351_REG_18_CLK2_CONTROL, dt);
+    si5351_disable_all_output();    // Disable all output
+}
+
+void SI5351A::si5351_disable_output(void)
+{
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
+}
+
+void SI5351A::si5351_disable_all_output(void)
+{
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
+    si5351_write(SI5351_REG_16_CLK0_CONTROL, 0x80);
+    si5351_write(SI5351_REG_17_CLK1_CONTROL, 0x80);
+    si5351_write(SI5351_REG_18_CLK2_CONTROL, 0x80);
+}
+
+void SI5351A::si5351_enable_output(void)
+{
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xf8);
+}
+
+void SI5351A::si5351_reset_pll(void)
+{
+    si5351_write(SI5351_REG_177_PLL_RESET, 0xa0);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Sets the multiplier for the specified PLL
+
+    @param  pll   The PLL to configure, which must be one of the following:
+                  - SI5351_PLL_A
+                  - SI5351_PLL_B
+    @param  mult  The PLL integer multiplier (must be between 15 and 90)
+    @param  num   The 20-bit numerator for fractional output (0..1,048,575).
+                  Set this to '0' for integer output.
+    @param  denom The 20-bit denominator for fractional output (1..1,048,575).
+                  Set this to '1' or higher to avoid divider by zero errors.
+    @section PLL Configuration
+    fVCO is the PLL output, and must be between 600..900MHz, where:
+        fVCO = fXTAL * (a+(b/c))
+    fXTAL = the crystal input frequency
+    a     = an integer between 15 and 90
+    b     = the fractional numerator (0..1,048,575)
+    c     = the fractional denominator (1..1,048,575)
+    NOTE: Try to use integers whenever possible to avoid clock jitter
+    (only use the a part, setting b to '0' and c to '1').
+    See: http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+*/
+/**************************************************************************/
+void SI5351A::si5351_setupPLL(
+                uint8_t     pll,
+                uint8_t     mult,
+                uint32_t    num,
+                uint32_t    denom)
+{
+    uint32_t P1;       /* PLL config register P1 */
+    uint32_t P2;       /* PLL config register P2 */
+    uint32_t P3;       /* PLL config register P3 */
+
+    /* Basic validation */
+    DBG("DBG: Enter si5351_setupPLL\r\n");
+    DBG("DBG: pll=%u, mult=%u, num=%u, denom=%u\r\n", pll, mult, num, denom);
+    bool err = false;
+    if (mult <= 14){        /* mult = 15..90 */
+        DBG("DBG: mult lower value error\r\n");
+        err = true;
+    }
+    if (mult >= 91){
+        DBG("DBG: mult bigger value error\r\n");
+        err = true;
+    }
+    if (denom == 0){        /* Avoid divide by zero */
+        DBG("DBG: denom = 0 error\r\n");
+        err = true;
+    }
+    if (num >=0x100000){    /* 20-bit limit */
+        DBG("DBG: num value error\r\n");
+        err = true;
+    }
+    if (denom >=0x100000){  /* 20-bit limit */
+        DBG("DBG: denom value error\r\n");
+        err = true;
+    }
+    if (err == true){
+        if (pll == SI5351_PLL_A){
+            plla_freq = 0;
+        } else { // SI5351_PLL_B
+            pllb_freq = 0;
+        }
+        DBG("DBG: return by error\r\n");
+        return;
+    }
+    /* Feedback Multisynth Divider Equation
+     * where: a = mult, b = num and c = denom
+     * P1 register is an 18-bit value using following formula:
+     *    P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512
+     * P2 register is a 20-bit value using the following formula:
+     *    P2[19:0] = 128 * num - denom * floor(128*(num/denom))
+     * P3 register is a 20-bit value using the following formula:
+     *    P3[19:0] = denom
+     */
+    /* Set the main PLL config registers */
+    if (num == 0) {
+        DBG("DBG: num = 0\r\n");
+        /* Integer mode */
+        P1 = 128 * mult - 512;
+        P2 = num;
+        P3 = denom;
+    } else {
+        /* Fractional mode */
+        DBG("DBG: Fractional mode\r\n");
+        P1 = (uint32_t)
+                (128 * mult + floor(128 * ((double)num/(double)denom)) - 512);
+        P2 = (uint32_t)
+                (128 * num - denom * floor(128 * ((double)num/(double)denom)));
+        P3 = denom;
+    }
+    /* Get the appropriate starting point for the PLL registers */
+    const uint8_t pllreg_base[] = {
+        SI5351_REG_26_PLL_A,
+        SI5351_REG_34_PLL_B
+    };
+    uint8_t baseaddr = pllreg_base[pll];
+    /* The datasheet is a nightmare of typos and inconsistencies here! */
+    si5351_write(baseaddr,   (P3 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+1, (P3 & 0x000000ff));
+    si5351_write(baseaddr+2, (P1 & 0x00030000) >> 16);
+    si5351_write(baseaddr+3, (P1 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+4, (P1 & 0x000000ff));
+    si5351_write(baseaddr+5,
+                ((P3 & 0x000f0000) >> 12) | ((P2 & 0x000f0000) >> 16) );
+    si5351_write(baseaddr+6, (P2 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+7, (P2 & 0x000000ff));
+    /* Reset both PLLs */
+    si5351_write(SI5351_REG_177_PLL_RESET,
+                 SI5351_PLL_RESET_B | SI5351_PLL_RESET_A);
+    /* Store the frequency settings for use with the Multisynth helper */
+    DBG("DBG: Use 26(PLLA) or 34(PLLB)) ->%u and write data\r\n", baseaddr);
+    double f = base_freq * (mult + ((double)num / (double)denom));
+    DBG("DBG: PLL f=%u\r\n", (uint32_t)f);
+    if (pll == SI5351_PLL_A){
+        plla_freq = f;
+    } else { // SI5351_PLL_B
+        pllb_freq = f;
+    }
+}
+
+/**************************************************************************/
+/*!
+    @brief  Configures the Multisynth divider, which determines the
+            output clock frequency based on the specified PLL input.
+
+    @param  output    The output channel to use (0..2)
+    @param  pllSource   The PLL input source to use, which must be one of:
+                      - SI5351_PLL_A
+                      - SI5351_PLL_B
+    @param  div       The integer divider for the Multisynth output.
+                      If pure integer values are used, this value must
+                      be one of:
+                      - SI5351_MULTISYNTH_DIV_4
+                      - SI5351_MULTISYNTH_DIV_6
+                      - SI5351_MULTISYNTH_DIV_8
+                      If fractional output is used, this value must be
+                      between 8 and 900.
+    @param  num       The 20-bit numerator for fractional output
+                      (0..1,048,575). Set this to '0' for integer output.
+    @param  denom     The 20-bit denominator for fractional output
+                      (1..1,048,575). Set this to '1' or higher to
+                      avoid divide by zero errors.
+    @section Output Clock Configuration
+
+    The multisynth dividers are applied to the specified PLL output,
+    and are used to reduce the PLL output to a valid range (500kHz
+    to 160MHz). The relationship can be seen in this formula, where
+    fVCO is the PLL output frequency and MSx is the multisynth
+    divider:
+        fOUT = fVCO / MSx
+    Valid multisynth dividers are 4, 6, or 8 when using integers,
+    or any fractional values between 8 + 1/1,048,575 and 900 + 0/1
+
+    The following formula is used for the fractional mode divider:
+        a + b / c
+    a = The integer value, which must be 4, 6 or 8 in integer mode (MSx_INT=1)
+        or 6..1800 in fractional mode (MSx_INT=0).
+    b = The fractional numerator (0..1,048,575)
+    c = The fractional denominator (1..1,048,575)
+
+    @note   Try to use integers whenever possible to avoid clock jitter
+    @note   For output frequencies > 150MHz, you must set the divider
+            to 4 and adjust to PLL to generate the frequency (for example
+            a PLL of 640 to generate a 160MHz output clock). This is not
+            yet supported in the driver, which limits frequencies to
+            500kHz .. 150MHz.
+    @note   For frequencies below 500kHz (down to 8kHz) Rx_DIV must be
+            used, but this isn't currently implemented in the driver.
+*/
+/**************************************************************************/
+double SI5351A::si5351_setupMultisynth( 
+                uint8_t     output,
+                uint8_t     pllSource,
+                uint32_t    div,
+                uint32_t    num,
+                uint32_t    denom,
+                uint8_t     factor)
+{
+    uint32_t P1;       /* Multisynth config register P1 */
+    uint32_t P2;       /* Multisynth config register P2 */
+    uint32_t P3;       /* Multisynth config register P3 */
+    uint32_t div4 = 0;
+    uint32_t multisynth_vale;
+    double   multisynth_double;
+
+    DBG("DBG: Enter si5351_setupMultisynth\r\n");
+    DBG("DBG: ch=%u, pll=%u, div=%u, num=%u, denom=%u, factor=%u\r\n",
+                output, pllSource, div, num, denom, factor);
+    if (output > 3){
+        return 0;
+    }         /* Channel range */
+    if ((div <= 3) || (div >= 1801)){   /* Divider integer 6..1800 */
+        DBG("DBG: div out of range error\r\n");
+        return 0;
+    }
+    if (denom == 0){                    /* Avoid divide by zero */
+        DBG("DBG: denom=0 error\r\n");
+        return 0;
+    }
+    if (num >=0x100000){                /* 20-bit limit */
+        DBG("DBG: num bigger error\r\n");
+        return 0;
+    }
+    if (denom >=0x100000){              /* 20-bit limit */
+        DBG("DBG: denom bigger error\r\n");
+        return 0;
+    }
+    DBG("DBG: Passed range check\r\n");
+    /* Get the appropriate starting point for the PLL registers */
+    const uint8_t msreg_base[] = {
+        SI5351_REG_42_MULTISYNTH0,
+        SI5351_REG_50_MULTISYNTH1,
+        SI5351_REG_58_MULTISYNTH2,
+    };
+    uint8_t baseaddr = msreg_base[output];
+    /* Output Multisynth Divider Equations
+     * where: a = div, b = num and c = denom
+     * P1 register is an 18-bit value using following formula:
+     *    P1[17:0] = 128 * a + floor(128*(b/c)) - 512
+     * P2 register is a 20-bit value using the following formula:
+     *    P2[19:0] = 128 * b - c * floor(128*(b/c))
+     * P3 register is a 20-bit value using the following formula:
+     *    P3[19:0] = c
+    */
+    /* Set the main PLL config registers */
+    if (div == 4) {
+        DBG("DBG: enter div==4\r\n");
+        div4 = SI5351_DIVBY4;
+        P1 = P2 = 0;
+        P3 = 1;
+        multisynth_double = 4.0f;      
+    } else if (num == 0) {
+        DBG("DBG: enter num==0\r\n");
+        /* Integer mode */
+        P1 = 128 * div - 512;
+        P2 = 0;
+        P3 = 1;
+        multisynth_double = (double)div;
+    } else {
+        /* Fractional mode */
+        DBG("DBG: enter Fractional mode\r\n");
+        multisynth_vale = (floor)((double)num /(double)denom);
+        P1 = 128 * div + 128 * multisynth_vale - 512;
+        P2 = 128 * num - denom * 128 * multisynth_vale;
+        P3 = denom;
+        multisynth_double = div + (double)num /(double)denom;
+        /* a + b/c between 6..1800 */
+        if ((multisynth_double < 6.0f) || (multisynth_double > 1800.0f)){
+            return 0;
+        }
+    }
+    /* Set the MSx config registers */
+    si5351_write(baseaddr,   (P3 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+1, (P3 & 0x000000ff));
+    si5351_write(baseaddr+2, ((P1 & 0x00030000) >> 16) | div4 | factor);
+    si5351_write(baseaddr+3, (P1 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+4, (P1 & 0x000000ff));
+    si5351_write(baseaddr+5,
+                ((P3 & 0x000f0000) >> 12) | ((P2 & 0x000f0000) >> 16));
+    si5351_write(baseaddr+6, (P2 & 0x0000ff00) >> 8);
+    si5351_write(baseaddr+7, (P2 & 0x000000ff));
+    /* Configure the clk control and enable the output */
+    const uint8_t clkctrl[] = {
+        SI5351_REG_16_CLK0_CONTROL,
+        SI5351_REG_17_CLK1_CONTROL,
+        SI5351_REG_18_CLK2_CONTROL
+    };
+    uint8_t dat;
+    dat = drv_current | SI5351_CLK_INPUT_MULTISYNTH_N;
+    if (pllSource == SI5351_PLL_B){
+        dat |= SI5351_CLK_PLL_SELECT_B;
+    }
+    if (num == 0){
+        dat |= SI5351_CLK_INTEGER_MODE;
+    }
+    DBG("DBG: CLK%u: reg_%u=0x%02x\r\n", output, clkctrl[output], dat);  
+    si5351_write(clkctrl[output], dat);
+    DBG("DBG: a+b/c=%8.2f\r\n", multisynth_double); 
+    return multisynth_double;
+}
+
+uint32_t SI5351A::gcd(uint32_t x, uint32_t y)
+{
+    int32_t z;
+    while (y != 0) {
+        z = x % y;
+        x = y;
+        y = z;
+    }
+    return x;
+}
+
+double SI5351A::si5351_set_frequency_fixedpll(
+                uint8_t     channel,
+                uint32_t    pll,
+                uint32_t    pllfreq,
+                uint32_t    freq,
+                uint8_t     factor)
+{
+    DBG("DBG: Enter si5351_set_frequency_fixedpll\r\n");
+    uint32_t div = (floor)((double)pllfreq / (double)freq); // range: 8 ~ 1800
+    uint32_t num = pllfreq - freq * div;
+    uint32_t denom = freq;
+    //int32_t k = freq / (1<<20) + 1;
+    uint32_t k = gcd(num, denom);
+    num /= k;
+    denom /= k;
+    while (denom >= (1<<20)) {
+      num >>= 1;
+      denom >>= 1;
+    }
+    DBG("DBG: CLK%u: PLL=%u,div=%u,num=%u,denom=%u\r\n",
+            channel, pll, div, num, denom);
+    double x = si5351_setupMultisynth(channel, pll, div, num, denom, factor);
+    if (x == 0.0f){ return 0;}
+    x = (double)pll_freq / x;
+    DBG("DBG: Freqency=%.0f[Hz]\r\n", x);
+    return x;
+}
+
+double SI5351A::si5351_set_frequency_fixeddiv(
+                uint8_t     channel,
+                uint32_t    pll,
+                uint32_t    freq,
+                uint32_t    div)
+{
+    DBG("DBG: si5351_set_frequency_fixeddiv\r\n");
+    uint32_t pllfreq = freq * div;
+    uint32_t multi = pllfreq / base_freq;
+    uint32_t num = pllfreq - multi * base_freq;
+    uint32_t denom = base_freq;
+    uint32_t k = gcd(num, denom);
+    num /= k;
+    denom /= k;
+    while (denom >= (1<<20)) {
+      num >>= 1;
+      denom >>= 1;
+    }
+    si5351_setupPLL(pll, multi, num, denom);
+    num = 0;
+    double x = si5351_setupMultisynth(channel, pll, div, num, denom, 0);
+    if (x == 0.0f){ return 0.0f;}
+    if (pll == SI5351_PLL_A){
+        x = plla_freq / x;
+    } else { // SI5351_PLL_B
+        x = pllb_freq / x;
+    }
+    DBG("DBG: Freqency=%.0f[Hz]\r\n", x);
+    return x;
+}
+
+void SI5351A::si5351_bulk_write(const uint8_t *buf, uint8_t len)
+{
+    _i2c.write(addr, (const char*)buf, len, false);
+}
+
+void SI5351A::si5351_write(uint8_t reg, uint8_t dat)
+{
+    uint8_t buf[] = { reg, dat };
+    _i2c.write(addr,(const char *)buf, 2, false);
+}
+
+void SI5351A::si5351_read(const uint8_t *buf)
+{
+    int n = (int)buf[1];
+    _i2c.write(addr,(const char *)buf, 1, true);
+    _i2c.read(addr, (char *)buf, n, false);
+}
+
+//----------- DEBUG PURPOSE ----------------------------------------------------
+//  Print memory contents
+void SI5351A::put_dump (const uint8_t *buff, uint8_t ofs, uint8_t cnt)
+{
+    printf("%03u ", ofs);
+    for(int n = 0; n < cnt; n++) {      // show hex
+        printf(" %02x", buff[n]);
+    }
+    printf("\r\n");
+}
+
+void SI5351A::prnt_reg(uint8_t offset, uint8_t n)
+{  
+    uint8_t buf[16];
+    buf[0] = offset;
+    buf[1] = n;
+    si5351_read(buf);
+    put_dump(buf, offset, n);
+}
+
+void SI5351A::debug_reg_print(void)
+{
+    printf("Show Si5351A registers\r\n");
+    printf("reg   0  1  2  3  4  5  6  7  8  9\r\n");
+    prnt_reg(0, 4);     // reg0 to reg3
+    prnt_reg(9, 1);     // reg9
+    prnt_reg(15, 4);    // reg15 to reg18
+    prnt_reg(24, 10);   // reg24 to reg33
+    prnt_reg(34, 10);   // reg34 to reg43
+    prnt_reg(44, 10);   // reg44 to reg53
+    prnt_reg(54, 10);   // reg54 to reg63
+    prnt_reg(64, 2);    // reg64 to reg65
+    prnt_reg(149, 10);  // reg149 to reg158
+    prnt_reg(159, 3);   // reg159 to reg161
+    prnt_reg(165, 3);   // reg165 to reg167
+    prnt_reg(177, 1);   // reg177
+    prnt_reg(183, 1);   // reg183
+    prnt_reg(187, 1);   // reg187
+}
+
+void SI5351A::debug_current_config(void)
+{
+    uint8_t buf[16];
+    uint8_t dt0;
+    uint8_t dt1;
+    //Step1
+    printf("[BASE CLOCK] --> ");
+    buf[0] = 183;   // register address
+    buf[1] = 1;     // # of reading bytes
+    si5351_read(buf);
+    printf("Xtal=%u[Hz] with internal cap=", base_freq);
+    dt0 = buf[0] & 0xc0;
+    switch (dt0){
+        case 0xc0:
+            printf("10");
+            break;
+        case 0x80:
+            printf("8");
+            break;
+        case 0x40:
+            printf("6");
+            break;
+        default:
+            printf("?(bad config)");
+            break;
+    }
+    printf("[pF]\r\n");
+    //Step2
+    printf("[PLLn]       --> ");
+    buf[0] = 15;
+    buf[1] = 1;
+    si5351_read(buf);
+    dt0 = buf[0];
+    dt1 = dt0 >> 6;
+    printf("Clock in divide by %u", 1U << dt1);
+    printf(" PLLA clock source is ");
+    if (dt0 & 0x04){
+        printf("none XTAL(bad config)");
+    } else {
+        printf("XTAL");
+    }
+    printf(" & PLLB = ");
+    if (dt0 & 0x08){
+        printf("none XTAL(bad config)");
+    } else {
+        printf("XTAL");
+    }
+    printf("\r\n");
+    //Step3
+    printf("[CLK0,1,2]   --> ");
+    printf("CLKn output E:enable/D:disable  * ");
+    buf[0] = 9; // register address
+    buf[1] = 1; // # of reading bytes
+    si5351_read(buf);
+    dt0 = buf[0] & 0x07;
+    buf[0] = 3;     // register address
+    buf[1] = 1;     // # of reading bytes
+    si5351_read(buf);
+    dt1 = buf[0] & 0x07;
+    printf("CLK2:");
+    if ((dt0 & 0x04) && (dt1 & 0x04)){
+        printf("D");
+    } else {
+        printf("E");
+    }
+    printf(", CLK1:");
+    if ((dt0 & 0x02) && (dt1 & 0x02)){
+        printf("D");
+    } else {
+        printf("E");
+    }
+    printf(", CLK0:");
+    if ((dt0 & 0x01) && (dt1 & 0x01)){
+        printf("D");
+    } else {
+        printf("E");
+    }
+    printf("\r\n");
+    //Step4
+    buf[0] = 16;
+    buf[1] = 3;
+    si5351_read(buf);
+    printf("-->    CLK0 * ");
+    reg_16_17_18(buf[0]);
+    printf("-->    CLK1 * ");
+    reg_16_17_18(buf[1]);
+    printf("-->    CLK2 * ");
+    reg_16_17_18(buf[2]);
+    //Step5
+    printf("[PLLn P1,P2,P3]\r\n");
+    printf("-->    PLLA * ");
+    buf[0] = 26; // register address
+    buf[1] = 8; // # of reading bytes
+    si5351_read(buf);
+    reg_pll_8bytes(buf);
+    printf("-->    PLLB * ");
+    buf[0] = 34; // register address
+    buf[1] = 8; // # of reading bytes
+    si5351_read(buf);
+    reg_pll_8bytes(buf);
+    printf("[MultiSynth-n P1,P2,P3]\r\n");    
+    printf("--> Mltsyn0 * ");
+    buf[0] = 42; // register address
+    buf[1] = 8; // # of reading bytes
+    si5351_read(buf);
+    reg_mltisyc_8bytes(buf);
+    printf("--> Mltsyn1 * ");
+    buf[0] = 50; // register address
+    buf[1] = 8; // # of reading bytes
+    si5351_read(buf);
+    reg_mltisyc_8bytes(buf);
+    printf("--> Mltsyn2 * ");
+    buf[0] = 58; // register address
+    buf[1] = 8; // # of reading bytes
+    si5351_read(buf);
+    reg_mltisyc_8bytes(buf);
+}
+
+void SI5351A::reg_pll_8bytes(uint8_t *buf)
+{
+    uint32_t dt = ((uint32_t)(buf[5] & 0xf0) << 12) + ((uint32_t)buf[0] << 8)
+                     + (uint32_t)buf[1];
+    printf("P3=%6u (0x%05x), ", dt, dt);
+    dt = ((uint32_t)(buf[5] & 0x0f) << 16) + ((uint32_t)buf[6] << 8)
+                     + (uint32_t)buf[7];
+    printf("P2=%6u (0x%05x), ", dt, dt);   
+    dt = ((uint32_t)(buf[2] & 0x03) << 16) + ((uint32_t)buf[3] << 8)
+                     + (uint32_t)buf[4];
+    printf("P1=%6u (0x%05x)\r\n", dt, dt); 
+}    
+
+void SI5351A::reg_mltisyc_8bytes(uint8_t *buf)
+{
+    uint32_t dt = ((uint32_t)(buf[5] & 0xf0) << 12) + ((uint32_t)buf[0] << 8)
+                     + (uint32_t)buf[1];
+    printf("P3=%6u (0x%05x), ", dt, dt);
+    dt = ((uint32_t)(buf[5] & 0x0f) << 16) + ((uint32_t)buf[6] << 8)
+                     + (uint32_t)buf[7];
+    printf("P2=%6u (0x%05x), ", dt, dt);   
+    dt = ((uint32_t)(buf[2] & 0x03) << 16) + ((uint32_t)buf[3] << 8)
+                     + (uint32_t)buf[4];
+    printf("P1=%6u (0x%05x), ", dt, dt);
+    uint8_t d = buf[2];
+    if ((d & 0x0c) == 0x0c){
+        printf("Divided by");
+    } else {
+        printf("Div >");
+    }
+    printf(" 4 mode, ");
+    dt >>= 4;
+    dt &=0x07;
+    printf("R-reg: Divided by %u\r\n", 1U << dt);
+} 
+
+void SI5351A::reg_16_17_18(uint8_t dt)
+{
+    printf("Power ");
+    if (dt & 0x80){
+        printf("down(not used)");
+    } else {
+        printf("up(running)");
+    }
+    printf(", MultiSynth-> ");
+    if (dt & 0x40){
+        printf("Integer");
+    } else {
+        printf("Fractional");
+    }
+    printf(" mode, PLL-> ");
+    if (dt & 0x20){
+        printf("PLLB");
+    } else {
+        printf("PLLA");
+    }
+    printf(", Clock-> ");
+    if (dt & 0x20){
+        printf("inverted");
+    } else {
+        printf("not inverted");
+    }   
+    printf(", Drive strength-> ");
+    printf("%umA\r\n", 2 + 2 * (dt & 0x03));     
+}
+
+/**************************************************************************/
+/*!
+    @brief  Configures the Si5351 with config settings generated in
+            ClockBuilder. You can use this function to make sure that
+            your HW is properly configure and that there are no problems
+            with the board itself.
+
+            Running this function should provide the following output:
+            * Channel 0: 120.00 MHz
+            * Channel 1: 12.00  MHz
+            * Channel 2: 13.56  MHz
+    @note   This will overwrite all of the config registers!
+*/
+/**************************************************************************/
+/* Test setup from SI5351 ClockBuilder
+ * -----------------------------------
+ * XTAL:      25     MHz
+ * Channel 0: 120.00 MHz
+ * Channel 1: 12.00  MHz
+ * Channel 2: 13.56  MHz
+ */
+static const uint8_t m_si5351_regs_15to92_149to170[100][2] =
+{
+  {  15, 0x00 },    /* Input source = crystal for PLLA and PLLB */
+  /* CLK0 Control: 8mA drive, Multisynth 0 as CLK0 source, Clock not inverted,
+   Source = PLLA, Multisynth 0 in integer mode, clock powered up */
+  {  16, 0x4F },
+  /* CLK1 Control: 8mA drive, Multisynth 1 as CLK1 source, Clock not inverted,
+   Source = PLLA, Multisynth 1 in integer mode, clock powered up */  
+  {  17, 0x4F },
+  /* CLK2 Control: 8mA drive, Multisynth 2 as CLK2 source, Clock not inverted,
+   Source = PLLB, Multisynth 2 in integer mode, clock powered up */
+  {  18, 0x6F },
+  {  19, 0x80 },    /* CLK3 Control: Not used ... clock powered down */
+  {  20, 0x80 },    /* CLK4 Control: Not used ... clock powered down */
+  {  21, 0x80 },    /* CLK5 Control: Not used ... clock powered down */
+  {  22, 0x80 },    /* CLK6 Control: Not used ... clock powered down */
+  {  23, 0x80 },    /* CLK7 Control: Not used ... clock powered down */
+  {  24, 0x00 },    /* Clock disable state 0..3 (low when disabled) */
+  {  25, 0x00 },    /* Clock disable state 4..7 (low when disabled) */
+  /* PLL_A Setup */
+  {  26, 0x00 },  {  27, 0x05 },  {  28, 0x00 },  {  29, 0x0C },
+  {  30, 0x66 },  {  31, 0x00 },  {  32, 0x00 },  {  33, 0x02 },
+  /* PLL_B Setup */
+  {  34, 0x02 },  {  35, 0x71 },  {  36, 0x00 },  {  37, 0x0C },
+  {  38, 0x1A },  {  39, 0x00 },  {  40, 0x00 },  {  41, 0x86 },
+  /* Multisynth Setup */
+  {  42, 0x00 },  {  43, 0x01 },  {  44, 0x00 },  {  45, 0x01 },
+  {  46, 0x00 },  {  47, 0x00 },  {  48, 0x00 },  {  49, 0x00 },
+  {  50, 0x00 },  {  51, 0x01 },  {  52, 0x00 },  {  53, 0x1C },
+  {  54, 0x00 },  {  55, 0x00 },  {  56, 0x00 },  {  57, 0x00 },
+  {  58, 0x00 },  {  59, 0x01 },  {  60, 0x00 },  {  61, 0x18 },
+  {  62, 0x00 },  {  63, 0x00 },  {  64, 0x00 },  {  65, 0x00 },
+  {  66, 0x00 },  {  67, 0x00 },  {  68, 0x00 },  {  69, 0x00 },
+  {  70, 0x00 },  {  71, 0x00 },  {  72, 0x00 },  {  73, 0x00 },
+  {  74, 0x00 },  {  75, 0x00 },  {  76, 0x00 },  {  77, 0x00 },
+  {  78, 0x00 },  {  79, 0x00 },  {  80, 0x00 },  {  81, 0x00 },
+  {  82, 0x00 },  {  83, 0x00 },  {  84, 0x00 },  {  85, 0x00 },
+  {  86, 0x00 },  {  87, 0x00 },  {  88, 0x00 },  {  89, 0x00 },
+  {  90, 0x00 },  {  91, 0x00 },  {  92, 0x00 },
+  /* Misc Config Register */
+  { 149, 0x00 },  { 150, 0x00 },  { 151, 0x00 },  { 152, 0x00 },
+  { 153, 0x00 },  { 154, 0x00 },  { 155, 0x00 },  { 156, 0x00 },
+  { 157, 0x00 },  { 158, 0x00 },  { 159, 0x00 },  { 160, 0x00 },
+  { 161, 0x00 },  { 162, 0x00 },  { 163, 0x00 },  { 164, 0x00 },
+  { 165, 0x00 },  { 166, 0x00 },  { 167, 0x00 },  { 168, 0x00 },
+  { 169, 0x00 },  { 170, 0x00 }
+};
+
+void SI5351A::debug_example_clock(void)
+{
+    /* Disable all outputs setting CLKx_DIS high */
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
+    /* Writes configuration data to device using the register map contents
+     generated by ClockBuilder Desktop (registers 15-92 + 149-170) */
+    for (uint16_t i = 0; i < sizeof(m_si5351_regs_15to92_149to170)/2; i++){
+        si5351_write(m_si5351_regs_15to92_149to170[i][0],
+                              m_si5351_regs_15to92_149to170[i][1]);
+    }
+    /* Apply soft reset */
+    si5351_write(SI5351_REG_177_PLL_RESET, 0xac);
+    /* Enabled desired outputs (see Register 3) */
+    si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x00);
+    printf("Please check following output\r\n");
+    printf("CLK0: 120.00MHz, CLK1: 12.00MHz, CLK2: 13.56MHz\r\n");
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Original & reference files
+//---------------------------------------------------------------------------------------------------------------------
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Adafruit_SI5351.cpp
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+/**************************************************************************/
+/*!
+    @file     Adafruit_SI5351.cpp
+    @author   K. Townsend (Adafruit Industries)
+
+    @brief    Driver for the SI5351 160MHz Clock Gen
+
+    @section  REFERENCES
+
+    Si5351A/B/C Datasheet:
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+
+    Manually Generating an Si5351 Register Map:
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+
+    @section  LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2014, Adafruit Industries
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+#if defined(__AVR__)
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#else
+#include "pgmspace.h"
+#endif
+#include <stdlib.h>
+
+#include <Adafruit_SI5351.h>
+
+/**************************************************************************/
+/*!
+    Constructor
+*/
+/**************************************************************************/
+Adafruit_SI5351::Adafruit_SI5351(void)
+{
+  m_si5351Config.initialised     = false;
+  m_si5351Config.crystalFreq     = SI5351_CRYSTAL_FREQ_25MHZ;
+  m_si5351Config.crystalLoad     = SI5351_CRYSTAL_LOAD_10PF;
+  m_si5351Config.crystalPPM      = 30;
+  m_si5351Config.plla_configured = false;
+  m_si5351Config.plla_freq       = 0;
+  m_si5351Config.pllb_configured = false;
+  m_si5351Config.pllb_freq       = 0;
+}
+
+/**************************************************************************/
+/*!
+    Initializes I2C and configures the breakout (call this function before
+    doing anything else)
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::begin(void)
+{
+  /* Initialise I2C */
+  Wire.begin();
+
+  /* ToDo: Make sure we're actually connected */
+
+  /* Disable all outputs setting CLKx_DIS high */
+  ASSERT_STATUS(write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xFF));
+
+  /* Power down all output drivers */
+  ASSERT_STATUS(write8(SI5351_REGISTER_16_CLK0_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_17_CLK1_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_18_CLK2_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_19_CLK3_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_20_CLK4_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_21_CLK5_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_22_CLK6_CONTROL, 0x80));
+  ASSERT_STATUS(write8(SI5351_REGISTER_23_CLK7_CONTROL, 0x80));
+
+  /* Set the load capacitance for the XTAL */
+  ASSERT_STATUS(write8(SI5351_REGISTER_183_CRYSTAL_INTERNAL_LOAD_CAPACITANCE,
+                       m_si5351Config.crystalLoad));
+
+  /* Set interrupt masks as required (see Register 2 description in AN619).
+     By default, ClockBuilder Desktop sets this register to 0x18.
+     Note that the least significant nibble must remain 0x8, but the most
+     significant nibble may be modified to suit your needs. */
+
+  /* Reset the PLL config fields just in case we call init again */
+  m_si5351Config.plla_configured = false;
+  m_si5351Config.plla_freq = 0;
+  m_si5351Config.pllb_configured = false;
+  m_si5351Config.pllb_freq = 0;
+
+  /* All done! */
+  m_si5351Config.initialised = true;
+
+  return ERROR_NONE;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Configures the Si5351 with config settings generated in
+            ClockBuilder. You can use this function to make sure that
+            your HW is properly configure and that there are no problems
+            with the board itself.
+
+            Running this function should provide the following output:
+            * Channel 0: 120.00 MHz
+            * Channel 1: 12.00  MHz
+            * Channel 2: 13.56  MHz
+
+    @note   This will overwrite all of the config registers!
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::setClockBuilderData(void)
+{
+  uint16_t i = 0;
+
+  /* Make sure we've called init first */
+  ASSERT(m_si5351Config.initialised, ERROR_DEVICENOTINITIALISED);
+
+  /* Disable all outputs setting CLKx_DIS high */
+  ASSERT_STATUS(write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xFF));
+
+  /* Writes configuration data to device using the register map contents
+     generated by ClockBuilder Desktop (registers 15-92 + 149-170) */
+  for (i=0; i<sizeof(m_si5351_regs_15to92_149to170)/2; i++)
+  {
+    ASSERT_STATUS(write8( m_si5351_regs_15to92_149to170[i][0],
+                          m_si5351_regs_15to92_149to170[i][1] ));
+  }
+
+  /* Apply soft reset */
+  ASSERT_STATUS(write8(SI5351_REGISTER_177_PLL_RESET, 0xAC));
+
+  /* Enabled desired outputs (see Register 3) */
+  ASSERT_STATUS(write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0x00));
+
+  return ERROR_NONE;
+}
+
+/**************************************************************************/
+/*!
+  @brief  Sets the multiplier for the specified PLL using integer values
+
+  @param  pll   The PLL to configure, which must be one of the following:
+                - SI5351_PLL_A
+                - SI5351_PLL_B
+  @param  mult  The PLL integer multiplier (must be between 15 and 90)
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::setupPLLInt(si5351PLL_t pll, uint8_t mult)
+{
+  return setupPLL(pll, mult, 0, 1);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Sets the multiplier for the specified PLL
+
+    @param  pll   The PLL to configure, which must be one of the following:
+                  - SI5351_PLL_A
+                  - SI5351_PLL_B
+    @param  mult  The PLL integer multiplier (must be between 15 and 90)
+    @param  num   The 20-bit numerator for fractional output (0..1,048,575).
+                  Set this to '0' for integer output.
+    @param  denom The 20-bit denominator for fractional output (1..1,048,575).
+                  Set this to '1' or higher to avoid divider by zero errors.
+
+    @section PLL Configuration
+
+    fVCO is the PLL output, and must be between 600..900MHz, where:
+
+        fVCO = fXTAL * (a+(b/c))
+
+    fXTAL = the crystal input frequency
+    a     = an integer between 15 and 90
+    b     = the fractional numerator (0..1,048,575)
+    c     = the fractional denominator (1..1,048,575)
+
+    NOTE: Try to use integers whenever possible to avoid clock jitter
+    (only use the a part, setting b to '0' and c to '1').
+
+    See: http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::setupPLL(si5351PLL_t pll,
+                                uint8_t     mult,
+                                uint32_t    num,
+                                uint32_t    denom)
+{
+  uint32_t P1;       /* PLL config register P1 */
+  uint32_t P2;       /* PLL config register P2 */
+  uint32_t P3;       /* PLL config register P3 */
+
+  /* Basic validation */
+  ASSERT( m_si5351Config.initialised, ERROR_DEVICENOTINITIALISED );
+  ASSERT( (mult > 14) && (mult < 91), ERROR_INVALIDPARAMETER ); /* mult = 15..90 */
+  ASSERT( denom > 0,                  ERROR_INVALIDPARAMETER ); /* Avoid divide by zero */
+  ASSERT( num <= 0xFFFFF,             ERROR_INVALIDPARAMETER ); /* 20-bit limit */
+  ASSERT( denom <= 0xFFFFF,           ERROR_INVALIDPARAMETER ); /* 20-bit limit */
+
+  /* Feedback Multisynth Divider Equation
+   *
+   * where: a = mult, b = num and c = denom
+   *
+   * P1 register is an 18-bit value using following formula:
+   *
+   *    P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512
+   *
+   * P2 register is a 20-bit value using the following formula:
+   *
+   *    P2[19:0] = 128 * num - denom * floor(128*(num/denom))
+   *
+   * P3 register is a 20-bit value using the following formula:
+   *
+   *    P3[19:0] = denom
+   */
+
+  /* Set the main PLL config registers */
+  if (num == 0)
+  {
+    /* Integer mode */
+    P1 = 128 * mult - 512;
+    P2 = num;
+    P3 = denom;
+  }
+  else
+  {
+    /* Fractional mode */
+    P1 = (uint32_t)(128 * mult + floor(128 * ((float)num/(float)denom)) - 512);
+    P2 = (uint32_t)(128 * num - denom * floor(128 * ((float)num/(float)denom)));
+    P3 = denom;
+  }
+
+  /* Get the appropriate starting point for the PLL registers */
+  uint8_t baseaddr = (pll == SI5351_PLL_A ? 26 : 34);
+
+  /* The datasheet is a nightmare of typos and inconsistencies here! */
+  ASSERT_STATUS( write8( baseaddr,   (P3 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+1, (P3 & 0x000000FF)));
+  ASSERT_STATUS( write8( baseaddr+2, (P1 & 0x00030000) >> 16));
+  ASSERT_STATUS( write8( baseaddr+3, (P1 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+4, (P1 & 0x000000FF)));
+  ASSERT_STATUS( write8( baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) ));
+  ASSERT_STATUS( write8( baseaddr+6, (P2 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+7, (P2 & 0x000000FF)));
+
+  /* Reset both PLLs */
+  ASSERT_STATUS( write8(SI5351_REGISTER_177_PLL_RESET, (1<<7) | (1<<5) ));
+
+  /* Store the frequency settings for use with the Multisynth helper */
+  if (pll == SI5351_PLL_A)
+  {
+    float fvco = m_si5351Config.crystalFreq * (mult + ( (float)num / (float)denom ));
+    m_si5351Config.plla_configured = true;
+    m_si5351Config.plla_freq = (uint32_t)floor(fvco);
+  }
+  else
+  {
+    float fvco = m_si5351Config.crystalFreq * (mult + ( (float)num / (float)denom ));
+    m_si5351Config.pllb_configured = true;
+    m_si5351Config.pllb_freq = (uint32_t)floor(fvco);
+  }
+
+  return ERROR_NONE;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Configures the Multisynth divider using integer output.
+
+    @param  output    The output channel to use (0..2)
+    @param  pllSource   The PLL input source to use, which must be one of:
+                      - SI5351_PLL_A
+                      - SI5351_PLL_B
+    @param  div       The integer divider for the Multisynth output,
+                      which must be one of the following values:
+                      - SI5351_MULTISYNTH_DIV_4
+                      - SI5351_MULTISYNTH_DIV_6
+                      - SI5351_MULTISYNTH_DIV_8
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::setupMultisynthInt(uint8_t               output,
+                                          si5351PLL_t           pllSource,
+                                          si5351MultisynthDiv_t div)
+{
+  return setupMultisynth(output, pllSource, div, 0, 1);
+}
+
+
+err_t Adafruit_SI5351::setupRdiv(uint8_t  output, si5351RDiv_t div) {
+  ASSERT( output < 3,                 ERROR_INVALIDPARAMETER);  /* Channel range */
+
+  uint8_t Rreg, regval;
+
+  if (output == 0) Rreg = SI5351_REGISTER_44_MULTISYNTH0_PARAMETERS_3;
+  if (output == 1) Rreg = SI5351_REGISTER_52_MULTISYNTH1_PARAMETERS_3;
+  if (output == 2) Rreg = SI5351_REGISTER_60_MULTISYNTH2_PARAMETERS_3;
+
+  read8(Rreg, &regval);
+
+  regval &= 0x0F;
+  uint8_t divider = div;
+  divider &= 0x07;
+  divider <<= 4;
+  regval |= divider;
+  return write8(Rreg, regval);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Configures the Multisynth divider, which determines the
+            output clock frequency based on the specified PLL input.
+
+    @param  output    The output channel to use (0..2)
+    @param  pllSource   The PLL input source to use, which must be one of:
+                      - SI5351_PLL_A
+                      - SI5351_PLL_B
+    @param  div       The integer divider for the Multisynth output.
+                      If pure integer values are used, this value must
+                      be one of:
+                      - SI5351_MULTISYNTH_DIV_4
+                      - SI5351_MULTISYNTH_DIV_6
+                      - SI5351_MULTISYNTH_DIV_8
+                      If fractional output is used, this value must be
+                      between 8 and 900.
+    @param  num       The 20-bit numerator for fractional output
+                      (0..1,048,575). Set this to '0' for integer output.
+    @param  denom     The 20-bit denominator for fractional output
+                      (1..1,048,575). Set this to '1' or higher to
+                      avoid divide by zero errors.
+
+    @section Output Clock Configuration
+
+    The multisynth dividers are applied to the specified PLL output,
+    and are used to reduce the PLL output to a valid range (500kHz
+    to 160MHz). The relationship can be seen in this formula, where
+    fVCO is the PLL output frequency and MSx is the multisynth
+    divider:
+
+        fOUT = fVCO / MSx
+
+    Valid multisynth dividers are 4, 6, or 8 when using integers,
+    or any fractional values between 8 + 1/1,048,575 and 900 + 0/1
+
+    The following formula is used for the fractional mode divider:
+
+        a + b / c
+
+    a = The integer value, which must be 4, 6 or 8 in integer mode (MSx_INT=1)
+        or 8..900 in fractional mode (MSx_INT=0).
+    b = The fractional numerator (0..1,048,575)
+    c = The fractional denominator (1..1,048,575)
+
+    @note   Try to use integers whenever possible to avoid clock jitter
+
+    @note   For output frequencies > 150MHz, you must set the divider
+            to 4 and adjust to PLL to generate the frequency (for example
+            a PLL of 640 to generate a 160MHz output clock). This is not
+            yet supported in the driver, which limits frequencies to
+            500kHz .. 150MHz.
+
+    @note   For frequencies below 500kHz (down to 8kHz) Rx_DIV must be
+            used, but this isn't currently implemented in the driver.
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::setupMultisynth(uint8_t     output,
+                                       si5351PLL_t pllSource,
+                                       uint32_t    div,
+                                       uint32_t    num,
+                                       uint32_t    denom)
+{
+  uint32_t P1;       /* Multisynth config register P1 */
+  uint32_t P2;       /* Multisynth config register P2 */
+  uint32_t P3;       /* Multisynth config register P3 */
+
+  /* Basic validation */
+  ASSERT( m_si5351Config.initialised, ERROR_DEVICENOTINITIALISED);
+  ASSERT( output < 3,                 ERROR_INVALIDPARAMETER);  /* Channel range */
+  ASSERT( div > 3,                    ERROR_INVALIDPARAMETER);  /* Divider integer value */
+  ASSERT( div < 901,                  ERROR_INVALIDPARAMETER);  /* Divider integer value */
+  ASSERT( denom > 0,                  ERROR_INVALIDPARAMETER ); /* Avoid divide by zero */
+  ASSERT( num <= 0xFFFFF,             ERROR_INVALIDPARAMETER ); /* 20-bit limit */
+  ASSERT( denom <= 0xFFFFF,           ERROR_INVALIDPARAMETER ); /* 20-bit limit */
+
+  /* Make sure the requested PLL has been initialised */
+  if (pllSource == SI5351_PLL_A)
+  {
+    ASSERT(m_si5351Config.plla_configured = true, ERROR_INVALIDPARAMETER);
+  }
+  else
+  {
+    ASSERT(m_si5351Config.pllb_configured = true, ERROR_INVALIDPARAMETER);
+  }
+
+  /* Output Multisynth Divider Equations
+   *
+   * where: a = div, b = num and c = denom
+   *
+   * P1 register is an 18-bit value using following formula:
+   *
+   *    P1[17:0] = 128 * a + floor(128*(b/c)) - 512
+   *
+   * P2 register is a 20-bit value using the following formula:
+   *
+   *    P2[19:0] = 128 * b - c * floor(128*(b/c))
+   *
+   * P3 register is a 20-bit value using the following formula:
+   *
+   *    P3[19:0] = c
+   */
+
+  /* Set the main PLL config registers */
+  if (num == 0)
+  {
+    /* Integer mode */
+    P1 = 128 * div - 512;
+    P2 = num;
+    P3 = denom;
+  }
+  else
+  {
+    /* Fractional mode */
+    P1 = (uint32_t)(128 * div + floor(128 * ((float)num/(float)denom)) - 512);
+    P2 = (uint32_t)(128 * num - denom * floor(128 * ((float)num/(float)denom)));
+    P3 = denom;
+  }
+
+  /* Get the appropriate starting point for the PLL registers */
+  uint8_t baseaddr = 0;
+  switch (output)
+  {
+    case 0:
+      baseaddr = SI5351_REGISTER_42_MULTISYNTH0_PARAMETERS_1;
+      break;
+    case 1:
+      baseaddr = SI5351_REGISTER_50_MULTISYNTH1_PARAMETERS_1;
+      break;
+    case 2:
+      baseaddr = SI5351_REGISTER_58_MULTISYNTH2_PARAMETERS_1;
+      break;
+  }
+
+  /* Set the MSx config registers */
+  ASSERT_STATUS( write8( baseaddr,   (P3 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+1, (P3 & 0x000000FF)));
+  ASSERT_STATUS( write8( baseaddr+2, (P1 & 0x00030000) >> 16)); /* ToDo: Add DIVBY4 (>150MHz) and R0 support (<500kHz) later */
+  ASSERT_STATUS( write8( baseaddr+3, (P1 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+4, (P1 & 0x000000FF)));
+  ASSERT_STATUS( write8( baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) ));
+  ASSERT_STATUS( write8( baseaddr+6, (P2 & 0x0000FF00) >> 8));
+  ASSERT_STATUS( write8( baseaddr+7, (P2 & 0x000000FF)));
+
+  /* Configure the clk control and enable the output */
+  uint8_t clkControlReg = 0x0F;                             /* 8mA drive strength, MS0 as CLK0 source, Clock not inverted, powered up */
+  if (pllSource == SI5351_PLL_B) clkControlReg |= (1 << 5); /* Uses PLLB */
+  if (num == 0) clkControlReg |= (1 << 6);                  /* Integer mode */
+  switch (output)
+  {
+    case 0:
+      ASSERT_STATUS(write8(SI5351_REGISTER_16_CLK0_CONTROL, clkControlReg));
+      break;
+    case 1:
+      ASSERT_STATUS(write8(SI5351_REGISTER_17_CLK1_CONTROL, clkControlReg));
+      break;
+    case 2:
+      ASSERT_STATUS(write8(SI5351_REGISTER_18_CLK2_CONTROL, clkControlReg));
+      break;
+  }
+
+  return ERROR_NONE;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Enables or disables all clock outputs
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::enableOutputs(bool enabled)
+{
+  /* Make sure we've called init first */
+  ASSERT(m_si5351Config.initialised, ERROR_DEVICENOTINITIALISED);
+
+  /* Enabled desired outputs (see Register 3) */
+  ASSERT_STATUS(write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, enabled ? 0x00: 0xFF));
+
+  return ERROR_NONE;
+}
+
+/* ---------------------------------------------------------------------- */
+/* PRUVATE FUNCTIONS                                                      */
+/* ---------------------------------------------------------------------- */
+
+/**************************************************************************/
+/*!
+    @brief  Writes a register and an 8 bit value over I2C
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::write8 (uint8_t reg, uint8_t value)
+{
+  Wire.beginTransmission(SI5351_ADDRESS);
+  #if ARDUINO >= 100
+  Wire.write(reg);
+  Wire.write(value & 0xFF);
+  #else
+  Wire.send(reg);
+  Wire.send(value & 0xFF);
+  #endif
+  Wire.endTransmission();
+
+  // ToDo: Check for I2C errors
+
+  return ERROR_NONE;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Reads an 8 bit value over I2C
+*/
+/**************************************************************************/
+err_t Adafruit_SI5351::read8(uint8_t reg, uint8_t *value)
+{
+  Wire.beginTransmission(SI5351_ADDRESS);
+  #if ARDUINO >= 100
+  Wire.write(reg);
+  #else
+  Wire.send(reg);
+  #endif
+  Wire.endTransmission();
+
+  Wire.requestFrom(SI5351_ADDRESS, 1);
+  #if ARDUINO >= 100
+  *value = Wire.read();
+  #else
+  *value = Wire.read();
+  #endif
+
+  // ToDo: Check for I2C errors
+
+  return ERROR_NONE;
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Adafruit_SI5351.h
+////////////////////////////////////////////////////////////////////////////////
+#if 0
+/**************************************************************************/
+/*!
+    @file     Adafruit_SI5351.h
+    @author   K. Townsend (Adafruit Industries)
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2014, Adafruit Industries
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+#ifndef _SI5351_H_
+#define _SI5351_H_
+
+#if ARDUINO >= 100
+ #include <Arduino.h>
+#else
+ #include <WProgram.h>
+#endif
+//#include <Adafruit_Sensor.h>
+#include <Wire.h>
+
+#include "errors.h"
+#include "asserts.h"
+
+#define SI5351_ADDRESS            (0x60) // Assumes ADDR pin = low
+#define SI5351_READBIT            (0x01)
+
+/* Test setup from SI5351 ClockBuilder
+ * -----------------------------------
+ * XTAL:      25     MHz
+ * Channel 0: 120.00 MHz
+ * Channel 1: 12.00  MHz
+ * Channel 2: 13.56  MHz
+ */
+static const uint8_t m_si5351_regs_15to92_149to170[100][2] =
+{
+  {  15, 0x00 },    /* Input source = crystal for PLLA and PLLB */
+  {  16, 0x4F },    /* CLK0 Control: 8mA drive, Multisynth 0 as CLK0 source, Clock not inverted, Source = PLLA, Multisynth 0 in integer mode, clock powered up */
+  {  17, 0x4F },    /* CLK1 Control: 8mA drive, Multisynth 1 as CLK1 source, Clock not inverted, Source = PLLA, Multisynth 1 in integer mode, clock powered up */
+  {  18, 0x6F },    /* CLK2 Control: 8mA drive, Multisynth 2 as CLK2 source, Clock not inverted, Source = PLLB, Multisynth 2 in integer mode, clock powered up */
+  {  19, 0x80 },    /* CLK3 Control: Not used ... clock powered down */
+  {  20, 0x80 },    /* CLK4 Control: Not used ... clock powered down */
+  {  21, 0x80 },    /* CLK5 Control: Not used ... clock powered down */
+  {  22, 0x80 },    /* CLK6 Control: Not used ... clock powered down */
+  {  23, 0x80 },    /* CLK7 Control: Not used ... clock powered down */
+  {  24, 0x00 },    /* Clock disable state 0..3 (low when disabled) */
+  {  25, 0x00 },    /* Clock disable state 4..7 (low when disabled) */
+  /* PLL_A Setup */
+  {  26, 0x00 },
+  {  27, 0x05 },
+  {  28, 0x00 },
+  {  29, 0x0C },
+  {  30, 0x66 },
+  {  31, 0x00 },
+  {  32, 0x00 },
+  {  33, 0x02 },
+  /* PLL_B Setup */
+  {  34, 0x02 },
+  {  35, 0x71 },
+  {  36, 0x00 },
+  {  37, 0x0C },
+  {  38, 0x1A },
+  {  39, 0x00 },
+  {  40, 0x00 },
+  {  41, 0x86 },
+  /* Multisynth Setup */
+  {  42, 0x00 },
+  {  43, 0x01 },
+  {  44, 0x00 },
+  {  45, 0x01 },
+  {  46, 0x00 },
+  {  47, 0x00 },
+  {  48, 0x00 },
+  {  49, 0x00 },
+  {  50, 0x00 },
+  {  51, 0x01 },
+  {  52, 0x00 },
+  {  53, 0x1C },
+  {  54, 0x00 },
+  {  55, 0x00 },
+  {  56, 0x00 },
+  {  57, 0x00 },
+  {  58, 0x00 },
+  {  59, 0x01 },
+  {  60, 0x00 },
+  {  61, 0x18 },
+  {  62, 0x00 },
+  {  63, 0x00 },
+  {  64, 0x00 },
+  {  65, 0x00 },
+  {  66, 0x00 },
+  {  67, 0x00 },
+  {  68, 0x00 },
+  {  69, 0x00 },
+  {  70, 0x00 },
+  {  71, 0x00 },
+  {  72, 0x00 },
+  {  73, 0x00 },
+  {  74, 0x00 },
+  {  75, 0x00 },
+  {  76, 0x00 },
+  {  77, 0x00 },
+  {  78, 0x00 },
+  {  79, 0x00 },
+  {  80, 0x00 },
+  {  81, 0x00 },
+  {  82, 0x00 },
+  {  83, 0x00 },
+  {  84, 0x00 },
+  {  85, 0x00 },
+  {  86, 0x00 },
+  {  87, 0x00 },
+  {  88, 0x00 },
+  {  89, 0x00 },
+  {  90, 0x00 },
+  {  91, 0x00 },
+  {  92, 0x00 },
+  /* Misc Config Register */
+  { 149, 0x00 },
+  { 150, 0x00 },
+  { 151, 0x00 },
+  { 152, 0x00 },
+  { 153, 0x00 },
+  { 154, 0x00 },
+  { 155, 0x00 },
+  { 156, 0x00 },
+  { 157, 0x00 },
+  { 158, 0x00 },
+  { 159, 0x00 },
+  { 160, 0x00 },
+  { 161, 0x00 },
+  { 162, 0x00 },
+  { 163, 0x00 },
+  { 164, 0x00 },
+  { 165, 0x00 },
+  { 166, 0x00 },
+  { 167, 0x00 },
+  { 168, 0x00 },
+  { 169, 0x00 },
+  { 170, 0x00 }
+};
+
+/* See http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf for registers 26..41 */
+enum
+{
+  SI5351_REGISTER_0_DEVICE_STATUS                       = 0,
+  SI5351_REGISTER_1_INTERRUPT_STATUS_STICKY             = 1,
+  SI5351_REGISTER_2_INTERRUPT_STATUS_MASK               = 2,
+  SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL               = 3,
+  SI5351_REGISTER_9_OEB_PIN_ENABLE_CONTROL              = 9,
+  SI5351_REGISTER_15_PLL_INPUT_SOURCE                   = 15,
+  SI5351_REGISTER_16_CLK0_CONTROL                       = 16,
+  SI5351_REGISTER_17_CLK1_CONTROL                       = 17,
+  SI5351_REGISTER_18_CLK2_CONTROL                       = 18,
+  SI5351_REGISTER_19_CLK3_CONTROL                       = 19,
+  SI5351_REGISTER_20_CLK4_CONTROL                       = 20,
+  SI5351_REGISTER_21_CLK5_CONTROL                       = 21,
+  SI5351_REGISTER_22_CLK6_CONTROL                       = 22,
+  SI5351_REGISTER_23_CLK7_CONTROL                       = 23,
+  SI5351_REGISTER_24_CLK3_0_DISABLE_STATE               = 24,
+  SI5351_REGISTER_25_CLK7_4_DISABLE_STATE               = 25,
+  SI5351_REGISTER_42_MULTISYNTH0_PARAMETERS_1           = 42,
+  SI5351_REGISTER_43_MULTISYNTH0_PARAMETERS_2           = 43,
+  SI5351_REGISTER_44_MULTISYNTH0_PARAMETERS_3           = 44,
+  SI5351_REGISTER_45_MULTISYNTH0_PARAMETERS_4           = 45,
+  SI5351_REGISTER_46_MULTISYNTH0_PARAMETERS_5           = 46,
+  SI5351_REGISTER_47_MULTISYNTH0_PARAMETERS_6           = 47,
+  SI5351_REGISTER_48_MULTISYNTH0_PARAMETERS_7           = 48,
+  SI5351_REGISTER_49_MULTISYNTH0_PARAMETERS_8           = 49,
+  SI5351_REGISTER_50_MULTISYNTH1_PARAMETERS_1           = 50,
+  SI5351_REGISTER_51_MULTISYNTH1_PARAMETERS_2           = 51,
+  SI5351_REGISTER_52_MULTISYNTH1_PARAMETERS_3           = 52,
+  SI5351_REGISTER_53_MULTISYNTH1_PARAMETERS_4           = 53,
+  SI5351_REGISTER_54_MULTISYNTH1_PARAMETERS_5           = 54,
+  SI5351_REGISTER_55_MULTISYNTH1_PARAMETERS_6           = 55,
+  SI5351_REGISTER_56_MULTISYNTH1_PARAMETERS_7           = 56,
+  SI5351_REGISTER_57_MULTISYNTH1_PARAMETERS_8           = 57,
+  SI5351_REGISTER_58_MULTISYNTH2_PARAMETERS_1           = 58,
+  SI5351_REGISTER_59_MULTISYNTH2_PARAMETERS_2           = 59,
+  SI5351_REGISTER_60_MULTISYNTH2_PARAMETERS_3           = 60,
+  SI5351_REGISTER_61_MULTISYNTH2_PARAMETERS_4           = 61,
+  SI5351_REGISTER_62_MULTISYNTH2_PARAMETERS_5           = 62,
+  SI5351_REGISTER_63_MULTISYNTH2_PARAMETERS_6           = 63,
+  SI5351_REGISTER_64_MULTISYNTH2_PARAMETERS_7           = 64,
+  SI5351_REGISTER_65_MULTISYNTH2_PARAMETERS_8           = 65,
+  SI5351_REGISTER_66_MULTISYNTH3_PARAMETERS_1           = 66,
+  SI5351_REGISTER_67_MULTISYNTH3_PARAMETERS_2           = 67,
+  SI5351_REGISTER_68_MULTISYNTH3_PARAMETERS_3           = 68,
+  SI5351_REGISTER_69_MULTISYNTH3_PARAMETERS_4           = 69,
+  SI5351_REGISTER_70_MULTISYNTH3_PARAMETERS_5           = 70,
+  SI5351_REGISTER_71_MULTISYNTH3_PARAMETERS_6           = 71,
+  SI5351_REGISTER_72_MULTISYNTH3_PARAMETERS_7           = 72,
+  SI5351_REGISTER_73_MULTISYNTH3_PARAMETERS_8           = 73,
+  SI5351_REGISTER_74_MULTISYNTH4_PARAMETERS_1           = 74,
+  SI5351_REGISTER_75_MULTISYNTH4_PARAMETERS_2           = 75,
+  SI5351_REGISTER_76_MULTISYNTH4_PARAMETERS_3           = 76,
+  SI5351_REGISTER_77_MULTISYNTH4_PARAMETERS_4           = 77,
+  SI5351_REGISTER_78_MULTISYNTH4_PARAMETERS_5           = 78,
+  SI5351_REGISTER_79_MULTISYNTH4_PARAMETERS_6           = 79,
+  SI5351_REGISTER_80_MULTISYNTH4_PARAMETERS_7           = 80,
+  SI5351_REGISTER_81_MULTISYNTH4_PARAMETERS_8           = 81,
+  SI5351_REGISTER_82_MULTISYNTH5_PARAMETERS_1           = 82,
+  SI5351_REGISTER_83_MULTISYNTH5_PARAMETERS_2           = 83,
+  SI5351_REGISTER_84_MULTISYNTH5_PARAMETERS_3           = 84,
+  SI5351_REGISTER_85_MULTISYNTH5_PARAMETERS_4           = 85,
+  SI5351_REGISTER_86_MULTISYNTH5_PARAMETERS_5           = 86,
+  SI5351_REGISTER_87_MULTISYNTH5_PARAMETERS_6           = 87,
+  SI5351_REGISTER_88_MULTISYNTH5_PARAMETERS_7           = 88,
+  SI5351_REGISTER_89_MULTISYNTH5_PARAMETERS_8           = 89,
+  SI5351_REGISTER_90_MULTISYNTH6_PARAMETERS             = 90,
+  SI5351_REGISTER_91_MULTISYNTH7_PARAMETERS             = 91,
+  SI5351_REGISTER_092_CLOCK_6_7_OUTPUT_DIVIDER          = 92,
+  SI5351_REGISTER_165_CLK0_INITIAL_PHASE_OFFSET         = 165,
+  SI5351_REGISTER_166_CLK1_INITIAL_PHASE_OFFSET         = 166,
+  SI5351_REGISTER_167_CLK2_INITIAL_PHASE_OFFSET         = 167,
+  SI5351_REGISTER_168_CLK3_INITIAL_PHASE_OFFSET         = 168,
+  SI5351_REGISTER_169_CLK4_INITIAL_PHASE_OFFSET         = 169,
+  SI5351_REGISTER_170_CLK5_INITIAL_PHASE_OFFSET         = 170,
+  SI5351_REGISTER_177_PLL_RESET                         = 177,
+  SI5351_REGISTER_183_CRYSTAL_INTERNAL_LOAD_CAPACITANCE = 183
+};
+
+typedef enum
+{
+  SI5351_PLL_A = 0,
+  SI5351_PLL_B,
+} si5351PLL_t;
+
+typedef enum
+{
+  SI5351_CRYSTAL_LOAD_6PF  = (1<<6),
+  SI5351_CRYSTAL_LOAD_8PF  = (2<<6),
+  SI5351_CRYSTAL_LOAD_10PF = (3<<6)
+} si5351CrystalLoad_t;
+
+typedef enum
+{
+  SI5351_CRYSTAL_FREQ_25MHZ = (25000000),
+  SI5351_CRYSTAL_FREQ_27MHZ = (27000000)
+} si5351CrystalFreq_t;
+
+typedef enum
+{
+  SI5351_MULTISYNTH_DIV_4  = 4,
+  SI5351_MULTISYNTH_DIV_6  = 6,
+  SI5351_MULTISYNTH_DIV_8  = 8
+} si5351MultisynthDiv_t;
+
+typedef enum
+{
+  SI5351_R_DIV_1   = 0,
+  SI5351_R_DIV_2   = 1,
+  SI5351_R_DIV_4   = 2,
+  SI5351_R_DIV_8   = 3,
+  SI5351_R_DIV_16  = 4,
+  SI5351_R_DIV_32  = 5,
+  SI5351_R_DIV_64  = 6,
+  SI5351_R_DIV_128 = 7,
+} si5351RDiv_t;
+
+typedef struct
+{
+  bool                initialised;
+  si5351CrystalFreq_t crystalFreq;
+  si5351CrystalLoad_t crystalLoad;
+  uint32_t            crystalPPM;
+  bool                plla_configured;
+  uint32_t            plla_freq;
+  bool                pllb_configured;
+  uint32_t            pllb_freq;
+} si5351Config_t;
+
+class Adafruit_SI5351
+{
+ public:
+  Adafruit_SI5351(void);
+
+  err_t begin(void);
+  err_t setClockBuilderData(void);
+  err_t setupPLL(si5351PLL_t pll, uint8_t mult, uint32_t num, uint32_t denom);
+  err_t setupPLLInt(si5351PLL_t pll, uint8_t mult);
+  err_t setupMultisynth(uint8_t output, si5351PLL_t pllSource, uint32_t div, uint32_t num, uint32_t denom);
+  err_t setupMultisynthInt(uint8_t output, si5351PLL_t pllSource, si5351MultisynthDiv_t div);
+  err_t enableOutputs(bool enabled);
+  err_t setupRdiv(uint8_t  output, si5351RDiv_t div);
+
+ private:
+  si5351Config_t m_si5351Config;
+
+  err_t write8(uint8_t reg, uint8_t value);
+  err_t read8(uint8_t reg, uint8_t *value);
+};
+
+#endif
+
+#endif
+//