/* * si5351_driver.c * * Created on: 16. aug. 2024 * Author: Christian L. V. Madsen (OZ1CM) */ #include "si5351_driver.h" #include #include #include enum{ SI5351_I2C_GET = 0, SI5351_I2C_SET = 1, }; static int readRegister(si5351_driver *inst,uint8_t data_addr, uint8_t *data, uint32_t len){ // Write what kind of addr we would like to read from: inst->i2c_transfer_evt(inst->i2c_transfer_inst,&data_addr,1, SI5351_I2C_SET); // Read data: inst->i2c_transfer_evt(inst->i2c_transfer_inst,data,len, SI5351_I2C_GET); //HAL_Delay(1); return 0; } static int writeRegister(si5351_driver *inst,uint8_t data_addr, uint8_t *data, uint32_t len){ uint8_t *data_ptr = malloc(len+1); *data_ptr = data_addr; // Copy data memcpy(&data_ptr[1],data,len); // Write data: inst->i2c_transfer_evt(inst->i2c_transfer_inst,data_ptr,len+1, SI5351_I2C_SET); free(data_ptr); /* uint8_t txBuf[512] = {data_addr}; uint8_t *txBuf_ptr = &txBuf[1]; // add register addr to data: memcpy(txBuf_ptr,data,len); // Write data: inst->i2c_transfer_evt(inst->i2c_transfer_inst,txBuf,len+1, SI5351_I2C_SET); //HAL_Delay(1);*/ return 0; } int cm_setPllParamRaw(si5351_driver *inst, si5351_PLLs sel_pll, uint32_t MSNx_P1, uint32_t MSNx_P2, uint32_t MSNx_P3){ switch(sel_pll){ case SI5351_PLL_A: inst->device_data.multiSynthNAParam.MSNx_P1_17_16 = (MSNx_P1 >> 16) & 0x2; inst->device_data.multiSynthNAParam.MSNx_P1_15_8 = (MSNx_P1 >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P1_7_0 = MSNx_P1 & 0xff; inst->device_data.multiSynthNAParam.MSNx_P2_19_16 = ((MSNx_P2 & 0x000F0000) >> 16); inst->device_data.multiSynthNAParam.MSNx_P2_15_8 = (MSNx_P2 >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P2_7_0 = MSNx_P2 & 0xff; inst->device_data.multiSynthNAParam.MSNx_P3_19_16 = ((MSNx_P3 & 0x000F0000) >> 12); inst->device_data.multiSynthNAParam.MSNx_P3_15_8 = (MSNx_P3 >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P3_7_0 = MSNx_P3 & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_NA_0, (uint8_t*) &inst->device_data.multiSynthNAParam, sizeof(si5351_multiSynthNxParameters_t)); break; case SI5351_PLL_B: inst->device_data.multiSynthNBParam.MSNx_P1_17_16 = (MSNx_P1 >> 16) & 0x2; inst->device_data.multiSynthNBParam.MSNx_P1_15_8 = (MSNx_P1 >> 8) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P1_7_0 = MSNx_P1 & 0xff; inst->device_data.multiSynthNBParam.MSNx_P2_19_16 = ((MSNx_P2 & 0x000F0000) >> 16); inst->device_data.multiSynthNBParam.MSNx_P2_15_8 = (MSNx_P2 >> 8) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P2_7_0 = MSNx_P2 & 0xff; inst->device_data.multiSynthNBParam.MSNx_P3_19_16 = ((MSNx_P3 & 0x000F0000) >> 12); inst->device_data.multiSynthNBParam.MSNx_P3_15_8 = (MSNx_P3 >> 8) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P3_7_0 = MSNx_P3 & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_NB_0, (uint8_t*) &inst->device_data.multiSynthNBParam, sizeof(si5351_multiSynthNxParameters_t)); break; default: break; } // reset PLL's cm_resetPLLs(inst, 1,1); return 0; } int cm_setOutputMultiSynthRaw(si5351_driver *inst,si5351_Outputs clk_output, uint32_t MSx_P1, uint32_t MSx_P2, uint32_t MSx_P3){ switch(clk_output){ case SI5351_OUTPUT_0: inst->device_data.multiSynth0Param.MSx_P1_17_16 = (MSx_P1 >> 16) & 0x3; // last division.. inst->device_data.multiSynth0Param.MSx_P1_15_8 = (MSx_P1 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P1_7_0 = MSx_P1 & 0xff; inst->device_data.multiSynth0Param.MSx_P2_19_16 = (MSx_P2 >> 16) & 0x3; inst->device_data.multiSynth0Param.MSx_P2_15_8 = (MSx_P2 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P2_7_0 = MSx_P2 & 0xff; inst->device_data.multiSynth0Param.MSx_P3_19_16 = (MSx_P3 >> 12) & 0x3; inst->device_data.multiSynth0Param.MSx_P3_15_8 = (MSx_P3 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P3_7_0 = MSx_P3 & 0xff; inst->device_data.multiSynth0Param.MSx_DIVBY4 = 0; inst->device_data.multiSynth0Param.Rx_DIV = 0; writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_0, (uint8_t*) &inst->device_data.multiSynth0Param, sizeof(si5351_multiSynthxParameters_t)); break; case SI5351_OUTPUT_1: inst->device_data.multiSynth1Param.MSx_P1_17_16 = (MSx_P1 >> 16) & 0x3; // last division.. inst->device_data.multiSynth1Param.MSx_P1_15_8 = (MSx_P1 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P1_7_0 = MSx_P1 & 0xff; inst->device_data.multiSynth1Param.MSx_P2_19_16 = (MSx_P2 >> 16) & 0x3; inst->device_data.multiSynth1Param.MSx_P2_15_8 = (MSx_P2 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P2_7_0 = MSx_P2 & 0xff; inst->device_data.multiSynth1Param.MSx_P3_19_16 = (MSx_P3 >> 12) & 0x3; inst->device_data.multiSynth1Param.MSx_P3_15_8 = (MSx_P3 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P3_7_0 = MSx_P3 & 0xff; inst->device_data.multiSynth1Param.MSx_DIVBY4 = 0; inst->device_data.multiSynth1Param.Rx_DIV = 0; writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_1, (uint8_t*) &inst->device_data.multiSynth1Param, sizeof(si5351_multiSynthxParameters_t)); break; case SI5351_OUTPUT_2: inst->device_data.multiSynth2Param.MSx_P1_17_16 = (MSx_P1 >> 16) & 0x3; // last division.. inst->device_data.multiSynth2Param.MSx_P1_15_8 = (MSx_P1 >> 8) & 0xff; inst->device_data.multiSynth2Param.MSx_P1_7_0 = MSx_P1 & 0xff; inst->device_data.multiSynth2Param.MSx_P2_19_16 = (MSx_P2 >> 16) & 0x3; inst->device_data.multiSynth2Param.MSx_P2_15_8 = (MSx_P2 >> 8) & 0xff; inst->device_data.multiSynth2Param.MSx_P2_7_0 = MSx_P2 & 0xff; inst->device_data.multiSynth2Param.MSx_P3_19_16 = (MSx_P3 >> 12) & 0x3; inst->device_data.multiSynth2Param.MSx_P3_15_8 = (MSx_P3 >> 8) & 0xff; inst->device_data.multiSynth2Param.MSx_P3_7_0 = MSx_P3 & 0xff; inst->device_data.multiSynth2Param.MSx_DIVBY4 = 0; inst->device_data.multiSynth2Param.Rx_DIV = 0; writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_2, (uint8_t*) &inst->device_data.multiSynth2Param, sizeof(si5351_multiSynthxParameters_t)); default: break; } return 0; } int cm_setPLLParameters(si5351_driver *inst,si5351_PLLs sel_pll, uint32_t a, uint32_t b, uint32_t c){ uint32_t temp_val = 0; switch(sel_pll){ case SI5351_PLL_A: temp_val = (float)128 * (float)a + ((float)128 * ((float)b/(float)c)) - (float)512; inst->device_data.multiSynthNAParam.MSNx_P1_17_16 = (temp_val >> 16) & 0x2; inst->device_data.multiSynthNAParam.MSNx_P1_15_8 = (temp_val >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P1_7_0 = temp_val & 0xff; temp_val = 0; temp_val = (uint32_t)(128 * b - c * floor(128 * ((float)b / (float)c))); inst->device_data.multiSynthNAParam.MSNx_P2_19_16 = ((temp_val & 0x000F0000) >> 16); inst->device_data.multiSynthNAParam.MSNx_P2_15_8 = (temp_val >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P2_7_0 = temp_val & 0xff; inst->device_data.multiSynthNAParam.MSNx_P3_19_16 = ((c & 0x000F0000) >> 12); inst->device_data.multiSynthNAParam.MSNx_P3_15_8 = (c >> 8) & 0xff; inst->device_data.multiSynthNAParam.MSNx_P3_7_0 = c & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_NA_0, (uint8_t*) &inst->device_data.multiSynthNAParam, sizeof(si5351_multiSynthNxParameters_t)); break; case SI5351_PLL_B: temp_val = 128 * a + (128 * (b/c)) - 512; inst->device_data.multiSynthNBParam.MSNx_P1_17_16 = (temp_val >> 15) & 0x2; inst->device_data.multiSynthNBParam.MSNx_P1_15_8 = (temp_val >> 8) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P1_7_0 = temp_val & 0xff; temp_val = 0; temp_val = 128 * b - c * (128 * (b/c)); inst->device_data.multiSynthNBParam.MSNx_P2_19_16 = (temp_val >> 15) & 0x4; inst->device_data.multiSynthNBParam.MSNx_P2_15_8 = (temp_val >> 7) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P2_7_0 = temp_val & 0xff; inst->device_data.multiSynthNBParam.MSNx_P3_19_16 = (c >> 15) & 0x4; inst->device_data.multiSynthNBParam.MSNx_P3_15_8 = (c >> 7) & 0xff; inst->device_data.multiSynthNBParam.MSNx_P3_7_0 = c & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_NB_0, (uint8_t*) &inst->device_data.multiSynthNBParam, sizeof(si5351_multiSynthNxParameters_t)); break; } // reset PLL's cm_resetPLLs(inst, 1,1); return 0; } int cm_setOutputMultiSynth(si5351_driver *inst,si5351_Outputs clk_output, uint32_t d, uint32_t e, uint32_t f){ /* Integer mode */ uint32_t P1 = 128 * d - 512; uint32_t P2 = e; uint32_t P3 = f; switch(clk_output){ case SI5351_OUTPUT_0: inst->device_data.multiSynth0Param.MSx_P1_17_16 = (P1 >> 16) & 0x3; // last division.. inst->device_data.multiSynth0Param.MSx_P1_15_8 = (P1 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P1_7_0 = P1 & 0xff; inst->device_data.multiSynth0Param.MSx_P2_19_16 = (P2 >> 16) & 0x3; inst->device_data.multiSynth0Param.MSx_P2_15_8 = (P2 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P2_7_0 = P2 & 0xff; inst->device_data.multiSynth0Param.MSx_P3_19_16 = (P3 >> 12) & 0x3; inst->device_data.multiSynth0Param.MSx_P3_15_8 = (P3 >> 8) & 0xff; inst->device_data.multiSynth0Param.MSx_P3_7_0 = P3 & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_0, (uint8_t*) &inst->device_data.multiSynth0Param, sizeof(si5351_multiSynthxParameters_t)); break; case SI5351_OUTPUT_1: inst->device_data.multiSynth1Param.MSx_P1_17_16 = (P1 >> 16) & 0x3; // last division.. inst->device_data.multiSynth1Param.MSx_P1_15_8 = (P1 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P1_7_0 = P1 & 0xff; inst->device_data.multiSynth1Param.MSx_P2_19_16 = (P2 >> 16) & 0x3; inst->device_data.multiSynth1Param.MSx_P2_15_8 = (P2 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P2_7_0 = P2 & 0xff; inst->device_data.multiSynth1Param.MSx_P3_19_16 = (P3 >> 12) & 0x3; inst->device_data.multiSynth1Param.MSx_P3_15_8 = (P3 >> 8) & 0xff; inst->device_data.multiSynth1Param.MSx_P3_7_0 = P3 & 0xff; writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_1, (uint8_t*) &inst->device_data.multiSynth1Param, sizeof(si5351_multiSynthxParameters_t)); break; default: break; } return 0; } int cm_setOutputEnable(si5351_driver *inst,si5351_Outputs clk_output, si5351_Outputs_state outputState){ switch (clk_output) { case SI5351_OUTPUT_0: inst->device_data.outputEnableControl.CLK0_OEB = outputState; break; case SI5351_OUTPUT_1: inst->device_data.outputEnableControl.CLK1_OEB = outputState; break; case SI5351_OUTPUT_2: inst->device_data.outputEnableControl.CLK2_OEB = outputState; break; default: break; } writeRegister(inst,SI5351_REG_OUTPUT_ENABLE_CONTROL, (uint8_t*) &inst->device_data.outputEnableControl, sizeof(si5351_outputEnableControl_t)); return 0; } int cm_setCLKControl(si5351_driver *inst, si5351_Outputs clk_output, si5351_CLK_PDN clk_pdn){ switch (clk_output) { case SI5351_OUTPUT_0: inst->device_data.CLK0_control.CLK_PDN = clk_pdn; inst->device_data.CLK0_control.MSx_INT = 1; inst->device_data.CLK0_control.CLK_SRC = 0b11; inst->device_data.CLK0_control.MSx_SRC = 0; inst->device_data.CLK0_control.CLK_IDRV = SI5351_DRIVE_STRENGTH_8MA; writeRegister(inst,SI5351_REG_CLK_0_CONTROL, (uint8_t*) &inst->device_data.CLK0_control, sizeof(si5351_CLK_Control_t)); break; case SI5351_OUTPUT_1: inst->device_data.CLK1_control.CLK_PDN = clk_pdn; inst->device_data.CLK1_control.MSx_INT = 1; inst->device_data.CLK1_control.MSx_SRC = 1; inst->device_data.CLK1_control.CLK_SRC = 0b11; inst->device_data.CLK1_control.CLK_IDRV = SI5351_DRIVE_STRENGTH_8MA; writeRegister(inst,SI5351_REG_CLK_1_CONTROL, (uint8_t*) &inst->device_data.CLK1_control, sizeof(si5351_CLK_Control_t)); break; case SI5351_OUTPUT_2: inst->device_data.CLK2_control.CLK_PDN = clk_pdn; inst->device_data.CLK2_control.MSx_INT = 1; inst->device_data.CLK2_control.MSx_SRC = 1; inst->device_data.CLK2_control.CLK_SRC = 0b00; inst->device_data.CLK2_control.CLK_IDRV = SI5351_DRIVE_STRENGTH_8MA; writeRegister(inst,SI5351_REG_CLK_2_CONTROL, (uint8_t*) &inst->device_data.CLK2_control, sizeof(si5351_CLK_Control_t)); break; default: break; } return 0; } int cm_setInputSource(si5351_driver *inst, si5351_ClkSource clk_source){ switch(clk_source){ case SI5351_CLK_SOURCE_XTAL: inst->device_data.pllInputSource.PLLA_SRC = 0; inst->device_data.pllInputSource.PLLB_SRC = 0; break; case SI5351_CLK_SOURCE_CLOCKSOURCE: inst->device_data.pllInputSource.PLLA_SRC = 1; break; default: break; } // Write to register: writeRegister(inst,SI5351_REG_PLL_INPUT_SOURCE, (uint8_t*) &inst->device_data.pllInputSource, sizeof(si5351_PLLInputSource_t)); return 0; } int cm_resetPLLs(si5351_driver *inst, uint8_t reset_PLLA, uint8_t reset_PLLB){ inst->device_data.PLL_Reset.PLLA_RST = (reset_PLLA != 0)? 1 : 0; inst->device_data.PLL_Reset.PLLB_RST = (reset_PLLB != 0)? 1 : 0; // Write to register: writeRegister(inst,SI5351_REG_PLL_RESET, (uint8_t*) &inst->device_data.PLL_Reset, sizeof(si5351_PLL_Reset_t)); return 0; } uint8_t cm_si5351_getRevisionNumber(si5351_driver *inst){ // Read Device Status register: readRegister(inst,0x00, (uint8_t *) &inst->device_data.deviceStatus, sizeof(si5351_deviceStat_t)); return inst->device_data.deviceStatus.REVID; } int cm_si5351_init(si5351_driver *inst, void *i2c_transfer_inst, setGet_I2C_Event_fpt i2c_transfer_evt){ if(inst == NULL)return -1; if(i2c_transfer_inst == NULL)return -1; if(i2c_transfer_evt == NULL)return -1; inst->i2c_transfer_inst = i2c_transfer_inst; inst->i2c_transfer_evt = i2c_transfer_evt; int ret = 0; memset(&(inst->device_data),0x00,sizeof(si5351_data)); /* Disable all outputs setting CLKx_DIS high */ uint8_t temp = 0xff; writeRegister(inst,SI5351_REG_OUTPUT_ENABLE_CONTROL,&temp,1); /* Power down all output drivers */ temp = 0x80; writeRegister(inst,SI5351_REG_CLK_0_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_1_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_2_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_3_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_4_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_5_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_6_CONTROL,&temp,1); writeRegister(inst,SI5351_REG_CLK_7_CONTROL,&temp,1); temp = SI5351_CRYSTAL_LOAD_10PF; writeRegister(inst,SI5351_REG_CRYSTAL_LOAD_CAPACITANCE,&temp,1); // Enable xtal clk.. temp = 0b01000000; writeRegister(inst,SI5351_REG_FANOUT_ENABLE,&temp,1); return ret; }