diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f354117..7fe8eef2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check - args: --features=${{ matrix.mcu }},rtic,high --examples + args: --features=${{ matrix.mcu }},rtic1,high --examples - uses: actions-rs/cargo@v1 with: diff --git a/.vscode/settings.json b/.vscode/settings.json index a0bc436b..19719248 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,19 +2,8 @@ "rust.all_targets": false, "rust.target": "thumbv7m-none-eabi", "rust.all_features": false, - "rust.features": [ - "rtic", - "stm32f103", - "medium" - ], + "rust.features": ["rtic1", "stm32f103", "medium"], "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave.extraArgs": [ - "--target", - "thumbv7m-none-eabi" - ], - "rust-analyzer.cargo.features": [ - "rtic", - "stm32f103", - "medium" - ] + "rust-analyzer.checkOnSave.extraArgs": ["--target", "thumbv7m-none-eabi"], + "rust-analyzer.cargo.features": ["rtic1", "stm32f103", "medium"] } diff --git a/.zed/settings.json b/.zed/settings.json index d43a68e7..f1f9d992 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -3,7 +3,7 @@ "rust-analyzer": { "initialization_options": { "cargo": { - "features": ["defmt", "rtic", "stm32f103", "medium"] + "features": ["defmt", "rtic1", "stm32f103", "medium"] }, "check": { "allTargets": false, diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6a5b38..24ce7f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Breaking changes + +- move pin connecting to timer channels after `Pwm` initialization [#517] + ### Changed - `timer.rs` refactoring +- `rtic` feature renamed to `rtic1` [#517] + +### Added + +- `rtic2` feature, timer `Capture` support [#517] + +[#517]: https://github.com/stm32-rs/stm32f1xx-hal/pull/517 ## [v0.11.0] - 2025-09-09 diff --git a/Cargo.toml b/Cargo.toml index 4edab856..7f2b7012 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ readme = "README.md" version = "0.11.0" [package.metadata.docs.rs] -features = ["stm32f103", "rtic", "high"] +features = ["stm32f103", "rtic1", "high"] default-target = "x86_64-unknown-linux-gnu" [dependencies] @@ -28,11 +28,21 @@ nb = "1.1" embedded-dma = "0.2.0" bxcan = "0.8.0" void = { default-features = false, version = "1.0.2" } -fugit = "0.3.7" +fugit = "0.3.9" fugit-timer = "0.1.3" -rtic-monotonic = { version = "1.0", optional = true } bitflags = "1.3.2" vcell = "0.1.3" +# rtic1 +rtic-monotonic = { version = "1.0", optional = true } +systick-monotonic = { version = "1.0.1", optional = true } +cortex-m-rtic = { version = "1.1.4", features = [ + "test-critical-section", +], optional = true } +# rtic2 +rtic-time = { version = "2.0.1", optional = true } +embedded-hal-async = { version = "1.0", optional = true } +rtic = { version = "2.2", features = ["thumbv7-backend"], optional = true } +atomic-polyfill = { version = "1.0.3", optional = true } [dependencies.stm32f1] version = "0.16.0" @@ -57,12 +67,12 @@ version = "0.8.0" optional = true [dev-dependencies] +defmt-rtt = "1.0" panic-halt = "1.0.0" panic-semihosting = "0.6.0" panic-itm = "0.4.2" panic-probe = "1.0" rtt-target = "0.6.1" -cortex-m-rtic = "1.1.4" cortex-m-semihosting = "0.5.0" heapless = "0.9.1" mfrc522 = { version = "0.8.0", features = ["eh02"] } @@ -93,7 +103,22 @@ has-can = [] # Devices with Dac has-dac = [] -rtic = ["rtic-monotonic"] +## Support monotonic timers and other stuff that can be used by [RTICv1 framework](https://crates.io/crates/cortex-m-rtic) +rtic1 = ["dep:rtic-monotonic", "dep:systick-monotonic", "cortex-m-rtic"] + +## Support monotonic timers and other stuff that can be used by [RTICv2 framework](https://crates.io/crates/rtic) +## +## Requires nightly rust compiler +rtic2 = [ + "dep:rtic-time", + "dep:rtic", + "dep:atomic-polyfill", + "dep:embedded-hal-async", +] +rtic-tim2 = [] +rtic-tim3 = [] +rtic-tim4 = [] +rtic-tim5 = [] [profile.dev] incremental = false @@ -114,7 +139,7 @@ required-features = ["stm32f103", "stm32-usbd"] [[example]] name = "usb_serial_rtic" -required-features = ["stm32f103", "stm32-usbd"] +required-features = ["stm32f103", "stm32-usbd", "rtic1"] [[example]] name = "blinky_timer_irq" @@ -130,12 +155,16 @@ required-features = ["medium"] [[example]] name = "timer-interrupt-rtic" -required-features = ["stm32f103", "medium"] +required-features = ["stm32f103", "medium", "rtic1"] [[example]] name = "exti" required-features = [] +[[example]] +name = "exti_rtic" +required-features = ["rtic1"] + [[example]] name = "can-echo" required-features = ["has-can"] @@ -146,7 +175,7 @@ required-features = ["has-can"] [[example]] name = "can-rtic" -required-features = ["has-can"] +required-features = ["has-can", "rtic1"] [[example]] name = "gpio_input" @@ -155,3 +184,7 @@ required-features = ["stm32f103"] [[example]] name = "serial-interrupt-idle" required-features = ["stm32f103", "medium"] + +[[example]] +name = "rtic2-timer-input-capture" +required-features = ["stm32f103", "rtic2"] diff --git a/examples/pwm.rs b/examples/pwm.rs index b229b00f..81adefe8 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -9,12 +9,7 @@ use panic_halt as _; use cortex_m::asm; use cortex_m_rt::entry; -use stm32f1xx_hal::{ - pac, - prelude::*, - time::ms, - timer::{Channel, Tim2NoRemap}, -}; +use stm32f1xx_hal::{pac, prelude::*, time::ms}; #[entry] fn main() -> ! { @@ -22,90 +17,70 @@ fn main() -> ! { let mut rcc = p.RCC.constrain(); - let mut afio = p.AFIO.constrain(&mut rcc); - - let mut gpioa = p.GPIOA.split(&mut rcc); + let gpioa = p.GPIOA.split(&mut rcc); // let mut gpiob = p.GPIOB.split(&mut rcc); // TIM2 - let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); - let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); - let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); + let c1 = gpioa.pa0; + let c2 = gpioa.pa1; + let c3 = gpioa.pa2; // If you don't want to use all channels, just leave some out - // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); - let pins = (c1, c2, c3); + // let c4 = gpioa.pa3; // TIM3 - // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl); - // let c2 = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); - // let c3 = gpiob.pb0.into_alternate_push_pull(&mut gpiob.crl); - // let c4 = gpiob.pb1.into_alternate_push_pull(&mut gpiob.crl); + // let c1 = gpioa.pa6; + // let c2 = gpioa.pa7; + // let c3 = gpiob.pb0; + // let c4 = gpiob.pb1; // TIM4 (Only available with the "medium" density feature) - // let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - // let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl); - // let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); - // let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); + // let c1 = gpiob.pb6; + // let c2 = gpiob.pb7; + // let c3 = gpiob.pb8; + // let c4 = gpiob.pb9; //let mut pwm = - // Timer::new(p.TIM2, &mut rcc).pwm_hz::(pins, &mut afio.mapr, 1.kHz()); + // Timer::new(p.TIM2, &mut rcc).pwm_hz(pins, 1.kHz()); // or - let mut pwm = p - .TIM2 - .pwm_hz::(pins, &mut afio.mapr, 1.kHz(), &mut rcc); + let (mut pwm_mgr, (pwm_c1, pwm_c2, pwm_c3, ..)) = p.TIM2.pwm_hz(1.kHz(), &mut rcc); // Enable clock on each of the channels - pwm.enable(Channel::C1); - pwm.enable(Channel::C2); - pwm.enable(Channel::C3); + let mut c1 = pwm_c1.with(c1); + c1.enable(); + let mut c2 = pwm_c2.with(c2); + c2.enable(); + let mut c3 = pwm_c3.with(c3); + c3.enable(); //// Operations affecting all defined channels on the Timer // Adjust period to 0.5 seconds - pwm.set_period(ms(500).into_rate()); + pwm_mgr.set_period(ms(500).into_rate()); asm::bkpt(); // Return to the original frequency - pwm.set_period(1.kHz()); + pwm_mgr.set_period(1.kHz()); asm::bkpt(); - let max = pwm.get_max_duty(); + let max = pwm_mgr.get_max_duty(); //// Operations affecting single channels can be accessed through //// the Pwm object or via dereferencing to the pin. // Use the Pwm object to set C3 to full strength - pwm.set_duty(Channel::C3, max); + c3.set_duty(max); asm::bkpt(); // Use the Pwm object to set C3 to be dim - pwm.set_duty(Channel::C3, max / 4); + c3.set_duty(max / 4); asm::bkpt(); // Use the Pwm object to set C3 to be zero - pwm.set_duty(Channel::C3, 0); - - asm::bkpt(); - - // Extract the PwmChannel for C3 - let mut pwm_channel = pwm.split().2; - - // Use the PwmChannel object to set C3 to be full strength - pwm_channel.set_duty(max); - - asm::bkpt(); - - // Use the PwmChannel object to set C3 to be dim - pwm_channel.set_duty(max / 4); - - asm::bkpt(); - - // Use the PwmChannel object to set C3 to be zero - pwm_channel.set_duty(0); + c3.set_duty(0); asm::bkpt(); diff --git a/examples/pwm_custom.rs b/examples/pwm_custom.rs index 654d4771..52bc9b03 100644 --- a/examples/pwm_custom.rs +++ b/examples/pwm_custom.rs @@ -8,7 +8,7 @@ use panic_halt as _; use cortex_m::asm; -use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; +use stm32f1xx_hal::{pac, prelude::*}; use cortex_m_rt::entry; @@ -27,30 +27,31 @@ fn main() -> ! { let p0 = pb4.into_alternate_push_pull(&mut gpiob.crl); let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); - let pwm = Timer::new(p.TIM3, &mut rcc).pwm_hz((p0, p1), &mut afio.mapr, 1.kHz()); + let (pwm, pwm_channels) = p.TIM3.remap(&mut afio.mapr).pwm_hz(1.kHz(), &mut rcc); let max = pwm.get_max_duty(); - let mut pwm_channels = pwm.split(); + let mut c1 = pwm_channels.0.with(p0); + let mut c2 = pwm_channels.1.with(p1); // Enable the individual channels - pwm_channels.0.enable(); - pwm_channels.1.enable(); + c1.enable(); + c2.enable(); // full - pwm_channels.0.set_duty(max); - pwm_channels.1.set_duty(max); + c1.set_duty(max); + c2.set_duty(max); asm::bkpt(); // dim - pwm_channels.1.set_duty(max / 4); + c2.set_duty(max / 4); asm::bkpt(); // zero - pwm_channels.0.set_duty(0); - pwm_channels.1.set_duty(0); + c1.set_duty(0); + c2.set_duty(0); asm::bkpt(); diff --git a/examples/rtic2-timer-input-capture.rs b/examples/rtic2-timer-input-capture.rs new file mode 100644 index 00000000..dfcaf64a --- /dev/null +++ b/examples/rtic2-timer-input-capture.rs @@ -0,0 +1,81 @@ +#![no_main] +#![no_std] + +use defmt_rtt as _; +use panic_probe as _; +use stm32f1xx_hal::{ + pac, + pac::{TIM2, TIM3}, + prelude::*, + rcc, + timer::{CaptureChannel, CaptureHzManager, CapturePolarity, Event, PwmChannel, Timer}, +}; + +use rtic::app; + +#[app(device = pac, dispatchers = [USART1], peripherals = true)] +mod app { + use super::*; + + #[shared] + struct Shared {} + + #[local] + struct Local { + tim3: CaptureHzManager, + ch1: CaptureChannel, + } + + #[init] + fn init(ctx: init::Context) -> (Shared, Local) { + let dp = ctx.device; + let mut flash = dp.FLASH.constrain(); + let mut rcc = dp + .RCC + .freeze(rcc::Config::hsi().sysclk(48.MHz()), &mut flash.acr); + let gpioa = dp.GPIOA.split(&mut rcc); + + // Configuration of TIM2 in PWM mode + let timer = Timer::new(dp.TIM2, &mut rcc); + let (_, (ch1, ..)) = timer.pwm_hz(893.Hz()); + let mut tim_2: PwmChannel = ch1.with(gpioa.pa0); + tim_2.set_duty(50); + tim_2.enable(); + + // It is necessary to connect pins PA0 and PA5 through a resistor of 1 kΩ - 10 kΩ + + // Configuration of TIM3 in input capture mode + let (mut tim3, (ch1, ..)) = Timer::new(dp.TIM3, &mut rcc).capture_hz(48.MHz()); + let mut ch1 = ch1.with(gpioa.pa6); + tim3.listen(Event::C1); + + ch1.set_polarity(CapturePolarity::ActiveHigh); + ch1.enable(); + + defmt::info!("Start"); + + (Shared {}, Local { tim3, ch1 }) + } + + #[task(binds = TIM3, local = [tim3, ch1, prev_capture: u32 = 0], priority = 3)] + fn tim3_interrupt(cx: tim3_interrupt::Context) { + if cx.local.tim3.get_interrupt().contains(Event::C1) { + let timer_clock = cx.local.tim3.get_timer_clock(); + let max_auto_reload = cx.local.tim3.get_max_auto_reload(); + let current_capture = cx.local.ch1.get_capture(); + + let delta = if current_capture >= *cx.local.prev_capture { + current_capture - *cx.local.prev_capture + } else { + (max_auto_reload - *cx.local.prev_capture) + current_capture + }; + + let freq = timer_clock as f32 / delta as f32; + + defmt::info!("Freq: {} Hz", freq); // Output = Freq: 893.00665 Hz + + *cx.local.prev_capture = current_capture; + cx.local.tim3.clear_interrupt(Event::C1); + } + } +} diff --git a/src/prelude.rs b/src/prelude.rs index fae1a65c..b7d6a5b4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -25,8 +25,10 @@ pub use crate::spi::SpiExt as _; pub use crate::time::U32Ext as _stm32_hal_time_U32Ext; pub use crate::timer::pwm_input::PwmInputExt as _; pub use crate::timer::pwm_input::QeiExt as _; -#[cfg(feature = "rtic")] +#[cfg(feature = "rtic1")] pub use crate::timer::MonoTimerExt as _stm32f4xx_hal_timer_MonoTimerExt; +#[cfg(feature = "rtic2")] +pub use crate::timer::MonoTimerExt as _; pub use crate::timer::PwmExt as _stm32f4xx_hal_timer_PwmExt; pub use crate::timer::SysTimerExt as _stm32f4xx_hal_timer_SysCounterExt; pub use crate::timer::TimerExt as _stm32f4xx_hal_timer_TimerExt; diff --git a/src/timer.rs b/src/timer.rs index ffa5c00a..841c27f7 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -48,6 +48,7 @@ */ #![allow(non_upper_case_globals)] +use crate::afio::Rmp; use crate::bb; use crate::pac::{self, DBGMCU as DBG}; @@ -58,15 +59,19 @@ use cortex_m::peripheral::SYST; use crate::time::Hertz; -#[cfg(feature = "rtic")] +#[cfg(feature = "rtic1")] pub mod monotonic; -#[cfg(feature = "rtic")] +#[cfg(feature = "rtic1")] pub use monotonic::*; -pub(crate) mod pins; -pub mod pwm_input; -pub use pins::*; +#[cfg(feature = "rtic2")] +pub mod monotonics; +#[cfg(feature = "rtic2")] +pub use monotonics::*; pub mod delay; +pub mod pwm_input; pub use delay::*; +pub mod capture; +pub use capture::*; pub mod counter; pub use counter::*; pub mod pwm; @@ -90,6 +95,15 @@ pub enum Channel { C4 = 3, } +pub use crate::afio::{TimC, TimNC}; + +/// Channel wrapper +pub struct Ch; +pub const C1: u8 = 0; +pub const C2: u8 = 1; +pub const C3: u8 = 2; +pub const C4: u8 = 3; + /// Compare/PWM polarity #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Polarity { @@ -384,8 +398,8 @@ mod sealed { fn read_cc_value(channel: u8) -> u32; fn set_cc_value(channel: u8, value: u32); fn enable_channel(channel: u8, b: bool); - fn set_channel_polarity(channel: u8, p: Polarity); - fn set_nchannel_polarity(channel: u8, p: Polarity); + fn set_pwm_channel_polarity(channel: u8, p: Polarity); + fn set_pwm_nchannel_polarity(channel: u8, p: Polarity); fn set_capture_channel_polarity(channel: u8, p: CapturePolarity); } @@ -417,6 +431,16 @@ mod sealed { type Mms; fn master_mode(&mut self, mode: Self::Mms); } + + pub trait Split { + type Channels; + fn split() -> Self::Channels; + } + + pub trait SplitCapture { + type CaptureChannels; + fn split_capture() -> Self::CaptureChannels; + } } pub(crate) use sealed::{Advanced, General, MasterTimer, WithCapture, WithChannel, WithPwm}; @@ -425,6 +449,45 @@ pub trait Instance: { } +use sealed::{Split, SplitCapture}; +macro_rules! split { + ($TIM:ty: 1) => { + split!($TIM, C1); + }; + ($TIM:ty: 2) => { + split!($TIM, C1, C2); + }; + ($TIM:ty: 4) => { + split!($TIM, C1, C2, C3, C4); + }; + ($TIM:ty, $($C:ident),+) => { + impl Split for $TIM { + type Channels = ($(PwmChannelDisabled<$TIM, $C, 0>,)+); + fn split() -> Self::Channels { + ($(PwmChannelDisabled::<_, $C, 0>::new(),)+) + } + } + impl SplitCapture for $TIM { + type CaptureChannels = ($(CaptureChannelDisabled<$TIM, $C, 0>,)+); + fn split_capture() -> Self::CaptureChannels { + ($(CaptureChannelDisabled::<_, $C, 0>::new(),)+) + } + } + impl Split for Rmp<$TIM, R> { + type Channels = ($(PwmChannelDisabled<$TIM, $C, R>,)+); + fn split() -> Self::Channels { + ($(PwmChannelDisabled::<_, $C, R>::new(),)+) + } + } + impl SplitCapture for Rmp<$TIM, R> { + type CaptureChannels = ($(CaptureChannelDisabled<$TIM, $C, R>,)+); + fn split_capture() -> Self::CaptureChannels { + ($(CaptureChannelDisabled::<_, $C, R>::new(),)+) + } + } + }; +} + macro_rules! hal { ($TIM:ty: [ $Timer:ident, @@ -560,7 +623,7 @@ macro_rules! hal { } #[inline(always)] - fn set_channel_polarity(c: u8, p: Polarity) { + fn set_pwm_channel_polarity(c: u8, p: Polarity) { let tim = unsafe { &*<$TIM>::ptr() }; if c < Self::CH_NUMBER { unsafe { bb::write(tim.ccer(), c*4 + 1, p == Polarity::ActiveLow); } @@ -568,7 +631,7 @@ macro_rules! hal { } #[inline(always)] - fn set_nchannel_polarity(c: u8, p: Polarity) { + fn set_pwm_nchannel_polarity(c: u8, p: Polarity) { let tim = unsafe { &*<$TIM>::ptr() }; if c < Self::COMP_CH_NUMBER { unsafe { bb::write(tim.ccer(), c*4 + 3, p == Polarity::ActiveLow); } @@ -648,6 +711,7 @@ macro_rules! hal { )? with_output!($TIM: $cnum $(, $aoe)?); + split!($TIM: $cnum); )? $(impl MasterTimer for $TIM { diff --git a/src/timer/capture.rs b/src/timer/capture.rs new file mode 100644 index 00000000..34bc0acc --- /dev/null +++ b/src/timer/capture.rs @@ -0,0 +1,258 @@ +//! Provides the core functionality of the Input Capture mode. +//! +//! The main way to enable the Input Capture mode is by calling +//! ```rust,ignore +//! Timer::new(dp.TIM5, &clocks).capture_hz(24.MHz()); +//! ``` +//! In the `capture_hz` method, the desired timer counter frequency is specified. +//! For high accuracy, it is recommended to use 32-bit timers (TIM2, TIM5) and to select the highest possible frequency, ideally the maximum frequency equal to the timer's clock frequency. +//! This returns a `CaptureHzManager` and a tuple of all `CaptureChannel`s supported by the timer. Additionally, the [`CaptureExt`] trait is implemented for `pac::TIMx` to simplify the creation of a new structure. +//! +//! ```rust,ignore +//! let (cc_manager, (cc_ch1, cc_ch2, ...)) = dp.TIM5.capture_hz(24.MHz(), &clocks); +//! ``` +//! +//! To enable a [`CaptureChannel`], you need to pass one or more valid pins supported by the channel using the `with` method. +//! +//! [`CaptureHzManager`] also provides additional methods for managing the Input Capture mode, such as `set_prescaler` and `set_filter`. + +use super::sealed::{Split, SplitCapture}; +use super::{ + CaptureFilter, CaptureMode, CapturePolarity, CapturePrescaler, Instance, Timer, WithCapture, +}; +pub use super::{Ch, C1, C2, C3, C4}; +use crate::afio::{RInto, TimC}; +use crate::rcc::Rcc; +use core::ops::{Deref, DerefMut}; +use fugit::HertzU32 as Hertz; + +pub trait CaptureExt +where + Self: Sized + Instance + WithCapture + SplitCapture, +{ + fn capture_hz( + self, + freq: Hertz, + rcc: &mut Rcc, + ) -> (CaptureHzManager, Self::CaptureChannels); +} + +impl CaptureExt for TIM +where + Self: Sized + Instance + WithCapture + SplitCapture, +{ + fn capture_hz( + self, + time: Hertz, + rcc: &mut Rcc, + ) -> (CaptureHzManager, Self::CaptureChannels) { + Timer::new(self, rcc).capture_hz(time) + } +} + +impl Timer { + // At a timer clock frequency of 100 MHz, + // the frequency should be in the range from 2000 Hz to the timer clock frequency. + // It is recommended to use 32-bit timers (TIM2, TIM5). + pub fn capture_hz(mut self, freq: Hertz) -> (CaptureHzManager, TIM::CaptureChannels) { + // The reference manual is a bit ambiguous about when enabling this bit is really + // necessary, but since we MUST enable the preload for the output channels then we + // might as well enable for the auto-reload too + self.tim.enable_preload(true); + + let psc = self.clk.raw() / freq.raw(); + assert!(self.clk.raw() % freq.raw() == 0); + assert!( + psc <= u16::MAX.into(), + "PSC value {} exceeds 16-bit limit (65535)", + psc + ); + + self.tim.set_prescaler(psc as u16 - 1); + self.tim.set_auto_reload(TIM::max_auto_reload()).unwrap(); + + // Trigger update event to load the registers + self.tim.trigger_update(); + + self.tim.start_capture(); + + (CaptureHzManager { timer: self }, TIM::split_capture()) + } +} + +pub struct CaptureChannelDisabled { + pub(super) tim: TIM, +} + +impl CaptureChannelDisabled { + pub(crate) fn new() -> Self { + Self { + tim: unsafe { TIM::steal() }, + } + } +} +impl CaptureChannelDisabled +where + TIM: Instance + WithCapture + crate::Steal + TimC, +{ + pub fn with(mut self, pin: impl RInto) -> CaptureChannel { + self.tim.preload_capture(C, CaptureMode::InputCapture); + CaptureChannel { + tim: self.tim, + pin: pin.rinto(), + } + } +} + +pub struct CaptureChannel, const C: u8, const COMP: bool = false> { + pub(super) tim: TIM, + #[allow(unused)] + pin: TIM::In, + // TODO: add complementary pins +} + +impl, const C: u8, const COMP: bool> + CaptureChannel +{ + pub const fn channel(&self) -> u8 { + C + } + /*pub fn release(mut self) -> (CaptureChannelDisabled, TIM::In) { + self.disable(); + (CaptureChannelDisabled { tim: self.tim }, self.pin) + }*/ + pub fn erase(self) -> CaptureErasedChannel { + CaptureErasedChannel { + _tim: self.tim, + channel: C, + } + } + + pub fn set_prescaler(&mut self, psc: CapturePrescaler) { + self.tim.prescaler_capture(C, psc); + } + + pub fn set_filter(&mut self, filter: CaptureFilter) { + self.tim.filter_capture(C, filter); + } +} + +pub struct CaptureErasedChannel { + _tim: TIM, + channel: u8, +} + +impl CaptureErasedChannel { + pub const fn channel(&self) -> u8 { + self.channel + } +} + +macro_rules! ch_impl { + () => { + /// Disable input capture channel + #[inline] + pub fn disable(&mut self) { + TIM::enable_channel(self.channel(), false); + } + + /// Enable input capture channel + #[inline] + pub fn enable(&mut self) { + TIM::enable_channel(self.channel(), true); + } + + /// Get capture value + #[inline] + pub fn get_capture(&self) -> u32 { + TIM::read_cc_value(self.channel()) + } + + /// Set input capture channel polarity + #[inline] + pub fn set_polarity(&mut self, p: CapturePolarity) { + TIM::set_capture_channel_polarity(self.channel(), p); + } + }; +} + +impl, const C: u8, const COMP: bool> + CaptureChannel +{ + ch_impl!(); +} + +impl CaptureErasedChannel { + ch_impl!(); +} + +pub struct CaptureHzManager +where + TIM: Instance + WithCapture, +{ + pub(super) timer: Timer, +} + +impl CaptureHzManager +where + TIM: Instance + WithCapture + Split, +{ + pub fn release(mut self, _channels: TIM::Channels) -> Timer { + // stop timer + self.tim.cr1_reset(); + self.timer + } +} + +impl Deref for CaptureHzManager +where + TIM: Instance + WithCapture, +{ + type Target = Timer; + fn deref(&self) -> &Self::Target { + &self.timer + } +} + +impl DerefMut for CaptureHzManager +where + TIM: Instance + WithCapture, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.timer + } +} + +impl CaptureHzManager +where + TIM: Instance + WithCapture, +{ + /// Get the PWM frequency of the timer in Hertz + pub fn get_timer_clock(&self) -> u32 { + let clk = self.clk; + let psc = self.tim.read_prescaler() as u32; + + // The frequency of the timer counter increment + (clk / (psc + 1)).raw() + } + + /// Set the frequency of the timer counter increment + pub fn set_timer_clock(&mut self, freq: Hertz) { + let clk = self.clk; + let psc = clk.raw() / freq.raw(); + assert!(self.clk.raw() % freq.raw() == 0); + assert!( + psc <= u16::MAX.into(), + "PSC value {} exceeds 16-bit limit (65535)", + psc + ); + + self.tim.set_prescaler(psc as u16 - 1); + self.tim.set_auto_reload(TIM::max_auto_reload()).unwrap(); + self.tim.cnt_reset(); + } + + pub fn get_max_auto_reload(&mut self) -> u32 { + TIM::max_auto_reload() + } +} diff --git a/src/timer/hal_02.rs b/src/timer/hal_02.rs index 42c4d26d..89942085 100644 --- a/src/timer/hal_02.rs +++ b/src/timer/hal_02.rs @@ -11,8 +11,8 @@ use fugit::{ExtU32, HertzU32 as Hertz, TimerDurationU32}; use void::Void; use super::{ - pins::sealed::Remap, pwm::Pins, Channel, Counter, CounterHz, Delay, Error, Instance, Pwm, - PwmChannel, PwmHz, SysCounter, SysCounterHz, SysDelay, WithPwm, + Counter, CounterHz, Delay, ErasedChannel, Error, Instance, PwmChannel, SysCounter, + SysCounterHz, SysDelay, TimC, WithPwm, }; impl DelayUs for SysDelay { @@ -133,7 +133,9 @@ impl Cancel for SysCounter { } } -impl embedded_hal_02::PwmPin for PwmChannel { +impl, const C: u8, const COMP: bool> embedded_hal_02::PwmPin + for PwmChannel +{ type Duty = u16; fn disable(&mut self) { @@ -153,46 +155,23 @@ impl embedded_hal_02::PwmPin for PwmChanne } } -impl embedded_hal_02::Pwm for PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - type Channel = Channel; +impl embedded_hal_02::PwmPin for ErasedChannel { type Duty = u16; - type Time = Hertz; - fn enable(&mut self, channel: Self::Channel) { - self.enable(channel) - } - - fn disable(&mut self, channel: Self::Channel) { - self.disable(channel) + fn disable(&mut self) { + self.disable() } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty(channel) + fn enable(&mut self) { + self.enable() } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - self.set_duty(channel, duty) + fn get_duty(&self) -> Self::Duty { + self.get_duty() } - - /// If `0` returned means max_duty is 2^16 fn get_max_duty(&self) -> Self::Duty { self.get_max_duty() } - - fn get_period(&self) -> Self::Time { - self.get_period() - } - - fn set_period(&mut self, period: T) - where - T: Into, - { - self.set_period(period.into()) + fn set_duty(&mut self, duty: Self::Duty) { + self.set_duty(duty) } } @@ -263,46 +242,3 @@ impl Cancel for Counter { self.cancel() } } - -impl embedded_hal_02::Pwm for Pwm -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - type Channel = Channel; - type Duty = u16; - type Time = TimerDurationU32; - - fn enable(&mut self, channel: Self::Channel) { - self.enable(channel) - } - - fn disable(&mut self, channel: Self::Channel) { - self.disable(channel) - } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty(channel) - } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - self.set_duty(channel, duty) - } - - /// If `0` returned means max_duty is 2^16 - fn get_max_duty(&self) -> Self::Duty { - self.get_max_duty() - } - - fn get_period(&self) -> Self::Time { - self.get_period() - } - - fn set_period(&mut self, period: T) - where - T: Into, - { - self.set_period(period.into()) - } -} diff --git a/src/timer/hal_1.rs b/src/timer/hal_1.rs index d022771d..c7444ea9 100644 --- a/src/timer/hal_1.rs +++ b/src/timer/hal_1.rs @@ -1,12 +1,7 @@ -//! Delay implementation based on general-purpose 32 bit timers and System timer (SysTick). -//! -//! TIM2 and TIM5 are a general purpose 32-bit auto-reload up/downcounter with -//! a 16-bit prescaler. - use core::convert::Infallible; use embedded_hal::delay::DelayNs; -use super::{Delay, Instance, PwmChannel, SysDelay, WithPwm}; +use super::{Delay, ErasedChannel, Instance, PwmChannel, SysDelay, TimC, WithPwm}; use fugit::ExtU32Ceil; impl DelayNs for SysDelay { @@ -33,11 +28,29 @@ impl DelayNs for Delay { } } -impl embedded_hal::pwm::ErrorType for PwmChannel { +impl, const C: u8, const COMP: bool> embedded_hal::pwm::ErrorType + for PwmChannel +{ + type Error = Infallible; +} + +impl, const C: u8, const COMP: bool> + embedded_hal::pwm::SetDutyCycle for PwmChannel +{ + fn max_duty_cycle(&self) -> u16 { + self.get_max_duty() + } + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + self.set_duty(duty); + Ok(()) + } +} + +impl embedded_hal::pwm::ErrorType for ErasedChannel { type Error = Infallible; } -impl embedded_hal::pwm::SetDutyCycle for PwmChannel { +impl embedded_hal::pwm::SetDutyCycle for ErasedChannel { fn max_duty_cycle(&self) -> u16 { self.get_max_duty() } diff --git a/src/timer/monotonics.rs b/src/timer/monotonics.rs new file mode 100644 index 00000000..0996a2de --- /dev/null +++ b/src/timer/monotonics.rs @@ -0,0 +1,299 @@ +// RTICv2 Monotonic impl +use super::{FTimer, General}; +use crate::{pac, rcc::Rcc}; +use atomic_polyfill::{AtomicU64, Ordering}; +use core::marker::PhantomData; +use rtic_time::timer_queue::TimerQueueBackend; +use rtic_time::{ + half_period_counter::calculate_now, monotonic::TimerQueueBasedMonotonic, + timer_queue::TimerQueue, Monotonic, +}; + +pub struct MonoTimer { + _tim: PhantomData, +} +/// `MonoTimer` with precision of 1 μs (1 MHz sampling) +pub type MonoTimerUs = MonoTimer; + +pub struct MonoTimerBackend { + _tim: PhantomData, +} + +impl TimerQueueBasedMonotonic for MonoTimer +where + MonoTimerBackend: TimerQueueBackend, +{ + type Backend = MonoTimerBackend; + type Instant = fugit::TimerInstantU64; + type Duration = fugit::TimerDurationU64; +} + +pub trait MonoTimerExt: Sized { + fn monotonic( + self, + nvic: &mut cortex_m::peripheral::NVIC, + rcc: &mut Rcc, + ) -> MonoTimer; + fn monotonic_us( + self, + nvic: &mut cortex_m::peripheral::NVIC, + rcc: &mut Rcc, + ) -> MonoTimer { + self.monotonic::<1_000_000>(nvic, rcc) + } +} + +impl embedded_hal::delay::DelayNs for MonoTimer +where + Self: + Monotonic, Duration = fugit::TimerDurationU64>, +{ + fn delay_ns(&mut self, ns: u32) { + let now = Self::now(); + let mut done = now + ::Duration::nanos_at_least(ns.into()); + if now != done { + // Compensate for sub-tick uncertainty + done += ::Duration::from_ticks(1); + } + + while Self::now() < done {} + } + + fn delay_us(&mut self, us: u32) { + let now = Self::now(); + let mut done = now + ::Duration::micros_at_least(us.into()); + if now != done { + // Compensate for sub-tick uncertainty + done += ::Duration::from_ticks(1); + } + + while Self::now() < done {} + } + + fn delay_ms(&mut self, ms: u32) { + let now = Self::now(); + let mut done = now + ::Duration::millis_at_least(ms.into()); + if now != done { + // Compensate for sub-tick uncertainty + done += ::Duration::from_ticks(1); + } + + while Self::now() < done {} + } +} + +impl embedded_hal_async::delay::DelayNs for MonoTimer +where + Self: + Monotonic, Duration = fugit::TimerDurationU64>, +{ + #[inline] + async fn delay_ns(&mut self, ns: u32) { + Self::delay(::Duration::nanos_at_least(ns.into())).await; + } + + #[inline] + async fn delay_us(&mut self, us: u32) { + Self::delay(::Duration::micros_at_least(us.into())).await; + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) { + Self::delay(::Duration::millis_at_least(ms.into())).await; + } +} + +const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { + ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) +} + +pub(crate) unsafe fn set_monotonic_prio( + nvic: &mut cortex_m::peripheral::NVIC, + prio_bits: u8, + interrupt: impl cortex_m::interrupt::InterruptNumber, +) { + extern "C" { + static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8; + } + + let max_prio = RTIC_ASYNC_MAX_LOGICAL_PRIO.max(1).min(1 << prio_bits); + + let hw_prio = cortex_logical2hw(max_prio, prio_bits); + + nvic.set_priority(interrupt, hw_prio); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_stm32_timer_interrupt { + ($timer:ident) => { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn $timer() { + use monomod::TimerQueueBackend; + use $crate::timer::monotonics as monomod; + monomod::MonoTimerBackend::<$crate::pac::$timer>::timer_queue() + .on_monotonic_interrupt(); + } + }; +} + +macro_rules! make_timer { + ($tim: ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + static $overflow: AtomicU64 = AtomicU64::new(0); + static $tq: TimerQueue> = TimerQueue::new(); + + impl MonoTimerExt for pac::$timer { + fn monotonic( + self, + nvic: &mut cortex_m::peripheral::NVIC, + rcc: &mut Rcc, + ) -> MonoTimer { + FTimer::new(self, rcc).monotonic(nvic) + } + } + + impl FTimer { + pub fn monotonic( + mut self, + nvic: &mut cortex_m::peripheral::NVIC, + ) -> MonoTimer { + __internal_create_stm32_timer_interrupt!($timer); + + // Enable full-period interrupt. + self.tim.dier().modify(|_, w| w.uie().set_bit()); + + // Configure and enable half-period interrupt + self.tim.ccr1().write(|w| { + w.ccr().set( + (::Width::MAX + - (::Width::MAX >> 1)) + .into(), + ) + }); + self.tim.dier().modify(|_, w| w.cc1ie().set_bit()); + + // Trigger an update event to load the prescaler value to the clock. + self.tim.egr().write(|w| w.ug().set_bit()); + + // The above line raises an update event which will indicate that the timer is already finished. + // Since this is not the case, it should be cleared. + self.tim.sr().write(|w| w.uif().clear_bit()); + + $tq.initialize(MonoTimerBackend:: { _tim: PhantomData }); + $overflow.store(0, Ordering::SeqCst); + + // Start the counter. + self.tim.enable_counter(true); + + // SAFETY: We take full ownership of the peripheral and interrupt vector, + // plus we are not using any external shared resources so we won't impact + // basepri/source masking based critical sections. + unsafe { + set_monotonic_prio(nvic, pac::NVIC_PRIO_BITS, ::IRQ); + cortex_m::peripheral::NVIC::unmask(::IRQ); + } + MonoTimer { _tim: PhantomData } + } + } + + impl MonoTimerBackend { + #[inline(always)] + fn tim() -> &'static pac::$tim::RegisterBlock { + unsafe { &*::ptr() } + } + } + + impl TimerQueueBackend for MonoTimerBackend { + type Ticks = u64; + + fn now() -> Self::Ticks { + calculate_now( + || $overflow.load(Ordering::Relaxed), + || Self::tim().cnt().read().cnt().bits(), + ) + } + + fn set_compare(instant: Self::Ticks) { + let now = Self::now(); + + // Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left. + // `wrapping_sub` takes care of the u64 integer overflow special case. + let val = + if instant.wrapping_sub(now) <= (::Width::MAX as u64) { + instant as ::Width + } else { + // In the past or will overflow + 0 + }; + + Self::tim().ccr2().write(|r| r.ccr().set(val.into())); + } + + fn clear_compare_flag() { + Self::tim().sr().write(|w| w.cc2if().clear_bit()); + } + + fn pend_interrupt() { + cortex_m::peripheral::NVIC::pend(::IRQ); + } + + fn enable_timer() { + Self::tim().dier().modify(|_, w| w.cc2ie().set_bit()); + } + + fn disable_timer() { + Self::tim().dier().modify(|_, w| w.cc2ie().clear_bit()); + } + + fn on_interrupt() { + // Full period + if Self::tim().sr().read().uif().bit_is_set() { + Self::tim().sr().write(|w| w.uif().clear_bit()); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 1, "Monotonic must have missed an interrupt!"); + } + // Half period + if Self::tim().sr().read().cc1if().bit_is_set() { + Self::tim().sr().write(|w| w.cc1if().clear_bit()); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!"); + } + } + + fn timer_queue() -> &'static TimerQueue { + &$tq + } + } + }; +} + +#[cfg(all(feature = "rtic-tim2"))] +make_timer!(tim2, TIM2, TIMER2_OVERFLOWS, TIMER2_TQ); + +#[cfg(all(feature = "rtic-tim3"))] +make_timer!(tim3, TIM3, TIMER3_OVERFLOWS, TIMER3_TQ); + +#[cfg(all(feature = "medium", feature = "rtic-tim4"))] +make_timer!(tim4, TIM4, TIMER4_OVERFLOWS, TIMER4_TQ); + +#[cfg(all(any(feature = "high", feature = "connectivity"), feature = "rtic-tim5"))] +make_timer!(tim5, TIM5, TIMER5_OVERFLOWS, TIMER5_TQ); + +pub trait Irq { + const IRQ: pac::Interrupt; +} +impl Irq for pac::TIM2 { + const IRQ: pac::Interrupt = pac::Interrupt::TIM2; +} +impl Irq for pac::TIM3 { + const IRQ: pac::Interrupt = pac::Interrupt::TIM3; +} +#[cfg(feature = "medium")] +impl Irq for pac::TIM4 { + const IRQ: pac::Interrupt = pac::Interrupt::TIM4; +} +#[cfg(any(feature = "high", feature = "connectivity"))] +impl Irq for pac::TIM5 { + const IRQ: pac::Interrupt = pac::Interrupt::TIM5; +} diff --git a/src/timer/pins.rs b/src/timer/pins.rs deleted file mode 100644 index e0e9c716..00000000 --- a/src/timer/pins.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::pac; - -pub trait CPin {} -pub struct Ch; -pub const C1: u8 = 0; -pub const C2: u8 = 1; -pub const C3: u8 = 2; -pub const C4: u8 = 3; - -pub(crate) mod sealed { - pub trait Remap { - type Periph; - const REMAP: u8; - - fn remap(mapr: &mut crate::afio::MAPR); - } -} - -macro_rules! remap { - ($($name:ident: ($TIMX:ty, $state:literal, $P1:ident, $P2:ident, $P3:ident, $P4:ident, { $remapex:expr }),)+) => { - $( - pub struct $name; - impl sealed::Remap for $name { - type Periph = $TIMX; - const REMAP: u8 = $state; - - fn remap(mapr: &mut crate::afio::MAPR) { - mapr.modify_mapr($remapex); - } - } - impl CPin<$name, 0> for crate::gpio::$P1 {} - impl CPin<$name, 1> for crate::gpio::$P2 {} - impl CPin<$name, 2> for crate::gpio::$P3 {} - impl CPin<$name, 3> for crate::gpio::$P4 {} - )+ - } -} - -#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))] -remap!( - Tim1NoRemap: (pac::TIM1, 0b00, PA8, PA9, PA10, PA11, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), - //Tim1PartialRemap: (pac::TIM1, 0b01, PA8, PA9, PA10, PA11), - Tim1FullRemap: (pac::TIM1, 0b11, PE9, PE11, PE13, PE14, {|_, w| unsafe { w.tim1_remap().bits(Self::REMAP)}}), -); - -remap!( - Tim2NoRemap: (pac::TIM2, 0b00, PA0, PA1, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2PartialRemap1: (pac::TIM2, 0b01, PA15, PB3, PA2, PA3, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2PartialRemap2: (pac::TIM2, 0b10, PA0, PA1, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - Tim2FullRemap: (pac::TIM2, 0b11, PA15, PB3, PB10, PB11, {|_, w| unsafe { w.tim2_remap().bits(Self::REMAP)}}), - - Tim3NoRemap: (pac::TIM3, 0b00, PA6, PA7, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), - Tim3PartialRemap: (pac::TIM3, 0b10, PB4, PB5, PB0, PB1, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), - Tim3FullRemap: (pac::TIM3, 0b11, PC6, PC7, PC8, PC9, {|_, w| unsafe { w.tim3_remap().bits(Self::REMAP)}}), -); - -#[cfg(feature = "medium")] -remap!( - Tim4NoRemap: (pac::TIM4, 0b00, PB6, PB7, PB8, PB9, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), - Tim4Remap: (pac::TIM4, 0b01, PD12, PD13, PD14, PD15, {|_, w| w.tim4_remap().bit(Self::REMAP == 1)}), -); diff --git a/src/timer/pwm.rs b/src/timer/pwm.rs index c693980e..7653402e 100644 --- a/src/timer/pwm.rs +++ b/src/timer/pwm.rs @@ -1,421 +1,361 @@ -/*! - # Pulse width modulation - - The general purpose timers (`TIM2`, `TIM3`, and `TIM4`) can be used to output - pulse width modulated signals on some pins. The timers support up to 4 - simultaneous pwm outputs in separate `Channels` - - ## Usage for pre-defined channel combinations - - This crate only defines basic channel combinations for default AFIO remappings, - where all the channels are enabled. Start by setting all the pins for the - timer you want to use to alternate push pull pins: - - ```rust - let gpioa = ..; // Set up and split GPIOA - // Select the pins you want to use - let pins = ( - gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl), - gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl), - ); - - // Set up the timer as a PWM output. If selected pins may correspond to different remap options, - // then you must specify the remap generic parameter. Otherwise, if there is no such ambiguity, - // the remap generic parameter can be omitted without complains from the compiler. - let (c1, c2, c3, c4) = Timer::tim2(p.TIM2, &clocks) - .pwm_hz::(pins, &mut afio.mapr, 1.kHz()) - .3; - - // Start using the channels - c1.set_duty(c1.get_max_duty()); - // ... - ``` - - Then call the `pwm` function on the corresponding timer. - - NOTE: In some cases you need to specify remap you need, especially for TIM2 - (see [Alternate function remapping](super)): - - ``` - let device: pac::Peripherals = ..; - - // Put the timer in PWM mode using the specified pins - // with a frequency of 100 Hz. - let (c0, c1, c2, c3) = Timer::tim2(device.TIM2, &clocks) - .pwm_hz::(pins, &mut afio.mapr, 100.Hz()); - - // Set the duty cycle of channel 0 to 50% - c0.set_duty(c0.get_max_duty() / 2); - // PWM outputs are disabled by default - c0.enable() - ``` -*/ - -use crate::afio::MAPR; -use crate::gpio::{self, Alternate}; - -use super::{compute_arr_presc, Channel, FTimer, Instance, Ocm, Timer, WithPwm}; +//! Provides basic Pulse-width modulation (PWM) capabilities +//! +//! There are 2 main structures [`Pwm`] and [`PwmHz`]. Both structures implement [`embedded_hal_02::Pwm`] and have some additional API. +//! +//! First one is based on [`FTimer`] with fixed prescaler +//! and easy to use with [`fugit::TimerDurationU32`] for setting pulse width and period without advanced calculations. +//! +//! Second one is based on [`Timer`] with dynamic internally calculated prescaler and require [`fugit::Hertz`] to set period. +//! +//! The main way to run PWM is calling [`FTimer::pwm`] with initial `period`/`frequency` corresponding PWM period. +//! This returns [`PwmManager`] and a tuple of all [`PwmChannel`]s supported by timer. +//! Also there is [`PwmExt`] trait implemented on `pac::TIMx` to simplify creating new structure. +//! +//! ```rust,ignore +//! let (pwm_manager, (pwm_ch1, pwm_ch2, ..)) = dp.TIM1.pwm_us(100.micros(), &mut rcc); +//! ``` +//! +//! Each `PwmChannel` implements [`embedded_hal::pwm::SetDutyCycle`]. +//! They are disabled. +//! To enable `PwmChannel` you need to pass one or more regular pins allowed by channel +//! using `with` or `with_open_drain`. +//! Also you can pass complementary pins by `.with_complementary(other_complementary_pin)`. +//! After connecting pins you can dynamically enable main or complementary channels with `enable` and `enable_complementary` +//! and change their polarity with `set_polarity` and `set_complementary_polarity`. +//! +//! ```rust,ignore +//! let mut pwm_c1 = pwm_c1.with(gpioa.pa8).with_complementary(gpioa.pa7); +//! pwm_c1.enable(); +//! pwm_c1.enable_complementary(); +//! ``` +//! +//! By default `PwmChannel` contains information about connected pins to be possible to `release` them. +//! But you can `erase` this information to constuct [`ErasedChannel`] which can be collected to array. +//! Note that this operation is irreversible. +//! +//! `PwmManager` allows you to change PWM `period`/`frequency` and also has methods for advanced PWM control. + +use super::sealed::Split; +use super::{ + compute_arr_presc, Advanced, CenterAlignedMode, FTimer, IdleState, Instance, Ocm, Polarity, + TimC, TimNC, Timer, WithPwm, +}; +pub use super::{Ch, C1, C2, C3, C4}; +use crate::afio::{RInto, Rmp}; use crate::rcc::Rcc; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use fugit::{HertzU32 as Hertz, TimerDurationU32}; -pub trait Pins { - const C1: bool = false; - const C2: bool = false; - const C3: bool = false; - const C4: bool = false; - type Channels; - - fn check_used(c: Channel) -> Channel { - if (c == Channel::C1 && Self::C1) - || (c == Channel::C2 && Self::C2) - || (c == Channel::C3 && Self::C3) - || (c == Channel::C4 && Self::C4) - { - c - } else { - panic!("Unused channel") - } - } - - fn split() -> Self::Channels; -} - -pub use super::{pins::sealed::Remap, CPin, Ch, C1, C2, C3, C4}; - -pub struct PwmChannel { - pub(super) _tim: PhantomData, -} - -macro_rules! pins_impl { - ( $( ( $($PINX:ident),+ ), ( $($ENCHX:ident),+ ); )+ ) => { - $( - #[allow(unused_parens)] - impl Pins),+)> for ($($PINX),+) - where - TIM: Instance + WithPwm, - REMAP: Remap, - $($PINX: CPin + gpio::PinExt>,)+ - { - $(const $ENCHX: bool = true;)+ - type Channels = ($(PwmChannel),+); - fn split() -> Self::Channels { - ($(PwmChannel::::new()),+) - } - } - )+ - }; -} - -pins_impl!( - (P1, P2, P3, P4), (C1, C2, C3, C4); - (P2, P3, P4), (C2, C3, C4); - (P1, P3, P4), (C1, C3, C4); - (P1, P2, P4), (C1, C2, C4); - (P1, P2, P3), (C1, C2, C3); - (P3, P4), (C3, C4); - (P2, P4), (C2, C4); - (P2, P3), (C2, C3); - (P1, P4), (C1, C4); - (P1, P3), (C1, C3); - (P1, P2), (C1, C2); - (P1), (C1); - (P2), (C2); - (P3), (C3); - (P4), (C4); -); - pub trait PwmExt where - Self: Sized + Instance + WithPwm, + Self: Sized + Instance + WithPwm + Split, { - fn pwm( + fn pwm( self, - pins: PINS, - mapr: &mut MAPR, time: TimerDurationU32, rcc: &mut Rcc, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins; + ) -> (PwmManager, Self::Channels); - fn pwm_hz( - self, - pins: PINS, - mapr: &mut MAPR, - freq: Hertz, - rcc: &mut Rcc, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins; + fn pwm_hz(self, freq: Hertz, rcc: &mut Rcc) -> (PwmHzManager, Self::Channels); - fn pwm_us( + fn pwm_us( self, - pins: PINS, - mapr: &mut MAPR, time: TimerDurationU32<1_000_000>, rcc: &mut Rcc, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - self.pwm::<_, _, _, 1_000_000>(pins, mapr, time, rcc) + ) -> (PwmManager, Self::Channels) { + self.pwm::<1_000_000>(time, rcc) } } impl PwmExt for TIM where - Self: Sized + Instance + WithPwm, + Self: Sized + Instance + WithPwm + Split, { - fn pwm( + fn pwm( self, - pins: PINS, - mapr: &mut MAPR, time: TimerDurationU32, rcc: &mut Rcc, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - FTimer::::new(self, rcc).pwm(pins, mapr, time) + ) -> (PwmManager, Self::Channels) { + FTimer::::new(self, rcc).pwm(time) } - fn pwm_hz( + fn pwm_hz(self, freq: Hertz, rcc: &mut Rcc) -> (PwmHzManager, Self::Channels) { + Timer::new(self, rcc).pwm_hz(freq) + } +} + +impl Rmp +where + TIM: Sized + Instance + WithPwm, + Self: Split, +{ + pub fn pwm( self, - pins: PINS, - mapr: &mut MAPR, - time: Hertz, + time: TimerDurationU32, rcc: &mut Rcc, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins, - { - Timer::new(self, rcc).pwm_hz(pins, mapr, time) + ) -> (PwmManager, ::Channels) { + let mut timer = FTimer::::new(self.0, rcc); + timer._pwm_init(time); + (PwmManager { timer }, Self::split()) } -} -impl PwmChannel { - pub(crate) fn new() -> Self { - Self { - _tim: core::marker::PhantomData, - } + pub fn pwm_us( + self, + time: TimerDurationU32<1_000_000>, + rcc: &mut Rcc, + ) -> (PwmManager, ::Channels) { + self.pwm::<1_000_000>(time, rcc) + } + + pub fn pwm_hz( + self, + freq: Hertz, + rcc: &mut Rcc, + ) -> (PwmHzManager, ::Channels) { + let mut timer = Timer::new(self.0, rcc); + timer._pwm_hz_init(freq); + (PwmHzManager { timer }, Self::split()) } } -impl PwmChannel { - #[inline] - pub fn disable(&mut self) { - TIM::enable_channel(C, false); +impl Timer { + pub fn pwm_hz(mut self, freq: Hertz) -> (PwmHzManager, TIM::Channels) { + self._pwm_hz_init(freq); + (PwmHzManager { timer: self }, TIM::split()) } +} +impl Timer { + fn _pwm_hz_init(&mut self, freq: Hertz) { + // The reference manual is a bit ambiguous about when enabling this bit is really + // necessary, but since we MUST enable the preload for the output channels then we + // might as well enable for the auto-reload too + self.tim.enable_preload(true); - #[inline] - pub fn enable(&mut self) { - TIM::enable_channel(C, true); + let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw()); + self.tim.set_prescaler(psc); + self.tim.set_auto_reload(arr).unwrap(); + + // Trigger update event to load the registers + self.tim.trigger_update(); + + self.tim.start_pwm(); } +} - #[inline] - pub fn get_duty(&self) -> u16 { - TIM::read_cc_value(C) as u16 +impl FTimer { + pub fn pwm(mut self, time: TimerDurationU32) -> (PwmManager, TIM::Channels) { + self._pwm_init(time); + (PwmManager { timer: self }, TIM::split()) } +} +impl FTimer { + fn _pwm_init(&mut self, time: TimerDurationU32) { + // The reference manual is a bit ambiguous about when enabling this bit is really + // necessary, but since we MUST enable the preload for the output channels then we + // might as well enable for the auto-reload too + self.tim.enable_preload(true); - /// If `0` returned means max_duty is 2^16 - #[inline] - pub fn get_max_duty(&self) -> u16 { - (TIM::read_auto_reload() as u16).wrapping_add(1) + self.tim.set_auto_reload(time.ticks() - 1).unwrap(); + + // Trigger update event to load the registers + self.tim.trigger_update(); + + self.tim.start_pwm(); } +} + +pub struct PwmChannelDisabled { + pub(super) tim: TIM, +} - #[inline] - pub fn set_duty(&mut self, duty: u16) { - TIM::set_cc_value(C, duty as u32) +impl PwmChannelDisabled { + pub(crate) fn new() -> Self { + Self { + tim: unsafe { TIM::steal() }, + } } } -pub struct PwmHz +impl PwmChannelDisabled where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, + TIM: TimC, { - timer: Timer, - _pins: PhantomData<(REMAP, P, PINS)>, + pub fn with(mut self, pin: impl RInto) -> PwmChannel { + self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1); + PwmChannel { + tim: self.tim, + regular: pin.rinto(), + } + } } -impl PwmHz +impl PwmChannelDisabled where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, + TIM: TimC, + TIM: TimNC, { - pub fn release(mut self) -> Timer { - // stop timer - self.tim.cr1_reset(); - self.timer + pub fn with_regular_and_complementary( + mut self, + reg_pin: impl RInto, + comp_pin: impl RInto, + ) -> PwmChannel { + self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1); + let regular = reg_pin.rinto(); + let _ = comp_pin.rinto(); + PwmChannel { + tim: self.tim, + regular, + } } +} - pub fn split(self) -> PINS::Channels { - PINS::split() - } +pub struct PwmChannel, const C: u8, const COMP: bool = false> { + pub(super) tim: TIM, + #[allow(unused)] + regular: TIM::Out, + // TODO: add complementary pins } -impl Deref for PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - type Target = Timer; - fn deref(&self) -> &Self::Target { - &self.timer +impl, const C: u8, const COMP: bool> PwmChannel { + pub const fn channel(&self) -> u8 { + C + } + /*pub fn release(mut self) -> (PwmChannelDisabled, TIM::Out) { + self.tim.freeze_output_channel(C); + (PwmChannelDisabled { tim: self.tim }, self.regular) + }*/ + pub fn erase(self) -> ErasedChannel { + ErasedChannel { + _tim: self.tim, + channel: C, + } } } -impl DerefMut for PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.timer +pub struct ErasedChannel { + _tim: TIM, + channel: u8, +} + +impl ErasedChannel { + pub const fn channel(&self) -> u8 { + self.channel } } -impl Timer { - pub fn pwm_hz( - mut self, - _pins: PINS, - mapr: &mut MAPR, - freq: Hertz, - ) -> PwmHz - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - - if PINS::C1 { - self.tim - .preload_output_channel_in_mode(Channel::C1 as u8, Ocm::PwmMode1); +macro_rules! ch_impl { + () => { + /// Disable PWM channel + #[inline] + pub fn disable(&mut self) { + TIM::enable_channel(self.channel(), false); } - if PINS::C2 && TIM::CH_NUMBER > 1 { - self.tim - .preload_output_channel_in_mode(Channel::C2 as u8, Ocm::PwmMode1); - } - if PINS::C3 && TIM::CH_NUMBER > 2 { - self.tim - .preload_output_channel_in_mode(Channel::C3 as u8, Ocm::PwmMode1); - } - if PINS::C4 && TIM::CH_NUMBER > 3 { - self.tim - .preload_output_channel_in_mode(Channel::C4 as u8, Ocm::PwmMode1); + + /// Enable PWM channel + #[inline] + pub fn enable(&mut self) { + TIM::enable_channel(self.channel(), true); } - // The reference manual is a bit ambiguous about when enabling this bit is really - // necessary, but since we MUST enable the preload for the output channels then we - // might as well enable for the auto-reload too - self.tim.enable_preload(true); + /// Get PWM channel duty cycle + #[inline] + pub fn get_duty(&self) -> u16 { + TIM::read_cc_value(self.channel()) as u16 + } - let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw()); - self.tim.set_prescaler(psc); - self.tim.set_auto_reload(arr).unwrap(); + /// Get the maximum duty cycle value of the PWM channel + /// + /// If `0` returned means max_duty is 2^16 + #[inline] + pub fn get_max_duty(&self) -> u16 { + (TIM::read_auto_reload() as u16).wrapping_add(1) + } - // Trigger update event to load the registers - self.tim.trigger_update(); + /// Set PWM channel duty cycle + #[inline] + pub fn set_duty(&mut self, duty: u16) { + TIM::set_cc_value(self.channel(), duty as u32) + } - self.tim.start_pwm(); + /// Set PWM channel polarity + #[inline] + pub fn set_polarity(&mut self, p: Polarity) { + TIM::set_pwm_channel_polarity(self.channel(), p); + } - PwmHz { - timer: self, - _pins: PhantomData, + /// Set complementary PWM channel polarity + #[inline] + pub fn set_complementary_polarity(&mut self, p: Polarity) { + TIM::set_pwm_nchannel_polarity(self.channel(), p); } - } + }; } -impl PwmHz -where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, -{ - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) - } - - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) - } +macro_rules! chN_impl { + () => { + /// Disable complementary PWM channel + #[inline] + pub fn disable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), false); + } - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 - } + /// Enable complementary PWM channel + #[inline] + pub fn enable_complementary(&mut self) { + TIM::enable_nchannel(self.channel(), true); + } - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty as u32) - } + /// Set PWM channel idle state + #[inline] + pub fn set_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), false, s); + } - /// If `0` returned means max_duty is 2^16 - pub fn get_max_duty(&self) -> u16 { - (TIM::read_auto_reload() as u16).wrapping_add(1) - } + /// Set complementary PWM channel idle state + #[inline] + pub fn set_complementary_idle_state(&mut self, s: IdleState) { + TIM::idle_state(self.channel(), true, s); + } + }; +} - pub fn get_period(&self) -> Hertz { - let clk = self.clk; - let psc = self.tim.read_prescaler() as u32; - let arr = TIM::read_auto_reload(); +impl, const C: u8, const COMP: bool> PwmChannel { + ch_impl!(); +} - // Length in ms of an internal clock pulse - clk / ((psc + 1) * (arr + 1)) - } +impl, const C: u8> PwmChannel { + chN_impl!(); +} - pub fn set_period(&mut self, period: Hertz) { - let clk = self.clk; +impl ErasedChannel { + ch_impl!(); +} - let (psc, arr) = compute_arr_presc(period.raw(), clk.raw()); - self.tim.set_prescaler(psc); - self.tim.set_auto_reload(arr).unwrap(); - } +impl ErasedChannel { + chN_impl!(); } -pub struct Pwm +pub struct PwmManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { - timer: FTimer, - _pins: PhantomData<(REMAP, P, PINS)>, + pub(super) timer: FTimer, } -impl Pwm +impl PwmManager where - TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, + TIM: Instance + WithPwm + Split, { - pub fn split(self) -> PINS::Channels { - PINS::split() - } - - pub fn release(mut self) -> FTimer { + pub fn release(mut self, _channels: TIM::Channels) -> FTimer { // stop counter self.tim.cr1_reset(); self.timer } } -impl Deref for Pwm +impl Deref for PwmManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { type Target = FTimer; fn deref(&self) -> &Self::Target { @@ -423,98 +363,186 @@ where } } -impl DerefMut for Pwm +impl DerefMut for PwmManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.timer } } -impl FTimer { - pub fn pwm( - mut self, - _pins: PINS, - mapr: &mut MAPR, - time: TimerDurationU32, - ) -> Pwm - where - REMAP: Remap, - PINS: Pins, - { - REMAP::remap(mapr); - - if PINS::C1 { - self.tim - .preload_output_channel_in_mode(Channel::C1 as u8, Ocm::PwmMode1); - } - if PINS::C2 && TIM::CH_NUMBER > 1 { - self.tim - .preload_output_channel_in_mode(Channel::C2 as u8, Ocm::PwmMode1); - } - if PINS::C3 && TIM::CH_NUMBER > 2 { - self.tim - .preload_output_channel_in_mode(Channel::C3 as u8, Ocm::PwmMode1); - } - if PINS::C4 && TIM::CH_NUMBER > 3 { - self.tim - .preload_output_channel_in_mode(Channel::C4 as u8, Ocm::PwmMode1); - } - - // The reference manual is a bit ambiguous about when enabling this bit is really - // necessary, but since we MUST enable the preload for the output channels then we - // might as well enable for the auto-reload too - self.tim.enable_preload(true); - - self.tim.set_auto_reload(time.ticks() - 1).unwrap(); - - // Trigger update event to load the registers - self.tim.trigger_update(); +pub struct PwmHzManager +where + TIM: Instance + WithPwm, +{ + pub(super) timer: Timer, +} - self.tim.start_pwm(); +/*impl PwmHzManager +where + TIM: Instance + WithPwm + Split, +{ + pub fn release(mut self, _channels: TIM::Channels) -> Timer { + // stop timer + self.tim.cr1_reset(); + self.timer + } +}*/ - Pwm { - timer: self, - _pins: PhantomData, - } +impl Deref for PwmHzManager +where + TIM: Instance + WithPwm, +{ + type Target = Timer; + fn deref(&self) -> &Self::Target { + &self.timer } } -impl Pwm +impl DerefMut for PwmHzManager where TIM: Instance + WithPwm, - REMAP: Remap, - PINS: Pins, { - pub fn enable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, true) + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.timer } +} - pub fn disable(&mut self, channel: Channel) { - TIM::enable_channel(PINS::check_used(channel) as u8, false) +impl PwmManager +where + TIM: Instance + WithPwm, +{ + /// Get the maximum duty cycle value of the timer + /// + /// If `0` returned means max_duty is 2^16 + pub fn get_max_duty(&self) -> u16 { + (TIM::read_auto_reload() as u16).wrapping_add(1) } - pub fn get_duty(&self, channel: Channel) -> u16 { - TIM::read_cc_value(PINS::check_used(channel) as u8) as u16 + /// Get the PWM frequency of the timer as a duration + pub fn get_period(&self) -> TimerDurationU32 { + TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1) } - pub fn set_duty(&mut self, channel: Channel, duty: u16) { - TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into()) + /// Set the PWM frequency for the timer from a duration + pub fn set_period(&mut self, period: TimerDurationU32) { + self.tim.set_auto_reload(period.ticks() - 1).unwrap(); + self.tim.cnt_reset(); } +} +impl PwmHzManager +where + TIM: Instance + WithPwm, +{ + /// Get the maximum duty cycle value of the timer + /// /// If `0` returned means max_duty is 2^16 pub fn get_max_duty(&self) -> u16 { (TIM::read_auto_reload() as u16).wrapping_add(1) } - pub fn get_period(&self) -> TimerDurationU32 { - TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1) + /// Get the PWM frequency of the timer in Hertz + pub fn get_period(&self) -> Hertz { + let clk = self.clk; + let psc = self.tim.read_prescaler() as u32; + let arr = TIM::read_auto_reload(); + + // Length in ms of an internal clock pulse + clk / ((psc + 1) * (arr + 1)) } - pub fn set_period(&mut self, period: TimerDurationU32) { - self.tim.set_auto_reload(period.ticks() - 1).unwrap(); + /// Set the PWM frequency for the timer in Hertz + pub fn set_period(&mut self, period: Hertz) { + let clk = self.clk; + + let (psc, arr) = compute_arr_presc(period.raw(), clk.raw()); + self.tim.set_prescaler(psc); + self.tim.set_auto_reload(arr).unwrap(); + self.tim.cnt_reset(); + } +} + +macro_rules! impl_advanced { + () => { + /// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states + /// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state is called 'dead time'. + /// + /// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by + /// short circuit in the moment of switching. + #[inline] + pub fn set_dead_time(&mut self, dts_ticks: u16) { + let bits = pack_ceil_dead_time(dts_ticks); + TIM::set_dtg_value(bits); + } + + /// Set raw dead time (DTG) bits + /// + /// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of + /// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics + #[inline] + pub fn set_dead_time_bits(&mut self, bits: u8) { + TIM::set_dtg_value(bits); + } + + /// Return dead time for complementary pins in the unit of DTS ticks + #[inline] + pub fn get_dead_time(&self) -> u16 { + unpack_dead_time(TIM::read_dtg_value()) + } + + /// Get raw dead time (DTG) bits + #[inline] + pub fn get_dead_time_bits(&self) -> u8 { + TIM::read_dtg_value() + } + + /// Sets the alignment mode + #[inline] + pub fn set_cms(&mut self, mode: CenterAlignedMode) { + self.tim.enable_counter(false); + TIM::set_cms(mode); + self.tim.enable_counter(true); + } + }; +} + +impl PwmManager +where + TIM: Instance + WithPwm + Advanced, +{ + impl_advanced!(); +} + +impl PwmHzManager +where + TIM: Instance + WithPwm + Advanced, +{ + impl_advanced!(); +} + +/// Convert number dead time ticks to raw DTG register bits. +/// Values greater than 1009 result in maximum dead time of 126 us +const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 { + match dts_ticks { + 0..=127 => dts_ticks as u8, + 128..=254 => ((((dts_ticks + 1) >> 1) - 64) as u8) | 0b_1000_0000, + 255..=504 => ((((dts_ticks + 7) >> 3) - 32) as u8) | 0b_1100_0000, + 505..=1008 => ((((dts_ticks + 15) >> 4) - 32) as u8) | 0b_1110_0000, + 1009.. => 0xff, + } +} + +/// Convert raw DTG register bits value to number of dead time ticks +const fn unpack_dead_time(bits: u8) -> u16 { + if bits & 0b_1000_0000 == 0 { + bits as u16 + } else if bits & 0b_0100_0000 == 0 { + (((bits & !0b_1000_0000) as u16) + 64) * 2 + } else if bits & 0b_0010_0000 == 0 { + (((bits & !0b_1100_0000) as u16) + 32) * 8 + } else { + (((bits & !0b_1110_0000) as u16) + 32) * 16 } } diff --git a/tools/check.py b/tools/check.py index 2b4b2026..4fd93d70 100755 --- a/tools/check.py +++ b/tools/check.py @@ -21,30 +21,32 @@ def run(mcu, cargo_cmd): def main(): cargo_meta = json.loads( - subprocess.check_output("cargo metadata --no-deps --format-version=1", - shell=True, - universal_newlines=True) + subprocess.check_output( + "cargo metadata --no-deps --format-version=1", + shell=True, + universal_newlines=True, ) + ) crate_info = cargo_meta["packages"][0] - features = ["{},rtic,high,stm32-usbd".format(x) - for x in crate_info["features"].keys() - if x.startswith("stm32f1")] + features = [ + "{},rtic1,defmt,high,stm32-usbd".format(x) + for x in crate_info["features"].keys() + if x.startswith("stm32f1") + ] - if 'size_check' in sys.argv: - cargo_cmd = ['cargo', 'build', '--release'] + if "size_check" in sys.argv: + cargo_cmd = ["cargo", "build", "--release"] else: - cargo_cmd = ['cargo', 'check'] - - if '--examples' in sys.argv: - cargo_cmd += ['--examples'] + cargo_cmd = ["cargo", "check"] - if not all(map(lambda f: run(f, cargo_cmd), - features)): + if "--examples" in sys.argv: + cargo_cmd += ["--examples"] + + if not all(map(lambda f: run(f, cargo_cmd), features)): sys.exit(-1) if __name__ == "__main__": main() -