diff --git a/CHANGELOG.md b/CHANGELOG.md index 655c7b10..c59295c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Breaking changes -- Relax pin type generics for `Serial`, `I2c`, `Spi`, `Can`. [#462] +- Relax pin type generics for `Serial`, `I2c`, `Spi`, `Can`, `Qei`, `PwmInput`. [#462] [#516] ~~Use enums of pin tuples and `Enum::from<(tuple)>` for pin remap before passing to peripheral.~~ Use pin enums and `impl RInto<(enum), R>` for peripheral constructors. Add `RInto` trait and `Rmp` peripheral wrapper, add `remap` for peripherals. [#514] @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Take `&Clocks` instead of `Clocks` [#498] - Temporary replace `stm32f1` with `stm32f1-staging` [#503] - `Spi` now takes `Option` for `SCK`, `MISO`, `MOSI` [#514] +- move `Qei` mod inside `pwm_input` mod [#516] ### Changed @@ -66,6 +67,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#510]: https://github.com/stm32-rs/stm32f1xx-hal/pull/510 [#511]: https://github.com/stm32-rs/stm32f1xx-hal/pull/511 [#514]: https://github.com/stm32-rs/stm32f1xx-hal/pull/514 +[#516]: https://github.com/stm32-rs/stm32f1xx-hal/pull/516 ## [v0.10.0] - 2022-12-12 diff --git a/examples/pwm_input.rs b/examples/pwm_input.rs index 7e34230f..e3987f0a 100644 --- a/examples/pwm_input.rs +++ b/examples/pwm_input.rs @@ -7,11 +7,7 @@ use panic_halt as _; use cortex_m_rt::entry; -use stm32f1xx_hal::{ - pac, - prelude::*, - timer::{pwm_input::*, Timer}, -}; +use stm32f1xx_hal::{pac, prelude::*, timer::pwm_input::*}; #[entry] fn main() -> ! { @@ -31,11 +27,11 @@ fn main() -> ! { let (_pa15, _pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); let pb5 = gpiob.pb5; - let pwm_input = Timer::new(p.TIM3, &clocks).pwm_input( + let pwm_input = p.TIM3.remap(&mut afio.mapr).pwm_input( (pb4, pb5), - &mut afio.mapr, &mut dbg, Configuration::Frequency(10.kHz()), + &clocks, ); loop { diff --git a/examples/qei.rs b/examples/qei.rs index 766e6f74..266afd3b 100644 --- a/examples/qei.rs +++ b/examples/qei.rs @@ -9,7 +9,11 @@ use panic_semihosting as _; use cortex_m_semihosting::hprintln; use cortex_m_rt::entry; -use stm32f1xx_hal::{pac, prelude::*, qei::QeiOptions, timer::Timer}; +use stm32f1xx_hal::{ + pac, + prelude::*, + timer::{pwm_input::QeiOptions, Timer}, +}; #[entry] fn main() -> ! { @@ -21,8 +25,6 @@ fn main() -> ! { let clocks = rcc.cfgr.freeze(&mut flash.acr); - let mut afio = dp.AFIO.constrain(); - // let gpioa = dp.GPIOA.split(); let gpiob = dp.GPIOB.split(); @@ -38,7 +40,7 @@ fn main() -> ! { let c1 = gpiob.pb6; let c2 = gpiob.pb7; - let qei = Timer::new(dp.TIM4, &clocks).qei((c1, c2), &mut afio.mapr, QeiOptions::default()); + let qei = Timer::new(dp.TIM4, &clocks).qei((c1, c2), QeiOptions::default()); let mut delay = cp.SYST.delay(&clocks); loop { diff --git a/src/lib.rs b/src/lib.rs index c7877bbf..35e1bbdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,6 @@ pub mod flash; pub mod gpio; pub mod i2c; pub mod prelude; -pub mod qei; pub mod rcc; pub mod rtc; pub mod serial; diff --git a/src/prelude.rs b/src/prelude.rs index 4a02e896..65c7f48e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -20,6 +20,8 @@ pub use crate::rcc::RccExt as _stm32_hal_rcc_RccExt; pub use crate::serial::SerialExt as _; pub use crate::spi::SpiExt as _; pub use crate::time::U32Ext as _stm32_hal_time_U32Ext; +pub use crate::timer::pwm_input::PwmInputExt as _; +pub use crate::timer::pwm_input::QeiExt as _; #[cfg(feature = "rtic")] pub use crate::timer::MonoTimerExt as _stm32f4xx_hal_timer_MonoTimerExt; pub use crate::timer::PwmExt as _stm32f4xx_hal_timer_PwmExt; diff --git a/src/qei.rs b/src/qei.rs deleted file mode 100644 index a79c75af..00000000 --- a/src/qei.rs +++ /dev/null @@ -1,201 +0,0 @@ -/** - # Quadrature Encoder Interface - - NOTE: In some cases you need to specify remap you need, especially for TIM2 - (see [Alternate function remapping](super::timer)): -*/ -use core::marker::PhantomData; - -use crate::pac; -use embedded_hal_02 as hal; -pub use hal::Direction; - -use crate::afio::MAPR; - -use crate::timer::pwm_input::Pins; -use crate::timer::{pins::sealed::Remap, Timer}; - -/// SMS (Slave Mode Selection) register -#[derive(Copy, Clone, Debug)] -pub enum SlaveMode { - /// Counter counts up/down on TI2FP1 edge depending on TI1FP2 level. - EncoderMode1 = 0b001, - /// Encoder mode 2 - Counter counts up/down on TI1FP2 edge depending on TI2FP1 level. - EncoderMode2 = 0b010, - /// Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the - /// level of the other input. - EncoderMode3 = 0b011, - /// Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter and - /// generates an update of the registers. - ResetMode = 0b100, - /// Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not - /// reset). Only the start of the counter is controlled. - TriggerMode = 0b110, - /// External Clock Mode 1 - Rising edges of the selected trigger (TRGI) clock the counter. - ExternalClockMode1 = 0b111, -} - -/// Quadrature Encoder Interface (QEI) options -/// -/// The `Default` implementation provides a configuration for a 4-count pulse which counts from -/// 0-65535. The counter wraps back to 0 on overflow. -#[derive(Copy, Clone, Debug)] -pub struct QeiOptions { - /// Encoder slave mode - pub slave_mode: SlaveMode, - - /// Autoreload value - /// - /// This value allows the maximum count to be configured, up to 65535. Setting a lower value - /// will overflow the counter to 0 sooner. - pub auto_reload_value: u16, -} - -impl Default for QeiOptions { - fn default() -> Self { - Self { - slave_mode: SlaveMode::EncoderMode3, - auto_reload_value: u16::MAX, - } - } -} - -pub struct Qei { - tim: TIM, - pins: PINS, - _remap: PhantomData, -} - -#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -impl Timer { - pub fn qei( - self, - pins: PINS, - mapr: &mut MAPR, - options: QeiOptions, - ) -> Qei - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| unsafe { w.tim1_remap().bits(REMAP::REMAP) }); - - let Self { tim, clk: _ } = self; - Qei::_tim1(tim, pins, options) - } -} - -impl Timer { - pub fn qei( - self, - pins: PINS, - mapr: &mut MAPR, - options: QeiOptions, - ) -> Qei - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(REMAP::REMAP) }); - - let Self { tim, clk: _ } = self; - Qei::_tim2(tim, pins, options) - } -} - -impl Timer { - pub fn qei( - self, - pins: PINS, - mapr: &mut MAPR, - options: QeiOptions, - ) -> Qei - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(REMAP::REMAP) }); - - let Self { tim, clk: _ } = self; - Qei::_tim3(tim, pins, options) - } -} - -#[cfg(feature = "medium")] -impl Timer { - pub fn qei( - self, - pins: PINS, - mapr: &mut MAPR, - options: QeiOptions, - ) -> Qei - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.tim4_remap().bit(REMAP::REMAP == 1)); - - let Self { tim, clk: _ } = self; - Qei::_tim4(tim, pins, options) - } -} - -macro_rules! hal { - ($TIMX:ty: $timX:ident, $timXen:ident, $timXrst:ident) => { - impl Qei<$TIMX, REMAP, PINS> { - fn $timX(tim: $TIMX, pins: PINS, options: QeiOptions) -> Self { - // Configure TxC1 and TxC2 as captures - tim.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2()); - - // enable and configure to capture on rising edge - tim.ccer().write(|w| { - w.cc1e().set_bit(); - w.cc1p().clear_bit(); - w.cc2e().set_bit(); - w.cc2p().clear_bit() - }); - - // configure as quadrature encoder - tim.smcr().write(|w| w.sms().set(options.slave_mode as u8)); - - tim.arr().write(|w| w.arr().set(options.auto_reload_value)); - tim.cr1().write(|w| w.cen().set_bit()); - - Qei { - tim, - pins, - _remap: PhantomData, - } - } - - pub fn release(self) -> ($TIMX, PINS) { - (self.tim, self.pins) - } - } - - impl hal::Qei for Qei<$TIMX, REMAP, PINS> { - type Count = u16; - - fn count(&self) -> u16 { - self.tim.cnt().read().cnt().bits() - } - - fn direction(&self) -> Direction { - if self.tim.cr1().read().dir().bit_is_clear() { - Direction::Upcounting - } else { - Direction::Downcounting - } - } - } - }; -} - -#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -hal!(pac::TIM1: _tim1, tim1en, tim1rst); - -hal!(pac::TIM2: _tim2, tim2en, tim2rst); -hal!(pac::TIM3: _tim3, tim3en, tim3rst); - -#[cfg(feature = "medium")] -hal!(pac::TIM4: _tim4, tim4en, tim4rst); diff --git a/src/timer/pwm_input.rs b/src/timer/pwm_input.rs index 78bb7c42..df0d7d7a 100644 --- a/src/timer/pwm_input.rs +++ b/src/timer/pwm_input.rs @@ -1,34 +1,32 @@ //! This module allows Timer peripherals to be configured as pwm input. //! In this mode, the timer sample a squared signal to find it's frequency and duty cycle. - -use core::marker::PhantomData; -use core::mem; +//! +//! Also this module provides Quadrature Encoder Interface use crate::pac::{self, DBGMCU as DBG}; -use crate::afio::MAPR; -use crate::gpio::{self, Input}; +use crate::afio::{RInto, Rmp, TimC}; use crate::rcc::{BusTimerClock, Clocks}; use crate::time::Hertz; -use crate::timer::Timer; +use crate::timer::{General, Timer}; -pub trait Pins {} +use embedded_hal_02 as hal; +pub use hal::Direction; -use super::pins::{sealed::Remap, CPin}; +pub trait Instance: General + TimC<0> + TimC<1> {} -impl Pins for (P1, P2) -where - REMAP: Remap, - P1: CPin + gpio::PinExt>, - P2: CPin + gpio::PinExt>, -{ -} +#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] +impl Instance for pac::TIM1 {} +impl Instance for pac::TIM2 {} +impl Instance for pac::TIM3 {} +#[cfg(feature = "medium")] +impl Instance for pac::TIM4 {} /// PWM Input -pub struct PwmInput { - _timer: PhantomData, - _remap: PhantomData, - _pins: PhantomData, +#[allow(unused)] +pub struct PwmInput { + timer: TIM, + pins: (>::In, >::In), } /// How the data is read from the timer @@ -74,82 +72,80 @@ pub enum Configuration { RawValues { arr: u16, presc: u16 }, } -#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -impl Timer { - pub fn pwm_input( - mut self, - pins: PINS, - mapr: &mut MAPR, - dbg: &mut DBG, - mode: Configuration, - ) -> PwmInput - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - self.stop_in_debug(dbg, false); - let Self { tim, clk } = self; - tim1(tim, pins, clk, mode) - } +/// SMS (Slave Mode Selection) register +#[derive(Copy, Clone, Debug)] +pub enum SlaveMode { + /// Counter counts up/down on TI2FP1 edge depending on TI1FP2 level. + EncoderMode1 = 0b001, + /// Encoder mode 2 - Counter counts up/down on TI1FP2 edge depending on TI2FP1 level. + EncoderMode2 = 0b010, + /// Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the + /// level of the other input. + EncoderMode3 = 0b011, + /// Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter and + /// generates an update of the registers. + ResetMode = 0b100, + /// Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not + /// reset). Only the start of the counter is controlled. + TriggerMode = 0b110, + /// External Clock Mode 1 - Rising edges of the selected trigger (TRGI) clock the counter. + ExternalClockMode1 = 0b111, } -impl Timer { - pub fn pwm_input( - mut self, - pins: PINS, - mapr: &mut MAPR, - dbg: &mut DBG, - mode: Configuration, - ) -> PwmInput - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - self.stop_in_debug(dbg, false); - let Self { tim, clk } = self; - tim2(tim, pins, clk, mode) - } +/// Quadrature Encoder Interface (QEI) options +/// +/// The `Default` implementation provides a configuration for a 4-count pulse which counts from +/// 0-65535. The counter wraps back to 0 on overflow. +#[derive(Copy, Clone, Debug)] +pub struct QeiOptions { + /// Encoder slave mode + pub slave_mode: SlaveMode, + + /// Autoreload value + /// + /// This value allows the maximum count to be configured, up to 65535. Setting a lower value + /// will overflow the counter to 0 sooner. + pub auto_reload_value: u16, } -impl Timer { - pub fn pwm_input( - mut self, - pins: PINS, - mapr: &mut MAPR, - dbg: &mut DBG, - mode: Configuration, - ) -> PwmInput - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - self.stop_in_debug(dbg, false); - let Self { tim, clk } = self; - tim3(tim, pins, clk, mode) +impl Default for QeiOptions { + fn default() -> Self { + Self { + slave_mode: SlaveMode::EncoderMode3, + auto_reload_value: u16::MAX, + } } } -#[cfg(feature = "medium")] -impl Timer { - pub fn pwm_input( - mut self, - pins: PINS, - mapr: &mut MAPR, +/// Quadrature Encoder Interface (QEI) +pub struct Qei { + tim: TIM, + pins: (>::In, >::In), +} + +pub trait PwmInputExt: Sized + Instance { + fn pwm_input( + self, + pins: ( + impl RInto<>::In, 0>, + impl RInto<>::In, 0>, + ), dbg: &mut DBG, mode: Configuration, - ) -> PwmInput - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - self.stop_in_debug(dbg, false); - let Self { tim, clk } = self; - tim4(tim, pins, clk, mode) - } + clocks: &Clocks, + ) -> PwmInput; +} + +pub trait QeiExt: Sized + Instance { + fn qei( + self, + pins: ( + impl RInto<>::In, 0>, + impl RInto<>::In, 0>, + ), + options: QeiOptions, + clocks: &Clocks, + ) -> Qei; } /// Courtesy of @TeXitoi (https://github.com/stm32-rs/stm32f1xx-hal/pull/10#discussion_r259535503) @@ -162,17 +158,102 @@ fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) { (core::cmp::max(1, arr as u16), presc as u16) } macro_rules! hal { - ($TIMX:ty: $timX:ident) => { - fn $timX( - tim: $TIMX, - _pins: PINS, + ($TIM:ty: $timX:ident, $qeix:ident) => { + impl Timer<$TIM> { + pub fn pwm_input( + mut self, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, 0>, + impl RInto<<$TIM as TimC<1>>::In, 0>, + ), + dbg: &mut DBG, + mode: Configuration, + ) -> PwmInput<$TIM> { + self.stop_in_debug(dbg, false); + let Self { tim, clk } = self; + $timX(tim, pins, clk, mode) + } + pub fn qei( + self, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, 0>, + impl RInto<<$TIM as TimC<1>>::In, 0>, + ), + options: QeiOptions, + ) -> Qei<$TIM> { + let Self { tim, clk: _ } = self; + $qeix(tim, pins, options) + } + } + impl PwmInputExt for $TIM { + fn pwm_input( + self, + pins: ( + impl RInto<>::In, 0>, + impl RInto<>::In, 0>, + ), + dbg: &mut DBG, + mode: Configuration, + clocks: &Clocks, + ) -> PwmInput { + Timer::new(self, clocks).pwm_input(pins, dbg, mode) + } + } + impl QeiExt for $TIM { + fn qei( + self, + pins: ( + impl RInto<>::In, 0>, + impl RInto<>::In, 0>, + ), + options: QeiOptions, + clocks: &Clocks, + ) -> Qei { + Timer::new(self, clocks).qei(pins, options) + } + } + + impl Rmp<$TIM, R> { + pub fn pwm_input( + self, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, R>, + impl RInto<<$TIM as TimC<1>>::In, R>, + ), + dbg: &mut DBG, + mode: Configuration, + clocks: &Clocks, + ) -> PwmInput<$TIM> { + let mut tim = Timer::new(self.0, clocks); + tim.stop_in_debug(dbg, false); + let Timer { tim, clk } = tim; + $timX(tim, pins, clk, mode) + } + pub fn qei( + self, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, R>, + impl RInto<<$TIM as TimC<1>>::In, R>, + ), + options: QeiOptions, + clocks: &Clocks, + ) -> Qei<$TIM> { + let Timer { tim, clk: _ } = Timer::new(self.0, clocks); + $qeix(tim, pins, options) + } + } + + fn $timX( + tim: $TIM, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, R>, + impl RInto<<$TIM as TimC<1>>::In, R>, + ), clk: Hertz, mode: Configuration, - ) -> PwmInput<$TIMX, REMAP, PINS> - where - REMAP: Remap, - PINS: Pins, - { + ) -> PwmInput<$TIM> { + let pins = (pins.0.rinto(), pins.1.rinto()); + use Configuration::*; // Disable capture on both channels during setting // (for Channel X bit is CCXE) @@ -235,22 +316,18 @@ macro_rules! hal { .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit()); tim.cr1().modify(|_, w| w.cen().set_bit()); - unsafe { mem::MaybeUninit::uninit().assume_init() } + PwmInput { timer: tim, pins } } - impl PwmInput<$TIMX, REMAP, PINS> - where - REMAP: Remap, - PINS: Pins, - { + impl PwmInput<$TIM> { /// Return the frequency sampled by the timer pub fn read_frequency(&self, mode: ReadMode, clocks: &Clocks) -> Result { if let ReadMode::WaitForNextCapture = mode { self.wait_for_capture(); } - let presc = unsafe { (*<$TIMX>::ptr()).psc().read().bits() as u16 }; - let ccr1 = unsafe { (*<$TIMX>::ptr()).ccr1().read().bits() as u16 }; + let presc = unsafe { (*<$TIM>::ptr()).psc().read().bits() as u16 }; + let ccr1 = unsafe { (*<$TIM>::ptr()).ccr1().read().bits() as u16 }; // Formulas : // @@ -269,7 +346,7 @@ macro_rules! hal { if ccr1 == 0 { Err(Error::FrequencyTooLow) } else { - let clk = <$TIMX>::timer_clock(&clocks); + let clk = <$TIM>::timer_clock(&clocks); Ok(clk / ((presc + 1) as u32 * (ccr1 + 1) as u32)) } } @@ -282,8 +359,8 @@ macro_rules! hal { // Formulas : // Duty_cycle = (CCR2+1)/(CCR1+1) - let ccr1 = unsafe { (*<$TIMX>::ptr()).ccr1().read().bits() as u16 }; - let ccr2 = unsafe { (*<$TIMX>::ptr()).ccr2().read().bits() as u16 }; + let ccr1 = unsafe { (*<$TIM>::ptr()).ccr1().read().bits() as u16 }; + let ccr2 = unsafe { (*<$TIM>::ptr()).ccr2().read().bits() as u16 }; if ccr1 == 0 { Err(Error::FrequencyTooLow) } else { @@ -293,22 +370,77 @@ macro_rules! hal { /// Wait until the timer has captured a period fn wait_for_capture(&self) { - unsafe { &(*<$TIMX>::ptr()) }.sr().write(|w| { + unsafe { &(*<$TIM>::ptr()) }.sr().write(|w| { w.uif().clear_bit(); w.cc1if().clear_bit(); w.cc1of().clear_bit() }); - while unsafe { (*<$TIMX>::ptr()).sr().read().cc1if().bit_is_clear() } {} + while unsafe { (*<$TIM>::ptr()).sr().read().cc1if().bit_is_clear() } {} + } + + pub fn release(self) -> ($TIM, (<$TIM as TimC<0>>::In, <$TIM as TimC<1>>::In)) { + (self.timer, self.pins) + } + } + + fn $qeix( + tim: $TIM, + pins: ( + impl RInto<<$TIM as TimC<0>>::In, R>, + impl RInto<<$TIM as TimC<1>>::In, R>, + ), + options: QeiOptions, + ) -> Qei<$TIM> { + let pins = (pins.0.rinto(), pins.1.rinto()); + // Configure TxC1 and TxC2 as captures + tim.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2()); + + // enable and configure to capture on rising edge + tim.ccer().write(|w| { + w.cc1e().set_bit(); + w.cc1p().clear_bit(); + w.cc2e().set_bit(); + w.cc2p().clear_bit() + }); + + // configure as quadrature encoder + tim.smcr().write(|w| w.sms().set(options.slave_mode as u8)); + + tim.arr().write(|w| w.arr().set(options.auto_reload_value)); + tim.cr1().write(|w| w.cen().set_bit()); + + Qei { tim, pins } + } + + impl Qei<$TIM> { + pub fn release(self) -> ($TIM, (<$TIM as TimC<0>>::In, <$TIM as TimC<1>>::In)) { + (self.tim, self.pins) + } + } + + impl hal::Qei for Qei<$TIM> { + type Count = u16; + + fn count(&self) -> u16 { + self.tim.cnt().read().cnt().bits() + } + + fn direction(&self) -> Direction { + if self.tim.cr1().read().dir().bit_is_clear() { + Direction::Upcounting + } else { + Direction::Downcounting + } } } }; } #[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -hal!(pac::TIM1: tim1); +hal!(pac::TIM1: tim1, qei1); -hal!(pac::TIM2: tim2); -hal!(pac::TIM3: tim3); +hal!(pac::TIM2: tim2, qei2); +hal!(pac::TIM3: tim3, qei3); #[cfg(feature = "medium")] -hal!(pac::TIM4: tim4); +hal!(pac::TIM4: tim4, qei4);