aboutsummaryrefslogtreecommitdiffstats
path: root/sw/eval-clock-cw-tx/src/si_clock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sw/eval-clock-cw-tx/src/si_clock.rs')
-rw-r--r--sw/eval-clock-cw-tx/src/si_clock.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/sw/eval-clock-cw-tx/src/si_clock.rs b/sw/eval-clock-cw-tx/src/si_clock.rs
new file mode 100644
index 0000000..a4b0e7a
--- /dev/null
+++ b/sw/eval-clock-cw-tx/src/si_clock.rs
@@ -0,0 +1,132 @@
+/*
+ The MIT License (MIT)
+
+ Copyright (c) 2020 Matthias P. Braendli
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+use core::convert::TryInto;
+
+use si5351::{Si5351, Si5351Device};
+use embedded_hal::blocking::i2c::{WriteRead, Write};
+
+const REF_CLOCK : u32 = 25_000_000;
+const PLL_A_MULT : u32 = 32;
+
+fn gcd(x: u32, y: u32) -> u32 {
+ let mut x = x;
+ let mut y = y;
+ while y != 0 {
+ let t = y;
+ y = x % y;
+ x = t;
+ }
+ x
+}
+
+fn clock_settings_for_pll(freq: u32, pll: u32) -> (u16, u32, u32) {
+ let a = pll / freq;
+ let b = pll - (a * freq);
+ let gcd = gcd(b, freq);
+
+ let b = b / gcd;
+ let c = freq / gcd;
+ (a.try_into().unwrap(), b, c)
+}
+
+fn clock_settings_with_pll_calculation(freq: u32) -> (u16, u8, u32, u32) {
+ let mut divider : u32 = 900_000_000 / freq; // Calculate the division ratio. 900,000,000 is the maximum internal
+
+ if (divider % 2) == 1 {
+ divider -= 1 // Ensure an even integer division ratio
+ }
+
+ let pll_freq = divider * freq;
+ // mult is an integer that must be in the range 15..90
+ let mult = pll_freq / REF_CLOCK;
+ let l = pll_freq % REF_CLOCK;
+
+ let denom = 1048575;
+ let num = f64::from(l) * f64::from(denom) / f64::from(REF_CLOCK);
+
+ (divider.try_into().unwrap(), mult.try_into().unwrap(), num as u32, denom)
+}
+
+fn set_bfo(siclock: &mut dyn Si5351, freq: u32) -> Result<(), si5351::Error>
+{
+ if freq == 0 {
+ siclock.set_clock_enabled(si5351::ClockOutput::Clk2, false);
+ }
+ else {
+ let (a, b, c) = clock_settings_for_pll(freq, PLL_A_MULT * REF_CLOCK);
+ siclock.setup_multisynth(si5351::Multisynth::MS2, a, b, c, si5351::OutputDivider::Div1)?;
+ siclock.select_clock_pll(si5351::ClockOutput::Clk2, si5351::PLL::A);
+ siclock.set_clock_enabled(si5351::ClockOutput::Clk2, true);
+ }
+ siclock.flush_clock_control(si5351::ClockOutput::Clk2)
+}
+
+fn set_vfo(siclock: &mut dyn Si5351, freq: u32)
+{
+ let (div, mult, num, denom) = clock_settings_with_pll_calculation(freq);
+
+ siclock.setup_pll(si5351::PLL::B, mult, num, denom).unwrap();
+ siclock.setup_multisynth_int(si5351::Multisynth::MS0, div, si5351::OutputDivider::Div1).unwrap();
+ siclock.select_clock_pll(si5351::ClockOutput::Clk0, si5351::PLL::B);
+ siclock.set_clock_enabled(si5351::ClockOutput::Clk0, true);
+ siclock.flush_clock_control(si5351::ClockOutput::Clk0).unwrap();
+}
+
+pub struct SiClock<I2C> {
+ siclock : Si5351Device<I2C>,
+}
+
+impl<I2C, E> SiClock<I2C>
+ where
+ I2C: WriteRead<Error = E> + Write<Error = E>,
+{
+ pub fn new(i2c: I2C, bfo: u32, vfo: u32) -> SiClock<I2C> {
+ let mut siclock = Si5351Device::new(i2c, false, REF_CLOCK);
+ siclock.init(si5351::CrystalLoad::_10).unwrap();
+
+ // See freqplan.py for Si5351 frequency plan
+ // CLK1 unused
+ siclock.setup_pll_int(si5351::PLL::A, 32).unwrap();
+
+ set_bfo(&mut siclock, bfo).unwrap();
+
+ siclock.reset_pll(si5351::PLL::A).unwrap();
+
+ set_vfo(&mut siclock, vfo);
+
+ siclock.reset_pll(si5351::PLL::B).unwrap();
+
+ siclock.flush_output_enabled().unwrap();
+
+ SiClock{siclock}
+ }
+
+ pub fn set_vfo(&mut self, freq: u32) {
+ set_vfo(&mut self.siclock, freq)
+ }
+
+ pub fn set_bfo(&mut self, freq: u32) -> Result<(), si5351::Error> {
+ set_bfo(&mut self.siclock, freq)
+ }
+}