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