Skip to content

Instantly share code, notes, and snippets.

@edy555
Last active September 11, 2016 03:59
Show Gist options
  • Save edy555/f1ee7ef44fe4f5c6f7618ac4cbbe66fb to your computer and use it in GitHub Desktop.
Save edy555/f1ee7ef44fe4f5c6f7618ac4cbbe66fb to your computer and use it in GitHub Desktop.
Si5351Aで任意の周波数を設定するためのコード。I2C部分は環境に合わせて適当に置き換える必要がある。
#define SI5351_PLL_A 0
#define SI5351_PLL_B 1
#define SI5351_MULTISYNTH_DIV_4 4
#define SI5351_MULTISYNTH_DIV_6 6
#define SI5351_MULTISYNTH_DIV_8 8
#define SI5351_R_DIV_1 (0<<4)
#define SI5351_R_DIV_2 (1<<4)
#define SI5351_R_DIV_4 (2<<4)
#define SI5351_R_DIV_8 (3<<4)
#define SI5351_R_DIV_16 (4<<4)
#define SI5351_R_DIV_32 (5<<4)
#define SI5351_R_DIV_64 (6<<4)
#define SI5351_R_DIV_128 (7<<4)
#define SI5351_DIVBY4 (3<<2)
#define SI5351_REG_3_OUTPUT_ENABLE_CONTROL 3
#define SI5351_REG_16_CLK0_CONTROL 16
#define SI5351_REG_17_CLK1_CONTROL 17
#define SI5351_REG_18_CLK2_CONTROL 18
#define SI5351_REG_26_PLL_A 26
#define SI5351_REG_34_PLL_B 34
#define SI5351_REG_42_MULTISYNTH0 42
#define SI5351_REG_50_MULTISYNTH1 50
#define SI5351_REG_58_MULTISYNTH2 58
#define SI5351_CLK_POWERDOWN (1<<7)
#define SI5351_CLK_INTEGER_MODE (1<<6)
#define SI5351_CLK_PLL_SELECT_B (1<<5)
#define SI5351_CLK_INVERT (1<<4)
#define SI5351_CLK_INPUT_MASK (3<<2)
#define SI5351_CLK_INPUT_XTAL (0<<2)
#define SI5351_CLK_INPUT_CLKIN (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
#define SI5351_REG_177_PLL_RESET 177
#define SI5351_PLL_RESET_B (1<<7)
#define SI5351_PLL_RESET_A (1<<5)
#define SI5351_REG_183_CRYSTAL_LOAD 183
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
void si5351_set_frequency(int channel, int pll, int freq);
#include "si5351.h"
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);
}
// 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)
9, SI5351_REG_26_PLL_A, /*P3*/0, 1, /*P1*/0, 14, 0, /*P3/P2*/0, 0, 0,
// RESET PLL
2, SI5351_REG_177_PLL_RESET, SI5351_PLL_RESET_A | SI5351_PLL_RESET_B,
// setup multisynth (832MHz / 104 = 8MHz, 104/2-2=50)
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,
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;
}
}
#include "si5351.h"
#define SI5351_I2C_ADDR (0x60<<1)
static void
si5351_write(uint8_t reg, uint8_t dat)
{
int addr = SI5351_I2C_ADDR>>1;
uint8_t buf[] = { reg, dat };
i2cAcquireBus(&I2CD1);
(void)i2cMasterTransmitTimeout(&I2CD1, addr, buf, 2, NULL, 0, 1000);
i2cReleaseBus(&I2CD1);
}
void si5351_disable_output(void)
{
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0xff);
si5351_write(SI5351_REG_16_CLK0_CONTROL, 0x80);
si5351_write(SI5351_REG_17_CLK1_CONTROL, 0x80);
si5351_write(SI5351_REG_18_CLK2_CONTROL, 0x80);
}
void si5351_enable_output(void)
{
si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, 0x00);
}
void si5351_reset_pll(void)
{
si5351_write(SI5351_REG_177_PLL_RESET, 0xAC);
}
void si5351_setupPLL(uint8_t pll, /* SI5351_PLL_A or SI5351_PLL_B */
uint8_t mult,
uint32_t num,
uint32_t denom)
{
/* Get the appropriate starting point for the PLL registers */
const uint8_t pllreg_base[] = {
SI5351_REG_26_PLL_A,
SI5351_REG_34_PLL_B
};
uint8_t baseaddr = pllreg_base[pll];
uint32_t P1;
uint32_t P2;
uint32_t P3;
/* Set the main PLL config registers */
if (num == 0) {
/* Integer mode */
P1 = 128 * mult - 512;
P2 = 0;
P3 = 1;
} else {
/* Fractional mode */
P1 = 128 * mult + ((128 * num) / denom) - 512;
P2 = 128 * num - denom * ((128 * num) / denom);
P3 = denom;
}
/* The datasheet is a nightmare of typos and inconsistencies here! */
si5351_write(baseaddr, (P3 & 0x0000FF00) >> 8);
si5351_write(baseaddr+1, (P3 & 0x000000FF));
si5351_write(baseaddr+2, (P1 & 0x00030000) >> 16);
si5351_write(baseaddr+3, (P1 & 0x0000FF00) >> 8);
si5351_write(baseaddr+4, (P1 & 0x000000FF));
si5351_write(baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) );
si5351_write(baseaddr+6, (P2 & 0x0000FF00) >> 8);
si5351_write(baseaddr+7, (P2 & 0x000000FF));
}
void
si5351_setupMultisynth(uint8_t output,
uint8_t pllSource,
uint32_t div, // 4,6,8, 8+
uint32_t num,
uint32_t denom)
{
/* Get the appropriate starting point for the PLL registers */
const uint8_t msreg_base[] = {
SI5351_REG_42_MULTISYNTH0,
SI5351_REG_50_MULTISYNTH1,
SI5351_REG_58_MULTISYNTH2,
};
uint8_t baseaddr = msreg_base[output];
const uint8_t clkctrl[] = {
SI5351_REG_16_CLK0_CONTROL,
SI5351_REG_17_CLK1_CONTROL,
SI5351_REG_18_CLK2_CONTROL
};
uint8_t dat;
uint32_t P1;
uint32_t P2;
uint32_t P3;
uint32_t div4 = 0;
if (div == 4) {
div4 = SI5351_DIVBY4;
P1 = P2 = 0;
P3 = 1;
} else if (num == 0) {
/* Integer mode */
P1 = 128 * div - 512;
P2 = 0;
P3 = 1;
} else {
/* Fractional mode */
P1 = 128 * div + ((128 * num) / denom) - 512;
P2 = 128 * num - denom * ((128 * num) / denom);
P3 = denom;
}
/* Set the MSx config registers */
si5351_write(baseaddr, (P3 & 0x0000FF00) >> 8);
si5351_write(baseaddr+1, (P3 & 0x000000FF));
si5351_write(baseaddr+2, ((P1 & 0x00030000) >> 16) | div4);
si5351_write(baseaddr+3, (P1 & 0x0000FF00) >> 8);
si5351_write(baseaddr+4, (P1 & 0x000000FF));
si5351_write(baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
si5351_write(baseaddr+6, (P2 & 0x0000FF00) >> 8);
si5351_write(baseaddr+7, (P2 & 0x000000FF));
/* Configure the clk control and enable the output */
dat = SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_INPUT_MULTISYNTH_N;
if (pllSource == SI5351_PLL_B)
dat |= SI5351_CLK_PLL_SELECT_B;
if (num == 0)
dat |= SI5351_CLK_INTEGER_MODE;
si5351_write(clkctrl[output], dat);
}
static uint32_t
gcd(uint32_t x, uint32_t y)
{
uint32_t z;
while (y != 0) {
z = x % y;
x = y;
y = z;
}
return x;
}
#define XTALFREQ 26000000L
#define PLL_N 32
#define PLLFREQ (XTALFREQ * PLL_N)
void
si5351_set_frequency_fixedpll(int channel, int pll, int pllfreq, int freq)
{
int32_t div = pllfreq / freq; // range: 8 ~ 1800
int32_t num = pllfreq - freq * div;
int32_t denom = freq;
//int32_t k = freq / (1<<20) + 1;
int32_t k = gcd(num, denom);
num /= k;
denom /= k;
while (denom >= (1<<20)) {
num >>= 1;
denom >>= 1;
}
si5351_setupMultisynth(channel, pll, div, num, denom);
}
void
si5351_set_frequency_fixeddiv(int channel, int pll, int freq, int div)
{
int32_t pllfreq = freq * div;
int32_t multi = pllfreq / XTALFREQ;
int32_t num = pllfreq - multi * XTALFREQ;
int32_t denom = XTALFREQ;
int32_t k = gcd(num, denom);
num /= k;
denom /= k;
while (denom >= (1<<20)) {
num >>= 1;
denom >>= 1;
}
si5351_setupPLL(pll, multi, num, denom);
si5351_setupMultisynth(channel, pll, div, 0, 1);
}
#define XTALFREQ 26000000L
#define PLL_N 32
#define PLLFREQ (XTALFREQ * PLL_N)
/*
* 1~100MHz fixed PLL 832MHz, fractional divider
* 100~150MHz fractional PLL 600-900MHz, fixed divider 6
* 150~200MHz fractional PLL 600-900MHz, fixed divider 4
*/
void
si5351_set_frequency(int channel, int pll, int freq)
{
si5351_disable_output();
if (freq <= 100000000) {
si5351_setupPLL(pll, PLL_N, 0, 1);
si5351_set_frequency_fixedpll(channel, pll, PLLFREQ, freq);
} else if (freq < 150000000) {
si5351_set_frequency_fixeddiv(channel, pll, freq, 6);
} else {
si5351_set_frequency_fixeddiv(channel, pll, freq, 4);
}
si5351_reset_pll();
si5351_enable_output();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment