rename si5351 driver, remove external files

This commit is contained in:
Thomas Kuschel 2022-07-16 20:39:01 +02:00
parent 152ccf00e4
commit c35afd9bf8
12 changed files with 2532 additions and 2530 deletions

View File

@ -81,9 +81,9 @@
</toolChain>
</folderInfo>
<sourceEntries>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
<entry excluding="external_sources" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
</sourceEntries>
</configuration>
</storageModule>
@ -135,10 +135,10 @@
<listOptionValue builtIn="false" value="../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2"/>
<listOptionValue builtIn="false" value="../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F"/>
</option>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.extra.416845131" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.extra" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.w_switch_enum.1231848687" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.w_switch_enum" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.pedantic.1768191128" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.pedantic" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.treataserror.302105726" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.treataserror" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.extra.416845131" name="Enable extra warning flags (-Wextra)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.extra" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.w_switch_enum.1231848687" name="Warn if switch is used on an enum type and the switch statement lacks case for some enumerations (-Wswitch-enum)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.w_switch_enum" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.pedantic.1768191128" name="Issue all warnings demanded by strict ISO C and ISO C++ (-pedantic)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.pedantic" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.treataserror.302105726" name="Treat warnings as errors (-Werror)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.warnings.treataserror" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.105765051" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/>
</tool>
<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.1355958959" name="MCU G++ Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler">
@ -164,9 +164,9 @@
</toolChain>
</folderInfo>
<sourceEntries>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
<entry excluding="external_sources" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
</sourceEntries>
</configuration>
</storageModule>

View File

@ -1,525 +1,149 @@
/*
* si5351.h
/******************************************************************************
* 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
*
* Created on: Feb 16, 2018
* Author: Petr Polasek
* originally written by Petr Polasek, created Feb 16, 2018
******************************************************************************
* DO NOT EDIT THIS FILE FOR CONFIGURATION, USE THE FOLLOWING PROCEDURE:
*
* To make this library useable on any other device than
* STM32Fxxx Cortex Mx, please edit these parts of the library:
* 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:
*
* DEFINES:
* SI5351_I2C_PERIPHERAL - the I2C peripheral name according
* to your devices HAL library
* I2C_TIMEOUT - time for the communication to time out
* /\* USER CODE BEGIN 2 *\/
*
* TYPEDEFS:
* Si5351_ConfigTypeDef - the I2Cx parameter should be changed
* so that its type corresponds to your HAL library
* si5351_inst_t si5351_inst;
* si5351_initialize(&hi2c1);
*
* FUNCTIONS:
* Si5351_WriteRegister
* Si5351_ReadRegister
* You need to write your own I2C handlers here
* /\* 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 <stdint.h>
#include <si5351_reg.h> /* register map of the Si5351 */
/* #include <errno.h> 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 <errno.h>
*/
#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__ */

View File

@ -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 <stdint.h>
#include <stm32_si5351_reg.h> /* register map of the Si5351 */
/* #include <errno.h> 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 <errno.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__ 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_

View File

@ -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 (;;) {

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdlib.h>
#include <string.h>
#ifdef DEBUG
#define SI5351_DEBUG 0
#include <stdio.h>
#endif
#include <assert.h>
/* 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<<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 phase, uint8_t clk) {
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;
}
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;
}

View File

@ -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;

View File

@ -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

View File

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

View File

@ -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);

View File

@ -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