initial version, only supports CLK0
This commit is contained in:
parent
635aff1bc8
commit
477ddce481
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "si5351"
|
||||||
|
version = "0.1.0"
|
||||||
|
categories = ["embedded", "hardware-support", "no-std"]
|
||||||
|
authors = ["Ilya Epifanov <elijah.epifanov@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embedded-hal = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies.cast]
|
||||||
|
default-features = false
|
||||||
|
version = "0.2.2"
|
||||||
142
src/lib.rs
Normal file
142
src/lib.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//#![deny(missing_docs)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![feature(unsize)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate cast;
|
||||||
|
extern crate embedded_hal as hal;
|
||||||
|
|
||||||
|
use cast::{f32, u32};
|
||||||
|
use core::mem;
|
||||||
|
use hal::blocking::i2c::{Write, WriteRead};
|
||||||
|
|
||||||
|
mod si5351;
|
||||||
|
|
||||||
|
/// Si5351 driver
|
||||||
|
pub struct Si5351<I2C> {
|
||||||
|
i2c: I2C,
|
||||||
|
address: u8,
|
||||||
|
xtal_freq: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2C, E> Si5351<I2C>
|
||||||
|
where
|
||||||
|
I2C: WriteRead<Error=E> + Write<Error=E>,
|
||||||
|
{
|
||||||
|
/// Creates a new driver from a I2C peripheral
|
||||||
|
pub fn new(i2c: I2C, address_bit: bool, xtal_freq: u32) -> Result<Self, E> {
|
||||||
|
let si5351 = Si5351 {
|
||||||
|
i2c,
|
||||||
|
address: si5351::ADDRESS | if address_bit { 1 } else { 0 },
|
||||||
|
xtal_freq,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(si5351)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_frequency(&mut self, freq: u32) -> Result<(), E> {
|
||||||
|
let pll_freq: u32;
|
||||||
|
let l: u32;
|
||||||
|
let f: f32;
|
||||||
|
let mult: u8;
|
||||||
|
let num: u32;
|
||||||
|
let denom: u32;
|
||||||
|
let divider: u32;
|
||||||
|
|
||||||
|
divider = match 900000000 / freq {
|
||||||
|
d if d % 2 == 0 => d,
|
||||||
|
d => d - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
pll_freq = divider * freq;
|
||||||
|
|
||||||
|
mult = (pll_freq / self.xtal_freq) as u8;
|
||||||
|
l = pll_freq % self.xtal_freq;
|
||||||
|
f = (l as f32) * (1048575 as f32) / (self.xtal_freq as f32);
|
||||||
|
num = f as u32;
|
||||||
|
denom = 1048575;
|
||||||
|
|
||||||
|
self.setup_pll(si5351::PLL::A, mult, num, denom)?;
|
||||||
|
self.setup_multisynth(si5351::Multisynth::MS0, divider, 0)?;
|
||||||
|
self.reset_pll(si5351::PLL::A)?;
|
||||||
|
self.enable_clock(si5351::ClockOutput::CLK0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_pll(&mut self, pll: si5351::PLL, mult: u8, num: u32, denom: u32) -> Result<(), E> {
|
||||||
|
let p1: u32;
|
||||||
|
let p2: u32;
|
||||||
|
let p3: u32;
|
||||||
|
let ratio = (128f32 * (f32(num) / f32(denom))) as u32;
|
||||||
|
|
||||||
|
p1 = u32(128u32 * mult as u32 + ratio - 512);
|
||||||
|
p2 = u32(128 * num - denom * ratio);
|
||||||
|
p3 = denom;
|
||||||
|
|
||||||
|
self.write_pll_register(pll, 0, ((p3 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_pll_register(pll, 1, p3 as u8)?;
|
||||||
|
self.write_pll_register(pll, 2, ((p1 & 0x00030000) >> 16) as u8)?;
|
||||||
|
self.write_pll_register(pll, 3, ((p1 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_pll_register(pll, 4, p1 as u8)?;
|
||||||
|
self.write_pll_register(pll, 5, (((p3 & 0x000F0000) >> 12) | ((p2 & 0x000F0000) >> 16)) as u8)?;
|
||||||
|
self.write_pll_register(pll, 6, ((p2 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_pll_register(pll, 7, p2 as u8)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_multisynth(&mut self, synth: si5351::Multisynth, divider: u32, r_div: u8) -> Result<(), E> {
|
||||||
|
let p1: u32;
|
||||||
|
let p2: u32;
|
||||||
|
let p3: u32;
|
||||||
|
|
||||||
|
p1 = 128 * divider - 512;
|
||||||
|
p2 = 0;
|
||||||
|
p3 = 1;
|
||||||
|
|
||||||
|
self.write_synth_register(synth, 0, ((p3 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_synth_register(synth, 1, p3 as u8)?;
|
||||||
|
self.write_synth_register(synth, 2, ((p1 & 0x00030000) >> 16) as u8 | r_div)?;
|
||||||
|
self.write_synth_register(synth, 3, ((p1 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_synth_register(synth, 4, p1 as u8)?;
|
||||||
|
self.write_synth_register(synth, 5, (((p3 & 0x000F0000) >> 12) | ((p2 & 0x000F0000) >> 16)) as u8)?;
|
||||||
|
self.write_synth_register(synth, 6, ((p2 & 0x0000FF00) >> 8) as u8)?;
|
||||||
|
self.write_synth_register(synth, 7, p2 as u8)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_pll(&mut self, pll: si5351::PLL) -> Result<(), E> {
|
||||||
|
self.write_register(si5351::Register::PLL_RESET,
|
||||||
|
match pll {
|
||||||
|
si5351::PLL::A => 0b0010_0000,
|
||||||
|
si5351::PLL::B => 0b1000_0000
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_clock(&mut self, clock: si5351::ClockOutput) -> Result<(), E> {
|
||||||
|
self.write_register(clock.register(), 0x4f)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn read_register(&mut self, reg: si5351::Register) -> Result<u8, E> {
|
||||||
|
let mut buffer: [u8; 1] = unsafe { mem::uninitialized() };
|
||||||
|
self.i2c.write_read(self.address, &[reg.addr()], &mut buffer)?;
|
||||||
|
Ok(buffer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_register(&mut self, reg: si5351::Register, byte: u8) -> Result<(), E> {
|
||||||
|
self.i2c.write(self.address, &[reg.addr(), byte])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_pll_register(&mut self, pll: si5351::PLL, reg: u8, byte: u8) -> Result<(), E> {
|
||||||
|
self.i2c.write(self.address, &[pll.base_addr() + reg, byte])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_synth_register(&mut self, pll: si5351::Multisynth, reg: u8, byte: u8) -> Result<(), E> {
|
||||||
|
self.i2c.write(self.address, &[pll.base_addr() + reg, byte])
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/si5351.rs
Normal file
65
src/si5351.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
pub const ADDRESS: u8 = 0b1100_0000;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum PLL {
|
||||||
|
A = 26,
|
||||||
|
B = 34
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PLL {
|
||||||
|
pub fn base_addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Multisynth {
|
||||||
|
MS0 = 42,
|
||||||
|
MS1 = 50,
|
||||||
|
MS2 = 58
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Multisynth {
|
||||||
|
pub fn base_addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Register {
|
||||||
|
PLL_RESET = 177,
|
||||||
|
CLK0 = 16,
|
||||||
|
CLK1 = 17,
|
||||||
|
CLK2 = 18
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum ClockOutput {
|
||||||
|
CLK0 = 16,
|
||||||
|
CLK1 = 17,
|
||||||
|
CLK2 = 18
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockOutput {
|
||||||
|
pub fn register(&self) -> Register {
|
||||||
|
match self {
|
||||||
|
&ClockOutput::CLK0 => Register::CLK0,
|
||||||
|
&ClockOutput::CLK1 => Register::CLK1,
|
||||||
|
&ClockOutput::CLK2 => Register::CLK2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user