diff options
| author | Tomasz Kramkowski <tomasz@kramkow.ski> | 2025-10-16 19:33:17 +0100 | 
|---|---|---|
| committer | Tomasz Kramkowski <tomasz@kramkow.ski> | 2025-10-16 19:33:17 +0100 | 
| commit | cdf2a5026e45ac2b65001ed41cc67d86590a681a (patch) | |
| tree | 56fda9ee1cc0159e725952802547e5e6d327428e /src/pulse.rs | |
| download | scale-drv-example-master.tar.gz scale-drv-example-master.tar.xz scale-drv-example-master.zip | |
Diffstat (limited to 'src/pulse.rs')
| -rw-r--r-- | src/pulse.rs | 119 | 
1 files changed, 119 insertions, 0 deletions
| diff --git a/src/pulse.rs b/src/pulse.rs new file mode 100644 index 0000000..0fbb409 --- /dev/null +++ b/src/pulse.rs @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2024-2025 Tomasz Kramkowski <tomasz@kramkow.ski> +// SPDX-License-Identifier: GPL-3.0-or-later + +use embassy_stm32::{ +    interrupt::typelevel::{Binding, Interrupt}, +    pac::timer::vals, +    time::Hertz, +    timer::{ +        low_level::{self, mode, OutputCompareMode, OutputPolarity, Timer}, +        simple_pwm::PwmPin, +        Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, UpdateInterruptHandler, +    }, +    Peri, +}; + +/// The counting mode setting for the timer +pub enum CountingMode { +    EdgeAlignedUp, +    EdgeAlignedDown, +} + +impl From<CountingMode> for low_level::CountingMode { +    fn from(value: CountingMode) -> Self { +        match value { +            CountingMode::EdgeAlignedUp => low_level::CountingMode::EdgeAlignedUp, +            CountingMode::EdgeAlignedDown => low_level::CountingMode::EdgeAlignedDown, +        } +    } +} + +/// A clock pulse wrapper driver based on STM32 4ch Timers +pub struct Pulse<'d, T: GeneralInstance4Channel> { +    inner: Timer<'d, T, mode::WithIrq>, +} + +impl<'d, T: GeneralInstance4Channel> Pulse<'d, T> { +    /// Create a new Pulse driver from the timer, pin channels, and +    /// configuration. +    pub fn new( +        tim: Peri<'d, T>, +        ch1: Option<PwmPin<'d, T, Ch1>>, +        ch2: Option<PwmPin<'d, T, Ch2>>, +        ch3: Option<PwmPin<'d, T, Ch3>>, +        ch4: Option<PwmPin<'d, T, Ch4>>, +        period: Hertz, +        counting_mode: CountingMode, +        irq: impl Binding<T::UpdateInterrupt, UpdateInterruptHandler<T>> + Copy + 'd, +    ) -> Self { +        let this = Self { +            inner: Timer::new_with_interrupt(tim, irq), +        }; + +        this.inner.set_counting_mode(counting_mode.into()); + +        // Configure OPM +        this.inner.regs_gp16().cr1().modify(|r| r.set_opm(true)); + +        this.inner.set_frequency(period); + +        this.inner.enable_outputs(); + +        // Calculate 50% duty +        let half_duty = this.inner.get_max_compare_value().div_ceil(2); + +        // Enable channels +        [ +            (Channel::Ch1, ch1.is_some()), +            (Channel::Ch2, ch2.is_some()), +            (Channel::Ch3, ch3.is_some()), +            (Channel::Ch4, ch4.is_some()), +        ] +        .iter() +        .filter_map(|(channel, present)| present.then_some(channel)) +        .for_each(|&channel| { +            // In OPM, Mode1 + ActiveLow and Mode2 + ActiveHigh are identical +            this.inner +                .set_output_compare_mode(channel, OutputCompareMode::PwmMode1); +            this.inner +                .set_output_polarity(channel, OutputPolarity::ActiveLow); + +            // Configure duty cycle +            this.inner.set_output_compare_preload(channel, true); +            this.inner.set_compare_value(channel, half_duty); + +            this.inner.enable_channel(channel, true); +        }); + +        // Initialise the counter registers +        { +            let regs = this.inner.regs_core(); +            // Disable update interrupt generation +            regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); +            // Force shadow registers to update +            regs.egr().write(|r| r.set_ug(true)); +            // Re-enable interrupt generation +            regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); +        } + +        // NVIC Configuration +        T::UpdateInterrupt::unpend(); +        unsafe { T::UpdateInterrupt::enable() }; + +        this +    } + +    /// Trigger the pulse on all channel pins, wait for it to finish +    pub async fn trigger_and_wait(&mut self) { +        // TODO: Needed? +        self.inner.clear_update_interrupt(); +        self.inner.start(); +        self.inner.wait_for_update().await; +    } +} + +impl<'d, T: GeneralInstance4Channel> Drop for Pulse<'d, T> { +    fn drop(&mut self) { +        T::UpdateInterrupt::disable() +    } +} | 
