From 416ecc73d8211e348bf51a5cfe84075673b18963 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 29 Aug 2023 20:06:53 -0500 Subject: [PATCH 1/2] add qei draft --- embassy-stm32/src/timer/qei.rs | 142 +++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 embassy-stm32/src/timer/qei.rs diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs new file mode 100644 index 00000000..fa881472 --- /dev/null +++ b/embassy-stm32/src/timer/qei.rs @@ -0,0 +1,142 @@ +//! # Quadrature Encoder Interface +use crate::{ + gpio::PushPull, + pac, rcc, + timer::{CPin, General}, +}; + +pub trait QeiExt: Sized + Instance { + fn qei( + self, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + ) -> Qei; +} + +impl QeiExt for TIM { + fn qei( + self, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + ) -> Qei { + Qei::new(self, pins) + } +} + +/// Hardware quadrature encoder interface peripheral +pub struct Qei { + tim: TIM, + pins: ( + >::Ch, + >::Ch, + ), +} + +impl Qei { + /// Configures a TIM peripheral as a quadrature encoder interface input + pub fn new( + mut tim: TIM, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + ) -> Self { + // Enable and reset clock. + unsafe { + TIM::enable_unchecked(); + TIM::reset_unchecked(); + } + + let pins = (pins.0.into(), pins.1.into()); + tim.setup_qei(); + + Qei { tim, pins } + } + + /// Releases the TIM peripheral and QEI pins + #[allow(clippy::type_complexity)] + pub fn release( + self, + ) -> ( + TIM, + ( + >::Ch, + >::Ch, + ), + ) { + (self.tim, self.pins) + } + + /// Set current count number + pub fn set_count(&mut self, value: TIM::Width) -> &mut Self { + self.tim.write_count(value); + self + } +} + +impl embedded_hal::Qei for Qei { + type Count = TIM::Width; + + fn count(&self) -> Self::Count { + self.tim.read_count() + } + + fn direction(&self) -> embedded_hal::Direction { + if self.tim.read_direction() { + embedded_hal::Direction::Upcounting + } else { + embedded_hal::Direction::Downcounting + } + } +} + +pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + General + CPin<0> + CPin<1> { + fn setup_qei(&mut self); + + fn read_direction(&self) -> bool; +} + +macro_rules! hal { + ($TIM:ty) => { + impl Instance for $TIM { + fn setup_qei(&mut self) { + // Configure TxC1 and TxC2 as captures + #[cfg(not(feature = "gpio-f410"))] + self.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2()); + #[cfg(feature = "gpio-f410")] + self.ccmr1_input() + .write(|w| unsafe { w.cc1s().bits(0b01).cc2s().bits(0b01) }); + // enable and configure to capture on rising edge + self.ccer.write(|w| { + w.cc1e().set_bit().cc1p().clear_bit(); + w.cc2e().set_bit().cc2p().clear_bit() + }); + self.smcr.write(|w| w.sms().encoder_mode_3()); + self.set_auto_reload(<$TIM as General>::Width::MAX as u32) + .unwrap(); + self.cr1.write(|w| w.cen().set_bit()); + } + + fn read_direction(&self) -> bool { + self.cr1.read().dir().bit_is_clear() + } + } + }; +} + +#[cfg(feature = "tim1")] +hal! { pac::TIM1 } +#[cfg(feature = "tim2")] +hal! { pac::TIM2 } +#[cfg(feature = "tim3")] +hal! { pac::TIM3 } +#[cfg(feature = "tim4")] +hal! { pac::TIM4 } +#[cfg(feature = "tim5")] +hal! { pac::TIM5 } +#[cfg(feature = "tim8")] +hal! { pac::TIM8 } From c10fb7c1c4ca40749494f4873c2144906c2f1a17 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 30 Aug 2023 18:10:26 -0500 Subject: [PATCH 2/2] stm32: implement qei --- embassy-stm32/src/timer/mod.rs | 1 + embassy-stm32/src/timer/qei.rs | 216 +++++++++++++-------------------- 2 files changed, 86 insertions(+), 131 deletions(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 4ffb2a28..6036a456 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,4 +1,5 @@ pub mod complementary_pwm; +pub mod qei; pub mod simple_pwm; use stm32_metapac::timer::vals; diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index fa881472..c0f9288a 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -1,142 +1,96 @@ -//! # Quadrature Encoder Interface -use crate::{ - gpio::PushPull, - pac, rcc, - timer::{CPin, General}, -}; +use core::marker::PhantomData; -pub trait QeiExt: Sized + Instance { - fn qei( - self, - pins: ( - impl Into<>::Ch>, - impl Into<>::Ch>, - ), - ) -> Qei; +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::*; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::Peripheral; + +pub enum Direction { + Upcounting, + Downcounting, } -impl QeiExt for TIM { - fn qei( - self, - pins: ( - impl Into<>::Ch>, - impl Into<>::Ch>, - ), - ) -> Qei { - Qei::new(self, pins) - } +pub struct Ch1; +pub struct Ch2; + +pub struct QeiPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, } -/// Hardware quadrature encoder interface peripheral -pub struct Qei { - tim: TIM, - pins: ( - >::Ch, - >::Ch, - ), -} - -impl Qei { - /// Configures a TIM peripheral as a quadrature encoder interface input - pub fn new( - mut tim: TIM, - pins: ( - impl Into<>::Ch>, - impl Into<>::Ch>, - ), - ) -> Self { - // Enable and reset clock. - unsafe { - TIM::enable_unchecked(); - TIM::reset_unchecked(); - } - - let pins = (pins.0.into(), pins.1.into()); - tim.setup_qei(); - - Qei { tim, pins } - } - - /// Releases the TIM peripheral and QEI pins - #[allow(clippy::type_complexity)] - pub fn release( - self, - ) -> ( - TIM, - ( - >::Ch, - >::Ch, - ), - ) { - (self.tim, self.pins) - } - - /// Set current count number - pub fn set_count(&mut self, value: TIM::Width) -> &mut Self { - self.tim.write_count(value); - self - } -} - -impl embedded_hal::Qei for Qei { - type Count = TIM::Width; - - fn count(&self) -> Self::Count { - self.tim.read_count() - } - - fn direction(&self) -> embedded_hal::Direction { - if self.tim.read_direction() { - embedded_hal::Direction::Upcounting - } else { - embedded_hal::Direction::Downcounting - } - } -} - -pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + General + CPin<0> + CPin<1> { - fn setup_qei(&mut self); - - fn read_direction(&self) -> bool; -} - -macro_rules! hal { - ($TIM:ty) => { - impl Instance for $TIM { - fn setup_qei(&mut self) { - // Configure TxC1 and TxC2 as captures - #[cfg(not(feature = "gpio-f410"))] - self.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2()); - #[cfg(feature = "gpio-f410")] - self.ccmr1_input() - .write(|w| unsafe { w.cc1s().bits(0b01).cc2s().bits(0b01) }); - // enable and configure to capture on rising edge - self.ccer.write(|w| { - w.cc1e().set_bit().cc1p().clear_bit(); - w.cc2e().set_bit().cc2p().clear_bit() +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { + impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::Input); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); }); - self.smcr.write(|w| w.sms().encoder_mode_3()); - self.set_auto_reload(<$TIM as General>::Width::MAX as u32) - .unwrap(); - self.cr1.write(|w| w.cen().set_bit()); - } - - fn read_direction(&self) -> bool { - self.cr1.read().dir().bit_is_clear() + QeiPin { + _pin: pin.map_into(), + phantom: PhantomData, + } } } }; } -#[cfg(feature = "tim1")] -hal! { pac::TIM1 } -#[cfg(feature = "tim2")] -hal! { pac::TIM2 } -#[cfg(feature = "tim3")] -hal! { pac::TIM3 } -#[cfg(feature = "tim4")] -hal! { pac::TIM4 } -#[cfg(feature = "tim5")] -hal! { pac::TIM5 } -#[cfg(feature = "tim8")] -hal! { pac::TIM8 } +channel_impl!(new_ch1, Ch1, Channel1Pin); +channel_impl!(new_ch2, Ch2, Channel2Pin); + +pub struct SimplePwm<'d, T> { + _inner: PeripheralRef<'d, T>, +} + +impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { + pub fn new(tim: impl Peripheral

+ 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { + Self::new_inner(tim) + } + + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + // Configure TxC1 and TxC2 as captures + T::regs_gp16().ccmr_input(0).modify(|w| { + w.set_ccs(0, vals::CcmrInputCcs::TI4); + w.set_ccs(1, vals::CcmrInputCcs::TI4); + }); + + // enable and configure to capture on rising edge + T::regs_gp16().ccer().modify(|w| { + w.set_cce(0, true); + w.set_cce(1, true); + + w.set_ccp(0, false); + w.set_ccp(1, false); + }); + + T::regs_gp16().smcr().modify(|w| { + w.set_sms(vals::Sms::ENCODER_MODE_3); + }); + + T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX)); + T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + + Self { _inner: tim } + } + + pub fn read_direction(&self) -> Direction { + match T::regs_gp16().cr1().read().dir() { + vals::Dir::DOWN => Direction::Downcounting, + vals::Dir::UP => Direction::Upcounting, + } + } + + pub fn count(&self) -> u16 { + T::regs_gp16().cnt().read().cnt() + } +}