use crate::pac::{self, DBGMCU as DBG};
use crate::rcc::{BusTimerClock, Clocks};
use crate::time::Hertz;
use crate::timer::{General, Timer};
use embedded_hal_02 as hal;
pub use hal::Direction;
pub trait InC<const C: u8> {
type In;
}
pub trait Instance: General + TimC<0> + TimC<1> {}
#[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 {}
#[allow(unused)]
pub struct PwmInput<TIM: Instance> {
timer: TIM,
pins: (<TIM as TimC<0>>::In, <TIM as TimC<1>>::In),
}
pub enum ReadMode {
Instant,
WaitForNextCapture,
}
#[derive(Debug)]
pub enum Error {
FrequencyTooLow,
}
pub enum Configuration {
Frequency(Hertz),
DutyCycle(Hertz),
RawFrequency(Hertz),
RawValues { arr: u16, presc: u16 },
}
#[derive(Copy, Clone, Debug)]
pub enum SlaveMode {
EncoderMode1 = 0b001,
EncoderMode2 = 0b010,
EncoderMode3 = 0b011,
ResetMode = 0b100,
TriggerMode = 0b110,
ExternalClockMode1 = 0b111,
}
#[derive(Copy, Clone, Debug)]
pub struct QeiOptions {
pub slave_mode: SlaveMode,
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: Instance> {
tim: TIM,
pins: (<TIM as TimC<0>>::In, <TIM as TimC<1>>::In),
}
pub trait PwmInputExt: Sized + Instance {
fn pwm_input(
self,
pins: (
impl RInto<<Self as TimC<0>>::In, 0>,
impl RInto<<Self as TimC<1>>::In, 0>,
),
dbg: &mut DBG,
mode: Configuration,
clocks: &Clocks,
) -> PwmInput<Self>;
}
pub trait QeiExt: Sized + Instance {
fn qei(
self,
pins: (
impl RInto<<Self as TimC<0>>::In, 0>,
impl RInto<<Self as TimC<1>>::In, 0>,
),
options: QeiOptions,
clocks: &Clocks,
) -> Qei<Self>;
}
fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) {
if freq == 0 {
return (u16::MAX, u16::MAX);
}
let presc = clock / freq.saturating_mul(u16::MAX as u32 + 1);
let arr = clock / freq.saturating_mul(presc + 1);
(core::cmp::max(1, arr as u16), presc as u16)
}
macro_rules! hal {
($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<<Self as TimC<0>>::In, 0>,
impl RInto<<Self as TimC<1>>::In, 0>,
),
dbg: &mut DBG,
mode: Configuration,
clocks: &Clocks,
) -> PwmInput<Self> {
Timer::new(self, clocks).pwm_input(pins, dbg, mode)
}
}
impl QeiExt for $TIM {
fn qei(
self,
pins: (
impl RInto<<Self as TimC<0>>::In, 0>,
impl RInto<<Self as TimC<1>>::In, 0>,
),
options: QeiOptions,
clocks: &Clocks,
) -> Qei<Self> {
Timer::new(self, clocks).qei(pins, options)
}
}
impl<const R: u8> 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<const R: u8>(
tim: $TIM,
pins: (
impl RInto<<$TIM as TimC<0>>::In, R>,
impl RInto<<$TIM as TimC<1>>::In, R>,
),
clk: Hertz,
mode: Configuration,
) -> PwmInput<$TIM> {
let pins = (pins.0.rinto(), pins.1.rinto());
use Configuration::*;
tim.ccer().modify(|_, w| {
w.cc1e().clear_bit();
w.cc2e().clear_bit();
w.cc1p().clear_bit();
w.cc2p().set_bit()
});
tim.ccmr1_input().modify(|_, w| w.cc1s().ti1().cc2s().ti1());
tim.dier().write(|w| w.cc1ie().set_bit());
tim.smcr()
.modify(|_, w| unsafe { w.ts().bits(0b101).sms().bits(0b100) });
match mode {
Frequency(f) => {
let freq = f.raw();
let max_freq = if freq > 5 { freq / 5 } else { 1 };
let (arr, presc) = compute_arr_presc(max_freq, clk.raw());
tim.arr().write(|w| w.arr().set(arr));
tim.psc().write(|w| w.psc().set(presc));
}
DutyCycle(f) => {
let freq = f.raw();
let max_freq = if freq > 2 {
freq / 2 + freq / 4 + freq / 8
} else {
1
};
let (arr, presc) = compute_arr_presc(max_freq, clk.raw());
tim.arr().write(|w| w.arr().set(arr));
tim.psc().write(|w| w.psc().set(presc));
}
RawFrequency(f) => {
let freq = f.raw();
let (arr, presc) = compute_arr_presc(freq, clk.raw());
tim.arr().write(|w| w.arr().set(arr));
tim.psc().write(|w| w.psc().set(presc));
}
RawValues { arr, presc } => {
tim.arr().write(|w| w.arr().set(arr));
tim.psc().write(|w| w.psc().set(presc));
}
}
tim.ccer()
.modify(|_, w| w.cc1e().set_bit().cc2e().set_bit());
tim.cr1().modify(|_, w| w.cen().set_bit());
PwmInput { timer: tim, pins }
}
impl PwmInput<$TIM> {
pub fn read_frequency(&self, mode: ReadMode, clocks: &Clocks) -> Result<Hertz, Error> {
if let ReadMode::WaitForNextCapture = mode {
self.wait_for_capture();
}
let presc = unsafe { (*<$TIM>::ptr()).psc().read().bits() as u16 };
let ccr1 = unsafe { (*<$TIM>::ptr()).ccr1().read().bits() as u16 };
if ccr1 == 0 {
Err(Error::FrequencyTooLow)
} else {
let clk = <$TIM>::timer_clock(&clocks);
Ok(clk / ((presc + 1) as u32 * (ccr1 + 1) as u32))
}
}
pub fn read_duty(&self, mode: ReadMode) -> Result<(u16, u16), Error> {
if let ReadMode::WaitForNextCapture = mode {
self.wait_for_capture();
}
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 {
Ok((ccr2, ccr1))
}
}
fn wait_for_capture(&self) {
unsafe { &(*<$TIM>::ptr()) }.sr().write(|w| {
w.uif().clear_bit();
w.cc1if().clear_bit();
w.cc1of().clear_bit()
});
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<const R: u8>(
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());
tim.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2());
tim.ccer().write(|w| {
w.cc1e().set_bit();
w.cc1p().clear_bit();
w.cc2e().set_bit();
w.cc2p().clear_bit()
});
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, qei1);
hal!(pac::TIM2: tim2, qei2);
hal!(pac::TIM3: tim3, qei3);
#[cfg(feature = "medium")]
hal!(pac::TIM4: tim4, qei4);