diff options
Diffstat (limited to 'sw/dart-70/src/si_clock.rs')
-rw-r--r-- | sw/dart-70/src/si_clock.rs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/sw/dart-70/src/si_clock.rs b/sw/dart-70/src/si_clock.rs new file mode 100644 index 0000000..6de4c25 --- /dev/null +++ b/sw/dart-70/src/si_clock.rs @@ -0,0 +1,137 @@ +/* + 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) +{ + if freq == 0 { + siclock.set_clock_enabled(si5351::ClockOutput::Clk0, false); + } + else { + 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) + } +} |