initial si5351 unit test, not working yet
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 4 Jun 2016 22:46:05 +0000 (22:46 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 4 Jun 2016 22:46:05 +0000 (22:46 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@2814 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/stm32/Makefile
codec2-dev/stm32/inc/new_i2c.h [new file with mode: 0644]
codec2-dev/stm32/inc/si53xx.h [new file with mode: 0644]
codec2-dev/stm32/src/new_i2c.c [new file with mode: 0644]
codec2-dev/stm32/src/si5351_ut.c [new file with mode: 0644]
codec2-dev/stm32/src/si53xx.c [new file with mode: 0644]

index 9faa8e7cbf630db329c0e3e4a7d8e32e501b7767..098cd6cbf2aec8afe28f08619467e2b27784106d 100644 (file)
@@ -436,7 +436,7 @@ SRCS += src/startup_stm32f4xx.s src/init.c
 
 OBJS = $(SRCS:.c=.o)
 
-all: libstm32f4.a codec2_profile.bin fft_test.bin dac_ut.bin dac_play.bin adc_rec.bin pwm_ut.bin fdmdv_profile.bin sm1000_leds_switches_ut.bin sm1000.bin adcdac_ut.bin freedv_tx_profile.bin freedv_rx_profile.bin adc_sd.bin usb_vcp_ut.bin tuner_ut.bin fast_dac_ut.bin adc_sfdr_ut.bin adc_rec_usb.bin
+all: libstm32f4.a codec2_profile.bin fft_test.bin dac_ut.bin dac_play.bin adc_rec.bin pwm_ut.bin fdmdv_profile.bin sm1000_leds_switches_ut.bin sm1000.bin adcdac_ut.bin freedv_tx_profile.bin freedv_rx_profile.bin adc_sd.bin usb_vcp_ut.bin tuner_ut.bin fast_dac_ut.bin adc_sfdr_ut.bin adc_rec_usb.bin si5351_ut.bin
 
 # Rule for making directories automatically.
 # Note we don't use -p as it's a GNU extension.
@@ -790,7 +790,6 @@ adc_sfdr_ut.elf: $(ADC_SFDR_UT_SRCS:.c=.O3.o) src/stm32f4_adc_tuner.o \
        $(CC) $(CFLAGS) -O3 $^ -o $@ $(LIBPATHS) $(LIBS)
 
 
-
 FM_LODUC_PLAY_SRCS=\
 src/fm_loduc_play.c \
 gdb_stdio.c \
@@ -809,6 +808,19 @@ fm_loduc_play.elf: $(FM_LODUC_PLAY_SRCS) src/stm32f4_dacloduc.o
 
 # ---------------------------------------------------------------------------------
 
+SI5351_UT_SRCS=\
+src/si5351_ut.c \
+src/new_i2c.c \
+src/si53xx.c \
+src/system_stm32f4xx.c \
+src/startup_stm32f4xx.s \
+src/init.c \
+
+si5351_ut.elf: $(SI5351_UT_SRCS:.c=.O3.o) libstm32f4.a
+       $(CC) $(CFLAGS) -O3 $^ -o $@ $(LIBPATHS) $(LIBS)
+
+# ---------------------------------------------------------------------------------
+
 # Objects that require the peripheral library
 src/sm1000_main.o: $(PERIPHLIBDIR)/.unpack
 src/codec2_profile.o: $(PERIPHLIBDIR)/.unpack
diff --git a/codec2-dev/stm32/inc/new_i2c.h b/codec2-dev/stm32/inc/new_i2c.h
new file mode 100644 (file)
index 0000000..3649ae9
--- /dev/null
@@ -0,0 +1,107 @@
+/* 
+ * File:   new_i2c.h
+ * Author: leon (zs6lmg@gmail.com or leon@lrlabs.com)
+ *
+ * Created on March 17, 2016, 6:09 PM
+ * 
+ * GNU license apply.
+ * 
+ */
+
+/*
+  Copyright (C) 2016 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NEWI2C_H
+#define        NEWI2C_H
+
+#include <stdint.h>
+
+//
+// I2C1 device Enable
+//
+#define I2C_D1 0
+// I2C1 only PORTB
+// SCL - B6 or B8
+// SDA - B7 or B9
+#if I2C_D1
+#define I2C_DEVICE I2C1
+#define I2C_DX_P_SCK GPIOB
+#define I2C_DX_P_SDA GPIOB
+#define I2C_DX_SCK 6
+#define I2C_DX_SDA 9
+// could not get macro expansion in gcc to play with us
+#define I2C_DX_CLK_SCK RCC_AHB1Periph_GPIOB
+#define I2C_DX_CLK_SDA RCC_AHB1Periph_GPIOB
+#endif
+
+//
+// I2C2 device Enable
+//
+#define I2C_D2 0
+// I2C2 on PORTB, PORTF and PORTH
+// SCL - B10 F1 H4
+// SDA - B11 F0 H5
+#if I2C_D2
+#define I2C_DEVICE I2C2
+#define I2C_DX_P_SCK GPIOB
+#define I2C_DX_P_SDA GPIOB
+#define I2C_DX_SCK 10
+#define I2C_DX_SDA 11
+// could not get macro expansion in gcc to play with us
+#define I2C_DX_CLK_SCK RCC_AHB1Periph_GPIOB
+#define I2C_DX_CLK_SDA RCC_AHB1Periph_GPIOB
+#endif
+
+//
+// I2C3 device Enable
+//
+#define I2C_D3 1
+// I2C2 on PORTA and PORTH
+// SCL - A8 H7
+// SDA - C9 H8
+#if I2C_D3
+#define I2C_DEVICE I2C3
+#define I2C_DX_P_SCK GPIOA
+#define I2C_DX_P_SDA GPIOC
+#define I2C_DX_SCK 8
+#define I2C_DX_SDA 9
+// could not get macro expansion in gcc to play with us
+#define I2C_DX_CLK_SCK RCC_AHB1Periph_GPIOA
+#define I2C_DX_CLK_SDA RCC_AHB1Periph_GPIOC
+#endif
+
+
+
+
+#define I2C_SPEED                100000
+#define I2C_STIMEOUT             ((uint32_t)0x1000)
+#define I2C_LTIMEOUT             ((uint32_t)(300 * 0x1000))
+
+// software I2C model for testing
+// 
+#define I2Cmodel 1
+
+//void I2C_GPIO_Init(void);
+void I2C_Setup(void);
+
+uint32_t I2C_NewWriteRegister(uint8_t Addr,uint8_t Register,uint8_t Value);
+uint32_t I2C_NewWriteRegisterN(uint8_t Addr,uint8_t Register,uint8_t *Value,uint8_t N);
+uint32_t I2C_NewReadRegister(uint8_t Addr,uint8_t Register);
+uint32_t I2C_NewReadRegisterN(uint8_t Addr,uint8_t Register,uint8_t *buffer, uint8_t N);
+
+#endif /* NEWI2C_H */
+
diff --git a/codec2-dev/stm32/inc/si53xx.h b/codec2-dev/stm32/inc/si53xx.h
new file mode 100644 (file)
index 0000000..02cb4c4
--- /dev/null
@@ -0,0 +1,315 @@
+/*------------------------------------------------------------------------------
+ * 
+ * Ported to stm32F4xx non c++ by Leon Lessing leon@lrlabs.com or zs6lmg@gmail.com
+ * 
+ * 
+ * Copyright (C) 2015-2016 Jason Milldrum <milldrum@gmail.com>
+ *                         Dana H. Myers <k6jq@comcast.net>
+ *
+ * Many defines derived from clk-si5351.h in the Linux kernel.
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * do_div() macro derived from /include/asm-generic/div64.h in
+ * the Linux kernel.
+ * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * 
+ *------------------------------------------------------------------------------
+*/
+
+
+#ifndef SI53XX_H_
+#define SI53XX_H_
+
+#include <stdint.h>
+
+
+#define SI5351_BUS_BASE_ADDR                   0x60
+#define SI5351_XTAL_FREQ                       25000000
+#define SI5351_PLL_FIXED                       90000000000ULL
+#define SI5351_FREQ_MULT                       100ULL
+#define SI5351_DEFAULT_CLK                     1000000000ULL
+
+#define SI5351_PLL_VCO_MIN                     600000000
+#define SI5351_PLL_VCO_MAX                     900000000
+#define SI5351_MULTISYNTH_MIN_FREQ             1000000
+#define SI5351_MULTISYNTH_DIVBY4_FREQ          150000000
+#define SI5351_MULTISYNTH_MAX_FREQ             160000000
+#define SI5351_MULTISYNTH_SHARE_MAX            112500000
+#define SI5351_MULTISYNTH67_MAX_FREQ           SI5351_MULTISYNTH_DIVBY4_FREQ
+#define SI5351_CLKOUT_MIN_FREQ                 8000
+#define SI5351_CLKOUT_MAX_FREQ                 SI5351_MULTISYNTH_MAX_FREQ
+#define SI5351_CLKOUT67_MAX_FREQ               SI5351_MULTISYNTH67_MAX_FREQ
+
+#define SI5351_PLL_A_MIN                       15
+#define SI5351_PLL_A_MAX                       90
+#define SI5351_PLL_B_MAX                       (SI5351_PLL_C_MAX-1)
+#define SI5351_PLL_C_MAX                       1048575
+#define SI5351_MULTISYNTH_A_MIN                        6
+#define SI5351_MULTISYNTH_A_MAX                        1800
+#define SI5351_MULTISYNTH67_A_MAX              254
+#define SI5351_MULTISYNTH_B_MAX                        (SI5351_MULTISYNTH_C_MAX-1)
+#define SI5351_MULTISYNTH_C_MAX                        1048575
+#define SI5351_MULTISYNTH_P1_MAX               ((1<<18)-1)
+#define SI5351_MULTISYNTH_P2_MAX               ((1<<20)-1)
+#define SI5351_MULTISYNTH_P3_MAX               ((1<<20)-1)
+
+#define SI5351_DEVICE_STATUS                   0
+#define SI5351_INTERRUPT_STATUS                        1
+#define SI5351_INTERRUPT_MASK                  2
+#define SI5351_STATUS_SYS_INIT                 (1<<7)
+#define SI5351_STATUS_LOL_B                    (1<<6)
+#define SI5351_STATUS_LOL_A                    (1<<5)
+#define SI5351_STATUS_LOS                      (1<<4)
+#define SI5351_OUTPUT_ENABLE_CTRL              3
+#define SI5351_OEB_PIN_ENABLE_CTRL             9
+#define SI5351_PLL_INPUT_SOURCE                        15
+#define SI5351_CLKIN_DIV_MASK                  (3<<6)
+#define SI5351_CLKIN_DIV_1                     (0<<6)
+#define SI5351_CLKIN_DIV_2                     (1<<6)
+#define SI5351_CLKIN_DIV_4                     (2<<6)
+#define SI5351_CLKIN_DIV_8                     (3<<6)
+#define SI5351_PLLB_SOURCE                     (1<<3)
+#define SI5351_PLLA_SOURCE                     (1<<2)
+
+#define SI5351_CLK0_CTRL                       16
+#define SI5351_CLK1_CTRL                       17
+#define SI5351_CLK2_CTRL                       18
+#define SI5351_CLK3_CTRL                       19
+#define SI5351_CLK4_CTRL                       20
+#define SI5351_CLK5_CTRL                       21
+#define SI5351_CLK6_CTRL                       22
+#define SI5351_CLK7_CTRL                       23
+#define SI5351_CLK_POWERDOWN                   (1<<7)
+#define SI5351_CLK_INTEGER_MODE                        (1<<6)
+#define SI5351_CLK_PLL_SELECT                  (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)
+
+#define SI5351_CLK3_0_DISABLE_STATE            24
+#define SI5351_CLK7_4_DISABLE_STATE            25
+#define SI5351_CLK_DISABLE_STATE_MASK          3
+#define SI5351_CLK_DISABLE_STATE_LOW           0
+#define SI5351_CLK_DISABLE_STATE_HIGH          1
+#define SI5351_CLK_DISABLE_STATE_FLOAT         2
+#define SI5351_CLK_DISABLE_STATE_NEVER         3
+
+#define SI5351_PARAMETERS_LENGTH               8
+#define SI5351_PLLA_PARAMETERS                 26
+#define SI5351_PLLB_PARAMETERS                 34
+#define SI5351_CLK0_PARAMETERS                 42
+#define SI5351_CLK1_PARAMETERS                 50
+#define SI5351_CLK2_PARAMETERS                 58
+#define SI5351_CLK3_PARAMETERS                 66
+#define SI5351_CLK4_PARAMETERS                 74
+#define SI5351_CLK5_PARAMETERS                 82
+#define SI5351_CLK6_PARAMETERS                 90
+#define SI5351_CLK7_PARAMETERS                 91
+#define SI5351_CLK6_7_OUTPUT_DIVIDER           92
+#define SI5351_OUTPUT_CLK_DIV_MASK             (7 << 4)
+#define SI5351_OUTPUT_CLK6_DIV_MASK            (7 << 0)
+#define SI5351_OUTPUT_CLK_DIV_SHIFT            4
+#define SI5351_OUTPUT_CLK_DIV6_SHIFT           0
+#define SI5351_OUTPUT_CLK_DIV_1                        0
+#define SI5351_OUTPUT_CLK_DIV_2                        1
+#define SI5351_OUTPUT_CLK_DIV_4                        2
+#define SI5351_OUTPUT_CLK_DIV_8                        3
+#define SI5351_OUTPUT_CLK_DIV_16               4
+#define SI5351_OUTPUT_CLK_DIV_32               5
+#define SI5351_OUTPUT_CLK_DIV_64               46
+#define SI5351_OUTPUT_CLK_DIV_128              7
+#define SI5351_OUTPUT_CLK_DIVBY4               (3<<2)
+
+#define SI5351_SSC_PARAM0                      149
+#define SI5351_SSC_PARAM1                      150
+#define SI5351_SSC_PARAM2                      151
+#define SI5351_SSC_PARAM3                      152
+#define SI5351_SSC_PARAM4                      153
+#define SI5351_SSC_PARAM5                      154
+#define SI5351_SSC_PARAM6                      155
+#define SI5351_SSC_PARAM7                      156
+#define SI5351_SSC_PARAM8                      157
+#define SI5351_SSC_PARAM9                      158
+#define SI5351_SSC_PARAM10                     159
+#define SI5351_SSC_PARAM11                     160
+#define SI5351_SSC_PARAM12                     161
+
+#define SI5351_VXCO_PARAMETERS_LOW             162
+#define SI5351_VXCO_PARAMETERS_MID              163
+#define SI5351_VXCO_PARAMETERS_HIGH             164
+
+#define SI5351_CLK0_PHASE_OFFSET               165
+#define SI5351_CLK1_PHASE_OFFSET               166
+#define SI5351_CLK2_PHASE_OFFSET               167
+#define SI5351_CLK3_PHASE_OFFSET               168
+#define SI5351_CLK4_PHASE_OFFSET               169
+#define SI5351_CLK5_PHASE_OFFSET               170
+
+#define SI5351_PLL_RESET                       177
+#define SI5351_PLL_RESET_B                     (1<<7)
+#define SI5351_PLL_RESET_A                     (1<<5)
+
+#define SI5351_CRYSTAL_LOAD                    183
+#define SI5351_CRYSTAL_LOAD_MASK               (3<<6)
+#define SI5351_CRYSTAL_LOAD_0PF                 (0<<6) 
+#define SI5351_CRYSTAL_LOAD_6PF                 (1<<6)
+#define SI5351_CRYSTAL_LOAD_8PF                (2<<6)
+#define SI5351_CRYSTAL_LOAD_10PF               (3<<6)
+
+#define SI5351_FANOUT_ENABLE                   187
+#define SI5351_CLKIN_ENABLE                    (1<<7)
+#define SI5351_XTAL_ENABLE                     (1<<6)
+#define SI5351_MULTISYNTH_ENABLE               (1<<4)
+
+/* Macro definitions */
+
+#define RFRAC_DENOM ((1L << 20) - 1)
+
+/*
+ * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
+ *
+ * The semantics of do_div() are:
+ *
+ * uint32_t do_div(uint64_t *n, uint32_t base)
+ * {
+ *      uint32_t remainder = *n % base;
+ *      *n = *n / base;
+ *      return remainder;
+ * }
+ *
+ * NOTE: macro parameter n is evaluated multiple times,
+ *       beware of side effects!
+ */
+
+# define do_div(n,base) ({                                      \
+        uint64_t __base = (base);                               \
+        uint64_t __rem;                                         \
+        __rem = ((uint64_t)(n)) % __base;                       \
+        (n) = ((uint64_t)(n)) / __base;                         \
+        __rem;                                                  \
+ })
+
+/* Enum definitions */
+
+/*
+ * enum si5351_variant - SiLabs Si5351 chip variant
+ * @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
+ * @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
+ * @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
+ * @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
+ */
+ enum si5351_variant {SI5351_VARIANT_A, SI5351_VARIANT_A3,     SI5351_VARIANT_B,
+   SI5351_VARIANT_C};
+
+ enum si5351_clock {SI5351_CLK0, SI5351_CLK1, SI5351_CLK2, SI5351_CLK3,
+       SI5351_CLK4, SI5351_CLK5, SI5351_CLK6, SI5351_CLK7, SI5351_CLKNONE};
+
+ enum si5351_pll {SI5351_PLLA, SI5351_PLLB};
+
+ enum si5351_drive {SI5351_DRIVE_2MA, SI5351_DRIVE_4MA, SI5351_DRIVE_6MA,
+   SI5351_DRIVE_8MA};
+
+ enum si5351_clock_source {SI5351_CLK_SRC_XTAL, SI5351_CLK_SRC_CLKIN,
+   SI5351_CLK_SRC_MS0, SI5351_CLK_SRC_MS};
+
+ enum si5351_clock_disable {SI5351_CLK_DISABLE_LOW, SI5351_CLK_DISABLE_HIGH,
+   SI5351_CLK_DISABLE_HI_Z, SI5351_CLK_DISABLE_NEVER};
+
+ enum si5351_clock_fanout {SI5351_FANOUT_CLKIN, SI5351_FANOUT_XO,
+   SI5351_FANOUT_MS};
+
+ enum si5351_pll_input{SI5351_PLL_INPUT_XO, SI5351_PLL_INPUT_CLKIN};
+
+/* Struct definitions */
+
+struct Si5351RegSet {
+    uint32_t p1;
+    uint32_t p2;
+    uint32_t p3;
+};
+
+struct Si5351Status {
+    uint8_t SYS_INIT;
+    uint8_t LOL_B;
+    uint8_t LOL_A;
+    uint8_t LOS;
+    uint8_t REVID;
+};
+
+struct Si5351IntStatus {
+    uint8_t SYS_INIT_STKY;
+    uint8_t LOL_B_STKY;
+    uint8_t LOL_A_STKY;
+    uint8_t LOS_STKY;
+};
+
+typedef struct {
+    struct Si5351Status dev_status;
+    struct Si5351IntStatus dev_int_status;
+    uint64_t plla_freq;
+    uint64_t pllb_freq;
+    uint64_t clk0_freq;
+    uint64_t clk1_freq;
+    uint64_t clk2_freq;
+    uint8_t clk0_int_mode, clk1_int_mode, clk2_int_mode;
+    int32_t ref_correction;
+    uint8_t lock_plla, lock_pllb;
+    uint32_t xtal_freq;
+    uint32_t I2C_ErrorCode;
+    uint8_t I2C_add;
+} T_Si5351_data;
+
+
+
+T_Si5351_data   Si5351_Config;
+
+/* private routines */
+
+
+void si5351_write(uint8_t REGaddr, uint8_t data);
+void si5351_write_bulk(uint8_t REGaddr, uint8_t bytes, uint8_t *data);
+uint8_t si5351_read(uint8_t REGaddr);
+uint64_t si5351_multisynth_calc(uint64_t freq, uint64_t pll_freq, struct Si5351RegSet *reg);
+void si5351_set_ms_source(enum si5351_clock clk, enum si5351_pll pll);
+void si5351_set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4);
+void si5351_set_pll(uint64_t pll_freq, enum si5351_pll target_pll);
+void si5351_ms_div(enum si5351_clock clk, uint8_t r_div, uint8_t div_by_4);
+void si5351_set_int(enum si5351_clock clk, uint8_t enable);
+uint64_t si5351_pll_calc(uint64_t freq, struct Si5351RegSet *reg, int32_t correction);
+uint8_t si5351_select_r_div(uint64_t *freq);
+
+
+/* public routines */
+
+
+
+void si5351_init(uint8_t I2C_Address,  uint8_t xtal_load_c, uint32_t ref_osc_freq);
+void si5351_pll_reset(enum si5351_pll target_pll);
+void si5351_set_clock_pwr(enum si5351_clock clk, uint8_t pwr);
+void si5351_set_clock_invert(enum si5351_clock clk, uint8_t inv);
+uint8_t si5351_set_freq(uint64_t freq, uint64_t pll_freq, enum si5351_clock clk);
+
+#endif
diff --git a/codec2-dev/stm32/src/new_i2c.c b/codec2-dev/stm32/src/new_i2c.c
new file mode 100644 (file)
index 0000000..0f678f4
--- /dev/null
@@ -0,0 +1,430 @@
+/* 
+ * File:   new_i2c.h
+ * Author: leon (zs6lmg@gmail.com or leon@lrlabs.com)
+ *
+ * Created on March 17, 2016, 6:09 PM
+ * 
+ * GNU license apply.
+ * 
+ */
+
+
+/*
+  Copyright (C) 2016 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stm32f4xx.h"
+#include "stm32f4xx_conf.h"
+#include "new_i2c.h"
+//#include "cds_i2s.h"
+
+//
+//
+//
+#define CONCAT_(x,y) x##y
+#define CONCAT(x,y) CONCAT_(x,y)
+//
+//
+//
+
+//
+//
+//
+//
+#define I2C_SDA_AFPIN_ID CONCAT(GPIO_PinSource,I2C_DX_SDA)
+#define I2C_SCK_AFPIN_ID CONCAT(GPIO_PinSource,I2C_DX_SCK)
+#define I2C_SDA_PIN_ID CONCAT(GPIO_Pin_,I2C_DX_SDA)
+#define I2C_SCK_PIN_ID CONCAT(GPIO_Pin_,I2C_DX_SCK)
+
+
+/* definition to expand macro then apply to pragma message */
+// Verify that my shortcuts are actually working
+//#define VALUE_TO_STRING(x) #x
+//#define VALUE(x) VALUE_TO_STRING(x)
+//
+//#define VAR_NAME_VALUE(var) #var "="  VALUE(var)
+//#pragma message(VAR_NAME_VALUE(I2C_SCK_AFPIN_ID))
+//#pragma message(VAR_NAME_VALUE(I2C_SDA_AFPIN_ID))
+
+/*
+ * @brief Setup I2C interface pins
+ * PB6 is SCL
+ * PB9 is SDA
+ * 
+ * @param       None
+ * 
+ * @return      None
+ */
+void I2C_Setup(void) {
+    I2C_InitTypeDef I2C_Init_S;
+    GPIO_InitTypeDef GPIO_Init_P;
+    // setup the clock for the GPIO device
+    RCC_AHB1PeriphClockCmd(I2C_DX_CLK_SCK|I2C_DX_CLK_SDA, ENABLE);
+    // enable clock for the i2c device
+#if I2C_D1
+    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
+    // Performing a Reset
+    //RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+    //RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
+    //RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
+#elif I2C_D3
+    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C3, ENABLE);
+#else
+#error "Problem on I2C"
+#endif
+    // setup the pins
+    GPIO_Init_P.GPIO_Pin=I2C_SDA_PIN_ID;
+    GPIO_Init_P.GPIO_Mode=GPIO_Mode_AF;
+    GPIO_Init_P.GPIO_Speed=GPIO_Speed_50MHz;
+    GPIO_Init_P.GPIO_OType=GPIO_OType_OD;
+    GPIO_Init_P.GPIO_PuPd=GPIO_PuPd_NOPULL;
+    GPIO_Init(I2C_DX_P_SDA, &GPIO_Init_P);
+    //
+    //
+    GPIO_Init_P.GPIO_Pin=I2C_SCK_PIN_ID;
+    GPIO_Init(I2C_DX_P_SCK, &GPIO_Init_P);
+    // assign alternate functions
+#if I2C_D1
+    GPIO_PinAFConfig(I2C_DX_P_SDA, I2C_SDA_AFPIN_ID, GPIO_AF_I2C1);
+    GPIO_PinAFConfig(I2C_DX_P_SCK, I2C_SCK_AFPIN_ID, GPIO_AF_I2C1);
+#elif I2C_D3
+    GPIO_PinAFConfig(I2C_DX_P_SDA, I2C_SDA_AFPIN_ID, GPIO_AF_I2C3);
+    GPIO_PinAFConfig(I2C_DX_P_SCK, I2C_SCK_AFPIN_ID, GPIO_AF_I2C3);
+#else
+#error "Device not defined"
+#endif
+    //
+    I2C_Cmd(I2C_DEVICE, DISABLE);
+    I2C_DeInit(I2C_DEVICE);
+    //
+    //
+    I2C_Init_S.I2C_Mode=I2C_Mode_I2C;
+    I2C_Init_S.I2C_DutyCycle=I2C_DutyCycle_2;
+    I2C_Init_S.I2C_Ack=I2C_Ack_Enable;
+    I2C_Init_S.I2C_OwnAddress1=0x00;
+    I2C_Init_S.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
+    I2C_Init_S.I2C_ClockSpeed=I2C_SPEED;
+    I2C_Cmd(I2C_DEVICE, ENABLE);
+    I2C_Init(I2C_DEVICE, &I2C_Init_S);
+}
+
+
+
+uint32_t I2C_timeout(uint32_t retval) {
+    I2C_GenerateSTOP(I2C_DEVICE, ENABLE);
+    I2C_SoftwareResetCmd(I2C_DEVICE, ENABLE);
+    I2C_SoftwareResetCmd(I2C_DEVICE, DISABLE);
+    I2C_DeInit(I2C_DEVICE);
+    I2C_Setup();
+    return(retval);
+}
+
+
+
+/**
+ * @brief  Writes a Byte to a given register through the control interface (I2C)
+ * @param  Addr: I2C address to write to.
+ * @param  Register: Register inside device to write to.
+ * @param  Value: the Byte value to be written into destination register.
+ * @return 0 all ok.
+ *  0x101 i2c busy.
+ *  0x102 master mode not selected.
+ *  0x103 master transmitter mode.
+ *  0x104 send data failed.
+ *  0x105 no reply from device.
+ */
+uint32_t I2C_NewWriteRegister(uint8_t Addr, uint8_t Register, uint8_t Value) {
+    uint32_t result = 0;
+
+    /* check if bus is busy */
+    __IO uint32_t Timeout = I2C_LTIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_BUSY )) {
+        if ((Timeout--) == 0) return 0x101;
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_MODE_SELECT)) {
+        if ((Timeout--) == 0) return 0x102;
+    }
+    I2C_Send7bitAddress(I2C_DEVICE,Addr,I2C_Direction_Transmitter);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
+        if ((Timeout--) == 0) return 0x103;
+    }
+    I2C_SendData(I2C_DEVICE, Register);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_BYTE_TRANSMITTING)) {
+        if ((Timeout--) == 0) return 0x104;
+    }
+    I2C_SendData(I2C_DEVICE, Value);
+    // Wait for reply
+    Timeout = I2C_LTIMEOUT;
+    while (!I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_BTF)) {
+        if ((Timeout--) == 0) return 0x105;
+    }
+    I2C_GenerateSTOP(I2C_DEVICE, ENABLE);
+    return result;
+}
+
+/**
+ * @brief  Writes more than 1 bytes to a given register through the control interface (I2C)
+ * @param  Addr: I2C address to write to.
+ * @param  Register: Register inside device to write to.
+ * @param  Value: the Byte(s) to be written into destination register.
+ * @param  N: Number of byte(s) to write
+ * @return 0 all ok.
+ *  0x101 i2c busy.
+ *  0x102 master mode not selected.
+ *  0x103 master transmitter mode.
+ *  0x104 send data failed.
+ *  0x105 no reply from device.
+ */
+uint32_t I2C_NewWriteRegisterN(uint8_t Addr, uint8_t Register, uint8_t *Value, uint8_t N) {
+    uint32_t result = 0;
+
+    /* check if bus is busy */
+    __IO uint8_t i;
+    __IO uint32_t Timeout = I2C_LTIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_BUSY )) {
+        if ((Timeout--) == 0) return 0x101;
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_MODE_SELECT)) {
+        if ((Timeout--) == 0) return 0x102;
+    }
+    I2C_Send7bitAddress(I2C_DEVICE,Addr,I2C_Direction_Transmitter);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
+        if ((Timeout--) == 0) return 0x103;
+    }
+    I2C_SendData(I2C_DEVICE, Register);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_BYTE_TRANSMITTING)) {
+        if ((Timeout--) == 0) return 0x104;
+    }
+    for (i = 0; i < N; i++) {
+        I2C_SendData(I2C_DEVICE, *(Value++));
+        // Wait for reply
+        Timeout = I2C_LTIMEOUT;
+        while (!I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_BTF)) {
+            if ((Timeout--) == 0) return 0x105;
+        }
+    }
+    I2C_GenerateSTOP(I2C_DEVICE, ENABLE);
+    return result;
+}
+
+
+
+/**
+ * @brief  Reads a Byte to a given register via I2C
+ * @param  Addr: I2C address.
+ * @param  Register: Register inside device to read.
+ * @return value read.
+ *  0x101 i2c busy.
+ *  0x102 master mode not selected.
+ *  0x103 master transmitter mode.
+ *  0x104 send data failed.
+ *  0x105 no reply from device.
+ *  0x106 direction receive.
+ *  0x107 data register not empty.
+ *  0x108 waiting data.
+ */
+uint32_t I2C_NewReadRegister(uint8_t Addr,uint8_t Register) {
+    uint32_t result = 0;
+    __IO uint32_t Timeout = I2C_LTIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE,I2C_FLAG_BUSY)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x101);
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+#if (I2Cmodel == 1)
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C_DEVICE,I2C_FLAG_SB)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x102);
+    }
+    I2C_AcknowledgeConfig(I2C_DEVICE, DISABLE);
+    I2C_Send7bitAddress(I2C_DEVICE, Addr, I2C_Direction_Transmitter);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_ADDR)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x103);
+    }
+    (void) I2C_DEVICE->SR2;
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x104);
+    }
+    I2C_SendData(I2C_DEVICE, Register);
+    Timeout = I2C_STIMEOUT;
+    while ( (!I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_TXE)) ||
+            (!I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF))
+          )  {
+        if ((Timeout--) == 0) return I2C_timeout(0x105);
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_SB)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x106);
+    }
+    I2C_Send7bitAddress(I2C_DEVICE, Addr, I2C_Direction_Receiver);
+    Timeout = I2C_STIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_ADDR) == RESET) {
+        if ((Timeout--) == 0) return I2C_timeout(0x107);
+    }
+    (void) I2C_DEVICE->SR2;
+    while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x108);
+    }
+    I2C_GenerateSTOP(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    result = I2C_ReceiveData(I2C_DEVICE );
+    Timeout = I2C_STIMEOUT;
+    while (I2C_DEVICE ->CR1 & I2C_CR1_STOP ) {
+        if ((Timeout--) == 0) return I2C_timeout(0x109);
+    }
+    I2C_AcknowledgeConfig(I2C_DEVICE, ENABLE);
+    I2C_ClearFlag(I2C_DEVICE, I2C_FLAG_AF );
+#else
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_MODE_SELECT)) {
+        if ((Timeout--) == 0) return 0x102;
+    }
+    I2C_Send7bitAddress(I2C_DEVICE,Addr,I2C_Direction_Transmitter);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
+        if ((Timeout--) == 0) return 0x103;
+    }
+    I2C_SendData(I2C_DEVICE, Register);
+    Timeout = I2C_STIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE,I2C_FLAG_BTF) == RESET) {
+            if ((Timeout--) == 0) return 0x104;
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_MODE_SELECT)) {
+        if ((Timeout--) == 0) return 0x105;
+    }
+    I2C_Send7bitAddress(I2C_DEVICE,Addr,I2C_Direction_Receiver);
+    Timeout = I2C_STIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_ADDR) == RESET) {
+        if ((Timeout--) == 0) return 0x106;
+    }
+    I2C_AcknowledgeConfig(I2C_DEVICE,DISABLE);
+    (void) I2C_DEVICE->SR2;
+    I2C_GenerateSTOP(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE,I2C_FLAG_RXNE) == RESET) {
+        if ((Timeout--) == 0) return 0x107;
+    }
+    result = I2C_ReceiveData(I2C_DEVICE );
+    Timeout = I2C_STIMEOUT;
+    while (I2C_DEVICE ->CR1 & I2C_CR1_STOP ) {
+        if ((Timeout--) == 0) return 0x108;
+    }
+    I2C_AcknowledgeConfig(I2C_DEVICE, ENABLE);
+    I2C_ClearFlag(I2C_DEVICE, I2C_FLAG_AF );
+#endif
+    return result;
+}
+
+/**
+ * @brief  Reads more than one byte from a given register via I2C
+ * @param  Addr: I2C address.
+ * @param  Register: Register inside device to read.
+ * @return value read.
+ *  0x101 i2c busy.
+ *  0x102 master mode not selected.
+ *  0x103 master transmitter mode.
+ *  0x104 send data failed.
+ *  0x105 no reply from device.
+ *  0x106 direction receive.
+ *  0x107 data register not empty.
+ *  0x108 waiting data.
+ */
+uint32_t I2C_NewReadRegisterN(uint8_t Addr,uint8_t Register,uint8_t *buffer, uint8_t N) {
+    uint32_t result = 0;
+    uint8_t     cnt;
+    __IO uint32_t Timeout = I2C_LTIMEOUT;
+    if ( (N==0)||(N>64) ) return 0x109;
+    while (I2C_GetFlagStatus(I2C_DEVICE,I2C_FLAG_BUSY)) {
+        if ((Timeout--) == 0) return 0x101;
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_MODE_SELECT)) {
+        if ((Timeout--) == 0) return 0x102;
+    }
+    if(N==1) {
+        // ACK disable
+        I2C_AcknowledgeConfig(I2C_DEVICE, DISABLE);
+    } else {
+        // ACK enable
+        I2C_AcknowledgeConfig(I2C_DEVICE, ENABLE);
+    }
+    I2C_Send7bitAddress(I2C_DEVICE,Addr,I2C_Direction_Transmitter);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_CheckEvent(I2C_DEVICE,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
+        if ((Timeout--) == 0) return 0x103;
+    }
+    (void) I2C_DEVICE->SR2;
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x104);
+    }
+    I2C_SendData(I2C_DEVICE, Register);
+    Timeout = I2C_STIMEOUT;
+    while ( (!I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_TXE)) ||
+            (!I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF))
+          )  {
+        if ((Timeout--) == 0) return I2C_timeout(0x105);
+    }
+    I2C_GenerateSTART(I2C_DEVICE, ENABLE);
+    Timeout = I2C_STIMEOUT;
+    while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_SB)) {
+        if ((Timeout--) == 0) return I2C_timeout(0x106);
+    }
+    I2C_Send7bitAddress(I2C_DEVICE, Addr, I2C_Direction_Receiver);
+    Timeout = I2C_STIMEOUT;
+    while (I2C_GetFlagStatus(I2C_DEVICE, I2C_FLAG_ADDR) == RESET) {
+        if ((Timeout--) == 0) return I2C_timeout(0x107);
+    }
+    (void) I2C_DEVICE->SR2;
+    for (cnt=0; cnt<N; cnt++) {
+        if((cnt+1) >= N) {
+            // ACK disable
+            I2C_AcknowledgeConfig(I2C1, DISABLE);
+            // Stop-Sequenz
+            I2C_GenerateSTOP(I2C1, ENABLE);
+        }
+        Timeout = I2C_STIMEOUT;
+        while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE)) {
+            if ((Timeout--) == 0) return I2C_timeout(0x108);
+        }
+        result=I2C_ReceiveData(I2C_DEVICE);
+        Timeout = I2C_STIMEOUT;
+        while (I2C_DEVICE ->CR1 & I2C_CR1_STOP ) {
+            if ((Timeout--) == 0) return I2C_timeout(0x109);
+        }
+        *buffer=(uint8_t)(result & 0xff);
+        buffer++;
+        Timeout = I2C_STIMEOUT;
+    }
+    I2C_AcknowledgeConfig(I2C_DEVICE, ENABLE);
+    //I2C_ClearFlag(I2C_DEVICE, I2C_FLAG_AF );
+    if (result<0x100) result=0;
+    return result;
+}
+
diff --git a/codec2-dev/stm32/src/si5351_ut.c b/codec2-dev/stm32/src/si5351_ut.c
new file mode 100644 (file)
index 0000000..0b5187d
--- /dev/null
@@ -0,0 +1,38 @@
+/*---------------------------------------------------------------------------*\
+
+  FILE........: si5351_ut.c
+  AUTHOR......: David Rowe
+  DATE CREATED: June 2016
+
+  Generates a 10MHz signal on CLK0 ouput of Si5351, should be visible in 
+  attenuated form on SP7/SP7 of SM2000.
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2016 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include "new_i2c.h"
+#include "si53xx.h"
+
+int main(void) {
+    I2C_Setup();
+    si5351_init(0x60, 5, 25000000);
+    si5351_set_freq(10000000, 0, SI5351_CLK0);
+    while(1);
+}
diff --git a/codec2-dev/stm32/src/si53xx.c b/codec2-dev/stm32/src/si53xx.c
new file mode 100644 (file)
index 0000000..79e634b
--- /dev/null
@@ -0,0 +1,826 @@
+/*------------------------------------------------------------------------------
+ * 
+ * Ported to stm32F4xx non c++ by Leon Lessing leon@lrlabs.com or zs6lmg@gmail.com
+ * 
+ * 
+ * Copyright (C) 2015-2016 Jason Milldrum <milldrum@gmail.com>
+ *                         Dana H. Myers <k6jq@comcast.net>
+ *
+ * Many defines derived from clk-si5351.h in the Linux kernel.
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * do_div() macro derived from /include/asm-generic/div64.h in
+ * the Linux kernel.
+ * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * 
+ *------------------------------------------------------------------------------
+*/
+
+#include "stm32f4xx.h"
+#include "stm32f4xx_conf.h"
+//#include "system.h"
+
+#include "new_i2c.h"
+//#include "inc/xprintf.h"
+#include "stdlib.h"
+#include "string.h"
+#include "math.h"
+#include "si53xx.h"
+
+
+void si5351_write(uint8_t REGaddr, uint8_t data) {
+    // ignoring errors
+    // Waiting for the bite
+    Si5351_Config.I2C_ErrorCode=I2C_NewWriteRegister(Si5351_Config.I2C_add, REGaddr, data);
+}
+
+void si5351_write_bulk(uint8_t REGaddr, uint8_t bytes, uint8_t *data) {
+    Si5351_Config.I2C_ErrorCode=I2C_NewWriteRegisterN(Si5351_Config.I2C_add, REGaddr, data, bytes);
+}
+
+
+uint8_t si5351_read(uint8_t REGaddr) {
+    uint8_t reg_val;
+    Si5351_Config.I2C_ErrorCode=I2C_NewReadRegister(Si5351_Config.I2C_add,REGaddr);
+    if (Si5351_Config.I2C_ErrorCode>0xff) {
+        reg_val=0;
+    } else {
+        reg_val=(uint8_t)(Si5351_Config.I2C_ErrorCode & 0xff);
+        Si5351_Config.I2C_ErrorCode=0;
+    }
+    return reg_val;
+}
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_init(uint8_t xtal_load_c, uint32_t ref_osc_freq)
+ * Setup communications to the Si5351 and set the crystal
+ * load capacitance.
+ *
+ * @param I2C_Address - enter I2C address here, use 0 to use .h file defined value
+ * 
+ * @param xtal_load_c - Crystal load capacitance. Use the SI5351_CRYSTAL_LOAD_*PF
+ * defines in the header file
+ * 
+ * @param ref_osc_freq - Crystal/reference oscillator frequency in 1 Hz increments.
+ * Defaults to 25000000 if a 0 is used here.
+ * 
+ *------------------------------------------------------------------------------
+ */
+void si5351_init(uint8_t I2C_Address, uint8_t xtal_load_c, uint32_t ref_osc_freq) {
+    Si5351_Config.clk0_freq=0;
+    Si5351_Config.lock_plla = SI5351_CLKNONE;
+    Si5351_Config.lock_pllb = SI5351_CLKNONE;
+    Si5351_Config.clk0_int_mode = 0;
+    Si5351_Config.clk1_int_mode = 0;
+    Si5351_Config.clk2_int_mode = 0;
+    Si5351_Config.plla_freq = 0;
+    Si5351_Config.pllb_freq = 0;
+    Si5351_Config.clk0_freq = 0;
+    Si5351_Config.clk1_freq = 0;
+    Si5351_Config.clk2_freq = 0;
+    Si5351_Config.xtal_freq = SI5351_XTAL_FREQ;
+    Si5351_Config.I2C_add = SI5351_BUS_BASE_ADDR << 1;
+    if (I2C_Address != 0) Si5351_Config.I2C_add=I2C_Address << 1;
+    // Set crystal load capacitance
+    uint8_t reg_val = 0x12; // 0b010010 reserved value bits
+    reg_val |= xtal_load_c;
+    si5351_write(SI5351_CRYSTAL_LOAD, reg_val);
+    // Change the ref osc freq if different from default
+    // Divide down if greater than 30 MHz
+    if (ref_osc_freq != 0) {
+        uint8_t reg_val;
+        reg_val = si5351_read(SI5351_PLL_INPUT_SOURCE);
+        //
+        // Clear the bits first
+        reg_val &= ~(SI5351_CLKIN_DIV_MASK);
+        if(ref_osc_freq <= 30000000) {
+            Si5351_Config.xtal_freq = ref_osc_freq;
+            reg_val |= SI5351_CLKIN_DIV_1;
+        } else if(ref_osc_freq > 30000000 && ref_osc_freq <= 60000000) {
+            Si5351_Config.xtal_freq = ref_osc_freq / 2;
+            reg_val |= SI5351_CLKIN_DIV_2;
+        } else if(ref_osc_freq > 60000000 && ref_osc_freq <= 100000000) {
+            Si5351_Config.xtal_freq = ref_osc_freq / 4;
+            reg_val |= SI5351_CLKIN_DIV_4;
+        }
+        si5351_write(SI5351_PLL_INPUT_SOURCE, reg_val);
+    }
+    // Initialize the CLK outputs according to flowchart in datasheet
+    // First, turn them off
+    si5351_write(16, 0x80);
+    si5351_write(17, 0x80);
+    si5351_write(18, 0x80);
+
+    // Turn the clocks back on...
+    si5351_write(16, 0x0c);
+    si5351_write(17, 0x0c);
+    si5351_write(18, 0x0c);
+
+    // Then reset the PLLs
+    si5351_pll_reset(SI5351_PLLA);
+    si5351_pll_reset(SI5351_PLLB);
+}
+
+
+
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_set_freq(uint64_t freq, uint64_t pll_freq, enum si5351_clock output)
+ * Sets the clock frequency of the specified CLK output
+ *
+ * @param freq - Output frequency in Hz
+ * 
+ * @param pll_freq - Frequency of the PLL driving the Multisynth
+ *   Use a 0 to have the function choose a PLL frequency
+ * 
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ *------------------------------------------------------------------------------
+ */
+uint8_t si5351_set_freq(uint64_t freq, uint64_t pll_freq, enum si5351_clock clk) {
+    struct Si5351RegSet ms_reg;
+    enum si5351_pll target_pll;
+    uint8_t write_pll = 0;
+    uint8_t r_div = SI5351_OUTPUT_CLK_DIV_1;
+    uint8_t int_mode = 0;
+    uint8_t div_by_4 = 0;
+
+    // PLL bounds checking
+    if(pll_freq != 0) {
+        if ((pll_freq < SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT)
+                || (pll_freq > SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT)) {
+            return 1;
+        }
+    }
+
+    // Lower bounds check
+    if(freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT) {
+        freq = SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT;
+    }
+
+    // Upper bounds check
+    if(freq > SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT) {
+        freq = SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT;
+    }
+
+    // Select the proper R div value
+    r_div = si5351_select_r_div(&freq);
+
+    // Calculate the synth parameters
+    // If pll_freq is 0 and freq < 150 MHz, let the algorithm pick a PLL frequency
+    if ((pll_freq) && (freq < SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT)) {
+        si5351_multisynth_calc(freq, pll_freq, &ms_reg);
+        write_pll = 0;
+        div_by_4 = 0;
+        int_mode = 0;
+
+        switch(clk)      {
+            case SI5351_CLK0:
+                    Si5351_Config.clk0_freq = freq;
+                    break;
+            case SI5351_CLK1:
+                    Si5351_Config.clk1_freq = freq;
+                    break;
+            case SI5351_CLK2:
+                    Si5351_Config.clk2_freq = freq;
+                    break;
+            default:
+                    break;
+        }
+    } else {
+        // The PLL must be calculated and set by firmware when 150 MHz <= freq <= 160 MHz
+        if(freq >= SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT) {
+            pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
+            write_pll = 1;
+            div_by_4 = 1;
+            int_mode = 1;
+        }
+
+        // Determine which PLL to use
+        // CLK0 gets PLLA, CLK1 gets PLLB
+        // CLK2 gets PLLB if necessary
+        // Only good for Si5351A3 variant at the moment
+        switch(clk) {
+            case SI5351_CLK0:
+                    pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
+                    target_pll = SI5351_PLLA;
+                    write_pll = 1;
+                    si5351_set_ms_source(SI5351_CLK0, SI5351_PLLA);
+
+                    Si5351_Config.plla_freq = pll_freq;
+                    Si5351_Config.clk0_freq = freq;
+                    break;
+            case SI5351_CLK1:
+                    // Check to see if PLLB is locked due to other output being < 1.024 MHz or >= 112.5 MHz
+                    if(Si5351_Config.lock_pllb == SI5351_CLK2) {
+                        // We can't have a 2nd output < 1.024 MHz or >= 112.5 MHz on the same PLL unless exact same freq, so exit
+                        if((freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT
+                                || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128)
+                                && freq != Si5351_Config.clk2_freq) {
+                            Si5351_Config.clk1_freq = 0;
+                            return 1;
+                        } else {
+                            // Else, set multisynth to same PLL freq as CLK2
+                            pll_freq = Si5351_Config.pllb_freq;
+                            si5351_multisynth_calc(freq, pll_freq, &ms_reg);
+                            write_pll = 0;
+                            si5351_set_ms_source(SI5351_CLK1, SI5351_PLLB);
+                        }
+                    } else {
+                        Si5351_Config.pllb_freq = pll_freq;
+                        pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
+                        write_pll = 1;
+                        si5351_set_ms_source(SI5351_CLK1, SI5351_PLLB);
+                    }
+
+                    if(freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT
+                            || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128) {
+                        Si5351_Config.lock_pllb = SI5351_CLK1;
+
+                        // Recalc and rewrite the multisynth parameters on CLK2
+                        if(Si5351_Config.clk2_freq != 0) {
+                            struct Si5351RegSet ms_temp_reg;
+                            r_div = si5351_select_r_div(&Si5351_Config.clk2_freq);
+                            si5351_multisynth_calc(Si5351_Config.clk2_freq, \
+                                    Si5351_Config.pllb_freq, &ms_temp_reg);
+                            si5351_set_ms(SI5351_CLK2, ms_temp_reg, 0, r_div, 0);
+                        }
+                    } else {
+                        Si5351_Config.lock_pllb = SI5351_CLKNONE;
+                    }
+
+                    target_pll = SI5351_PLLB;
+                    Si5351_Config.clk1_freq = freq;
+                    break;
+            case SI5351_CLK2:
+                    // Check to see if PLLB is locked due to other output being < 1.024 MHz or >= 112.5 MHz
+                    if(Si5351_Config.lock_pllb == SI5351_CLK1) {
+                        // We can't have a 2nd output < 1.024 MHz  or >= 112.5 MHz on the same PLL unless exact same freq, so exit
+                        if((freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT
+                                || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128)
+                                && freq != Si5351_Config.clk2_freq) {
+                            Si5351_Config.clk2_freq = 0;
+                            return 1;
+                        } else {
+                            // Else, set multisynth to same PLL freq as CLK1
+                            pll_freq = Si5351_Config.pllb_freq;
+                            si5351_multisynth_calc(freq, pll_freq, &ms_reg);
+                            write_pll = 0;
+                            si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
+                        }
+                    } else {
+                        // need to account for CLK2 set before CLK1
+                        Si5351_Config.pllb_freq = pll_freq;
+                        pll_freq = si5351_multisynth_calc(freq, 0, &ms_reg);
+                        write_pll = 1;
+                        si5351_set_ms_source(SI5351_CLK2, SI5351_PLLB);
+                    }
+
+                    if(freq >= SI5351_MULTISYNTH_SHARE_MAX * SI5351_FREQ_MULT 
+                            || freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128) {
+                        Si5351_Config.lock_pllb = SI5351_CLK2;
+
+                        if(Si5351_Config.clk1_freq != 0) {
+                            // Recalc and rewrite the multisynth parameters on CLK1
+                            struct Si5351RegSet ms_temp_reg;
+                            r_div = si5351_select_r_div(&Si5351_Config.clk1_freq);
+                            si5351_multisynth_calc(Si5351_Config.clk1_freq, Si5351_Config.pllb_freq, &ms_temp_reg);
+                            si5351_set_ms(SI5351_CLK1, ms_temp_reg, 0, r_div, 0);
+                        }
+                    } else {
+                        Si5351_Config.lock_pllb = SI5351_CLKNONE;
+                    }
+
+                    target_pll = SI5351_PLLB;
+                    Si5351_Config.clk2_freq = freq;
+                    break;
+            default:
+                    return 1;
+        }
+    }
+
+    // Set multisynth registers (MS must be set before PLL)
+    si5351_set_ms(clk, ms_reg, int_mode, r_div, div_by_4);
+
+    // Set PLL if necessary
+    if (write_pll == 1) {
+        si5351_set_pll(pll_freq, target_pll);
+    }
+
+    return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_set_pll(uint64_t pll_freq, enum si5351_pll target_pll)
+ *
+ * Set the specified PLL to a specific oscillation frequency
+ *
+ * @param pll_freq - Desired PLL frequency
+ * 
+ * @param target_pll - Which PLL to set
+ *     (use the si5351_pll enum)
+ *------------------------------------------------------------------------------
+ */
+void si5351_set_pll(uint64_t pll_freq, enum si5351_pll target_pll) {
+  struct Si5351RegSet pll_reg;
+
+  si5351_pll_calc(pll_freq, &pll_reg, Si5351_Config.ref_correction);
+
+  // Derive the register values to write
+
+  // Prepare an array for parameters to be written to
+  uint8_t *params;
+  uint8_t buffer[20];
+  uint8_t i = 0;
+  uint8_t temp;
+  params = (uint8_t *)&buffer;
+
+  // Registers 26-27
+  temp = ((pll_reg.p3 >> 8) & 0xFF);
+  params[i++] = temp;
+
+  temp = (uint8_t)(pll_reg.p3  & 0xFF);
+  params[i++] = temp;
+
+  // Register 28
+  temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03);
+  params[i++] = temp;
+
+  // Registers 29-30
+  temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF);
+  params[i++] = temp;
+
+  temp = (uint8_t)(pll_reg.p1  & 0xFF);
+  params[i++] = temp;
+
+  // Register 31
+  temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0);
+  temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F);
+  params[i++] = temp;
+
+  // Registers 32-33
+  temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF);
+  params[i++] = temp;
+
+  temp = (uint8_t)(pll_reg.p2  & 0xFF);
+  params[i++] = temp;
+
+  // Write the parameters
+  if (target_pll == SI5351_PLLA) {
+    si5351_write_bulk(SI5351_PLLA_PARAMETERS, i, params);
+  } else if (target_pll == SI5351_PLLB) {
+    si5351_write_bulk(SI5351_PLLB_PARAMETERS, i, params);
+  }
+}
+
+
+
+
+/*------------------------------------------------------------------------------
+ * @brief  si5351_pll_reset(enum si5351_pll target_pll)
+ * target_pll - Which PLL to reset
+ *     (use the si5351_pll enum)
+ *
+ * Apply a reset to the indicated PLL.
+ * @param SI5351_PLL_RESET_A or SI5351_PLL_RESET_B
+ * 
+ * @return none
+ *------------------------------------------------------------------------------
+ */
+void si5351_pll_reset(enum si5351_pll target_pll) {
+    if(target_pll == SI5351_PLLA) {
+        si5351_write(SI5351_PLL_RESET, SI5351_PLL_RESET_A);
+    } else if(target_pll == SI5351_PLLB) {
+        si5351_write(SI5351_PLL_RESET, SI5351_PLL_RESET_B);
+    }
+}
+
+/*------------------------------------------------------------------------------
+ * @brief set_int(enum si5351_clock clk, uint8_t int_mode)
+ * Set the indicated multisynth into integer mode.
+ *
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ * 
+ * @param enable - Set to 1 to enable, 0 to disable
+ *
+ *------------------------------------------------------------------------------
+ */
+void si5351_set_int(enum si5351_clock clk, uint8_t enable){
+    uint8_t reg_val;
+    reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
+
+    if(enable == 1) {
+            reg_val |= (SI5351_CLK_INTEGER_MODE);
+    } else {
+            reg_val &= ~(SI5351_CLK_INTEGER_MODE);
+    }
+
+    si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
+
+    // Integer mode indication
+    switch(clk) {
+       case SI5351_CLK0:
+               Si5351_Config.clk0_int_mode = enable;
+               break;
+       case SI5351_CLK1:
+               Si5351_Config.clk1_int_mode = enable;
+               break;
+       case SI5351_CLK2:
+               Si5351_Config.clk2_int_mode = enable;
+               break;
+       default:
+               break;
+    }
+}
+
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_set_clock_pwr(enum si5351_clock clk, uint8_t pwr)
+ * Enable or disable power to a clock output (a power
+ * saving feature).
+ *
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ * 
+ * @param pwr - Set to 1 to enable, 0 to disable
+ *
+ *------------------------------------------------------------------------------
+ */
+void si5351_set_clock_pwr(enum si5351_clock clk, uint8_t pwr) {
+    uint8_t reg_val;
+    reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
+    if(pwr == 1) {
+        reg_val &= 0b01111111;
+    } else {
+        reg_val |= 0b10000000;
+    }
+    si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
+}
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_set_clock_invert(enum si5351_clock clk, uint8_t inv)
+ * Enable to invert the clock output waveform.
+ *
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ * 
+ * @param inv - Set to 1 to enable, 0 to disable
+ *
+ * -----------------------------------------------------------------------------
+ */
+void si5351_set_clock_invert(enum si5351_clock clk, uint8_t inv) {
+    uint8_t reg_val;
+    reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
+    if(inv == 1) {
+        reg_val |= (SI5351_CLK_INVERT);
+    } else {
+        reg_val &= ~(SI5351_CLK_INVERT);
+    }
+    si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
+}
+
+
+/*------------------------------------------------------------------------------
+ * @brief si5351_set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg,
+ *  uint8_t int_mode, uint8_t r_div, uint8_t div_by_4)
+ *
+ * Set the specified multisynth parameters. Not normally needed, but public for advanced users.
+ *
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ * 
+ * @param int_mode - Set integer mode
+ *  Set to 1 to enable, 0 to disable
+ * 
+ * @param r_div - Desired r_div ratio
+ * 
+ * @param div_by_4 - Set Divide By 4 mode
+ *   Set to 1 to enable, 0 to disable
+ *------------------------------------------------------------------------------
+ */
+void si5351_set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4) {
+    uint8_t *params;
+    uint8_t buffer[20];
+    uint8_t i = 0;
+    uint8_t temp;
+    uint8_t reg_val;
+
+    params = (uint8_t *)&buffer;
+    // Registers 42-43 for CLK0
+    temp = (uint8_t)((ms_reg.p3 >> 8) & 0xFF);
+    params[i++] = temp;
+
+    temp = (uint8_t)(ms_reg.p3  & 0xFF);
+    params[i++] = temp;
+
+    // Register 44 for CLK0
+    reg_val = si5351_read((SI5351_CLK0_PARAMETERS + 2) + (clk * 8));
+    reg_val &= ~(0x03);
+    temp = reg_val | ((uint8_t)((ms_reg.p1 >> 16) & 0x03));
+    params[i++] = temp;
+
+    // Registers 45-46 for CLK0
+    temp = (uint8_t)((ms_reg.p1 >> 8) & 0xFF);
+    params[i++] = temp;
+
+    temp = (uint8_t)(ms_reg.p1  & 0xFF);
+    params[i++] = temp;
+
+    // Register 47 for CLK0
+    temp = (uint8_t)((ms_reg.p3 >> 12) & 0xF0);
+    temp += (uint8_t)((ms_reg.p2 >> 16) & 0x0F);
+    params[i++] = temp;
+
+    // Registers 48-49 for CLK0
+    temp = (uint8_t)((ms_reg.p2 >> 8) & 0xFF);
+    params[i++] = temp;
+
+    temp = (uint8_t)(ms_reg.p2  & 0xFF);
+    params[i++] = temp;
+
+    // Write the parameters
+    switch (clk) {
+        case SI5351_CLK0:
+                si5351_write_bulk(SI5351_CLK0_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK1:
+                si5351_write_bulk(SI5351_CLK1_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK2:
+                si5351_write_bulk(SI5351_CLK2_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK3:
+                si5351_write_bulk(SI5351_CLK3_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK4:
+                si5351_write_bulk(SI5351_CLK4_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK5:
+                si5351_write_bulk(SI5351_CLK5_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK6:
+                si5351_write_bulk(SI5351_CLK6_PARAMETERS, i, params);
+                break;
+        case SI5351_CLK7:
+                si5351_write_bulk(SI5351_CLK7_PARAMETERS, i, params);
+                break;
+        case SI5351_CLKNONE:
+                return;
+    }
+
+    si5351_set_int(clk, int_mode);
+    si5351_ms_div(clk, r_div, div_by_4);
+}
+
+void si5351_ms_div(enum si5351_clock clk, uint8_t r_div, uint8_t div_by_4) {
+    uint8_t reg_val, reg_addr;
+
+    switch(clk) {
+        case SI5351_CLK0:
+                reg_addr = SI5351_CLK0_PARAMETERS + 2;
+                break;
+        case SI5351_CLK1:
+                reg_addr = SI5351_CLK1_PARAMETERS + 2;
+                break;
+        case SI5351_CLK2:
+                reg_addr = SI5351_CLK2_PARAMETERS + 2;
+                break;
+        case SI5351_CLK3:
+                reg_addr = SI5351_CLK3_PARAMETERS + 2;
+                break;
+        case SI5351_CLK4:
+                reg_addr = SI5351_CLK4_PARAMETERS + 2;
+                break;
+        case SI5351_CLK5:
+                reg_addr = SI5351_CLK5_PARAMETERS + 2;
+                break;
+        case SI5351_CLK6:
+                return;
+        case SI5351_CLK7:
+                return;
+        case SI5351_CLKNONE:
+                return;
+    }
+
+    reg_val = si5351_read(reg_addr);
+
+    // Clear the relevant bits
+    reg_val &= ~(0x7c);
+
+    if(div_by_4 == 0) {
+            reg_val &= ~(SI5351_OUTPUT_CLK_DIVBY4);
+    } else {
+            reg_val |= (SI5351_OUTPUT_CLK_DIVBY4);
+    }
+
+    reg_val |= (r_div << SI5351_OUTPUT_CLK_DIV_SHIFT);
+
+    si5351_write(reg_addr, reg_val);
+}
+
+/*------------------------------------------------------------------------------
+ * @brief set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
+ * Set the desired PLL source for a multisynth.
+ *
+ * @param clk - Clock output
+ *   (use the si5351_clock enum)
+ * 
+ * @param pll - Which PLL to use as the source
+ *     (use the si5351_pll enum)
+ *
+ *------------------------------------------------------------------------------
+ */
+void si5351_set_ms_source(enum si5351_clock clk, enum si5351_pll pll) {
+    uint8_t reg_val;
+
+    reg_val = si5351_read(SI5351_CLK0_CTRL + (uint8_t)clk);
+
+    if(pll == SI5351_PLLA) {
+            reg_val &= ~(SI5351_CLK_PLL_SELECT);
+    } else if (pll == SI5351_PLLB) {
+            reg_val |= SI5351_CLK_PLL_SELECT;
+    }
+    si5351_write(SI5351_CLK0_CTRL + (uint8_t)clk, reg_val);
+}
+
+uint64_t si5351_multisynth_calc(uint64_t freq, uint64_t pll_freq, struct Si5351RegSet *reg) {
+    uint64_t lltmp;
+    uint32_t a, b, c, p1, p2, p3;
+    uint8_t divby4;
+    uint8_t ret_val = 0;
+
+    // Multisynth bounds checking
+    if (freq > SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT) {
+        freq = SI5351_MULTISYNTH_MAX_FREQ * SI5351_FREQ_MULT;
+    }
+    if (freq < SI5351_MULTISYNTH_MIN_FREQ * SI5351_FREQ_MULT) {
+        freq = SI5351_MULTISYNTH_MIN_FREQ * SI5351_FREQ_MULT;
+    }
+
+    divby4 = 0;
+    if (freq >= SI5351_MULTISYNTH_DIVBY4_FREQ * SI5351_FREQ_MULT) {
+        divby4 = 1;
+    }
+
+    if(pll_freq == 0) {
+        // Find largest integer divider for max
+        // VCO frequency and given target frequency
+        if(divby4 == 0) {
+            lltmp = SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT;
+            do_div(lltmp, freq);
+            a = (uint32_t)lltmp;
+        } else {
+            a = 4;
+        }
+
+        b = 0;
+        c = 1;
+        pll_freq = a * freq;
+    } else {
+        // Preset PLL, so return the actual freq for these params instead of PLL freq
+        ret_val = 1;
+        // Determine integer part of feedback equation
+        a = pll_freq / freq;
+        if (a < SI5351_MULTISYNTH_A_MIN) {
+            freq = pll_freq / SI5351_MULTISYNTH_A_MIN;
+        }
+        if (a > SI5351_MULTISYNTH_A_MAX) {
+            freq = pll_freq / SI5351_MULTISYNTH_A_MAX;
+        }
+        b = (pll_freq % freq * RFRAC_DENOM) / freq;
+        c = b ? RFRAC_DENOM : 1;
+    }
+
+    // Calculate parameters
+    if (divby4 == 1) {
+        p3 = 1;
+        p2 = 0;
+        p1 = 0;
+    } else {
+        p1 = 128 * a + ((128 * b) / c) - 512;
+        p2 = 128 * b - c * ((128 * b) / c);
+        p3 = c;
+    }
+
+    reg->p1 = p1;
+    reg->p2 = p2;
+    reg->p3 = p3;
+
+    if(ret_val == 0) {
+        return pll_freq;
+    } else {
+        return freq;
+    }
+}
+
+uint64_t si5351_pll_calc(uint64_t freq, struct Si5351RegSet *reg, int32_t correction) {
+    uint64_t ref_freq = Si5351_Config.xtal_freq * SI5351_FREQ_MULT;
+    uint32_t a, b, c, p1, p2, p3;
+    uint64_t lltmp, rfrac, denom;
+
+
+    // Factor calibration value into nominal crystal frequency
+    // Measured in parts-per-billion
+
+    ref_freq = ref_freq + (int32_t)((((((int64_t)correction) << 31) / 1000000000LL) * ref_freq) >> 31);
+
+    // PLL bounds checking
+    if (freq < SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT) {
+        freq = SI5351_PLL_VCO_MIN * SI5351_FREQ_MULT;
+    }
+    if (freq > SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT) {
+        freq = SI5351_PLL_VCO_MAX * SI5351_FREQ_MULT;
+    }
+
+    // Determine integer part of feedback equation
+    a = freq / ref_freq;
+
+    if (a < SI5351_PLL_A_MIN) {
+        freq = ref_freq * SI5351_PLL_A_MIN;
+    }
+    if (a > SI5351_PLL_A_MAX) {
+        freq = ref_freq * SI5351_PLL_A_MAX;
+    }
+
+    // Find best approximation for b/c = fVCO mod fIN
+    denom = 1000ULL * 1000ULL;
+    lltmp = freq % ref_freq;
+    lltmp *= denom;
+    do_div(lltmp, ref_freq);
+    rfrac = lltmp;
+
+    b = (((uint64_t)(freq % ref_freq)) * RFRAC_DENOM) / ref_freq;
+    c = b ? RFRAC_DENOM : 1;
+
+       // Calculate parameters
+    p1 = 128 * a + ((128 * b) / c) - 512;
+    p2 = 128 * b - c * ((128 * b) / c);
+    p3 = c;
+
+    // Recalculate frequency as fIN * (a + b/c)
+    lltmp  = ref_freq;
+    lltmp *= b;
+    do_div(lltmp, c);
+    freq = lltmp;
+    freq += ref_freq * a;
+
+    reg->p1 = p1;
+    reg->p2 = p2;
+    reg->p3 = p3;
+
+    return freq;
+}
+
+uint8_t si5351_select_r_div(uint64_t *freq) {
+    uint8_t r_div = SI5351_OUTPUT_CLK_DIV_1;
+
+    // Choose the correct R divider
+    if((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT) 
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 2)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_128;
+        *freq *= 128ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 2)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 4)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_64;
+        *freq *= 64ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 4)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 8)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_32;
+        *freq *= 32ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 8)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 16)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_16;
+        *freq *= 16ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 16)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 32)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_8;
+        *freq *= 8ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 32)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 64)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_4;
+        *freq *= 4ULL;
+    } else if ((*freq >= SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 64)
+            && (*freq < SI5351_CLKOUT_MIN_FREQ * SI5351_FREQ_MULT * 128)) {
+        r_div = SI5351_OUTPUT_CLK_DIV_2;
+        *freq *= 2ULL;
+    }
+
+    return r_div;
+}
+