Compare commits

...

14 Commits

Author SHA1 Message Date
0d639adc45 semi working quadrature output 2023-09-27 23:15:31 -04:00
46d6bf1001 Added manual phase offset 2023-09-26 19:46:06 -04:00
208c5709d6 Initial Fork cleanup 2023-09-26 18:57:06 -04:00
a489493a66 Initial fork 2023-09-26 18:47:07 -04:00
Ilya Epifanov
e509c36df9 cargo fix --edition-idioms 2019-01-20 23:14:24 +01:00
Ilya Epifanov
d431678011 2018 edition 2019-01-20 23:13:40 +01:00
Ilya Epifanov
bf425b8b6c taking an i2c bus by value 2019-01-20 23:11:13 +01:00
Ilya Epifanov
ec33c2de17 0.1.5 release 2018-03-19 13:39:26 +01:00
Ilya Epifanov
ba20217b63 updated docs 2018-03-19 13:39:11 +01:00
Ilya Epifanov
6d56a07fd4 fixed cargo categories 2018-03-19 13:22:41 +01:00
Ilya Epifanov
771fe86bd9 added links to docs.rs, crates.io and ci to the readme 2018-03-19 13:12:21 +01:00
Ilya Epifanov
fd5a6e6373 0.1.4 release 2018-03-19 12:59:45 +01:00
Ilya Epifanov
8d7eee8842 added docs 2018-03-19 12:08:14 +01:00
Ilya Epifanov
429a530b65 added copyright notice and MIT license as a fallback 2018-03-02 21:07:49 +01:00
9 changed files with 222 additions and 116 deletions

View File

@@ -1,54 +0,0 @@
# Based on the "trust" template v0.1.2
# https://github.com/japaric/trust/tree/v0.1.2
language: rust
services: docker
matrix:
include:
# Linux
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
# Bare metal
- env: TARGET=thumbv6m-none-eabi
rust: nightly
- env: TARGET=thumbv7m-none-eabi
rust: nightly
- env: TARGET=thumbv7em-none-eabi
rust: nightly
- env: TARGET=thumbv7em-none-eabihf
rust: nightly
before_install:
- set -e
- rustup self update
install:
- sh ci/install.sh
- source ~/.cargo/env || true
script:
- bash ci/script.sh
after_script: set +e
cache:
cargo: true
directories:
- $HOME/.xargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
branches:
only:
- /^v\d+\.\d+\.\d+.*$/
- auto
- master
- try
notifications:
email:
on_success: never

View File

@@ -1,11 +1,19 @@
[package] [package]
name = "si5351" name = "si5351"
version = "0.1.2" version = "0.2.0"
categories = ["embedded-hal-driver", "ham-radio", "clock", "pll" ] keywords = ["embedded", "no-std", "embedded-hal-driver", "clock", "ham-radio"]
categories = ["embedded", "no-std"]
authors = ["Ilya Epifanov <elijah.epifanov@gmail.com>"] authors = ["Ilya Epifanov <elijah.epifanov@gmail.com>"]
description = "A platform agnostic driver for the Si5351 clock generator" description = "A platform agnostic driver for the Si5351 clock generator"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
exclude = [".travis.yml", ".gitignore"]
repository = "https://github.com/ilya-epifanov/si5351"
edition = "2018"
[badges.travis-ci]
branch = "master"
repository = "jamwaffles/ssd1306"
[dependencies] [dependencies]
embedded-hal = "0.1" embedded-hal = "0.2.2"
bitflags = "1.0" bitflags = "1.0"

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright [yyyy] [name of copyright owner] Copyright 2018 Ilya Epifanov
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

25
LICENSE-MIT Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2018 Ilya Epifanov
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.

View File

@@ -1 +1,18 @@
# si5351 # si5351
Fork of github.com/ilya-epifanov/si5351
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
* MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.

View File

@@ -1,7 +0,0 @@
[dependencies.core]
stage = 0
[dependencies.compiler_builtins]
git = "https://github.com/rust-lang-nursery/compiler-builtins"
features = ["mem"]
stage = 1

View File

