usdx-kamocat/code/drivers/si5351.c

116 wiersze
3.4 KiB
C

/*
* si5351.c
*
* Created on: Jul 7, 2020
* Author: marshal
*/
#include "si5351.h"
#include "../trig.h"
const uint8_t si5351 = 0x60; // i2c address
void pll_reset(uint8_t PLLx){
uint8_t buf[2];
buf[0]=177;
buf[1]=PLLx ? 0x80 : 0x20;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
}
void synthStart(struct synth * cfg){
uint8_t buf[2];
buf[0]=cfg->channel+16;
buf[1]=cfg->PLLx ? 0x2F : 0x1F;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
pll_reset(cfg->PLLx);
}
void synthStop(struct synth * cfg){
uint8_t buf[2];
buf[0]=cfg->channel+16;
buf[1]=0x80;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
}
void synthSetPhase(struct synth * cfg, float degrees){
if(degrees < 0)
degrees += 360; // Only allow positive delay
//FIXME: If more than 180 degrees, we should use the clock invert feature
// Since the delay is before the divider, we scale by the divide ratio
degrees *= ((1./360)*(cfg->reg.synth))/(1<<25);
cfg->phase = degrees > 127 ? 127 : degrees;
uint8_t buf[2];
buf[0]=cfg->channel+165;
buf[1]=cfg->phase;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 2, NULL, 0, TIME_MS2I(100));
pll_reset(cfg->PLLx);
}
void synthWriteParam(uint8_t reg, uint64_t val, uint8_t div){
uint8_t buf[9];
buf[0]=reg*8+26;
buf[1]=0xFF; // denominator is always 0xFFFFF
buf[2]=0xFF;
buf[3]=val>>36 & 0x03;
buf[3]|=div<<4;
buf[4]=val>>28;
buf[5]=val>>20;
buf[6]=val>>16 | 0xF0;
buf[7]=val>>8;
buf[8]=val;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 9, NULL, 0, TIME_MS2I(100));
}
void synthWriteConfig(struct synth * cfg){
synthWriteParam(cfg->PLLx, cfg->reg.pll, 0);
synthWriteParam(cfg->channel+2, cfg->reg.synth, cfg->reg.divide);
}
void synthSetCarrier(struct synth * cfg, float carrier){
// Calculate PLL and Multisynth values
const double xtal = 27e6;
double pll = 27; // Good starting value to put vxco at 750MHz
double div = xtal * pll / carrier;
double delta = div - (xtal*pll)/(carrier+1); // Linearized change per Hz
int8_t delta_shift = round(log2(delta)); // round to nearest power of 2
double delta2 = (0x40000000>>delta_shift)*(1.0/0x40000000U); // (Computing a negative power of 2)
div *= delta2/delta;
div = round(div/2)*2+1; // Minimize chance of rollover
pll = carrier * div / xtal; // Adjust pll to match
// If desired frequency is very low, we need to use the additional divider register
uint8_t div2;
for(div2=0; div>2048; ++div2){
div *= 0.5;
--delta_shift;
}
// Store values into config
const uint32_t fixed_shift = 1<<27; //For shifting values to fixed-point
cfg->reg.pll = pll * fixed_shift;
cfg->reg.synth = div * fixed_shift;
cfg->reg.shift = delta_shift;
cfg->reg.divide = div2;
synthWriteConfig(cfg);
}
void synthSetBaseband(struct synth * cfg, int32_t baseband){
int32_t diff = baseband - cfg->baseband;
diff >>= cfg->reg.shift;
cfg->reg.synth -= diff;
uint32_t val = cfg->reg.synth;
uint8_t buf[5];
/* This could potentially be sped up by dropping the last byte */
buf[0]=8*(cfg->channel)+46;
buf[1]=val>>20;
buf[2]=(val>>16) | 0xF0;
buf[3]=val>>8;
buf[4]=val;
i2cMasterTransmitTimeout(&I2CD1, si5351, buf, 5, NULL, 0, TIME_MS2I(100));
}
void synthInit(struct synth * cfg, uint8_t channel, uint8_t pll){
cfg->channel = channel;
cfg->PLLx = pll;
}