use num_traits::float::Float;
use cortex_m::interrupt::free;
#[cfg(feature = "embedded-hal")]
use embedded_hal::{
blocking::delay::{DelayMs, DelayUs},
timer::{CountDown, Periodic},
};
#[cfg(feature = "embedded-hal")]
use void::Void;
use crate::{
clocks::Clocks,
pac::{self, RCC},
rcc_en_reset,
};
use cfg_if::cfg_if;
use paste::paste;
#[derive(Clone, Copy, Debug)]
pub struct ValueError {}
pub struct Timer<TIM> {
clock_speed: u32, tim: TIM, }
pub enum TimerInterrupt {
Update,
Trigger,
CaptureCompare1,
CaptureCompare2,
CaptureCompare3,
CaptureCompare4,
UpdateDma,
TriggerDma,
CaptureCompare1Dma,
CaptureCompare2Dma,
CaptureCompare3Dma,
CaptureCompare4Dma,
}
#[derive(Clone, Copy)]
pub enum Alignment {
Edge,
Center1,
Center2,
Center3,
}
#[derive(Clone, Copy)]
pub enum TimChannel {
C1,
C2,
C3,
C4,
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum CountDir {
Up = 0,
Down = 1,
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum CaptureCompare {
Output = 0b00,
InputTi1 = 0b01,
InputTi2 = 0b10,
InputTrc = 0b11,
}
#[derive(Clone, Copy)]
pub enum Polarity {
ActiveHigh,
ActiveLow,
}
impl Polarity {
fn bit(&self) -> bool {
match self {
Self::ActiveHigh => false,
Self::ActiveLow => true,
}
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum OutputCompare {
Frozen = 0b0000,
Active = 0b0001,
Inactive = 0b0010,
ForceInactive = 0b0100,
ForceActive = 0b0101,
Pwm1 = 0b0110,
Pwm2 = 0b0111,
RetriggerableOpmMode1 = 0b1000,
RetriggerableOpmMode2 = 0b1001,
CombinedPwm1 = 0b1100,
CombinedPwm2 = 0b1101,
AsymmetricPwm1 = 0b1110,
AsymmetricPwm2 = 0b1111,
}
impl OutputCompare {
pub fn left_bit(&self) -> bool {
matches!(
self,
Self::RetriggerableOpmMode1
| Self::RetriggerableOpmMode2
| Self::CombinedPwm1
| Self::CombinedPwm2
| Self::AsymmetricPwm1
| Self::AsymmetricPwm2
)
}
}
macro_rules! hal {
($TIMX:ident, $tim:ident, $apb:expr) => {
impl Timer<pac::$TIMX> {
paste! {
pub fn [<new_ $tim>](tim: pac::$TIMX, freq: f32, clocks: &Clocks) -> Self {
free(|_| {
let rcc = unsafe { &(*RCC::ptr()) };
rcc_en_reset!([<apb $apb>], $tim, rcc);
});
let clock_speed = match $apb {
1 => clocks.apb1_timer(),
_ => clocks.apb2_timer(),
};
let mut timer = Timer { clock_speed, tim };
timer.set_freq(freq).ok();
timer.tim.egr.write(|w| w.ug().set_bit());
timer.clear_interrupt(TimerInterrupt::Update);
timer
}
}
pub fn enable_interrupt(&mut self, interrupt: TimerInterrupt) {
match interrupt {
TimerInterrupt::Update => self.tim.dier.modify(|_, w| w.uie().set_bit()),
_ => unimplemented!("TODO TEMP PROBLEMS"),
}
}
pub fn clear_interrupt(&mut self, interrupt: TimerInterrupt) {
match interrupt {
TimerInterrupt::Update => self.tim.sr.modify(|_, w| w.uif().clear_bit()),
_ => unimplemented!("Clearing DMA flags is unimplemented using this function."),
}
}
pub fn enable(&mut self) {
self.tim.cr1.modify(|_, w| w.cen().set_bit());
}
pub fn disable(&mut self) {
self.tim.cr1.modify(|_, w| w.cen().clear_bit());
}
pub fn is_enabled(&self) -> bool {
self.tim.cr1.read().cen().bit_is_set()
}
pub fn set_freq(&mut self, freq: f32) -> Result<(), ValueError> {
let (psc, arr) = calc_freq_vals(freq, self.clock_speed)?;
self.tim.arr.write(|w| unsafe { w.bits(arr.into()) });
self.tim.psc.write(|w| unsafe { w.bits(psc.into()) });
Ok(())
}
pub fn set_auto_reload(&mut self, arr: u32) {
self.tim.arr.write(|w| unsafe { w.bits(arr.into()) });
}
pub fn set_prescaler(&mut self, psc: u16) {
self.tim.psc.write(|w| unsafe { w.bits(psc.into()) });
}
pub fn reset_countdown(&mut self) {
self.tim.cnt.write(|w| unsafe { w.bits(0) });
}
pub fn countdown(&self) -> u32 {
self.tim.cnt.read().bits()
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayMs<u32> for Timer<pac::$TIMX> {
fn delay_ms(&mut self, ms: u32) {
self.delay_us(ms as u32 * 1_000);
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayMs<u16> for Timer<pac::$TIMX> {
fn delay_ms(&mut self, ms: u16) {
self.delay_us(ms as u32 * 1_000);
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayMs<u8> for Timer<pac::$TIMX> {
fn delay_ms(&mut self, ms: u8) {
self.delay_us(ms as u32 * 1_000);
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayUs<u32> for Timer<pac::$TIMX> {
fn delay_us(&mut self, us: u32) {
self.set_freq(1. / (us as f32 * 1_000.)).ok();
self.reset_countdown();
self.enable();
while self.countdown() != 0 {}
self.disable();
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayUs<u16> for Timer<pac::$TIMX> {
fn delay_us(&mut self, us: u16) {
self.delay_us(us as u32);
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl DelayUs<u8> for Timer<pac::$TIMX> {
fn delay_us(&mut self, us: u8) {
self.delay_us(us as u32);
}
}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl Periodic for Timer<pac::$TIMX> {}
#[cfg(feature = "embedded-hal")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-hal")))]
impl CountDown for Timer<pac::$TIMX> {
type Time = f32;
fn start<T>(&mut self, timeout: T)
where
T: Into<f32>, {
self.disable();
self.set_freq(timeout.into()).ok();
self.tim.egr.write(|w| w.ug().set_bit());
self.clear_interrupt(TimerInterrupt::Update);
self.enable();
}
fn wait(&mut self) -> nb::Result<(), Void> {
if self.tim.sr.read().uif().bit_is_clear() {
Err(nb::Error::WouldBlock)
} else {
self.clear_interrupt(TimerInterrupt::Update);
Ok(())
}
}
}
};
}
fn calc_freq_vals(freq: f32, clock_speed: u32) -> Result<(u16, u16), ValueError> {
let max_val = 65_535;
let rhs = clock_speed as f32 / freq;
let arr = rhs.sqrt().round() as u16 - 1;
let psc = arr;
if arr > max_val || psc > max_val {
return Err(ValueError {});
}
Ok((psc, arr))
}
macro_rules! pwm_features {
($TIMX:ident, $res:ident) => {
impl Timer<pac::$TIMX> {
pub fn enable_pwm_output(
&mut self,
channel: TimChannel,
compare: OutputCompare,
dir: CountDir,
duty: f32,
) {
self.set_preload(channel, true);
self.set_output_compare(channel, compare);
self.set_duty(channel, (self.get_max_duty() as f32 * duty) as $res);
self.tim.cr1.modify(|_, w| w.dir().bit(dir as u8 != 0));
self.enable_capture_compare(channel);
}
pub fn _enable_pwm_input(
&mut self,
channel: TimChannel,
compare: OutputCompare,
dir: CountDir,
duty: f32,
) {
}
pub fn set_output_compare(&mut self, channel: TimChannel, mode: OutputCompare) {
match channel {
TimChannel::C1 => {
self.tim
.ccmr1_output()
.modify(|_, w| unsafe { w.oc1m().bits(mode as u8) });
#[cfg(any(feature = "f302", feature = "f303"))]
self.tim
.ccmr1_output()
.modify(|_, w| w.oc1m_3().bit(mode.left_bit()));
}
TimChannel::C2 => {
self.tim
.ccmr1_output()
.modify(|_, w| unsafe { w.oc1m().bits(mode as u8) });
#[cfg(any(feature = "f302", feature = "f303"))] self.tim
.ccmr1_output()
.modify(|_, w| w.oc1m_3().bit(mode.left_bit()));
}
TimChannel::C3 => {
self.tim
.ccmr1_output()
.modify(|_, w| unsafe { w.oc1m().bits(mode as u8) });
#[cfg(any(feature = "f302", feature = "f303"))] self.tim
.ccmr1_output()
.modify(|_, w| w.oc1m_3().bit(mode.left_bit()));
}
TimChannel::C4 => {
self.tim
.ccmr2_output()
.modify(|_, w| unsafe { w.oc4m().bits(mode as u8) });
#[cfg(any(feature = "f302", feature = "f303"))] self.tim
.ccmr2_output()
.modify(|_, w| w.oc4m_3().bit(mode.left_bit()));
}
}
}
pub fn get_duty(&self, channel: TimChannel) -> $res {
cfg_if! {
if #[cfg(feature = "g0")] {
match channel {
TimChannel::C1 => self.tim.ccr1.read().bits(),
TimChannel::C2 => self.tim.ccr2.read().bits(),
TimChannel::C3 => self.tim.ccr3.read().bits(),
TimChannel::C4 => self.tim.ccr4.read().bits(),
}
} else if #[cfg(any(feature = "g4", feature = "wb", feature = "wl"))] {
match channel {
TimChannel::C1 => self.tim.ccr1.read().ccr1().bits(),
TimChannel::C2 => self.tim.ccr2.read().ccr2().bits(),
TimChannel::C3 => self.tim.ccr3.read().ccr3().bits(),
TimChannel::C4 => self.tim.ccr4.read().ccr4().bits(),
}
} else {
match channel {
TimChannel::C1 => self.tim.ccr1.read().ccr().bits(),
TimChannel::C2 => self.tim.ccr2.read().ccr().bits(),
TimChannel::C3 => self.tim.ccr3.read().ccr().bits(),
TimChannel::C4 => self.tim.ccr4.read().ccr().bits(),
}
}
}
}
pub fn set_duty(&mut self, channel: TimChannel, duty: $res) {
cfg_if! {
if #[cfg(feature = "g0")] {
match channel {
TimChannel::C1 => self.tim.ccr1.read().bits(),
TimChannel::C2 => self.tim.ccr2.read().bits(),
TimChannel::C3 => self.tim.ccr3.read().bits(),
TimChannel::C4 => self.tim.ccr4.read().bits(),
};
} else if #[cfg(any(feature = "g4", feature = "wb", feature = "wl"))] {
unsafe {
match channel {
TimChannel::C1 => self.tim.ccr1.write(|w| w.ccr1().bits(duty)),
TimChannel::C2 => self.tim.ccr2.write(|w| w.ccr2().bits(duty)),
TimChannel::C3 => self.tim.ccr3.write(|w| w.ccr3().bits(duty)),
TimChannel::C4 => self.tim.ccr4.write(|w| w.ccr4().bits(duty)),
}
}
} else {
match channel {
TimChannel::C1 => self.tim.ccr1.write(|w| w.ccr().bits(duty)),
TimChannel::C2 => self.tim.ccr2.write(|w| w.ccr().bits(duty)),
TimChannel::C3 => self.tim.ccr3.write(|w| w.ccr().bits(duty)),
TimChannel::C4 => self.tim.ccr4.write(|w| w.ccr().bits(duty)),
}
}
}
}
pub fn get_max_duty(&self) -> $res {
#[cfg(feature = "g0")]
return self.tim.arr.read().bits();
#[cfg(not(feature = "g0"))]
return self.tim.arr.read().arr().bits();
}
pub fn set_alignment(&mut self, alignment: Alignment) {
let word = match alignment {
Alignment::Edge => 0b00,
Alignment::Center1 => 0b01,
Alignment::Center2 => 0b10,
Alignment::Center3 => 0b11,
};
self.tim.cr1.modify(|_, w| unsafe { w.cms().bits(word) });
}
pub fn set_polarity(&mut self, channel: TimChannel, polarity: Polarity) {
match channel {
TimChannel::C1 => self.tim.ccer.modify(|_, w| w.cc1p().bit(polarity.bit())),
TimChannel::C2 => self.tim.ccer.modify(|_, w| w.cc2p().bit(polarity.bit())),
TimChannel::C3 => self.tim.ccer.modify(|_, w| w.cc3p().bit(polarity.bit())),
TimChannel::C4 => self.tim.ccer.modify(|_, w| w.cc4p().bit(polarity.bit())),
}
}
pub fn set_complementary_polarity(&mut self, channel: TimChannel, polarity: Polarity) {
match channel {
TimChannel::C1 => self.tim.ccer.modify(|_, w| w.cc1np().bit(polarity.bit())),
TimChannel::C2 => self.tim.ccer.modify(|_, w| w.cc2np().bit(polarity.bit())),
TimChannel::C3 => self.tim.ccer.modify(|_, w| w.cc3np().bit(polarity.bit())),
TimChannel::C4 => self.tim.ccer.modify(|_, w| w.cc4np().bit(polarity.bit())),
}
}
pub fn disable_capture_compare(&mut self, channel: TimChannel) {
match channel {
TimChannel::C1 => self.tim.ccer.modify(|_, w| w.cc1e().clear_bit()),
TimChannel::C2 => self.tim.ccer.modify(|_, w| w.cc2e().clear_bit()),
TimChannel::C3 => self.tim.ccer.modify(|_, w| w.cc3e().clear_bit()),
TimChannel::C4 => self.tim.ccer.modify(|_, w| w.cc4e().clear_bit()),
}
}
pub fn enable_capture_compare(&mut self, channel: TimChannel) {
match channel {
TimChannel::C1 => self.tim.ccer.modify(|_, w| w.cc1e().set_bit()),
TimChannel::C2 => self.tim.ccer.modify(|_, w| w.cc2e().set_bit()),
TimChannel::C3 => self.tim.ccer.modify(|_, w| w.cc3e().set_bit()),
TimChannel::C4 => self.tim.ccer.modify(|_, w| w.cc4e().set_bit()),
}
}
pub fn set_capture_compare(&mut self, channel: TimChannel, mode: CaptureCompare) {
match channel {
TimChannel::C1 => self
.tim
.ccmr1_output()
.modify(unsafe { |_, w| w.cc1s().bits(mode as u8) }),
TimChannel::C2 => self
.tim
.ccmr1_output()
.modify(unsafe { |_, w| w.cc2s().bits(mode as u8) }),
TimChannel::C3 => self
.tim
.ccmr2_output()
.modify(unsafe { |_, w| w.cc3s().bits(mode as u8) }),
TimChannel::C4 => self
.tim
.ccmr2_output()
.modify(unsafe { |_, w| w.cc4s().bits(mode as u8) }),
}
}
pub fn set_auto_reload_preload(&mut self, mode: bool) {
self.tim.cr1.modify(|_, w| w.arpe().bit(mode));
}
pub fn set_preload(&mut self, channel: TimChannel, value: bool) {
match channel {
TimChannel::C1 => self.tim.ccmr1_output().modify(|_, w| w.oc1pe().bit(value)),
TimChannel::C2 => self.tim.ccmr1_output().modify(|_, w| w.oc2pe().bit(value)),
TimChannel::C3 => self.tim.ccmr2_output().modify(|_, w| w.oc3pe().bit(value)),
TimChannel::C4 => self.tim.ccmr2_output().modify(|_, w| w.oc4pe().bit(value)),
}
self.tim.egr.write(|w| w.ug().set_bit()); }
}
};
}
#[cfg(not(any(feature = "f373")))]
hal!(TIM1, tim1, 2);
#[cfg(not(any(
feature = "f373",
feature = "f4",
feature = "l4",
feature = "l5",
feature = "g0",
feature = "wl", // todo temp!
)))]
pwm_features!(TIM1, u16);
#[cfg(any(feature = "g0"))]
pwm_features!(TIM1, u32);
cfg_if! {
if #[cfg(not(any(
feature = "f410",
feature = "g070",
)))] {
hal!(TIM2, tim2, 1);
}
}
#[cfg(feature = "g4")]
pwm_features!(TIM2, u16);
#[cfg(not(any(
feature = "l5",
feature = "g070",
feature = "g4",
feature = "f410",
feature = "wb",
feature = "wl"
)))]
pwm_features!(TIM2, u32);
#[cfg(not(any(
feature = "f301",
feature = "l4x1",
feature = "l412",
feature = "l4x3",
feature = "f410",
feature = "wb",
feature = "wl"
)))]
hal!(TIM3, tim3, 1);
#[cfg(not(any(
feature = "l4x1",
feature = "l4x3",
feature = "l5",
feature = "f410",
feature = "g0",
feature = "wb",
feature = "wl"
)))]
pwm_features!(TIM3, u16);
#[cfg(feature = "g0")]
pwm_features!(TIM3, u32);
cfg_if! {
if #[cfg(not(any(
feature = "f301",
feature = "f3x4",
feature = "f410",
feature = "l4x1",
feature = "l4x2",
feature = "l412",
feature = "l4x3",
feature = "l552",
feature = "g0",
feature = "wb",
feature = "wl"
)))] {
hal!(TIM4, tim4, 1);
}
}
cfg_if! {
if #[cfg(not(any(
feature = "f301",
feature = "f3x4",
feature = "f410",
feature = "l4x1",
feature = "l4x2",
feature = "l412",
feature = "l4x3",
feature = "l5",
feature = "g0",
feature = "wb",
feature = "wl"
)))] {
pwm_features!(TIM4, u16);
}
}
cfg_if! {
if #[cfg(any(
feature = "f373",
feature = "l4x5",
feature = "l4x6",
feature = "l562",
feature = "h7",
all(feature = "f4", not(feature = "f410")),
))] {
hal!(TIM5, tim5, 1);
}
}
cfg_if! {
if #[cfg(any(
feature = "f373",
feature = "l4x5",
feature = "l4x6",
feature = "h7",
all(feature = "f4", not(feature = "f410")),
))] {
pwm_features!(TIM5, u32);
}
}
cfg_if! {
if #[cfg(not(any(
feature = "f401",
feature = "f410",
feature = "f411",
feature = "g031",
feature = "g041",
feature = "g070",
feature = "g030",
feature = "wb",
feature = "wl"
)))] {
hal!(TIM6, tim6, 1);
}
}
#[cfg(not(any(
feature = "f301",
feature = "f302",
feature = "f401",
feature = "f410",
feature = "f411",
feature = "g031",
feature = "g041",
feature = "g030",
feature = "wb",
feature = "wl"
)))]
hal!(TIM7, tim7, 1);
#[cfg(any(
feature = "f303",
feature = "l4x5",
feature = "l4x6",
feature = "l562",
feature = "g4"
))]
hal!(TIM8, tim8, 2);
#[cfg(not(any(
feature = "l5",
feature = "f4",
feature = "g031",
feature = "g031",
feature = "g041",
feature = "g030",
feature = "wb",
feature = "wl"
)))]
hal!(TIM15, tim15, 2);
#[cfg(not(feature = "f4"))]
hal!(TIM16, tim16, 2);
cfg_if! {
if #[cfg(not(any(
feature = "l4x1",
feature = "l4x2",
feature = "l412",
feature = "l4x3",
feature = "f4",
)))] {
hal!(TIM17, tim17, 2);
}
}
cfg_if! {
if #[cfg(any(feature = "f373"))] {
hal!(TIM12, tim12, 1);
hal!(TIM13, tim13, 1);
hal!(TIM14, tim14, 1);
hal!(TIM19, tim19, 2);
}
}
#[cfg(any(feature = "f303"))]
hal!(TIM20, tim20, 2);