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.
$(CC) $(CFLAGS) -O3 $^ -o $@ $(LIBPATHS) $(LIBS)
-
FM_LODUC_PLAY_SRCS=\
src/fm_loduc_play.c \
gdb_stdio.c \
# ---------------------------------------------------------------------------------
+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
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*------------------------------------------------------------------------------
+ *
+ * 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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*---------------------------------------------------------------------------*\
+
+ 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);
+}
--- /dev/null
+/*------------------------------------------------------------------------------
+ *
+ * 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;
+}
+