use stm32_metapac::timer::vals::{self, Sms};
use super::low_level::Timer;
pub use super::{Ch1, Ch2};
use super::{GeneralInstance4Channel, TimerPin};
use crate::Peri;
use crate::gpio::{AfType, Flex, Pull};
use crate::timer::TimerChannel;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy)]
pub struct Config {
pub ch1_pull: Pull,
pub ch2_pull: Pull,
pub mode: QeiMode,
pub auto_reload: u16,
}
impl Default for Config {
fn default() -> Self {
Self {
ch1_pull: Pull::None,
ch2_pull: Pull::None,
mode: QeiMode::Mode3,
auto_reload: u16::MAX,
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy)]
pub enum QeiMode {
Mode1,
Mode2,
Mode3,
}
impl From<QeiMode> for Sms {
fn from(mode: QeiMode) -> Self {
match mode {
QeiMode::Mode1 => Sms::ENCODER_MODE_1,
QeiMode::Mode2 => Sms::ENCODER_MODE_2,
QeiMode::Mode3 => Sms::ENCODER_MODE_3,
}
}
}
pub enum Direction {
Upcounting,
Downcounting,
}
trait SealedQeiChannel: TimerChannel {}
#[expect(private_bounds)]
pub trait QeiChannel: SealedQeiChannel {}
impl QeiChannel for Ch1 {}
impl QeiChannel for Ch2 {}
impl SealedQeiChannel for Ch1 {}
impl SealedQeiChannel for Ch2 {}
pub struct Qei<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
_ch1: Flex<'d>,
_ch2: Flex<'d>,
}
impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
#[allow(unused)]
pub fn new<CH1: QeiChannel, CH2: QeiChannel, #[cfg(afio)] A>(
tim: Peri<'d, T>,
ch1: Peri<'d, if_afio!(impl TimerPin<T, CH1, A>)>,
ch2: Peri<'d, if_afio!(impl TimerPin<T, CH2, A>)>,
config: Config,
) -> Self {
critical_section::with(|_| {
ch1.set_low();
ch2.set_low();
});
let inner = Timer::new(tim);
let r = inner.regs_gp16();
r.ccmr_input(0).modify(|w| {
w.set_ccs(0, vals::CcmrInputCcs::TI4);
w.set_ccs(1, vals::CcmrInputCcs::TI4);
});
r.ccer().modify(|w| {
w.set_cce(0, true);
w.set_cce(1, true);
w.set_ccp(0, false);
w.set_ccp(1, false);
});
r.smcr().modify(|w| {
w.set_sms(config.mode.into());
});
r.arr().modify(|w| w.set_arr(config.auto_reload));
r.cr1().modify(|w| w.set_cen(true));
Self {
inner,
_ch1: new_pin!(ch1, AfType::input(config.ch1_pull)).unwrap(),
_ch2: new_pin!(ch2, AfType::input(config.ch2_pull)).unwrap(),
}
}
pub fn read_direction(&self) -> Direction {
match self.inner.regs_gp16().cr1().read().dir() {
vals::Dir::DOWN => Direction::Downcounting,
vals::Dir::UP => Direction::Upcounting,
}
}
pub fn count(&self) -> u16 {
self.inner.regs_gp16().cnt().read().cnt()
}
pub fn reset(&mut self) {
self.inner.regs_gp16().cnt().modify(|w| w.set_cnt(0));
}
}