/** ****************************************************************************** * @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, created Feb 16, 2018 ******************************************************************************/ /* Defines for compilation ---------------------------------------------------*/ #define OPTIMIZED 1 /* Includes ------------------------------------------------------------------*/ #include // @TODO try to get rid off including the HAL here, using pointer to the specified functions #ifdef DEBUG #include #include #endif #include "stm32l4xx_hal.h" #include "stm32_si5351.h" /* Private typedef -----------------------------------------------------------*/ /* @brief Si5351 handle structure definition */ typedef struct __SI5351_HandleTypeDef { void * i2c_handle; uint32_t xtal_frequency; /*!< XTAL or CLKIN frequency */ int si5351_num; struct __SI5351_HandleTypeDef *next; char debug_msg[1000]; 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 */ } si5351_HandleTypeDef; #ifndef __GNUC__ /*__arm__*/ /* Keil ARM compiler does not support typeof */ #define snprintb(buf, n, value) __fprintb(buf, n, (uint32_t *)&value, sizeof(uint32_t)) #define snprintb16(buf, n, value) __fprintb(buf, n, (uint16_t *)&value, sizeof(uint16_t)) #define snprintb8(buf, n, value) __fprintb(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 define ------------------------------------------------------------*/ #define SI5351_VERSION 1 /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ si5351_HandleTypeDef * first_handle = NULL; /* pointer to the first instancee */ int si5351_errno = 0; /* error_number for functions whith return == NULL */ /* Private function prototypes -----------------------------------------------*/ int __fprintb(FILE *stream, void *value, size_t size); /* Private functions ---------------------------------------------------------*/ /* wrapper function for receiving bytes from I2C bus */ 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; } } 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<<1, (uint16_t)regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff); return si5351_error_status_i2c(status); } 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<<1, (uint16_t)regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff); return si5351_error_status_i2c(status); } /** @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) * @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) { 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; } /* 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; 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 } free(si5351_handle); return 0; } /** @brief Check if there is any I2C device ready on the bus * @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_isready(si5351_inst_t inst) { int status; if(!inst) return -EINVAL; /* call HAL function for device ready check */ status = HAL_I2C_IsDeviceReady(inst->i2c_handle, inst->i2c_address << 1, 3, 100 /*HAL_MAX_DELAY*/ ); // HAL_MAX_DELAY is blocking, use 10 ms /* @TODO: create a pointer to that function for more flexiblity using other tools as HAL */ return status; } /** @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; int cx = 0; if(!inst) 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; /* 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 */ #ifdef 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; #ifdef 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 */ #ifdef DEBUG status = si5351_read(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 } while(0); return status; } char * si5351_read_debug_msg(si5351_inst_t inst) { if (!inst) return NULL; return inst->debug_msg; } /*! * @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; } 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; } #if 0 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; } /* * 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 * */ //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