Added si5153 module

This commit is contained in:
Sebastian 2024-01-14 16:28:11 +01:00
parent 954c98f25e
commit 9c8e54b4ae
3 changed files with 257 additions and 2 deletions

View File

@ -24,4 +24,5 @@ usb-device = "0.2.8"
usbd-serial = "0.1.1"
cheapsdo-protocol = { path = "../protocol" }
postcard = {version = "1.0.8", features = ["use-defmt"]}
heapless = {version = "0.8.0", features = ["defmt-03"]}
heapless = {version = "0.8.0", features = ["defmt-03"]}
num = {version = "0.4.1", default-features = false, features = ["libm"]}

View File

@ -6,6 +6,8 @@ use defmt_rtt as _; // global logger
use panic_probe as _;
use stm32f1xx_hal as _;
mod si5153;
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
@ -23,7 +25,7 @@ mod app {
use stm32f1xx_hal::{
gpio::{self, gpioa, gpioc, Alternate, Output, PushPull},
pac,
i2c, pac,
pac::{RCC, TIM2, TIM3, TIM4},
prelude::*,
rcc::Enable,
@ -39,6 +41,8 @@ mod app {
use cheapsdo_protocol::{DeviceMessage, HostMessage, StatusMessage};
use crate::si5153;
const USB_BUFFER_SIZE: usize = 64;
#[local]
@ -198,6 +202,33 @@ mod app {
.device_class(usbd_serial::USB_CLASS_CDC)
.build();
let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);
let mut i2c1 = i2c::BlockingI2c::i2c1(
cx.device.I2C1,
(scl, sda),
&mut afio.mapr,
i2c::Mode::Fast {
frequency: 400_000.Hz(),
duty_cycle: i2c::DutyCycle::Ratio2to1,
},
clocks,
1000,
10,
1000,
1000,
);
defmt::info!("I2C Setup done");
let mut si_pll = si5153::Si5153::new(&i2c1);
si_pll.init(&mut i2c1, 10_000_000, 800_000_000, 800_000_000);
si_pll.set_ms_source(&mut i2c1, si5153::Multisynth::MS0, si5153::PLL::A);
defmt::info!("si5153 Setup done");
si_pll.set_ms_freq(&mut i2c1, si5153::Multisynth::MS0, 100_000_000);
si_pll.enable_ms_output(&mut i2c1, si5153::Multisynth::MS0);
update_pwm::spawn().unwrap();
(

223
firmware/src/si5153.rs Normal file
View File

@ -0,0 +1,223 @@
use core::marker::PhantomData;
use embedded_hal::blocking::i2c;
const I2C_ADDR: u8 = 96;
const CLK_ENABLE_CONTROL: u8 = 3;
//const PLLX_SRC: u8 = 15;
const PLL_RESET: u8 = 177;
const XTAL_LOAD_CAP: u8 = 183;
#[derive(PartialEq, Copy, Clone)]
pub enum PLL {
A,
B,
}
const PLL_BASE_ADDR: [u8; 2] = [26, 34];
impl PLL {
fn base_address(&self) -> u8 {
return PLL_BASE_ADDR[*self as usize];
}
}
#[derive(Copy, Clone)]
pub enum Multisynth {
MS0,
MS1,
MS2,
}
const MS_BASE_ADDR: [u8; 3] = [42, 50, 58];
const MS_CTRL_ADDR: [u8; 3] = [16, 17, 18];
const CLK_PHOFF_ADDR: [u8; 3] = [165, 166, 167];
impl Multisynth {
fn base_address(&self) -> u8 {
return MS_BASE_ADDR[*self as usize];
}
fn ctrl_address(&self) -> u8 {
return MS_CTRL_ADDR[*self as usize];
}
fn phoff_address(&self) -> u8 {
defmt::debug!("Adress: {}", CLK_PHOFF_ADDR[*self as usize]);
return CLK_PHOFF_ADDR[*self as usize];
}
}
pub struct PllParams {
pub p1: u32,
pub p2: u32,
pub p3: u32,
}
pub struct Si5153<I2C> {
// Marker that makes sure we always get the same I2C
i2c: PhantomData<I2C>,
freq_xtal: u32,
pll_freqs: [u32; 2],
outputs: u8,
ms_srcs: [PLL; 3],
}
impl<I2C, E> Si5153<I2C>
where
I2C: i2c::Write<Error = E>,
{
pub fn new(_i2c: &I2C) -> Self {
Si5153 {
i2c: PhantomData,
freq_xtal: 0,
pll_freqs: [0, 0],
outputs: 0,
ms_srcs: [PLL::A, PLL::A, PLL::A],
}
}
pub fn init(&mut self, i2c: &mut I2C, freq_xtal: u32, freq_a: u32, freq_b: u32) {
self.freq_xtal = freq_xtal;
self.pll_freqs[PLL::A as usize] = freq_a;
self.pll_freqs[PLL::B as usize] = freq_b;
self.outputs = 0xFF;
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs); // Disable all outputs
self.write_byte_reg(i2c, XTAL_LOAD_CAP, 0xD2); //crystal load capacitor = 10pF
self.set_pll_freq(i2c, PLL::A, freq_a);
self.set_pll_freq(i2c, PLL::B, freq_b);
for ms in [Multisynth::MS0, Multisynth::MS1, Multisynth::MS2].iter() {
self.ms_srcs[*ms as usize] = PLL::A;
self.write_byte_reg(i2c, ms.ctrl_address(), 0x0F); // MSi as Source, PLLA to MSi, 8 mA output
self.write_byte_reg(i2c, ms.phoff_address(), 0); // Phase offset to 0.
}
self.write_byte_reg(i2c, PLL_RESET, 0xA0); // Reset both PLLs
}
pub fn set_pll_freq(&mut self, i2c: &mut I2C, pll: PLL, freq: u32) {
// Divider is a + (b/c)
let (a, b, c) = as_fraction(freq, self.freq_xtal);
let params = PllParams {
p1: 128 * a + (128 * b / c) - 512,
p2: 128 * b - c * (128 * b / c),
p3: c,
};
self.write_params(i2c, pll.base_address(), &params);
self.pll_freqs[pll as usize] = freq;
}
pub fn enable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
self.outputs &= !(1 << (synth as u8));
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs);
}
pub fn disable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
self.outputs |= 1 << (synth as u8);
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs);
}
pub fn set_ms_source(&mut self, i2c: &mut I2C, synth: Multisynth, pll: PLL) {
let value: u8 = if pll == PLL::A {
self.ms_srcs[synth as usize] = PLL::A;
0x0F // MS as Source, PLLA to MS, 8 mA output
} else {
self.ms_srcs[synth as usize] = PLL::B;
0x2F // MS as Source, PLLB to MS, 8 mA output
};
self.write_byte_reg(i2c, synth.ctrl_address(), value);
}
pub fn set_ms_freq(&mut self, i2c: &mut I2C, synth: Multisynth, freq: u32) {
let pll = self.ms_srcs[synth as usize];
let pll_freq = self.pll_freqs[pll as usize];
let (a, b, c) = as_fraction(pll_freq, freq);
let params = PllParams {
p1: 128 * a + (128 * b / c) - 512,
p2: 128 * b - c * (128 * b / c),
p3: c,
};
self.write_params(i2c, synth.base_address(), &params)
}
pub fn set_ms_phase(&mut self, i2c: &mut I2C, synth: Multisynth, phase: u8) {
self.write_byte_reg(i2c, synth.phoff_address(), phase);
match self.ms_srcs[synth as usize] {
PLL::A => {
self.write_byte_reg(i2c, PLL_RESET, 1 << 5);
}
PLL::B => {
self.write_byte_reg(i2c, PLL_RESET, 1 << 7);
}
}
}
pub fn pll_reset(&mut self, i2c: &mut I2C) {
self.write_byte_reg(i2c, PLL_RESET, 0xA0);
}
fn write_byte_reg(&self, i2c: &mut I2C, reg_addr: u8, data: u8) {
let res = i2c.write(I2C_ADDR, &[reg_addr, data]);
if res.is_err() {
panic!("i2c write failed. regAdder: {}", reg_addr)
}
}
fn write_params(&self, i2c: &mut I2C, base: u8, params: &PllParams) {
let data: [u8; 9] = [
base,
((params.p3 & 0x00FF00) >> 8) as u8,
(params.p3 & 0x0000FF) as u8,
((params.p1 & 0x030000) >> 16) as u8,
((params.p1 & 0x00FF00) >> 8) as u8,
(params.p1 & 0x0000FF) as u8,
(((params.p3 & 0x0F0000) >> 12) | ((params.p2 & 0x0F0000) >> 16)) as u8,
((params.p2 & 0x00FF00) >> 8) as u8,
(params.p2 & 0x0000FF) as u8,
];
let res = i2c.write(I2C_ADDR, &data);
if res.is_err() {
panic!("i2c write failed. regAdder: {}", base)
}
}
pub fn write_synth_params(&self, i2c: &mut I2C, synth: Multisynth, params: &PllParams) {
self.write_params(i2c, synth.base_address(), params);
}
}
// Turns a fraction x/y into a + b/c with minimal c.
// If c is larger than 0x0FFFFF, c will be limited to that value and
// a best effort approximation for b will be computed
fn as_fraction(x: u32, y: u32) -> (u32, u32, u32) {
let gcd = num::integer::gcd(x, y);
let num = x / gcd;
let denom = y / gcd;
let a = num / denom;
if denom < 0x0FFFFF {
let rm = num % denom;
let c = denom;
let b = rm;
(a, b, c)
} else {
let rm = x % y;
let c = 0x0FFFFF;
let b = ((rm as u64) * (c as u64) / (y as u64)) as u32;
(a, b, c)
}
}