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/

si5351a.cpp

Committer:
kenjiArai
Date:
2017-08-23
Revision:
4:8c63d15c8c2e
Parent:
3:af2d99cfb3f0

File content as of revision 4:8c63d15c8c2e:

/*
 * 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:  August   23rd, 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(new I2C(p_sda, p_scl)), _i2c(*_i2c_p)
{
    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~110MHz fixed PLL (XTAL * PLL_N)MHz, fractional divider
 * 110~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_110MHZ){
        if((channel == SI5351_CLK1) && (clk2_state == CLK_OUT_FIXEDDIV)){
                DBG("DBG: Error CLK2 uses as over 110MHz!!\r\n");
                return 0;
        } else if((channel == SI5351_CLK2) && (clk1_state == CLK_OUT_FIXEDDIV)){
                DBG("DBG: Error CLK1 uses as over 110MHz!!\r\n");
                return 0;
        }
        DBG("DBG: Passed condition\r\n");
        if (freq > FREQ_450KHZ){ // over 450KHz to 110MHz
            si5351_set_PLL_input_condition(FREQ_900MHZ);
            si5351_setupPLL(pll, pll_n, 0, 1);
            f = si5351_set_frequency_fixedpll(channel, pll, pll_freq, freq, 0);
        } else if (freq > FREQ_75KHZ){
            si5351_set_PLL_input_condition(FREQ_600MHZ);
            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_set_PLL_input_condition(FREQ_600MHZ);
            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_set_PLL_input_condition(FREQ_600MHZ);
            si5351_setupPLL(pll, pll_n, 0, 1);
            f = si5351_set_frequency_fixedpll(
                    channel, pll, pll_freq, freq * 128, SI5351_R_DIV_128);
#if defined(RANGE_EXTENDED)
            // CAUTION !!!!!!
            if (f == 0){    // This part is outside of specification!!
                DBG("DBG: Out of range but try again!\r\n");
                pll_n = 15; // around 375MHz
                pll_freq = base_freq * pll_n;
                pll_freq = (uint32_t)((double)pll_freq / compensation);
                si5351_setupPLL(pll, pll_n, 0, 1);
                f = si5351_set_frequency_fixedpll(
                        channel, pll, pll_freq, freq * 128, SI5351_R_DIV_128);
            }
#endif
            f /= 128.0f;
        }
        state = CLK_OUT_FIXEDPLL;
    } else {
        DBG("DBG: Set over 110MHz clock\r\n");
        if((channel == SI5351_CLK1) && (clk2_state == CLK_OUT_FIXEDPLL)){
                DBG("DBG: Error CLK2 uses as under 110MHz!!\r\n");
                return 0;
        } else if((channel == SI5351_CLK2) && (clk1_state == CLK_OUT_FIXEDPLL)){
                DBG("DBG: Error CLK1 uses as under 110MHz!!\r\n");
                return 0;
        }
        DBG("DBG: Passed condition\r\n");
        if (freq < FREQ_150MHZ) {
            DBG("DBG: Set 110MHz to 150MHz clock\r\n");
            f = si5351_set_frequency_fixeddiv(channel, pll, freq, 6);
        } else {
#if defined(RANGE_EXTENDED)
            DBG("DBG: Set over 150MHz clock (RANGE_EXTENDED)\r\n");
            f = si5351_set_frequency_fixeddiv(channel, pll, freq, 4);
#else
            DBG("DBG: Set over 150MHz clock\r\n");
            if (freq > FREQ_200MHZ) {
                DBG("DBG: Over 200MHz\r\n");
                f = 0;
            } else {
                f = si5351_set_frequency_fixeddiv(channel, pll, freq, 4);
            }
#endif
        }
        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 110MHz\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 110MHz\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 110MHz\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 110MHz
    if ((freq > FREQ_110MHZ) || (freq < FREQ_450KHZ)){// between 450KHz-110MHz
        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;
    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
    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;
    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;
    }
}

void SI5351A::f_compensation(uint32_t target_f, uint32_t measured_f)
{
    double new_comp_data;

    if (compensation == 1.0f){
        compensation = (double)target_f / (double)measured_f;
        pll_freq = (uint32_t)((double)pll_freq / compensation);
    } else {
        new_comp_data = (double)target_f / (double)measured_f;
        pll_freq = (uint32_t)((double)pll_freq / new_comp_data);
        compensation *= new_comp_data;
    }    
}

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;
    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;
    compensation = 1.0f;
    si5351_set_PLL_input_condition(FREQ_900MHZ);
    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);
}

void SI5351A::si5351_set_PLL_input_condition(uint32_t freq)
{
    uint8_t n;
    uint32_t dt;

    n = (uint8_t)(freq / base_freq);
    dt = base_freq * n;
    while (true){
        if (dt > FREQ_900MHZ){    --n;}
        if (dt < FREQ_600MHZ){    ++n;}
        dt = base_freq * n;
        if ((dt <= FREQ_900MHZ) && (dt >= FREQ_600MHZ)){ break;}
    }
    pll_n = n;
    pll_freq = dt;
    pll_freq = (uint32_t)((double)pll_freq / compensation);
    DBG("DBG: Change PLL_N data / pll_n=%u, pll_freq=%u\r\n", pll_n, pll_freq);
}

/******************************************************************************/
/*!
    @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);
    DBG("DBG: PLL f(pll_freq)=%u\r\n", (uint32_t)pll_freq);
    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;
    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");
        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;
        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");
    printf("--> compensation=%f\r\n", compensation);
    printf("--> pll_freq=XTAL*PLL_N=%u\r\n", pll_freq);
    //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
//