diff --git a/Cargo.toml b/Cargo.toml index 73c11909..43b37cd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,8 +48,13 @@ default-features = false version = "1.0.2" [dependencies.embedded-hal] +# TODO: Update version when I2S proposal is finalized: +# https://github.com/rust-embedded/embedded-hal/pull/204 features = ["unproven"] -version = "0.2.3" +# version = "0.2.3" +# git = "https://github.com/eldruin/embedded-hal/" +# branch = "i2s-v0.2.x" +path = "../embedded-hal" [dev-dependencies] panic-semihosting = "0.5.3" diff --git a/src/i2s.rs b/src/i2s.rs new file mode 100644 index 00000000..fb5c324b --- /dev/null +++ b/src/i2s.rs @@ -0,0 +1,1205 @@ +use core::ops::Deref; +use core::ptr; + +use embedded_hal::blocking::i2s::{Read, Write, WriteIter}; + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::stm32::{spi1, RCC, SPI1, SPI2}; + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::stm32::SPI3; + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::stm32::SPI4; + +#[cfg(any( + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::stm32::SPI5; + +#[cfg(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::stm32::SPI6; + +#[cfg(any( + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioa::PA9; +#[cfg(any( + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423" +))] +use crate::gpio::gpioa::{PA1, PA11}; +#[cfg(any( + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423" +))] +use crate::gpio::gpioa::{PA10, PA12}; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioa::{PA15, PA4, PA5, PA6, PA7}; +// NOTE: Added PA4 and PA15 here. + +#[cfg(any( + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f446" +))] +use crate::gpio::gpiob::PB0; +#[cfg(any( + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423" +))] +use crate::gpio::gpiob::PB12; +#[cfg(any(feature = "stm32f446"))] +use crate::gpio::gpiob::PB2; +#[cfg(any( + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423" +))] +use crate::gpio::gpiob::PB8; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpiob::{PB10, PB13, PB14, PB15, PB3, PB4, PB5}; + +#[cfg(any(feature = "stm32f446", feature = "stm32f469", feature = "stm32f479"))] +use crate::gpio::gpioc::PC1; +#[cfg(any( + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f446" +))] +use crate::gpio::gpioc::PC7; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioc::{PC10, PC11, PC12}; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioc::{PC2, PC3}; + +#[cfg(any(feature = "stm32f446"))] +use crate::gpio::gpiod::PD0; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpiod::{PD3, PD6}; + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioe::{PE12, PE13, PE14, PE2, PE5, PE6}; + +#[cfg(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpiof::{PF11, PF7, PF8, PF9}; + +#[cfg(any(feature = "stm32f446"))] +use crate::gpio::gpiog::PG11; +#[cfg(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpiog::PG14; +#[cfg(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpiog::{PG12, PG13}; + +#[cfg(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioh::{PH6, PH7}; + +#[cfg(any( + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::gpioi::{PI1, PI2, PI3}; + +#[cfg(any( + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f423", + feature = "stm32f446" +))] +use crate::gpio::AF7; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +use crate::gpio::{Alternate, AF5, AF6, AF7}; + +use crate::rcc::Clocks; +use crate::time::Hertz; + +/// I2S error +#[derive(Debug)] +pub enum Error { + /// Overrun occurred + Overrun, + /// Mode fault occurred + ModeFault, + /// CRC error + Crc, + #[doc(hidden)] + _Extensible, +} + +pub trait Pins {} +pub trait PinCk {} +pub trait PinWs {} +pub trait PinSd {} +pub trait PinSdExt {} + +impl Pins for (CK, WS, SD, SDEXT) +where + CK: PinCk, + WS: PinWs, + SD: PinSd, + SDEXT: PinSdExt, +{ +} + +/// A filler type for when the CK pin is unnecessary +pub struct NoCk; +/// A filler type for when the Ws pin is unnecessary +pub struct NoWs; +/// A filler type for when the Sd pin is unnecessary +pub struct NoSd; +/// A filler type for when the SdExt pin is unnecessary +pub struct NoSdExt; + +macro_rules! pins { + ($($SPIX:ty: CK: [$($CK:ty),*] WS: [$($WS:ty),*] SD: [$($SD:ty),*] SDEXT: [$($SDEXT:ty),*])+) => { + $( + $( + impl PinCk<$SPIX> for $CK {} + )* + $( + impl PinWs<$SPIX> for $WS {} + )* + $( + impl PinSd<$SPIX> for $SD {} + )* + $( + impl PinSdExt<$SPIX> for $SDEXT {} + )* + )+ + } +} + +// #[cfg(any( +// feature = "stm32f401", +// feature = "stm32f405", +// feature = "stm32f407", +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f415", +// feature = "stm32f417", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f446", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// pins! { +// SPI1: +// CK: [ +// NoCk, +// PA5>, +// PB3> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PA7>, +// PB5> +// ] +// SDEXT: [ +// NoSdExt, +// PA6>, +// PB4> +// ] + +// SPI2: +// CK: [ +// NoCk, +// PB10>, +// PB13> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PB15>, +// PC3> +// ] +// SDEXT: [ +// NoSdExt, +// PB14>, +// PC2> +// ] +// } + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +pins! { + SPI3: + CK: [ + NoCk, + PB3>, + PC10> + ] + WS: [ + NoWs, + PA4>, + PA15> + ] + SD: [ + NoSd, + PB5>, + PC12> + ] + SDEXT: [ + NoSdExt, + PB4>, + PC11> + ] +} + +// #[cfg(any( +// feature = "stm32f401", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f446", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// pins! { +// SPI2: +// CK: [PD3>] +// WS: [] // TODO: Fill in. +// SD: [] +// SDEXT: [] +// SPI3: +// CK: [] +// WS: [] // TODO: Fill in. +// SD: [] +// SDEXT: [PD6>] +// SPI4: +// CK: [ +// NoCk, +// PE2>, +// PE12> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PE6>, +// PE14> +// ] +// SDEXT: [ +// NoSdExt, +// PE5>, +// PE13> +// ] +// } + +// #[cfg(any( +// feature = "stm32f405", +// feature = "stm32f407", +// feature = "stm32f415", +// feature = "stm32f417", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// pins! { +// SPI2: +// CK: [PI1>] +// WS: [] // TODO: Fill in. +// SD: [PI3>] +// SDEXT: [PI2>] +// } + +// #[cfg(any( +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423", +// feature = "stm32f446" +// ))] +// pins! { +// SPI2: +// CK: [PC7>] +// WS: [] // TODO: Fill in. +// SD: [] +// SDEXT: [] +// } + +// #[cfg(any( +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423" +// ))] +// pins! { +// SPI5: +// CK: [ +// NoCk, +// PB0> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PA10>, +// PB8> +// ] +// SDEXT: [ +// NoSdExt, +// PA12> +// ] +// } + +// #[cfg(any( +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423" +// ))] +// pins! { +// SPI3: +// CK: [PB12>] +// WS: [] // TODO: Fill in. +// SD: [] +// SDEXT: [] +// SPI4: +// CK: [PB13>] +// WS: [] // TODO: Fill in. +// SD: [PA1>] +// SDEXT: [PA11>] +// SPI5: +// CK: [ +// PE2>, +// PE12> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// PE6>, +// PE14> +// ] +// SDEXT: [ +// PE5>, +// PE13> +// ] +// } + +// #[cfg(any(feature = "stm32f413", feature = "stm32f423"))] +// pins! { +// SPI2: +// CK: [PA9>] +// WS: [] // TODO: Fill in. +// SD: [PA10>] +// SDEXT: [PA12>] +// } + +// #[cfg(any( +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// pins! { +// SPI5: +// CK: [ +// NoCk, +// PF7>, +// PH6> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PF9>, +// PF11> +// ] +// SDEXT: [ +// NoSdExt, +// PF8>, +// PH7> +// ] + +// SPI6: +// CK: [ +// NoCk, +// PG13> +// ] +// WS: [] // TODO: Fill in. +// SD: [ +// NoSd, +// PG14> +// ] +// SDEXT: [ +// NoSdExt, +// PG12> +// ] +// } + +// #[cfg(any(feature = "stm32f446"))] +// pins! { +// SPI2: +// CK: [PA9>] +// WS: [] // TODO: Fill in. +// SD: [PC1>] +// SDEXT: [] + +// SPI3: +// CK: [] +// WS: [] // TODO: Fill in. +// SD: [ +// PB0>, +// PB2>, +// PD0> +// ] +// SDEXT: [] + +// SPI4: +// CK: [PG11>] +// WS: [] // TODO: Fill in. +// SD: [PG13>] +// SDEXT: [ +// PG12>, +// PD0> +// ] +// } + +// #[cfg(any(feature = "stm32f469", feature = "stm32f479"))] +// pins! { +// SPI2: +// CK: [PA9>] +// WS: [] // TODO: Fill in. +// SD: [PC1>] +// SDEXT: [] +// } + +// /// Interrupt events +// pub enum Event { +// /// New data has been received +// Rxne, +// /// Data can be sent +// Txe, +// /// An error occurred +// Error, +// } + +#[derive(Debug)] +pub struct I2s { + spi: SPI, + pins: PINS, +} + +// #[cfg(any( +// feature = "stm32f401", +// feature = "stm32f405", +// feature = "stm32f407", +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f415", +// feature = "stm32f417", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f446", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// impl Spi { +// pub fn spi1(spi: SPI1, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb2enr.modify(|_, w| w.spi1en().set_bit()); + +// Spi { spi, pins }.init(mode, freq, clocks.pclk2()) +// } +// } + +// #[cfg(any( +// feature = "stm32f401", +// feature = "stm32f405", +// feature = "stm32f407", +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f415", +// feature = "stm32f417", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f446", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// impl Spi { +// pub fn spi2(spi: SPI2, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb1enr.modify(|_, w| w.spi2en().set_bit()); + +// Spi { spi, pins }.init(mode, freq, clocks.pclk1()) +// } +// } + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479" +))] +impl I2s { + pub fn i2s3(spi: SPI3, pins: PINS, freq: Hertz, clocks: Clocks) -> Self + where + PINS: Pins, + { + // NOTE(unsafe) This executes only during initialisation + let rcc = unsafe { &(*RCC::ptr()) }; + + // Enable clock for SPI + rcc.apb1enr.modify(|_, w| w.spi3en().set_bit()); + + // TODO: Use PLL I2S clock. + // if clocks.plli2sclk().is_some() { + // I2s { spi, pins }.init(freq, clocks.plli2sclk()) + // } + + I2s { spi, pins }.init(freq, freq) + } +} + +// impl I2s { +// pub fn i2s3(spi: SPI3, pins: PINS, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb1enr.modify(|_, w| w.spi3en().set_bit()); + +// // TODO: Use Real clock value from I2S PLL. +// I2s { spi, pins }.init(freq, freq) +// } +// } + +// #[cfg(any( +// feature = "stm32f401", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f446", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// impl Spi { +// pub fn spi4(spi: SPI4, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb2enr.modify(|_, w| w.spi4en().set_bit()); + +// Spi { spi, pins }.init(mode, freq, clocks.pclk2()) +// } +// } + +// #[cfg(any( +// feature = "stm32f410", +// feature = "stm32f411", +// feature = "stm32f412", +// feature = "stm32f413", +// feature = "stm32f423", +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// impl Spi { +// pub fn spi5(spi: SPI5, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb2enr.modify(|_, w| w.spi5en().set_bit()); + +// Spi { spi, pins }.init(mode, freq, clocks.pclk2()) +// } +// } + +// #[cfg(any( +// feature = "stm32f427", +// feature = "stm32f429", +// feature = "stm32f437", +// feature = "stm32f439", +// feature = "stm32f469", +// feature = "stm32f479" +// ))] +// impl Spi { +// pub fn spi6(spi: SPI6, pins: PINS, mode: Mode, freq: Hertz, clocks: Clocks) -> Self +// where +// PINS: Pins, +// { +// // NOTE(unsafe) This executes only during initialisation +// let rcc = unsafe { &(*RCC::ptr()) }; + +// // Enable clock for SPI +// rcc.apb2enr.modify(|_, w| w.spi6en().set_bit()); + +// Spi { spi, pins }.init(mode, freq, clocks.pclk2()) +// } +// } + +impl I2s +where + SPI: Deref, +{ + pub fn init(self, _freq: Hertz, _clock: Hertz) -> Self { + // disable SS output + self.spi.cr2.write(|w| w.ssoe().clear_bit()); + + // TODO: Calculate baud rate. + // let br = match clock.0 / freq.0 { + // 0 => unreachable!(), + // 1..=2 => 0b000, + // 3..=5 => 0b001, + // 6..=11 => 0b010, + // 12..=23 => 0b011, + // 24..=47 => 0b100, + // 48..=95 => 0b101, + // 96..=191 => 0b110, + // _ => 0b111, + // }; + let br: u8 = 0; + + // Configure clock polarity. + // self.spi.i2scfgr.write(|w| w.ckpol().idle_high()); + + // Configure the I2S precsaler and enable MCKL output. + // NOTE: Hardcoded for 48KHz audio sampling rate with PLLI2S at 86MHz. + // I2S uses DIV=3, ODD=true to achive a 12.285714 MHz MCKL. + // FS = I2SxCLK / [(16*2)*(((2*I2SDIV)+ODD)*8)] when the channel frame is 16-bit wide. + // FS = 86MHz (from PLLI2S above) / [(16*2)*(((2*3)+1)*8)] = 48KHz + // NOTE: Unsafe because the division can be set incorrectly. + self.spi + .i2spr + .write(|w| unsafe { w.i2sdiv().bits(3).odd().odd().mckoe().enabled() }); + + // Configure I2S. + // TODO: Configurable I2S standard and data length from user input. + self.spi.i2scfgr.write(|w| { + w + // SPI/I2S Mode. + .i2smod() + .i2smode() + // I2S standard. + .i2sstd() + .philips() + // Data and channel length. + .datlen() + .sixteen_bit() + .chlen() + .sixteen_bit() + // Clock steady state polarity. + .ckpol() + .idle_high() + // Master TX mode and enable. + .i2scfg() + .master_tx() + .i2se() + .enabled() + }); + + self + } + + // TODO: Configure interrupts for I2S. + // /// Enable interrupts for the given `event`: + // /// - Received data ready to be read (RXNE) + // /// - Transmit data register empty (TXE) + // /// - Transfer error + // pub fn listen(&mut self, event: Event) { + // match event { + // Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().set_bit()), + // Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().set_bit()), + // Event::Error => self.spi.cr2.modify(|_, w| w.errie().set_bit()), + // } + // } + + // /// Disable interrupts for the given `event`: + // /// - Received data ready to be read (RXNE) + // /// - Transmit data register empty (TXE) + // /// - Transfer error + // pub fn unlisten(&mut self, event: Event) { + // match event { + // Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().clear_bit()), + // Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().clear_bit()), + // Event::Error => self.spi.cr2.modify(|_, w| w.errie().clear_bit()), + // } + // } + + /// Return `true` if the BSY flag is set, indicating that the I2S is busy + /// communicating. + pub fn is_bsy(&self) -> bool { + self.spi.sr.read().bsy().bit_is_set() + } + + /// Return `true` if the TXE flag is set, i.e. new data to transmit + /// can be written to the SPI. + pub fn is_txe(&self) -> bool { + self.spi.sr.read().txe().bit_is_set() + } + + /// Return `true` if the RXNE flag is set, i.e. new data has been received + /// and can be read from the SPI. + pub fn is_rxne(&self) -> bool { + self.spi.sr.read().rxne().bit_is_set() + } + + /// Return the value of the CHSIDE flag, i.e. which channel to transmit next. + pub fn ch_side(&self) -> bool { + self.spi.sr.read().chside().bit_is_set() + } + + /// Return `true` if the UDR flag is set, i.e. no new data was available + /// for transmission while in slave mode. + pub fn is_udr(&self) -> bool { + self.spi.sr.read().udr().bit_is_set() + } + + /// Return `true` if the OVR flag is set, i.e. new data has been received + /// while the receive data register was already filled. + pub fn is_ovr(&self) -> bool { + self.spi.sr.read().ovr().bit_is_set() + } + + /// Return `true` if the FRE flag is set, i.e. there was an unexpected change + /// in the WS line by the master while in slave mode. + pub fn is_fre(&self) -> bool { + self.spi.sr.read().fre().bit_is_set() + } + + pub fn free(self) -> (SPI, PINS) { + (self.spi, self.pins) + } +} + +impl Read for I2s +where + SPI: Deref, +{ + type Error = Error; + + fn try_read<'w>( + &mut self, + left_words: &'w mut [W], + right_words: &'w mut [W], + ) -> Result<(), Self::Error> { + // TODO: Check preconditions, return errors. + // NOTE: Not tested. + for (lw, rw) in left_words.iter_mut().zip(right_words.iter_mut()) { + *lw = unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const W) }; + *rw = unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const W) }; + } + Ok(()) + } +} + +impl Write for I2s +where + SPI: Deref, + W: Copy, +{ + type Error = Error; + + fn try_write<'w>( + &mut self, + left_words: &'w [W], + right_words: &'w [W], + ) -> Result<(), Self::Error> { + // TODO: Check preconditions, return errors. + for (lw, rw) in left_words.iter().zip(right_words.iter()) { + while !self.is_txe() {} // Wait for TX enable after the previous word. + unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut W, *lw) } + while !self.is_txe() {} // Wait for TX enable after the left word. + unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut W, *rw) } + } + Ok(()) + } +} + +impl WriteIter for I2s +where + SPI: Deref, + // W: Copy, +{ + type Error = Error; + + fn try_write_iter(&mut self, left_words: LW, right_words: RW) -> Result<(), Self::Error> + where + LW: IntoIterator, + RW: IntoIterator, + { + // TODO: Check preconditions, return errors. + for (lw, rw) in left_words.into_iter().zip(right_words.into_iter()) { + while !self.is_txe() {} // Wait for TX enable after the previous word. + unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut W, lw) } + while !self.is_txe() {} // Wait for TX enable after the left word. + unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut W, rw) } + } + Ok(()) + } +} + +// impl spi::FullDuplex for Spi +// where +// SPI: Deref, +// { +// type Error = Error; + +// fn read(&mut self) -> nb::Result { +// let sr = self.spi.sr.read(); + +// Err(if sr.ovr().bit_is_set() { +// nb::Error::Other(Error::Overrun) +// } else if sr.modf().bit_is_set() { +// nb::Error::Other(Error::ModeFault) +// } else if sr.crcerr().bit_is_set() { +// nb::Error::Other(Error::Crc) +// } else if sr.rxne().bit_is_set() { +// // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows +// // reading a half-word) +// return Ok(unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) }); +// } else { +// nb::Error::WouldBlock +// }) +// } + +// fn send(&mut self, byte: u8) -> nb::Result<(), Error> { +// let sr = self.spi.sr.read(); + +// Err(if sr.ovr().bit_is_set() { +// nb::Error::Other(Error::Overrun) +// } else if sr.modf().bit_is_set() { +// nb::Error::Other(Error::ModeFault) +// } else if sr.crcerr().bit_is_set() { +// nb::Error::Other(Error::Crc) +// } else if sr.txe().bit_is_set() { +// // NOTE(write_volatile) see note above +// unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) } +// return Ok(()); +// } else { +// nb::Error::WouldBlock +// }) +// } +// } + +// impl embedded_hal::blocking::spi::transfer::Default for Spi where +// SPI: Deref +// { +// } + +// impl embedded_hal::blocking::spi::write::Default for Spi where +// SPI: Deref +// { +// } diff --git a/src/lib.rs b/src/lib.rs index be09750d..b0cc385e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,8 @@ pub mod delay; pub mod gpio; #[cfg(feature = "device-selected")] pub mod i2c; +#[cfg(feature = "device-selected")] +pub mod i2s; #[cfg(all( feature = "usb_fs", any( diff --git a/src/rcc.rs b/src/rcc.rs index 88f7ef06..4d34d3e0 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -19,6 +19,7 @@ impl RccExt for RCC { pclk2: None, sysclk: None, pll48clk: false, + plli2sclk: None, }, } } @@ -124,6 +125,7 @@ pub struct CFGR { pclk2: Option, sysclk: Option, pll48clk: bool, + plli2sclk: Option, } impl CFGR { @@ -174,13 +176,21 @@ impl CFGR { self } + pub fn plli2sclk(mut self, freq: F) -> Self + where + F: Into, + { + self.plli2sclk = Some(freq.into().0); + self + } + #[inline(always)] - fn pll_setup(&self) -> (bool, bool, u32, Option) { + fn pll_setup(&self) -> (bool, bool, u32, Option, Option) { let pllsrcclk = self.hse.unwrap_or(HSI); let sysclk = self.sysclk.unwrap_or(pllsrcclk); let sysclk_on_pll = sysclk != pllsrcclk; - if !sysclk_on_pll && !self.pll48clk { - return (false, false, sysclk, None); + if !sysclk_on_pll && !self.pll48clk && !self.plli2sclk.is_some() { + return (false, false, sysclk, None, None); } // Sysclk output divisor must be one of 2, 4, 6 or 8 @@ -240,12 +250,34 @@ impl CFGR { w.pllsrc().bit(self.hse.is_some()) }); + // NOTE: Hardcoded for 48KHz audio sampling rate with VCO at 2MHz. + // plli2sclk = 2MHz * 258 / 6 = 86MHz + // I2S uses DIV=3, ODD=true to achive a 12.285714 MHz MCKL. + // TODO: Calculate PLLI2S N an R values from desired frequency. + let plli2sn = 258; + let plli2sr = 6; + let plli2sclk = vco_in * plli2sn / plli2sr; + if self.plli2sclk.is_some() { + unsafe { &*RCC::ptr() }.plli2scfgr.write(|w| unsafe { + w.plli2sn() + .bits(plli2sn as u16) + .plli2sr() + .bits(plli2sr as u8) + }); + } + let real_sysclk = if sysclk_on_pll { vco_in * plln / sysclk_div } else { sysclk }; - (true, sysclk_on_pll, real_sysclk, Some(Hertz(pll48clk))) + ( + true, + sysclk_on_pll, + real_sysclk, + Some(Hertz(pll48clk)), + Some(Hertz(plli2sclk)), + ) } fn flash_setup(sysclk: u32) { @@ -288,7 +320,7 @@ impl CFGR { pub fn freeze(self) -> Clocks { let rcc = unsafe { &*RCC::ptr() }; - let (use_pll, sysclk_on_pll, sysclk, pll48clk) = self.pll_setup(); + let (use_pll, sysclk_on_pll, sysclk, pll48clk, plli2sclk) = self.pll_setup(); assert!(!sysclk_on_pll || sysclk <= SYSCLK_MAX && sysclk >= SYSCLK_MIN); @@ -346,13 +378,13 @@ impl CFGR { Self::flash_setup(sysclk); if self.hse.is_some() { - // enable HSE and wait for it to be ready + // Enable HSE and wait for it to be ready. rcc.cr.modify(|_, w| w.hseon().set_bit()); while rcc.cr.read().hserdy().bit_is_clear() {} } if use_pll { - // Enable PLL + // Enable PLL and wait for it to be ready. rcc.cr.modify(|_, w| w.pllon().set_bit()); // Enable voltage regulator overdrive if HCLK is above the limit @@ -380,6 +412,12 @@ impl CFGR { while rcc.cr.read().pllrdy().bit_is_clear() {} } + if self.plli2sclk.is_some() { + // Enable PLLI2S and wait for it to be ready. + rcc.cr.modify(|_, w| w.plli2son().set_bit()); + while rcc.cr.read().plli2srdy().bit_is_clear() {} + } + // Set scaling factors rcc.cfgr.modify(|_, w| unsafe { w.ppre2() @@ -413,6 +451,7 @@ impl CFGR { ppre2, sysclk: Hertz(sysclk), pll48clk, + plli2sclk, }; if self.pll48clk { @@ -435,6 +474,7 @@ pub struct Clocks { ppre2: u8, sysclk: Hertz, pll48clk: Option, + plli2sclk: Option, } impl Clocks { @@ -473,6 +513,11 @@ impl Clocks { self.pll48clk } + /// Returns the frequency of the PLLI2S clock + pub fn plli2sclk(&self) -> Option { + self.plli2sclk + } + /// Returns true if the PLL48 clock is within USB /// specifications. It is required to use the USB functionality. pub fn is_pll48clk_valid(&self) -> bool {