Téléverser les fichiers vers "lib/Si5351mcu/src"
This commit is contained in:
parent
880c22ff50
commit
7cc911f734
394
lib/Si5351mcu/src/si5351mcu.cpp
Normal file
394
lib/Si5351mcu/src/si5351mcu.cpp
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
|
||||
*
|
||||
* Copyright (C) 2017 Pavel Milanes <pavelmc@gmail.com>
|
||||
*
|
||||
* Many chunk of codes are derived-from/copied from other libs
|
||||
* all GNU GPL licenced:
|
||||
* - Linux Kernel (www.kernel.org)
|
||||
* - Hans Summers libs and demo code (qrp-labs.com)
|
||||
* - Etherkit (NT7S) Si5351 libs on github
|
||||
* - DK7IH example.
|
||||
* - Jerry Gaffke integer routines for the bitx20 group
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "si5351mcu.h"
|
||||
|
||||
// wire library loading, if not defined
|
||||
#ifndef WIRE_H
|
||||
#include "Wire.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* This is the default init procedure, it set the Si5351 with this params:
|
||||
* XTAL 27.000 Mhz
|
||||
*****************************************************************************/
|
||||
void Si5351mcu::init(void) {
|
||||
// init with the default freq
|
||||
init(int_xtal);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* This is the custom init procedure, it's used to pass a custom xtal
|
||||
* and has the duty of init the I2C protocol handshake
|
||||
*****************************************************************************/
|
||||
void Si5351mcu::init(uint32_t nxtal) {
|
||||
// set the new base xtal freq
|
||||
base_xtal = int_xtal = nxtal;
|
||||
|
||||
// start I2C (wire) procedures
|
||||
Wire.begin();
|
||||
|
||||
// shut off the spread spectrum by default, DWaite contibuted code
|
||||
uint8_t regval;
|
||||
regval = i2cRead(149);
|
||||
regval &= ~0x80; // set bit 7 LOW to turn OFF spread spectrum mode
|
||||
i2cWrite(149, regval);
|
||||
|
||||
// power off all the outputs
|
||||
off();
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* This function set the freq of the corresponding clock.
|
||||
*
|
||||
* In my tests my Si5351 can work between 7,8 Khz and ~225 Mhz [~250 MHz with
|
||||
* overclocking] as usual YMMV
|
||||
*
|
||||
* Click noise:
|
||||
* - The lib has a reset programmed [aka: click noise] every time it needs to
|
||||
* change the output divider of a particular MSynth, if you move in big steps
|
||||
* this can lead to an increased rate of click noise per tunning step.
|
||||
* - If you move at a pace of a few Hz each time the output divider will
|
||||
* change at a low rate, hence less click noise per tunning step.
|
||||
* - The output divider moves [change] faster at high frequencies, so at HF the
|
||||
* clikc noise is at the real minimum possible.
|
||||
*
|
||||
* [See the README.md file for other details]
|
||||
****************************************************************************/
|
||||
void Si5351mcu::setFreq(uint8_t clk, uint32_t freq) {
|
||||
uint8_t a, R = 1, pll_stride = 0, msyn_stride = 0;
|
||||
uint32_t b, c, f, fvco, outdivider;
|
||||
uint32_t MSx_P1, MSNx_P1, MSNx_P2, MSNx_P3;
|
||||
|
||||
// Overclock option
|
||||
#ifdef SI_OVERCLOCK
|
||||
// user a overclock setting for the VCO, max value in my hardware
|
||||
// was 1.05 to 1.1 GHz, as usual YMMV [See README.md for details]
|
||||
outdivider = SI_OVERCLOCK / freq;
|
||||
#else
|
||||
// normal VCO from the datasheet and AN
|
||||
// With 900 MHz beeing the maximum internal PLL-Frequency
|
||||
outdivider = 900000000 / freq;
|
||||
#endif
|
||||
|
||||
// use additional Output divider ("R")
|
||||
while (outdivider > 900) {
|
||||
R = R * 2;
|
||||
outdivider = outdivider / 2;
|
||||
}
|
||||
|
||||
// finds the even divider which delivers the intended Frequency
|
||||
if (outdivider % 2) outdivider--;
|
||||
|
||||
// Calculate the PLL-Frequency (given the even divider)
|
||||
fvco = outdivider * R * freq;
|
||||
|
||||
// Convert the Output Divider to the bit-setting required in register 44
|
||||
switch (R) {
|
||||
case 1: R = 0; break;
|
||||
case 2: R = 16; break;
|
||||
case 4: R = 32; break;
|
||||
case 8: R = 48; break;
|
||||
case 16: R = 64; break;
|
||||
case 32: R = 80; break;
|
||||
case 64: R = 96; break;
|
||||
case 128: R = 112; break;
|
||||
}
|
||||
|
||||
// we have now the integer part of the output msynth
|
||||
// the b & c is fixed below
|
||||
|
||||
MSx_P1 = 128 * outdivider - 512;
|
||||
|
||||
// calc the a/b/c for the PLL Msynth
|
||||
/***************************************************************************
|
||||
* We will use integer only on the b/c relation, and will >> 5 (/32) both
|
||||
* to fit it on the 1048 k limit of C and keep the relation
|
||||
* the most accurate possible, this works fine with xtals from
|
||||
* 24 to 28 Mhz.
|
||||
*
|
||||
* This will give errors of about +/- 2 Hz maximum
|
||||
* as per my test and simulations in the worst case, well below the
|
||||
* XTAl ppm error...
|
||||
*
|
||||
* This will free more than 1K of the final eeprom
|
||||
*
|
||||
****************************************************************************/
|
||||
a = fvco / int_xtal;
|
||||
b = (fvco % int_xtal) >> 5; // Integer part of the fraction
|
||||
// scaled to match "c" limits
|
||||
c = int_xtal >> 5; // "c" scaled to match it's limits
|
||||
// in the register
|
||||
|
||||
// f is (128*b)/c to mimic the Floor(128*(b/c)) from the datasheet
|
||||
f = (128 * b) / c;
|
||||
|
||||
// build the registers to write
|
||||
MSNx_P1 = 128 * a + f - 512;
|
||||
MSNx_P2 = 128 * b - f * c;
|
||||
MSNx_P3 = c;
|
||||
|
||||
// PLLs and CLK# registers are allocated with a stride, we handle that with
|
||||
// the stride var to make code smaller
|
||||
if (clk > 0 ) pll_stride = 8;
|
||||
|
||||
// HEX makes it easier to human read on bit shifts
|
||||
uint8_t reg_bank_26[] = {
|
||||
(MSNx_P3 & 0xFF00) >> 8, // Bits [15:8] of MSNx_P3 in register 26
|
||||
MSNx_P3 & 0xFF,
|
||||
(MSNx_P1 & 0x030000L) >> 16,
|
||||
(MSNx_P1 & 0xFF00) >> 8, // Bits [15:8] of MSNx_P1 in register 29
|
||||
MSNx_P1 & 0xFF, // Bits [7:0] of MSNx_P1 in register 30
|
||||
((MSNx_P3 & 0x0F0000L) >> 12) | ((MSNx_P2 & 0x0F0000) >> 16), // Parts of MSNx_P3 and MSNx_P1
|
||||
(MSNx_P2 & 0xFF00) >> 8, // Bits [15:8] of MSNx_P2 in register 32
|
||||
MSNx_P2 & 0xFF // Bits [7:0] of MSNx_P2 in register 33
|
||||
};
|
||||
|
||||
// We could do this here - but move it next to the reg_bank_42 write
|
||||
// i2cWriteBurst(26 + pll_stride, reg_bank_26, sizeof(reg_bank_26));
|
||||
|
||||
// Write the output divider msynth only if we need to, in this way we can
|
||||
// speed up the frequency changes almost by half the time most of the time
|
||||
// and the main goal is to avoid the nasty click noise on freq change
|
||||
if (omsynth[clk] != outdivider || o_Rdiv[clk] != R ) {
|
||||
|
||||
// CLK# registers are exactly 8 * clk# bytes stride from a base register.
|
||||
msyn_stride = clk * 8;
|
||||
|
||||
// keep track of the change
|
||||
omsynth[clk] = (uint16_t) outdivider;
|
||||
o_Rdiv[clk] = R; // cache it now, before we OR mask up R for special divide by 4
|
||||
|
||||
// See datasheet, special trick when MSx == 4
|
||||
// MSx_P1 is always 0 if outdivider == 4, from the above equations, so there is
|
||||
// no need to set it to 0. ... MSx_P1 = 128 * outdivider - 512;
|
||||
//
|
||||
// See para 4.1.3 on the datasheet.
|
||||
//
|
||||
|
||||
if ( outdivider == 4 ) {
|
||||
R |= 0x0C; // bit set OR mask for MSYNTH divide by 4, for reg 44 {3:2]
|
||||
}
|
||||
|
||||
// HEX makes it easier to human read on bit shifts
|
||||
uint8_t reg_bank_42[] = {
|
||||
0, // Bits [15:8] of MS0_P3 (always 0) in register 42
|
||||
1, // Bits [7:0] of MS0_P3 (always 1) in register 43
|
||||
((MSx_P1 & 0x030000L ) >> 16) | R, // Bits [17:16] of MSx_P1 in bits [1:0] and R in [7:4] | [3:2]
|
||||
(MSx_P1 & 0xFF00) >> 8, // Bits [15:8] of MSx_P1 in register 45
|
||||
MSx_P1 & 0xFF, // Bits [7:0] of MSx_P1 in register 46
|
||||
0, // Bits [19:16] of MS0_P2 and MS0_P3 are always 0
|
||||
0, // Bits [15:8] of MS0_P2 are always 0
|
||||
0 // Bits [7:0] of MS0_P2 are always 0
|
||||
};
|
||||
|
||||
// Get the two write bursts as close together as possible,
|
||||
// to attempt to reduce any more click glitches. This is
|
||||
// at the expense of only 24 increased bytes compilation size in AVR 328.
|
||||
// Everything is already precalculated above, reducing any delay,
|
||||
// by not doing calculations between the burst writes.
|
||||
|
||||
i2cWriteBurst(26 + pll_stride, reg_bank_26, sizeof(reg_bank_26));
|
||||
i2cWriteBurst(42 + msyn_stride, reg_bank_42, sizeof(reg_bank_42));
|
||||
|
||||
//
|
||||
// https://www.silabs.com/documents/public/application-notes/Si5350-Si5351%20FAQ.pdf
|
||||
//
|
||||
// 11.1 "The Int, R and N register values inside the Interpolative Dividers are updated
|
||||
// when the LSB of R is written via I2C." - Q. does this mean reg 44 or 49 (misprint ?) ???
|
||||
//
|
||||
// 10.1 "All outputs are within +/- 500ps of one another at power up (if pre-programmed)
|
||||
// or if PLLA and PLLB are reset simultaneously via register 177."
|
||||
//
|
||||
// 17.1 "The PLL can track any abrupt input frequency changes of 3–4% without losing
|
||||
// lock to it. Any input frequency changes greater than this amount will not
|
||||
// necessarily track from the input to the output
|
||||
|
||||
// must reset the so called "PLL", in fact the output msynth
|
||||
reset();
|
||||
|
||||
}
|
||||
else {
|
||||
i2cWriteBurst(26 + pll_stride, reg_bank_26, sizeof(reg_bank_26));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Reset of the PLLs and multisynths output enable
|
||||
*
|
||||
* This must be called to soft reset the PLLs and cycle the output of the
|
||||
* multisynths: this is the "click" noise source in the RF spectrum.
|
||||
*
|
||||
* So it must be avoided at all costs, so this lib just call it at the
|
||||
* initialization of the PLLs and when a correction is applied
|
||||
*
|
||||
* If you are concerned with accuracy you can implement a reset every
|
||||
* other Mhz to be sure it get exactly on spot.
|
||||
****************************************************************************/
|
||||
void Si5351mcu::reset(void) {
|
||||
// This soft-resets PLL A & B (32 + 128) in just one step
|
||||
i2cWrite(177, 0xA0);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Function to disable all outputs
|
||||
*
|
||||
* The PLL are kept running, just the m-synths are powered off.
|
||||
*
|
||||
* This allows to keep the chip warm and exactly on freq the next time you
|
||||
* enable an output.
|
||||
****************************************************************************/
|
||||
void Si5351mcu::off(void) {
|
||||
// This disable all the CLK outputs
|
||||
for (byte i=0; i < SICHANNELS; i++) {
|
||||
disable(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Function to set the correction in Hz over the Si5351 XTAL.
|
||||
*
|
||||
* This will call a reset of the PLLs and multi-synths so it will produce a
|
||||
* click every time it's called
|
||||
****************************************************************************/
|
||||
void Si5351mcu::correction(int32_t diff) {
|
||||
// apply some corrections to the xtal
|
||||
int_xtal = base_xtal + diff;
|
||||
|
||||
// reset the PLLs to apply the correction
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* This function enables the selected output
|
||||
*
|
||||
* Beware: ZERO is clock output enabled, in register 16+CLK
|
||||
*****************************************************************************/
|
||||
void Si5351mcu::enable(uint8_t clk) {
|
||||
// var to handle the mask of the registers value
|
||||
uint8_t m = SICLK0_R;
|
||||
|
||||
if (clk > 0) {
|
||||
m = SICLK12_R;
|
||||
}
|
||||
|
||||
// write the register value
|
||||
i2cWrite(16 + clk, m + clkpower[clk]);
|
||||
|
||||
// 1 & 2 are mutually exclusive
|
||||
if (clk == 1) disable(2);
|
||||
if (clk == 2) disable(1);
|
||||
|
||||
// update the status of the clk
|
||||
clkOn[clk] = 1;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* This function disables the selected output
|
||||
*
|
||||
* Beware: ONE is clock output disabled, in register 16+CLK
|
||||
* *****************************************************************************/
|
||||
void Si5351mcu::disable(uint8_t clk) {
|
||||
// send
|
||||
i2cWrite(16 + clk, 0x80);
|
||||
|
||||
// update the status of the clk
|
||||
clkOn[clk] = 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Set the power output for each output independently
|
||||
***************************************************************************/
|
||||
void Si5351mcu::setPower(uint8_t clk, uint8_t power) {
|
||||
// set the power to the correct var
|
||||
clkpower[clk] = power;
|
||||
|
||||
// now enable the output to get it applied
|
||||
enable(clk);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* method to send multi-byte burst register data.
|
||||
***************************************************************************/
|
||||
uint8_t Si5351mcu::i2cWriteBurst( const uint8_t start_register,
|
||||
const uint8_t *data,
|
||||
const uint8_t numbytes) {
|
||||
|
||||
// This method saves the massive overhead of having to keep opening
|
||||
// and closing the I2C bus for consecutive register writes. It
|
||||
// also saves numbytes - 1 writes for register address selection.
|
||||
Wire.beginTransmission(SIADDR);
|
||||
|
||||
Wire.write(start_register);
|
||||
Wire.write(data, numbytes);
|
||||
// All of the bytes queued up in the above write() calls are buffered
|
||||
// up and will be sent to the slave in one "burst", on the call to
|
||||
// endTransmission(). This also sends the I2C STOP to the Slave.
|
||||
return Wire.endTransmission();
|
||||
// returns non zero on error
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* function to send the register data to the Si5351, arduino way.
|
||||
***************************************************************************/
|
||||
void Si5351mcu::i2cWrite( const uint8_t regist, const uint8_t value) {
|
||||
// Using the "burst" method instead of
|
||||
// doing it longhand saves a few bytes
|
||||
i2cWriteBurst( regist, &value, 1);
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Read i2C register, returns -1 on error or timeout
|
||||
***************************************************************************/
|
||||
int16_t Si5351mcu::i2cRead( const uint8_t regist ) {
|
||||
int value;
|
||||
|
||||
Wire.beginTransmission(SIADDR);
|
||||
Wire.write(regist);
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.requestFrom(SIADDR, 1);
|
||||
if ( Wire.available() ) {
|
||||
value = Wire.read();
|
||||
}
|
||||
else {
|
||||
value = -1; // "EOF" in C
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
166
lib/Si5351mcu/src/si5351mcu.h
Normal file
166
lib/Si5351mcu/src/si5351mcu.h
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
|
||||
*
|
||||
* Copyright (C) 2017 Pavel Milanes <pavelmc@gmail.com>
|
||||
*
|
||||
* Many chunk of codes are derived-from/copied from other libs
|
||||
* all GNU GPL licenced:
|
||||
* - Linux Kernel (www.kernel.org)
|
||||
* - Hans Summers libs and demo code (qrp-labs.com)
|
||||
* - Etherkit (NT7S) Si5351 libs on github
|
||||
* - DK7IH example.
|
||||
* - Jerry Gaffke integer routines for the bitx20 group
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* This lib tricks:
|
||||
*
|
||||
* CLK0 will use PLLA
|
||||
* CLK1 will use PLLB
|
||||
* CLK2 will use PLLB
|
||||
*
|
||||
* Lib defaults
|
||||
* - XTAL is 27 Mhz.
|
||||
* - Always put the internal 8pF across the xtal legs to GND
|
||||
* - lowest power output (2mA)
|
||||
* - After the init all outputs are off, you need to enable them in your code.
|
||||
*
|
||||
* The correction procedure is not for the PPM as other libs, this
|
||||
* is just +/- Hz to the XTAL freq, you may get a click noise after
|
||||
* applying a correction
|
||||
*
|
||||
* The init procedure is mandatory as it set the Xtal (the default or a custom
|
||||
* one) and prepare the Wire (I2C) library for operation.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SI5351MCU_H
|
||||
#define SI5351MCU_H
|
||||
|
||||
// rigor includes
|
||||
#include "Arduino.h"
|
||||
#include "Wire.h"
|
||||
|
||||
// default I2C address of the Si5351A - other variants may differ
|
||||
#define SIADDR 0x60
|
||||
|
||||
// The number of output channels - 3 for Si5351A 10 pin
|
||||
#define SICHANNELS 3
|
||||
|
||||
// register's power modifiers
|
||||
#define SIOUT_2mA 0
|
||||
#define SIOUT_4mA 1
|
||||
#define SIOUT_6mA 2
|
||||
#define SIOUT_8mA 3
|
||||
|
||||
// registers base (2mA by default)
|
||||
#define SICLK0_R 76 // 0b01001100
|
||||
#define SICLK12_R 108 // 0b01101100
|
||||
|
||||
class Si5351mcu {
|
||||
private:
|
||||
// base xtal freq, over this we apply the correction factor
|
||||
// by default 27 MHz
|
||||
uint32_t base_xtal = 27000000L;
|
||||
|
||||
// this is the work value, with the correction applied
|
||||
// via the correction() procedure
|
||||
uint32_t int_xtal = base_xtal;
|
||||
|
||||
// clk# power holders (2ma by default)
|
||||
uint8_t clkpower[SICHANNELS] = { 0 };
|
||||
|
||||
// local var to keep track of when to reset the "pll"
|
||||
/*********************************************************
|
||||
* BAD CONCEPT on the datasheet and AN:
|
||||
*
|
||||
* The chip has a soft-reset for PLL A & B but in
|
||||
* practice the PLL does not need to be reseted.
|
||||
*
|
||||
* Test shows that if you fix the Msynth output
|
||||
* dividers and move any of the VCO from bottom to top
|
||||
* the frequency moves smooth and clean, no reset needed
|
||||
*
|
||||
* The reset is needed when you changes the value of the
|
||||
* Msynth output divider, even so it's not always needed
|
||||
* so we use this var to keep track of all three and only
|
||||
* reset the "PLL" when this value changes to be sure
|
||||
*
|
||||
* It's a word (16 bit) because the final max value is 900
|
||||
*********************************************************/
|
||||
uint16_t omsynth[SICHANNELS] = { 0 };
|
||||
uint8_t o_Rdiv[SICHANNELS] = { 0 };
|
||||
|
||||
public:
|
||||
// var to check the clock state
|
||||
bool clkOn[SICHANNELS] = { 0 }; // This should not really be public - use isEnabled()
|
||||
|
||||
public:
|
||||
// default init procedure
|
||||
void init(void);
|
||||
|
||||
// custom init procedure (XTAL in Hz);
|
||||
void init(uint32_t);
|
||||
|
||||
// reset all PLLs
|
||||
static void reset(void);
|
||||
|
||||
// set CLKx(0..2) to freq (Hz)
|
||||
void setFreq(uint8_t, uint32_t);
|
||||
|
||||
// pass a correction factor
|
||||
void correction(int32_t);
|
||||
|
||||
// enable some CLKx output
|
||||
void enable(uint8_t);
|
||||
|
||||
// disable some CLKx output
|
||||
void disable(uint8_t);
|
||||
|
||||
// disable all outputs
|
||||
void off(void);
|
||||
|
||||
// set power output to a specific clk
|
||||
void setPower(uint8_t, uint8_t);
|
||||
|
||||
// used to talk with the chip, via Arduino Wire lib
|
||||
//
|
||||
// declared as static, since they do not reference any this-> class attributes
|
||||
//
|
||||
static void i2cWrite( const uint8_t reg, const uint8_t val );
|
||||
static uint8_t i2cWriteBurst( const uint8_t start_register, const uint8_t *data, const uint8_t numbytes );
|
||||
static int16_t i2cRead( const uint8_t reg );
|
||||
|
||||
inline const bool isEnabled( const uint8_t channel ) {
|
||||
return channel < SICHANNELS && clkOn[ channel ] != 0;
|
||||
};
|
||||
|
||||
inline const uint8_t getPower( const uint8_t channel ) {
|
||||
return channel < SICHANNELS ? clkpower[ channel ] : 0;
|
||||
};
|
||||
|
||||
inline const uint32_t getXtalBase( void ) {
|
||||
return base_xtal;
|
||||
};
|
||||
|
||||
inline const uint32_t getXtalCurrent( void ) {
|
||||
return int_xtal;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //SI5351MCU_H
|
||||
Loading…
Reference in New Issue
Block a user