Here is the working source code for use with StartKit (or any other XMOS target) to configure the SiLabs Si5351A PLL. Have placed some comments into the source code. The posted code will configure the Adafruit PCB to generate 18.432 Mhz but you can easily alter this clock value to suit using the SiLabs Clock Builder tool.
After we read up on Github, will post there for easier access. For now, this code is hosted on our company server.
Code: Select all
// Copyright (c) 2016, XMOS Ltd, All rights reserved
//
// Author : K. Bhatia (Mon2)
// Company : Axxon Computer Corporation
// Version : 1.0
// Compile Date : 06-02-2016
//
#include <xs1.h>
#include "i2c.h"
#include "debug_print.h"
// I2C interface ports
port p_scl = XS1_PORT_1E; // X0D12 on J7 of StartKit (3rd hole) & apply a 2.2k to 10k pull up to 3V3 (I2C spec)
port p_sda = XS1_PORT_1F; // X0D13 = Square PCB pad on J7 of StartKit (first hole) & apply a 2.2k to 10k pull up to 3V3 (I2C spec)
// Connect 3V3 from XMOS StartKit pad to Vin of Adafruit Si5351A PCB (Digikey sells these finished PLL PCBs)
// Connect Ground from XMOS StartKit pad to Ground of the Si5351A moule PCB
//
// Connect D12 pad of StartKit J7 (I2C_SCL) to I2C_SCL of Si5351A PCB
// Connect D13 pad of StartKit J7 (I2C_SDA) to I2C_SDA of Si5351A PCB
//
// This is important since the XMOS port pins are NOT, repeat NOT 5V0 tolerant !!
//
// Use the SiLabs Clock Builder tool to generate the C-code Header file to generate the PLL values shown below
//
// Be sure to use 25 Mhz as the ref clock since Adafruit boards are fitted with a 25 Mhz crystal !!!
//
// SI5351A register address defines
#define SI5351A_I2C_ADDR 0x60
#define SI5351A_CLKx_DIS 0x03
#define SI5351A_Reg16 0x16
unsigned char pll_reg[31][2] = {
// PLL values are 31 entries x 2 = 62 bytes which are dynamic to the selected clock value
// the static values are hard-coded in the write_to_pll routine
//18.432 Mhz
{ 15,0x00},
{ 16,0x4F},
{ 17,0x80},
{ 18,0x80},
{ 19,0x80},
{ 20,0x80},
{ 21,0x80},
{ 22,0x80},
{ 23,0x80},
{ 24,0x00},
{ 25,0x00},
{ 26,0x0C},
{ 27,0x35},
{ 28,0x00},
{ 29,0x0C},
{ 30,0x02},
{ 31,0x00},
{ 32,0x01},
{ 33,0x96},
{ 34,0x00},
{ 35,0x00},
{ 36,0x00},
{ 37,0x00},
{ 38,0x00},
{ 39,0x00},
{ 40,0x00},
{ 41,0x00},
{ 42,0x00},
{ 43,0x01},
{ 44,0x00},
{ 45,0x11}
/*
// 14.7456 Mhz (25 Mhz as ref xtal)
{ 15,0x00},
{ 16,0x4F},
{ 17,0x80},
{ 18,0x80},
{ 19,0x80},
{ 20,0x80},
{ 21,0x80},
{ 22,0x80},
{ 23,0x80},
{ 24,0x00},
{ 25,0x00},
{ 26,0x3D},
{ 27,0x09},
{ 28,0x00},
{ 29,0x0C},
{ 30,0x27},
{ 31,0x00},
{ 32,0x35},
{ 33,0xA1},
{ 34,0x00},
{ 35,0x00},
{ 36,0x00},
{ 37,0x00},
{ 38,0x00},
{ 39,0x00},
{ 40,0x00},
{ 41,0x00},
{ 42,0x00},
{ 43,0x01},
{ 44,0x00},
{ 45,0x16}
*/
/*
// 5 Mhz
{ 15,0x00},
{ 16,0x4F},
{ 17,0xA3},
{ 18,0xA3},
{ 19,0x80},
{ 20,0x80},
{ 21,0x80},
{ 22,0xC0},
{ 23,0x80},
{ 24,0x00},
{ 25,0x00},
{ 26,0x00},
{ 27,0x01},
{ 28,0x00},
{ 29,0x0D},
{ 30,0x00},
{ 31,0x00},
{ 32,0x00},
{ 33,0x00},
{ 34,0x00},
{ 35,0x00},
{ 36,0x00},
{ 37,0x00},
{ 38,0x00},
{ 39,0x00},
{ 40,0x00},
{ 41,0x00},
{ 42,0x00},
{ 43,0x01},
{ 44,0x00},
{ 45,0x49},
*/
};
// sourced from AN10084
void delay(void)
{
timer t;
unsigned int start_time, end_time;
/**
How to wait for a period of time using a timer
----------------------------------------------
Timers can be used to pause a task until a certain amount of time has
passed. First input the current time from the timer:
**/
t :> start_time;
/**
Next compute the time you want the task to wait until, by adding the
desired number of timer ticks to the start time:
**/
end_time = start_time + 500;
/**
Finally use the following statement to wait for end_time to be
reached:
**/
t when timerafter(end_time) :> void;
}
// sourced from AN00156
void si5351a_pll_configuration(client interface i2c_master_if i2c) {
unsigned int i;
// Configure SI5351A
// Disable all outputs setting CLKx_DIS high
// Write Reg3 = 0xFF
i2c.write_reg(SI5351A_I2C_ADDR, SI5351A_CLKx_DIS, 0xff);
delay();
// Power down all output drivers
// Write Reg16 – Reg 23 = 0x80
i2c.write_reg(SI5351A_I2C_ADDR, SI5351A_Reg16, 0x80);
// prime the unique PLL values for our desired clock
for (i=0; i<=30;i++)
i2c.write_reg(SI5351A_I2C_ADDR, pll_reg[i][0], pll_reg[i][1]);
// Write Reg46 – Reg92 = 0
for (i=46; i<=92;i++)
i2c.write_reg(SI5351A_I2C_ADDR, i, 0);
// Write Reg149 – Reg170 = 0
for (i=149; i<=170;i++)
i2c.write_reg(SI5351A_I2C_ADDR, i, 0);
// Apply soft reset
// Write Reg 177 = 0xAC
i2c.write_reg(SI5351A_I2C_ADDR, 177, 0xac);
// Enable SI5351A (CLK0 only)
// Write Reg 3 = 0xFE = Enable CLK0 (only)
i2c.write_reg(SI5351A_I2C_ADDR, SI5351A_CLKx_DIS, 0xfe); // note that a '0' turns the PLL output ON !!
delay();
} // End PLL_configuration
int main(void) {
i2c_master_if i2c[1];
par {
i2c_master(i2c, 1, p_scl, p_sda, 10);
si5351a_pll_configuration(i2c[0]);
}
return 0;
}
Chime back if you have any questions.