diff --git a/CHANGELOG.md b/CHANGELOG.md index adeb7e0..78f6ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Basic API for EDAC functionality +- PWM implementation and example ## [0.2.2] diff --git a/Cargo.toml b/Cargo.toml index a6fedac..931a634 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,7 @@ required-features = ["rt"] [[example]] name = "tests" required-features = ["rt"] + +[[example]] +name = "pwm" +required-features = ["rt"] diff --git a/examples/pwm.rs b/examples/pwm.rs new file mode 100644 index 0000000..b09c190 --- /dev/null +++ b/examples/pwm.rs @@ -0,0 +1,78 @@ +//! Simple PWM example +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use embedded_hal::PwmPin; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use va108xx_hal::{ + gpio::PinsA, + pac::{self, interrupt}, + prelude::*, + pwm::{self, get_duty_from_percent, ReducedPwmPin, PWMA, PWMB}, + timer::{default_ms_irq_handler, set_up_ms_timer, Delay}, +}; + +#[entry] +fn main() -> ! { + rtt_init_print!(); + rprintln!("-- VA108xx PWM example application--"); + let mut dp = pac::Peripherals::take().unwrap(); + let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA); + let mut pwm = pwm::PwmPin::new( + (pinsa.pa3.into_funsel_1(), dp.TIM3), + 50.mhz(), + &mut dp.SYSCONFIG, + 10.hz(), + ); + let timer = set_up_ms_timer( + &mut dp.SYSCONFIG, + &mut dp.IRQSEL, + 50.mhz().into(), + dp.TIM0, + pac::Interrupt::OC0, + ); + let mut delay = Delay::new(timer); + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0); + } + let mut current_duty_cycle = 0.0; + PwmPin::set_duty(&mut pwm, get_duty_from_percent(current_duty_cycle)); + PwmPin::enable(&mut pwm); + + // Delete type information, increased code readibility for the rest of the code + let mut reduced_pin = ReducedPwmPin::from(pwm); + loop { + // Increase duty cycle continuously + while current_duty_cycle < 1.0 { + delay.delay_ms(200); + current_duty_cycle += 0.02; + PwmPin::set_duty(&mut reduced_pin, get_duty_from_percent(current_duty_cycle)); + } + + // Switch to PWMB and decrease the window with a high signal from 100 % to 0 % + // continously + current_duty_cycle = 0.0; + let mut upper_limit = 1.0; + let mut lower_limit = 0.0; + let mut pwmb: ReducedPwmPin = ReducedPwmPin::from(reduced_pin); + pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit)); + pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit)); + while lower_limit < 0.5 { + delay.delay_ms(200); + lower_limit += 0.01; + upper_limit -= 0.01; + pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit)); + pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit)); + rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit()); + rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit()); + } + reduced_pin = ReducedPwmPin::::from(pwmb); + } +} + +#[interrupt] +fn OC0() { + default_ms_irq_handler() +} diff --git a/src/gpio/pins.rs b/src/gpio/pins.rs index 265a82d..f2b7c7c 100644 --- a/src/gpio/pins.rs +++ b/src/gpio/pins.rs @@ -731,7 +731,7 @@ impl InputPin for Pin { /// Provide a safe register interface for [`Pin`]s /// /// This `struct` takes ownership of a [`PinId`] and provides an API to -/// access the corresponding regsiters. +/// access the corresponding registers. pub(in crate::gpio) struct Registers { id: PhantomData, } diff --git a/src/gpio/reg.rs b/src/gpio/reg.rs index 3a6648f..8f94912 100644 --- a/src/gpio/reg.rs +++ b/src/gpio/reg.rs @@ -77,6 +77,7 @@ impl From for ModeFields { fields } } + //================================================================================================== // Register Interface //================================================================================================== diff --git a/src/i2c.rs b/src/i2c.rs index 1405b3b..0134831 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -2,7 +2,7 @@ //! //! ## Examples //! -//! - [Blocking I2C example]() +//! - [REB1 I2C temperature sensor example](https://github.com/robamu-org/vorago-reb1-rs/blob/main/examples/temp-sensor.rs) use crate::{ clock::{enable_peripheral_clock, PeripheralClocks}, pac::{I2CA, I2CB, SYSCONFIG}, diff --git a/src/lib.rs b/src/lib.rs index 9a138f2..73b0404 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod clock; pub mod gpio; pub mod i2c; pub mod prelude; +pub mod pwm; pub mod spi; pub mod time; pub mod timer; diff --git a/src/pwm.rs b/src/pwm.rs new file mode 100644 index 0000000..531b837 --- /dev/null +++ b/src/pwm.rs @@ -0,0 +1,457 @@ +//! API for Pulse-Width Modulation (PWM) +//! +//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks +//! +//! ## Examples +//! +//! - [PWM example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/pwm.rs) +use core::marker::PhantomData; + +use crate::{clock::enable_peripheral_clock, gpio::DynPinId}; +pub use crate::{gpio::PinId, prelude::*, time::Hertz, timer::*}; + +use va108xx::SYSCONFIG; + +const DUTY_MAX: u16 = u16::MAX; + +pub struct PwmBase { + sys_clk: Hertz, + /// For PWMB, this is the upper limit + current_duty: u16, + /// For PWMA, this value will not be used + current_lower_limit: u16, + current_period: Hertz, + current_rst_val: u32, +} + +enum StatusSelPwm { + PwmA = 3, + PwmB = 4, +} + +pub struct PWMA {} +pub struct PWMB {} + +//================================================================================================== +// Common +//================================================================================================== + +macro_rules! pwm_common_func { + () => { + #[inline] + fn enable_pwm_a(&mut self) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) }); + } + + #[inline] + fn enable_pwm_b(&mut self) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) }); + } + + #[inline] + pub fn get_period(&self) -> Hertz { + self.pwm_base.current_period + } + + #[inline] + pub fn set_period(&mut self, period: impl Into) { + self.pwm_base.current_period = period.into(); + // Avoid division by 0 + if self.pwm_base.current_period.0 == 0 { + return; + } + self.pwm_base.current_rst_val = + self.pwm_base.sys_clk.0 / self.pwm_base.current_period.0; + self.reg + .get_reg_block() + .rst_value + .write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) }); + } + }; +} + +macro_rules! pwmb_func { + () => { + pub fn pwmb_lower_limit(&self) -> u16 { + self.pwm_base.current_lower_limit + } + + pub fn pwmb_upper_limit(&self) -> u16 { + self.pwm_base.current_duty + } + + /// Set the lower limit for PWMB + /// + /// The PWM signal will be 1 as long as the current RST counter is larger than + /// the lower limit. For example, with a lower limit of 0.5 and and an upper limit + /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high + /// state + pub fn set_pwmb_lower_limit(&mut self, duty: u16) { + self.pwm_base.current_lower_limit = duty; + let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64 + * self.pwm_base.current_lower_limit as u64) + / DUTY_MAX as u64; + self.reg + .get_reg_block() + .pwmb_value + .write(|w| unsafe { w.bits(pwmb_val as u32) }); + } + + /// Set the higher limit for PWMB + /// + /// The PWM signal will be 1 as long as the current RST counter is smaller than + /// the higher limit. For example, with a lower limit of 0.5 and and an upper limit + /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high + /// state + pub fn set_pwmb_upper_limit(&mut self, duty: u16) { + self.pwm_base.current_duty = duty; + let pwma_val: u64 = (self.pwm_base.current_rst_val as u64 + * self.pwm_base.current_duty as u64) + / DUTY_MAX as u64; + self.reg + .get_reg_block() + .pwma_value() + .write(|w| unsafe { w.bits(pwma_val as u32) }); + } + }; +} + +//================================================================================================== +// Strongly typed PWM pin +//================================================================================================== + +pub struct PwmPin { + reg: TimRegister, + pwm_base: PwmBase, + _mode: PhantomData, +} + +impl PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + /// Create a new stronlgy typed PWM pin + pub fn new( + vtp: (PIN, TIM), + sys_clk: impl Into + Copy, + sys_cfg: &mut SYSCONFIG, + initial_period: impl Into + Copy, + ) -> Self { + let mut pin = PwmPin { + pwm_base: PwmBase { + current_duty: 0, + current_lower_limit: 0, + current_period: initial_period.into(), + current_rst_val: 0, + sys_clk: sys_clk.into(), + }, + reg: unsafe { TimRegister::new(vtp.0, vtp.1) }, + _mode: PhantomData, + }; + enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio); + enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig); + sys_cfg + .tim_clk_enable + .modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) }); + pin.enable_pwm_a(); + pin.set_period(initial_period); + pin + } + pub fn release(self) -> (PIN, TIM) { + self.reg.release() + } + + pwm_common_func!(); +} + +impl From> for PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + fn from(other: PwmPin) -> Self { + let mut pwmb = Self { + reg: other.reg, + pwm_base: other.pwm_base, + _mode: PhantomData, + }; + pwmb.enable_pwm_b(); + pwmb + } +} + +impl From> for PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + fn from(other: PwmPin) -> Self { + let mut pwmb = Self { + reg: other.reg, + pwm_base: other.pwm_base, + _mode: PhantomData, + }; + pwmb.enable_pwm_a(); + pwmb + } +} + +impl PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + pub fn pwma( + vtp: (PIN, TIM), + sys_clk: impl Into + Copy, + sys_cfg: &mut SYSCONFIG, + initial_period: impl Into + Copy, + ) -> Self { + let mut pin: PwmPin = Self::new(vtp, sys_clk, sys_cfg, initial_period); + pin.enable_pwm_a(); + pin + } +} + +impl PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + pub fn pwmb( + vtp: (PIN, TIM), + sys_clk: impl Into + Copy, + sys_cfg: &mut SYSCONFIG, + initial_period: impl Into + Copy, + ) -> Self { + let mut pin: PwmPin = Self::new(vtp, sys_clk, sys_cfg, initial_period); + pin.enable_pwm_b(); + pin + } +} + +//================================================================================================== +// Reduced PWM pin +//================================================================================================== + +/// Reduced version where type information is deleted +pub struct ReducedPwmPin { + reg: TimDynRegister, + pwm_base: PwmBase, + _pin_id: DynPinId, + _mode: PhantomData, +} + +impl From> for ReducedPwmPin { + fn from(pwm_pin: PwmPin) -> Self { + ReducedPwmPin { + reg: TimDynRegister::from(pwm_pin.reg), + pwm_base: pwm_pin.pwm_base, + _pin_id: PIN::DYN, + _mode: PhantomData, + } + } +} + +impl ReducedPwmPin { + pwm_common_func!(); +} + +impl From> for ReducedPwmPin { + fn from(other: ReducedPwmPin) -> Self { + let mut pwmb = Self { + reg: other.reg, + pwm_base: other.pwm_base, + _pin_id: other._pin_id, + _mode: PhantomData, + }; + pwmb.enable_pwm_b(); + pwmb + } +} + +impl From> for ReducedPwmPin { + fn from(other: ReducedPwmPin) -> Self { + let mut pwmb = Self { + reg: other.reg, + pwm_base: other.pwm_base, + _pin_id: other._pin_id, + _mode: PhantomData, + }; + pwmb.enable_pwm_a(); + pwmb + } +} + +//================================================================================================== +// PWMB implementations +//================================================================================================== + +impl PwmPin +where + (PIN, TIM): ValidTimAndPin, +{ + pwmb_func!(); +} + +impl ReducedPwmPin { + pwmb_func!(); +} + +//================================================================================================== +// Embedded HAL implementation: PWMA only +//================================================================================================== + +macro_rules! pwm_pin_impl { + () => { + #[inline] + fn disable(&mut self) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| w.enable().clear_bit()); + } + + #[inline] + fn enable(&mut self) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| w.enable().set_bit()); + } + + #[inline] + fn set_duty(&mut self, duty: Self::Duty) { + self.pwm_base.current_duty = duty; + let pwma_val: u64 = (self.pwm_base.current_rst_val as u64 + * (DUTY_MAX as u64 - self.pwm_base.current_duty as u64)) + / DUTY_MAX as u64; + self.reg + .get_reg_block() + .pwma_value() + .write(|w| unsafe { w.bits(pwma_val as u32) }); + } + + #[inline] + fn get_duty(&self) -> Self::Duty { + self.pwm_base.current_duty + } + + #[inline] + fn get_max_duty(&self) -> Self::Duty { + DUTY_MAX + } + }; +} + +macro_rules! pwm_impl { + () => { + #[inline] + fn disable(&mut self, _channel: Self::Channel) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| w.enable().clear_bit()); + } + + #[inline] + fn enable(&mut self, _channel: Self::Channel) { + self.reg + .get_reg_block() + .ctrl + .modify(|_, w| w.enable().set_bit()); + } + + #[inline] + fn get_period(&self) -> Self::Time { + self.pwm_base.current_period + } + + #[inline] + fn set_duty(&mut self, _channel: Self::Channel, duty: Self::Duty) { + self.pwm_base.current_duty = duty; + let pwma_val: u64 = (self.pwm_base.current_rst_val as u64 + * (DUTY_MAX as u64 - self.pwm_base.current_duty as u64)) + / DUTY_MAX as u64; + self.reg + .get_reg_block() + .pwma_value() + .write(|w| unsafe { w.bits(pwma_val as u32) }); + } + + #[inline] + fn set_period

