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.h

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
 *
 */

#ifndef         MBED_SI5351A
#define         MBED_SI5351A

#if 0           // Range extend mode -> set 1
#define         RANGE_EXTENDED
#endif
#if defined(RANGE_EXTENDED)
#warning "Si5351A PLL range is only 600MHz to 900MHz."
#warning "If you use RANGE_EXTENDED mode, PLL sets 250MHz to 1.3GHz."
#warning "This causes high possibility to make a \
             PARMANENT DAMAGE for the chip!!!!!"
#warning "Don't use this mode!!!"
#endif

////////////// ADDRESS /////////////////////////////////////////////////////////
//  7bit address = 0b1100000(0x60) -> 8bit = 0b11000000(0xc0)
//      -> Fixed adddress (No other choises)
#define SI5351_I2C_ADDR     (0x60<<1)

////////////// REGISTER DEFINITION /////////////////////////////////////////////
#define SI5351_REG_3_OUTPUT_ENABLE_CONTROL  3
#define SI5351_REG_16_CLK0_CONTROL          16
#define SI5351_REG_17_CLK1_CONTROL          17
#define SI5351_REG_18_CLK2_CONTROL          18
#define SI5351_REG_26_PLL_A                 26
#define SI5351_REG_34_PLL_B                 34
#define SI5351_REG_42_MULTISYNTH0           42
#define SI5351_REG_44_MULTISYNTH0_P3        44
#define SI5351_REG_50_MULTISYNTH1           50
#define SI5351_REG_52_MULTISYNTH1_P3        52
#define SI5351_REG_58_MULTISYNTH2           58
#define SI5351_REG_60_MULTISYNTH2_P3        60
#define SI5351_REG_177_PLL_RESET            177
#define SI5351_REG_183_CRYSTAL_LOAD         183

////////////// Configration ////////////////////////////////////////////////////
// PLLn
#define SI5351_PLL_A        0
#define SI5351_PLL_B        1
// CLKn
#define SI5351_CLK0         0
#define SI5351_CLK1         1
#define SI5351_CLK2         2
// REG_44_MULTISYNTH0, REG_52_MULTISYNTH1, REG_60_MULTISYNTH2
#define SI5351_R_DIV_1      (0<<4)
#define SI5351_R_DIV_2      (1<<4)
#define SI5351_R_DIV_4      (2<<4)
#define SI5351_R_DIV_8      (3<<4)
#define SI5351_R_DIV_16     (4<<4)
#define SI5351_R_DIV_32     (5<<4)
#define SI5351_R_DIV_64     (6<<4)
#define SI5351_R_DIV_128    (7<<4)
#define SI5351_DIVBY4       (3<<2)
// REG_16_CLK0_CONTROL, REG_17_CLK1_CONTROL, REG_18_CLK2_CONTROL
#define SI5351_CLK_POWERDOWN                (1<<7)
#define SI5351_CLK_INTEGER_MODE             (1<<6)
#define SI5351_CLK_PLL_SELECT_B             (1<<5)
#define SI5351_CLK_INVERT                   (1<<4)
#define SI5351_CLK_INPUT_MASK               (3<<2)
#define SI5351_CLK_INPUT_XTAL               (0<<2)
#define SI5351_CLK_INPUT_CLKIN              (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4     (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N       (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK      (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA       (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA       (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA       (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA       (3<<0)
// REG_177_PLL_RESET
#define SI5351_PLL_RESET_B          (1<<7)
#define SI5351_PLL_RESET_A          (1<<5)
// REG_183_CRYSTAL_LOAD
#define SI5351_CRYSTAL_LOAD_6PF     (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF     (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF    (3<<6)

// Frequency
#define FREQ_900MHZ     (900000000UL)
#define FREQ_600MHZ     (600000000UL)
#define FREQ_200MHZ     (200000000UL)
#define FREQ_150MHZ     (150000000UL)
#define FREQ_110MHZ     (110000000UL)
#define FREQ_1MHZ       (1000000UL)
#define FREQ_450KHZ     (450000UL)
#define FREQ_75KHZ      (75000UL)
#define FREQ_20KHZ      (20000UL)

typedef enum {  // Operating mode
  CLK_OUT_NOT_USED  = 0, CLK_OUT_FIXEDPLL, CLK_OUT_FIXEDDIV
} OperatingMode;

/** Silicon Laboratories Inc. Si5351A
 *
 * @code
 * #include "mbed.h"
 * #include "si5351a.h"
 *
 * I2C i2c(I2C_SDA, I2C_SCL);       // communication with Si5351A
 * SI5351 clk(i2c, 25000000UL);     // Base clock = 25MHz
 *
 * int main() {
 *   clk.set_frequency(SI5351_CLK0, 10000000);   // CLK0=10MHz
 *   while(true) {
 *      wait(1000);
 *   }
 * }
 *
 * // ---------  CAUTION & RESTRICTION -----------------------------------------
 * // 1) SETTING METHOD
 * // 2.6KHz~100MHz: fixed PLL(around 900 or around 600MHz), fractional divider
 * // 100~150MHz: fractional PLL 600-900MHz, fixed divider 6
 * // 150~200MHz: fractional PLL 600-900MHz, fixed divider 4
 * //
 * // 2) RESOURCE USAGE
 * // PLLA -> only for CLK0 (You can change freqency any time to any value.)
 * // PLLB -> use for bothe CLK1 & CLK2
 * // If you set a freq. less than 100MHz,
 * //    You can change both CLK1 & CLK2 independently.
 * // Over 100MHz, you may have a trouble becase need to change PLLB freq.
 * //
 * // 3) DISCONTINUITY
 * // If you use multiple output, you will lose output signal when you change
 * //   the output frequency even not specific CLKn during I2C acccess. 
 * // --------------------------------------------------------------------------
 *
 * @endcode
 */

