use core::marker::PhantomData;
use super::PeripheralGuard;
use crate::{
mcpwm::{FrequencyError, PeripheralClockConfig, PwmClockGuard, PwmPeripheral},
pac,
time::Rate,
};
pub struct Timer<const TIM: u8, PWM> {
pub(super) phantom: PhantomData<PWM>,
_guard: PeripheralGuard,
_pwm_clock_guard: PwmClockGuard,
}
impl<const TIM: u8, PWM: PwmPeripheral> Timer<TIM, PWM> {
pub(super) fn new(guard: PeripheralGuard) -> Self {
Timer {
phantom: PhantomData,
_guard: guard,
_pwm_clock_guard: PwmClockGuard::new::<PWM>(),
}
}
pub fn start(&mut self, timer_config: TimerClockConfig) {
self.cfg0().write(|w| unsafe {
w.prescale().bits(timer_config.prescaler);
w.period().bits(timer_config.period);
w.period_upmethod()
.bits(timer_config.period_updating_method as u8)
});
self.cfg1().write(|w| unsafe {
w.start().bits(2);
w.mod_().bits(timer_config.mode as u8)
});
}
pub fn stop(&mut self) {
self.cfg1().write(|w| unsafe { w.mod_().bits(0) });
}
pub fn set_counter(&mut self, phase: u16, direction: CounterDirection) {
let tmr = unsafe { Self::tmr() };
let sw = tmr.sync().read().sw().bit_is_set();
tmr.sync().write(|w| {
w.phase_direction().bit(direction as u8 != 0);
unsafe {
w.phase().bits(phase);
}
w.sw().bit(!sw)
});
}
pub fn status(&self) -> (u16, CounterDirection) {
let reg = unsafe { Self::tmr() }.status().read();
(reg.value().bits(), reg.direction().bit_is_set().into())
}
fn cfg0(&mut self) -> &pac::mcpwm0::timer::CFG0 {
unsafe { Self::tmr() }.cfg0()
}
fn cfg1(&mut self) -> &pac::mcpwm0::timer::CFG1 {
unsafe { Self::tmr() }.cfg1()
}
unsafe fn tmr() -> &'static pac::mcpwm0::TIMER {
let block = unsafe { &*PWM::block() };
block.timer(TIM as usize)
}
}
#[derive(Copy, Clone)]
pub struct TimerClockConfig {
frequency: Rate,
period: u16,
period_updating_method: PeriodUpdatingMethod,
prescaler: u8,
mode: PwmWorkingMode,
}
impl TimerClockConfig {
pub(super) fn with_prescaler(
clock: &PeripheralClockConfig,
period: u16,
mode: PwmWorkingMode,
prescaler: u8,
) -> Self {
let cycle_period = match mode {
PwmWorkingMode::Increase | PwmWorkingMode::Decrease => period as u32 + 1,
PwmWorkingMode::UpDown => period as u32 * 2,
};
let frequency = clock.frequency / (prescaler as u32 + 1) / cycle_period;
TimerClockConfig {
frequency,
prescaler,
period,
period_updating_method: PeriodUpdatingMethod::Immediately,
mode,
}
}
pub(super) fn with_frequency(
clock: &PeripheralClockConfig,
period: u16,
mode: PwmWorkingMode,
target_freq: Rate,
) -> Result<Self, FrequencyError> {
let cycle_period = match mode {
PwmWorkingMode::Increase | PwmWorkingMode::Decrease => period as u32 + 1,
PwmWorkingMode::UpDown => period as u32 * 2,
};
let target_timer_frequency = target_freq
.as_hz()
.checked_mul(cycle_period)
.ok_or(FrequencyError)?;
if target_timer_frequency == 0 || target_freq > clock.frequency {
return Err(FrequencyError);
}
let prescaler = clock.frequency.as_hz() / target_timer_frequency - 1;
if prescaler > u8::MAX as u32 {
return Err(FrequencyError);
}
let frequency = clock.frequency / (prescaler + 1) / cycle_period;
Ok(TimerClockConfig {
frequency,
prescaler: prescaler as u8,
period,
period_updating_method: PeriodUpdatingMethod::Immediately,
mode,
})
}
pub fn with_period_updating_method(self, method: PeriodUpdatingMethod) -> Self {
Self {
period_updating_method: method,
..self
}
}
pub fn frequency(&self) -> Rate {
self.frequency
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum PeriodUpdatingMethod {
Immediately = 0,
TimerEqualsZero = 1,
Sync = 2,
TimerEqualsZeroOrSync = 3,
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum PwmWorkingMode {
Increase = 1,
Decrease = 2,
UpDown = 3,
}
#[derive(Debug)]
#[repr(u8)]
pub enum CounterDirection {
Increasing = 0,
Decreasing = 1,
}
impl From<bool> for CounterDirection {
fn from(bit: bool) -> Self {
match bit {
false => CounterDirection::Increasing,
true => CounterDirection::Decreasing,
}
}
}