Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,29 @@ The standard I2C bus address for the Si5351 is 0x60, however there are other ICs

Si5351 si5351(0x61);

Selecting an I2C Bus
--------------------
By default the library uses the standard _Wire_ bus. On boards with more than one I2C peripheral (ESP32, RP2040, SAMD, Teensy, STM32 and others), pass a pointer to the _TwoWire_ instance the Si5351 is connected to, optionally followed by the bus address:

#include "Wire.h"
#include "si5351.h"

Si5351 si5351(&Wire1); // second I2C bus, address 0x60
// Si5351 si5351(&Wire1, 0x61); // second I2C bus, alternate address

void setup()
{
#if defined(ARDUINO_ARCH_ESP32)
Wire1.setPins(13, 15); // ESP32 only: choose SDA/SCL before init()
#endif
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Wire1.setClock(400000); // optional: set the bus speed after init()
}

A _TwoWire_ object you create yourself (for example `TwoWire wire1(1);` on ESP32) works the same way: `Si5351 si5351(&wire1);`.

_init()_ calls _begin()_ on the selected bus with no arguments, as earlier versions did on _Wire_. That keeps pins set beforehand with _setPins()_ (ESP32) or _setSDA()/setSCL()_ (RP2040). If your board needs pins passed to _begin()_ itself, call _begin(...)_ on the bus yourself before _init()_. On ESP32 a bus that is already started is left as it is; the core logs the warning "Bus already started in Master Mode", which is harmless and the same as with earlier versions of this library. On most other cores (AVR, SAMD, RP2040, STM32 and others) _begin()_ runs again inside _init()_ and may reset a bus speed chosen earlier with _setClock()_, so call _setClock()_ after _init()_. To get the default _Wire_ bus through the bus constructor, pass `nullptr` or `(TwoWire *)0`; a plain `NULL` or `0` is treated as I2C address 0, as in earlier versions. The existing address-only constructor, `Si5351 si5351(0x61);`, still selects _Wire_.

Startup Conditions
------------------
This library initializes the Si5351 parameters to the following values upon startup and on reset:
Expand Down Expand Up @@ -711,6 +734,13 @@ This library does not currently support the spread spectrum function of the Si53
Changelog
---------

* v2.4.0

