Table of contents
Controlling the Si5351 series including the Si5351A from an Arduino might at first glance seem rather complex or perhaps even scary. Therefore you might revert to using the RFzero code as it is. But with a little help from your friends from RFzero, you will be in a much better position to tweak the examples, add functionality or write your own formulas and functions to calculate the registers and control the frequency or quadrature I/Q phase shift of the Si5351A.
Theory behind the Si5351A
The Si5351A consists of two synthesizers, three output dividers, one per output channel (CLK0, CLK1 and CLK2), and one output stage per channel that are all controlled via the I2C bus. A common oscillator circuit, operating at either 25 MHz or 27 MHz, is also built in, but it needs an external crystal.
The synthesizer
If you are not familiar with RF synthesizers, and how they work, you can see them as RF generators, that generate frequencies derived from another frequency, i.e. the reference frequency. In the RFzero this is what the 27 MHz crystal is used for.
A synthesizer consists of three main electronic circuits: a phase comparator, a VCO and a feedback divider. Skyworks names the feedback divider the Feedback Multisynth Divider (FMD). For each different programmed setting of the FMD the VCO will generate a different frequency.
In many RF applications this is all that is needed, if the wanted output frequencies are in wide steps, e.g. in 25 kHz steps used for FM channels.
The output multisynth divider
To improve the frequency resolution, from wide steps to finer steps, the synthesizer in the Si5351A is followed by a divider circuit, which Skyworks names the Output Multisynth Divider (OMD). In the Si5351A Skyworks has added a second divider stage, called R, that can divide the frequency generated by the synthesizer even further.
The drawback of an output divider is, that the output frequency (fout) will be lower than the VCO frequency (fvco). In the Si5351A there is a minimum division of four. This means that the VCO frequency is always at least four times higher than the output frequency. But instead of having wide frequency steps, e.g. 25 kHz, the steps can be much lower, e.g. 1 Hz. It all depends on how the FMD and OMD ratios are setup.
The math
If you are still with us please stay a bit longer, and we will take you by the hand and walk you through the math, and how to generate the wanted output frequency (fout) derived from the reference frequency (fref). In math lingo the output frequency can be expressed with the following formula:
The Si5351A FMD and OMD ratios can also be expressed as:
Please note that Skyworks also names the OMD variables, a, b and c, but, they are NOT THE SAME as those for the FMD. This is an obvious place to get the math wrong. So to avoid any possible confusion in the RFzero world they are named d, e and f respectively instead. Please remember this, if you dive into the RFzero code or Si5351A programming guide. But now we can write the complete relationship between the reference frequency and the output frequency:
That’s easy – right? Hmmm, not quite. We now have one equation with seven variables to get from the reference frequency to the output frequency. But the complexity doesn’t stop here. There are some constraints to the different variables and the electronic circuit too:
- The VCO frequency can only be from 600 MHz to 900 MHz. Yes, over-clocking is possible, and so in under-clocking, to extend the output frequency range beyond 2,3 kHz to 200 MHz. But let’s leave this for now.
- The FMD ratio can be from 15 + 0/1 048 575 and to 90 + 0/1 048 575, i.e. a = 15, b = 0 and c = 1 048 575 to a = 90, b = 0 and c = 1 048 575. Please remember that c can never be zero (0), as this will violate fundamental math principles.
- The OMD ratio can be 4 and from 6 to 2048. Please remember that f can never be zero (0), as this will violate fundamental math principles.
- The R divider can only be 1, 2, 4, 8, 16, 32, 64 or 128.
- If the output frequency is above 150 MHz d is always 4.
- If the output frequency is below 500 kHz R should be used, i.e. higher than 1.
You also need to understand a bit more about synthesizer and divider circuits. The FMD and OMD ratios are what is called fractional, e.g. 10 + 23 / 4567. But synthesizer and divider circuits produce more spurious and phase noise/time jitter when operated in fractional mode than in integer mode, e.g. 10 + 0 / 4567 = 10. So a clever RF designer will assess the situation, and decide if fractional modes are need for both the FMD and the OMD, a mix or use both in integer mode. If a mix is sufficient the OMD should be operated in integer mode. Furthermore, are even numbers for a and d preferred again due to spurious and noise/jitter. The software will have take all this into account.
Agreed, this doesn’t help a lot. But here comes the first trick to get started. Do a calculation using an initial VCO frequency of 600 MHz, if the wanted output frequency is below 150 MHz. So let’s continue with these design advises and choose an output frequency of 28,567 MHz which means that R = 1.
The second trick is then to work backwards from the output frequency by first calculating the R · OMD ratio:
This means that d is 21, because R is 1, and the e/f ratio is 0,0032555046032…
This is neither an integer nor an even one if rounded. So the next trick is to round d up to an even integer, i.e. 22, and check if the resulting VCO frequency is still within the valid range from 600 MHz to 900 MHz. If not then increase, or decrease, the value of d by two. Since we want to use the OMD in integer mode, the e/f ratio doesn’t matter, and the FMD will have to take care of this instead. So 22 · 28,567 MHz = 628,474 MHz which is a fine fvco.
We now know that d = 22, e = 0 and f = 1 or any other value as long as f is less than or equal to 1 048 575. So we can continue to find the FMD, i.e. a, b and c:
where a = 23 is the integer part of the FMD and b/c = 0,2768148148148… is the remainder.
Hang in there we are almost done. But how do we solve the b/c = 0,2768148148148… equation? The easiest way to do this, which also gives the best resolution, is to set c to 1 048 575. This results in b equals 290 261.
That’s it! We now have the following values:
- a = 23
- b = 290 261
- c = 1 048 575
- d = 22
- e = 0 (static)
- f = 1 (static)
- R = 1
But will the output frequency be correct then? Well, let’s find out:
OK, not spot on but -111 mHz away.
What if the output frequency is less than 500 kHz? In this case the R divider comes into play. Fortunately the procedure is simple. Multiply the wanted output frequency below 500 kHz with a valid R value (1, 2, 4, 8 … 128) so the “new frequency” becomes greater than 500 kHz. Then do the above math using this new frequency as fout in the above equations, e.g. if you want 100 kHz an R = 8 will bring fout up to 800 kHz. The datasheet says that the R divider can be used below 500 kHz, but to get to the lowest frequency the Si5351A can generate, this border frequency should be set to 292 969 Hz instead coming from fvco / dmax = 600 MHz / 2048.
Frequency code for the Si5351A
Below is a function that calculates the FMD, OMD and R variables and returns the register values for the Si5351A. In this code the OMD is set to operate in integer mode, thus e = 0 and f = 1.
The code works for frequencies from 2289 Hz and up. According to Skyworks the Si5351A can work up to 200 MHz. It is actually possible to use the Si5351A beyond 290 MHz, but the maximum frequency depends very much of the actual Si5351A device. Once you get closer to the frequency limit of the Si5351A, you will experience, that the synthesizer has difficulties getting into a steady state (locked). Beyond 200 MHz the signal-to-noise ratio also decreases. Another way to use the Si5351A at higher frequencies is to use one of the harmonics. But please keep in mind that higher output frequency also means worse spectral performance.
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 |
void CalcRegisters(const uint32_t fout, uint8_t *regs) { uint32_t fref = 27000000UL; // The reference frequency // Calc Output Multisynth Divider and R with e = 0 and f = 1 => msx_p2 = 0 and msx_p3 = 1 uint32_t d = 4; uint32_t msx_p1 = 0; // If fout > 150 MHz then MSx_P1 = 0 and MSx_DIVBY4 = 0xC0, see datasheet 4.1.3 int msx_divby4 = 0; int rx_div = 0; int r = 1; if (fout > 150000000UL) msx_divby4 = 0x0C; // MSx_DIVBY4[1:0] = 0b11, see datasheet 4.1.3 else if (fout < 292969UL) // If fout < 500 kHz then use R divider, see datasheet 4.2.2. In reality this means > 292 968,75 Hz when d = 2048 { int rd = 0; while ((r < 128) && (r * fout < 292969UL)) { r <<= 1; rd++; } rx_div = rd << 4; d = 600000000UL / (r * fout); // Use lowest VCO frequency but handle d minimum if (d % 2) // Make d even to reduce spurious and phase noise/jitter, see datasheet 4.1.2.1. d++; if (d * r * fout < 600000000UL) // VCO frequency to low check and maintain an even d value d += 2; } else // 292968 Hz <= fout <= 150 MHz { d = 600000000UL / fout; // Use lowest VCO frequency but handle d minimum if (d < 6) d = 6; else if (d % 2) // Make d even to reduce phase noise/jitter, see datasheet 4.1.2.1. d++; if (d * fout < 600000000UL) // VCO frequency to low check and maintain an even d value d += 2; } msx_p1 = 128 * d - 512; uint32_t fvco = (uint32_t) d * r * fout; // Calc Feedback Multisynth Divider double fmd = (double)fvco / fref; // The FMD value has been found int a = fmd; // a is the integer part of the FMD value double b_c = (double)fmd - a; // Get b/c uint32_t c = 1048575UL; uint32_t b = (double)b_c * c; if (b > 0) { c = (double)b / b_c + 0.5; // Improves frequency precision in some cases if (c > 1048575UL) c = 1048575UL; } uint32_t msnx_p1 = 128 * a + 128 * b / c - 512; // See datasheet 3.2 uint32_t msnx_p2 = 128 * b - c * (128 * b / c); uint32_t msnx_p3 = c; // Feedback Multisynth Divider register values regs[0] = (msnx_p3 >> 8) & 0xFF; regs[1] = msnx_p3 & 0xFF; regs[2] = (msnx_p1 >> 16) & 0x03; regs[3] = (msnx_p1 >> 8) & 0xFF; regs[4] = msnx_p1 & 0xFF; regs[5] = ((msnx_p3 >> 12) & 0xF0) + ((msnx_p2 >> 16) & 0x0F); regs[6] = (msnx_p2 >> 8) & 0xFF; regs[7] = msnx_p2 & 0xFF; // Output Multisynth Divider and R register values regs[8] = 0; // (msx_p3 >> 8) & 0xFF regs[9] = 1; // msx_p3 & 0xFF regs[10] = rx_div + msx_divby4 + ((msx_p1 >> 16) & 0x03); regs[11] = (msx_p1 >> 8) & 0xFF; regs[12] = msx_p1 & 0xFF; regs[13] = 0; // ((msx_p3 >> 12) & 0xF0) + (msx_p2 >> 16) & 0x0F regs[14] = 0; // (msx_p2 >> 8) & 0xFF regs[15] = 0; // msx_p2 & 0xFF return; } |
Here is an online tool to calculate the FMD, OMD and R register values.
Putting it all together
We are now almost ready generate a frequency with the Si5351A. But before we can do this a pair of I2C (Wire) read and write functions are needed.
The I2C (Wire) read function.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
uint8_t ReadRegister(uint8_t regAddr) { int data = 0xFF; // Set value often not seen Wire.beginTransmission(0x60); // The I2C address of the Si5351A Wire.write((uint8_t)regAddr); Wire.endTransmission(); Wire.requestFrom(0x60, 1); if (Wire.available()) data = Wire.read(); return data; } |
The I2C (Wire) write function.
1 2 3 4 5 6 7 |
void WriteRegister(uint8_t regAddr, uint8_t data) { Wire.beginTransmission(0x60); // The I2C address of the Si5351A Wire.write((uint8_t) regAddr); Wire.write((uint8_t) data); Wire.endTransmission(); } |
For the RFzero the Wire instance must be replaced by WireLocal.
Initializing the Si5351A
In the below example the structure of the initialization of the Si5351A is taken from the flowchart in the datasheet.
The FMD for PLLA is used and connected to CLK0. The OMD for CLK0 is set to integer mode. Only CLK0 is initialized and with an output stage current of 8 mA. The reference load is set to 6 pF.
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 |
void Initialize() { // Initialize Si5351A while (ReadRegister(0) & 0x80); // Wait for Si5351A to initialize WriteRegister(3, 0xFF); // Output Enable Control, disable all for (int i = 16; i < 24; i++) WriteRegister (i, 0x80); // CLKi Control, power down CLKi WriteRegister(15, 0x00); // PLL Input Source, select the XTAL input as the reference clock for PLLA and PLLB WriteRegister(24, 0x00); // CLK3–0 Disable State, unused are low and never disable CLK0 // Output Multisynth0, e = 0, f = 1, MS0_P2 and MSO_P3 WriteRegister(42, 0x00); WriteRegister(43, 0x01); WriteRegister(47, 0x00); WriteRegister(48, 0x00); WriteRegister(49, 0x00); WriteRegister(16, 0x4F); // Power up CLK0, PLLA, MS0 operates in integer mode, Output Clock 0 is not inverted, Select MultiSynth 0 as the source for CLK0 and 8 mA // Reference load configuration WriteRegister(183, 0x12); // Set reference load C: 6 pF = 0x12, 8 pF = 0x92, 10 pF = 0xD2 // Turn CLK0 output on WriteRegister(3, 0xFE); // Output Enable Control. Active low } |
The program
The final program is now ready. Add each of the above functions to the .ino file and complete the setup() function.
The setup() and loop() part of the program.
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 |
void setup() { uint8_t regs[16]; // Registers holding the FMD and OMD values const uint32_t freq = 28567000; // The wanted output frequency Initialize(); CalcRegisters(freq, regs); // Load PLLA Feedback Multisynth NA for (int i = 0; i < 8; i++) WriteRegister(26 + i, regs[i]); // Load Output Multisynth0 with d (e and f already set during init. and never changed) for (int i = 10; i < 13; i++) WriteRegister(34 + i, regs[i]); // Reset PLLA delayMicroseconds(500); // Allow registers to settle before resetting the PLL WriteRegister(177, 0x20); } void loop() { } |
Quadrature I/Q and 90° phase offset
Before diving into the math please remember that two different frequencies can never have a static phase difference. Only the same frequency may have a static phase difference.
In the Si5351A programming guide the below formula for applying a phase offset has to be used:
Making a 90° phase offset, e.g. for I/Q quadrature purposes, is very easy when the OMD is operated in integer mode. But first we must find the relationship between toff and 90°. In general it applies that time and frequency are related as:
e.g. a signal with a frequency of 1 kHz has a time cycle of 1 ms. Furthermore do we know that one cycle of a signal is 360° thus 90° is one quarter of the cycle:
From the frequency math we know that the relationship between the wanted frequency and VCO frequency is:
Thus we can now put all the elements into the formula:
So to apply a 90° phase offset load the relevant register with the value of the OMD. Only seven bits are available thus, the max value is 127. However, to keep the OMD an even integer the max value is then 126. Since the specified lower VCO frequency is 600 MHz, the lowest I/Q output frequency is then 4,762 MHz. For a lower frequency please see how to extended the frequency range in the description below.
Extended frequency range
As mentioned in the theory section it is possible to extend the frequency range by “overclocking” or “underclocking” the Si5351A. This means that the VCO will operate over or under its specified frequency range from 600 MHz to 900 MHz. In either case the outer limits are very device specific. This means that one Si5351A may go a bit further than another one, and you cannot tell before trying.
When operating outside the specified frequency range, from 600 MHz to 900 MHz, the RF performance is reduced which means worse spectrum. When operating close to the limits, but still in lock, the signal becomes much less clean. When operating outside the min/max VCO frequency range, the PLL will not lock, thus the signal becomes very unstable and often useless.
Empirical data shows that the typical maximum VCO frequency is slightly lower than 1,2 GHz. Given that the minimum OMD is 4, this means that the maximum output frequency is somewhere between 290 MHz and 300 MHz. At the lower end the minimum VCO frequency is around 420 MHz resulting in a minimum output frequency around 1,6 kHz. For easy I/Q purposes the minimum output frequency is around 3,3 MHz with an even integer OMD.