289 lines
7.1 KiB
C
289 lines
7.1 KiB
C
/*
|
|
* si5351_driver.c
|
|
*
|
|
* Created on: 16. aug. 2024
|
|
* Author: Christian L. V. Madsen (OZ1CM)
|
|
*/
|
|
#include "si5351_driver.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
enum{
|
|
SI5351_I2C_GET = 0,
|
|
SI5351_I2C_SET = 1,
|
|
|
|
};
|
|
|
|
static int clear_buffer(si5351_driver *inst){
|
|
|
|
memset(inst->device_data,0, sizeof(inst->device_data));
|
|
|
|
}
|
|
|
|
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, uint32_t len){
|
|
|
|
// Set address
|
|
inst->device_data[0] = data_addr;
|
|
inst->i2c_transfer_evt(inst->i2c_transfer_inst,&inst->device_data[0],len+1, SI5351_I2C_SET);
|
|
clear_buffer(inst);
|
|
return 0;
|
|
}
|
|
|
|
static int writeRegisterbyte(si5351_driver *inst,uint8_t data_addr, uint8_t data){
|
|
|
|
// Set address
|
|
uint8_t buff[2] = {data_addr,data};
|
|
inst->i2c_transfer_evt(inst->i2c_transfer_inst,&buff[0],2, SI5351_I2C_SET);
|
|
clear_buffer(inst);
|
|
return 0;
|
|
}
|
|
|
|
int cm_setPllParamRaw(si5351_driver *inst, si5351_PLLs sel_pll, uint32_t MSNx_P1, uint32_t MSNx_P2, uint32_t MSNx_P3){
|
|
|
|
si5351_multiSynthNxParameters_t *multiSynthNxParam = (void*)&inst->device_data[1];
|
|
|
|
multiSynthNxParam->MSNx_P1_17_16 = (MSNx_P1 >> 16) & 0x3;
|
|
multiSynthNxParam->MSNx_P1_15_8 = (MSNx_P1 >> 8) & 0xff;
|
|
multiSynthNxParam->MSNx_P1_7_0 = MSNx_P1 & 0xff;
|
|
|
|
multiSynthNxParam->MSNx_P2_19_16 = (MSNx_P2 >> 16) & 0xf;
|
|
multiSynthNxParam->MSNx_P2_15_8 = (MSNx_P2 >> 8) & 0xff;
|
|
multiSynthNxParam->MSNx_P2_7_0 = MSNx_P2 & 0xff;
|
|
|
|
multiSynthNxParam->MSNx_P3_19_16 = (MSNx_P3 >> 16) & 0xf;
|
|
multiSynthNxParam->MSNx_P3_15_8 = (MSNx_P3 >> 8) & 0xff;
|
|
multiSynthNxParam->MSNx_P3_7_0 = MSNx_P3 & 0xff;
|
|
|
|
switch(sel_pll){
|
|
|
|
case SI5351_PLL_A:
|
|
writeRegister(inst,SI5351_REG_MULTISYNTH_NA_0, sizeof(si5351_multiSynthNxParameters_t));
|
|
break;
|
|
|
|
case SI5351_PLL_B:
|
|
writeRegister(inst,SI5351_REG_MULTISYNTH_NB_0, 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){
|
|
|
|
|
|
si5351_multiSynthxParameters_t *multiSynthxParam = (void*)&inst->device_data[1];
|
|
multiSynthxParam->MSx_P1_17_16 = (MSx_P1 >> 16) & 0x3; // last division..
|
|
multiSynthxParam->MSx_P1_15_8 = (MSx_P1 >> 8) & 0xff;
|
|
multiSynthxParam->MSx_P1_7_0 = MSx_P1 & 0xff;
|
|
|
|
multiSynthxParam->MSx_P2_19_16 = (MSx_P2 >> 16) & 0xf;
|
|
multiSynthxParam->MSx_P2_15_8 = (MSx_P2 >> 8) & 0xff;
|
|
multiSynthxParam->MSx_P2_7_0 = MSx_P2 & 0xff;
|
|
|
|
multiSynthxParam->MSx_P3_19_16 = (MSx_P3 >> 16) & 0xf;
|
|
multiSynthxParam->MSx_P3_15_8 = (MSx_P3 >> 8) & 0xff;
|
|
multiSynthxParam->MSx_P3_7_0 = MSx_P3 & 0xff;
|
|
|
|
multiSynthxParam->MSx_DIVBY4 = 0;
|
|
multiSynthxParam->Rx_DIV = 0;
|
|
|
|
switch(clk_output){
|
|
|
|
case SI5351_OUTPUT_0:
|
|
writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_0, sizeof(si5351_multiSynthxParameters_t));
|
|
break;
|
|
|
|
case SI5351_OUTPUT_1:
|
|
writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_1, sizeof(si5351_multiSynthxParameters_t));
|
|
break;
|
|
|
|
case SI5351_OUTPUT_2:
|
|
writeRegister(inst,SI5351_REG_MULTISYNTH_OUT_2, sizeof(si5351_multiSynthxParameters_t));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int cm_setOutputEnable(si5351_driver *inst,si5351_Outputs clk_output, si5351_Outputs_state outputState){
|
|
|
|
si5351_outputEnableControl_t *outputEnableControl = (void*)&inst->device_data[1];
|
|
switch (clk_output)
|
|
{
|
|
|
|
case SI5351_OUTPUT_0:
|
|
outputEnableControl->CLK0_OEB = outputState;
|
|
break;
|
|
|
|
case SI5351_OUTPUT_1:
|
|
outputEnableControl->CLK1_OEB = outputState;
|
|
break;
|
|
|
|
case SI5351_OUTPUT_2:
|
|
outputEnableControl->CLK2_OEB = outputState;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
writeRegister(inst,SI5351_REG_OUTPUT_ENABLE_CONTROL, sizeof(si5351_outputEnableControl_t));
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cm_setCLKControl(si5351_driver *inst, si5351_Outputs clk_output, si5351_CLK_PDN clk_pdn){
|
|
|
|
si5351_CLK_Control_t *CLKx_control = (void*)&inst->device_data[1];
|
|
|
|
CLKx_control->CLK_PDN = clk_pdn;
|
|
CLKx_control->CLK_INV = 0;
|
|
CLKx_control->CLK_IDRV = SI5351_DRIVE_STRENGTH_8MA;
|
|
|
|
switch (clk_output)
|
|
{
|
|
|
|
case SI5351_OUTPUT_0:
|
|
CLKx_control->CLK_SRC = 0b11;
|
|
CLKx_control->MSx_SRC = 0;
|
|
CLKx_control->MSx_INT = 0;
|
|
writeRegister(inst,SI5351_REG_CLK_0_CONTROL, sizeof(si5351_CLK_Control_t));
|
|
break;
|
|
|
|
case SI5351_OUTPUT_1:
|
|
CLKx_control->CLK_SRC = 0b11;
|
|
CLKx_control->MSx_SRC = 1;
|
|
CLKx_control->MSx_INT = 0;
|
|
writeRegister(inst,SI5351_REG_CLK_1_CONTROL, sizeof(si5351_CLK_Control_t));
|
|
break;
|
|
|
|
case SI5351_OUTPUT_2:
|
|
CLKx_control->CLK_SRC = 0b00;
|
|
CLKx_control->MSx_SRC = 0;
|
|
CLKx_control->MSx_INT = 1;
|
|
writeRegister(inst,SI5351_REG_CLK_2_CONTROL, sizeof(si5351_CLK_Control_t));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cm_setInputSource(si5351_driver *inst, si5351_ClkSource clk_source){
|
|
|
|
si5351_PLLInputSource_t *pllInputSource = (void*)&inst->device_data[1];
|
|
|
|
switch(clk_source){
|
|
|
|
case SI5351_CLK_SOURCE_XTAL:
|
|
pllInputSource->PLLA_SRC = 0;
|
|
pllInputSource->PLLB_SRC = 0;
|
|
break;
|
|
|
|
case SI5351_CLK_SOURCE_CLOCKSOURCE:
|
|
pllInputSource->PLLA_SRC = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
// Write to register:
|
|
writeRegister(inst,SI5351_REG_PLL_INPUT_SOURCE, sizeof(si5351_PLLInputSource_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cm_resetPLLs(si5351_driver *inst, uint8_t reset_PLLA, uint8_t reset_PLLB){
|
|
|
|
si5351_PLL_Reset_t *PLL_Reset = (void*)&inst->device_data[1];
|
|
|
|
PLL_Reset->PLLA_RST = (reset_PLLA != 0)? 1 : 0;
|
|
PLL_Reset->PLLB_RST = (reset_PLLB != 0)? 1 : 0;
|
|
|
|
// Write to register:
|
|
writeRegister(inst,SI5351_REG_PLL_RESET, sizeof(si5351_PLL_Reset_t));
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
writeRegisterbyte(inst,SI5351_REG_OUTPUT_ENABLE_CONTROL,temp);
|
|
|
|
/* Power down all output drivers */
|
|
temp = 0x80;
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_0_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_1_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_2_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_3_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_4_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_5_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_6_CONTROL,temp);
|
|
writeRegisterbyte(inst,SI5351_REG_CLK_7_CONTROL,temp);
|
|
|
|
temp = SI5351_CRYSTAL_LOAD_10PF;
|
|
|
|
writeRegisterbyte(inst,SI5351_REG_CRYSTAL_LOAD_CAPACITANCE,temp);
|
|
|
|
// Enable xtal clk..
|
|
temp = 0b01000000;
|
|
writeRegisterbyte(inst,SI5351_REG_FANOUT_ENABLE,temp);
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|