(&mut self, period: P) + where + P: Into, + { + self.pwm_base.current_period = period.into(); + // Avoid division by 0 + if self.pwm_base.current_period.0 == 0 { + return; + } + self.pwm_base.current_rst_val = + self.pwm_base.sys_clk.0 / self.pwm_base.current_period.0; + let reg_block = self.reg.get_reg_block(); + reg_block + .rst_value + .write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) }); + reg_block + .cnt_value + .write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) }); + } + + #[inline(always)] + fn get_duty(&self, _channel: Self::Channel) -> Self::Duty { + self.pwm_base.current_duty + } + + #[inline(always)] + fn get_max_duty(&self) -> Self::Duty { + DUTY_MAX + } + }; +} + +impl embedded_hal::Pwm for PwmPin { + type Channel = (); + type Duty = u16; + type Time = Hertz; + + pwm_impl!(); +} + +impl embedded_hal::Pwm for ReducedPwmPin { + type Channel = (); + type Duty = u16; + type Time = Hertz; + + pwm_impl!(); +} + +impl embedded_hal::PwmPin for PwmPin { + type Duty = u16; + + pwm_pin_impl!(); +} + +impl embedded_hal::PwmPin for ReducedPwmPin { + type Duty = u16; + + pwm_pin_impl!(); +} + +/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0. +/// +/// Please note that this might load a lot of floating point code because this processor does not +/// have a FPU +pub fn get_duty_from_percent(percent: f32) -> u16 { + if percent > 1.0 { + DUTY_MAX + } else if percent <= 0.0 { + 0 + } else { + (percent * DUTY_MAX as f32) as u16 + } +} diff --git a/src/timer.rs b/src/timer.rs index f581064..9f5f40f 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -5,8 +5,15 @@ //! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs) use crate::{ clock::{enable_peripheral_clock, PeripheralClocks}, - pac, + gpio::{ + AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14, + PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8, + PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21, + PB22, PB23, PB3, PB4, PB5, PB6, + }, + pac::{self, tim0}, prelude::*, + private::Sealed, time::Hertz, timer, }; @@ -22,15 +29,9 @@ use void::Void; const IRQ_DST_NONE: u32 = 0xffffffff; pub static MS_COUNTER: Mutex> = Mutex::new(Cell::new(0)); -/// Hardware timers -pub struct CountDownTimer { - tim: TIM, - curr_freq: Hertz, - sys_clk: Hertz, - rst_val: u32, - last_cnt: u32, - listening: bool, -} +//================================================================================================== +// Defintions +//================================================================================================== /// Interrupt events pub enum Event { @@ -42,6 +43,234 @@ pub enum TimerErrors { Canceled, } +//================================================================================================== +// Valid TIM and PIN combinations +//================================================================================================== + +pub trait TimPin { + const DYN: DynPinId; +} + +pub trait ValidTim { + // TIM ID ranging from 0 to 23 for 24 TIM peripherals + const TIM_ID: u8; +} + +macro_rules! tim_marker { + ($TIMX:ident, $ID:expr) => { + impl ValidTim for $TIMX { + const TIM_ID: u8 = $ID; + } + }; +} + +tim_marker!(TIM0, 0); +tim_marker!(TIM1, 1); +tim_marker!(TIM2, 2); +tim_marker!(TIM3, 3); +tim_marker!(TIM4, 4); +tim_marker!(TIM5, 5); +tim_marker!(TIM6, 6); +tim_marker!(TIM7, 7); +tim_marker!(TIM8, 8); +tim_marker!(TIM9, 9); +tim_marker!(TIM10, 10); +tim_marker!(TIM11, 11); +tim_marker!(TIM12, 12); +tim_marker!(TIM13, 13); +tim_marker!(TIM14, 14); +tim_marker!(TIM15, 15); +tim_marker!(TIM16, 16); +tim_marker!(TIM17, 17); +tim_marker!(TIM18, 18); +tim_marker!(TIM19, 19); +tim_marker!(TIM20, 20); +tim_marker!(TIM21, 21); +tim_marker!(TIM22, 22); +tim_marker!(TIM23, 23); + +pub trait ValidTimAndPin: Sealed {} + +macro_rules! pin_and_tim { + ($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:ident) => { + impl TimPin for Pin<$PAX, $ALTFUNC> + where + $PAX: PinId, + { + const DYN: DynPinId = $PAX::DYN; + } + + impl ValidTimAndPin for (Pin<$PAX, $ALTFUNC>, $TIMX) + where + Pin<$PAX, $ALTFUNC>: TimPin, + $PAX: PinId, + { + } + + impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {} + }; +} + +pin_and_tim!(PA31, AltFunc2, 23, TIM23); +pin_and_tim!(PA30, AltFunc2, 22, TIM22); +pin_and_tim!(PA29, AltFunc2, 21, TIM21); +pin_and_tim!(PA28, AltFunc2, 20, TIM20); +pin_and_tim!(PA27, AltFunc2, 19, TIM19); +pin_and_tim!(PA26, AltFunc2, 18, TIM18); +pin_and_tim!(PA25, AltFunc2, 17, TIM17); +pin_and_tim!(PA24, AltFunc2, 16, TIM16); + +pin_and_tim!(PA15, AltFunc1, 15, TIM15); +pin_and_tim!(PA14, AltFunc1, 14, TIM14); +pin_and_tim!(PA13, AltFunc1, 13, TIM13); +pin_and_tim!(PA12, AltFunc1, 12, TIM12); +pin_and_tim!(PA11, AltFunc1, 11, TIM11); +pin_and_tim!(PA10, AltFunc1, 10, TIM10); +pin_and_tim!(PA9, AltFunc1, 9, TIM9); +pin_and_tim!(PA8, AltFunc1, 8, TIM8); +pin_and_tim!(PA7, AltFunc1, 7, TIM7); +pin_and_tim!(PA6, AltFunc1, 6, TIM6); +pin_and_tim!(PA5, AltFunc1, 5, TIM5); +pin_and_tim!(PA4, AltFunc1, 4, TIM4); +pin_and_tim!(PA3, AltFunc1, 3, TIM3); +pin_and_tim!(PA2, AltFunc1, 2, TIM2); +pin_and_tim!(PA1, AltFunc1, 1, TIM1); +pin_and_tim!(PA0, AltFunc1, 0, TIM0); + +pin_and_tim!(PB23, AltFunc3, 23, TIM23); +pin_and_tim!(PB22, AltFunc3, 22, TIM22); +pin_and_tim!(PB21, AltFunc3, 21, TIM21); +pin_and_tim!(PB20, AltFunc3, 20, TIM20); +pin_and_tim!(PB19, AltFunc3, 19, TIM19); +pin_and_tim!(PB18, AltFunc3, 18, TIM18); +pin_and_tim!(PB17, AltFunc3, 17, TIM17); +pin_and_tim!(PB16, AltFunc3, 16, TIM16); +pin_and_tim!(PB15, AltFunc3, 15, TIM15); +pin_and_tim!(PB14, AltFunc3, 14, TIM14); +pin_and_tim!(PB13, AltFunc3, 13, TIM13); +pin_and_tim!(PB12, AltFunc3, 12, TIM12); +pin_and_tim!(PB11, AltFunc3, 11, TIM11); +pin_and_tim!(PB10, AltFunc3, 10, TIM10); + +pin_and_tim!(PB6, AltFunc3, 6, TIM6); +pin_and_tim!(PB5, AltFunc3, 5, TIM5); +pin_and_tim!(PB4, AltFunc3, 4, TIM4); +pin_and_tim!(PB3, AltFunc3, 3, TIM3); +pin_and_tim!(PB2, AltFunc3, 2, TIM2); +pin_and_tim!(PB1, AltFunc3, 1, TIM1); +pin_and_tim!(PB0, AltFunc3, 0, TIM0); + +//================================================================================================== +// Register Interface +//================================================================================================== + +pub type TimRegBlock = tim0::RegisterBlock; + +/// Register interface. +/// +/// This interface provides valid TIM pins a way to access their corresponding TIM +/// registers +/// +/// # Safety +/// +/// Users should only implement the [`id`] function. No default function +/// implementations should be overridden. The implementing type must also have +/// "control" over the corresponding pin ID, i.e. it must guarantee that a each +/// pin ID is a singleton. +pub(super) unsafe trait TimRegInterface { + fn tim_id(&self) -> u8; + fn pin_id(&self) -> DynPinId; + + const PORT_BASE: *const tim0::RegisterBlock = TIM0::ptr() as *const _; + + /// All 24 TIM blocks are identical. This helper functions returns the correct + /// memory mapped peripheral depending on the TIM ID. + #[inline(always)] + fn get_reg_block(&self) -> &TimRegBlock { + unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) } + } + + #[inline(always)] + fn mask_32(&self) -> u32 { + 1 << self.tim_id() + } +} + +/// Provide a safe register interface for [`ValidTimAndPin`]s +/// +/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to +/// access the corresponding registers. +pub(super) struct TimRegister { + pin: PIN, + tim: TIM, +} + +impl TimRegister +where + (PIN, TIM): ValidTimAndPin, +{ + #[inline] + pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self { + TimRegister { pin, tim } + } + + pub(super) fn release(self) -> (PIN, TIM) { + (self.pin, self.tim) + } +} + +unsafe impl TimRegInterface for TimRegister { + #[inline(always)] + fn tim_id(&self) -> u8 { + TIM::TIM_ID + } + + #[inline(always)] + fn pin_id(&self) -> DynPinId { + PIN::DYN + } +} + +pub(super) struct TimDynRegister { + tim_id: u8, + pin_id: DynPinId, +} + +impl From> for TimDynRegister { + fn from(_reg: TimRegister) -> Self { + Self { + tim_id: TIM::TIM_ID, + pin_id: PIN::DYN, + } + } +} + +unsafe impl TimRegInterface for TimDynRegister { + #[inline(always)] + fn tim_id(&self) -> u8 { + self.tim_id + } + + #[inline(always)] + fn pin_id(&self) -> DynPinId { + self.pin_id + } +} + +//================================================================================================== +// Timers +//================================================================================================== + +/// Hardware timers +pub struct CountDownTimer { + tim: TIM, + curr_freq: Hertz, + sys_clk: Hertz, + rst_val: u32, + last_cnt: u32, + listening: bool, +} + fn enable_tim_clk(syscfg: &mut SYSCONFIG, idx: u8) { syscfg .tim_clk_enable