{"id":13410214,"url":"https://github.com/pavelmc/Si5351mcu","last_synced_at":"2025-03-14T15:32:04.738Z","repository":{"id":40605217,"uuid":"87822973","full_name":"pavelmc/Si5351mcu","owner":"pavelmc","description":"Arduino Si5351 library tuned for size and click free.","archived":false,"fork":false,"pushed_at":"2022-03-22T19:39:05.000Z","size":88,"stargazers_count":60,"open_issues_count":4,"forks_count":25,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-07-31T20:41:13.691Z","etag":null,"topics":["arduino","bitx20","click-free","co7wt","dds","hamradio","homebrew","pll","si5351","vfo"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pavelmc.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-10T14:49:19.000Z","updated_at":"2024-07-11T08:53:40.000Z","dependencies_parsed_at":"2022-08-09T23:50:35.738Z","dependency_job_id":null,"html_url":"https://github.com/pavelmc/Si5351mcu","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pavelmc%2FSi5351mcu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pavelmc%2FSi5351mcu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pavelmc%2FSi5351mcu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pavelmc%2FSi5351mcu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pavelmc","download_url":"https://codeload.github.com/pavelmc/Si5351mcu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243600701,"owners_count":20317320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","bitx20","click-free","co7wt","dds","hamradio","homebrew","pll","si5351","vfo"],"created_at":"2024-07-30T20:01:05.580Z","updated_at":"2025-03-14T15:32:04.449Z","avatar_url":"https://github.com/pavelmc.png","language":"C++","funding_links":[],"categories":["Arduino \u0026 Pic related","C++"],"sub_categories":["Libraries"],"readme":"# Arduino Si5351 Library tuned for size and click noise free #\n\nThis 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).\n\n## Inspiration ##\n\nThis work is based on the previous work of these great people:\n\n* [Etherkit/NT7S:](https://github.com/etherkit/Si5351Arduino) The mainstream full featured lib, with big code as well (based on Linux kernel code)\n* [QRP Labs demo code from Hans Summers:](http://qrp-labs.com/synth/si5351ademo.html) The smallest and simple ones on the net\n* [DK7IH demo code:](https://radiotransmitter.wordpress.com/category/si5351a/) The first clickless noise code on the wild\n* [Jerry Gaffke](https://github.com/afarhan/ubitx.git) integer routines for the Raduino and ubitx\n\n## Set your Goal and make an estrategy ##\n\nThere 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.\n\nMy goal is this:\n\n* Keep it as small as possible (Smallest firmware footprint)\n* Less phase and click noise possible (Playing with every trick possible)\n* Make it as fast as possible (thanks to @birdwes for I2C busrt mode write)\n\nThe main purpose is to be used in Radio receiver projects, so this two mentioned goals are the golden rule.\n\nLet's list some of goals achievements and bonuses:\n\n**Small firmware footprint:**\n\nA 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)\n\nThe same settings with the [Si5351Arduino library (Etherkit)](https://github.com/etherkit/Si5351Arduino) will give you a bigger firmware space of ~10 kBytes or 31% of an Arduino Uno.\n\n[Jerry Gaffke](https://github.com/afarhan/ubitx.git) 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.\n\n**Phase noise to the minimum:**\n\nWe use every trick on the datasheet, OEM Application Notes or the Internet to minimize phase noise. (Even a few ones learned on the process)\n\nFor example the [Etherkit](https://github.com/etherkit/Si5351Arduino) library and [Jerry Gaffke](https://github.com/afarhan/ubitx.git) 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)\n\n**Click noise free:**\n\nIf you play from the book (Datasheet and Application Notes) you will have a \"click-like\" noise burst every time you change the output frequency.\n\nThat'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)\n\nI 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.\n\n**Fast frequency changes:**\n\nThis 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)\n\nSummary: 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.\n\n**Two of three**\n\nYes, 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.\n\n## Features ##\n\nThis are so far the implemented features (Any particular wish? use the Issues tab for that):\n\n* Custom XTAL passing on init (Default is 27.000 MHz (See _Si.init()_ )\n* You can pass a correction to the xtal while running (See _Si.correction()_ )\n* You have a fast way to power off all outputs of the Chip at once. (See _Si.off()_ )\n* You can enable/disable any output at any time (See _Si.enable(clk) and Si.disable(clk)_ )\n* By default all outputs are off after the Si.init() procedure. You has to enable them by hand.\n* You can only have 2 of the 3 outputs running at any moment (See \"Two of three\" section below)\n* Power control on each output independently (See _Si.setPower(clk, level)_ on the lib header)\n* Initial power defaults to the lowest level (2mA) for all outputs.\n* You don't need to include and configure the Wire (I2C) library, this lib do that for you already.\n* 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.\n* 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_)\n* You has a way to verify the status of a particular clock (_Enabled/Disabled by the Si.clkOn[clk] var_)\n* 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)\n* Overclock, yes, you can move the limits upward up to ~250MHz (see the \"OVERCLOCK\" section below)\n* Improved the click noise algorithm to get even more click noise reduction (see Click noise free section below)\n* Fast frequency changes as part of the improved click noise algorithm (see Click noise free section below) \u0026 I2C writes in burst mode.\n\n## How to use the lib ##\n\nGet the lib by cloning this git repository or get it by clicking the green \"Download button\" on the page.\n\nMove it or extract it on your library directory\n\nInclude the lib in your code:\n\n\n```\n(... your code here ...)\n\n// now include the library\n#include \"si5351mcu.h\"\n\n// lib instantiation as \"Si\"\nSi5351mcu Si;\n\n(... more of your code here ...)\n\n```\n\nFollow this sequence on you setup() procedure:\n\n* Initialize the library with the default or optional Xtal Clock.\n* Apply correction factor (if needed)\n* Set some frequencies to the desired outputs.\n* Enable the desired outputs\n\nHere you have an example code (\"Si\" is the lib instance):\n\n```\nsetup() {\n    (... your code here ...)\n\n    //////////////////////////////////\n    //        Si5351 functions       /\n    //////////////////////////////////\n\n    // Init the library, in this case with the default 27.000 Mhz Xtal\n    Si.init();\n\n    // commented Init procedure for a not default 25.000 MHz xtal\n    //Si.init(25000000L);\n\n    // Optional, apply a pre-calculated correction factor\n    Si.correction(-150);    // Xtal is low by 150 Hz\n\n    // Enable the desired outputs with some frequencies\n    Si.setFreq(0, 25000000);       // CLK0 output 25.000 MHz\n    Si.setFreq(1, 145000000);      // CLK1 output 145.000 MHz\n\n    // enable the outputs\n    Si.enable(0);\n    Si.enable(1);\n\n    (... more of your code here ...)\n}\n\n```\n\nIf 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.\n\nUse it, you can enable, disable, change the power or frequency, see this code fragment with some examples:\n\n```\nloop() {\n    (... your code here ...)\n\n    // disable clk1\n    Si.disable(1);\n\n    // change the power of clk0 to 4mA\n    Si.setPower(0, SIOUT_4mA);\n\n    // apply a correction factor of 300 Hz (correction will be applied on the next Si.setFreq() call)\n    Si.correction(300);\n\n    // change the clk0 output frequency\n    Si.setFreq(0, 7110000);\n\n    // power of all outputs\n    Si.off();\n\n    (... more of your code here ...)\n}\n```\n\n\n## OVERCLOCK ##\n\nYes, 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.\n\nBut what if we can move the VCO frequency to a higher values?\n\nThe 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.\n\nWith 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.\n\n**Some \"must include\" WARNINGS:**\n\n* 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.\n* Moving the upper limit has its penalty on the lower level, your bottom frequency will move from the ~3 kHz to ~10 kHz range.\n* 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.\n* 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.\n* I recommend to only use the outputs beyond 150 MHz as fixed value and don't move them if you cares about phase noise.\n\n**How to do it?**\n\nYou need to declare a macro with the overclock value **BEFORE** the library include, just like this:\n\n```\n(... your code here ...)\n\n// Using the overclock feature for the Si5351mcu library\n#define SI_OVERCLOCK 1000000000L      // 1.0 GHz in Hz\n\n// now include the library\n#include \"si5351mcu.h\"\n\n// lib instantiation as \"Si\"\nSi5351mcu Si;\n\n// now you can generate frequencies from ~10 kHz up to 250 MHz.\n\n(... more of your code here ...)\n\n```\n\n## Click noise free ##\n\nClick-like noise came from a few sources as per my testing:\n\n* Turn off then on the CLKx outputs (Register 3. Output Enable Control)\n* Power down then on the Msynths (CLKx_PDN bits for every Msynth)\n* Reset the PLL (Register 177: PLL Reset)\n\nWe are concerned about click noise only when changing from one frequency to the other, so if we don't touch the output enable control or the power down msynth registers once activated; then we are set to avoid click from this two first sources.\n\nThe last one is tricky, in theory a PLL does not need to be reseted every time you change it's output frequency as it's a **P**hase **L**ocked **L**oop and it's a self correcting algorithm/hardware.\n\nThat last idea was put to test on a simple scheme: what if I set a fixed output divider Msynth and move the VCO for it's entire range without resetting it on any point?\n\nIf the \"PLL reset\" is in deed needed I will have some strange behavior at some point, right?\n\nBut practice confirmed my idea, I can set a output Msynth of 6 and move the VCO (PLL) for the entire range (600 to 900 MHz) and get a continuous and stable output from 100 to 150 MHz.\n\nThen what is for the \"PLL reset\" (Register 177) in practice?\n\nSome further test showed that this \"reset\" function is applied no to the PLL Msynth, but to the output Msynth and not in every case, yes, it has a bad name or a bad explained name.\n\nAfter some test I find that you need the \"PLL reset\" (Register 177) trick only on some cases when you change the value of the output divider Msynth.\n\nImplementing that in code was easy, an array to keep track of the actual output divider Msynth and only write it to the chip and reset \"the PLL\" when it's needed.\n\nHey! that leads to a I2C time reduction by half (most of the time) as a side effect!\n\nMost of the time when you are making a sweep the output divider Msynth has a constant value and you only moves the VCO (PLL) Then I wrote just 8 bytes to the I2C bus (to control the VCO/PLL) instead of 16 (8 for the VCO/PLL \u0026 8 more for the output divider Msynth) or 17 (16 + reset byte) most of the time, cutting time between writes to half making frequency changes 2x fast as before.\n\n## Two of three ##\n\nYes, there is a tittle catch here with CLK1 and CLK2: both share PLL_B and as we use our own algorithm to calculate the frequencies and minimize phase noise you can only use one of them at a time.\n\nNote: _In practice you can, but the other will move from the frequency you set, which is an unexpected behavior, so I made them mutually exclusive (CLK1 and CLK2)._\n\nThis are the valid combinations for independent clocks output.\n\n* CLK0 and CLK1\n* CLK0 and CLK2\n\nAgain: You can't use CLK1 and CLK2 at the same time, as soon as you set one of them the other will shut off. That's why you get two of three and one of them must be always CLK0.\n\n## Author \u0026 contributors ##\n\nThe main author is Pavel Milanes, CO7WT, a cuban amateur radio operator; reachable at pavelmc@gmail.com, Until now I have no contributors or sponsors.\n\nBut I have received the contributions stated below:\n\n* @birdwes:\n  - I2C busrt mode\n  - Found \u0026 fixed a bug about freq calculations.\n\n## Where to download the latest version? ##\n\nAlways download the latest version from the [github repository](https://github.com/pavelmc/Si5351mcu/)\n\nSee ChangeLog.md and Version files on this repository to know what are the latest changes and versions.\n\n## If you like to give thanks... ##\n\nNo payment of whatsoever is required to use this code: this is [Free/Libre Software](https://en.wikipedia.org/wiki/Software_Libre), nevertheless donations are very welcomed.\n\nI live in Cuba island and the Internet is very expensive here (USD $10.00 per 1GB of data), you can donate anonymously internet time or cell mobile data to me via [Ding Topups](https://www.ding.com/) to keep me connected and developing for the homebrew/DIY community.\n\nIf you like to do so, please go to Ding, select Cuba, select Cubacel and use my number `(+53) 538-478-19` (feel free to call me if you like)\n\nThanks!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpavelmc%2FSi5351mcu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpavelmc%2FSi5351mcu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpavelmc%2FSi5351mcu/lists"}