class SI5351A
{
public:
    /** Configure data pin
      * @param data SDA and SCL pins
      * @param External base clock frequency
      * @param Internal capacitor value (10pF, 8pF & 6pF/ Default 8pF)
      * @param Output current drive strength(Default 2mA) same value CLK0,1,2
      */
    SI5351A(PinName p_sda, PinName p_scl,
            uint32_t base_clk_freq,
            uint8_t xtal_cap = SI5351_CRYSTAL_LOAD_8PF,
            uint8_t drive_current = SI5351_CLK_DRIVE_STRENGTH_2MA
    );

    /** Configure data pin (with other devices on I2C line)
      * @param I2C previous definition
      * @param External base clock frequency
      * @param Internal capacitor value (10pF, 8pF & 6pF/ Default 8pF)
      * @param Output current drive strength(Default 2mA) same value CLK0,1,2
      */
    SI5351A(I2C& p_i2c,
            uint32_t base_clk_freq,
            uint8_t xtal_cap = SI5351_CRYSTAL_LOAD_8PF,
            uint8_t drive_current = SI5351_CLK_DRIVE_STRENGTH_2MA
    );

    /** Set frequency
      * @param output channel CLK0=0, CLK1=1, CLK2=2
      * @param target frequency (unit = Hz)
      * @return output frequency
      */
    uint32_t set_frequency(uint8_t channel, uint32_t freq);

    /** shift frequency after setting frequency (Range: 750KHz to 100MHz )
      * @param desired channel CLK0=0, CLK1=1, CLK2=2
      * @param sift(+/-) frequency (unit = Hz)
      * @return output frequency
      */
    uint32_t shift_freq(uint8_t channel, int32_t diff);

    /** read frequency
      * @param select channel CLK0=0, CLK1=1, CLK2=2
      * @return output frequency
      */
    uint32_t read_freq(uint8_t channel);

    /** compensation frequency
      * @param Target freuency (setting value)
      * @param Measured freuency (actual value)
      * @return none
      */
    void f_compensation(uint32_t target_f, uint32_t measured_f);

    /** reset Si5351A all registers
      * @param none
      * @return none
      */
    void all_reset(void);

    //--------------- Debug interface ------------------------------------------

    /** debug / print registers
      * @param none
      * @return none (but print on console)
      */
    void debug_reg_print(void);

    /** debug / check registers and shows current configlation
      * @param none
      * @return none (but print on console)
      */
    void debug_current_config(void);

    /** debug / set CLK0: 120.00MHz, CLK1: 12.00MHz, CLK2: 13.56MHz
      *          as demonstration purpose for hardware check  (@25MHz Xtal)
      * @param none
      * @return none
      */
    void debug_example_clock(void);

protected:
    I2C *_i2c_p;
    I2C &_i2c;

    uint32_t gcd(uint32_t x, uint32_t y);
    void si5351_read(const uint8_t *buf);
    void si5351_write(uint8_t reg, uint8_t dat);
    void si5351_bulk_write(const uint8_t *buf, uint8_t len);
    double si5351_set_frequency_fixeddiv(
                        uint8_t     channel,
                        uint32_t    pll,
                        uint32_t    freq,
                        uint32_t    div);
    double si5351_set_frequency_fixedpll(
                        uint8_t     channel,
                        uint32_t    pll,
                        uint32_t    pllfreq,
                        uint32_t    freq,
                        uint8_t     factor);
    double si5351_setupMultisynth(
                        uint8_t     output,
                        uint8_t     pllSource,
                        uint32_t    div,
                        uint32_t    num,
                        uint32_t    denom,
                        uint8_t     factor);
    void si5351_setupPLL(
                        uint8_t     pll,
                        uint8_t     mult,
                        uint32_t    num,
                        uint32_t    denom);
    void si5351_set_PLL_input_condition(uint32_t freq);
    void si5351_reset_pll(void);
    void si5351_enable_output(void);
    void si5351_disable_output(void);
    void si5351_disable_all_output(void);
    void si5351_init(void);
    void put_dump(const uint8_t *buff, uint8_t ofs, uint8_t cnt);
    void prnt_reg(uint8_t offset, uint8_t n);
    void reg_16_17_18(uint8_t dt);
    void reg_pll_8bytes(uint8_t *buf);
    void reg_mltisyc_8bytes(uint8_t *buf);

private:
    uint8_t     addr;           // Chip I2C address
    uint32_t    base_freq;      // Xtal oscilation freq.
    uint8_t     x_cap;          // Internal capacitor value
    uint8_t     drv_current;    // Output current drive strength
    uint8_t     pll_n;          // PLL div number (PLL_N)
    uint32_t    pll_freq;       // XTAL * pll_n
    uint32_t    plla_freq;      // Calculated freq of PLLA
    uint32_t    pllb_freq;      // Calculated freq of PLLB
    double      compensation;   // Compensation data
    // Setting frequency
    double      clk0_freq;
    double      clk1_freq;
    double      clk2_freq;
    // operating mode
    uint8_t     clk0_state;
    uint8_t     clk1_state;
    uint8_t     clk2_state;

};   // class SI5351A

#endif  // MBED_SI5351A