use core::mem::ManuallyDrop;
use crate::pac;
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
pub use pac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource};
use super::*;
use crate::pac::timer::vals;
use crate::rcc;
use crate::time::Hertz;
#[derive(Clone, Copy)]
pub enum InputCaptureMode {
Rising,
Falling,
BothEdges,
}
#[derive(Clone, Copy)]
pub enum InputTISelection {
Normal,
Alternate,
TRC,
}
impl From<InputTISelection> for pac::timer::vals::CcmrInputCcs {
fn from(tisel: InputTISelection) -> Self {
match tisel {
InputTISelection::Normal => pac::timer::vals::CcmrInputCcs::TI4,
InputTISelection::Alternate => pac::timer::vals::CcmrInputCcs::TI3,
InputTISelection::TRC => pac::timer::vals::CcmrInputCcs::TRC,
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CountingMode {
#[default]
EdgeAlignedUp,
EdgeAlignedDown,
CenterAlignedDownInterrupts,
CenterAlignedUpInterrupts,
CenterAlignedBothInterrupts,
}
impl CountingMode {
pub fn is_edge_aligned(&self) -> bool {
matches!(
self,
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown
)
}
pub fn is_center_aligned(&self) -> bool {
matches!(
self,
CountingMode::CenterAlignedDownInterrupts
| CountingMode::CenterAlignedUpInterrupts
| CountingMode::CenterAlignedBothInterrupts
)
}
}
impl From<CountingMode> for (vals::Cms, vals::Dir) {
fn from(value: CountingMode) -> Self {
match value {
CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
}
}
}
impl From<(vals::Cms, vals::Dir)> for CountingMode {
fn from(value: (vals::Cms, vals::Dir)) -> Self {
match value {
(vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
(vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
(vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
(vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
(vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
}
}
}
#[derive(Clone, Copy)]
pub enum OutputCompareMode {
Frozen,
ActiveOnMatch,
InactiveOnMatch,
Toggle,
ForceInactive,
ForceActive,
PwmMode1,
PwmMode2,
}
impl From<OutputCompareMode> for pac::timer::vals::Ocm {
fn from(mode: OutputCompareMode) -> Self {
match mode {
OutputCompareMode::Frozen => pac::timer::vals::Ocm::FROZEN,
OutputCompareMode::ActiveOnMatch => pac::timer::vals::Ocm::ACTIVEONMATCH,
OutputCompareMode::InactiveOnMatch => pac::timer::vals::Ocm::INACTIVEONMATCH,
OutputCompareMode::Toggle => pac::timer::vals::Ocm::TOGGLE,
OutputCompareMode::ForceInactive => pac::timer::vals::Ocm::FORCEINACTIVE,
OutputCompareMode::ForceActive => pac::timer::vals::Ocm::FORCEACTIVE,
OutputCompareMode::PwmMode1 => pac::timer::vals::Ocm::PWMMODE1,
OutputCompareMode::PwmMode2 => pac::timer::vals::Ocm::PWMMODE2,
}
}
}
#[derive(Clone, Copy)]
pub enum OutputPolarity {
ActiveHigh,
ActiveLow,
}
impl From<OutputPolarity> for bool {
fn from(mode: OutputPolarity) -> Self {
match mode {
OutputPolarity::ActiveHigh => false,
OutputPolarity::ActiveLow => true,
}
}
}
pub struct Timer<'d, T: CoreInstance> {
tim: PeripheralRef<'d, T>,
}
impl<'d, T: CoreInstance> Drop for Timer<'d, T> {
fn drop(&mut self) {
rcc::disable::<T>();
}
}
impl<'d, T: CoreInstance> Timer<'d, T> {
pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(tim);
rcc::enable_and_reset::<T>();
Self { tim }
}
pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> {
let tim = unsafe { self.tim.clone_unchecked() };
ManuallyDrop::new(Self { tim })
}
pub fn regs_core(&self) -> crate::pac::timer::TimCore {
unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) }
}
#[cfg(py32f072)]
fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 {
unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
}
pub fn start(&self) {
self.regs_core().cr1().modify(|r| r.set_cen(true));
}
pub fn stop(&self) {
self.regs_core().cr1().modify(|r| r.set_cen(false));
}
pub fn reset(&self) {
self.regs_core().cnt().write(|r| r.set_cnt(0));
}
pub fn set_frequency(&self, frequency: Hertz) {
let f = frequency.0;
assert!(f > 0);
let timer_f = T::frequency().0;
match T::BITS {
TimerBits::Bits16 => {
let pclk_ticks_per_timer_period = timer_f / f;
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1);
let arr = unwrap!(u16::try_from(divide_by - 1));
let regs = self.regs_core();
regs.psc().write_value(psc);
regs.arr().write(|r| r.set_arr(arr));
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
regs.egr().write(|r| r.set_ug(true));
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
}
#[cfg(py32f072)]
TimerBits::Bits32 => {
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1);
let arr: u32 = unwrap!(u32::try_from(divide_by - 1));
let regs = self.regs_gp32_unchecked();
regs.psc().write_value(psc);
regs.arr().write_value(arr);
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
regs.egr().write(|r| r.set_ug(true));
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
}
}
}
pub fn set_tick_freq(&mut self, freq: Hertz) {
let f = freq;
assert!(f.0 > 0);
let timer_f = self.get_clock_frequency();
let pclk_ticks_per_timer_period = timer_f / f;
let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into());
let regs = self.regs_core();
regs.psc().write_value(psc);
regs.egr().write(|r| r.set_ug(true));
}
pub fn clear_update_interrupt(&self) -> bool {
let regs = self.regs_core();
let sr = regs.sr().read();
if sr.uif() {
regs.sr().modify(|r| {
r.set_uif(false);
});
true
} else {
false
}
}
pub fn enable_update_interrupt(&self, enable: bool) {
self.regs_core().dier().modify(|r| r.set_uie(enable));
}
pub fn set_autoreload_preload(&self, enable: bool) {
self.regs_core().cr1().modify(|r| r.set_arpe(enable));
}
pub fn get_frequency(&self) -> Hertz {
let timer_f = T::frequency();
match T::BITS {
TimerBits::Bits16 => {
let regs = self.regs_core();
let arr = regs.arr().read().arr();
let psc = regs.psc().read();
timer_f / arr / (psc + 1)
}
#[cfg(py32f072)]
TimerBits::Bits32 => {
let regs = self.regs_gp32_unchecked();
let arr = regs.arr().read();
let psc = regs.psc().read();
timer_f / arr / (psc + 1)
}
}
}
pub fn get_clock_frequency(&self) -> Hertz {
T::frequency()
}
}
impl<'d, T: BasicNoCr2Instance> Timer<'d, T> {
pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 {
unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) }
}
pub fn enable_update_dma(&self, enable: bool) {
self.regs_basic_no_cr2()
.dier()
.modify(|r| r.set_ude(enable));
}
pub fn get_update_dma_state(&self) -> bool {
self.regs_basic_no_cr2().dier().read().ude()
}
}
impl<'d, T: BasicInstance> Timer<'d, T> {
pub fn regs_basic(&self) -> crate::pac::timer::TimBasic {
unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) }
}
}
impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch {
unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) }
}
pub fn set_clock_division(&self, ckd: vals::Ckd) {
self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
}
pub fn get_max_compare_value(&self) -> u32 {
match T::BITS {
TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32,
#[cfg(py32f072)]
TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
}
}
}
impl<'d, T: GeneralInstance2Channel> Timer<'d, T> {
pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch {
unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) }
}
}
impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }
}
pub fn enable_outputs(&self) {
self.tim.enable_outputs()
}
pub fn set_counting_mode(&self, mode: CountingMode) {
let (cms, dir) = mode.into();
let timer_enabled = self.regs_core().cr1().read().cen();
assert!(!timer_enabled);
self.regs_gp16().cr1().modify(|r| r.set_dir(dir));
self.regs_gp16().cr1().modify(|r| r.set_cms(cms))
}
pub fn get_counting_mode(&self) -> CountingMode {
let cr1 = self.regs_gp16().cr1().read();
(cr1.cms(), cr1.dir()).into()
}
pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_icf(raw_channel % 2, icf));
}
pub fn clear_input_interrupt(&self, channel: Channel) {
self.regs_gp16()
.sr()
.modify(|r| r.set_ccif(channel.index(), false));
}
pub fn get_input_interrupt(&self, channel: Channel) -> bool {
self.regs_gp16().sr().read().ccif(channel.index())
}
pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) {
self.regs_gp16()
.dier()
.modify(|r| r.set_ccie(channel.index(), enable));
}
pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_icpsc(raw_channel % 2, factor));
}
pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
}
pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {
self.regs_gp16().ccer().modify(|r| match mode {
InputCaptureMode::Rising => {
r.set_ccnp(channel.index(), false);
r.set_ccp(channel.index(), false);
}
InputCaptureMode::Falling => {
r.set_ccnp(channel.index(), false);
r.set_ccp(channel.index(), true);
}
InputCaptureMode::BothEdges => {
r.set_ccnp(channel.index(), true);
r.set_ccp(channel.index(), true);
}
});
}
pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {
let raw_channel: usize = channel.index();
self.regs_gp16()
.ccmr_output(raw_channel / 2)
.modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
}
pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
self.regs_gp16()
.ccer()
.modify(|w| w.set_ccp(channel.index(), polarity.into()));
}
pub fn enable_channel(&self, channel: Channel, enable: bool) {
self.regs_gp16()
.ccer()
.modify(|w| w.set_cce(channel.index(), enable));
}
pub fn get_channel_enable_state(&self, channel: Channel) -> bool {
self.regs_gp16().ccer().read().cce(channel.index())
}
pub fn set_compare_value(&self, channel: Channel, value: u32) {
match T::BITS {
TimerBits::Bits16 => {
let value = unwrap!(u16::try_from(value));
self.regs_gp16()
.ccr(channel.index())
.modify(|w| w.set_ccr(value));
}
#[cfg(py32f072)]
TimerBits::Bits32 => {
self.regs_gp32_unchecked()
.ccr(channel.index())
.write_value(value);
}
}
}
pub fn get_compare_value(&self, channel: Channel) -> u32 {
match T::BITS {
TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32,
#[cfg(py32f072)]
TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(),
}
}
pub fn get_capture_value(&self, channel: Channel) -> u32 {
self.get_compare_value(channel)
}
pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) {
let channel_index = channel.index();
self.regs_gp16()
.ccmr_output(channel_index / 2)
.modify(|w| w.set_ocpe(channel_index % 2, preload));
}
pub fn get_cc_dma_selection(&self) -> vals::Ccds {
self.regs_gp16().cr2().read().ccds()
}
pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) {
self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
}
pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {
self.regs_gp16().dier().read().ccde(channel.index())
}
pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
self.regs_gp16()
.dier()
.modify(|w| w.set_ccde(channel.index(), ccde))
}
pub fn set_slave_mode(&self, sms: SlaveMode) {
self.regs_gp16().smcr().modify(|r| r.set_sms(sms));
}
pub fn set_trigger_source(&self, ts: TriggerSource) {
self.regs_gp16().smcr().modify(|r| r.set_ts(ts));
}
}
impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> {
pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp {
unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }
}
pub fn set_dead_time_clock_division(&self, value: vals::Ckd) {
self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
}
pub fn set_dead_time_value(&self, value: u8) {
self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
}
pub fn set_moe(&self, enable: bool) {
self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));
}
}
impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> {
pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp {
unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) }
}
}
impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {
pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv {
unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) }
}
pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
self.regs_advanced()
.ccer()
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
}
pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) {
self.regs_advanced()
.ccer()
.modify(|w| w.set_ccne(channel.index(), enable));
}
}