use core::marker::PhantomData;
use embassy_hal_internal::{Peri, PeripheralType};
use crate::{
event_link::InterruptEvent,
module_stop::ModuleStop,
pac::gpt::{
regs::{Gtdnsr, Gtupsr},
vals::{Ccr, Mode, Tpcs, Ud},
},
};
#[allow(private_bounds)]
pub trait Instance<Width: TimerWidth>: SealedInstance + ModuleStop + PeripheralType {
const COMP_A_EVENT: crate::event_link::InterruptEvent;
const COMP_B_EVENT: crate::event_link::InterruptEvent;
const COMP_C_EVENT: crate::event_link::InterruptEvent;
const OVERFLOW_EVENT: crate::event_link::InterruptEvent;
const UNDERFLOW_EVENT: crate::event_link::InterruptEvent;
}
pub(crate) trait SealedInstance: PeripheralType {
const INDEX: usize;
fn regs() -> crate::pac::gpt::Gpt;
}
pub(crate) trait TimerWidth: Into<u32> + Send + Sync + 'static {
fn max() -> u64;
}
impl TimerWidth for u32 {
#[inline(always)]
fn max() -> u64 {
u32::MAX.into()
}
}
impl TimerWidth for u16 {
#[inline(always)]
fn max() -> u64 {
u16::MAX.into()
}
}
#[allow(private_bounds)]
pub struct InterruptTimer<'d, W: TimerWidth, I: Instance<W>> {
phantom_i: PhantomData<&'d I>,
phantom_w: PhantomData<W>,
triangle: bool,
}
#[allow(private_bounds)]
impl<'d, W: TimerWidth, I: Instance<W>> InterruptTimer<'d, W, I> {
pub fn new(peri: Peri<'d, I>) -> Self {
let _ = peri;
I::start_module();
let gpt = I::regs();
gpt.gtupsr().write_value(Gtupsr(0));
gpt.gtdnsr().write_value(Gtdnsr(0));
gpt.gtcnt().write_value(0);
gpt.gtssr().modify(|r| r.set_cstrt(true));
Self {
phantom_i: PhantomData,
phantom_w: PhantomData,
triangle: false,
}
}
#[inline(always)]
pub fn set_frequency(&mut self, frequency: u32) {
let gpt = I::regs();
gpt.gtcr().modify(|r| r.set_tpcs(Tpcs::Div16));
let clocks = crate::clock::clock_status();
let mut period = (clocks.peripheral_d.to_Hz() as u64 / 16) / (frequency as u64);
if period > W::max() {
period /= 2;
self.triangle = true;
gpt.gtcr().modify(|r| r.set_md(Mode::TrianglePwm1));
gpt.gtber().modify(|w| w.set_ccra(Ccr::NoBuffer));
} else {
self.triangle = false;
gpt.gtcr().modify(|r| r.set_md(Mode::SawWaveOneShot));
}
assert!(period <= W::max());
self.set_period(period as u32);
}
#[inline(always)]
fn set_period(&mut self, period: u32) {
let gpt = I::regs();
gpt.gtpr().write_value(period);
}
#[inline]
pub fn start(&mut self) -> InterruptEvent {
let gpt = I::regs();
gpt.gtuddtyc().write(|r| {
r.set_udf(true);
r.set_ud(Ud::Up);
});
gpt.gtuddtyc().write(|r| {
r.set_udf(false);
r.set_ud(Ud::Up);
});
gpt.gtcnt().write_value(0);
gpt.gtstr().modify(|r| r.set_cstrt(I::INDEX, true));
if self.triangle {
I::UNDERFLOW_EVENT
} else {
I::OVERFLOW_EVENT
}
}
#[inline(always)]
pub fn stop(&mut self) {
let gpt = I::regs();
gpt.gtstr().modify(|r| r.set_cstrt(I::INDEX, false));
}
}
impl<'d, W: TimerWidth, I: Instance<W>> Drop for InterruptTimer<'d, W, I> {
fn drop(&mut self) {
error!(
"GPT{}: Drop not yet implemented, module will not be stopped",
I::INDEX
);
}
}
macro_rules! timer_instance {
($peripheral:ident, $width:ident, $index:literal, $cmpa_int:ident, $cmpb_int:ident, $cmpc_int:ident, $overflow_int:ident, $underflow_int:ident) => {
impl crate::timer_gpt::Instance<$width> for crate::peripherals::$peripheral {
const COMP_A_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$cmpa_int;
const COMP_B_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$cmpb_int;
const COMP_C_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$cmpc_int;
const OVERFLOW_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$overflow_int;
const UNDERFLOW_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$underflow_int;
}
impl crate::timer_gpt::SealedInstance for crate::peripherals::$peripheral {
const INDEX: usize = $index;
#[inline(always)]
fn regs() -> crate::pac::gpt::Gpt {
crate::pac::$peripheral
}
}
};
}
pub(crate) use timer_instance;