| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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()
    }
}
 |