2017-01-01 12:03:21 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2014-2015, TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* The software is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with GNU Radio; see the file COPYING. If not, write to
|
|
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
|
*/
|
2016-09-05 00:27:44 +02:00
|
|
|
#include "hal.h"
|
2019-10-05 12:56:38 +02:00
|
|
|
#include "nanovna.h"
|
2016-09-05 00:27:44 +02:00
|
|
|
#include "si5351.h"
|
|
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
#define XTALFREQ 26000000L
|
|
|
|
|
// MCLK (processor clock, audio codec) frequency clock
|
|
|
|
|
#define CLK2_FREQUENCY 8000000L
|
|
|
|
|
|
|
|
|
|
// Fixed PLL mode multiplier
|
|
|
|
|
#define PLL_N 32
|
|
|
|
|
|
|
|
|
|
//
|
2016-09-05 00:27:44 +02:00
|
|
|
#define SI5351_I2C_ADDR (0x60<<1)
|
|
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
static uint8_t current_band = 0;
|
|
|
|
|
static uint32_t current_freq = 0;
|
2016-09-05 00:27:44 +02:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
si5351_bulk_write(const uint8_t *buf, int len)
|
|
|
|
|
{
|
|
|
|
|
int addr = SI5351_I2C_ADDR>>1;
|
|
|
|
|
i2cAcquireBus(&I2CD1);
|
|
|
|
|
(void)i2cMasterTransmitTimeout(&I2CD1, addr, buf, len, NULL, 0, 1000);
|
|
|
|
|
i2cReleaseBus(&I2CD1);
|
|
|
|
|
}
|
2020-03-05 20:36:44 +01:00
|
|
|
#if 0
|
|
|
|
|
static void si5351_bulk_read(uint8_t reg, uint8_t* buf, int len)
|
|
|
|
|
{
|
|
|
|
|
int addr = SI5351_I2C_ADDR>>1;
|
|
|
|
|
i2cAcquireBus(&I2CD1);
|
|
|
|
|
msg_t mr = i2cMasterTransmitTimeout(&I2CD1, addr, ®, 1, buf, len, 1000);
|
|
|
|
|
i2cReleaseBus(&I2CD1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
si5351_write(uint8_t reg, uint8_t dat)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buf[] = { reg, dat };
|
|
|
|
|
si5351_bulk_write(buf, 2);
|
|
|
|
|
}
|
2016-09-05 00:27:44 +02:00
|
|
|
|
|
|
|
|
// register addr, length, data, ...
|
|
|
|
|
const uint8_t si5351_configs[] = {
|
|
|
|
|
2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff,
|
|
|
|
|
4, SI5351_REG_16_CLK0_CONTROL, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN,
|
|
|
|
|
2, SI5351_REG_183_CRYSTAL_LOAD, SI5351_CRYSTAL_LOAD_8PF,
|
|
|
|
|
// setup PLL (26MHz * 32 = 832MHz, 32/2-2=14)
|
2020-03-05 20:36:44 +01:00
|
|
|
// 9, SI5351_REG_PLL_A, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
|
|
|
|
|
// 9, SI5351_REG_PLL_B, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
|
2016-09-05 00:27:44 +02:00
|
|
|
// RESET PLL
|
2020-03-05 20:36:44 +01:00
|
|
|
2, SI5351_REG_177_PLL_RESET, SI5351_PLL_RESET_A | SI5351_PLL_RESET_B | 0x0C, //
|
2016-09-05 00:27:44 +02:00
|
|
|
// setup multisynth (832MHz / 104 = 8MHz, 104/2-2=50)
|
2020-03-05 20:36:44 +01:00
|
|
|
// 9, SI5351_REG_58_MULTISYNTH2, /*P3*/0, 1, /*P1*/0, 50, 0, /*P2|P3*/0, 0, 0,
|
|
|
|
|
// 2, SI5351_REG_18_CLK2_CONTROL, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_INPUT_MULTISYNTH_N | SI5351_CLK_INTEGER_MODE,
|
|
|
|
|
// 2, SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0,
|
2016-09-05 00:27:44 +02:00
|
|
|
0 // sentinel
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
si5351_init(void)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t *p = si5351_configs;
|
|
|
|
|
while (*p) {
|
|
|
|
|
uint8_t len = *p++;
|
|
|
|
|
si5351_bulk_write(p, len);
|
|
|
|
|
p += len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:57:18 +01:00
|
|
|
static const uint8_t disable_output[] = {
|
|
|
|
|
SI5351_REG_16_CLK0_CONTROL,
|
2020-03-05 20:36:44 +01:00
|
|
|
SI5351_CLK_POWERDOWN, // CLK 0
|
|
|
|
|
SI5351_CLK_POWERDOWN, // CLK 1
|
|
|
|
|
SI5351_CLK_POWERDOWN // CLK 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Get the appropriate starting point for the PLL registers */
|
|
|
|
|
static const uint8_t msreg_base[] = {
|
|
|
|
|
SI5351_REG_42_MULTISYNTH0,
|
|
|
|
|
SI5351_REG_50_MULTISYNTH1,
|
|
|
|
|
SI5351_REG_58_MULTISYNTH2,
|
|
|
|
|
};
|
|
|
|
|
static const uint8_t clkctrl[] = {
|
|
|
|
|
SI5351_REG_16_CLK0_CONTROL,
|
|
|
|
|
SI5351_REG_17_CLK1_CONTROL,
|
|
|
|
|
SI5351_REG_18_CLK2_CONTROL
|
2020-02-28 16:57:18 +01:00
|
|
|
};
|
|
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
static void si5351_reset_pll(uint8_t mask)
|
|
|
|
|
{
|
|
|
|
|
// Writing a 1<<5 will reset PLLA, 1<<7 reset PLLB, this is a self clearing bits.
|
|
|
|
|
// !!! Need delay before reset PLL for apply PLL freq changes before
|
|
|
|
|
chThdSleepMicroseconds(200);
|
|
|
|
|
si5351_write(SI5351_REG_177_PLL_RESET, mask | 0x0C);
|
|
|
|
|
// chThdSleepMicroseconds(250);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 02:02:57 +02:00
|
|
|
void si5351_disable_output(void)
|
|
|
|
|
{
|
2020-03-05 20:36:44 +01:00
|
|
|
// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, (SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN));
|
2016-09-06 02:02:57 +02:00
|
|
|
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
|
2020-02-28 16:57:18 +01:00
|
|
|
si5351_bulk_write(disable_output, sizeof(disable_output));
|
2016-09-06 02:02:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void si5351_enable_output(void)
|
|
|
|
|
{
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_reset_pll( SI5351_PLL_RESET_A | SI5351_PLL_RESET_B );
|
|
|
|
|
// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, ~(SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN));
|
2016-09-06 02:02:57 +02:00
|
|
|
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x00);
|
2020-03-05 20:36:44 +01:00
|
|
|
current_freq = 0;
|
|
|
|
|
current_band = 0;
|
2016-09-06 02:02:57 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:57:18 +01:00
|
|
|
static void si5351_setupPLL(uint8_t pll, /* SI5351_PLL_A or SI5351_PLL_B */
|
2016-09-05 00:27:44 +02:00
|
|
|
uint8_t mult,
|
|
|
|
|
uint32_t num,
|
|
|
|
|
uint32_t denom)
|
|
|
|
|
{
|
|
|
|
|
uint32_t P1;
|
|
|
|
|
uint32_t P2;
|
|
|
|
|
uint32_t P3;
|
|
|
|
|
|
|
|
|
|
/* Feedback Multisynth Divider Equation
|
|
|
|
|
* where: a = mult, b = num and c = denom
|
|
|
|
|
* P1 register is an 18-bit value using following formula:
|
|
|
|
|
* P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512
|
|
|
|
|
* P2 register is a 20-bit value using the following formula:
|
|
|
|
|
* P2[19:0] = 128 * num - denom * floor(128*(num/denom))
|
|
|
|
|
* P3 register is a 20-bit value using the following formula:
|
|
|
|
|
* P3[19:0] = denom
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Set the main PLL config registers */
|
|
|
|
|
if (num == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Integer mode */
|
|
|
|
|
P1 = 128 * mult - 512;
|
2016-09-06 02:02:57 +02:00
|
|
|
P2 = 0;
|
|
|
|
|
P3 = 1;
|
2016-09-05 00:27:44 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Fractional mode */
|
|
|
|
|
//P1 = (uint32_t)(128 * mult + floor(128 * ((float)num/(float)denom)) - 512);
|
|
|
|
|
P1 = 128 * mult + ((128 * num) / denom) - 512;
|
|
|
|
|
//P2 = (uint32_t)(128 * num - denom * floor(128 * ((float)num/(float)denom)));
|
|
|
|
|
P2 = 128 * num - denom * ((128 * num) / denom);
|
|
|
|
|
P3 = denom;
|
|
|
|
|
}
|
2020-03-05 20:36:44 +01:00
|
|
|
// Pll MSN(A|B) registers Datasheet
|
|
|
|
|
// MSN_P3[15:8]
|
|
|
|
|
// MSN_P3[ 7:0]
|
|
|
|
|
// Reserved | MSN_P1[17:16]
|
|
|
|
|
// MSN_P1[15:8]
|
|
|
|
|
// MSN_P1[ 7:0]
|
|
|
|
|
// MSN_P3[19:16] | MSN_P2[19:16]
|
|
|
|
|
// MSN_P2[15:8]
|
|
|
|
|
// MSN_P2[ 7:0]
|
2016-09-19 06:13:42 +02:00
|
|
|
uint8_t reg[9];
|
2020-03-05 20:36:44 +01:00
|
|
|
reg[0] = pll;//reg_base[pll];
|
2016-09-19 06:13:42 +02:00
|
|
|
reg[1] = (P3 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[2] = (P3 & 0x000000FF);
|
|
|
|
|
reg[3] = (P1 & 0x00030000) >> 16;
|
|
|
|
|
reg[4] = (P1 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[5] = (P1 & 0x000000FF);
|
|
|
|
|
reg[6] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
|
|
|
|
|
reg[7] = (P2 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[8] = (P2 & 0x000000FF);
|
|
|
|
|
si5351_bulk_write(reg, 9);
|
2016-09-05 00:27:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
|
2020-02-28 16:57:18 +01:00
|
|
|
static void
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_setupMultisynth(uint8_t channel,
|
2020-02-28 16:57:18 +01:00
|
|
|
uint8_t pllSource,
|
2016-09-05 00:27:44 +02:00
|
|
|
uint32_t div, // 4,6,8, 8+ ~ 900
|
|
|
|
|
uint32_t num,
|
2016-09-10 11:24:44 +02:00
|
|
|
uint32_t denom,
|
2017-02-01 21:18:29 +01:00
|
|
|
uint32_t rdiv, // SI5351_R_DIV_1~128
|
2016-09-10 11:24:44 +02:00
|
|
|
uint8_t drive_strength)
|
2016-09-05 00:27:44 +02:00
|
|
|
{
|
|
|
|
|
uint8_t dat;
|
|
|
|
|
|
|
|
|
|
uint32_t P1;
|
|
|
|
|
uint32_t P2;
|
|
|
|
|
uint32_t P3;
|
2016-09-10 11:24:44 +02:00
|
|
|
uint32_t div4 = 0;
|
2016-09-05 00:27:44 +02:00
|
|
|
|
|
|
|
|
/* Output Multisynth Divider Equations
|
|
|
|
|
* where: a = div, b = num and c = denom
|
|
|
|
|
* P1 register is an 18-bit value using following formula:
|
|
|
|
|
* P1[17:0] = 128 * a + floor(128*(b/c)) - 512
|
|
|
|
|
* P2 register is a 20-bit value using the following formula:
|
|
|
|
|
* P2[19:0] = 128 * b - c * floor(128*(b/c))
|
|
|
|
|
* P3 register is a 20-bit value using the following formula:
|
|
|
|
|
* P3[19:0] = c
|
|
|
|
|
*/
|
|
|
|
|
/* Set the main PLL config registers */
|
2016-09-10 11:24:44 +02:00
|
|
|
if (div == 4) {
|
|
|
|
|
div4 = SI5351_DIVBY4;
|
|
|
|
|
P1 = P2 = 0;
|
|
|
|
|
P3 = 1;
|
|
|
|
|
} else if (num == 0) {
|
2016-09-05 00:27:44 +02:00
|
|
|
/* Integer mode */
|
|
|
|
|
P1 = 128 * div - 512;
|
2016-09-06 02:02:57 +02:00
|
|
|
P2 = 0;
|
|
|
|
|
P3 = 1;
|
2016-09-10 11:24:44 +02:00
|
|
|
} else {
|
2016-09-05 00:27:44 +02:00
|
|
|
/* Fractional mode */
|
|
|
|
|
P1 = 128 * div + ((128 * num) / denom) - 512;
|
|
|
|
|
P2 = 128 * num - denom * ((128 * num) / denom);
|
|
|
|
|
P3 = denom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the MSx config registers */
|
2016-09-19 06:13:42 +02:00
|
|
|
uint8_t reg[9];
|
2020-03-05 20:36:44 +01:00
|
|
|
reg[0] = msreg_base[channel];
|
2016-09-19 06:13:42 +02:00
|
|
|
reg[1] = (P3 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[2] = (P3 & 0x000000FF);
|
2017-02-01 21:18:29 +01:00
|
|
|
reg[3] = ((P1 & 0x00030000) >> 16) | div4 | rdiv;
|
2016-09-19 06:13:42 +02:00
|
|
|
reg[4] = (P1 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[5] = (P1 & 0x000000FF);
|
|
|
|
|
reg[6] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
|
|
|
|
|
reg[7] = (P2 & 0x0000FF00) >> 8;
|
|
|
|
|
reg[8] = (P2 & 0x000000FF);
|
|
|
|
|
si5351_bulk_write(reg, 9);
|
2016-09-05 00:27:44 +02:00
|
|
|
|
|
|
|
|
/* Configure the clk control and enable the output */
|
2016-09-10 11:24:44 +02:00
|
|
|
dat = drive_strength | SI5351_CLK_INPUT_MULTISYNTH_N;
|
2020-03-05 20:36:44 +01:00
|
|
|
if (pllSource == SI5351_REG_PLL_B)
|
2016-09-05 00:27:44 +02:00
|
|
|
dat |= SI5351_CLK_PLL_SELECT_B;
|
|
|
|
|
if (num == 0)
|
|
|
|
|
dat |= SI5351_CLK_INTEGER_MODE;
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_write(clkctrl[channel], dat);
|
2016-09-05 00:27:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:57:18 +01:00
|
|
|
static void
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_set_frequency_fixedpll(uint8_t channel, uint32_t pllSource, uint64_t pllfreq, uint32_t freq, uint32_t rdiv, uint8_t drive_strength)
|
2016-09-05 00:27:44 +02:00
|
|
|
{
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t denom = freq;
|
|
|
|
|
uint32_t div = pllfreq / denom; // range: 8 ~ 1800
|
|
|
|
|
uint32_t num = pllfreq % denom;
|
2019-12-09 00:05:59 +01:00
|
|
|
|
|
|
|
|
// cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t max_denominator = (1 << 20) - 1;
|
2019-12-09 00:05:59 +01:00
|
|
|
if (denom > max_denominator) {
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
|
2019-12-09 00:05:59 +01:00
|
|
|
while (denom != 0) {
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t a = num / denom;
|
|
|
|
|
uint32_t b = num % denom;
|
|
|
|
|
uint32_t q2 = q0 + a*q1;
|
2019-12-09 00:05:59 +01:00
|
|
|
if (q2 > max_denominator)
|
|
|
|
|
break;
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t p2 = p0 + a*p1;
|
|
|
|
|
p0 = p1;
|
|
|
|
|
q0 = q1;
|
|
|
|
|
p1 = p2;
|
|
|
|
|
q1 = q2;
|
|
|
|
|
num = denom; denom = b;
|
2019-12-09 00:05:59 +01:00
|
|
|
}
|
|
|
|
|
num = p1;
|
|
|
|
|
denom = q1;
|
2016-09-10 11:24:44 +02:00
|
|
|
}
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_setupMultisynth(channel, pllSource, div, num, denom, rdiv, drive_strength);
|
|
|
|
|
}
|
2019-12-09 00:05:59 +01:00
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
void si5351_setupPLL_freq(uint32_t pll, uint32_t freq, uint32_t div, uint32_t mul){
|
|
|
|
|
uint32_t denom = XTALFREQ * mul;
|
|
|
|
|
uint64_t pllfreq = (uint64_t)freq * div;
|
|
|
|
|
uint32_t multi = pllfreq / denom;
|
|
|
|
|
uint32_t num = pllfreq % denom;
|
|
|
|
|
|
|
|
|
|
// cf. https://github.com/python/cpython/blob/master/Lib/fractions.py#L227
|
|
|
|
|
uint32_t max_denominator = (1 << 20) - 1;
|
|
|
|
|
if (denom > max_denominator) {
|
|
|
|
|
uint32_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
|
|
|
|
|
while (denom != 0) {
|
|
|
|
|
uint32_t a = num / denom;
|
|
|
|
|
uint32_t b = num % denom;
|
|
|
|
|
uint32_t q2 = q0 + a*q1;
|
|
|
|
|
if (q2 > max_denominator)
|
|
|
|
|
break;
|
|
|
|
|
uint32_t p2 = p0 + a*p1;
|
|
|
|
|
p0 = p1; q0 = q1; p1 = p2; q1 = q2;
|
|
|
|
|
num = denom; denom = b;
|
|
|
|
|
}
|
|
|
|
|
num = p1;
|
|
|
|
|
denom = q1;
|
|
|
|
|
}
|
|
|
|
|
si5351_setupPLL(pll, multi, num, denom);
|
2016-09-05 00:27:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
#if 0
|
2020-02-28 16:57:18 +01:00
|
|
|
static void
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_set_frequency_fixeddiv(uint8_t channel, uint32_t pll, uint32_t freq, uint32_t div,
|
|
|
|
|
uint8_t drive_strength, uint32_t mul)
|
2016-09-05 01:33:05 +02:00
|
|
|
{
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_setupPLL_freq(pll, freq, div, mul);
|
|
|
|
|
si5351_setupMultisynth(channel, pll, div, 0, 1, SI5351_R_DIV_1, drive_strength);
|
2016-09-05 01:33:05 +02:00
|
|
|
}
|
2016-09-05 00:27:44 +02:00
|
|
|
|
2016-09-05 01:33:05 +02:00
|
|
|
void
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_set_frequency(int channel, uint32_t freq, uint8_t drive_strength){
|
2016-09-05 01:33:05 +02:00
|
|
|
if (freq <= 100000000) {
|
2017-02-01 21:40:39 +01:00
|
|
|
si5351_setupPLL(SI5351_PLL_B, 32, 0, 1);
|
2019-12-09 00:05:59 +01:00
|
|
|
si5351_set_frequency_fixedpll(channel, SI5351_PLL_B, PLLFREQ, freq, SI5351_R_DIV_1, drive_strength, 1);
|
2016-09-06 02:02:57 +02:00
|
|
|
} else if (freq < 150000000) {
|
2019-12-09 00:05:59 +01:00
|
|
|
si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 6, drive_strength, 1);
|
2016-09-06 02:02:57 +02:00
|
|
|
} else {
|
2019-12-09 00:05:59 +01:00
|
|
|
si5351_set_frequency_fixeddiv(channel, SI5351_PLL_B, freq, 4, drive_strength, 1);
|
2016-09-06 02:02:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-28 16:57:18 +01:00
|
|
|
#endif
|
2016-09-24 03:25:13 +02:00
|
|
|
|
2020-03-05 20:36:44 +01:00
|
|
|
#define DELAY_NORMAL 2
|
|
|
|
|
#define DELAY_BANDCHANGE 2
|
2019-09-07 06:13:25 +02:00
|
|
|
|
2016-09-06 02:02:57 +02:00
|
|
|
/*
|
|
|
|
|
* configure output as follows:
|
|
|
|
|
* CLK0: frequency + offset
|
|
|
|
|
* CLK1: frequency
|
|
|
|
|
* CLK2: fixed 8MHz
|
|
|
|
|
*/
|
2020-03-05 20:36:44 +01:00
|
|
|
|
2016-09-24 03:25:13 +02:00
|
|
|
int
|
2019-10-06 00:56:25 +02:00
|
|
|
si5351_set_frequency_with_offset(uint32_t freq, int offset, uint8_t drive_strength)
|
2016-09-06 02:02:57 +02:00
|
|
|
{
|
2020-03-05 20:36:44 +01:00
|
|
|
uint8_t band;
|
2019-09-07 06:13:25 +02:00
|
|
|
int delay = DELAY_NORMAL;
|
2020-03-05 20:36:44 +01:00
|
|
|
if (freq == current_freq)
|
|
|
|
|
return delay;
|
2017-02-01 21:40:39 +01:00
|
|
|
uint32_t ofreq = freq + offset;
|
2020-01-18 04:07:21 +01:00
|
|
|
uint32_t mul = 1, omul = 1;
|
2017-02-01 21:40:39 +01:00
|
|
|
uint32_t rdiv = SI5351_R_DIV_1;
|
2020-03-05 20:36:44 +01:00
|
|
|
uint32_t fdiv;
|
|
|
|
|
current_freq = freq;
|
|
|
|
|
if (freq >= FREQ_HARMONICS * 7U) {
|
2020-01-18 04:07:21 +01:00
|
|
|
mul = 9;
|
|
|
|
|
omul = 11;
|
2020-03-05 20:36:44 +01:00
|
|
|
} else if (freq >= FREQ_HARMONICS * 5U) {
|
2020-01-18 04:07:21 +01:00
|
|
|
mul = 7;
|
|
|
|
|
omul = 9;
|
2020-03-05 20:36:44 +01:00
|
|
|
} else if (freq >= FREQ_HARMONICS * 3U) {
|
2019-12-09 00:05:59 +01:00
|
|
|
mul = 5;
|
|
|
|
|
omul = 7;
|
2020-03-05 20:36:44 +01:00
|
|
|
} else if (freq >= FREQ_HARMONICS) {
|
2019-12-09 00:05:59 +01:00
|
|
|
mul = 3;
|
|
|
|
|
omul = 5;
|
2019-07-24 23:23:27 +02:00
|
|
|
}
|
2020-03-05 20:36:44 +01:00
|
|
|
else if (freq <= 500000U) {
|
2017-02-01 21:40:39 +01:00
|
|
|
rdiv = SI5351_R_DIV_64;
|
2020-03-05 20:36:44 +01:00
|
|
|
freq *= 64;
|
|
|
|
|
ofreq *= 64;
|
2020-01-18 04:07:21 +01:00
|
|
|
} else if (freq <= 4000000U) {
|
2017-02-01 21:40:39 +01:00
|
|
|
rdiv = SI5351_R_DIV_8;
|
2020-03-05 20:36:44 +01:00
|
|
|
freq *= 8;
|
|
|
|
|
ofreq *= 8;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Band 0
|
|
|
|
|
* 1~100MHz fixed PLL = XTALFREQ * PLL_N, fractional divider
|
|
|
|
|
* Band 1
|
|
|
|
|
* 100~150MHz fractional PLL = 600- 900MHz, fixed divider 6
|
|
|
|
|
* Band 2
|
|
|
|
|
* 150~300MHz fractional PLL = 600-1200MHz, fixed divider 4
|
|
|
|
|
*/
|
|
|
|
|
if ((freq / mul) < 100000000U) {
|
|
|
|
|
band = 1;
|
|
|
|
|
} else if ((freq / mul) < 150000000U) {
|
|
|
|
|
band = 2;
|
|
|
|
|
fdiv = 6;
|
|
|
|
|
} else {
|
|
|
|
|
band = 3;
|
|
|
|
|
fdiv = 4;
|
2017-02-01 21:40:39 +01:00
|
|
|
}
|
2016-09-24 03:25:13 +02:00
|
|
|
switch (band) {
|
|
|
|
|
case 1:
|
2020-03-05 20:36:44 +01:00
|
|
|
// Setup CH0 and CH1 constant PLL freq at band change, and set CH2 freq = CLK2_FREQUENCY
|
|
|
|
|
if (current_band != 1){
|
|
|
|
|
si5351_setupPLL(SI5351_REG_PLL_A, PLL_N, 0, 1);
|
|
|
|
|
si5351_setupPLL(SI5351_REG_PLL_B, PLL_N, 0, 1);
|
|
|
|
|
si5351_set_frequency_fixedpll(2, SI5351_REG_PLL_B, XTALFREQ * PLL_N, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA);
|
2017-02-01 21:18:29 +01:00
|
|
|
}
|
2020-03-05 20:36:44 +01:00
|
|
|
// Calculate and set CH0 and CH1 divider
|
|
|
|
|
si5351_set_frequency_fixedpll(0, SI5351_REG_PLL_A, XTALFREQ * PLL_N * omul, ofreq, rdiv, drive_strength);
|
|
|
|
|
si5351_set_frequency_fixedpll(1, SI5351_REG_PLL_A, XTALFREQ * PLL_N * mul, freq, rdiv, drive_strength);
|
2016-09-24 03:25:13 +02:00
|
|
|
break;
|
|
|
|
|
case 2:
|
2020-03-05 20:36:44 +01:00
|
|
|
case 3:
|
|
|
|
|
// Setup CH0 and CH1 constant fdiv divider at change
|
|
|
|
|
if (current_band != band){
|
|
|
|
|
si5351_setupMultisynth(0, SI5351_REG_PLL_A, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength);
|
|
|
|
|
si5351_setupMultisynth(1, SI5351_REG_PLL_B, fdiv, 0, 1, SI5351_R_DIV_1, drive_strength);
|
|
|
|
|
}
|
|
|
|
|
// Calculate and set CH0 and CH1 PLL freq
|
|
|
|
|
si5351_setupPLL_freq(SI5351_REG_PLL_A, ofreq, fdiv, omul);// set PLLA freq = (ofreq/omul)*fdiv
|
|
|
|
|
si5351_setupPLL_freq(SI5351_REG_PLL_B, freq, fdiv, mul);// set PLLB freq = ( freq/ mul)*fdiv
|
|
|
|
|
// Calculate CH2 freq = CLK2_FREQUENCY, depend from calculated before CH1 PLLB = (freq/mul)*fdiv
|
|
|
|
|
si5351_set_frequency_fixedpll(2, SI5351_REG_PLL_B, (freq/mul)*fdiv, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA);
|
2016-09-24 03:25:13 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current_band != band) {
|
2020-03-05 20:36:44 +01:00
|
|
|
si5351_reset_pll(SI5351_PLL_RESET_A|SI5351_PLL_RESET_B);
|
|
|
|
|
current_band = band;
|
|
|
|
|
delay+=DELAY_BANDCHANGE;
|
2016-10-13 13:25:31 +02:00
|
|
|
}
|
2016-09-24 03:25:13 +02:00
|
|
|
return delay;
|
2016-09-05 01:33:05 +02:00
|
|
|
}
|