Skip to content

I2s clock #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions src/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ impl RccExt for RCC {
pclk2: None,
sysclk: None,
pll48clk: false,
plli2sclk: None,
exti2sclk: None,
},
}
}
Expand Down Expand Up @@ -117,13 +119,65 @@ pub const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
/// Maximum APB1 peripheral clock frequency
pub const PCLK1_MAX: u32 = PCLK2_MAX / 2;

/// Maximum I2S clock frequency
// TODO check actual value across reference manual
pub const I2SCLK_MAX: u32 = 192_000_000;

/// Minimum division factor for PLLI2S ouptut clock
// TODO check actual value across reference manual
pub const PLLI2S_R_MIN: u8 = 2;

/// Maximum division factor for PLLI2S output clock
// TODO check actual value across reference manual
pub const PLLI2S_R_MAX: u8 = 7;

/// Minimum PLLI2S multiplication factor for PLLI2S vco
// TODO check actual value across reference manual
pub const PLLI2S_N_MIN: u16 = 50;

/// Maximum PLLI2S multiplication factor for PLLI2S vco
// TODO check actual value across reference manual
pub const PLLI2S_N_MAX: u16 = 432;

/// Minimum frequency at PLLI2S vco output
// TODO check actual value across datasheets
pub const PLLI2S_N_FREQ_MIN: u32 = 100_000_000;

/// Maximum frequency at PLLI2S vco output
// TODO check actual value across datasheets
pub const PLLI2S_N_FREQ_MAX: u32 = 432_000_000;

/// Minimum division factor for PLLI2S input clock
// TODO check actual value across reference manual
pub const PLLI2S_M_MIN: u8 = 2;

/// Maximum division factor for PLLI2S input clock
// TODO check actual value across reference manual
pub const PLLI2S_M_MAX: u8 = 63;

/// Minimum frequency for PLLI2S input clock
// TODO check actual value across datasheets
pub const PLLI2S_M_FREQ_MIN: u32 = 950_000;

/// Maximum frequency for PLLI2S input clock
// TODO check actual value across datasheets
pub const PLLI2S_M_FREQ_MAX: u32 = 2_100_000;

struct PLLI2SCFGR {
r: u8,
n: u16,
m: u8,
}

pub struct CFGR {
hse: Option<u32>,
hclk: Option<u32>,
pclk1: Option<u32>,
pclk2: Option<u32>,
sysclk: Option<u32>,
pll48clk: bool,
plli2sclk: Option<PLLI2SCFGR>,
exti2sclk: Option<u32>,
}

impl CFGR {
Expand Down Expand Up @@ -174,6 +228,21 @@ impl CFGR {
self
}

/// Enable and configure the PLLI2S clock, and select it as i2s clock source
pub fn plli2sclk(mut self, r: u8, n: u16, m: u8) -> Self {
self.plli2sclk = Some(PLLI2SCFGR { r, n, m });
self
}

/// Use external clock of frequency `freq` as i2s clock source
pub fn use_exti2sclk<F>(mut self, freq: F) -> Self
where
F: Into<Hertz>,
{
self.exti2sclk = Some(freq.into().0);
self
}

#[inline(always)]
fn pll_setup(&self) -> (bool, bool, u32, Option<Hertz>) {
let pllsrcclk = self.hse.unwrap_or(HSI);
Expand Down Expand Up @@ -248,6 +317,69 @@ impl CFGR {
(true, sysclk_on_pll, real_sysclk, Some(Hertz(pll48clk)))
}

// This function setup the hardware for i2s clock:
//
// Ok(Some(freq)) i2s clock is correctly set and have `freq` frequency
// Ok(None) i2s clock wasn't required
// Err(_) something is wrong with the provided configuration, the hardware is leave untouched
#[inline(always)]
fn i2sclk_setup(&self) -> Result<Option<Hertz>, ()> {
if let Some(exti2sclk) = self.exti2sclk {
if exti2sclk > I2SCLK_MAX {
return Err(());
}
unsafe {
let rcc = &*RCC::ptr();
rcc.cfgr.modify(|_, w| w.i2ssrc().set_bit());
}
return Ok(Some(Hertz(exti2sclk)));
}

if let Some(plli2sclk) = self.plli2sclk.as_ref() {
//check factors
if plli2sclk.r < PLLI2S_R_MIN || plli2sclk.r > PLLI2S_R_MAX {
return Err(());
}
if plli2sclk.n < PLLI2S_N_MIN || plli2sclk.n > PLLI2S_N_MAX {
return Err(());
}
if plli2sclk.m < PLLI2S_M_MIN || plli2sclk.m > PLLI2S_M_MAX {
return Err(());
}

//check clocks
let pllsrcclk = self.hse.unwrap_or(HSI);
let plli2s_m_freq = pllsrcclk / (plli2sclk.m as u32);
if plli2s_m_freq < PLLI2S_M_FREQ_MIN || plli2s_m_freq > PLLI2S_M_FREQ_MAX {
return Err(());
}
let plli2s_n_freq = plli2s_m_freq * (plli2sclk.n as u32);
if plli2s_n_freq < PLLI2S_N_FREQ_MIN || plli2s_n_freq > PLLI2S_N_FREQ_MAX {
return Err(());
}
let plli2s_freq = plli2s_n_freq / (plli2sclk.r as u32);
if plli2s_freq > I2SCLK_MAX {
return Err(());
}

//setup the hardware
unsafe {
let rcc = &*RCC::ptr();
rcc.plli2scfgr.modify(|_, w| {
w.plli2sr().bits(plli2sclk.r);
w.plli2sn().bits(plli2sclk.n);
w.plli2sm().bits(plli2sclk.m)
});
//run the clock
rcc.cr.modify(|_, w| w.plli2son().set_bit());
//wait a stable clock
while rcc.cr.read().plli2srdy().bit_is_clear() {}
}
return Ok(Some(Hertz(plli2s_freq)));
}
Ok(None)
}

fn flash_setup(sysclk: u32) {
use crate::stm32::FLASH;

Expand Down