Added PWM implementation and example

This commit is contained in:
Robin Müller 2021-12-05 17:28:30 +01:00
parent baec0980b7
commit 9af01f3067
9 changed files with 783 additions and 12 deletions

View File

@ -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]

View File

@ -47,3 +47,7 @@ required-features = ["rt"]
[[example]]
name = "tests"
required-features = ["rt"]
[[example]]
name = "pwm"
required-features = ["rt"]

78
examples/pwm.rs Normal file
View File

@ -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<PWMB> = 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::<PWMA>::from(pwmb);
}
}
#[interrupt]
fn OC0() {
default_ms_irq_handler()
}

View File

@ -731,7 +731,7 @@ impl<I: PinId> InputPin for Pin<I, OutputReadablePushPull> {
/// 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<I: PinId> {
id: PhantomData<I>,
}

View File

@ -77,6 +77,7 @@ impl From<DynPinMode> for ModeFields {
fields
}
}
//==================================================================================================
// Register Interface
//==================================================================================================

View File

@ -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},

View File

@ -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;

457
src/pwm.rs Normal file
View File

@ -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<Hertz>) {
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<PIN: TimPin, TIM: ValidTim, MODE = PWMA> {
reg: TimRegister<PIN, TIM>,
pwm_base: PwmBase,
_mode: PhantomData<MODE>,
}
impl<PIN: TimPin, TIM: ValidTim, MODE> PwmPin<PIN, TIM, MODE>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
/// Create a new stronlgy typed PWM pin
pub fn new(
vtp: (PIN, TIM),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut SYSCONFIG,
initial_period: impl Into<Hertz> + 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<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PWMA>> for PwmPin<PIN, TIM, PWMB>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
fn from(other: PwmPin<PIN, TIM, PWMA>) -> Self {
let mut pwmb = Self {
reg: other.reg,
pwm_base: other.pwm_base,
_mode: PhantomData,
};
pwmb.enable_pwm_b();
pwmb
}
}
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PWMB>> for PwmPin<PIN, TIM, PWMA>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
fn from(other: PwmPin<PIN, TIM, PWMB>) -> Self {
let mut pwmb = Self {
reg: other.reg,
pwm_base: other.pwm_base,
_mode: PhantomData,
};
pwmb.enable_pwm_a();
pwmb
}
}
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMA>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
pub fn pwma(
vtp: (PIN, TIM),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut SYSCONFIG,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<PIN, TIM, PWMA> = Self::new(vtp, sys_clk, sys_cfg, initial_period);
pin.enable_pwm_a();
pin
}
}
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMB>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
pub fn pwmb(
vtp: (PIN, TIM),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut SYSCONFIG,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<PIN, TIM, PWMB> = 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<MODE = PWMA> {
reg: TimDynRegister,
pwm_base: PwmBase,
_pin_id: DynPinId,
_mode: PhantomData<MODE>,
}
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PWMA> {
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
ReducedPwmPin {
reg: TimDynRegister::from(pwm_pin.reg),
pwm_base: pwm_pin.pwm_base,
_pin_id: PIN::DYN,
_mode: PhantomData,
}
}
}
impl<MODE> ReducedPwmPin<MODE> {
pwm_common_func!();
}
impl From<ReducedPwmPin<PWMA>> for ReducedPwmPin<PWMB> {
fn from(other: ReducedPwmPin<PWMA>) -> 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<ReducedPwmPin<PWMB>> for ReducedPwmPin<PWMA> {
fn from(other: ReducedPwmPin<PWMB>) -> 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<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMB>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
pwmb_func!();
}
impl ReducedPwmPin<PWMB> {
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<P>(&mut self, period: P)
where
P: Into<Self::Time>,
{
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<PIN: TimPin, TIM: ValidTim> embedded_hal::Pwm for PwmPin<PIN, TIM> {
type Channel = ();
type Duty = u16;
type Time = Hertz;
pwm_impl!();
}
impl embedded_hal::Pwm for ReducedPwmPin<PWMA> {
type Channel = ();
type Duty = u16;
type Time = Hertz;
pwm_impl!();
}
impl<PIN: TimPin, TIM: ValidTim> embedded_hal::PwmPin for PwmPin<PIN, TIM> {
type Duty = u16;
pwm_pin_impl!();
}
impl embedded_hal::PwmPin for ReducedPwmPin<PWMA> {
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
}
}

View File

@ -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<Cell<u32>> = Mutex::new(Cell::new(0));
/// Hardware timers
pub struct CountDownTimer<TIM> {
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<PIN: TimPin, TIM: ValidTim>: 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<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> 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: TimPin, TIM: ValidTim> {
pin: PIN,
tim: TIM,
}
impl<PIN: TimPin, TIM: ValidTim> TimRegister<PIN, TIM>
where
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
#[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<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimRegister<PIN, TIM> {
#[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<PIN: TimPin, TIM: ValidTim> From<TimRegister<PIN, TIM>> for TimDynRegister {
fn from(_reg: TimRegister<PIN, TIM>) -> 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: 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