use core::marker::PhantomData;
use fugit::HertzU32;
use crate::{
clock::Clocks,
mcpwm::{FrequencyError, PeripheralClockConfig, PwmPeripheral},
peripherals::mcpwm0::{TIMER0_CFG0, TIMER0_CFG1},
};
pub struct Timer<const TIM: u8, PWM> {
pub(super) phantom: PhantomData<PWM>,
}
impl<const TIM: u8, PWM: PwmPeripheral> Timer<TIM, PWM> {
pub(super) fn new() -> Self {
Timer {
phantom: PhantomData,
}
}
pub fn start(&mut self, timer_config: TimerClockConfig) {
self.cfg0().write(|w| {
w.timer0_prescale()
.variant(timer_config.prescaler)
.timer0_period()
.variant(timer_config.period)
.timer0_period_upmethod()
.variant(0)
});
self.cfg1().write(|w| {
w.timer0_start()
.variant(2)
.timer0_mod()
.variant(timer_config.mode as u8)
});
}
pub fn stop(&mut self) {
self.cfg1().write(|w| w.timer0_mod().variant(0));
}
pub fn set_counter(&mut self, phase: u16, direction: CounterDirection) {
let block = unsafe { &*PWM::block() };
match TIM {
0 => {
let sw = block.timer0_sync().read().sw().bit_is_set();
block.timer0_sync().write(|w| {
w.timer0_phase_direction()
.variant(direction as u8 != 0)
.timer0_phase()
.variant(phase)
.sw()
.variant(!sw)
});
}
1 => {
let sw = block.timer1_sync().read().sw().bit_is_set();
block.timer1_sync().write(|w| {
w.timer1_phase_direction()
.variant(direction as u8 != 0)
.timer1_phase()
.variant(phase)
.sw()
.variant(!sw)
});
}
2 => {
let sw = block.timer2_sync().read().sw().bit_is_set();
block.timer2_sync().write(|w| {
w.timer2_phase_direction()
.variant(direction as u8 != 0)
.timer2_phase()
.variant(phase)
.sw()
.variant(!sw)
});
}
_ => unreachable!(),
}
}
pub fn status(&self) -> (u16, CounterDirection) {
let block = unsafe { &*PWM::block() };
match TIM {
0 => {
let reg = block.timer0_status().read();
(
reg.timer0_value().bits(),
reg.timer0_direction().bit_is_set().into(),
)
}
1 => {
let reg = block.timer1_status().read();
(
reg.timer1_value().bits(),
reg.timer1_direction().bit_is_set().into(),
)
}
2 => {
let reg = block.timer2_status().read();
(
reg.timer2_value().bits(),
reg.timer2_direction().bit_is_set().into(),
)
}
_ => unreachable!(),
}
}
fn cfg0(&mut self) -> &TIMER0_CFG0 {
let block = unsafe { &*PWM::block() };
match TIM {
0 => block.timer0_cfg0(),
1 => unsafe { &*(&block.timer1_cfg0() as *const _ as *const _) },
2 => unsafe { &*(&block.timer2_cfg0() as *const _ as *const _) },
_ => unreachable!(),
}
}
fn cfg1(&mut self) -> &TIMER0_CFG1 {
let block = unsafe { &*PWM::block() };
match TIM {
0 => block.timer0_cfg1(),
1 => unsafe { &*(&block.timer1_cfg1() as *const _ as *const _) },
2 => unsafe { &*(&block.timer2_cfg1() as *const _ as *const _) },
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone)]
pub struct TimerClockConfig<'a> {
frequency: HertzU32,
period: u16,
prescaler: u8,
mode: PwmWorkingMode,
phantom: PhantomData<&'a Clocks<'a>>,
}
impl<'a> TimerClockConfig<'a> {
pub(super) fn with_prescaler(
clock: &PeripheralClockConfig<'a>,
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,
mode,
phantom: PhantomData,
}
}
pub(super) fn with_frequency(
clock: &PeripheralClockConfig<'a>,
period: u16,
mode: PwmWorkingMode,
target_freq: HertzU32,
) -> 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
.raw()
.checked_mul(cycle_period)
.ok_or(FrequencyError)?;
if target_timer_frequency == 0 || target_freq > clock.frequency {
return Err(FrequencyError);
}
let prescaler = clock.frequency.raw() / 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,
mode,
phantom: PhantomData,
})
}
pub fn frequency(&self) -> HertzU32 {
self.frequency
}
}
#[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,
}
}
}