diff --git a/src/lib.rs b/src/lib.rs index 8b5c921..7ef765c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ pub mod delay; pub mod gpio; pub mod i2c; pub mod prelude; +pub mod pwm; pub mod rcc; pub mod serial; pub mod spi; diff --git a/src/pwm.rs b/src/pwm.rs new file mode 100644 index 0000000..3d554ec --- /dev/null +++ b/src/pwm.rs @@ -0,0 +1,256 @@ +use core::marker::PhantomData; +use core::mem; + +#[cfg(any(feature = "stm32f042"))] +use crate::stm32::TIM2; + +use cast::{u16, u32}; +use embedded_hal as hal; + +use crate::gpio::{self, gpioa, gpiob, Alternate}; +use crate::rcc::Clocks; +use crate::time::Hertz; + +pub trait CH1 {} +pub trait CH2 {} +pub trait CH3 {} +pub trait CH4 {} + +macro_rules! pwm_pins { + ($($TIM:ident => { + ch1 => [$($ch1:ty),+ $(,)*], + ch2 => [$($ch2:ty),+ $(,)*], + ch3 => [$($ch3:ty),+ $(,)*], + ch4 => [$($ch4:ty),+ $(,)*], + })+) => { + $( + $( + impl CH1 for $ch1 {} + impl Pins<$TIM> for $ch1 { + const C1: bool = true; + const C2: bool = false; + const C3: bool = false; + const C4: bool = false; + type Channels = Pwm<$TIM, C1>; + } + )+ + $( + impl CH2 for $ch2 {} + impl Pins<$TIM> for $ch2 { + const C1: bool = false; + const C2: bool = true; + const C3: bool = false; + const C4: bool = false; + type Channels = Pwm<$TIM, C2>; + } + )+ + $( + impl CH3 for $ch3 {} + impl Pins<$TIM> for $ch3 { + const C1: bool = false; + const C2: bool = false; + const C3: bool = true; + const C4: bool = false; + type Channels = Pwm<$TIM, C3>; + } + )+ + $( + impl CH4 for $ch4 {} + impl Pins<$TIM> for $ch4 { + const C1: bool = false; + const C2: bool = false; + const C3: bool = false; + const C4: bool = true; + type Channels = Pwm<$TIM, C4>; + } + )+ + )+ + } +} + +#[cfg(any(feature = "stm32f042"))] +pwm_pins! { + TIM2 => { + ch1 => [gpioa::PA0>, gpioa::PA5>, gpioa::PA15>], + ch2 => [gpioa::PA1>, gpiob::PB3>], + ch3 => [gpioa::PA2>, gpiob::PB10>], + ch4 => [gpioa::PA3>, gpiob::PB11>], + } +} + +pub trait Pins { + const C1: bool; + const C2: bool; + const C3: bool; + const C4: bool; + type Channels; +} + +pub struct Pwm { + _channel: PhantomData, + _tim: PhantomData, +} + +pub struct C1; +pub struct C2; +pub struct C3; +pub struct C4; + +#[allow(unused)] +macro_rules! tim { + ($($TIM:ident: ($tim:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { + $( + pub fn $tim(tim: $TIM, _pins: PINS, freq: Hertz, clocks: Clocks) -> PINS::Channels + where + PINS: Pins<$TIM>, + { + let rcc = unsafe { &(*crate::stm32::RCC::ptr()) }; + rcc.$apbenr.modify(|_, w| w.$timXen().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); + + if PINS::C1 { + tim.ccmr1_output + .modify(|_, w| unsafe { w.oc1pe().set_bit().oc1m().bits(6) }); + } + + if PINS::C2 { + tim.ccmr1_output + .modify(|_, w| unsafe { w.oc2pe().set_bit().oc2m().bits(6) }); + } + + if PINS::C3 { + tim.ccmr2_output + .modify(|_, w| unsafe { w.oc3pe().set_bit().oc3m().bits(6) }); + } + + if PINS::C4 { + tim.ccmr2_output + .modify(|_, w| unsafe { w.oc4pe().set_bit().oc4m().bits(6) }); + } + + let clk = clocks.pclk().0; + let freq = freq.0; + let ticks = clk / freq; + let psc = u16(ticks / (1 << 16)).unwrap(); + tim.psc.write(|w| unsafe { w.psc().bits(psc) }); + let arr = u16(ticks / u32(psc + 1)).unwrap(); + tim.arr.write(|w| w.arr().bits(arr as u32)); + + tim.cr1.write(|w| unsafe { + w.cms() + .bits(0b00) + .dir() + .clear_bit() + .opm() + .clear_bit() + .cen() + .set_bit() + }); + + unsafe { mem::uninitialized() } + } + + impl hal::PwmPin for Pwm<$TIM, C1> { + type Duty = u16; + + fn disable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc1e().clear_bit()) } + } + + fn enable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc1e().set_bit()) } + } + + fn get_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).ccr1.read().ccr1().bits() as u16 } + } + + fn get_max_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).arr.read().arr().bits() as u16 } + } + + fn set_duty(&mut self, duty: Self::Duty) { + unsafe { (*$TIM::ptr()).ccr1.write(|w| w.ccr1().bits(duty as u32)) } + } + } + + impl hal::PwmPin for Pwm<$TIM, C2> { + type Duty = u16; + + fn disable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc2e().clear_bit()) } + } + + fn enable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc2e().set_bit()) } + } + + fn get_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).ccr2.read().ccr2().bits() as u16 } + } + + fn get_max_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).arr.read().arr().bits() as u16 } + } + + fn set_duty(&mut self, duty: Self::Duty) { + unsafe { (*$TIM::ptr()).ccr2.write(|w| w.ccr2().bits(duty as u32)) } + } + } + + impl hal::PwmPin for Pwm<$TIM, C3> { + type Duty = u16; + + fn disable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc3e().clear_bit()) } + } + + fn enable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc3e().set_bit()) } + } + + fn get_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).ccr3.read().ccr3().bits() as u16 } + } + + fn get_max_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).arr.read().arr().bits() as u16 } + } + + fn set_duty(&mut self, duty: Self::Duty) { + unsafe { (*$TIM::ptr()).ccr3.write(|w| w.ccr3().bits(duty as u32)) } + } + } + + impl hal::PwmPin for Pwm<$TIM, C4> { + type Duty = u16; + + fn disable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc4e().clear_bit()) } + } + + fn enable(&mut self) { + unsafe { (*$TIM::ptr()).ccer.write(|w| w.cc4e().set_bit()) } + } + + fn get_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).ccr4.read().ccr4().bits() as u16 } + } + + fn get_max_duty(&self) -> Self::Duty { + unsafe { (*$TIM::ptr()).arr.read().arr().bits() as u16 } + } + + fn set_duty(&mut self, duty: Self::Duty) { + unsafe { (*$TIM::ptr()).ccr4.write(|w| w.ccr4().bits(duty as u32)) } + } + } + )+ + } +} + +#[cfg(any(feature = "stm32f042"))] +tim! { + TIM2: (tim2, tim2en, tim2rst, apb1enr, apb1rstr), +}