diff --git a/.cproject b/.cproject
index c75a254..8e9c816 100644
--- a/.cproject
+++ b/.cproject
@@ -81,9 +81,9 @@
-
-
+
+
@@ -135,10 +135,10 @@
-
-
-
-
+
+
+
+
@@ -164,9 +164,9 @@
-
-
+
+
diff --git a/Core/Inc/si5351.h b/Core/Inc/si5351.h
index 70e939d..4e3660a 100644
--- a/Core/Inc/si5351.h
+++ b/Core/Inc/si5351.h
@@ -1,525 +1,149 @@
-/*
- * si5351.h
- *
- * Created on: Feb 16, 2018
- * Author: Petr Polasek
- *
- * To make this library useable on any other device than
- * STM32Fxxx Cortex Mx, please edit these parts of the library:
- *
- * DEFINES:
- * SI5351_I2C_PERIPHERAL - the I2C peripheral name according
- * to your devices HAL library
- * I2C_TIMEOUT - time for the communication to time out
- *
- * TYPEDEFS:
- * Si5351_ConfigTypeDef - the I2Cx parameter should be changed
- * so that its type corresponds to your HAL library
- *
- * FUNCTIONS:
- * Si5351_WriteRegister
- * Si5351_ReadRegister
- * You need to write your own I2C handlers here
- *
- */
+/******************************************************************************
+ * File Name : si5351.h
+ * Description : STM32 library/driver for the Si5351 clock chip
+ * from Skyworks Solutions, Inc. (former SiLabs)
+ ******************************************************************************
+ * @author: Thomas Kuschel KW4NZ, created 2022-05-11
+ *
+ * originally written by Petr Polasek, created Feb 16, 2018
+ ******************************************************************************
+ * DO NOT EDIT THIS FILE FOR CONFIGURATION, USE THE FOLLOWING PROCEDURE:
+ *
+ * Inside your main.c program or within your STM32 code:
+ * Include this header to the the main.c: #include "si5351.h"
+ * You've to initialize the I2C functionality first (e.g. with STM32CubeIDE)
+ * Afterwards, when there is a handle like "I2C_HandleTypeDef hi2c1;",
+ * you simply initialize this si5351 library in your main.c ,
+ * just after the MX_I2C1_Init();
+ * so between the USER CODE like using the given i2c handle "hi2c1" with default values:
+ *
+ * /\* USER CODE BEGIN 2 *\/
+ *
+ * si5351_inst_t si5351_inst;
+ * si5351_initialize(&hi2c1);
+ *
+ * /\* USER CODE END 2 *\/
+ *
+ * The 7-bit device (slave) address of the Si5351 consist of a 6-bit fixed
+ * address plus a user selectable LSB bit as shown in Figure 6 of the datasheet.
+ * The LSB bit is selectable as 0 or 1 using the optional A0 pin which is useful
+ * for applications that require more than one Si5351 on a single I2C bus.
+ * Only the Si5351A 20-QFN and Si5351A 16-QFN have the A0 LSB pin option.
+ * If a part does not have the A0 pin, the default address is 0x60 with
+ * the A0 bit set to 0.
+ *
+ * So additionally you may drive more then one Si5351 at the same or
+ * another I2C bus when calling the init function with the given
+ * I2C bus address (default: 0x60)
+ * ( - internally this I2C address is shifted to the left
+ * for the proper usage of the I2C HAL driver i.e. it becomes 0xC0 )
+ *
+ * Example:
+ * /\* Define handlers for the sum of three SI5351 clock generators: *\/
+ * si5351_HandleTypeDef hsi5351[3];
+ *
+ * /\* 1st SI5351 chip and with an A0 = 0: *\/
+ * hsi5351[0] = si5351_init(&hi2c1, 0x60, 25000000);
+ * /\* 2nd SI5351 chip on the same I2C bus "hi2c1" but address line A0 = 1 *\/
+ * hsi5351[1] = si5351_init(&hi2c1, 0x61, 27000000);
+ * /\* 3rd SI5351 chip on another IC2 bus with handle "hi2c2" *\/
+ * hsi5351[2] = si5351_init(&hi2c2, 0x60, 25000000);
+ *
+ * PROs:
+ * The library is preemptive and can be used within an operating system.
+ * All structures and used variables are dynamically allocated.
+ * CONs:
+ * Not fully tested.
+ * Tested with FreeRTOS and with 2 I2C bus systems.
+ *
+ * CHANGES:
+ * - Removed several defines, enums, etc. from the header file, b/c we do not
+ * want to export them to other programs
+ ******************************************************************************/
-#ifndef SI5351_H_
-#define SI5351_H_
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __SI5351_H__
+#define __SI5351_H__
-#include "stm32l4xx_hal.h"
-
-#define SI5351_I2C_ADDRESS 0xC0 //default I2C address of Si5351
-#define SI5351_I2C_PERIPHERAL I2C1 //default I2C interface
-
-#define SI5351_XTAL_FREQ 25000000 // sets default value, 25000000 for 25 MHz, 27000000 for 27 MHz
-#define SI5351_CLKIN_FREQ 0 // set in Hz
-
-#ifdef I2C_TIMEOUT
- #undef I2C_TIMEOUT
-#endif
-#define I2C_TIMEOUT 100000 //I2C timeout for wait loops
-
-#define SI5351_TIMEOUT (I2C_TIMEOUT * 10)
-
-#ifndef ENABLESTATE
- #define ENABLESTATE
- typedef enum
- {
- OFF = 0,
- ON = 1
- } EnableState;
+#ifdef __cplusplus
+extern "C" {
#endif
-/*
- * This section contains register addresses and bit masks for
- * the device status registers.
+/* Includes ------------------------------------------------------------------*/
+#include
+#include /* register map of the Si5351 */
+/* #include could also be included */
+/* Private includes ----------------------------------------------------------*/
+
+/* Exported types ------------------------------------------------------------*/
+/*!< Si5351 instance typedef -- handle as pointer */
+typedef struct __SI5351_HandleTypeDef *si5351_inst_t;
+
+/* Exported constants --------------------------------------------------------*/
+/** @enum errno_t Error Number Constants, @TODO you can also include
*/
-
-#define REG_DEV_STATUS 0
- #define DEV_SYS_INIT_MASK 0x80
- #define DEV_LOL_B_MASK 0x40
- #define DEV_LOL_A_MASK 0x20
- #define DEV_LOS_CLKIN_MASK 0x10
- #define DEV_LOS_XTAL_MASK 0x08
- #define DEV_REVID_MASK 0x03
-
-#define REG_DEV_STICKY 1
- #define DEV_STKY_SYS_INIT_MASK 0x80
- #define DEV_STKY_LOL_B_MASK 0x40
- #define DEV_STKY_LOL_A_MASK 0x20
- #define DEV_STKY_LOS_CLKIN_MASK 0x10
- #define DEV_STKY_LOS_XTAL_MASK 0x08
-
-#define REG_INT_MASK 2
- #define INT_MASK_SYS_INIT_MASK 0x80
- #define INT_MASK_LOL_B_MASK 0x40
- #define INT_MASK_LOL_A_MASK 0x20
- #define INT_MASK_LOS_CLKIN_MASK 0x10
- #define INT_MASK_LOS_XTAL_MASK 0x08
-
-/*
- * This section contains data structures for configuring the
- * oscillator, VCXO and CLKIN section.
- */
-
-#define REG_XTAL_CL 183
-#define XTAL_CL_MASK 0xC0
-#define PLL_CL_MASK 0x36
-
-//this sets the crystal load capacitance
-typedef enum
-{
- XTAL_Load_4_pF = 0x00,
- XTAL_Load_6_pF = 0x40,
- XTAL_Load_8_pF = 0x80,
- XTAL_Load_10_pF = 0xC0
-} Si5351_XTALLoadTypeDef;
-
-//The following is an unexplained parameter. However someone from SiLabs called it "VCO load cap".
-//Lower settings seem to be more stable on higher frequencies, higher settings are more stable on lower frequencies allowing to tune the PLL to <200 MHz.
-typedef enum
-{
- PLL_Capacitive_Load_0 = 0,
- PLL_Capacitive_Load_1 = 1,
- PLL_Capacitive_Load_2 = 2
-} Si5351_PLLCapacitiveLoadTypeDef;
-
-#define REG_CLKIN_DIV 15
-#define CLKIN_MASK 0xC0
-
-//this sets the CLKIN pre-divider, after division, CLKIN should
-//fall between 10-40 MHz
-typedef enum
-{
- CLKINDiv_Div1 = 0x00,
- CLKINDiv_Div2 = 0x40,
- CLKINDiv_Div4 = 0x80,
- CLKINDiv_Div8 = 0xC0
-} Si5351_CLKINDivTypeDef;
-
-#define REG_FANOUT_EN 187
-#define FANOUT_CLKIN_EN_MASK 0x80
-#define FANOUT_XO_EN_MASK 0x40
-#define FANOUT_MS_EN_MASK 0x10
-
-#define REG_VCXO_PARAM_0_7 162
-#define REG_VCXO_PARAM_8_15 163
-#define REG_VCXO_PARAM_16_21 164
-#define VCXO_PARAM_16_21_MASK 0x3F
-#define VCXO_PARAM_MASK 0x003FFFFF
-
-#define APR_MINIMUM 30 //minimum pull range
-#define APR_MAXIMUM 240 //maximum pull range
-
-#define CLKIN_MINIMUM 10000 //minimum CLKIN frequency after division in kHz
-#define CLKIN_MAXIMUM 40000 //maximum CLKIN frequency after division in kHz
-
-typedef struct
-{
- Si5351_XTALLoadTypeDef OSC_XTAL_Load; //capacitive load of XTAL, 10pF by default
- Si5351_CLKINDivTypeDef CLKIN_Div; //CLKIN predivision, input f to PLL must be 10-40 MHz
- uint8_t VCXO_Pull_Range_ppm; //can range from +-30 ppm to 240ppm
-} Si5351_OSCConfigTypeDef;
-
-/*
- * This section contains data structures for configuring the
- * PLL (PLLA and PLLB)
- */
-
-#define REG_PLL_CLOCK_SOURCE 15
-#define PLLA_CLOCK_SOURCE_MASK 0x04
-#define PLLB_CLOCK_SOURCE_MASK 0x08
-
-//this selects the clock source for the PLL
-typedef enum
-{
- PLL_Clock_Source_XTAL = 0x00,
- PLL_Clock_Source_CLKIN = 0x0C //0x04 for PLLA, 0x08 for PLLB, use mask!
-} Si5351_PLLClockSourceTypeDef;
-
-#define REG_FB_INT 22
-#define FB_INT_MASK 0x40
-
-#define REG_PLL_RESET 177
-#define PLLA_RESET_MASK 0x20
-#define PLLB_RESET_MASK 0x80
-
-#define REG_MSN_P1_0_7 30
-#define REG_MSN_P1_8_15 29
-#define REG_MSN_P1_16_17 28
-#define MSN_P1_16_17_MASK 0x03
-
-#define REG_MSN_P2_0_7 33
-#define REG_MSN_P2_8_15 32
-#define REG_MSN_P2_16_19 31
-#define MSN_P2_16_19_MASK 0x0F
-
-#define REG_MSN_P3_0_7 27
-#define REG_MSN_P3_8_15 26
-#define REG_MSN_P3_16_19 31
-#define MSN_P3_16_19_MASK 0xF0
-
-#define MSNA_MSNB_OFFSET 8
-
-typedef struct
-{
- uint32_t PLL_Multiplier_Integer;
- uint32_t PLL_Multiplier_Numerator;
- uint32_t PLL_Multiplier_Denominator;
- Si5351_PLLClockSourceTypeDef PLL_Clock_Source;
- Si5351_PLLCapacitiveLoadTypeDef PLL_Capacitive_Load;
-} Si5351_PLLConfigTypeDef;
-
-/*
- * This section contains data structures for configuring the
- * Spread Spectrum feature.
- */
-
-#define REG_SSC_MODE 151
-#define SSC_MODE_MASK 0x80
-
-//this selects the Spread Spectrum mode
-typedef enum
-{
- SS_Mode_DownSpread = 0x00,
- SS_Mode_CenterSpread = 0x80
-} Si5351_SSModeTypeDef;
-
-typedef enum
-{
- SS_NCLK_0 = 0x00,
- SS_NCLK_1 = 0x10,
- SS_NCLK_2 = 0x20,
- SS_NCLK_3 = 0x30,
- SS_NCLK_4 = 0x40,
- SS_NCLK_5 = 0x50,
- SS_NCLK_6 = 0x60,
- SS_NCLK_7 = 0x70,
- SS_NCLK_8 = 0x80,
- SS_NCLK_9 = 0x90,
- SS_NCLK_10 = 0xA0,
- SS_NCLK_11 = 0xB0,
- SS_NCLK_12 = 0xC0,
- SS_NCLK_13 = 0xD0,
- SS_NCLK_14 = 0xE0,
- SS_NCLK_15 = 0xF0
-} Si5351_SSNCLKTypeDef;
-
-#define REG_SSDN_P1_0_7 153
-#define REG_SSDN_P1_8_11 154
-#define SSDN_P1_8_11_MASK 0x0F
-
-#define REG_SSDN_P2_0_7 150
-#define REG_SSDN_P2_8_14 149
-#define SSDN_P2_8_14_MASK 0x7F
-
-#define REG_SSDN_P3_0_7 152
-#define REG_SSDN_P3_8_14 151
-#define SSDN_P3_8_14_MASK 0x7F
-
-#define REG_SSUDP_0_7 155
-#define REG_SSUDP_8_11 154
-#define SSUDP_8_11_MASK 0xF0
-
-#define REG_SSUP_P1_0_7 160
-#define REG_SSUP_P1_8_11 161
-#define SSUP_P1_8_11_MASK 0x0F
-
-#define REG_SSUP_P2_0_7 157
-#define REG_SSUP_P2_8_14 156
-#define SSUP_P2_8_14_MASK 0x7F
-
-#define REG_SSUP_P3_0_7 159
-#define REG_SSUP_P3_8_14 158
-#define SSUP_P3_8_14_MASK 0x7F
-
-#define REG_SSC_EN 149
-#define SSC_EN_MASK 0x80
-
-#define REG_SS_NCLK 161
-#define SS_NCLK_MASK 0xF0
-
-typedef struct
-{
- uint32_t SS_Amplitude_ppm; //amplitude of the SS feature in ppm of center frequency
- EnableState SS_Enable;
- Si5351_SSModeTypeDef SS_Mode;
- Si5351_SSNCLKTypeDef SS_NCLK;
-} Si5351_SSConfigTypeDef;
-
-/*
- * This section contains data structures for configuring the
- * Output Multisynth.
- */
-
-//this selects the Multisynth clock source
-typedef enum
-{
- MS_Clock_Source_PLLA = 0x00,
- MS_Clock_Source_PLLB = 0x20
-} Si5351_MSClockSourceTypeDef;
-
-#define REG_MS_P1_0_7 46
-#define REG_MS_P1_8_15 45
-#define REG_MS_P1_16_17 44
-#define MS_P1_16_17_MASK 0x03
-
-#define REG_MS_P2_0_7 49
-#define REG_MS_P2_8_15 48
-#define REG_MS_P2_16_19 47
-#define MS_P2_16_19_MASK 0x0F
-
-#define REG_MS_P3_0_7 43
-#define REG_MS_P3_8_15 42
-#define REG_MS_P3_16_19 47
-#define MS_P3_16_19_MASK 0xF0
-
-#define REG_MS67_P1 90
-
-#define REG_MS_INT 16
-#define MS_INT_MASK 0x40
-
-#define REG_MS_DIVBY4 44
-#define MS_DIVBY4_MASK 0x0C
-
-#define REG_MS_SRC 16
-#define MS_SRC_MASK 0x20
-
-#define MS_SETUP_STEP 1
-#define MS_DIVIDER_STEP 8
-
-typedef struct
-{
- Si5351_MSClockSourceTypeDef MS_Clock_Source; //select source on MS input
- uint32_t MS_Divider_Integer; //the integer part of divider, called "a"
- uint32_t MS_Divider_Numerator; //the numerator, called "b"
- uint32_t MS_Divider_Denominator; //the denominator, called "c"
-} Si5351_MSConfigTypeDef; //sets MS divider ( a+(b/c) ) and clock (PLLA/PLLB)
-
-/*
- * This section contains data structures for configuring the
- * CLK, R divider and output stage (joined together because they make
- * a tight block without any multiplexer).
- */
-
-#define REG_CLK_SRC 16
-#define CLK_SRC_MASK 0x0C
-
-//this sets the CLK source clock
-typedef enum
-{
- CLK_Clock_Source_XTAL = 0x00,
- CLK_Clock_Source_CLKIN = 0x04,
- CLK_Clock_Source_MS0_MS4 = 0x08, //this uses MS0 for CLK0..3 and MS4 for CLK4..7
- CLK_Clock_Source_MS_Own = 0x0C //this uses MSx for CLKx
-} Si5351_CLKClockSourceTypeDef; //configures multiplexer on CLK input
-
-#define REG_CLK_R_DIV 44
-#define CLK_R_DIV_MASK 0x70
-
-#define REG_CLK_R67_DIV 92
-#define CLK_R67_DIV_MASK 0x07
-
-//this sets the R divider ratio
-typedef enum
-{
- CLK_R_Div1 = 0x00,
- CLK_R_Div2 = 0x10,
- CLK_R_Div4 = 0x20,
- CLK_R_Div8 = 0x30,
- CLK_R_Div16 = 0x40,
- CLK_R_Div32 = 0x50,
- CLK_R_Div64 = 0x60,
- CLK_R_Div128 = 0x70
-} Si5351_CLKRDivTypeDef;
-
-#define REG_CLK_DIS_STATE 24
-#define CLK_DIS_STATE_MASK 0x03
-
-//this sets output buffer behaviour when disabled
-typedef enum
-{
- CLK_Disable_State_LOW = 0x00,
- CLK_Disable_State_HIGH = 0x01,
- CLK_Disable_State_HIGH_Z = 0x02, //three-stated when off
- CLK_Disable_State_ALWAYS_ON = 0x03 //cannot be disabled
-} Si5351_CLKDisableStateTypeDef;
-
-#define REG_CLK_IDRV 16
-#define CLK_IDRV_MASK 0x03
-
-//this sets current drive of the output buffer
-typedef enum
-{
- CLK_I_Drv_2mA = 0x00,
- CLK_I_Drv_4mA = 0x01,
- CLK_I_Drv_6mA = 0x02,
- CLK_I_Drv_8mA = 0x03
-} Si5351_CLKIDrvTypeDef;
-
-#define REG_CLK_PHOFF 165
-#define CLK_PHOFF_MASK 0x7F
-
-#define REG_CLK_EN 3
-
-#define REG_CLK_INV 16
-#define CLK_INV_MASK 0x10
-
-#define REG_CLK_PDN 16
-#define CLK_PDN_MASK 0x80
-
-#define REG_CLK_OEB 9
-
-#define CLK_PHOFF_STEP 1
-#define CLK_SETUP_STEP 1
-#define CLK_R_DIV_STEP 8
-
-typedef struct
-{
- Si5351_CLKClockSourceTypeDef CLK_Clock_Source; //clock source
-
- /* this sets the time offset of the CLK channel, basic unit
- * is one quarter of the VCO period (90deg offset),
- * set it to 4*fVCO*toffset, the value is 7-bit, the max time offset
- * varies between 35 and 53 ns (1 cycle for 28 and 19 MHz, respectively)
- * according to the current frequency of the VCO
- */
- uint8_t CLK_QuarterPeriod_Offset;
-
- Si5351_CLKRDivTypeDef CLK_R_Div; //R divider value (only powers of 2)
- EnableState CLK_Invert; //invert output clock
- EnableState CLK_Enable; //enable flag
- EnableState CLK_PowerDown; //powerdown flag
- Si5351_CLKDisableStateTypeDef CLK_Disable_State; //sets output behaviour when disabled
- Si5351_CLKIDrvTypeDef CLK_I_Drv; //output driver current drive strength
- EnableState CLK_Use_OEB_Pin; //allows using OEB pin to enable clock
-} Si5351_CLKConfigTypeDef;
-
-/*
- * This section contains main data structure for Si5351 configuration
- */
-
-typedef struct
-{
- /*
- * These are frequencies of the input clocks, set it in Hz.
- */
- uint32_t f_XTAL;
- uint32_t f_CLKIN;
-
- //Interrupt masking - enabling it disables the int source from pulling INTR low
- EnableState Interrupt_Mask_SysInit;
- EnableState Interrupt_Mask_PLLB;
- EnableState Interrupt_Mask_PLLA;
- EnableState Interrupt_Mask_CLKIN;
- EnableState Interrupt_Mask_XTAL;
-
- //Fanout enable - enables internal clock routing
- EnableState Fanout_MS_EN;
- EnableState Fanout_XO_EN;
- EnableState Fanout_CLKIN_EN;
-
- I2C_TypeDef *I2Cx; //the I2C interface that will be used
- uint8_t HW_I2C_Address; //I2C address of the Si5351 for the packages with A0 pin
- //(also, some duds with strange address reported)
- Si5351_OSCConfigTypeDef OSC; //Oscillator, CLKIN and VCXO settings
- Si5351_PLLConfigTypeDef PLL[2]; //PLL settings for PLLA and PLLB
- Si5351_MSConfigTypeDef MS[8]; //MultiSynth[0..7] settings
- Si5351_CLKConfigTypeDef CLK[8]; //CLK[0..7], R divider and output stage settings
- Si5351_SSConfigTypeDef SS; //spread spectrum settings
-} Si5351_ConfigTypeDef;
-
-/*
- * Typedefs for selecting PLL, MS and CLK to be used
- */
-
-//this selects PLL channel
-typedef enum
-{
- PLL_A = 0,
- PLL_B = 1
-} Si5351_PLLChannelTypeDef;
-
-//this selects Multisynth channel
-typedef enum
-{
- MS0 = 0,
- MS1 = 1,
- MS2 = 2,
- MS3 = 3,
- MS4 = 4,
- MS5 = 5,
- MS6 = 6,
- MS7 = 7
-} Si5351_MSChannelTypeDef;
-
-//this selects CLK channel
-typedef enum
-{
- CLK0 = 0,
- CLK1 = 1,
- CLK2 = 2,
- CLK3 = 3,
- CLK4 = 4,
- CLK5 = 5,
- CLK6 = 6,
- CLK7 = 7
-} Si5351_CLKChannelTypeDef;
-
-//this selects device status flag
-typedef enum
-{
- StatusBit_SysInit = DEV_SYS_INIT_MASK,
- StatusBit_PLLA = DEV_STKY_LOL_A_MASK,
- StatusBit_PLLB = DEV_LOL_B_MASK,
- StatusBit_CLKIN = DEV_LOS_CLKIN_MASK,
- StatusBit_XTAL = DEV_LOS_XTAL_MASK,
-} Si5351_StatusBitTypeDef;
-
-//these write to and read from a Si5351 register, for porting
-//purposes, these functions should be the only ones which should need edits
-int Si5351_WriteRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address, uint8_t reg_data);
-uint8_t Si5351_ReadRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address);
-
-void Si5351_StructInit(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-
-void Si5351_OSCConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-
-EnableState Si5351_CheckStatusBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
-EnableState Si5351_CheckStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
-void Si5351_InterruptConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-void Si5351_ClearStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
-
-void Si5351_PLLConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel);
-void Si5351_PLLReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel);
-void Si5351_PLLSimultaneousReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-
-void Si5351_SSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-
-void Si5351_MSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_MSChannelTypeDef MS_Channel);
-
-void Si5351_CLKPowerCmd(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel);
-void Si5351_CLKConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel);
-
-int Si5351_Init(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
-#endif /* SI5351_H_ */
+#if !defined _SYS_ERRNO_H_ && !defined __ERRNO_H__ && !defined __AT1_ERROR_NUMBERS__
+typedef enum {
+ EPERM = 1, /*!< Operation not permitted */
+ EIO = 5, /*!< I/O error */
+ ENOMEM = 12, /*!< Out of memory */
+ EFAULT = 14, /*!< Bad address */
+ EBUSY = 16, /*!< Device or resource busy */
+ ENODEV = 19, /*!< No such device */
+ EINVAL = 22, /*!< Invalid argument */
+ EADDRINUSE = 98,/*!< Address already in use */
+ ETIMEDOUT = 116 /*!< Connection timed out */
+} si5351_errno_t;
+#endif
+
+typedef enum {
+ SI5351_PLLA = 0,
+ SI5351_PLLB
+}si5351_pll_t;
+
+/* Exported variables --------------------------------------------------------*/
+
+/* Exported macros -----------------------------------------------------------*/
+#define __SI5351__ 1
+#define __SI5351_MINOR__ 1
+#define __SI5351_PATCHLEVEL__ 0
+
+#define SI5351_VERSION (__SI5351__ * 10000 \
+ + __SI5351_MINOR__ * 100 \
+ + __SI5351_PATCHLEVEL__)
+
+#ifndef SI5351_DEFAULTS
+#define SI5351_DEFAULTS 1
+#define SI5351_I2C_ADDR_DEFAULT 0x60
+#define SI5351_XTAL_DEFAULT 25000000u /* default xtal frequency in [Hz] */
+#define SI5351_NUMBER_OF_OUTPUTS 8
+#endif
+
+/* Exported functions --------------------------------------------------------*/
+#if SI5351_DEFAULTS
+si5351_inst_t si5351_initialize(void * i2c_handle);
+int si5351_deinitialize(void);
+#endif
+si5351_inst_t si5351_init(void * i2c_handle, uint8_t i2c_address, uint32_t xtal_frequency);
+int si5351_deinit(si5351_inst_t si5351_handle);
+int si5351_i2c_ready(si5351_inst_t inst);
+int si5351_program(si5351_inst_t inst);
+int si5351_enable_output(si5351_inst_t inst, uint8_t clk);
+int si5351_disable_output(si5351_inst_t inst, uint8_t clk);
+int si5351_set_clk0(si5351_inst_t inst, uint32_t frequency);
+int si5351_set_clk(si5351_inst_t inst, uint8_t clk, uint32_t frequency, si5351_pll_t pll);
+
+char * si5351_read_debug_msg(si5351_inst_t inst);
+char * si5351_read_register_debug(si5351_inst_t inst, char *buf, size_t bufsize, uint8_t regaddr);
+
+int si5351_get_instance(si5351_inst_t *inst);
+
+/* under development */
+int si5351_set_clk_phase(si5351_inst_t inst, uint8_t clk, uint32_t frequency, double phase, si5351_pll_t pll);
+int si5351_set_phase(si5351_inst_t inst, uint8_t clk, uint8_t phase);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __SI5351_H__ */
diff --git a/Core/Inc/stm32_si5351_reg.h b/Core/Inc/si5351_reg.h
similarity index 100%
rename from Core/Inc/stm32_si5351_reg.h
rename to Core/Inc/si5351_reg.h
diff --git a/Core/Inc/stm32_si5351.h b/Core/Inc/stm32_si5351.h
deleted file mode 100644
index 96dc5f5..0000000
--- a/Core/Inc/stm32_si5351.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/******************************************************************************
- * File Name : stm32_si5351.h
- * Description : STM32 library/driver for the Si5351 clock chip
- * from Skyworks Solutions, Inc. (former SiLabs)
- ******************************************************************************
- * @author: Thomas Kuschel KW4NZ, created 2022-05-11
- *
- * originally written by Petr Polasek, created Feb 16, 2018
- ******************************************************************************
- * DO NOT EDIT THIS FILE FOR CONFIGURATION, USE THE FOLLOWING PROCEDURE:
- *
- * Inside your main.c program or within your STM32 code:
- * Include this header to the the main.c: #include "stm32_si5351.h"
- * You've to initialize the I2C functionality first (e.g. with STM32CubeIDE)
- * Afterwards, when there is a handle like "I2C_HandleTypeDef hi2c1;",
- * you simply initialize this stm32_si5351 library in your main.c ,
- * just after the MX_I2C1_Init();
- * so between the USER CODE like using the given i2c handle "hi2c1" with default values:
- *
- * /\* USER CODE BEGIN 2 *\/
- *
- * si5351_inst_t si5351_inst;
- * si5351_initialize(&hi2c1);
- * /\* USER CODE END 2 *\/
- *
- * The 7-bit device (slave) address of the Si5351 consist of a 6-bit fixed
- * address plus a user selectable LSB bit as shown in Figure 6 of the datasheet.
- * The LSB bit is selectable as 0 or 1 using the optional A0 pin which is useful
- * for applications that require more than one Si5351 on a single I2C bus.
- * Only the Si5351A 20-QFN and Si5351A 16-QFN have the A0 LSB pin option.
- * If a part does not have the A0 pin, the default address is 0x60 with
- * the A0 bit set to 0.
- *
- * So additionally you may drive more then one Si5351 at the same or
- * another I2C bus when calling the init function with the given
- * I2C bus address (default: 0x60)
- * ( - internally this I2C address is shifted to the left
- * for the proper usage of the I2C HAL driver i.e. it becomes 0xC0 )
- *
- * Example:
- * \\ Define handlers for the sum of three SI5351 clock generators:
- * si5351_HandleTypeDef h_si5351[3];
- *
- * // 1st SI5351 chip and with an A0 = 0:
- * h_si5351[0] = si5351_init(&hi2c1, 25000000, 0x60);
- * // 2nd SI5351 chip on the same I2C bus "hi2c1" but address line A0 = 1
- * h_si5351[1] = si5351_init(&hi2c1, 27000000, 0x61);
- * // 3rd SI5351 chip on another IC2 bus with handle "hi2c2" *\/
- * h_si5351[2] = si5351_init(&hi2c2, 25000000, 0x60);
- *
- * PROs:
- * The library is preemptive and can be used within an operating system.
- * All structures and used variables are dynamically allocated.
- * CONs:
- * Not fully tested, but used with FreeRTOS and with 2 I2C bus systems.
- *
- * CHANGES:
- * - Removed several defines, enums, etc. from the header file, b/c we do not
- * want to export them to other programs
- *
- *
- * The old simple example to get 50 kHz output with an 25 MHz crystal:
- * Si5351_ConfigTypeDef Si5351_ConfigStruct;
- * Si5351_StructInit(&Si5351_ConfigStruct); //initialize the structure with default "safe" values
- * Si5351_ConfigStruct.OSC.OSC_XTAL_Load = XTAL_Load_8_pF; //use 8 pF load for crystal
- * Si5351_ConfigStruct.PLL[0].PLL_Clock_Source = PLL_Clock_Source_XTAL; //select xrystal as clock input for the PLL
- * Si5351_ConfigStruct.PLL[0].PLL_Multiplier_Integer = 32; //multiply the clock frequency by 32, this gets us 800 MHz clock
- * Si5351_ConfigStruct.MS[0].MS_Clock_Source = MS_Clock_Source_PLLA; //select PLLA as clock source for MultiSynth 0
- * Si5351_ConfigStruct.MS[0].MS_Divider_Integer = 250; //divide the 800 MHz by 250 this gets us 3.2 MHz
- * Si5351_ConfigStruct.CLK[0].CLK_R_Div = CLK_R_Div64; //divide the MultiSynth output by 64, this gets us 50 kHz
- * Si5351_ConfigStruct.CLK[0].CLK_Enable = ON; //turn on the output
- * Si5351_Init(&Si5351_ConfigStruct); //apply the changes
- *
- ******************************************************************************/
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef _STM32_SI5351_H_
-#define _STM32_SI5351_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Includes ------------------------------------------------------------------*/
-#include
-#include /* register map of the Si5351 */
-/* #include could also be included */
-/* Private includes ----------------------------------------------------------*/
-
-/* Exported types ------------------------------------------------------------*/
-/*!< Si5351 instance typedef -- handle as pointer */
-typedef struct __SI5351_HandleTypeDef *si5351_inst_t;
-
-/* Exported constants --------------------------------------------------------*/
-/** @enum errno_t Error Number Constants, @TODO you can also include
- */
-#if !defined _SYS_ERRNO_H_ && !defined __ERRNO_H__ && !defined __AT1_ERROR_NUMBERS__
-typedef enum {
- EPERM = 1, /*!< Operation not permitted */
- EIO = 5, /*!< I/O error */
- ENOMEM = 12, /*!< Out of memory */
- EFAULT = 14, /*!< Bad address */
- EBUSY = 16, /*!< Device or resource busy */
- ENODEV = 19, /*!< No such device */
- EINVAL = 22, /*!< Invalid argument */
- EADDRINUSE = 98,/*!< Address already in use */
- ETIMEDOUT = 116 /*!< Connection timed out */
-} si5351_errno_t;
-#endif
-
-typedef enum {
- SI5351_PLLA = 0,
- SI5351_PLLB
-}si5351_pll_t;
-
-/* Exported variables --------------------------------------------------------*/
-
-/* Exported macros -----------------------------------------------------------*/
-#define __SI5351__ 1
-#define __SI5351_MINOR__ 0
-#define __SI5351_PATCHLEVEL__ 1
-
-#define SI5351_VERSION (__SI5351__ * 10000 \
- + __SI5351_MINOR__ * 100 \
- + __SI5351_PATCHLEVEL__)
-
-#ifndef SI5351_DEFAULTS
-#define SI5351_DEFAULTS 1
-#define SI5351_NUMBER_OF_OUTPUTS 8
-#define SI5351_I2C_ADDRESS_DEFAULT 0x60
-#define SI5351_XTAL_DEFAULT 25000000u // default xtal in [Hz]
-#endif
-
-/* Exported functions --------------------------------------------------------*/
-si5351_inst_t si5351_init(void * i2c_handle, uint32_t xtal_frequency, uint8_t i2c_address, size_t datasize);
-int si5351_deinit(si5351_inst_t si5351_handle);
-int si5351_i2c_ready(si5351_inst_t inst);
-int si5351_program(si5351_inst_t inst);
-int si5351_enable_output(si5351_inst_t inst, uint8_t clk);
-int si5351_disable_output(si5351_inst_t inst, uint8_t clk);
-int si5351_set_clk0(si5351_inst_t inst, uint32_t frequency);
-int si5351_set_clk(si5351_inst_t inst, uint32_t frequency, uint8_t clk, si5351_pll_t pll);
-int si5351_set_clk_phase(si5351_inst_t inst, uint32_t frequency, double phase, uint8_t clk, si5351_pll_t pll);
-int si5351_set_phase(si5351_inst_t inst, uint8_t phase, uint8_t clk);
-
-#if SI5351_DEFAULTS
-si5351_inst_t si5351_initialize(void * i2c_handle);
-int si5351_deinitialize(void);
-#endif
-
-char * si5351_read_debug_msg(si5351_inst_t inst);
-char * si5351_read_register_debug(si5351_inst_t inst, char *buf, size_t bufsize, uint8_t regaddr);
-
-/* if initialized with data get a write and read functionality for data with: */
-int si5351_write_data(si5351_inst_t inst, void * data);
-int si5351_read_data(si5351_inst_t inst, void * data);
-
-int si5351_get_instance(si5351_inst_t *inst);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif // _STM32_SI5351_H_
diff --git a/Core/Src/main.c b/Core/Src/main.c
index d210853..2c94fbb 100644
--- a/Core/Src/main.c
+++ b/Core/Src/main.c
@@ -55,10 +55,10 @@ DMA_HandleTypeDef hdma_lpuart_rx;
RTC_HandleTypeDef hrtc;
-/* Definitions for defaultTask */
-osThreadId_t defaultTaskHandle;
-const osThreadAttr_t defaultTask_attributes = {
- .name = "defaultTask",
+/* Definitions for timeTask */
+osThreadId_t timeTaskHandle;
+const osThreadAttr_t timeTask_attributes = {
+ .name = "timeTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
@@ -106,7 +106,7 @@ static void MX_DMA_Init(void);
static void MX_I2C1_Init(void);
static void MX_LPUART1_UART_Init(void);
static void MX_RTC_Init(void);
-void StartDefaultTask(void *argument);
+void start_time_task(void *argument);
void start_terminal_task(void *argument);
void start_morse_task(void *argument);
void start_clk2_task(void *argument);
@@ -202,7 +202,7 @@ int main(void)
// ringbuf_test();
// 1st SI5351 chip at the I2C bus "hi2c1", address line A0 = 0
- si5351_inst = si5351_init(&hi2c1, 25000000, 0x60, 0);
+ si5351_inst = si5351_init(&hi2c1, 0x60, 25000000);
{
int ready = si5351_i2c_ready(si5351_inst);
printf("Si5351 device is %s.\n", (ready==1) ? "ready" : "N/A");
@@ -313,17 +313,17 @@ int main(void)
* 2 m RF power 1 W, MOE-MO5: 144.500, MO: 144.900 MHz, crossed dipole
*/
//si5351_set_clk0(si5351_inst, 3600000); /* MO FOX */
- // si5351_set_clk (si5351_inst, 3551000, 2, SI5351_PLLB);
- // si5351_set_clk (si5351_inst, 3551000, 2, SI5351_PLLB);
+ // si5351_set_clk (si5351_inst, 2, 3551000, SI5351_PLLB);
+ // si5351_set_clk (si5351_inst, 2, 3551000, SI5351_PLLB);
// si5351_set_clk0(si5351_inst, 3600000); /* MO FOX */
- // si5351_set_clk(si5351_inst, 99900000, 1, SI5351_PLLA);
+ // si5351_set_clk(si5351_inst, 1, 99900000, SI5351_PLLA);
// si5351_enable_output(si5351_inst,1);
// HAL_Delay(1000);
#if 0
si5351_set_clk0(si5351_inst, 3550000);
- si5351_set_clk(si5351_inst, 3570000, 2, SI5351_PLLB);
+ si5351_set_clk(si5351_inst, 2, 3570000, SI5351_PLLB);
si5351_enable_output(NULL,2);
#endif
@@ -356,8 +356,8 @@ int main(void)
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
- /* creation of defaultTask */
- defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
+ /* creation of timeTask */
+ timeTaskHandle = osThreadNew(start_time_task, NULL, &timeTask_attributes);
/* creation of terminalTask */
terminalTaskHandle = osThreadNew(start_terminal_task, NULL, &terminalTask_attributes);
@@ -966,14 +966,14 @@ void morse(si5351_inst_t inst, char * s, uint32_t delay, uint8_t clk) {
/* USER CODE END 4 */
-/* USER CODE BEGIN Header_StartDefaultTask */
+/* USER CODE BEGIN Header_start_time_task */
/**
- * @brief Function implementing the defaultTask thread.
+ * @brief Function implementing the timeTask thread.
* @param argument: Not used
* @retval None
*/
-/* USER CODE END Header_StartDefaultTask */
-void StartDefaultTask(void *argument)
+/* USER CODE END Header_start_time_task */
+void start_time_task(void *argument)
{
/* USER CODE BEGIN 5 */
(void) argument; //unused argument
@@ -1100,7 +1100,7 @@ void start_clk2_task(void *argument)
si5351_inst_t inst = argument;
int i = 0;
uint32_t morsedelay = delay;
- si5351_set_clk (inst, 3582000, clk, SI5351_PLLB);
+ si5351_set_clk (inst, clk, 3582000, SI5351_PLLB);
/* Infinite loop */
for (;;) {
diff --git a/Core/Src/si5351.c b/Core/Src/si5351.c
index f72acdd..2311418 100644
--- a/Core/Src/si5351.c
+++ b/Core/Src/si5351.c
@@ -1,889 +1,979 @@
-/*
- * si5351.c
+/**
+ ******************************************************************************
+ * @file stm32_si5351.c
+ * @brief STM32 library/driver for the Si5351 clock chip
+ from Skyworks Solutions, Inc. (former SiLabs)
+ ******************************************************************************
+ * @author: Thomas Kuschel KW4NZ
+ * created 2022-05-11
*
- * Created on: Jan 14, 2019
- * Author: Petr Polasek
+ * adapted, idea and some information from Petr Polasek
+ * thanks also to Aleksander Alekseev alias afiskon for his stm32 drivers
*
- * To make this library useable on any other device than
- * STM32Fxxx Cortex Mx, please edit these parts of the library:
- *
- * DEFINES:
- * SI5351_I2C_PERIPHERAL - the I2C peripheral name according
- * to your devices HAL library
- * I2C_TIMEOUT - time for the communication to time out
- *
- * TYPEDEFS:
- * Si5351_ConfigTypeDef - the I2Cx parameter should be changed
- * so that its type corresponds to your HAL library
- *
- * FUNCTIONS:
- * Si5351_WriteRegister
- * Si5351_ReadRegister
- * You need to write your own I2C handlers here
- *
- */
+ * A simple description can be found in the header file stm32_si5351.h, for
+ * more details read the supported README.md file
+ ******************************************************************************/
-//put your I2C HAL library name here
-//#include "stm32f10x_i2c.h"
+/* Defines for compilation ---------------------------------------------------*/
+/* if you want to automatically enable the clk output after setting synthesis */
+#define AUTOMATICALLY_ENABLE_OUTPUT 0
+/* enable some optimizing, usually set to 1 */
+#define OPTIMIZED 1
+
+/* Includes ------------------------------------------------------------------*/
+#include
+#include
+#ifdef DEBUG
+#define SI5351_DEBUG 0
+#include
+#endif
+
+#include
+/* including the HAL here */
#include "stm32l4xx_hal.h"
-
-#if 0
-
+/* Include the header file */
#include "si5351.h"
-int Si5351_WriteRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address, uint8_t reg_data)
-{
- uint32_t error_wait;
+/* Private typedef -----------------------------------------------------------*/
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
- {
- error_wait--;
- if (error_wait==0)
- {
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
- return 1;
- }
- }
- //wait for I2C to get ready, if not ready in time, reset I2C and return
+/* @brief Si5351 handle structure definition, private
+ */
+typedef struct __SI5351_HandleTypeDef {
+ void *i2c_handle; /*!< the I2C handle, must not be unique */
+ struct __SI5351_HandleTypeDef *next; /*!< next pointer to the following structure when there are several instances */
+ uint32_t xtal_frequency; /*!< XTAL or CLKIN frequency */
+#if SI5351_DEBUG
+ char debug_msg[1000]; /*!< debugging messages for extensive tests of the Si5351 chip, not required */
+#endif
+ uint8_t clk_is_pllb; /*!< assignment of PLLA or PLLB per CLK #, if bit set to 1 ...PLLB */
+ uint8_t clk_is_disabled; /*!< assignment of Output Enable Control, app. Register 3 */
+ uint8_t clk_has_phase_shift;/*!< assignment of an output with a phase shift offset */
+ uint8_t i2c_address; /*!< I2C address of the datasheet */
+ uint8_t interrupt_status_mask; /*!< Reg 2: Interrupt Status Mask */
+ uint8_t initialized :1; /*!< mark the driver initialized */
+ uint8_t programmed :1; /*!< mark the chip is programmed */
+} si5351_HandleTypeDef;
- I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
- //send START condition
+/* @brief SI5351 synthesis settings */
+typedef struct {
+ uint32_t pll_multiplier; /*!< in datasheet this value corresponds to feedback multisynth (N) a */
+ uint32_t pll_numerator; /*!< in datasheet this value corresponds to feedback multisynth (N) b */
+ uint32_t pll_denominator; /*!< in datasheet this value corresponds to feedback multisynth (N) c */
+ uint32_t out_multiplier; /*!< in datasheet this value corresponds to multisynth (M) a */
+ uint32_t out_numerator; /*!< in datasheet this value corresponds to multisynth (M) b */
+ uint32_t out_denominator; /*!< in datasheet this value corresponds to multisynth (M) c */
+ uint8_t out_r_divider; /*!< R divider, log2 value bit set to 1; 2,4,8,...,128; for frequencies < 500 kHz, otherwise set to 1 i.e. bit 0 */
+} synthesis_t;
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for START to be sent, if not sent in time, return
+typedef struct {
+ char band[4];
+ uint32_t qrg_min;
+ uint32_t qrg_max;
+ uint32_t multiplier;
+ uint8_t divider_bit;
+} band_t;
- I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Transmitter);
- //send address+RW bit
+/* Private define ------------------------------------------------------------*/
+#define SI5351_FREQUENCY_MIN 8000u
+#define SI5351_FREQUENCY_MAX 160000000u
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for address to be sent, if not sent in time, return
+#define _80M_BAND_MIN 3000000u
+#define _80M_BAND_MAX 4500000u
+#define _80M_BAND_OUT_MULT 200u
+#define _80M_BAND_R_DIV 0u /* 2^0 value */
- I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_address);
- //send reg address
+/* Private macro -------------------------------------------------------------*/
+#ifdef __arm__
+#ifdef __ARM_BIG_ENDIAN
+#define for_endian(size) for (int i = 0; i < size; ++i)
+#define last_loop_endian (i==size-1)
+#else
+#define for_endian(size) for (int i = size - 1; i >= 0; --i)
+#define last_loop_endian (i==0)
+#endif
+#else
+#error "Endianness not detected or another compiler"
+#endif
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for reg address to be sent
+#ifndef __GNUC__ /*__arm__*/ /* Keil ARM compiler does not support typeof */
+#define snprintb(buf, n, value) __snprintb(buf, n, (uint32_t *)&value, sizeof(uint32_t))
+#define snprintb16(buf, n, value) __snprintb(buf, n, (uint16_t *)&value, sizeof(uint16_t))
+#define snprintb8(buf, n, value) __snprintb(buf, n, (uint8_t *)&value, sizeof(uint8_t))
+#else /* for gcc, clang compilers */
+#define snprintb(buf, n, value) \
+({ \
+ typeof(value) _v = value; \
+ __snprintb(buf, n, (typeof(_v) *) &_v, sizeof(_v)); \
+})
+#endif
- I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_data);
- //send reg data
+/* Private variables ---------------------------------------------------------*/
+si5351_HandleTypeDef *first_handle = NULL; /* pointer to the first instance */
+int si5351_errno = 0; /* error_number for functions with return == NULL */
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for data to be sent, if not sent in time, return
+/* Private function prototypes -----------------------------------------------*/
+#ifdef DEBUG
+int __fprintb(FILE *stream, void *value, size_t size);
+#endif
+int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth);
+int si5351_set_pll(si5351_inst_t inst, synthesis_t *synth);
+int si5351_set_output(si5351_inst_t inst, synthesis_t *synth);
+int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk);
- I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
- //generate STOP condition
+/* Private functions ---------------------------------------------------------*/
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait until STOP is cleared
+/** Wrapper functions for receiving/transceiving bytes from I2C bus (HAL function set)
+ */
- return 0;
-}
-
-uint8_t Si5351_ReadRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address)
-{
- uint32_t error_wait;
-
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
- {
- error_wait--;
- if (error_wait==0)
- {
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
- return 1;
- }
- }
- //wait for I2C to get ready, if not ready in time, reset I2C and return
-
- I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
- //send START condition
-
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for START to be sent, if not sent in time, return
-
- I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Transmitter);
- //send address+RW bit
-
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for address to be sent, if not sent in time, return
-
- I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_address);
- //send reg address
-
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for reg address to be sent
-
- I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
- //generate STOP condition
-
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait until STOP is cleared
-
-
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
- {
- error_wait--;
- if (error_wait==0)
- {
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
- I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
- return 1;
- }
- }
- //wait for I2C to get ready, if not ready in time, reset I2C and return
-
- I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
- //send START condition
-
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for START to be sent, if not sent in time, return
-
- I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Receiver);
- //send address+RW bit
-
- error_wait = I2C_TIMEOUT;
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for address to be sent, if not sent in time, return
-
- while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR)
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait for data
-
- uint8_t reg_data;
- reg_data = I2C_ReceiveData(Si5351_ConfigStruct->I2Cx);
- //receive reg data
-
- I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
- //generate STOP condition
-
- error_wait = I2C_TIMEOUT;
- while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
- {
- error_wait--;
- if (error_wait==0) return 1;
- }
- //wait until STOP is cleared
-
- return reg_data;
-}
-
-//set safe values in the config structure
-void Si5351_StructInit(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint8_t i;
-
- Si5351_ConfigStruct->HW_I2C_Address = SI5351_I2C_ADDRESS;
- Si5351_ConfigStruct->I2Cx = SI5351_I2C_PERIPHERAL;
-
- Si5351_ConfigStruct->f_CLKIN = SI5351_CLKIN_FREQ;
- Si5351_ConfigStruct->f_XTAL = SI5351_XTAL_FREQ;
-
- Si5351_ConfigStruct->Interrupt_Mask_CLKIN = ON;
- Si5351_ConfigStruct->Interrupt_Mask_PLLA = ON;
- Si5351_ConfigStruct->Interrupt_Mask_PLLB = ON;
- Si5351_ConfigStruct->Interrupt_Mask_SysInit = ON;
- Si5351_ConfigStruct->Interrupt_Mask_XTAL = ON;
-
- Si5351_ConfigStruct->Fanout_CLKIN_EN = ON;
- Si5351_ConfigStruct->Fanout_MS_EN = ON;
- Si5351_ConfigStruct->Fanout_XO_EN = ON;
-
- Si5351_ConfigStruct->OSC.CLKIN_Div = CLKINDiv_Div1;
- Si5351_ConfigStruct->OSC.OSC_XTAL_Load = XTAL_Load_10_pF;
- Si5351_ConfigStruct->OSC.VCXO_Pull_Range_ppm = 0; //maybe should be set to 30 ppm, not clear from the AN-619
-
- for (i=0; i<=1; i++)
- {
- Si5351_ConfigStruct->PLL[i].PLL_Clock_Source = PLL_Clock_Source_XTAL;
- Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Integer = 32; //range 24..36 for 25 MHz clock
- Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Numerator = 0; //range 0..1048575
- Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Denominator = 1; //range 1..1048575
- Si5351_ConfigStruct->PLL[i].PLL_Capacitive_Load = PLL_Capacitive_Load_0; //select 0, unless you want to tune the PLL to <200 MHZ
- }
-
- Si5351_ConfigStruct->SS.SS_Amplitude_ppm = 0; //1.5% modulation = 15000
- Si5351_ConfigStruct->SS.SS_Enable = OFF;
- Si5351_ConfigStruct->SS.SS_Mode = SS_Mode_CenterSpread;
- Si5351_ConfigStruct->SS.SS_NCLK = SS_NCLK_0; //default value, this parameter is unexplained in documentation
-
- for (i=0; i<=7; i++)
- {
- Si5351_ConfigStruct->MS[i].MS_Clock_Source = MS_Clock_Source_PLLA;
- Si5351_ConfigStruct->MS[i].MS_Divider_Integer = 4;
- Si5351_ConfigStruct->MS[i].MS_Divider_Numerator = 0;
- Si5351_ConfigStruct->MS[i].MS_Divider_Denominator = 1;
-
- Si5351_ConfigStruct->CLK[i].CLK_Clock_Source = CLK_Clock_Source_MS_Own;
- Si5351_ConfigStruct->CLK[i].CLK_Disable_State = CLK_Disable_State_HIGH_Z;
- Si5351_ConfigStruct->CLK[i].CLK_Enable = OFF;
- Si5351_ConfigStruct->CLK[i].CLK_I_Drv = CLK_I_Drv_8mA;
- Si5351_ConfigStruct->CLK[i].CLK_Invert = OFF;
- Si5351_ConfigStruct->CLK[i].CLK_PowerDown = OFF;
- Si5351_ConfigStruct->CLK[i].CLK_QuarterPeriod_Offset = 0;
- Si5351_ConfigStruct->CLK[i].CLK_R_Div = CLK_R_Div1;
- Si5351_ConfigStruct->CLK[i].CLK_Use_OEB_Pin = OFF;
+/** @brief Give better error numbers based on the Linux error_no.h
+ * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
+ * @param xtal_frequency either the XTAL frequency (25/27 MHz) or CLock-In
+ * from 10 MHz to 100 MHz entered in Hz
+ * @param i2c_address I2C bus address of the device from datasheet typically 0x60 (or 0x61)
+ * @param datasize reserve an extra area of data space in bytes, access with function si5351_read_data() and si5351_write_data()
+ * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
+ */
+static int si5351_error_status_i2c(HAL_StatusTypeDef status) {
+ switch (status) {
+ case HAL_TIMEOUT:
+ return -ETIMEDOUT;
+ break;
+ case HAL_ERROR:
+ return -EIO;
+ break;
+ case HAL_BUSY:
+ return -EBUSY;
+ break;
+ case HAL_OK:
+ default:
+ return 0;
}
}
-void Si5351_OSCConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint8_t tmp;
- uint32_t VCXO_Param;
+/** @brief Read (blocking mode) one ore more data bytes from the I2C bus starting at the regaddr register address
+ * @param instance instance (handle) of the SI5351 driver
+ * @param regaddr starting register address of the SI5351
+ * @param data pointer to the first byte of data
+ * @param datasize datasize of the reading data, sizeof (data), when reading only one register (byte), set to 1
+ * @return errno 0 on success, see si5351_errno_t
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ * if applicable use interrupt controlled HAL functions
+ */
+int si5351_read(si5351_inst_t instance, uint8_t regaddr, uint8_t *data,
+ uint16_t size) {
- //set XTAL capacitive load and PLL VCO load capacitance
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_XTAL_CL);
- tmp &= ~(XTAL_CL_MASK | PLL_CL_MASK);
- tmp |= (XTAL_CL_MASK & (Si5351_ConfigStruct->OSC.OSC_XTAL_Load)) | (PLL_CL_MASK & ((Si5351_ConfigStruct->PLL[0].PLL_Capacitive_Load) << 1)) | (PLL_CL_MASK & ((Si5351_ConfigStruct->PLL[1].PLL_Capacitive_Load) << 4));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_XTAL_CL, tmp);
+ HAL_StatusTypeDef status;
+ status = HAL_I2C_Mem_Read(instance->i2c_handle, instance->i2c_address,
+ (uint16_t) regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff);
+ return si5351_error_status_i2c(status);
+}
- //set CLKIN pre-divider
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLKIN_DIV);
- tmp &= ~CLKIN_MASK;
- tmp |= CLKIN_MASK & Si5351_ConfigStruct->OSC.CLKIN_Div;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLKIN_DIV, tmp);
+/** @brief Write (blocking mode) one ore more data bytes to the I2C bus starting at the regaddr register address
+ * @param instance instance (handle) of the SI5351 driver
+ * @param regaddr starting register address of the SI5351
+ * @param data pointer to the first byte of data
+ * @param datasize datasize of the writing data, sizeof (data), when writing to one register (byte), set to 1
+ * @return errno 0 on success, see si5351_errno_t
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ * if applicable use interrupt controlled HAL functions
+ */
+int si5351_write(si5351_inst_t instance, uint8_t regaddr, uint8_t *data,
+ uint16_t size) {
- //set fanout of XO, MS0, MS4 and CLKIN - should be always on unless you
- //need to reduce power consumption
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FANOUT_EN);
- tmp &= ~(FANOUT_CLKIN_EN_MASK | FANOUT_MS_EN_MASK | FANOUT_XO_EN_MASK);
- if (Si5351_ConfigStruct->Fanout_CLKIN_EN == ON) tmp |= FANOUT_CLKIN_EN_MASK;
- if (Si5351_ConfigStruct->Fanout_MS_EN == ON) tmp |= FANOUT_MS_EN_MASK;
- if (Si5351_ConfigStruct->Fanout_XO_EN == ON) tmp |= FANOUT_XO_EN_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_FANOUT_EN, tmp);
+ HAL_StatusTypeDef status;
+ status = HAL_I2C_Mem_Write(instance->i2c_handle, instance->i2c_address,
+ (uint16_t) regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff);
+ return si5351_error_status_i2c(status);
+}
- //if "b" in PLLB set to 10^6, set VCXO parameter
- if (Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Denominator == 1000000)
- {
- VCXO_Param = VCXO_PARAM_MASK & (uint32_t)
- ((103 * Si5351_ConfigStruct->OSC.VCXO_Pull_Range_ppm
- * ((uint64_t)128000000 * Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Integer +
- Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Numerator))/100000000);
+/** @brief Check if there is any I2C device ready on the bus
+ * @param si5351_instance Given si5351 device handle
+ * @return 1 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_i2c_ready(si5351_inst_t inst) {
+
+ HAL_StatusTypeDef status;
+
+ if (!inst && !(inst = first_handle))
+ return -EINVAL;
+
+ /* call HAL function for device ready check */
+ status = HAL_I2C_IsDeviceReady(inst->i2c_handle, inst->i2c_address, 3,
+ 100 /*HAL_MAX_DELAY*/); // HAL_MAX_DELAY is blocking, use 100 ms
+ /* maybe create a pointer to that function for more flexiblity using other tools as HAL */
+ return (status == HAL_OK) ? 1 : si5351_error_status_i2c(status);
+}
+
+/* End of wrapper functions receiving/transceiving bytes */
+
+/** @brief Wrapper of init with default values: xtal set to 25 MHz, i2c_address set to 0x60
+ * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
+ * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
+ */
+si5351_HandleTypeDef* si5351_initialize(void *i2c_handle) {
+
+ return si5351_init(i2c_handle, SI5351_I2C_ADDR_DEFAULT, SI5351_XTAL_DEFAULT);
+}
+
+/** @brief Initialize the device Si5351 with the main parameters
+ * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
+ * @param i2c_address I2C bus address of the device from datasheet typically 0x60 (or 0x61)
+ * @param xtal_frequency either the XTAL frequency (25/27 MHz) or CLock-In
+ * from 10 MHz to 100 MHz entered in Hz
+ * @param datasize reserve an extra area of data space in bytes, access with function si5351_read_data() and si5351_write_data()
+ * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
+ */
+si5351_HandleTypeDef* si5351_init(void *i2c_handle, uint8_t i2c_address,
+ uint32_t xtal_frequency) {
+
+ si5351_HandleTypeDef *si5351_handle, *handle;
+ /* xtal frequency from 25000000 upto 27000000, clkin frequ range from 10000000 to 100000000 Hz */
+ /* i2c_address range up to 0x7F */
+ if (!i2c_handle || xtal_frequency < 10000000 || xtal_frequency > 100000000
+ || i2c_address > 0x7f) {
+ si5351_errno = EINVAL;
+ return NULL;
+ }
+ si5351_handle = calloc(1, sizeof(*si5351_handle));
+ if (si5351_handle == NULL) {
+ si5351_errno = ENOMEM; // cannot allocate any memory
+ return NULL;
+ }
+ /* shift the given address to HAL conformity (shift one bit left) */
+ i2c_address <<= 1;
+
+ /* check if there is already a handle with same i2c_handle and i2c_address */
+ if (first_handle == NULL) {
+ first_handle = si5351_handle;
} else {
- VCXO_Param = 0;
- }
-
- tmp = (uint8_t) VCXO_Param;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_0_7, tmp);
- tmp = (uint8_t)(VCXO_Param>>8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_8_15, tmp);
- tmp = (uint8_t)((VCXO_Param>>16) & VCXO_PARAM_16_21_MASK);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_16_21, tmp);
-}
-
-EnableState Si5351_CheckStatusBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
-{
- uint8_t tmp;
-
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STATUS);
- tmp &= StatusBit;
- return tmp;
-}
-
-EnableState Si5351_CheckStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
-{
- uint8_t tmp;
-
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STICKY);
- tmp &= StatusBit;
- return tmp;
-}
-
-void Si5351_InterruptConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint8_t tmp;
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_INT_MASK);
-
- tmp &= ~INT_MASK_LOS_XTAL_MASK;
- if (Si5351_ConfigStruct->Interrupt_Mask_XTAL == ON)
- {
- tmp |= INT_MASK_LOS_XTAL_MASK;
- }
-
- tmp &= ~INT_MASK_LOS_CLKIN_MASK;
- if (Si5351_ConfigStruct->Interrupt_Mask_CLKIN == ON)
- {
- tmp |= INT_MASK_LOS_CLKIN_MASK;
- }
-
- tmp &= ~INT_MASK_LOL_A_MASK;
- if (Si5351_ConfigStruct->Interrupt_Mask_PLLA == ON)
- {
- tmp |= INT_MASK_LOL_A_MASK;
- }
-
- tmp &= ~INT_MASK_LOL_B_MASK;
- if (Si5351_ConfigStruct->Interrupt_Mask_PLLB == ON)
- {
- tmp |= INT_MASK_LOL_B_MASK;
- }
-
- tmp &= ~INT_MASK_SYS_INIT_MASK;
- if (Si5351_ConfigStruct->Interrupt_Mask_SysInit == ON)
- {
- tmp |= INT_MASK_SYS_INIT_MASK;
- }
-
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_INT_MASK, tmp);
-}
-
-void Si5351_ClearStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
-{
- uint8_t tmp;
-
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STICKY);
- tmp &= ~StatusBit;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_DEV_STICKY, tmp);
-}
-
-void Si5351_PLLConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel)
-{
- uint8_t tmp, tmp_mask;
- uint32_t MSN_P1, MSN_P2, MSN_P3;
-
- //set PLL clock source
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_CLOCK_SOURCE);
- tmp_mask = PLLA_CLOCK_SOURCE_MASK << PLL_Channel;
- tmp &= ~tmp_mask;
- tmp |= tmp_mask & Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Clock_Source;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_CLOCK_SOURCE, tmp);
-
- //if new multiplier not even integer, disable the integer mode
- if ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator != 0) | ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer & 0x01) != 0 ))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel);
- tmp &= ~FB_INT_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel, tmp);
- }
-
- //configure the PLL multiplier
- MSN_P1 = 128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer + ((128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator) / Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator) - 512;
- MSN_P2 = 128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator - Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator * ((128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator) / Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator);
- MSN_P3 = Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator;
-
- tmp = (uint8_t) MSN_P1;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_0_7 + 8 * PLL_Channel, tmp);
- tmp = (uint8_t) (MSN_P1 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_8_15 + 8 * PLL_Channel, tmp);
- tmp = (uint8_t) (MSN_P1_16_17_MASK & (MSN_P1 >> 16));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_16_17 + 8 * PLL_Channel, tmp);
-
- tmp = (uint8_t) MSN_P2;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_0_7 + 8 * PLL_Channel, tmp);
- tmp = (uint8_t) (MSN_P2 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_8_15 + 8 * PLL_Channel, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MSN_P2_16_19);
- tmp &= ~MSN_P2_16_19_MASK;
- tmp |= (uint8_t) (MSN_P2_16_19_MASK & (MSN_P2 >> 16));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_16_19 + 8 * PLL_Channel, tmp);
-
- tmp = (uint8_t) MSN_P3;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_0_7 + 8 * PLL_Channel, tmp);
- tmp = (uint8_t) (MSN_P3 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_8_15 + 8 * PLL_Channel, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MSN_P3_16_19);
- tmp &= ~MSN_P3_16_19_MASK;
- tmp |= (uint8_t) (MSN_P3_16_19_MASK & ((MSN_P3 >> 16) << 4));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_16_19 + 8 * PLL_Channel, tmp);
-
- //if new multiplier is an even integer, enable integer mode
- if ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator == 0) & ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer & 0x01) == 0 ))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel);
- tmp |= FB_INT_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel, tmp);
- }
-}
-
-void Si5351_PLLReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel)
-{
- uint8_t tmp;
-
- //reset PLL
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_RESET);
- if (PLL_Channel == PLL_A)
- {
- tmp |= PLLA_RESET_MASK;
- } else {
- tmp |= PLLB_RESET_MASK;
- }
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_RESET, tmp);
-}
-
-void Si5351_PLLSimultaneousReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint8_t tmp;
-
- //reset PLL
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_RESET);
- tmp |= PLLA_RESET_MASK | PLLB_RESET_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_RESET, tmp);
-}
-
-void Si5351_SSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint8_t tmp;
- uint32_t SSUDP, SSUP_P1, SSUP_P2, SSUP_P3, SSDN_P1, SSDN_P2, SSDN_P3;
- uint64_t SSDN, SSUP;
-
- //turn off SS if it should be disabled
- if ((Si5351_ConfigStruct->SS.SS_Enable == OFF)|
- (((Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer & 0x01) == 0)
- & (Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator == 0)) )
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_EN);
- tmp &= ~SSC_EN_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_EN, tmp);
- }
-
- //set default value of SS_NCLK - spread spectrum reserved register
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SS_NCLK);
- tmp &= ~SS_NCLK_MASK;
- tmp |= SS_NCLK_MASK & (Si5351_ConfigStruct->SS.SS_NCLK);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SS_NCLK, tmp);
-
- //set SS mode
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_MODE);
- tmp &= ~SSC_MODE_MASK;
- tmp |= SSC_MODE_MASK & Si5351_ConfigStruct->SS.SS_Mode;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_MODE, tmp);
-
- //set SSUDP parameter
- if (Si5351_ConfigStruct->PLL[0].PLL_Clock_Source == PLL_Clock_Source_CLKIN)
- {
- SSUDP = (Si5351_ConfigStruct->f_CLKIN)/(4*31500);
- } else {
- SSUDP = (Si5351_ConfigStruct->f_XTAL)/(4*31500);
- }
-
- //set SSUDP parameter
- tmp = (uint8_t) SSUDP;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUDP_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUDP_8_11);
- tmp &= ~SSUDP_8_11_MASK;
- tmp |= (uint8_t) (SSUDP_8_11_MASK & ((SSUDP >> 8) << 4));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUDP_8_11, tmp);
-
- //calculate SSUP and SSDN parameters
- if (Si5351_ConfigStruct->SS.SS_Mode == SS_Mode_CenterSpread)
- {
- SSUP = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
- + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
- ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
- ) / ((1000000 - Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
-
- SSDN = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
- + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
- ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
- ) / ((1000000 + Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
-
- SSUP_P1 = (uint32_t) (SSUP/1000000);
- SSUP_P2 = (uint32_t)(32767*(SSUP/1000000-SSUP_P1));
- SSUP_P3 = 0x7FFF;
-
- } else {
-
- SSDN = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
- + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
- ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
- ) / ((1000000 + Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
-
- SSUP_P1 = 0;
- SSUP_P2 = 0;
- SSUP_P3 = 1;
-
- }
-
- //set SSDN parameter
- SSDN_P1 = (uint32_t) (SSDN/1000000);
- SSDN_P2 = (uint32_t)(32767*(SSDN/1000000-SSDN_P1));
- SSDN_P3 = 0x7FFF;
-
- //write SSUP parameter P1
- tmp = (uint8_t) SSUP_P1;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P1_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P1_8_11);
- tmp &= ~SSUP_P1_8_11_MASK;
- tmp |= (uint8_t)(SSUP_P1_8_11_MASK & (SSUP_P1 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P1_8_11, tmp);
-
- //write SSUP parameter P2
- tmp = (uint8_t) SSUP_P2;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P2_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P2_8_14);
- tmp &= ~SSUP_P2_8_14_MASK;
- tmp |= (uint8_t)(SSUP_P2_8_14_MASK & (SSUP_P2 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P2_8_14, tmp);
-
- //write SSUP parameter P3
- tmp = (uint8_t) SSUP_P3;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P3_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P3_8_14);
- tmp &= ~SSUP_P3_8_14_MASK;
- tmp |= (uint8_t)(SSUP_P3_8_14_MASK & (SSUP_P3 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P3_8_14, tmp);
-
- //write SSDN parameter P1
- tmp = (uint8_t) SSDN_P1;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P1_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P1_8_11);
- tmp &= ~SSDN_P1_8_11_MASK;
- tmp |= (uint8_t)(SSDN_P1_8_11_MASK & (SSDN_P1 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P1_8_11, tmp);
-
- //write SSDN parameter P2
- tmp = (uint8_t) SSDN_P2;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P2_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P2_8_14);
- tmp &= ~SSDN_P2_8_14_MASK;
- tmp |= (uint8_t)(SSDN_P2_8_14_MASK & (SSDN_P2 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P2_8_14, tmp);
-
- //write SSDN parameter P3
- tmp = (uint8_t) SSDN_P3;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P3_0_7, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P3_8_14);
- tmp &= ~SSDN_P3_8_14_MASK;
- tmp |= (uint8_t)(SSDN_P3_8_14_MASK & (SSDN_P3 >> 8));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P3_8_14, tmp);
-
- //turn on SS if it should be enabled
- if ((Si5351_ConfigStruct->SS.SS_Enable == ON)
- & (((Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer & 0x01) != 0)
- | (Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator != 0)))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_EN);
- tmp |= SSC_EN_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_EN, tmp);
- }
-}
-
-void Si5351_MSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_MSChannelTypeDef MS_Channel)
-{
- uint8_t tmp;
- uint32_t MS_P1, MS_P2, MS_P3;
-
- //configure MultiSynth clock source
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_SRC + MS_Channel);
- tmp &= ~MS_SRC_MASK;
- if (Si5351_ConfigStruct->MS[MS_Channel].MS_Clock_Source == MS_Clock_Source_PLLB)
- {
- tmp |= MS_SRC_MASK;
- }
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_SRC + MS_Channel, tmp);
-
- if (MS_Channel <= MS5) //configuration is simpler for MS6 and 7 since they are integer-only
- {
- //if next value not in even integer mode or if divider is not equal to 4, disable DIVBY4
- if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer != 4)|(Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator != 0))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel);
- tmp &= ~MS_DIVBY4_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel, tmp);
- }
-
- //if next value not in even integer mode or SS enabled, disable integer mode
- if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator != 0)|((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer & 0x01) != 0)|(Si5351_ConfigStruct->SS.SS_Enable == ON))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel);
- tmp &= ~MS_INT_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel, tmp);
- }
-
- //set new divider value
- MS_P1 = 128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer
- + ((128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator) / Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator)
- - 512;
- MS_P2 = 128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator
- - Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator
- * ((128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator) / Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator);
- MS_P3 = Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator;
-
- tmp = (uint8_t) MS_P1;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_0_7 + 8 * MS_Channel, tmp);
- tmp = (uint8_t) (MS_P1 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_8_15 + 8 * MS_Channel, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P1_16_17);
- tmp &= ~MS_P1_16_17_MASK;
- tmp |= (uint8_t) (MS_P1_16_17_MASK & (MS_P1 >> 16));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_16_17 + 8 * MS_Channel, tmp);
-
- tmp = (uint8_t) MS_P2;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_0_7 + 8 * MS_Channel, tmp);
- tmp = (uint8_t) (MS_P2 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_8_15 + 8 * MS_Channel, tmp);
- Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P2_16_19 + 8 * MS_Channel);
- tmp &= ~MS_P2_16_19_MASK;
- tmp |= (uint8_t) (MS_P2_16_19_MASK & (MS_P2 >> 16));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_16_19 + 8 * MS_Channel, tmp);
-
- tmp = (uint8_t) MS_P3;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_0_7 + 8 * MS_Channel, tmp);
- tmp = (uint8_t) (MS_P3 >> 8);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_8_15 + 8 * MS_Channel, tmp);
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P3_16_19 + 8 * MS_Channel);
- tmp &= ~MS_P3_16_19_MASK;
- tmp |= (uint8_t) (MS_P3_16_19_MASK & ((MS_P3 >> 16) << 4));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_16_19 + 8 * MS_Channel, tmp);
-
- //if next value is even integer and SS not enabled, enable integer mode
- if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator == 0) & ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer & 0x01) == 0) & (Si5351_ConfigStruct->SS.SS_Enable == OFF))
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel);
- tmp |= MS_INT_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel, tmp);
-
- //if next value in integer mode and if divider is equal to 4, enable DIVBY4
- if (Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer == 4)
- {
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel);
- tmp |= MS_DIVBY4_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel, tmp);
+ handle = first_handle;
+ while (handle) {
+ if ((handle->i2c_address == i2c_address)
+ && (handle->i2c_handle == i2c_handle)) {
+ /* the device is already set up, we have to free the memory, exit with NULL */
+ free(si5351_handle);
+ si5351_errno = EADDRINUSE; /* handle address already in use */
+ return NULL;
}
+ if (handle->next == NULL) {
+ handle->next = si5351_handle;
+ break;
+ }
+ handle = handle->next;
}
+ }
+ si5351_handle->i2c_handle = i2c_handle;
+ si5351_handle->i2c_address = i2c_address;
+ si5351_handle->xtal_frequency = xtal_frequency;
+ /* disable all interrupts (Si5351C only) */
+ si5351_handle->interrupt_status_mask = SI5351_SYS_INIT_MASK
+ | SI5351_LOL_B_MASK | SI5351_LOL_A_MASK | SI5351_LOS_CLKIN_MASK
+ | SI5351_LOS_XTAL_MASK;
+
+ si5351_handle->initialized = 1;
+ return si5351_handle;
+}
+
+/** @brief Deinitialize one device Si5351
+ * @param si5351_handle Given si5351 handle for freeing
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ENODEV if device handle not found
+ */
+int si5351_deinit(si5351_HandleTypeDef *si5351_handle) {
+
+ si5351_HandleTypeDef *handle;
+
+ if (si5351_handle == NULL)
+ return -EINVAL;
+
+ si5351_handle->initialized = 0;
+ //before freeing switch the pointer to the next item
+ if (si5351_handle == first_handle) {
+ first_handle = si5351_handle->next;
} else {
- //configure divider of Multisynth 6 and 7
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS67_P1 + (MS_Channel - MS6), (uint8_t)(Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer));
- //can be only even integers between 6 and 254, inclusive
- }
-}
-
-void Si5351_CLKPowerCmd(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel)
-{
- uint8_t tmp, tmp_mask;
-
- //set CLK disable state
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_DIS_STATE + (CLK_Channel >> 2)); //increment the address by 1 if CLKx>=CLK4
- tmp_mask = CLK_DIS_STATE_MASK << ((CLK_Channel & 0x03)<<1); //shift the mask according to the selected channel
- tmp &= ~tmp_mask;
- tmp |= tmp_mask & ((Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Disable_State) << ((CLK_Channel & 0x03)<<1));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_DIS_STATE + (CLK_Channel >> 2), tmp);
-
- //set OEB pin
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_OEB);
- tmp_mask = 1 << CLK_Channel;
- tmp &= ~tmp_mask;
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Use_OEB_Pin == OFF)
- {
- tmp |= tmp_mask;
- }
-
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Enable == OFF) //disable clock
- {
- //power down the clock
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_EN);
- tmp |= 1 << CLK_Channel;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_EN, tmp);
- }
-
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_PowerDown == ON) //power down clock
- {
- //power down output driver
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel);
- tmp |= CLK_PDN_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel, tmp);
- }
-
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_PowerDown == OFF) //power up clock
- {
- //power up output driver
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel);
- tmp &= ~CLK_PDN_MASK;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel, tmp);
- }
-
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Enable == ON) //enable clock
- {
- //power up the clock
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_EN);
- tmp &= ~(1 << CLK_Channel);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_EN, tmp);
- }
-}
-
-void Si5351_CLKConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel)
-{
- uint8_t tmp, tmp_mask;
-
- //set CLK source clock
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_SRC + CLK_Channel);
- tmp &= ~CLK_SRC_MASK;
- tmp |= CLK_SRC_MASK & Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Clock_Source;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_SRC + CLK_Channel, tmp);
-
- //set CLK inversion
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_INV + CLK_Channel);
- tmp &= ~CLK_INV_MASK;
- if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Invert == ON)
- {
- tmp |= CLK_INV_MASK;
- }
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_INV + CLK_Channel, tmp);
-
- //set CLK current drive
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_IDRV + CLK_Channel);
- tmp &= ~CLK_IDRV_MASK;
- tmp |= CLK_IDRV_MASK & Si5351_ConfigStruct->CLK[CLK_Channel].CLK_I_Drv;
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_IDRV + CLK_Channel, tmp);
-
- if (CLK_Channel <= CLK5) //CLK6 and 7 are integer only, which causes several limitations
- {
- //set CLK phase offset
- tmp = CLK_PHOFF_MASK & (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_QuarterPeriod_Offset);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PHOFF + CLK_Channel, tmp);
- //set Rx divider
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_R_DIV + CLK_Channel * CLK_R_DIV_STEP);
- tmp &= ~CLK_R_DIV_MASK;
- tmp |= CLK_R_DIV_MASK & (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_R_Div);
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_R_DIV + CLK_Channel * CLK_R_DIV_STEP, tmp);
- } else {
- //CLK6 and CLK7 have no fractional mode, so they lack the phase offset function
-
- //set Rx divider
- tmp_mask = CLK_R67_DIV_MASK << ((CLK_Channel-CLK6) << 2); //shift mask left by 4 if CLK7
- tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_R67_DIV);
- tmp &= ~tmp_mask;
- tmp |= tmp_mask & ((Si5351_ConfigStruct->CLK[CLK_Channel].CLK_R_Div >> 4) << ((CLK_Channel-CLK6) << 2));
- Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_R67_DIV, tmp);
- }
-}
-
-int Si5351_Init(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
-{
- uint32_t timeout = SI5351_TIMEOUT;
- uint8_t i;
-
- //wait for the 5351 to initialize
- while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_SysInit))
- {
- timeout--;
- if (timeout==0) return 1; //return 1 if initialization timed out
- }
-
- //configure oscillator, fanout, interrupts, VCXO
- Si5351_OSCConfig(Si5351_ConfigStruct);
- Si5351_InterruptConfig(Si5351_ConfigStruct);
-
- //configure PLLs
- for (i=PLL_A; i<=PLL_B; i++)
- {
- Si5351_PLLConfig(Si5351_ConfigStruct, i);
- Si5351_PLLReset(Si5351_ConfigStruct, i);
- }
-
- //configure Spread Spectrum
- Si5351_SSConfig(Si5351_ConfigStruct);
-
- //Configure Multisynths
- for (i=MS0; i<=MS7; i++)
- {
- Si5351_MSConfig(Si5351_ConfigStruct, i);
- }
-
- //configure outputs
- for (i=CLK0; i<=CLK7; i++)
- {
- Si5351_CLKConfig(Si5351_ConfigStruct, i);
- }
-
- //wait for PLLs to lock
- while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_SysInit | StatusBit_PLLA | StatusBit_PLLB))
- {
- timeout--;
- if (timeout==0) return 1; //return 1 if problem with any PLL
- }
-
- //clear sticky bits
- Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_SysInit | StatusBit_PLLA | StatusBit_PLLB);
-
- if (Si5351_ConfigStruct->f_CLKIN != 0) //if CLKIN used, check it as well
- {
- while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_CLKIN))
- {
- timeout--;
- if (timeout==0) return 1; //return 1 if initialization timed out
+ handle = first_handle;
+ while (handle) {
+ if (handle->next == si5351_handle) {
+ handle->next = si5351_handle->next;
+ break;
+ }
+ handle = handle->next;
}
- //clear CLKIN sticky bit
- Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_CLKIN);
+ if (NULL == handle)
+ return -ENODEV; // no such device
}
-
- if (Si5351_ConfigStruct->f_XTAL != 0) //if XTAL used, check it as well
- {
- while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_XTAL))
- {
- timeout--;
- if (timeout==0) return 1; //return 1 if initialization timed out
- }
- //clear XTAL sticky bit
- Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_XTAL);
- }
-
- //power on or off the outputs
- for (i=CLK0; i<=CLK7; i++)
- {
- Si5351_CLKPowerCmd(Si5351_ConfigStruct, i);
- }
-
+ free(si5351_handle);
return 0;
}
+
+/** @brief Deinitialize the first Si5351 device (wrapper function), for simplification
+ * @return 0 on success
+ * @retval -ENODEV if device handle not found
+ */
+int si5351_deinitialize(void) {
+
+ if (!first_handle)
+ return -ENODEV;
+ return si5351_deinit(first_handle);
+}
+
+/** @brief Program the si5351 with the already set values according to Figure 10 of the datasheet
+ * @param si5351_instance Given si5351 device handle
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_program(si5351_inst_t inst) {
+
+ uint8_t data;
+ int status = 0;
+#if SI5351_DEBUG
+ int cx = 0;
+#endif
+ if (!inst && !(inst = first_handle))
+ return -EINVAL;
+
+ do {
+ status = si5351_read(inst, SI5351_DEVICE_STATUS, &data, 1);
+ if (status)
+ return status;
+
+ } while (data & SI5351_SYS_INIT);
+
+ do {
+ /* Disable Outputs Set CLKx_DIS high, Reg. 3 = 0xFF */
+ data = SI5351_CLK7_OEB | SI5351_CLK6_OEB | SI5351_CLK5_OEB
+ | SI5351_CLK4_OEB | SI5351_CLK3_OEB | SI5351_CLK2_OEB
+ | SI5351_CLK1_OEB | SI5351_CLK0_OEB;
+ status = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL, &data, 1);
+ if (status)
+ break;
+ inst->clk_is_disabled = data;
+
+ /* power down all output drivers reg 16 -- 23 */
+ data = SI5351_CLK0_PDN; // 0x80
+#if OPTIMIZED
+ status = si5351_write(inst, SI5351_CLK0_CONTROL, &data,
+ SI5351_NUMBER_OF_OUTPUTS);
+ if (status)
+ break;
+#else
+ for(int i = SI5351_CLK0_CONTROL; i <= SI5351_CLK7_CONTROL; i++) {
+ status = si5351_write(inst, i, &data, 1);
+
+ if (status)
+ break;
+ }
+#endif
+ /* set interrupt masks (see register 2 description) */
+ status = si5351_write(inst, SI5351_INTERRUPT_STATUS_MASK,
+ &inst->interrupt_status_mask, 1);
+ if (status)
+ break;
+ /* for debugging purpose, read out fanout enable register */
+#if SI5351_DEBUG
+ status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1);
+ if (status)
+ break;
+ cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data);
+ cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data);
+#endif
+ data = SI5351_CLKIN_FANOUT_EN | SI5351_XO_FANOUT_EN
+ | SI5351_MS_FANOUT_EN; // set them to 1b
+ status = si5351_write(inst, SI5351_FANOUT_ENABLE, &data, 1);
+ if (status)
+ break;
+#if SI5351_DEBUG
+ status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1);
+ if (status)
+ break;
+ cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data);
+ cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data);
+#endif
+ /* Crystal Internal Load Capacitance */
+
+#if SI5351_DEBUG
+ data = SI5351_XTAL_CL_10_PF | SI5351_XTAL_RESERVED;
+ status = si5351_write(inst, SI5351_CRYSTAL_INTERNAL_LOAD_CAPACITANCE, &data, 1);
+ if (status)
+ break;
+ cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) XTAL int. Load Cap: 0x%x (%dd)\n", cx, data, data);
+#endif
+ inst->programmed = 1; /* the device is programmed */
+ } while (0);
+
+ return status;
+}
+
+/** @brief Select the right band to calculate even divisors for the output
+ * @param frequency
+ * @param band pointer of band_t return parameters as a structure min, max, divisor for each band
+ * @return 1 on success, when a frequency band is found
+ * @retval 0 when frequency not found
+ */
+int band_select(uint32_t frequency, band_t *band) {
+
+ static const band_t sband[] = { /* band in meters, frequ_lo, frequ_hi, multiplier, divider_bit */
+ { "80", 3000000, 4500000, 200, 0 }, { "40", 5625000, 7500000, 120, 0 }, {
+ "30", 7500000, 11250000, 80, 0 },
+ { "20", 11250000, 15000000, 60, 0 }, { "15", 15000000, 22500000, 40,
+ 0 }, { "10", 22500000, 32142000, 28, 0 }, { "8", 32142000,
+ 45000000, 20, 0 }, { "6", 45000000, 64285000, 14, 0 }, {
+ "4", 64285000, 76000000, 10, 0 }, { "3", 76000000, 11250000,
+ 8, 0 }, { "2", 11250000, 15000000, 6, 0 }, { "180", 1500000,
+ 2250000, 400, 0 }, { "120", 2250000, 3000000, 300, 0 }, {
+ "60", 4500000, 5625000, 160, 0 }, };
+
+ for (uint32_t i = 0; i < (sizeof(sband) / sizeof(sband[0])); i++) {
+ if (frequency > sband[i].qrg_min && frequency <= sband[i].qrg_max) {
+ memcpy(band, &sband[i], sizeof(*band));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** @brief Calculate all parameters for the synthesis of the si5351
+ * @param frequency selected
+ * @param xtal the crystal driving the Si5351
+ * @param synth pointer to a structure snythesis_t will be set with the calculated values
+ * @return 0 on success
+ */
+int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth) {
+
+ uint32_t t;
+ band_t band;
+
+ if (frequency < SI5351_FREQUENCY_MIN || frequency > SI5351_FREQUENCY_MAX)
+ return -EINVAL;
+
+ /* e.g. 80-m-Band 3.5 -- 4.0 MHz */
+ if (band_select(frequency, &band)) {
+ synth->out_r_divider = band.divider_bit;
+ synth->out_multiplier = band.multiplier;
+ synth->out_numerator = 0;
+ synth->out_denominator = 1;
+ synth->pll_multiplier = (band.multiplier * frequency) / xtal;
+ // t = (xtal >> 20) + 1;
+ t = xtal / 1000000; // even divider and 1 Hz grid
+ synth->pll_numerator = (band.multiplier * frequency) % xtal;
+ synth->pll_numerator /= t;
+ synth->pll_denominator = xtal / t;
+ return 0;
+ }
+ synth->out_r_divider = 0;
+ if (frequency < 81000000) {
+ // Valid for frequ in 0.5..112.5 MHz range 9000
+ // However an error is > 6 Hz above 81 MHz
+ // synth->pll_multiplier = 36; // PLL runs @ 900 MHz with XTAL 25 MHz, more flexible using the formular:
+ // making an even integer multiplier with
+ synth->pll_multiplier = (900000000 / xtal);
+ synth->pll_multiplier &= ~0x01u; // make it even
+ synth->pll_numerator = 0;
+ synth->pll_denominator = 1;
+ uint32_t Fpll = synth->pll_multiplier * xtal; // this was set to 900000000
+ synth->out_multiplier = Fpll / frequency;
+ t = (frequency >> 20) + 1;
+ synth->out_numerator = (Fpll % frequency) / t;
+ synth->out_denominator = frequency / t;
+ } else {
+ // Valid for Fclk in 75..160 MHz range
+ if (frequency >= 150000000) {
+ synth->out_multiplier = 4;
+ } else if (frequency >= 100000000) {
+ synth->out_multiplier = 6;
+ } else {
+ synth->out_multiplier = 8;
+ }
+ synth->out_numerator = 0;
+ synth->out_denominator = 1;
+
+ uint32_t numerator = synth->out_multiplier * frequency;
+ synth->pll_multiplier = numerator / xtal;
+ t = (xtal >> 20) + 1;
+ synth->pll_numerator = (numerator % xtal) / t;
+ synth->pll_denominator = xtal / t;
+ }
+ return 0;
+}
+
+/** @brief Enables the CLK output of the si5351 (after programming and setting the synthesis)
+ * @param si5351_instance Given si5351 device handle
+ * @param clk The CLK ouput to drive and enable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_enable_output(si5351_inst_t inst, uint8_t clk) {
+
+ int rv = 0;
+
+ if ((!inst && !(inst = first_handle)) || clk > 7)
+ return -EINVAL;
+
+ // check if this clock is already enabled
+ if (inst->clk_is_disabled & (1 << clk)) {
+ inst->clk_is_disabled &= (uint8_t) ~(1 << clk); // clear the bit
+ rv = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL,
+ &inst->clk_is_disabled, 1);
+ }
+ return rv;
+}
+
+/** @brief Disables the CLK output of the si5351 (after programming and setting the synthesis)
+ * @param si5351_instance Given si5351 device handle
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_disable_output(si5351_inst_t inst, uint8_t clk) {
+
+ int rv = 0;
+
+ if ((!inst && !(inst = first_handle)) || clk > 7)
+ return -EINVAL;
+
+ if (!(inst->clk_is_disabled & (1 << clk))) {
+ inst->clk_is_disabled |= (uint8_t) (1 << clk); // set the bit
+ rv = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL,
+ &inst->clk_is_disabled, 1);
+ }
+ return rv;
+}
+
+/** @brief Sets the CLK_0 output of the si5351 (simplified API)
+ * @param si5351_instance Given si5351 device handle
+ * @param frequency
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_set_clk0(si5351_inst_t inst, uint32_t frequency) {
+
+ int rv = 0;
+ synthesis_t synth;
+
+ if (!inst && !(inst = first_handle))
+ return -EINVAL;
+
+ do {
+ if (!inst->programmed) {
+ rv = si5351_program(inst);
+ if (rv)
+ break;
+ }
+ (void) calculation(frequency, inst->xtal_frequency, &synth);
+ rv = si5351_set_synthesis(inst, &synth, 0);
+ } while (0);
+ return rv;
+}
+
+/** @brief Sets the CLK_x output of the si5351
+ * @param si5351_instance Given si5351 device handle
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @param frequency
+ * @param pll the used PLL, either PLLA or PLLB
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_set_clk(si5351_inst_t inst, uint8_t clk, uint32_t frequency,
+ si5351_pll_t pll) {
+
+ int rv = 0;
+ synthesis_t synth;
+
+ if ((!inst && !(inst = first_handle)) || clk > 7)
+ return -EINVAL;
+
+ if (!pll)
+ inst->clk_is_pllb &= (uint8_t) ~(1u << clk); /* delete the bit */
+ else
+ inst->clk_is_pllb |= (uint8_t) (1u << clk); /* set the bit */
+
+ do {
+ if (!inst->programmed) {
+ rv = si5351_program(inst);
+ if (rv)
+ break;
+ }
+ (void) calculation(frequency, inst->xtal_frequency, &synth);
+ rv = si5351_set_synthesis(inst, &synth, clk);
+ } while (0);
+ return rv;
+}
+
+/** @brief Sets the CLK_x output of the si5351 with phase
+ * @param si5351_instance Given si5351 device handle
+ * @param frequency
+ * @param phase in degree as double value
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @param pll the used PLL, either PLLA or PLLB
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_set_clk_phase(si5351_inst_t inst, uint8_t clk, uint32_t frequency,
+ double phase, si5351_pll_t pll) {
+
+ int rv = 0;
+ synthesis_t synth;
+
+ if ((!inst && !(inst = first_handle)) || clk > 7)
+ return -EINVAL;
+
+ if (!pll)
+ inst->clk_is_pllb &= (uint8_t) ~(1u << clk); /* delete the bit */
+ else
+ inst->clk_is_pllb |= (uint8_t) (1u << clk); /* set the bit */
+
+ do {
+
+ if (!inst->programmed) {
+ rv = si5351_program(inst);
+ if (rv)
+ break;
+ }
+ /* calculate the phase */
+ if (phase > 0.0) {
+ inst->clk_has_phase_shift |= (uint8_t) (1u << clk); /* set phase shift */
+ double pll_frequency, phaseoff;
+ //pll_frequency = ((double)synth.pll_multiplier + (double)(synth.pll_numerator / synth.pll_denominator)) * (double)inst->xtal_frequency;
+ //phaseoff = 4.0 * pll_frequency;
+ (void) phaseoff;
+ (void) pll_frequency;
+
+ } else {
+ inst->clk_has_phase_shift &= (uint8_t) ~(1u << clk); /* reset phase shift */
+ }
+ uint8_t ph = (uint8_t) phase;
+ rv = si5351_write(inst, SI5351_CLK0_INITIAL_PHASE_OFFSET + clk, &ph, 1);
+
+ (void) calculation(frequency, inst->xtal_frequency, &synth);
+ rv = si5351_set_synthesis(inst, &synth, clk);
+ } while (0);
+ return rv;
+
+}
+
+/** @brief Resets the PLL of the si5351 (direct access to register)
+ * @param si5351_instance Given si5351 device handle
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_reset_pll(si5351_inst_t inst, uint8_t clk) {
+
+ /* internal function, no need to check inst nor clk */
+
+ uint8_t reset;
+
+#if 1
+ reset = (inst->clk_is_pllb & (1 << clk)) ?
+ SI5351_PLLB_RST : SI5351_PLLA_RST;
+#else
+ (void)clk;
+ reset = SI5351_PLL_RESET_VALUE;
+#endif
+ return si5351_write(inst, SI5351_PLL_RESET, &reset, 1);
+}
+
+/** @brief Sets the CLK_x output phase of the si5351 (direct access to register)
+ * @param si5351_instance Given si5351 device handle
+ * @param phase in uint8_t type value
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_set_phase(si5351_inst_t inst, uint8_t clk, uint8_t phase) {
+
+ int rv = 0;
+
+ if ((!inst && !(inst = first_handle)) || clk > 5 || phase > CLKx_PHOFF)
+ return -EINVAL;
+
+ /* if (phase) {
+ si5351_write(inst, )
+ }
+ */
+ rv = si5351_write(inst, SI5351_CLK0_INITIAL_PHASE_OFFSET + clk, &phase, 1);
+
+ si5351_reset_pll(inst, clk);
+
+ return rv;
+}
+
+/** @brief Sets the MSNx and MSx parameter registers of the Si5351
+ * @param si5351_instance Given si5351 device handle
+ * @param synth synthesis_t struct
+ * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
+ * @return 0 on success
+ * @retval -EINVAL when given a NULL handle
+ * @retval -ETIMEDOUT when HAL_TIMEOUT
+ * @retval -EIO when HAL_ERROR
+ * @retval -EBUSY when HAL_BUSY
+ */
+int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk) {
+
+ uint32_t MSNx_P1, MSNx_P2, MSNx_P3;
+ uint32_t MSx_P1, MSx_P2, MSx_P3;
+ int rv = 0;
+ uint8_t ms_data[8];
+ uint8_t regaddr;
+ uint8_t divby4 = 0;
+ uint8_t MSx_INT = 0;
+ uint8_t MSx_SRC = 0;
+
+ if (!inst && !(inst = first_handle))
+ return -EINVAL;
+
+ assert(clk < 8);
+ assert(synth->out_r_divider < 8); /* the divider is stored in 3 bits and the value is 2^out_r_divider */
+ assert(
+ synth->pll_multiplier >= 4u && synth->pll_multiplier <= 2048u
+ && synth->pll_multiplier != 5u
+ && synth->pll_multiplier != 7u);
+ assert(synth->pll_denominator != 0u && synth->pll_denominator < (1u << 20)); // MSNx_P3 maximum of 1048575
+ assert(
+ synth->out_multiplier >= 4u && synth->out_multiplier <= 2048u
+ && synth->out_multiplier != 5u
+ && synth->out_multiplier != 7u);
+ assert(synth->out_denominator != 0u && synth->out_denominator < (1u << 20));
+
+ /* PLL registers */
+ MSNx_P1 = 128 * synth->pll_multiplier
+ + 128 * synth->pll_numerator / synth->pll_denominator - 512;
+ // MSNx_P2 = 128 * synth->pll_numerator - synth->pll_denominator * (128 * synth->pll_numerator / synth->pll_denominator);
+ MSNx_P2 = (128 * synth->pll_numerator) % synth->pll_denominator;
+ MSNx_P3 = synth->pll_denominator;
+
+ assert(MSNx_P1 < (1u << 18));
+ assert(MSNx_P2 < (1u << 20));
+
+ /* OUTPUT (M) registers */
+ MSx_P1 = 128 * synth->out_multiplier
+ + 128 * synth->out_numerator / synth->out_denominator - 512;
+ // MSx_P2 = 128 * synth->out_numerator - synth->out_denominator * (128 * synth->out_numerator / synth->out_denominator);
+ MSx_P2 = (128 * synth->out_numerator) % synth->out_denominator;
+ MSx_P3 = synth->out_denominator;
+ if (synth->out_r_divider == 2) /* 2^2 = 4, see 4.1.3 in AN619 special case */
+ divby4 = 0x03;
+
+ assert(MSx_P1 < (1u << 18));
+ assert(MSx_P2 < (1u << 20));
+
+ /* distribute these registers to 8 bytes of the SI5351 */
+ /* start with MSNx values */
+ ms_data[0] = (uint8_t) (MSNx_P3 >> 8);
+ ms_data[1] = (uint8_t) MSNx_P3;
+ ms_data[2] = (uint8_t) ((MSNx_P1 >> 16) & 0x03);
+ ms_data[3] = (uint8_t) (MSNx_P1 >> 8);
+ ms_data[4] = (uint8_t) MSNx_P1;
+ ms_data[5] = (uint8_t) ((((MSNx_P3 >> 16) & 0x0F) << 4)
+ | ((MSNx_P2 >> 16) & 0x0F));
+ ms_data[6] = (uint8_t) (MSNx_P2 >> 8);
+ ms_data[7] = (uint8_t) MSNx_P2;
+ /* write MSNx registers dependent of SI5351_PLLA or SI5351_PLLB */
+ regaddr =
+ (inst->clk_is_pllb & (1u << clk)) ?
+ SI5351_MULTISYNTH_NB_PARAMETER_3_HI :
+ SI5351_MULTISYNTH_NA_PARAMETER_3_HI;
+ rv = si5351_write(inst, regaddr, ms_data,
+ sizeof(ms_data) / sizeof(ms_data[0]));
+ if (rv)
+ return rv;
+ /* write MSx registers dependent of CLK # */
+ ms_data[0] = (uint8_t) (MSx_P3 >> 8);
+ ms_data[1] = (uint8_t) MSx_P3;
+ ms_data[2] = (uint8_t) (((MSx_P1 >> 16) & 0x03)
+ | ((synth->out_r_divider & 0x07) << 4) | (divby4 << 2));
+ ms_data[3] = (uint8_t) (MSx_P1 >> 8);
+ ms_data[4] = (uint8_t) MSx_P1;
+ ms_data[5] = (uint8_t) ((((MSx_P3 >> 16) & 0x0F) << 4)
+ | ((MSx_P2 >> 16) & 0x0F));
+ ms_data[6] = (uint8_t) (MSx_P2 >> 8);
+ ms_data[7] = (uint8_t) MSx_P2;
+ regaddr = SI5351_MULTISYNTH0_PARAMETER_3_HI
+ + (uint8_t) (clk * sizeof(ms_data) / sizeof(ms_data[0]));
+ rv = si5351_write(inst, regaddr, ms_data,
+ sizeof(ms_data) / sizeof(ms_data[0]));
+ if (rv)
+ return rv;
+
+ MSx_INT = ((synth->out_numerator == 0)
+ && ((synth->out_multiplier & 0x01) == 0)
+ && !(inst->clk_has_phase_shift & (1 << clk)));
+ MSx_INT = 0;
+
+ MSx_SRC = !!(inst->clk_is_pllb & (1 << clk));
+
+ ms_data[0] = (uint8_t) (MSx_INT << 6 | MSx_SRC << 5 | SI5351_CLK_SRC_MS0
+ | SI5351_CLK_2_MA); //SI5351_CLK_6_MA; //SI5351_CLK_4_MA;
+ regaddr = SI5351_CLK0_CONTROL + clk;
+ rv = si5351_write(inst, regaddr, ms_data, 1);
+ if (rv)
+ return rv;
+
+ rv = si5351_reset_pll(inst, clk);
+ if (rv)
+ return rv;
+
+#if AUTOMATICALLY_ENABLE_OUTPUT
+ rv = si5351_enable_output(inst, clk);
+#endif
+
+ return rv;
+}
+
+#if SI5351_DEBUG
+/** @brief With this function, you can read the debug message for test purposes
+ * @param si5351_instance Given si5351 device handle
+ * @return * char as message for printing
+ * @retval NULL when not found
+ */
+char * si5351_read_debug_msg(si5351_inst_t inst) {
+
+ if (!inst && !(inst=first_handle))
+ return NULL;
+
+ return inst->debug_msg;
+}
#endif
+
+#ifdef DEBUG
+/*!
+ * @brief Output the value in binary representation and in groups of
+ * bytes
+ *
+ * @param buf string of char * to output to
+ * @param n amount of characters to output to
+ * @param value Value to output in binary representation
+ * @param size
+ * @return bytes written (int)
+ */
+int __snprintb(char *buf, size_t n, void *value, size_t size) {
+ uint8_t byte;
+ size_t blen = sizeof(byte) * 8;
+ uint8_t bits[blen + 1];
+ int cx = 0;
+
+ bits[blen] = '\0';
+ for_endian((int)size)
+ {
+ byte = ((uint8_t*) value)[i];
+ memset(bits, '0', blen);
+ for (int j = 0; byte && j < (int) blen; ++j) {
+ if (byte & 0x80)
+ bits[j] = '1';
+ byte <<= 1;
+ }
+ cx += snprintf(buf + cx, n - (size_t) cx, "%s%s", bits,
+ (last_loop_endian) ? "" : " ");
+ }
+ return cx;
+}
+
+/** @brief Function to read any register with binary and hex representation
+ * @param si5351_instance Given si5351 device handle
+ * @param buffer for printing typically 33 bytes char
+ * @param size of the buffer for printing typically 33 bytes
+ * @param register address
+ * @return * char as message for printing
+ * @retval NULL when not found
+ */
+char* si5351_read_register_debug(si5351_inst_t inst, char *buf, size_t bufsize,
+ uint8_t regaddr) {
+
+ uint8_t data;
+ int status;
+ int cx;
+
+ if (!inst)
+ return NULL;
+
+ status = si5351_read(inst, regaddr, &data, 1);
+ if (status)
+ return NULL;
+
+ cx = snprintf(buf, bufsize, "R%03u[0x%02x]=%3u[0x%02x][0b", regaddr,
+ regaddr, data, data);
+ cx += snprintb(buf + cx, bufsize - (size_t )cx, data);
+ cx += snprintf(buf + cx, bufsize - (size_t) cx, "]");
+ return buf;
+}
+#endif //DEBUG
+
+/* some additional functions */
+
+/** @brief Function to get the handle of a si5351 device instance
+ * @param si5351_instance Given a pointer to a si5351 device handle or
+ * a pointer to NULL for getting the first handle.
+ * @return si5351_inst_t handle
+ * @retval NULL when not found
+ * @warning The first given *inst must be set to NULL or set to a known handle
+ */
+int si5351_get_instance(si5351_inst_t *inst) {
+
+ si5351_inst_t tinst;
+ int rv = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ if (!(*inst))
+ *inst = first_handle;
+ else
+ *inst = (*inst)->next;
+
+ tinst = *inst;
+ while (tinst) {
+ rv++;
+ tinst = tinst->next;
+ }
+ return rv;
+}
+
+int si5351_get_i2c_address(si5351_inst_t inst) {
+ if (!inst && !(inst = first_handle))
+ return -EINVAL;
+
+ return inst->i2c_address;
+}
+
+void* si5351_get_i2c_handle(si5351_inst_t inst) {
+ if (!inst && !(inst = first_handle))
+ return NULL;
+
+ return (void*) inst->i2c_handle;
+}
+
diff --git a/Core/Src/stm32_si5351.c b/Core/Src/stm32_si5351.c
deleted file mode 100644
index ae4ff9d..0000000
--- a/Core/Src/stm32_si5351.c
+++ /dev/null
@@ -1,962 +0,0 @@
-/**
- ******************************************************************************
- * @file stm32_si5351.c
- * @brief STM32 library/driver for the Si5351 clock chip
- from Skyworks Solutions, Inc. (former SiLabs)
- ******************************************************************************
- * @author: Thomas Kuschel KW4NZ
- * created 2022-05-11
- *
- * adapted, idea and much information from Petr Polasek
- * thanks also to Aleksander Alekseev alias afiskon for his stm32 drivers
- *
- * A simple description can be found in the header file stm32_si5351.h, for
- * more details read the supported README.md file
- ******************************************************************************/
-
-/* Defines for compilation ---------------------------------------------------*/
-/* if you want to automatically enable the clk output after setting synthesis */
-#define AUTOMATICALLY_ENABLE_OUTPUT 0
-/* enable some optimizing, usually set to 1 */
-#define OPTIMIZED 1
-
-/* Includes ------------------------------------------------------------------*/
-#include
-#include
-#ifdef DEBUG
-#define SI5351_DEBUG 0
-#include
-#endif
-
-#include
-/* including the HAL here */
-#include "stm32l4xx_hal.h"
-/* Include the header file */
-#include "stm32_si5351.h"
-
-/* Private typedef -----------------------------------------------------------*/
-
-/* @brief Si5351 handle structure definition, private
-*/
-typedef struct __SI5351_HandleTypeDef {
- void * i2c_handle; /*!< the I2C handle, must not be unique */
- uint32_t xtal_frequency; /*!< XTAL or CLKIN frequency */
- void * data; /*!< data variable (dummy) for additional usage, set to NULL if not used */
- size_t datasize; /*!< size of data, set to 0 if not used */
- struct __SI5351_HandleTypeDef *next; /*!< next pointer to the following structure when there are several instances */
-#ifdef SI5351_DEBUG
- char debug_msg[1000]; /*!< debugging messages for extensive tests of the Si5351 chip, not required */
-#endif
- uint8_t clk_is_pllb; /*!< assignment of PLLA or PLLB per CLK #, if bit set to 1 ...PLLB */
- uint8_t clk_is_disabled; /*!< assignment of Output Enable Control, app. Register 3 */
- uint8_t clk_has_phase_shift; /*!< assignment of an output with a phase shift offset */
- uint8_t i2c_address; /*!< I2C address of the datasheet */
- uint8_t interrupt_status_mask; /*!< Reg 2: Interrupt Status Mask */
- uint8_t initialized:1; /*!< mark the driver initialized */
- uint8_t programmed:1; /*!< mark the chip is programmed */
-} si5351_HandleTypeDef;
-
-/* @brief SI5351 synthesis settings */
-typedef struct {
- uint32_t pll_multiplier; /*!< in datasheet this value corresponds to feedback multisynth (N) a */
- uint32_t pll_numerator; /*!< in datasheet this value corresponds to feedback multisynth (N) b */
- uint32_t pll_denominator; /*!< in datasheet this value corresponds to feedback multisynth (N) c */
- uint32_t out_multiplier; /*!< in datasheet this value corresponds to multisynth (M) a */
- uint32_t out_numerator; /*!< in datasheet this value corresponds to multisynth (M) b */
- uint32_t out_denominator; /*!< in datasheet this value corresponds to multisynth (M) c */
- uint8_t out_r_divider; /*!< R divider, log2 value bit set to 1; 2,4,8,...,128; for frequencies < 500 kHz, otherwise set to 1 i.e. bit 0 */
-} synthesis_t;
-
-typedef struct {
- char band[4];
- uint32_t qrg_min;
- uint32_t qrg_max;
- uint32_t multiplier;
- uint8_t divider_bit;
-} band_t;
-
-/* Private define ------------------------------------------------------------*/
-#define SI5351_FREQUENCY_MIN 8000u
-#define SI5351_FREQUENCY_MAX 160000000u
-
-
-#define _80M_BAND_MIN 3000000u
-#define _80M_BAND_MAX 4500000u
-#define _80M_BAND_OUT_MULT 200u
-#define _80M_BAND_R_DIV 0u /* 2^0 value */
-
-/* Private macro -------------------------------------------------------------*/
-#ifdef __arm__
-#ifdef __ARM_BIG_ENDIAN
-#define for_endian(size) for (int i = 0; i < size; ++i)
-#define last_loop_endian (i==size-1)
-#else
-#define for_endian(size) for (int i = size - 1; i >= 0; --i)
-#define last_loop_endian (i==0)
-#endif
-#else
-#error "Endianness not detected or another compiler"
-#endif
-
-#ifndef __GNUC__ /*__arm__*/ /* Keil ARM compiler does not support typeof */
-#define snprintb(buf, n, value) __snprintb(buf, n, (uint32_t *)&value, sizeof(uint32_t))
-#define snprintb16(buf, n, value) __snprintb(buf, n, (uint16_t *)&value, sizeof(uint16_t))
-#define snprintb8(buf, n, value) __snprintb(buf, n, (uint8_t *)&value, sizeof(uint8_t))
-#else /* for gcc, clang compilers */
-#define snprintb(buf, n, value) \
-({ \
- typeof(value) _v = value; \
- __snprintb(buf, n, (typeof(_v) *) &_v, sizeof(_v)); \
-})
-#endif
-
-/* Private variables ---------------------------------------------------------*/
-si5351_HandleTypeDef * first_handle = NULL; /* pointer to the first instance */
-int si5351_errno = 0; /* error_number for functions with return == NULL */
-
-/* Private function prototypes -----------------------------------------------*/
-#ifdef DEBUG
-int __fprintb(FILE *stream, void *value, size_t size);
-#endif
-int si5351_read(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, uint16_t size);
-int si5351_write(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, uint16_t size);
-int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth);
-int si5351_set_pll(si5351_inst_t inst, synthesis_t *synth);
-int si5351_set_output(si5351_inst_t inst, synthesis_t *synth);
-int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk);
-
-/* Private functions ---------------------------------------------------------*/
-
-/** Wrapper functions for receiving/transceiving bytes from I2C bus (HAL function set)
- */
-
-/** @brief Give better error numbers based on the Linux error_no.h
- * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
- * @param xtal_frequency either the XTAL frequency (25/27 MHz) or CLock-In
- * from 10 MHz to 100 MHz entered in Hz
- * @param i2c_address I2C bus address of the device from datasheet typically 0x60 (or 0x61)
- * @param datasize reserve an extra area of data space in bytes, access with function si5351_read_data() and si5351_write_data()
- * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
- */
-static int si5351_error_status_i2c(HAL_StatusTypeDef status) {
- switch (status) {
- case HAL_TIMEOUT: return -ETIMEDOUT; break;
- case HAL_ERROR: return -EIO; break;
- case HAL_BUSY: return -EBUSY; break;
- case HAL_OK:
- default: return 0;
- }
-}
-
-/** @brief Read (blocking mode) one ore more data bytes from the I2C bus starting at the regaddr register address
- * @param instance instance (handle) of the SI5351 driver
- * @param regaddr starting register address of the SI5351
- * @param data pointer to the first byte of data
- * @param datasize datasize of the reading data, sizeof (data), when reading only one register (byte), set to 1
- * @return errno 0 on success, see si5351_errno_t
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- * if applicable use interrupt controlled HAL functions
- */
-int si5351_read(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, uint16_t size) {
-
- HAL_StatusTypeDef status;
- status = HAL_I2C_Mem_Read(instance->i2c_handle, instance->i2c_address,
- (uint16_t)regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff);
- return si5351_error_status_i2c(status);
-}
-
-/** @brief Write (blocking mode) one ore more data bytes to the I2C bus starting at the regaddr register address
- * @param instance instance (handle) of the SI5351 driver
- * @param regaddr starting register address of the SI5351
- * @param data pointer to the first byte of data
- * @param datasize datasize of the writing data, sizeof (data), when writing to one register (byte), set to 1
- * @return errno 0 on success, see si5351_errno_t
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- * if applicable use interrupt controlled HAL functions
- */
-int si5351_write(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, uint16_t size) {
-
- HAL_StatusTypeDef status;
- status = HAL_I2C_Mem_Write(instance->i2c_handle, instance->i2c_address,
- (uint16_t)regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff);
- return si5351_error_status_i2c(status);
-}
-
-/** @brief Check if there is any I2C device ready on the bus
- * @param si5351_instance Given si5351 device handle
- * @return 1 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_i2c_ready(si5351_inst_t inst) {
-
- HAL_StatusTypeDef status;
-
- if(!inst && !(inst=first_handle))
- return -EINVAL;
-
- /* call HAL function for device ready check */
- status = HAL_I2C_IsDeviceReady(inst->i2c_handle, inst->i2c_address, 3, 100 /*HAL_MAX_DELAY*/ ); // HAL_MAX_DELAY is blocking, use 100 ms
- /* maybe create a pointer to that function for more flexiblity using other tools as HAL */
- return (status == HAL_OK) ? 1 : si5351_error_status_i2c(status);
-}
-
-/* End of wrapper functions receiving/transceiving bytes */
-
-
-/** @brief Wrapper of init with default values: xtal set to 25 MHz, i2c_address set to 0x60
- * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
- * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
- */
-si5351_HandleTypeDef *si5351_initialize(void * i2c_handle) {
-
- return si5351_init(i2c_handle, SI5351_XTAL_DEFAULT, SI5351_I2C_ADDRESS_DEFAULT, 0);
-}
-
-/** @brief Initialize the device Si5351 with the main parameters
- * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1
- * @param xtal_frequency either the XTAL frequency (25/27 MHz) or CLock-In
- * from 10 MHz to 100 MHz entered in Hz
- * @param i2c_address I2C bus address of the device from datasheet typically 0x60 (or 0x61)
- * @param datasize reserve an extra area of data space in bytes, access with function si5351_read_data() and si5351_write_data()
- * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno
- */
-si5351_HandleTypeDef *si5351_init(void * i2c_handle, uint32_t xtal_frequency, uint8_t i2c_address, size_t datasize) {
-
- si5351_HandleTypeDef *si5351_handle, *handle;
- /* xtal frequency from 25000000 upto 27000000, clkin frequ range from 10000000 to 100000000 Hz */
- /* i2c_address range up to 0x7F */
- if (!i2c_handle || xtal_frequency < 10000000 || xtal_frequency > 100000000 || i2c_address > 0x7f) {
- si5351_errno = EINVAL;
- return NULL;
- }
- si5351_handle = calloc(1, sizeof(*si5351_handle));
- if (si5351_handle == NULL) {
- si5351_errno = ENOMEM; // cannot allocate any memory
- return NULL;
- }
- /* shift the given address to HAL conformity (shift one bit left) */
- i2c_address <<= 1;
-
- /* check if there is already a handle with same i2c_handle and i2c_address */
- if (first_handle == NULL) {
- first_handle = si5351_handle;
- } else {
- handle = first_handle;
- while (handle) {
- if ((handle->i2c_address == i2c_address) && (handle->i2c_handle == i2c_handle)) {
- /* the device is already set up, we have to free the memory, exit with NULL */
- free(si5351_handle);
- si5351_errno = EADDRINUSE; /* handle address already in use */
- return NULL;
- }
- if (handle->next == NULL) {
- handle->next = si5351_handle;
- break;
- }
- handle = handle->next;
- }
- }
- si5351_handle->i2c_handle = i2c_handle;
- si5351_handle->i2c_address = i2c_address;
- si5351_handle->xtal_frequency = xtal_frequency;
- /* disable all interrupts (Si5351C only) */
- si5351_handle->interrupt_status_mask = SI5351_SYS_INIT_MASK | SI5351_LOL_B_MASK |
- SI5351_LOL_A_MASK | SI5351_LOS_CLKIN_MASK | SI5351_LOS_XTAL_MASK;
- /* now allocate some memory for the data area given */
- if (datasize > 0) {
- si5351_handle->data = calloc(1, datasize);
- if (si5351_handle->data == NULL) {
- si5351_errno = ENOMEM; /* cannot allocate memory for data */
- /* we just leave it to NULL, do nothing more with this behavior */
- }
- }
- si5351_handle->initialized = 1;
- return si5351_handle;
-}
-
-/** @brief Deinitialize one device Si5351
- * @param si5351_handle Given si5351 handle for freeing
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ENODEV if device handle not found
- */
-int si5351_deinit(si5351_HandleTypeDef * si5351_handle) {
-
- si5351_HandleTypeDef *handle;
-
- if (si5351_handle == NULL)
- return -EINVAL;
-
- si5351_handle->initialized = 0;
- //before freeing switch the pointer to the next item
- if (si5351_handle == first_handle) {
- first_handle = si5351_handle->next;
- } else {
- handle = first_handle;
- while (handle) {
- if (handle->next == si5351_handle) {
- handle->next = si5351_handle->next;
- break;
- }
- handle = handle->next;
- }
- if (NULL == handle)
- return -ENODEV; // no such device
-
- }
- if (si5351_handle->data) {
- free(si5351_handle->data);
- }
- free(si5351_handle);
- return 0;
-}
-
-/** @brief Deinitialize the first Si5351 device (wrapper function), for simplification
- * @return 0 on success
- * @retval -ENODEV if device handle not found
- */
-int si5351_deinitialize(void) {
-
- if (!first_handle)
- return -ENODEV;
- return si5351_deinit(first_handle);
-}
-
-
-/** @brief Program the si5351 with the already set values according to Figure 10 of the datasheet
- * @param si5351_instance Given si5351 device handle
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_program(si5351_inst_t inst) {
-
- uint8_t data;
- int status = 0;
-#if SI5351_DEBUG
- int cx = 0;
-#endif
- if(!inst && !(inst=first_handle))
- return -EINVAL;
-
- do {
- status = si5351_read(inst, SI5351_DEVICE_STATUS, &data, 1);
- if (status)
- return status;
-
- } while(data & SI5351_SYS_INIT);
-
- do {
- /* Disable Outputs Set CLKx_DIS high, Reg. 3 = 0xFF */
- data = SI5351_CLK7_OEB | SI5351_CLK6_OEB | SI5351_CLK5_OEB | SI5351_CLK4_OEB |
- SI5351_CLK3_OEB | SI5351_CLK2_OEB | SI5351_CLK1_OEB | SI5351_CLK0_OEB;
- status = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL, &data, 1);
- if (status)
- break;
- inst->clk_is_disabled = data;
-
- /* power down all output drivers reg 16 -- 23 */
- data = SI5351_CLK0_PDN; // 0x80
-#if OPTIMIZED
- status = si5351_write(inst, SI5351_CLK0_CONTROL, &data, SI5351_NUMBER_OF_OUTPUTS);
- if (status)
- break;
-#else
- for(int i = SI5351_CLK0_CONTROL; i <= SI5351_CLK7_CONTROL; i++) {
- status = si5351_write(inst, i, &data, 1);
-
- if (status)
- break;
- }
-#endif
- /* set interrupt masks (see register 2 description) */
- status = si5351_write(inst, SI5351_INTERRUPT_STATUS_MASK, &inst->interrupt_status_mask, 1);
- if (status)
- break;
- /* for debugging purpose, read out fanout enable register */
-#if SI5351_DEBUG
- status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1);
- if (status)
- break;
- cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data);
- cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data);
-#endif
- data = SI5351_CLKIN_FANOUT_EN | SI5351_XO_FANOUT_EN | SI5351_MS_FANOUT_EN; // set them to 1b
- status = si5351_write(inst, SI5351_FANOUT_ENABLE, &data, 1);
- if (status)
- break;
-#if SI5351_DEBUG
- status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1);
- if (status)
- break;
- cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data);
- cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data);
-#endif
- /* Crystal Internal Load Capacitance */
-
-#if SI5351_DEBUG
- data = SI5351_XTAL_CL_10_PF | SI5351_XTAL_RESERVED;
- status = si5351_write(inst, SI5351_CRYSTAL_INTERNAL_LOAD_CAPACITANCE, &data, 1);
- if (status)
- break;
- cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) XTAL int. Load Cap: 0x%x (%dd)\n", cx, data, data);
-#endif
- inst->programmed = 1; /* the device is programmed */
- } while(0);
-
- return status;
-}
-
-/** @brief Select the right band to calculate even divisors for the output
- * @param frequency
- * @param band pointer of band_t return parameters as a structure min, max, divisor for each band
- * @return 1 on success, when a frequency band is found
- * @retval 0 when frequency not found
- */
-int band_select(uint32_t frequency, band_t *band) {
-
- static const band_t sband[] = { /* band in metres, frequ_lo, frequ_hi, multiplier, divider_bit */
- { "80", 3000000, 4500000, 200, 0},
- { "40", 5625000, 7500000, 120, 0},
- { "30", 7500000,11250000, 80, 0},
- { "20",11250000,15000000, 60, 0},
- { "15",15000000,22500000, 40, 0},
- { "10",22500000,32142000, 28, 0},
- { "8",32142000,45000000, 20, 0},
- { "6",45000000,64285000, 14, 0},
- { "4",64285000,76000000, 10, 0},
- { "3",76000000,11250000, 8, 0},
- { "2",11250000,15000000, 6, 0},
- {"180", 1500000, 2250000, 400, 0},
- {"120", 2250000, 3000000, 300, 0},
- { "60", 4500000, 5625000, 160, 0},
- };
-
- for (uint32_t i = 0; i < (sizeof(sband) / sizeof(sband[0])); i++) {
- if (frequency > sband[i].qrg_min && frequency <= sband[i].qrg_max) {
- memcpy(band, &sband[i], sizeof(*band));
- return 1;
- }
- }
- return 0;
-}
-
-/** @brief Calculate all parameters for the synthesis of the si5351
- * @param frequency selected
- * @param xtal the crystal driving the Si5351
- * @param synth pointer to a structure snythesis_t will be set with the calculated values
- * @return 0 on success
- */
-int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth) {
-
- uint32_t t;
- band_t band;
-
- if (frequency < SI5351_FREQUENCY_MIN || frequency > SI5351_FREQUENCY_MAX)
- return -EINVAL;
-
- /* e.g. 80-m-Band 3.5 -- 4.0 MHz */
- if (band_select(frequency, &band)) {
- synth->out_r_divider = band.divider_bit;
- synth->out_multiplier = band.multiplier;
- synth->out_numerator = 0;
- synth->out_denominator = 1;
- synth->pll_multiplier = (band.multiplier * frequency) / xtal;
- // t = (xtal >> 20) + 1;
- t = xtal / 1000000; // even divider and 1 Hz grid
- synth->pll_numerator = (band.multiplier * frequency) % xtal;
- synth->pll_numerator /= t;
- synth->pll_denominator = xtal / t;
- return 0;
- }
- synth->out_r_divider = 0;
- if (frequency < 81000000) {
- // Valid for frequ in 0.5..112.5 MHz range 9000
- // However an error is > 6 Hz above 81 MHz
- // synth->pll_multiplier = 36; // PLL runs @ 900 MHz with XTAL 25 MHz, more flexible using the formular:
- // making an even integer multiplier with
- synth->pll_multiplier = (900000000 / xtal);
- synth->pll_multiplier &= ~0x01u; // make it even
- synth->pll_numerator = 0;
- synth->pll_denominator = 1;
- uint32_t Fpll = synth->pll_multiplier * xtal; // this was set to 900000000
- synth->out_multiplier = Fpll / frequency;
- t = (frequency >> 20) + 1;
- synth->out_numerator = (Fpll % frequency) / t;
- synth->out_denominator = frequency / t;
- } else {
- // Valid for Fclk in 75..160 MHz range
- if(frequency >= 150000000) {
- synth->out_multiplier = 4;
- } else if (frequency >= 100000000) {
- synth->out_multiplier = 6;
- } else {
- synth->out_multiplier = 8;
- }
- synth->out_numerator = 0;
- synth->out_denominator = 1;
-
- uint32_t numerator = synth->out_multiplier*frequency;
- synth->pll_multiplier = numerator / xtal;
- t = (xtal >> 20) + 1;
- synth->pll_numerator = (numerator % xtal) / t;
- synth->pll_denominator = xtal / t;
- }
- return 0;
-}
-
-/** @brief Enables the CLK output of the si5351 (after programming and setting the synthesis)
- * @param si5351_instance Given si5351 device handle
- * @param clk The CLK ouput to drive and enable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_enable_output(si5351_inst_t inst, uint8_t clk) {
-
- int rv = 0;
-
- if((!inst && !(inst=first_handle)) || clk > 7)
- return -EINVAL;
-
- // check if this clock is already enabled
- if (inst->clk_is_disabled & (1 << clk)) {
- inst->clk_is_disabled &= (uint8_t)~(1 << clk); // clear the bit
- rv = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL, &inst->clk_is_disabled, 1);
- }
- return rv;
-}
-
-/** @brief Disables the CLK output of the si5351 (after programming and setting the synthesis)
- * @param si5351_instance Given si5351 device handle
- * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_disable_output(si5351_inst_t inst, uint8_t clk) {
-
- int rv = 0;
-
- if((!inst && !(inst=first_handle)) || clk > 7)
- return -EINVAL;
-
- if (!(inst->clk_is_disabled & (1 << clk))) {
- inst->clk_is_disabled |= (uint8_t)(1 << clk); // set the bit
- rv = si5351_write(inst, SI5351_OUTPUT_ENABLE_CONTROL, &inst->clk_is_disabled, 1);
- }
- return rv;
-}
-
-/** @brief Sets the CLK_0 output of the si5351 (simplified API)
- * @param si5351_instance Given si5351 device handle
- * @param frequency
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_set_clk0(si5351_inst_t inst, uint32_t frequency) {
-
- int rv = 0;
- synthesis_t synth;
-
- if(!inst && !(inst=first_handle))
- return -EINVAL;
-
- do {
- if(!inst->programmed) {
- rv = si5351_program(inst);
- if (rv)
- break;
- }
- (void)calculation(frequency, inst->xtal_frequency, &synth);
- rv = si5351_set_synthesis(inst, &synth, 0);
- } while(0);
- return rv;
-}
-
-/** @brief Sets the CLK_x output of the si5351
- * @param si5351_instance Given si5351 device handle
- * @param frequency
- * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @param pll the used PLL, either PLLA or PLLB
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_set_clk(si5351_inst_t inst, uint32_t frequency, uint8_t clk, si5351_pll_t pll) {
-
- int rv = 0;
- synthesis_t synth;
-
- if((!inst && !(inst=first_handle)) || clk > 7)
- return -EINVAL;
-
- if (!pll)
- inst->clk_is_pllb &= (uint8_t)~(1u << clk); /* delete the bit */
- else
- inst->clk_is_pllb |= (uint8_t)(1u << clk); /* set the bit */
-
- do {
- if(!inst->programmed) {
- rv = si5351_program(inst);
- if (rv)
- break;
- }
- (void)calculation(frequency, inst->xtal_frequency, &synth);
- rv = si5351_set_synthesis(inst, &synth, clk);
- } while(0);
- return rv;
-}
-
-/** @brief Sets the CLK_x output of the si5351 with phase
- * @param si5351_instance Given si5351 device handle
- * @param frequency
- * @param phase in degree as double value
- * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @param pll the used PLL, either PLLA or PLLB
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_set_clk_phase(si5351_inst_t inst, uint32_t frequency, double phase, uint8_t clk, si5351_pll_t pll) {
-
- int rv = 0;
- synthesis_t synth;
-
- if((!inst && !(inst=first_handle)) || clk > 7)
- return -EINVAL;
-
- if (!pll)
- inst->clk_is_pllb &= (uint8_t)~(1u << clk); /* delete the bit */
- else
- inst->clk_is_pllb |= (uint8_t)(1u << clk); /* set the bit */
-
- do {
-
- if(!inst->programmed) {
- rv = si5351_program(inst);
- if (rv)
- break;
- }
- /* calculate the phase */
- if (phase > 0.0) {
- inst->clk_has_phase_shift |= (uint8_t)(1u << clk); /* set phase shift */
- double pll_frequency, phaseoff;
- //pll_frequency = ((double)synth.pll_multiplier + (double)(synth.pll_numerator / synth.pll_denominator)) * (double)inst->xtal_frequency;
- //phaseoff = 4.0 * pll_frequency;
- (void)phaseoff;
- (void)pll_frequency;
-
- } else {
- inst->clk_has_phase_shift &= (uint8_t)~(1u << clk); /* reset phase shift */
- }
- uint8_t ph = (uint8_t) phase;
- rv = si5351_write(inst, SI5351_CLK0_INITIAL_PHASE_OFFSET + clk, &ph, 1);
-
- (void)calculation(frequency, inst->xtal_frequency, &synth);
- rv = si5351_set_synthesis(inst, &synth, clk);
- } while(0);
- return rv;
-
-}
-
-/** @brief Resets the PLL of the si5351 (direct access to register)
- * @param si5351_instance Given si5351 device handle
- * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_reset_pll(si5351_inst_t inst, uint8_t clk) {
-
- /* internal function, no need to check inst nor clk */
-
- uint8_t reset;
-
-#if 1
- reset = (inst->clk_is_pllb & (1< 5 || phase > CLKx_PHOFF)
- return -EINVAL;
-
-/* if (phase) {
- si5351_write(inst, )
- }
-*/
- rv = si5351_write(inst, SI5351_CLK0_INITIAL_PHASE_OFFSET + clk, &phase, 1);
-
- si5351_reset_pll(inst, clk);
-
- return rv;
-}
-
-/** @brief Sets the MSNx and MSx parameter registers of the Si5351
- * @param si5351_instance Given si5351 device handle
- * @param synth synthesis_t struct
- * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ...
- * @return 0 on success
- * @retval -EINVAL when given a NULL handle
- * @retval -ETIMEDOUT when HAL_TIMEOUT
- * @retval -EIO when HAL_ERROR
- * @retval -EBUSY when HAL_BUSY
- */
-int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk) {
-
- uint32_t MSNx_P1, MSNx_P2, MSNx_P3;
- uint32_t MSx_P1, MSx_P2, MSx_P3;
- int rv = 0;
- uint8_t ms_data[8];
- uint8_t regaddr;
- uint8_t divby4 = 0;
- uint8_t MSx_INT = 0;
- uint8_t MSx_SRC = 0;
-
- if(!inst && !(inst=first_handle))
- return -EINVAL;
-
- assert(clk < 8);
- assert(synth->out_r_divider < 8); /* the divider is stored in 3 bits and the value is 2^out_r_divider */
- assert(synth->pll_multiplier >= 4u && synth->pll_multiplier <= 2048u && synth->pll_multiplier != 5u && synth->pll_multiplier != 7u);
- assert(synth->pll_denominator != 0u && synth->pll_denominator < (1u << 20)); // MSNx_P3 maximum of 1048575
- assert(synth->out_multiplier >= 4u && synth->out_multiplier <= 2048u && synth->out_multiplier != 5u && synth->out_multiplier != 7u);
- assert(synth->out_denominator != 0u && synth->out_denominator < (1u << 20));
-
- /* PLL registers */
- MSNx_P1 = 128 * synth->pll_multiplier + 128 * synth->pll_numerator / synth->pll_denominator - 512;
- // MSNx_P2 = 128 * synth->pll_numerator - synth->pll_denominator * (128 * synth->pll_numerator / synth->pll_denominator);
- MSNx_P2 = (128 * synth->pll_numerator) % synth->pll_denominator;
- MSNx_P3 = synth->pll_denominator;
-
- assert(MSNx_P1 < (1u << 18));
- assert(MSNx_P2 < (1u << 20));
-
- /* OUTPUT (M) registers */
- MSx_P1 = 128 * synth->out_multiplier + 128 * synth->out_numerator / synth->out_denominator - 512;
- // MSx_P2 = 128 * synth->out_numerator - synth->out_denominator * (128 * synth->out_numerator / synth->out_denominator);
- MSx_P2 = (128 * synth->out_numerator) % synth->out_denominator;
- MSx_P3 = synth->out_denominator;
- if (synth->out_r_divider == 2) /* 2^2 = 4, see 4.1.3 in AN619 special case */
- divby4 = 0x03;
-
- assert(MSx_P1 < (1u << 18));
- assert(MSx_P2 < (1u << 20));
-
- /* distribute these registers to 8 bytes of the SI5351 */
- /* start with MSNx values */
- ms_data[0] = (uint8_t) (MSNx_P3 >> 8);
- ms_data[1] = (uint8_t) MSNx_P3;
- ms_data[2] = (uint8_t) ((MSNx_P1 >> 16) & 0x03);
- ms_data[3] = (uint8_t) (MSNx_P1 >> 8);
- ms_data[4] = (uint8_t) MSNx_P1;
- ms_data[5] = (uint8_t) ((((MSNx_P3 >> 16) & 0x0F) << 4) | ((MSNx_P2 >> 16) & 0x0F));
- ms_data[6] = (uint8_t) (MSNx_P2 >> 8);
- ms_data[7] = (uint8_t) MSNx_P2;
- /* write MSNx registers dependent of SI5351_PLLA or SI5351_PLLB */
- regaddr = (inst->clk_is_pllb & (1u << clk)) ? SI5351_MULTISYNTH_NB_PARAMETER_3_HI : SI5351_MULTISYNTH_NA_PARAMETER_3_HI;
- rv = si5351_write(inst, regaddr, ms_data, sizeof(ms_data) / sizeof(ms_data[0]));
- if (rv)
- return rv;
- /* write MSx registers dependent of CLK # */
- ms_data[0] = (uint8_t) (MSx_P3 >> 8);
- ms_data[1] = (uint8_t) MSx_P3;
- ms_data[2] = (uint8_t) (((MSx_P1 >> 16) & 0x03) | ((synth->out_r_divider & 0x07) << 4) | (divby4 << 2));
- ms_data[3] = (uint8_t) (MSx_P1 >> 8);
- ms_data[4] = (uint8_t) MSx_P1;
- ms_data[5] = (uint8_t) ((((MSx_P3 >> 16) & 0x0F) << 4) | ((MSx_P2 >> 16) & 0x0F));
- ms_data[6] = (uint8_t) (MSx_P2 >> 8);
- ms_data[7] = (uint8_t) MSx_P2;
- regaddr = SI5351_MULTISYNTH0_PARAMETER_3_HI + (uint8_t)(clk * sizeof(ms_data) / sizeof(ms_data[0]));
- rv = si5351_write(inst, regaddr, ms_data, sizeof(ms_data) / sizeof(ms_data[0]));
- if (rv)
- return rv;
-
- MSx_INT = ((synth->out_numerator == 0) && ((synth->out_multiplier & 0x01) == 0) &&
- !(inst->clk_has_phase_shift & (1<clk_is_pllb & (1 << clk));
-
- ms_data[0] = (uint8_t)(MSx_INT << 6 | MSx_SRC << 5 | SI5351_CLK_SRC_MS0 | SI5351_CLK_2_MA); //SI5351_CLK_6_MA; //SI5351_CLK_4_MA;
- regaddr = SI5351_CLK0_CONTROL + clk;
- rv = si5351_write(inst, regaddr, ms_data, 1);
- if (rv)
- return rv;
-
- rv = si5351_reset_pll(inst, clk);
- if (rv)
- return rv;
-
-#if AUTOMATICALLY_ENABLE_OUTPUT
- rv = si5351_enable_output(inst, clk);
-#endif
-
- return rv;
-}
-
-#if SI5351_DEBUG
-/** @brief With this function, you can read the debug message for test purposes
- * @param si5351_instance Given si5351 device handle
- * @return * char as message for printing
- * @retval NULL when not found
- */
-char * si5351_read_debug_msg(si5351_inst_t inst) {
-
- if (!inst && !(inst=first_handle))
- return NULL;
-
- return inst->debug_msg;
-}
-
-int si5351_write_data(si5351_inst_t inst, void * data) {
-
- if (!inst && !(inst=first_handle))
- return -EINVAL;
-
- memcpy(inst->data, data, inst->datasize);
- return (int)inst->datasize;
-}
-
-
-
-int si5351_read_data(si5351_inst_t inst, void * data) {
-
- if(!inst && !(inst=first_handle))
- return -EINVAL;
-
- memcpy(data, inst->data, inst->datasize);
- return (int)inst->datasize;
-}
-#endif
-
-#ifdef DEBUG
-/*!
- * @brief Output the value in binary representation and in groups of
- * bytes
- *
- * @param buf string of char * to output to
- * @param n amount of characters to output to
- * @param value Value to output in binary representation
- * @param size
- * @return bytes written (int)
- */
-int __snprintb(char *buf, size_t n, void *value, size_t size)
-{
- uint8_t byte;
- size_t blen = sizeof(byte) * 8;
- uint8_t bits[blen + 1];
- int cx = 0;
-
- bits[blen] = '\0';
- for_endian((int)size) {
- byte = ((uint8_t *) value)[i];
- memset(bits, '0', blen);
- for (int j = 0; byte && j < (int)blen; ++j) {
- if (byte & 0x80)
- bits[j] = '1';
- byte <<= 1;
- }
- cx += snprintf(buf + cx, n - (size_t)cx, "%s%s", bits, (last_loop_endian)? "":" ");
- }
- return cx;
-}
-
-/** @brief Function to read any register with binary and hex representation
- * @param si5351_instance Given si5351 device handle
- * @param buffer for printing typically 33 bytes char
- * @param size of the buffer for printing typically 33 bytes
- * @param register address
- * @return * char as message for printing
- * @retval NULL when not found
- */
-char * si5351_read_register_debug(si5351_inst_t inst, char *buf, size_t bufsize, uint8_t regaddr) {
-
- uint8_t data;
- int status;
- int cx;
-
- if (!inst)
- return NULL;
-
- status = si5351_read(inst, regaddr, &data, 1);
- if (status)
- return NULL;
-
- cx = snprintf(buf, bufsize, "R%03u[0x%02x]=%3u[0x%02x][0b", regaddr, regaddr, data, data);
- cx += snprintb(buf + cx, bufsize - (size_t)cx, data);
- cx += snprintf(buf + cx, bufsize - (size_t)cx, "]");
- return buf;
-}
-#endif //DEBUG
-
-/* some additional functions */
-/** @brief Function to get the handle of a si5351 device instance
- * @param si5351_instance Given si5351 device handle or NULL for the first handle.
- * @return si5351_inst_t handle
- * @retval NULL when not found
- */
-int si5351_get_instance(si5351_inst_t *inst) {
-
- si5351_inst_t tinst;
- int rv = 0;
-
- if (!inst)
- return -EINVAL;
-
- if (!(*inst))
- *inst = first_handle;
- else
- *inst = (*inst)->next;
-
- tinst = *inst;
- while (tinst) {
- rv++;
- tinst = tinst->next;
- }
- return rv;
-}
-
diff --git a/Core/Src/syscalls.c b/Core/Src/syscalls.c
index 4a81b3b..b6d55cc 100644
--- a/Core/Src/syscalls.c
+++ b/Core/Src/syscalls.c
@@ -71,7 +71,7 @@ __attribute__((weak)) int _read(int file, char *ptr, int len)
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
- *ptr++ = __io_getchar();
+ *ptr++ = (char)__io_getchar();
}
return len;
diff --git a/Core/external_sources/polasek/si5351.c b/Core/external_sources/polasek/si5351.c
new file mode 100644
index 0000000..f72acdd
--- /dev/null
+++ b/Core/external_sources/polasek/si5351.c
@@ -0,0 +1,889 @@
+/*
+ * si5351.c
+ *
+ * Created on: Jan 14, 2019
+ * Author: Petr Polasek
+ *
+ * To make this library useable on any other device than
+ * STM32Fxxx Cortex Mx, please edit these parts of the library:
+ *
+ * DEFINES:
+ * SI5351_I2C_PERIPHERAL - the I2C peripheral name according
+ * to your devices HAL library
+ * I2C_TIMEOUT - time for the communication to time out
+ *
+ * TYPEDEFS:
+ * Si5351_ConfigTypeDef - the I2Cx parameter should be changed
+ * so that its type corresponds to your HAL library
+ *
+ * FUNCTIONS:
+ * Si5351_WriteRegister
+ * Si5351_ReadRegister
+ * You need to write your own I2C handlers here
+ *
+ */
+
+//put your I2C HAL library name here
+//#include "stm32f10x_i2c.h"
+#include "stm32l4xx_hal.h"
+
+#if 0
+
+#include "si5351.h"
+
+int Si5351_WriteRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address, uint8_t reg_data)
+{
+ uint32_t error_wait;
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
+ {
+ error_wait--;
+ if (error_wait==0)
+ {
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
+ return 1;
+ }
+ }
+ //wait for I2C to get ready, if not ready in time, reset I2C and return
+
+ I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //send START condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for START to be sent, if not sent in time, return
+
+ I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Transmitter);
+ //send address+RW bit
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for address to be sent, if not sent in time, return
+
+ I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_address);
+ //send reg address
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for reg address to be sent
+
+ I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_data);
+ //send reg data
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for data to be sent, if not sent in time, return
+
+ I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //generate STOP condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait until STOP is cleared
+
+ return 0;
+}
+
+uint8_t Si5351_ReadRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address)
+{
+ uint32_t error_wait;
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
+ {
+ error_wait--;
+ if (error_wait==0)
+ {
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
+ return 1;
+ }
+ }
+ //wait for I2C to get ready, if not ready in time, reset I2C and return
+
+ I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //send START condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for START to be sent, if not sent in time, return
+
+ I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Transmitter);
+ //send address+RW bit
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for address to be sent, if not sent in time, return
+
+ I2C_SendData(Si5351_ConfigStruct->I2Cx, reg_address);
+ //send reg address
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for reg address to be sent
+
+ I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //generate STOP condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait until STOP is cleared
+
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_BUSY) == SET)
+ {
+ error_wait--;
+ if (error_wait==0)
+ {
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, ENABLE);
+ I2C_SoftwareResetCmd(Si5351_ConfigStruct->I2Cx, DISABLE);
+ return 1;
+ }
+ }
+ //wait for I2C to get ready, if not ready in time, reset I2C and return
+
+ I2C_GenerateSTART(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //send START condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for START to be sent, if not sent in time, return
+
+ I2C_Send7bitAddress(Si5351_ConfigStruct->I2Cx, Si5351_ConfigStruct->HW_I2C_Address, I2C_Direction_Receiver);
+ //send address+RW bit
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for address to be sent, if not sent in time, return
+
+ while (I2C_CheckEvent(Si5351_ConfigStruct->I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR)
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait for data
+
+ uint8_t reg_data;
+ reg_data = I2C_ReceiveData(Si5351_ConfigStruct->I2Cx);
+ //receive reg data
+
+ I2C_GenerateSTOP(Si5351_ConfigStruct->I2Cx, ENABLE);
+ //generate STOP condition
+
+ error_wait = I2C_TIMEOUT;
+ while (I2C_GetFlagStatus(Si5351_ConfigStruct->I2Cx, I2C_FLAG_STOPF))
+ {
+ error_wait--;
+ if (error_wait==0) return 1;
+ }
+ //wait until STOP is cleared
+
+ return reg_data;
+}
+
+//set safe values in the config structure
+void Si5351_StructInit(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint8_t i;
+
+ Si5351_ConfigStruct->HW_I2C_Address = SI5351_I2C_ADDRESS;
+ Si5351_ConfigStruct->I2Cx = SI5351_I2C_PERIPHERAL;
+
+ Si5351_ConfigStruct->f_CLKIN = SI5351_CLKIN_FREQ;
+ Si5351_ConfigStruct->f_XTAL = SI5351_XTAL_FREQ;
+
+ Si5351_ConfigStruct->Interrupt_Mask_CLKIN = ON;
+ Si5351_ConfigStruct->Interrupt_Mask_PLLA = ON;
+ Si5351_ConfigStruct->Interrupt_Mask_PLLB = ON;
+ Si5351_ConfigStruct->Interrupt_Mask_SysInit = ON;
+ Si5351_ConfigStruct->Interrupt_Mask_XTAL = ON;
+
+ Si5351_ConfigStruct->Fanout_CLKIN_EN = ON;
+ Si5351_ConfigStruct->Fanout_MS_EN = ON;
+ Si5351_ConfigStruct->Fanout_XO_EN = ON;
+
+ Si5351_ConfigStruct->OSC.CLKIN_Div = CLKINDiv_Div1;
+ Si5351_ConfigStruct->OSC.OSC_XTAL_Load = XTAL_Load_10_pF;
+ Si5351_ConfigStruct->OSC.VCXO_Pull_Range_ppm = 0; //maybe should be set to 30 ppm, not clear from the AN-619
+
+ for (i=0; i<=1; i++)
+ {
+ Si5351_ConfigStruct->PLL[i].PLL_Clock_Source = PLL_Clock_Source_XTAL;
+ Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Integer = 32; //range 24..36 for 25 MHz clock
+ Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Numerator = 0; //range 0..1048575
+ Si5351_ConfigStruct->PLL[i].PLL_Multiplier_Denominator = 1; //range 1..1048575
+ Si5351_ConfigStruct->PLL[i].PLL_Capacitive_Load = PLL_Capacitive_Load_0; //select 0, unless you want to tune the PLL to <200 MHZ
+ }
+
+ Si5351_ConfigStruct->SS.SS_Amplitude_ppm = 0; //1.5% modulation = 15000
+ Si5351_ConfigStruct->SS.SS_Enable = OFF;
+ Si5351_ConfigStruct->SS.SS_Mode = SS_Mode_CenterSpread;
+ Si5351_ConfigStruct->SS.SS_NCLK = SS_NCLK_0; //default value, this parameter is unexplained in documentation
+
+ for (i=0; i<=7; i++)
+ {
+ Si5351_ConfigStruct->MS[i].MS_Clock_Source = MS_Clock_Source_PLLA;
+ Si5351_ConfigStruct->MS[i].MS_Divider_Integer = 4;
+ Si5351_ConfigStruct->MS[i].MS_Divider_Numerator = 0;
+ Si5351_ConfigStruct->MS[i].MS_Divider_Denominator = 1;
+
+ Si5351_ConfigStruct->CLK[i].CLK_Clock_Source = CLK_Clock_Source_MS_Own;
+ Si5351_ConfigStruct->CLK[i].CLK_Disable_State = CLK_Disable_State_HIGH_Z;
+ Si5351_ConfigStruct->CLK[i].CLK_Enable = OFF;
+ Si5351_ConfigStruct->CLK[i].CLK_I_Drv = CLK_I_Drv_8mA;
+ Si5351_ConfigStruct->CLK[i].CLK_Invert = OFF;
+ Si5351_ConfigStruct->CLK[i].CLK_PowerDown = OFF;
+ Si5351_ConfigStruct->CLK[i].CLK_QuarterPeriod_Offset = 0;
+ Si5351_ConfigStruct->CLK[i].CLK_R_Div = CLK_R_Div1;
+ Si5351_ConfigStruct->CLK[i].CLK_Use_OEB_Pin = OFF;
+ }
+}
+
+void Si5351_OSCConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint8_t tmp;
+ uint32_t VCXO_Param;
+
+ //set XTAL capacitive load and PLL VCO load capacitance
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_XTAL_CL);
+ tmp &= ~(XTAL_CL_MASK | PLL_CL_MASK);
+ tmp |= (XTAL_CL_MASK & (Si5351_ConfigStruct->OSC.OSC_XTAL_Load)) | (PLL_CL_MASK & ((Si5351_ConfigStruct->PLL[0].PLL_Capacitive_Load) << 1)) | (PLL_CL_MASK & ((Si5351_ConfigStruct->PLL[1].PLL_Capacitive_Load) << 4));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_XTAL_CL, tmp);
+
+ //set CLKIN pre-divider
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLKIN_DIV);
+ tmp &= ~CLKIN_MASK;
+ tmp |= CLKIN_MASK & Si5351_ConfigStruct->OSC.CLKIN_Div;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLKIN_DIV, tmp);
+
+ //set fanout of XO, MS0, MS4 and CLKIN - should be always on unless you
+ //need to reduce power consumption
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FANOUT_EN);
+ tmp &= ~(FANOUT_CLKIN_EN_MASK | FANOUT_MS_EN_MASK | FANOUT_XO_EN_MASK);
+ if (Si5351_ConfigStruct->Fanout_CLKIN_EN == ON) tmp |= FANOUT_CLKIN_EN_MASK;
+ if (Si5351_ConfigStruct->Fanout_MS_EN == ON) tmp |= FANOUT_MS_EN_MASK;
+ if (Si5351_ConfigStruct->Fanout_XO_EN == ON) tmp |= FANOUT_XO_EN_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_FANOUT_EN, tmp);
+
+ //if "b" in PLLB set to 10^6, set VCXO parameter
+ if (Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Denominator == 1000000)
+ {
+ VCXO_Param = VCXO_PARAM_MASK & (uint32_t)
+ ((103 * Si5351_ConfigStruct->OSC.VCXO_Pull_Range_ppm
+ * ((uint64_t)128000000 * Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Integer +
+ Si5351_ConfigStruct->PLL[1].PLL_Multiplier_Numerator))/100000000);
+ } else {
+ VCXO_Param = 0;
+ }
+
+ tmp = (uint8_t) VCXO_Param;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_0_7, tmp);
+ tmp = (uint8_t)(VCXO_Param>>8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_8_15, tmp);
+ tmp = (uint8_t)((VCXO_Param>>16) & VCXO_PARAM_16_21_MASK);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_VCXO_PARAM_16_21, tmp);
+}
+
+EnableState Si5351_CheckStatusBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
+{
+ uint8_t tmp;
+
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STATUS);
+ tmp &= StatusBit;
+ return tmp;
+}
+
+EnableState Si5351_CheckStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
+{
+ uint8_t tmp;
+
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STICKY);
+ tmp &= StatusBit;
+ return tmp;
+}
+
+void Si5351_InterruptConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint8_t tmp;
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_INT_MASK);
+
+ tmp &= ~INT_MASK_LOS_XTAL_MASK;
+ if (Si5351_ConfigStruct->Interrupt_Mask_XTAL == ON)
+ {
+ tmp |= INT_MASK_LOS_XTAL_MASK;
+ }
+
+ tmp &= ~INT_MASK_LOS_CLKIN_MASK;
+ if (Si5351_ConfigStruct->Interrupt_Mask_CLKIN == ON)
+ {
+ tmp |= INT_MASK_LOS_CLKIN_MASK;
+ }
+
+ tmp &= ~INT_MASK_LOL_A_MASK;
+ if (Si5351_ConfigStruct->Interrupt_Mask_PLLA == ON)
+ {
+ tmp |= INT_MASK_LOL_A_MASK;
+ }
+
+ tmp &= ~INT_MASK_LOL_B_MASK;
+ if (Si5351_ConfigStruct->Interrupt_Mask_PLLB == ON)
+ {
+ tmp |= INT_MASK_LOL_B_MASK;
+ }
+
+ tmp &= ~INT_MASK_SYS_INIT_MASK;
+ if (Si5351_ConfigStruct->Interrupt_Mask_SysInit == ON)
+ {
+ tmp |= INT_MASK_SYS_INIT_MASK;
+ }
+
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_INT_MASK, tmp);
+}
+
+void Si5351_ClearStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit)
+{
+ uint8_t tmp;
+
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_DEV_STICKY);
+ tmp &= ~StatusBit;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_DEV_STICKY, tmp);
+}
+
+void Si5351_PLLConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel)
+{
+ uint8_t tmp, tmp_mask;
+ uint32_t MSN_P1, MSN_P2, MSN_P3;
+
+ //set PLL clock source
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_CLOCK_SOURCE);
+ tmp_mask = PLLA_CLOCK_SOURCE_MASK << PLL_Channel;
+ tmp &= ~tmp_mask;
+ tmp |= tmp_mask & Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Clock_Source;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_CLOCK_SOURCE, tmp);
+
+ //if new multiplier not even integer, disable the integer mode
+ if ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator != 0) | ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer & 0x01) != 0 ))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel);
+ tmp &= ~FB_INT_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel, tmp);
+ }
+
+ //configure the PLL multiplier
+ MSN_P1 = 128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer + ((128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator) / Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator) - 512;
+ MSN_P2 = 128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator - Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator * ((128 * Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator) / Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator);
+ MSN_P3 = Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Denominator;
+
+ tmp = (uint8_t) MSN_P1;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_0_7 + 8 * PLL_Channel, tmp);
+ tmp = (uint8_t) (MSN_P1 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_8_15 + 8 * PLL_Channel, tmp);
+ tmp = (uint8_t) (MSN_P1_16_17_MASK & (MSN_P1 >> 16));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P1_16_17 + 8 * PLL_Channel, tmp);
+
+ tmp = (uint8_t) MSN_P2;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_0_7 + 8 * PLL_Channel, tmp);
+ tmp = (uint8_t) (MSN_P2 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_8_15 + 8 * PLL_Channel, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MSN_P2_16_19);
+ tmp &= ~MSN_P2_16_19_MASK;
+ tmp |= (uint8_t) (MSN_P2_16_19_MASK & (MSN_P2 >> 16));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P2_16_19 + 8 * PLL_Channel, tmp);
+
+ tmp = (uint8_t) MSN_P3;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_0_7 + 8 * PLL_Channel, tmp);
+ tmp = (uint8_t) (MSN_P3 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_8_15 + 8 * PLL_Channel, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MSN_P3_16_19);
+ tmp &= ~MSN_P3_16_19_MASK;
+ tmp |= (uint8_t) (MSN_P3_16_19_MASK & ((MSN_P3 >> 16) << 4));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MSN_P3_16_19 + 8 * PLL_Channel, tmp);
+
+ //if new multiplier is an even integer, enable integer mode
+ if ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Numerator == 0) & ((Si5351_ConfigStruct->PLL[PLL_Channel].PLL_Multiplier_Integer & 0x01) == 0 ))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel);
+ tmp |= FB_INT_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_FB_INT + PLL_Channel, tmp);
+ }
+}
+
+void Si5351_PLLReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel)
+{
+ uint8_t tmp;
+
+ //reset PLL
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_RESET);
+ if (PLL_Channel == PLL_A)
+ {
+ tmp |= PLLA_RESET_MASK;
+ } else {
+ tmp |= PLLB_RESET_MASK;
+ }
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_RESET, tmp);
+}
+
+void Si5351_PLLSimultaneousReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint8_t tmp;
+
+ //reset PLL
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_PLL_RESET);
+ tmp |= PLLA_RESET_MASK | PLLB_RESET_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_PLL_RESET, tmp);
+}
+
+void Si5351_SSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint8_t tmp;
+ uint32_t SSUDP, SSUP_P1, SSUP_P2, SSUP_P3, SSDN_P1, SSDN_P2, SSDN_P3;
+ uint64_t SSDN, SSUP;
+
+ //turn off SS if it should be disabled
+ if ((Si5351_ConfigStruct->SS.SS_Enable == OFF)|
+ (((Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer & 0x01) == 0)
+ & (Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator == 0)) )
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_EN);
+ tmp &= ~SSC_EN_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_EN, tmp);
+ }
+
+ //set default value of SS_NCLK - spread spectrum reserved register
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SS_NCLK);
+ tmp &= ~SS_NCLK_MASK;
+ tmp |= SS_NCLK_MASK & (Si5351_ConfigStruct->SS.SS_NCLK);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SS_NCLK, tmp);
+
+ //set SS mode
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_MODE);
+ tmp &= ~SSC_MODE_MASK;
+ tmp |= SSC_MODE_MASK & Si5351_ConfigStruct->SS.SS_Mode;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_MODE, tmp);
+
+ //set SSUDP parameter
+ if (Si5351_ConfigStruct->PLL[0].PLL_Clock_Source == PLL_Clock_Source_CLKIN)
+ {
+ SSUDP = (Si5351_ConfigStruct->f_CLKIN)/(4*31500);
+ } else {
+ SSUDP = (Si5351_ConfigStruct->f_XTAL)/(4*31500);
+ }
+
+ //set SSUDP parameter
+ tmp = (uint8_t) SSUDP;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUDP_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUDP_8_11);
+ tmp &= ~SSUDP_8_11_MASK;
+ tmp |= (uint8_t) (SSUDP_8_11_MASK & ((SSUDP >> 8) << 4));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUDP_8_11, tmp);
+
+ //calculate SSUP and SSDN parameters
+ if (Si5351_ConfigStruct->SS.SS_Mode == SS_Mode_CenterSpread)
+ {
+ SSUP = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
+ + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
+ ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
+ ) / ((1000000 - Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
+
+ SSDN = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
+ + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
+ ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
+ ) / ((1000000 + Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
+
+ SSUP_P1 = (uint32_t) (SSUP/1000000);
+ SSUP_P2 = (uint32_t)(32767*(SSUP/1000000-SSUP_P1));
+ SSUP_P3 = 0x7FFF;
+
+ } else {
+
+ SSDN = ((uint64_t)(64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer
+ + (64000000*Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator)/(Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Denominator)
+ ) * Si5351_ConfigStruct->SS.SS_Amplitude_ppm
+ ) / ((1000000 + Si5351_ConfigStruct->SS.SS_Amplitude_ppm) * SSUDP);
+
+ SSUP_P1 = 0;
+ SSUP_P2 = 0;
+ SSUP_P3 = 1;
+
+ }
+
+ //set SSDN parameter
+ SSDN_P1 = (uint32_t) (SSDN/1000000);
+ SSDN_P2 = (uint32_t)(32767*(SSDN/1000000-SSDN_P1));
+ SSDN_P3 = 0x7FFF;
+
+ //write SSUP parameter P1
+ tmp = (uint8_t) SSUP_P1;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P1_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P1_8_11);
+ tmp &= ~SSUP_P1_8_11_MASK;
+ tmp |= (uint8_t)(SSUP_P1_8_11_MASK & (SSUP_P1 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P1_8_11, tmp);
+
+ //write SSUP parameter P2
+ tmp = (uint8_t) SSUP_P2;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P2_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P2_8_14);
+ tmp &= ~SSUP_P2_8_14_MASK;
+ tmp |= (uint8_t)(SSUP_P2_8_14_MASK & (SSUP_P2 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P2_8_14, tmp);
+
+ //write SSUP parameter P3
+ tmp = (uint8_t) SSUP_P3;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P3_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSUP_P3_8_14);
+ tmp &= ~SSUP_P3_8_14_MASK;
+ tmp |= (uint8_t)(SSUP_P3_8_14_MASK & (SSUP_P3 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSUP_P3_8_14, tmp);
+
+ //write SSDN parameter P1
+ tmp = (uint8_t) SSDN_P1;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P1_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P1_8_11);
+ tmp &= ~SSDN_P1_8_11_MASK;
+ tmp |= (uint8_t)(SSDN_P1_8_11_MASK & (SSDN_P1 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P1_8_11, tmp);
+
+ //write SSDN parameter P2
+ tmp = (uint8_t) SSDN_P2;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P2_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P2_8_14);
+ tmp &= ~SSDN_P2_8_14_MASK;
+ tmp |= (uint8_t)(SSDN_P2_8_14_MASK & (SSDN_P2 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P2_8_14, tmp);
+
+ //write SSDN parameter P3
+ tmp = (uint8_t) SSDN_P3;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P3_0_7, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSDN_P3_8_14);
+ tmp &= ~SSDN_P3_8_14_MASK;
+ tmp |= (uint8_t)(SSDN_P3_8_14_MASK & (SSDN_P3 >> 8));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSDN_P3_8_14, tmp);
+
+ //turn on SS if it should be enabled
+ if ((Si5351_ConfigStruct->SS.SS_Enable == ON)
+ & (((Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Integer & 0x01) != 0)
+ | (Si5351_ConfigStruct->PLL[0].PLL_Multiplier_Numerator != 0)))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_SSC_EN);
+ tmp |= SSC_EN_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_SSC_EN, tmp);
+ }
+}
+
+void Si5351_MSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_MSChannelTypeDef MS_Channel)
+{
+ uint8_t tmp;
+ uint32_t MS_P1, MS_P2, MS_P3;
+
+ //configure MultiSynth clock source
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_SRC + MS_Channel);
+ tmp &= ~MS_SRC_MASK;
+ if (Si5351_ConfigStruct->MS[MS_Channel].MS_Clock_Source == MS_Clock_Source_PLLB)
+ {
+ tmp |= MS_SRC_MASK;
+ }
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_SRC + MS_Channel, tmp);
+
+ if (MS_Channel <= MS5) //configuration is simpler for MS6 and 7 since they are integer-only
+ {
+ //if next value not in even integer mode or if divider is not equal to 4, disable DIVBY4
+ if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer != 4)|(Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator != 0))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel);
+ tmp &= ~MS_DIVBY4_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel, tmp);
+ }
+
+ //if next value not in even integer mode or SS enabled, disable integer mode
+ if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator != 0)|((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer & 0x01) != 0)|(Si5351_ConfigStruct->SS.SS_Enable == ON))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel);
+ tmp &= ~MS_INT_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel, tmp);
+ }
+
+ //set new divider value
+ MS_P1 = 128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer
+ + ((128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator) / Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator)
+ - 512;
+ MS_P2 = 128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator
+ - Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator
+ * ((128 * Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator) / Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator);
+ MS_P3 = Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Denominator;
+
+ tmp = (uint8_t) MS_P1;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_0_7 + 8 * MS_Channel, tmp);
+ tmp = (uint8_t) (MS_P1 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_8_15 + 8 * MS_Channel, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P1_16_17);
+ tmp &= ~MS_P1_16_17_MASK;
+ tmp |= (uint8_t) (MS_P1_16_17_MASK & (MS_P1 >> 16));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P1_16_17 + 8 * MS_Channel, tmp);
+
+ tmp = (uint8_t) MS_P2;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_0_7 + 8 * MS_Channel, tmp);
+ tmp = (uint8_t) (MS_P2 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_8_15 + 8 * MS_Channel, tmp);
+ Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P2_16_19 + 8 * MS_Channel);
+ tmp &= ~MS_P2_16_19_MASK;
+ tmp |= (uint8_t) (MS_P2_16_19_MASK & (MS_P2 >> 16));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P2_16_19 + 8 * MS_Channel, tmp);
+
+ tmp = (uint8_t) MS_P3;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_0_7 + 8 * MS_Channel, tmp);
+ tmp = (uint8_t) (MS_P3 >> 8);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_8_15 + 8 * MS_Channel, tmp);
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_P3_16_19 + 8 * MS_Channel);
+ tmp &= ~MS_P3_16_19_MASK;
+ tmp |= (uint8_t) (MS_P3_16_19_MASK & ((MS_P3 >> 16) << 4));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_P3_16_19 + 8 * MS_Channel, tmp);
+
+ //if next value is even integer and SS not enabled, enable integer mode
+ if ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Numerator == 0) & ((Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer & 0x01) == 0) & (Si5351_ConfigStruct->SS.SS_Enable == OFF))
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel);
+ tmp |= MS_INT_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_INT + MS_Channel, tmp);
+
+ //if next value in integer mode and if divider is equal to 4, enable DIVBY4
+ if (Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer == 4)
+ {
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel);
+ tmp |= MS_DIVBY4_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS_DIVBY4 + 8 * MS_Channel, tmp);
+ }
+ }
+ } else {
+ //configure divider of Multisynth 6 and 7
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_MS67_P1 + (MS_Channel - MS6), (uint8_t)(Si5351_ConfigStruct->MS[MS_Channel].MS_Divider_Integer));
+ //can be only even integers between 6 and 254, inclusive
+ }
+}
+
+void Si5351_CLKPowerCmd(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel)
+{
+ uint8_t tmp, tmp_mask;
+
+ //set CLK disable state
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_DIS_STATE + (CLK_Channel >> 2)); //increment the address by 1 if CLKx>=CLK4
+ tmp_mask = CLK_DIS_STATE_MASK << ((CLK_Channel & 0x03)<<1); //shift the mask according to the selected channel
+ tmp &= ~tmp_mask;
+ tmp |= tmp_mask & ((Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Disable_State) << ((CLK_Channel & 0x03)<<1));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_DIS_STATE + (CLK_Channel >> 2), tmp);
+
+ //set OEB pin
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_OEB);
+ tmp_mask = 1 << CLK_Channel;
+ tmp &= ~tmp_mask;
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Use_OEB_Pin == OFF)
+ {
+ tmp |= tmp_mask;
+ }
+
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Enable == OFF) //disable clock
+ {
+ //power down the clock
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_EN);
+ tmp |= 1 << CLK_Channel;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_EN, tmp);
+ }
+
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_PowerDown == ON) //power down clock
+ {
+ //power down output driver
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel);
+ tmp |= CLK_PDN_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel, tmp);
+ }
+
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_PowerDown == OFF) //power up clock
+ {
+ //power up output driver
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel);
+ tmp &= ~CLK_PDN_MASK;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PDN + CLK_Channel, tmp);
+ }
+
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Enable == ON) //enable clock
+ {
+ //power up the clock
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_EN);
+ tmp &= ~(1 << CLK_Channel);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_EN, tmp);
+ }
+}
+
+void Si5351_CLKConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel)
+{
+ uint8_t tmp, tmp_mask;
+
+ //set CLK source clock
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_SRC + CLK_Channel);
+ tmp &= ~CLK_SRC_MASK;
+ tmp |= CLK_SRC_MASK & Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Clock_Source;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_SRC + CLK_Channel, tmp);
+
+ //set CLK inversion
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_INV + CLK_Channel);
+ tmp &= ~CLK_INV_MASK;
+ if (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_Invert == ON)
+ {
+ tmp |= CLK_INV_MASK;
+ }
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_INV + CLK_Channel, tmp);
+
+ //set CLK current drive
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_IDRV + CLK_Channel);
+ tmp &= ~CLK_IDRV_MASK;
+ tmp |= CLK_IDRV_MASK & Si5351_ConfigStruct->CLK[CLK_Channel].CLK_I_Drv;
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_IDRV + CLK_Channel, tmp);
+
+ if (CLK_Channel <= CLK5) //CLK6 and 7 are integer only, which causes several limitations
+ {
+ //set CLK phase offset
+ tmp = CLK_PHOFF_MASK & (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_QuarterPeriod_Offset);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_PHOFF + CLK_Channel, tmp);
+ //set Rx divider
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_R_DIV + CLK_Channel * CLK_R_DIV_STEP);
+ tmp &= ~CLK_R_DIV_MASK;
+ tmp |= CLK_R_DIV_MASK & (Si5351_ConfigStruct->CLK[CLK_Channel].CLK_R_Div);
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_R_DIV + CLK_Channel * CLK_R_DIV_STEP, tmp);
+ } else {
+ //CLK6 and CLK7 have no fractional mode, so they lack the phase offset function
+
+ //set Rx divider
+ tmp_mask = CLK_R67_DIV_MASK << ((CLK_Channel-CLK6) << 2); //shift mask left by 4 if CLK7
+ tmp = Si5351_ReadRegister(Si5351_ConfigStruct, REG_CLK_R67_DIV);
+ tmp &= ~tmp_mask;
+ tmp |= tmp_mask & ((Si5351_ConfigStruct->CLK[CLK_Channel].CLK_R_Div >> 4) << ((CLK_Channel-CLK6) << 2));
+ Si5351_WriteRegister(Si5351_ConfigStruct, REG_CLK_R67_DIV, tmp);
+ }
+}
+
+int Si5351_Init(Si5351_ConfigTypeDef *Si5351_ConfigStruct)
+{
+ uint32_t timeout = SI5351_TIMEOUT;
+ uint8_t i;
+
+ //wait for the 5351 to initialize
+ while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_SysInit))
+ {
+ timeout--;
+ if (timeout==0) return 1; //return 1 if initialization timed out
+ }
+
+ //configure oscillator, fanout, interrupts, VCXO
+ Si5351_OSCConfig(Si5351_ConfigStruct);
+ Si5351_InterruptConfig(Si5351_ConfigStruct);
+
+ //configure PLLs
+ for (i=PLL_A; i<=PLL_B; i++)
+ {
+ Si5351_PLLConfig(Si5351_ConfigStruct, i);
+ Si5351_PLLReset(Si5351_ConfigStruct, i);
+ }
+
+ //configure Spread Spectrum
+ Si5351_SSConfig(Si5351_ConfigStruct);
+
+ //Configure Multisynths
+ for (i=MS0; i<=MS7; i++)
+ {
+ Si5351_MSConfig(Si5351_ConfigStruct, i);
+ }
+
+ //configure outputs
+ for (i=CLK0; i<=CLK7; i++)
+ {
+ Si5351_CLKConfig(Si5351_ConfigStruct, i);
+ }
+
+ //wait for PLLs to lock
+ while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_SysInit | StatusBit_PLLA | StatusBit_PLLB))
+ {
+ timeout--;
+ if (timeout==0) return 1; //return 1 if problem with any PLL
+ }
+
+ //clear sticky bits
+ Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_SysInit | StatusBit_PLLA | StatusBit_PLLB);
+
+ if (Si5351_ConfigStruct->f_CLKIN != 0) //if CLKIN used, check it as well
+ {
+ while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_CLKIN))
+ {
+ timeout--;
+ if (timeout==0) return 1; //return 1 if initialization timed out
+ }
+ //clear CLKIN sticky bit
+ Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_CLKIN);
+ }
+
+ if (Si5351_ConfigStruct->f_XTAL != 0) //if XTAL used, check it as well
+ {
+ while (Si5351_CheckStatusBit(Si5351_ConfigStruct, StatusBit_XTAL))
+ {
+ timeout--;
+ if (timeout==0) return 1; //return 1 if initialization timed out
+ }
+ //clear XTAL sticky bit
+ Si5351_ClearStickyBit(Si5351_ConfigStruct, StatusBit_XTAL);
+ }
+
+ //power on or off the outputs
+ for (i=CLK0; i<=CLK7; i++)
+ {
+ Si5351_CLKPowerCmd(Si5351_ConfigStruct, i);
+ }
+
+ return 0;
+}
+#endif
diff --git a/Core/external_sources/polasek/si5351.h b/Core/external_sources/polasek/si5351.h
new file mode 100644
index 0000000..70e939d
--- /dev/null
+++ b/Core/external_sources/polasek/si5351.h
@@ -0,0 +1,525 @@
+/*
+ * si5351.h
+ *
+ * Created on: Feb 16, 2018
+ * Author: Petr Polasek
+ *
+ * To make this library useable on any other device than
+ * STM32Fxxx Cortex Mx, please edit these parts of the library:
+ *
+ * DEFINES:
+ * SI5351_I2C_PERIPHERAL - the I2C peripheral name according
+ * to your devices HAL library
+ * I2C_TIMEOUT - time for the communication to time out
+ *
+ * TYPEDEFS:
+ * Si5351_ConfigTypeDef - the I2Cx parameter should be changed
+ * so that its type corresponds to your HAL library
+ *
+ * FUNCTIONS:
+ * Si5351_WriteRegister
+ * Si5351_ReadRegister
+ * You need to write your own I2C handlers here
+ *
+ */
+
+#ifndef SI5351_H_
+#define SI5351_H_
+
+#include "stm32l4xx_hal.h"
+
+#define SI5351_I2C_ADDRESS 0xC0 //default I2C address of Si5351
+#define SI5351_I2C_PERIPHERAL I2C1 //default I2C interface
+
+#define SI5351_XTAL_FREQ 25000000 // sets default value, 25000000 for 25 MHz, 27000000 for 27 MHz
+#define SI5351_CLKIN_FREQ 0 // set in Hz
+
+#ifdef I2C_TIMEOUT
+ #undef I2C_TIMEOUT
+#endif
+#define I2C_TIMEOUT 100000 //I2C timeout for wait loops
+
+#define SI5351_TIMEOUT (I2C_TIMEOUT * 10)
+
+#ifndef ENABLESTATE
+ #define ENABLESTATE
+ typedef enum
+ {
+ OFF = 0,
+ ON = 1
+ } EnableState;
+#endif
+
+/*
+ * This section contains register addresses and bit masks for
+ * the device status registers.
+ */
+
+#define REG_DEV_STATUS 0
+ #define DEV_SYS_INIT_MASK 0x80
+ #define DEV_LOL_B_MASK 0x40
+ #define DEV_LOL_A_MASK 0x20
+ #define DEV_LOS_CLKIN_MASK 0x10
+ #define DEV_LOS_XTAL_MASK 0x08
+ #define DEV_REVID_MASK 0x03
+
+#define REG_DEV_STICKY 1
+ #define DEV_STKY_SYS_INIT_MASK 0x80
+ #define DEV_STKY_LOL_B_MASK 0x40
+ #define DEV_STKY_LOL_A_MASK 0x20
+ #define DEV_STKY_LOS_CLKIN_MASK 0x10
+ #define DEV_STKY_LOS_XTAL_MASK 0x08
+
+#define REG_INT_MASK 2
+ #define INT_MASK_SYS_INIT_MASK 0x80
+ #define INT_MASK_LOL_B_MASK 0x40
+ #define INT_MASK_LOL_A_MASK 0x20
+ #define INT_MASK_LOS_CLKIN_MASK 0x10
+ #define INT_MASK_LOS_XTAL_MASK 0x08
+
+/*
+ * This section contains data structures for configuring the
+ * oscillator, VCXO and CLKIN section.
+ */
+
+#define REG_XTAL_CL 183
+#define XTAL_CL_MASK 0xC0
+#define PLL_CL_MASK 0x36
+
+//this sets the crystal load capacitance
+typedef enum
+{
+ XTAL_Load_4_pF = 0x00,
+ XTAL_Load_6_pF = 0x40,
+ XTAL_Load_8_pF = 0x80,
+ XTAL_Load_10_pF = 0xC0
+} Si5351_XTALLoadTypeDef;
+
+//The following is an unexplained parameter. However someone from SiLabs called it "VCO load cap".
+//Lower settings seem to be more stable on higher frequencies, higher settings are more stable on lower frequencies allowing to tune the PLL to <200 MHz.
+typedef enum
+{
+ PLL_Capacitive_Load_0 = 0,
+ PLL_Capacitive_Load_1 = 1,
+ PLL_Capacitive_Load_2 = 2
+} Si5351_PLLCapacitiveLoadTypeDef;
+
+#define REG_CLKIN_DIV 15
+#define CLKIN_MASK 0xC0
+
+//this sets the CLKIN pre-divider, after division, CLKIN should
+//fall between 10-40 MHz
+typedef enum
+{
+ CLKINDiv_Div1 = 0x00,
+ CLKINDiv_Div2 = 0x40,
+ CLKINDiv_Div4 = 0x80,
+ CLKINDiv_Div8 = 0xC0
+} Si5351_CLKINDivTypeDef;
+
+#define REG_FANOUT_EN 187
+#define FANOUT_CLKIN_EN_MASK 0x80
+#define FANOUT_XO_EN_MASK 0x40
+#define FANOUT_MS_EN_MASK 0x10
+
+#define REG_VCXO_PARAM_0_7 162
+#define REG_VCXO_PARAM_8_15 163
+#define REG_VCXO_PARAM_16_21 164
+#define VCXO_PARAM_16_21_MASK 0x3F
+#define VCXO_PARAM_MASK 0x003FFFFF
+
+#define APR_MINIMUM 30 //minimum pull range
+#define APR_MAXIMUM 240 //maximum pull range
+
+#define CLKIN_MINIMUM 10000 //minimum CLKIN frequency after division in kHz
+#define CLKIN_MAXIMUM 40000 //maximum CLKIN frequency after division in kHz
+
+typedef struct
+{
+ Si5351_XTALLoadTypeDef OSC_XTAL_Load; //capacitive load of XTAL, 10pF by default
+ Si5351_CLKINDivTypeDef CLKIN_Div; //CLKIN predivision, input f to PLL must be 10-40 MHz
+ uint8_t VCXO_Pull_Range_ppm; //can range from +-30 ppm to 240ppm
+} Si5351_OSCConfigTypeDef;
+
+/*
+ * This section contains data structures for configuring the
+ * PLL (PLLA and PLLB)
+ */
+
+#define REG_PLL_CLOCK_SOURCE 15
+#define PLLA_CLOCK_SOURCE_MASK 0x04
+#define PLLB_CLOCK_SOURCE_MASK 0x08
+
+//this selects the clock source for the PLL
+typedef enum
+{
+ PLL_Clock_Source_XTAL = 0x00,
+ PLL_Clock_Source_CLKIN = 0x0C //0x04 for PLLA, 0x08 for PLLB, use mask!
+} Si5351_PLLClockSourceTypeDef;
+
+#define REG_FB_INT 22
+#define FB_INT_MASK 0x40
+
+#define REG_PLL_RESET 177
+#define PLLA_RESET_MASK 0x20
+#define PLLB_RESET_MASK 0x80
+
+#define REG_MSN_P1_0_7 30
+#define REG_MSN_P1_8_15 29
+#define REG_MSN_P1_16_17 28
+#define MSN_P1_16_17_MASK 0x03
+
+#define REG_MSN_P2_0_7 33
+#define REG_MSN_P2_8_15 32
+#define REG_MSN_P2_16_19 31
+#define MSN_P2_16_19_MASK 0x0F
+
+#define REG_MSN_P3_0_7 27
+#define REG_MSN_P3_8_15 26
+#define REG_MSN_P3_16_19 31
+#define MSN_P3_16_19_MASK 0xF0
+
+#define MSNA_MSNB_OFFSET 8
+
+typedef struct
+{
+ uint32_t PLL_Multiplier_Integer;
+ uint32_t PLL_Multiplier_Numerator;
+ uint32_t PLL_Multiplier_Denominator;
+ Si5351_PLLClockSourceTypeDef PLL_Clock_Source;
+ Si5351_PLLCapacitiveLoadTypeDef PLL_Capacitive_Load;
+} Si5351_PLLConfigTypeDef;
+
+/*
+ * This section contains data structures for configuring the
+ * Spread Spectrum feature.
+ */
+
+#define REG_SSC_MODE 151
+#define SSC_MODE_MASK 0x80
+
+//this selects the Spread Spectrum mode
+typedef enum
+{
+ SS_Mode_DownSpread = 0x00,
+ SS_Mode_CenterSpread = 0x80
+} Si5351_SSModeTypeDef;
+
+typedef enum
+{
+ SS_NCLK_0 = 0x00,
+ SS_NCLK_1 = 0x10,
+ SS_NCLK_2 = 0x20,
+ SS_NCLK_3 = 0x30,
+ SS_NCLK_4 = 0x40,
+ SS_NCLK_5 = 0x50,
+ SS_NCLK_6 = 0x60,
+ SS_NCLK_7 = 0x70,
+ SS_NCLK_8 = 0x80,
+ SS_NCLK_9 = 0x90,
+ SS_NCLK_10 = 0xA0,
+ SS_NCLK_11 = 0xB0,
+ SS_NCLK_12 = 0xC0,
+ SS_NCLK_13 = 0xD0,
+ SS_NCLK_14 = 0xE0,
+ SS_NCLK_15 = 0xF0
+} Si5351_SSNCLKTypeDef;
+
+#define REG_SSDN_P1_0_7 153
+#define REG_SSDN_P1_8_11 154
+#define SSDN_P1_8_11_MASK 0x0F
+
+#define REG_SSDN_P2_0_7 150
+#define REG_SSDN_P2_8_14 149
+#define SSDN_P2_8_14_MASK 0x7F
+
+#define REG_SSDN_P3_0_7 152
+#define REG_SSDN_P3_8_14 151
+#define SSDN_P3_8_14_MASK 0x7F
+
+#define REG_SSUDP_0_7 155
+#define REG_SSUDP_8_11 154
+#define SSUDP_8_11_MASK 0xF0
+
+#define REG_SSUP_P1_0_7 160
+#define REG_SSUP_P1_8_11 161
+#define SSUP_P1_8_11_MASK 0x0F
+
+#define REG_SSUP_P2_0_7 157
+#define REG_SSUP_P2_8_14 156
+#define SSUP_P2_8_14_MASK 0x7F
+
+#define REG_SSUP_P3_0_7 159
+#define REG_SSUP_P3_8_14 158
+#define SSUP_P3_8_14_MASK 0x7F
+
+#define REG_SSC_EN 149
+#define SSC_EN_MASK 0x80
+
+#define REG_SS_NCLK 161
+#define SS_NCLK_MASK 0xF0
+
+typedef struct
+{
+ uint32_t SS_Amplitude_ppm; //amplitude of the SS feature in ppm of center frequency
+ EnableState SS_Enable;
+ Si5351_SSModeTypeDef SS_Mode;
+ Si5351_SSNCLKTypeDef SS_NCLK;
+} Si5351_SSConfigTypeDef;
+
+/*
+ * This section contains data structures for configuring the
+ * Output Multisynth.
+ */
+
+//this selects the Multisynth clock source
+typedef enum
+{
+ MS_Clock_Source_PLLA = 0x00,
+ MS_Clock_Source_PLLB = 0x20
+} Si5351_MSClockSourceTypeDef;
+
+#define REG_MS_P1_0_7 46
+#define REG_MS_P1_8_15 45
+#define REG_MS_P1_16_17 44
+#define MS_P1_16_17_MASK 0x03
+
+#define REG_MS_P2_0_7 49
+#define REG_MS_P2_8_15 48
+#define REG_MS_P2_16_19 47
+#define MS_P2_16_19_MASK 0x0F
+
+#define REG_MS_P3_0_7 43
+#define REG_MS_P3_8_15 42
+#define REG_MS_P3_16_19 47
+#define MS_P3_16_19_MASK 0xF0
+
+#define REG_MS67_P1 90
+
+#define REG_MS_INT 16
+#define MS_INT_MASK 0x40
+
+#define REG_MS_DIVBY4 44
+#define MS_DIVBY4_MASK 0x0C
+
+#define REG_MS_SRC 16
+#define MS_SRC_MASK 0x20
+
+#define MS_SETUP_STEP 1
+#define MS_DIVIDER_STEP 8
+
+typedef struct
+{
+ Si5351_MSClockSourceTypeDef MS_Clock_Source; //select source on MS input
+ uint32_t MS_Divider_Integer; //the integer part of divider, called "a"
+ uint32_t MS_Divider_Numerator; //the numerator, called "b"
+ uint32_t MS_Divider_Denominator; //the denominator, called "c"
+} Si5351_MSConfigTypeDef; //sets MS divider ( a+(b/c) ) and clock (PLLA/PLLB)
+
+/*
+ * This section contains data structures for configuring the
+ * CLK, R divider and output stage (joined together because they make
+ * a tight block without any multiplexer).
+ */
+
+#define REG_CLK_SRC 16
+#define CLK_SRC_MASK 0x0C
+
+//this sets the CLK source clock
+typedef enum
+{
+ CLK_Clock_Source_XTAL = 0x00,
+ CLK_Clock_Source_CLKIN = 0x04,
+ CLK_Clock_Source_MS0_MS4 = 0x08, //this uses MS0 for CLK0..3 and MS4 for CLK4..7
+ CLK_Clock_Source_MS_Own = 0x0C //this uses MSx for CLKx
+} Si5351_CLKClockSourceTypeDef; //configures multiplexer on CLK input
+
+#define REG_CLK_R_DIV 44
+#define CLK_R_DIV_MASK 0x70
+
+#define REG_CLK_R67_DIV 92
+#define CLK_R67_DIV_MASK 0x07
+
+//this sets the R divider ratio
+typedef enum
+{
+ CLK_R_Div1 = 0x00,
+ CLK_R_Div2 = 0x10,
+ CLK_R_Div4 = 0x20,
+ CLK_R_Div8 = 0x30,
+ CLK_R_Div16 = 0x40,
+ CLK_R_Div32 = 0x50,
+ CLK_R_Div64 = 0x60,
+ CLK_R_Div128 = 0x70
+} Si5351_CLKRDivTypeDef;
+
+#define REG_CLK_DIS_STATE 24
+#define CLK_DIS_STATE_MASK 0x03
+
+//this sets output buffer behaviour when disabled
+typedef enum
+{
+ CLK_Disable_State_LOW = 0x00,
+ CLK_Disable_State_HIGH = 0x01,
+ CLK_Disable_State_HIGH_Z = 0x02, //three-stated when off
+ CLK_Disable_State_ALWAYS_ON = 0x03 //cannot be disabled
+} Si5351_CLKDisableStateTypeDef;
+
+#define REG_CLK_IDRV 16
+#define CLK_IDRV_MASK 0x03
+
+//this sets current drive of the output buffer
+typedef enum
+{
+ CLK_I_Drv_2mA = 0x00,
+ CLK_I_Drv_4mA = 0x01,
+ CLK_I_Drv_6mA = 0x02,
+ CLK_I_Drv_8mA = 0x03
+} Si5351_CLKIDrvTypeDef;
+
+#define REG_CLK_PHOFF 165
+#define CLK_PHOFF_MASK 0x7F
+
+#define REG_CLK_EN 3
+
+#define REG_CLK_INV 16
+#define CLK_INV_MASK 0x10
+
+#define REG_CLK_PDN 16
+#define CLK_PDN_MASK 0x80
+
+#define REG_CLK_OEB 9
+
+#define CLK_PHOFF_STEP 1
+#define CLK_SETUP_STEP 1
+#define CLK_R_DIV_STEP 8
+
+typedef struct
+{
+ Si5351_CLKClockSourceTypeDef CLK_Clock_Source; //clock source
+
+ /* this sets the time offset of the CLK channel, basic unit
+ * is one quarter of the VCO period (90deg offset),
+ * set it to 4*fVCO*toffset, the value is 7-bit, the max time offset
+ * varies between 35 and 53 ns (1 cycle for 28 and 19 MHz, respectively)
+ * according to the current frequency of the VCO
+ */
+ uint8_t CLK_QuarterPeriod_Offset;
+
+ Si5351_CLKRDivTypeDef CLK_R_Div; //R divider value (only powers of 2)
+ EnableState CLK_Invert; //invert output clock
+ EnableState CLK_Enable; //enable flag
+ EnableState CLK_PowerDown; //powerdown flag
+ Si5351_CLKDisableStateTypeDef CLK_Disable_State; //sets output behaviour when disabled
+ Si5351_CLKIDrvTypeDef CLK_I_Drv; //output driver current drive strength
+ EnableState CLK_Use_OEB_Pin; //allows using OEB pin to enable clock
+} Si5351_CLKConfigTypeDef;
+
+/*
+ * This section contains main data structure for Si5351 configuration
+ */
+
+typedef struct
+{
+ /*
+ * These are frequencies of the input clocks, set it in Hz.
+ */
+ uint32_t f_XTAL;
+ uint32_t f_CLKIN;
+
+ //Interrupt masking - enabling it disables the int source from pulling INTR low
+ EnableState Interrupt_Mask_SysInit;
+ EnableState Interrupt_Mask_PLLB;
+ EnableState Interrupt_Mask_PLLA;
+ EnableState Interrupt_Mask_CLKIN;
+ EnableState Interrupt_Mask_XTAL;
+
+ //Fanout enable - enables internal clock routing
+ EnableState Fanout_MS_EN;
+ EnableState Fanout_XO_EN;
+ EnableState Fanout_CLKIN_EN;
+
+ I2C_TypeDef *I2Cx; //the I2C interface that will be used
+ uint8_t HW_I2C_Address; //I2C address of the Si5351 for the packages with A0 pin
+ //(also, some duds with strange address reported)
+ Si5351_OSCConfigTypeDef OSC; //Oscillator, CLKIN and VCXO settings
+ Si5351_PLLConfigTypeDef PLL[2]; //PLL settings for PLLA and PLLB
+ Si5351_MSConfigTypeDef MS[8]; //MultiSynth[0..7] settings
+ Si5351_CLKConfigTypeDef CLK[8]; //CLK[0..7], R divider and output stage settings
+ Si5351_SSConfigTypeDef SS; //spread spectrum settings
+} Si5351_ConfigTypeDef;
+
+/*
+ * Typedefs for selecting PLL, MS and CLK to be used
+ */
+
+//this selects PLL channel
+typedef enum
+{
+ PLL_A = 0,
+ PLL_B = 1
+} Si5351_PLLChannelTypeDef;
+
+//this selects Multisynth channel
+typedef enum
+{
+ MS0 = 0,
+ MS1 = 1,
+ MS2 = 2,
+ MS3 = 3,
+ MS4 = 4,
+ MS5 = 5,
+ MS6 = 6,
+ MS7 = 7
+} Si5351_MSChannelTypeDef;
+
+//this selects CLK channel
+typedef enum
+{
+ CLK0 = 0,
+ CLK1 = 1,
+ CLK2 = 2,
+ CLK3 = 3,
+ CLK4 = 4,
+ CLK5 = 5,
+ CLK6 = 6,
+ CLK7 = 7
+} Si5351_CLKChannelTypeDef;
+
+//this selects device status flag
+typedef enum
+{
+ StatusBit_SysInit = DEV_SYS_INIT_MASK,
+ StatusBit_PLLA = DEV_STKY_LOL_A_MASK,
+ StatusBit_PLLB = DEV_LOL_B_MASK,
+ StatusBit_CLKIN = DEV_LOS_CLKIN_MASK,
+ StatusBit_XTAL = DEV_LOS_XTAL_MASK,
+} Si5351_StatusBitTypeDef;
+
+//these write to and read from a Si5351 register, for porting
+//purposes, these functions should be the only ones which should need edits
+int Si5351_WriteRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address, uint8_t reg_data);
+uint8_t Si5351_ReadRegister(Si5351_ConfigTypeDef *Si5351_ConfigStruct, uint8_t reg_address);
+
+void Si5351_StructInit(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+
+void Si5351_OSCConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+
+EnableState Si5351_CheckStatusBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
+EnableState Si5351_CheckStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
+void Si5351_InterruptConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+void Si5351_ClearStickyBit(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_StatusBitTypeDef StatusBit);
+
+void Si5351_PLLConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel);
+void Si5351_PLLReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_PLLChannelTypeDef PLL_Channel);
+void Si5351_PLLSimultaneousReset(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+
+void Si5351_SSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+
+void Si5351_MSConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_MSChannelTypeDef MS_Channel);
+
+void Si5351_CLKPowerCmd(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel);
+void Si5351_CLKConfig(Si5351_ConfigTypeDef *Si5351_ConfigStruct, Si5351_CLKChannelTypeDef CLK_Channel);
+
+int Si5351_Init(Si5351_ConfigTypeDef *Si5351_ConfigStruct);
+#endif /* SI5351_H_ */
diff --git a/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_rtc.c b/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_rtc.c
index ec30f8d..5e68092 100644
--- a/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_rtc.c
+++ b/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_rtc.c
@@ -2666,7 +2666,7 @@ uint8_t RTC_ByteToBcd2(uint8_t Value)
while (temp >= 10U)
{
bcdhigh++;
- temp -= 10U;
+ temp -= 10;
}
return ((uint8_t)(bcdhigh << 4U) | temp);
diff --git a/stm32l4a6zg-f0x.at1.ioc b/stm32l4a6zg-f0x.at1.ioc
index 77329bd..077114d 100644
--- a/stm32l4a6zg-f0x.at1.ioc
+++ b/stm32l4a6zg-f0x.at1.ioc
@@ -13,7 +13,7 @@ Dma.RequestsNb=1
FREERTOS.BinarySemaphores01=si5351,Dynamic,NULL;command,Dynamic,NULL
FREERTOS.FootprintOK=true
FREERTOS.IPParameters=Tasks01,configUSE_NEWLIB_REENTRANT,FootprintOK,configTOTAL_HEAP_SIZE,BinarySemaphores01,configENABLE_BACKWARD_COMPATIBILITY,configUSE_POSIX_ERRNO
-FREERTOS.Tasks01=defaultTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;terminalTask,24,256,start_terminal_task,Default,NULL,Dynamic,NULL,NULL;morseTask,24,128,start_morse_task,Default,si5351_inst,Dynamic,NULL,NULL;clk2Task,40,128,start_clk2_task,Default,si5351_inst,Dynamic,NULL,NULL
+FREERTOS.Tasks01=timeTask,24,128,start_time_task,Default,NULL,Dynamic,NULL,NULL;terminalTask,24,256,start_terminal_task,Default,NULL,Dynamic,NULL,NULL;morseTask,24,128,start_morse_task,Default,si5351_inst,Dynamic,NULL,NULL;clk2Task,40,128,start_clk2_task,Default,si5351_inst,Dynamic,NULL,NULL
FREERTOS.configENABLE_BACKWARD_COMPATIBILITY=0
FREERTOS.configTOTAL_HEAP_SIZE=50000
FREERTOS.configUSE_NEWLIB_REENTRANT=1
@@ -511,4 +511,4 @@ VP_RTC_VS_RTC_Calendar.Signal=RTC_VS_RTC_Calendar
VP_SYS_VS_tim6.Mode=TIM6
VP_SYS_VS_tim6.Signal=SYS_VS_tim6
board=NUCLEO-L4A6ZG
-isbadioc=false
+isbadioc=true