* Add I2C bus selection: `Si5351(TwoWire *wire = &Wire, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR)` so the Si5351 can be on Wire1 or any other TwoWire bus (closes #103, #106). `Si5351()` and `Si5351(addr)` (any integer type, including a literal 0 or NULL) behave as before. No new C++11 features are required
* Passing `nullptr` or `(TwoWire *)0` to the bus constructor selects `Wire`; `NULL` or `0` still mean I2C address 0
* Cost on the Uno basic example: +138 B flash, +2 B RAM
* Fix compile error in the si5351_vcxo example (`get_correction()` now called with `SI5351_PLL_INPUT_XO`)

* v2.3.0

* Reduce flash usage by roughly 0.6 to 3 KB on AVR (depending on which features a sketch uses) with no change to the public API
Expand Down
2 changes: 1 addition & 1 deletion examples/si5351_vcxo/si5351_vcxo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void loop()
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print("corr: ");
Serial.print(si5351.get_correction());
Serial.print(si5351.get_correction(SI5351_PLL_INPUT_XO));
Serial.print(" SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"maintainer": true
}
],
"version": "2.3.0",
"version": "2.4.0",
"frameworks": "arduino",
"platforms": "*"
}
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=Etherkit Si5351
version=2.3.0
version=2.4.0
author=Jason Milldrum <milldrum@gmail.com>
maintainer=Jason Milldrum <milldrum@gmail.com>
sentence=A full-featured library for the Si5351 series of clock generator ICs from Silicon Labs
Expand Down
49 changes: 29 additions & 20 deletions src/si5351.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,16 @@ static void frac_to_reg(uint32_t a, uint32_t b, uint32_t c, struct Si5351RegSet
/* Public functions */
/********************/

Si5351::Si5351(uint8_t i2c_addr):
i2c_bus_addr(i2c_addr)
Si5351::Si5351(TwoWire *wire, uint8_t i2c_addr)
{
setup_obj(wire, i2c_addr);
}

// Shared constructor body (no C++11 delegating constructor needed)
void Si5351::setup_obj(TwoWire *wire, uint8_t i2c_addr)
{
i2c_bus_addr = i2c_addr;
_wire = wire ? wire : &Wire;
xtal_freq[0] = SI5351_XTAL_FREQ;

// Start by using XO ref osc as default for each PLL
Expand All @@ -96,13 +103,15 @@ Si5351::Si5351(uint8_t i2c_addr):
*/
bool Si5351::init(uint8_t xtal_load_c, uint32_t xo_freq, int32_t corr)
{
// Start I2C comms
Wire.begin();
// Start I2C comms on the selected bus. Called with no arguments so that
// pins chosen beforehand (e.g. ESP32 setPins()) are kept; on ESP32 a bus
// already started by the sketch with begin(sda, scl, freq) is left as is.
_wire->begin();

// Check for a device on the bus, bail out if it is not there
Wire.beginTransmission(i2c_bus_addr);
_wire->beginTransmission(i2c_bus_addr);
uint8_t reg_val;
reg_val = Wire.endTransmission();
reg_val = _wire->endTransmission();

if(reg_val == 0)
{
Expand Down Expand Up @@ -1200,37 +1209,37 @@ void Si5351::set_ref_freq(uint32_t ref_freq, enum si5351_pll_input ref_osc)

uint8_t Si5351::si5351_write_bulk(uint8_t addr, uint8_t bytes, uint8_t *data)
{
Wire.beginTransmission(i2c_bus_addr);
Wire.write(addr);
_wire->beginTransmission(i2c_bus_addr);
_wire->write(addr);
for(int i = 0; i < bytes; i++)
{
Wire.write(data[i]);
_wire->write(data[i]);
}
return Wire.endTransmission();
return _wire->endTransmission();

}

uint8_t Si5351::si5351_write(uint8_t addr, uint8_t data)
{
Wire.beginTransmission(i2c_bus_addr);
Wire.write(addr);
Wire.write(data);
return Wire.endTransmission();
_wire->beginTransmission(i2c_bus_addr);
_wire->write(addr);
_wire->write(data);
return _wire->endTransmission();
}

uint8_t Si5351::si5351_read(uint8_t addr)
{
uint8_t reg_val = 0;

Wire.beginTransmission(i2c_bus_addr);
Wire.write(addr);
Wire.endTransmission();
_wire->beginTransmission(i2c_bus_addr);
_wire->write(addr);
_wire->endTransmission();

Wire.requestFrom(i2c_bus_addr, (uint8_t)1);
_wire->requestFrom(i2c_bus_addr, (uint8_t)1);

while(Wire.available())
while(_wire->available())
{
reg_val = Wire.read();
reg_val = _wire->read();
}

return reg_val;
Expand Down
17 changes: 16 additions & 1 deletion src/si5351.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,20 @@ struct Si5351IntStatus
class Si5351
{
public:
Si5351(uint8_t i2c_addr = SI5351_BUS_BASE_ADDR);
// Select the I2C bus (TwoWire instance) and address, e.g. Si5351 si5351(&Wire1);
// A null bus pointer falls back to Wire.
Si5351(TwoWire *wire = &Wire, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR);
// Address only, on the default Wire bus (kept for existing sketches).
// One overload per integer type so that any integer argument, including a
// literal 0 or NULL, selects the address path exactly as before instead of
// being ambiguous with the TwoWire * constructor.
Si5351(uint8_t i2c_addr) { setup_obj(&Wire, i2c_addr); }
Si5351(int i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
Si5351(unsigned int i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
Si5351(long i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
Si5351(unsigned long i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
Si5351(long long i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
Si5351(unsigned long long i2c_addr) { setup_obj(&Wire, (uint8_t)i2c_addr); }
bool init(uint8_t, uint32_t, int32_t);
void reset(void);
uint8_t set_freq(uint64_t, enum si5351_clock);
Expand Down Expand Up @@ -318,6 +331,7 @@ class Si5351
enum si5351_pll_input pllb_ref_osc;
uint32_t xtal_freq[2];
private:
void setup_obj(TwoWire *, uint8_t);
uint64_t pll_calc(enum si5351_pll, uint64_t, struct Si5351RegSet *, int32_t, uint8_t);
uint64_t multisynth_calc(uint64_t, uint64_t, struct Si5351RegSet *);
uint64_t multisynth67_calc(uint64_t, uint64_t, struct Si5351RegSet *);
Expand All @@ -328,6 +342,7 @@ class Si5351
int32_t ref_correction[2];
uint8_t clkin_div;
uint8_t i2c_bus_addr;
TwoWire *_wire;
bool clk_first_set[8];
};

Expand Down
Loading