forked from nanovna-v2/NanoVNA2-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsynthesizers.cpp
182 lines (153 loc) · 5.96 KB
/
synthesizers.cpp
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
177
178
179
180
181
182
#include "synthesizers.hpp"
#include <board.hpp>
#include <mculib/si5351.hpp>
#include <mculib/printk.hpp>
using namespace board;
namespace synthesizers {
bool si5351_setup() {
using namespace Si5351;
si5351.SetFieldsToDefault(); //initialize the structure with default "safe" values
// hook up i2c
uint8_t devAddr = 0xC0;
si5351.ReadRegister = [devAddr](uint8_t addr) -> uint8_t {
return si5351_i2c.read_si5351(devAddr, addr);
};
si5351.WriteRegister = [devAddr](uint8_t addr, uint8_t data) -> int {
return si5351_i2c.write(devAddr, addr, data);
};
si5351.WriteRegisters = [devAddr](uint8_t* data, int len) -> int {
return si5351_i2c.write(devAddr, data, len);
};
si5351.OSC.OSC_XTAL_Load = XTAL_Load_4_pF; //use 4 pF load for crystal
auto rPLL = si5351_rxPLL;
auto tPLL = si5351_txPLL;
auto rPort = si5351_rxPort;
auto tPort = si5351_txPort;
auto pPort = si5351_passthruPort;
si5351.PLL[rPLL].PLL_Clock_Source = PLL_Clock_Source_XTAL; //select xrystal as clock input for the PLL
si5351.PLL[rPLL].PLL_Multiplier_Integer = 32*128; //multiply the clock frequency by 32, this gets us 800 MHz clock
si5351.PLL[rPLL].PLL_Multiplier_Numerator = 1*8;
si5351.PLL[rPLL].PLL_Multiplier_Denominator = xtalFreqHz / 1000;
si5351.PLL[tPLL].PLL_Clock_Source = PLL_Clock_Source_XTAL;
si5351.PLL[tPLL].PLL_Multiplier_Integer = 32*128;
si5351.PLL[tPLL].PLL_Multiplier_Numerator = 2*8;
si5351.PLL[tPLL].PLL_Multiplier_Denominator = xtalFreqHz / 1000;
si5351.MS[rPort].MS_Clock_Source = MS_Clock_Source_PLLA;
si5351.MS[rPort].MS_Divider_Integer = 8; // divide pll frequency by 8
si5351.MS[tPort].MS_Clock_Source = MS_Clock_Source_PLLA;
si5351.MS[tPort].MS_Divider_Integer = 8; // divide pll frequency by 8
si5351.CLK[rPort].CLK_R_Div = CLK_R_Div1; // divide by 1; 100MHz
si5351.CLK[rPort].CLK_Enable = ON; //turn on the output
si5351.CLK[rPort].CLK_I_Drv = CLK_I_Drv_8mA;
if(pPort >= 0) {
si5351.CLK[pPort].CLK_Clock_Source = CLK_Clock_Source_XTAL;
si5351.CLK[pPort].CLK_R_Div = CLK_R_Div1; // divide by 1; 24MHz
si5351.CLK[pPort].CLK_Enable = ON; //turn on the output
si5351.CLK[pPort].CLK_I_Drv = CLK_I_Drv_2mA;
}
si5351.CLK[tPort].CLK_R_Div = CLK_R_Div1; // divide by 1; 100MHz
si5351.CLK[tPort].CLK_Enable = ON; //turn on the output
si5351.CLK[tPort].CLK_I_Drv = CLK_I_Drv_2mA;
return si5351.Init() == 0;
}
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;
}
int si5351_set(uint32_t rxFreqHz, uint32_t txFreqHz) {
using namespace Si5351;
int ret = 0;
CLKRDiv rDiv = CLK_R_Div1;
// choose the same pll frequency and rdiv settings for both ports.
// PLL should be configured between 600 and 900 MHz
// Pick a multiple of 24Mhz. (The Xtal freq)
uint32_t pllFreqHz = 888000000;
uint32_t mult = pllFreqHz/xtalFreqHz;
uint32_t N = mult * 128;
pllFreqHz = xtalFreqHz * mult;
int divInputFreqHz = pllFreqHz;
if(rxFreqHz < 500000) { /* Below 500Khz */
rDiv = CLK_R_Div128;
divInputFreqHz /= 128;
} else if(rxFreqHz < 1000000) { /* Between 500hz and 1 Mhz */
rDiv = CLK_R_Div4;
divInputFreqHz /= 4;
} else if(rxFreqHz >= 100000000) { /* Above 100Mhz */
// div by 6 mode
int xtalFreqKHz = xtalFreqHz / 1000;
for(int i=0; i<2; i++) {
uint32_t freqHz = (i == 0) ? rxFreqHz : txFreqHz;
int pll = (i == 0) ? si5351_rxPLL : si5351_txPLL;
int port = (i == 0) ? si5351_rxPort : si5351_txPort;
// set msdiv to 6
if(si5351.MS[port].MS_Divider_Integer != 6
|| si5351.MS[port].MS_Divider_Numerator != 0) {
si5351.MS[port].MS_Divider_Integer = 6;
si5351.MS[port].MS_Divider_Numerator = 0;
si5351.MS[port].MS_Divider_Denominator = 1;
si5351.MS[port].MS_Clock_Source = (i == 1) ? MS_Clock_Source_PLLB : MS_Clock_Source_PLLA;
si5351.MSConfig((MSChannel) port);
}
if(si5351.CLK[port].CLK_R_Div != rDiv) {
si5351.CLK[port].CLK_R_Div = rDiv;
si5351.CLKConfig((CLKChannel) port);
}
// calculate pll settings
uint32_t mult = uint32_t(uint64_t(freqHz)*6*128/1000);
uint32_t N = mult/xtalFreqKHz;
uint32_t frac = mult - N*xtalFreqKHz;
si5351.PLL[pll].PLL_Multiplier_Integer = N;
si5351.PLL[pll].PLL_Multiplier_Numerator = frac;
si5351.PLL[pll].PLL_Multiplier_Denominator = xtalFreqKHz;
si5351.PLLConfig((PLLChannel) pll);
}
si5351.PLLReset2();
return 2;
}
if(si5351.PLL[si5351_rxPLL].PLL_Multiplier_Integer != N
|| si5351.PLL[si5351_rxPLL].PLL_Multiplier_Numerator != 0) {
si5351.PLL[si5351_rxPLL].PLL_Multiplier_Integer = N;
si5351.PLL[si5351_rxPLL].PLL_Multiplier_Numerator = 0;
si5351.PLL[si5351_rxPLL].PLL_Multiplier_Denominator = 1;
si5351.PLLConfig((PLLChannel) si5351_rxPLL);
si5351.PLLReset((PLLChannel) si5351_rxPLL);
ret = 2;
}
for(int i=0; i<2; i++) {
uint32_t freqHz = (i == 0) ? rxFreqHz : txFreqHz;
int port = (i == 0) ? si5351_rxPort : si5351_txPort;
int32_t div = divInputFreqHz / freqHz; // range: 8 ~ 1800
int32_t num = divInputFreqHz % freqHz;
int32_t denom = freqHz;
//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;
}
// f = divInputFreqHz / (div + num/denom)
// = divInputFreqHz / ((div*denom + num) / denom)
// = divInputFreqHz * denom / (div*denom + num)
//uint32_t f = uint32_t(uint64_t(divInputFreqHz) * denom / (uint64_t(div)*denom + num));
si5351.MS[port].MS_Divider_Integer = div;
si5351.MS[port].MS_Divider_Numerator = num;
si5351.MS[port].MS_Divider_Denominator = denom;
si5351.MS[port].MS_Clock_Source = (si5351_rxPLL == 1) ? MS_Clock_Source_PLLB : MS_Clock_Source_PLLA;
si5351.MSConfig((MSChannel) port);
//printk("freq %d, div %d, num %d, denom %d\n", freq_khz, div, num, denom);
if(si5351.CLK[port].CLK_R_Div != rDiv) {
si5351.CLK[port].CLK_R_Div = rDiv;
si5351.CLKConfig((CLKChannel) port);
if(ret < 1) ret = 1;
}
}
return ret;
}
}