-
Notifications
You must be signed in to change notification settings - Fork 3
/
si5351a.c
executable file
·176 lines (151 loc) · 6.48 KB
/
si5351a.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/******************************************************************************
Author: Hans Summers, 2015
Website: http://www.hanssummers.com\
A very very simple Si5351a demonstration
using the Si5351a module kit http://www.hanssummers.com/synth
Please also refer to SiLabs AN619 which describes all the registers to use
5-26-2015 N7IFC
Modify to use DSPic I2C2 Write routine and to output complimentry
output signals. Change crystal frequency to 25MHz in header file.
8-16-2015 N7IFC
Create seperate init routine, minimize small pll frequency change glitches
*****************************************************************************/
#include <stdint.h>
//#include "i2c.h"
#include "si5351a.h"
#include "DSPIC33E_hardware.h"
#include "ODR1_Control_1.h"
//
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
//
void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom)
{
uint32_t P1; // PLL config register P1
uint32_t P2; // PLL config register P2
uint32_t P3; // PLL config register P3
P1 = (uint32_t)(128 * ((float)num / (float)denom));
P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
P2 = (uint32_t)(128 * ((float)num / (float)denom));
P2 = (uint32_t)(128 * num - denom * P2);
P3 = denom;
I2C2_Byte_Write(pll + 0, (P3 & 0x0000FF00) >> 8);
I2C2_Byte_Write(pll + 1, (P3 & 0x000000FF));
I2C2_Byte_Write(pll + 2, (P1 & 0x00030000) >> 16);
I2C2_Byte_Write(pll + 3, (P1 & 0x0000FF00) >> 8);
I2C2_Byte_Write(pll + 4, (P1 & 0x000000FF));
I2C2_Byte_Write(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
I2C2_Byte_Write(pll + 6, (P2 & 0x0000FF00) >> 8);
I2C2_Byte_Write(pll + 7, (P2 & 0x000000FF));
}
//
// Set up MultiSynth with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate register, it is a #define in si5351a.h
//
void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv)
{
uint32_t P1; // Synth config register P1
uint32_t P2; // Synth config register P2
uint32_t P3; // Synth config register P3
P1 = 128 * divider - 512;
P2 = 0; // P2 = 0, P3 = 1 forces an integer value for the divider
P3 = 1;
I2C2_Byte_Write(synth + 0, (P3 & 0x0000FF00) >> 8);
I2C2_Byte_Write(synth + 1, (P3 & 0x000000FF));
I2C2_Byte_Write(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
I2C2_Byte_Write(synth + 3, (P1 & 0x0000FF00) >> 8);
I2C2_Byte_Write(synth + 4, (P1 & 0x000000FF));
I2C2_Byte_Write(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
I2C2_Byte_Write(synth + 6, (P2 & 0x0000FF00) >> 8);
I2C2_Byte_Write(synth + 7, (P2 & 0x000000FF));
}
//
// Switches off Si5351a output
// Example: si5351aOutputOff(SI_CLK0_CONTROL);
// will switch off output CLK0
//
void si5351aOutputOff(uint8_t clk)
{
I2C2_Byte_Write(clk, 0x80); // Refer to SiLabs AN619 to see bit values - 0x80 turns off the output stage
}
//
// Set CLK0 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL A
// and MultiSynth 0
// and produces the output on CLK0
//
void si5351aSetFrequency(uint32_t frequency)
{
uint32_t pllFreq;
uint32_t xtalFreq = XTAL_FREQ;
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t divider;
divider = 900000000 / frequency; // Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency: the divider * desired output frequency
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL A with the calculated multiplication ratio
setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(SI_SYNTH_MS_0, divider, SI_R_DIV_1);
// Method 1 of generating a differential clock output is to use
// multisynth 2 to drive output two and share PLL A.
//setupMultisynth(SI_SYNTH_MS_2, divider, SI_R_DIV_1); // Set Multisynth 2 the same
}
/******************************************************************************
* Function: Init_si5351a
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: Initialize the si5351a registers that only need set once.
*****************************************************************************/
void Init_si5351a(void)
{
si5351aSetFrequency(radio_freq * 2); // Set receive frequency
// set crystal load capacitance to 8pf
I2C2_Byte_Write(SI_XTAL_LD_CAP, 0b10010010);
// Turn OFF spread spectrum
I2C2_Byte_Write(SI5351_SSC_PARAM0, 0b00000000);
// Method 2 Allow fanout of Multisynth0 to drive output 0 and 2
I2C2_Byte_Write(187, 0b00010000);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
I2C2_Byte_Write(SI_PLL_RESET, 0xA0);
// Finally switch on the CLK0 output (0x4F)
// and set the MultiSynth0 input to be PLL A
I2C2_Byte_Write(SI_CLK0_CONTROL, 0b01001100);
// Turn off clock 1
//I2C2_Byte_Write(SI_CLK1_CONTROL, 0b11011000);
// Method 1 Turn on CLK2 and invert output use multi synth 2
// I2C2_Byte_Write(SI_CLK2_CONTROL, 0b01011100);
// Method 2 Turn on CLK2 and invert output share multi synth 0
// This method should have better performance than method 1
I2C2_Byte_Write(SI_CLK2_CONTROL, 0b01011000);
}