@@ -1,21 +0,0 @@
set -ex
main() {
# This fetches latest stable release of Xargo
local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/xargo \
| cut -d/ -f3 \
| grep -E '^v[0.1.0-9.]+$' \
| sort --version-sort \
| tail -n1)
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- \
--force \
--git japaric/xargo \
--tag $tag \
--target x86_64-unknown-linux-musl
rustup component add rust-src
}
main

View File

@@ -1,16 +0,0 @@
# This script takes care of testing your crate
set -euxo pipefail
main() {
case $TARGET in
x86_64-unknown-linux-gnu)
cargo check --target $TARGET
;;
*)
xargo check --target $TARGET
;;
esac
}
main

View File

@@ -1,14 +1,91 @@
/*
Copyright 2018 Ilya Epifanov
Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
http://opensource.org/licenses/MIT>, at your option. This file may not be
copied, modified, or distributed except according to those terms.
*/
/*!
A platform agnostic Rust driver for the [Si5351], based on the
[`embedded-hal`] traits.
## The Device
The Silicon Labs [Si5351] is an any-frequency CMOS clock generator.
The device has an I²C interface.
## Usage
Import this crate and an `embedded_hal` implementation:
```
extern crate stm32f103xx_hal as hal;
extern crate si5351;
```
Initialize I²C bus (differs between `embedded_hal` implementations):
```no_run
# extern crate stm32f103xx_hal as hal;
use hal::i2c::I2c;
type I2C = ...;
# fn main() {
let i2c: I2C = initialize_i2c();
# }
```
Then instantiate the device:
```no_run
# extern crate stm32f103xx_hal as hal;
# extern crate si5351;
use si5351;
use si5351::{Si5351, Si5351Device};
# fn main() {
let mut clock = Si5351Device<I2C>::new(i2c, false, 25_000_000);
clock.init(si5351::CrystalLoad::_10)?;
# }
```
Or, if you have an [Adafruit module], you can use shortcut functions to initializate it:
```no_run
# extern crate stm32f103xx_hal as hal;
# extern crate si5351;
use si5351;
use si5351::{Si5351, Si5351Device};
# fn main() {
let mut clock = Si5351Device<I2C>::new_adafruit_module(i2c);
clock.init_adafruit_module()?;
# }
```
And set frequency on one of the outputs:
```no_run
use si5351;
clock.set_frequency(si5351::PLL::A, si5351::ClockOutput::Clk0, 14_175_000)?;
```
[Si5351]: https://www.silabs.com/documents/public/data-sheets/Si5351-B.pdf
[`embedded-hal`]: https://github.com/japaric/embedded-hal
[Adafruit module]: https://www.adafruit.com/product/2045
*/
//#![deny(missing_docs)] //#![deny(missing_docs)]
#![deny(warnings)] #![deny(warnings)]
#![feature(unsize)]
#![no_std] #![no_std]
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
extern crate embedded_hal as hal; use embedded_hal as hal;
use core::mem; //use core::mem;
use hal::blocking::i2c::{Write, WriteRead}; use crate::hal::blocking::i2c::{Write, WriteRead};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@@ -150,6 +227,14 @@ enum Register {
Clk5 = 21, Clk5 = 21,
Clk6 = 22, Clk6 = 22,
Clk7 = 23, Clk7 = 23,
Clk0Phoff = 165,
Clk1Phoff,
Clk2Phoff,
Clk3Phoff,
Clk4Phoff,
Clk5Phoff,
Clk6Phoff, //Not in datasheet
Clk7Phoff, //Not in datasheet
PLLReset = 177, PLLReset = 177,
CrystalLoad = 183, CrystalLoad = 183,
} }
@@ -262,16 +347,17 @@ fn i2c_error<E>(_: E) -> Error {
} }
/// Si5351 driver /// Si5351 driver
pub struct Si5351Device<'a, I2C: 'a> { pub struct Si5351Device<I2C> {
i2c: &'a mut I2C, i2c: I2C,
address: u8, address: u8,
xtal_freq: u32, xtal_freq: u32,
clk_enabled_mask: u8, clk_enabled_mask: u8,
ms_int_mode_mask: u8, ms_int_mode_mask: u8,
ms_src_mask: u8, ms_src_mask: u8,
last_mdiv: u8,
} }
pub trait Si5351<'a> { pub trait Si5351 {
fn init_adafruit_module(&mut self) -> Result<(), Error>; fn init_adafruit_module(&mut self) -> Result<(), Error>;
fn init(&mut self, xtal_load: CrystalLoad) -> Result<(), Error>; fn init(&mut self, xtal_load: CrystalLoad) -> Result<(), Error>;
fn read_device_status(&mut self) -> Result<DeviceStatusBits, Error>; fn read_device_status(&mut self) -> Result<DeviceStatusBits, Error>;
@@ -290,14 +376,22 @@ pub trait Si5351<'a> {
fn setup_multisynth_int(&mut self, ms: Multisynth, mult: u16, r_div: OutputDivider) -> Result<(), Error>; fn setup_multisynth_int(&mut self, ms: Multisynth, mult: u16, r_div: OutputDivider) -> Result<(), Error>;
fn setup_multisynth(&mut self, ms: Multisynth, div: u16, num: u32, denom: u32, r_div: OutputDivider) -> Result<(), Error>; fn setup_multisynth(&mut self, ms: Multisynth, div: u16, num: u32, denom: u32, r_div: OutputDivider) -> Result<(), Error>;
fn select_clock_pll(&mut self, clocl: ClockOutput, pll: PLL); fn select_clock_pll(&mut self, clocl: ClockOutput, pll: PLL);
fn set_quad(&mut self, freq: u32) -> Result<f32, Error>;
fn set_phase_offset(&mut self, clk: ClockOutput, phase_offset: u8) -> Result<(), Error>;
fn reset_pll(&mut self, pll: PLL) -> Result<(), Error>;
} }
impl<'a, I2C, E> Si5351Device<'a, I2C> impl<T> Si5351Device<T> {
pub fn get_xtal_freq(&self) -> u32 {self.xtal_freq}
}
impl<I2C, E> Si5351Device<I2C>
where where
I2C: WriteRead<Error=E> + Write<Error=E>, I2C: WriteRead<Error=E> + Write<Error=E>,
{ {
/// Creates a new driver from a I2C peripheral /// Creates a new driver from a I2C peripheral
pub fn new(i2c: &'a mut I2C, address_bit: bool, xtal_freq: u32) -> Self { pub fn new(i2c: I2C, address_bit: bool, xtal_freq: u32) -> Self {
let si5351 = Si5351Device { let si5351 = Si5351Device {
i2c, i2c,
address: ADDRESS | if address_bit { 1 } else { 0 }, address: ADDRESS | if address_bit { 1 } else { 0 },
@@ -305,12 +399,14 @@ impl<'a, I2C, E> Si5351Device<'a, I2C>
clk_enabled_mask: 0, clk_enabled_mask: 0,
ms_int_mode_mask: 0, ms_int_mode_mask: 0,
ms_src_mask: 0, ms_src_mask: 0,
last_mdiv: 0,
}; };
si5351 si5351
} }
pub fn new_adafruit_module(i2c: &'a mut I2C) -> Self {
pub fn new_adafruit_module(i2c: I2C) -> Self {
Si5351Device::new(i2c, false, 25_000_000) Si5351Device::new(i2c, false, 25_000_000)
} }
@@ -361,7 +457,7 @@ impl<'a, I2C, E> Si5351Device<'a, I2C>
Ok(()) Ok(())
} }
fn reset_pll(&mut self, pll: PLL) -> Result<(), Error> { pub fn reset_pll(&mut self, pll: PLL) -> Result<(), Error> {
self.write_register(Register::PLLReset, match pll { self.write_register(Register::PLLReset, match pll {
PLL::A => PLLResetBits::PLLA_RST.bits(), PLL::A => PLLResetBits::PLLA_RST.bits(),
PLL::B => PLLResetBits::PLLB_RST.bits(), PLL::B => PLLResetBits::PLLB_RST.bits(),
@@ -371,7 +467,7 @@ impl<'a, I2C, E> Si5351Device<'a, I2C>
} }
fn read_register(&mut self, reg: Register) -> Result<u8, Error> { fn read_register(&mut self, reg: Register) -> Result<u8, Error> {
let mut buffer: [u8; 1] = unsafe { mem::uninitialized() }; let mut buffer: [u8; 1] = /*unsafe { mem::uninitialized() }*/ [0];
self.i2c.write_read(self.address, &[reg.addr()], &mut buffer).map_err(i2c_error)?; self.i2c.write_read(self.address, &[reg.addr()], &mut buffer).map_err(i2c_error)?;
Ok(buffer[0]) Ok(buffer[0])
} }
@@ -387,9 +483,14 @@ impl<'a, I2C, E> Si5351Device<'a, I2C>
} }
} }
impl<'a, I2C, E> Si5351<'a> for Si5351Device<'a, I2C> where impl<I2C, E> Si5351 for Si5351Device<I2C> where
I2C: WriteRead<Error=E> + Write<Error=E> I2C: WriteRead<Error=E> + Write<Error=E>
{ {
fn reset_pll(&mut self, pll: PLL) -> Result<(), Error> {
self.reset_pll(pll)
}
fn init_adafruit_module(&mut self) -> Result<(), Error> { fn init_adafruit_module(&mut self) -> Result<(), Error> {
self.init(CrystalLoad::_10) self.init(CrystalLoad::_10)
} }
@@ -477,6 +578,59 @@ impl<'a, I2C, E> Si5351<'a> for Si5351Device<'a, I2C> where
Ok(()) Ok(())
} }
fn set_quad(&mut self, freq: u32) -> Result<f32, Error> {
let ms0 = Multisynth::MS0;
let ms1 = Multisynth::MS1;
let mut ms_div = 126;
let mut mult = (freq*ms_div) as f32 / self.get_xtal_freq() as f32;
while mult > 90.0 {
ms_div -= (ms_div as f32 * 0.2) as u32;
mult = (freq*ms_div) as f32 / self.get_xtal_freq() as f32;
}
if mult < 15.0 {return Err(Error::InvalidParameter);}
//easy calc fraction not the most accurate
let denom: u32 = 1048575;
let num = ((mult % 1.0) * denom as f32) as u32;
let mult = mult as u8;
let ms_div = ms_div as u8;
self.setup_pll(PLL::A, mult, num, denom)?;
self.setup_multisynth_int(ms0, ms_div as u16, OutputDivider::Div1)?;
self.setup_multisynth_int(ms1, ms_div as u16, OutputDivider::Div1)?;
self.select_clock_pll(ClockOutput::Clk0, PLL::A);
self.select_clock_pll(ClockOutput::Clk1, PLL::A);
self.set_clock_enabled(ClockOutput::Clk0, true);
self.set_clock_enabled(ClockOutput::Clk1, true);
self.flush_clock_control(ClockOutput::Clk0)?;
self.flush_clock_control(ClockOutput::Clk1)?;
if self.last_mdiv != ms_div {
self.set_phase_offset(ClockOutput::Clk1, ms_div as u8)?;
self.reset_pll(PLL::A)?;
}
self.last_mdiv = ms_div;
self.flush_output_enabled()?;
let freq_num = self.xtal_freq as f32 * (mult as f32 + (num as f32 / denom as f32));
Ok( freq_num / ms_div as f32 )
}
fn set_phase_offset(&mut self, clk: ClockOutput, phase_offset: u8) -> Result<(), Error> {
if phase_offset & 1<<7 > 0 {return Err(Error::InvalidParameter);}
let reg = match clk {
ClockOutput::Clk0 => Register::Clk0Phoff,
ClockOutput::Clk1 => Register::Clk1Phoff,
ClockOutput::Clk2 => Register::Clk2Phoff,
ClockOutput::Clk3 => Register::Clk3Phoff,
ClockOutput::Clk4 => Register::Clk4Phoff,
ClockOutput::Clk5 => Register::Clk5Phoff,
ClockOutput::Clk6 => Register::Clk6Phoff,
ClockOutput::Clk7 => Register::Clk7Phoff,
};
self.write_register(reg, phase_offset)?;
Ok(())
}
fn set_clock_enabled(&mut self, clk: ClockOutput, enabled: bool) { fn set_clock_enabled(&mut self, clk: ClockOutput, enabled: bool) {
let bit = 1u8 << clk.ix(); let bit = 1u8 << clk.ix();