Skip to content

Commit 56a0294

Browse files
committed
[stm32] [timer] [qei] allow configuration of pull-up/down internal resistors on QeiPin
Refactor STM32 QEI to use Config struct to emulate UART peripheral and allow config of internal pull-up and -down, and enable setting encoder mode. Warn: Breaking API change So far as possible this is sympathetic to how I understood the UART module/peripheral. I kept things minimally configurable and future prood at the expense of a breaking API change by removing the QeiPin. I kept as much type discrimination code as I could, the specific generic traits about Channel1Pin and Channel2Pin from the timer parent module, and keeping owned references to the pins. I _removed_ some of the use of PhantomData as my naïve reading of the code leads me to believe it's not necessary now, and my breadboarded EC11 encoder toy works with all combinations of PU/PD and different counter modes. Thanks for taking the patch. boop
1 parent 9d94d68 commit 56a0294

File tree

1 file changed

+103
-30
lines changed

1 file changed

+103
-30
lines changed

embassy-stm32/src/timer/qei.rs

Lines changed: 103 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
//! Quadrature decoder using a timer.
22
3-
use core::marker::PhantomData;
4-
53
use embassy_hal_internal::{into_ref, PeripheralRef};
6-
use stm32_metapac::timer::vals;
4+
use stm32_metapac::timer::vals::{self, Sms};
75

86
use super::low_level::Timer;
97
use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel};
@@ -23,49 +21,120 @@ pub enum Ch1 {}
2321
/// Channel 2 marker type.
2422
pub enum Ch2 {}
2523

26-
/// Wrapper for using a pin with QEI.
27-
pub struct QeiPin<'d, T, Channel> {
28-
_pin: PeripheralRef<'d, AnyPin>,
29-
phantom: PhantomData<(T, Channel)>,
24+
#[doc = "See STMicro AN4013 for §2.3 for more information"]
25+
#[derive(Clone, Eq, PartialEq, Copy, Debug)]
26+
pub enum QeiMode {
27+
#[doc = "Direct alias for Sms::ENCODER_MODE_1"]
28+
Mode1,
29+
#[doc = "Direct alias for Sms::ENCODER_MODE_2"]
30+
Mode2,
31+
#[doc = "Direct alias for Sms::ENCODER_MODE_3"]
32+
Mode3,
3033
}
3134

32-
macro_rules! channel_impl {
33-
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
34-
impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> {
35-
#[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")]
36-
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
37-
into_ref!(pin);
38-
critical_section::with(|_| {
39-
pin.set_low();
40-
pin.set_as_af(pin.af_num(), AfType::input(Pull::None));
41-
});
42-
QeiPin {
43-
_pin: pin.map_into(),
44-
phantom: PhantomData,
45-
}
46-
}
35+
impl From<QeiMode> for Sms {
36+
fn from(mode: QeiMode) -> Self {
37+
match mode {
38+
QeiMode::Mode1 => Sms::ENCODER_MODE_1,
39+
QeiMode::Mode2 => Sms::ENCODER_MODE_2,
40+
QeiMode::Mode3 => Sms::ENCODER_MODE_3,
4741
}
48-
};
42+
}
43+
}
44+
45+
#[non_exhaustive]
46+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
47+
/// Config
48+
pub struct QeiConfig {
49+
/// Encoder Mode
50+
pub encoder_mode: QeiMode,
51+
52+
/// Set the pull configuration for the RX pin.
53+
pub pull: Pull,
4954
}
5055

51-
channel_impl!(new_ch1, Ch1, Channel1Pin);
52-
channel_impl!(new_ch2, Ch2, Channel2Pin);
56+
impl QeiConfig {
57+
/// Default constructor
58+
pub fn new() -> Self {
59+
Self::default()
60+
}
61+
62+
/// Set the encoder mode
63+
pub fn with_encoder_mode(mut self, mode: QeiMode) -> Self {
64+
self.encoder_mode = mode;
65+
self
66+
}
67+
68+
/// Set the pull configuration
69+
pub fn with_pull(mut self, pull: Pull) -> Self {
70+
self.pull = pull;
71+
self
72+
}
73+
}
74+
75+
impl Default for QeiConfig {
76+
#[doc = "Arbitrary defaults to preserve backwards compatibility"]
77+
fn default() -> Self {
78+
Self {
79+
encoder_mode: QeiMode::Mode3,
80+
pull: Pull::None,
81+
}
82+
}
83+
}
5384

5485
/// Quadrature decoder driver.
5586
pub struct Qei<'d, T: GeneralInstance4Channel> {
5687
inner: Timer<'d, T>,
88+
_ch1: PeripheralRef<'d, AnyPin>,
89+
_ch2: PeripheralRef<'d, AnyPin>,
5790
}
5891

5992
impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
6093
/// Create a new quadrature decoder driver.
61-
pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
62-
Self::new_inner(tim)
94+
pub fn new(
95+
tim: impl Peripheral<P = T> + 'd,
96+
ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
97+
ch2: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
98+
) -> Self {
99+
Self::new_inner(tim, ch1, ch2, QeiConfig::default())
100+
}
101+
102+
/// Create new quadrature encoder driver with non-default config.
103+
pub fn new_with_config(
104+
tim: impl Peripheral<P = T> + 'd,
105+
ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
106+
ch2: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
107+
config: QeiConfig,
108+
) -> Self {
109+
Self::new_inner(tim, ch1, ch2, config)
63110
}
64111

65-
fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
112+
fn new_inner(
113+
tim: impl Peripheral<P = T> + 'd,
114+
ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
115+
ch2: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
116+
config: QeiConfig,
117+
) -> Self {
66118
let inner = Timer::new(tim);
67119
let r = inner.regs_gp16();
68120

121+
// Due to generics and specific typesig of Peripheral<P> repeating
122+
// this block of code twice is less bad than the alternative
123+
// extra types and traits.
124+
//
125+
// Use of into_ref!() is destructive, and the resulting ref
126+
// is later used when constructing the return type of this fn
127+
into_ref!(ch1);
128+
critical_section::with(|_| {
129+
ch1.set_low();
130+
ch1.set_as_af(ch1.af_num(), AfType::input(config.pull));
131+
});
132+
into_ref!(ch2);
133+
critical_section::with(|_| {
134+
ch2.set_low();
135+
ch2.set_as_af(ch2.af_num(), AfType::input(config.pull));
136+
});
137+
69138
// Configure TxC1 and TxC2 as captures
70139
r.ccmr_input(0).modify(|w| {
71140
w.set_ccs(0, vals::CcmrInputCcs::TI4);
@@ -82,13 +151,17 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
82151
});
83152

84153
r.smcr().modify(|w| {
85-
w.set_sms(vals::Sms::ENCODER_MODE_3);
154+
w.set_sms(config.encoder_mode.into());
86155
});
87156

88157
r.arr().modify(|w| w.set_arr(u16::MAX));
89158
r.cr1().modify(|w| w.set_cen(true));
90159

91-
Self { inner }
160+
Self {
161+
inner,
162+
_ch1: ch1.map_into(),
163+
_ch2: ch2.map_into(),
164+
}
92165
}
93166

94167
/// Get direction.

0 commit comments

Comments
 (0)