Si5351mcu
Arduino Si5351 library tuned for size and click free.
Install / Use
/learn @pavelmc/Si5351mcuREADME
Arduino Si5351 Library tuned for size and click noise free
This library is tuned for size on the Arduino platform, it will control CLK0, CLK1 and CLK2 outputs for the Si5351A (the version with just 3 clocks out, but you will not be able to use the three at once).
Inspiration
This work is based on the previous work of these great people:
- Etherkit/NT7S: The mainstream full featured lib, with big code as well (based on Linux kernel code)
- QRP Labs demo code from Hans Summers: The smallest and simple ones on the net
- DK7IH demo code: The first clickless noise code on the wild
- Jerry Gaffke integer routines for the Raduino and ubitx
Set your Goal and make an estrategy
There is a few routines in the Internet to manage the Si5351 chip, all of them has a few distinct feature set because they use different strategies (different goals) that make them unique in some way.
My goal is this:
- Keep it as small as possible (Smallest firmware footprint)
- Less phase and click noise possible (Playing with every trick possible)
- Make it as fast as possible (thanks to @birdwes for I2C busrt mode write)
The main purpose is to be used in Radio receiver projects, so this two mentioned goals are the golden rule.
Let's list some of goals achievements and bonuses:
Small firmware footprint:
A basic sketch to set just only one clock out to a given frequency with a change in power and correcting the XTAL ppm error is only ~3.3 kBytes of firmware (~10% of an Arduino Uno)
The same settings with the Si5351Arduino library (Etherkit) will give you a bigger firmware space of ~10 kBytes or 31% of an Arduino Uno.
Jerry Gaffke embedded routines in the ubitx transceiver has the smallest footprint in the Arduino platform I have seen, but has worst phase noise and smallest frequency range.
Phase noise to the minimum:
We use every trick on the datasheet, OEM Application Notes or the Internet to minimize phase noise. (Even a few ones learned on the process)
For example the Etherkit library and Jerry Gaffke embedded routines uses some but not all the tricks to minimize phase noise (Etherkit one gives control over all features, Jerry Gaffke has small footprint and in the process he sacrifice phase noise and frequency range)
Click noise free:
If you play from the book (Datasheet and Application Notes) you will have a "click-like" noise burst every time you change the output frequency.
That's not a problem if you intend to use only fixed frequencies at the output, but if you plan to sweep or use it on any application that moves the frequency that click-like noise will haunt you. (like in a radio receiver or transceiver)
I have learned a few tricks from many sources in the Internet and after some local testing I have came across a scheme that make this lib virtually click-noise-less; see the "Click noise free" section below for details.
Fast frequency changes:
This was a side effect of the last trick to minimize the click noise, see the "Click noise free" section below for details; also with the I2C busrt write contribution from @birdwes even the I2C writes takes a lot less time (implemented since version 0.7.0)
Summary: other routines write all registers for every frequency change, one byte at a time; I write half of them most of the time and in a bust mode speeding up the process a lot.
Two of three
Yes, there is no such thing as free lunch, to get all the above features and the ones that follow I have to make some sacrifices, in this case spare one of the outputs. See "Two of three" section below.
Features
This are so far the implemented features (Any particular wish? use the Issues tab for that):
- Custom XTAL passing on init (Default is 27.000 MHz (See Si.init() )
- You can pass a correction to the xtal while running (See Si.correction() )
- You have a fast way to power off all outputs of the Chip at once. (See Si.off() )
- You can enable/disable any output at any time (See Si.enable(clk) and Si.disable(clk) )
- By default all outputs are off after the Si.init() procedure. You has to enable them by hand.
- You can only have 2 of the 3 outputs running at any moment (See "Two of three" section below)
- Power control on each output independently (See Si.setPower(clk, level) on the lib header)
- Initial power defaults to the lowest level (2mA) for all outputs.
- You don't need to include and configure the Wire (I2C) library, this lib do that for you already.
- I2C writes are handled in busrt mode, just init the I2C once per frequency change and dump the registers content and close; saving the init for each byte sent as normal.
- Frequency limits are not hard coded on the lib, so you can stress your hardware to it's particular limit (You can move usually from ~3kHz to ~225 MHz, far away from the 8kHz to 160 MHz limits from the datasheet)
- You has a way to verify the status of a particular clock (Enabled/Disabled by the Si.clkOn[clk] var)
- From v0.5 and beyond we saved more than 1 kbyte of your precious firmware space due to the use of all integer math now (Worst induced error is below +/- 1 Hz)
- Overclock, yes, you can move the limits upward up to ~250MHz (see the "OVERCLOCK" section below)
- Improved the click noise algorithm to get even more click noise reduction (see Click noise free section below)
- Fast frequency changes as part of the improved click noise algorithm (see Click noise free section below) & I2C writes in burst mode.
How to use the lib
Get the lib by cloning this git repository or get it by clicking the green "Download button" on the page.
Move it or extract it on your library directory
Include the lib in your code:
(... your code here ...)
// now include the library
#include "si5351mcu.h"
// lib instantiation as "Si"
Si5351mcu Si;
(... more of your code here ...)
Follow this sequence on you setup() procedure:
- Initialize the library with the default or optional Xtal Clock.
- Apply correction factor (if needed)
- Set some frequencies to the desired outputs.
- Enable the desired outputs
Here you have an example code ("Si" is the lib instance):
setup() {
(... your code here ...)
//////////////////////////////////
// Si5351 functions /
//////////////////////////////////
// Init the library, in this case with the default 27.000 Mhz Xtal
Si.init();
// commented Init procedure for a not default 25.000 MHz xtal
//Si.init(25000000L);
// Optional, apply a pre-calculated correction factor
Si.correction(-150); // Xtal is low by 150 Hz
// Enable the desired outputs with some frequencies
Si.setFreq(0, 25000000); // CLK0 output 25.000 MHz
Si.setFreq(1, 145000000); // CLK1 output 145.000 MHz
// enable the outputs
Si.enable(0);
Si.enable(1);
(... more of your code here ...)
}
If you need to apply/vary the correction factor after the setup process you will get a click noise on the next setFreq() to apply the changes.
Use it, you can enable, disable, change the power or frequency, see this code fragment with some examples:
loop() {
(... your code here ...)
// disable clk1
Si.disable(1);
// change the power of clk0 to 4mA
Si.setPower(0, SIOUT_4mA);
// apply a correction factor of 300 Hz (correction will be applied on the next Si.setFreq() call)
Si.correction(300);
// change the clk0 output frequency
Si.setFreq(0, 7110000);
// power of all outputs
Si.off();
(... more of your code here ...)
}
OVERCLOCK
Yes, you can overclock the Si5351, the datasheet states that the VCO moves from 600 to 900 MHz and that gives us a usable range from ~3 kHz to 225 MHz.
But what if we can move the VCO frequency to a higher values?
The overclock feature does just that, use a higher top limit for the VCO on the calculations. In my test with two batch of the Si5351A I can get safely up to 1.000 GHz without trouble; in one batch the PLL unlocks around 1.1 GHz and in the other about 1.050 GHz; so I recommend not going beyond 1.000 GHz.
With a maximum VCO of 1.000 GHz and a lower division factor of 4 we have jumped from a 225 MHz to 250 MHz top frequency that can be generated with our cheap chip.
Some "must include" WARNINGS:
- The chip was not intended to go that high, so, use it with caution and test it on your hardware moving the overclock limit in steps of 10 MHz starting with 900 MHz and testing with every change until it does not work; then go down by 10 MHz to be in a safe zone.
- Moving the upper limit has its penalty on the lower level, your bottom frequency will move from the ~3 kHz to ~10 kHz range.
- The phase noise of the output if worst as you use a higher frequency, at a fixed 250 MHz it's noticeable but no so bad for a TTL or CMOS clock application.
- The phase noise is specially bad if you make a sweep or move action beyond 180 MHz; the phase noise from the unlock state to next lock of the PLL is very noticeable in a spectrum analyzer, even on a cheap RTL-SDR one.
- I recommend to only use the outputs beyond 150 MHz as fixed value and don't move them if you cares about phase noise.
How to do it?
You need to declare a macro with the overclock value BEFORE the library include, just like this:
(... your code here ...)
// Using the overclock feature for the Si5351mcu library
#define SI_OVERCLOCK 1000000000L // 1.0 GHz in Hz
// now include the library
#include "si5351mcu.h"
// lib instantiation as "Si"
Si5351mcu Si;
// now you can generate frequencies from ~10 kHz up to 250
Related Skills
node-connect
349.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.7kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
