capsules_core/virtualizers/
virtual_pwm.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Virtualize a PWM interface.
6//!
7//! `MuxPwm` provides shared access to a single PWM interface for multiple
8//! users. `PwmPinUser` provides access to a specific PWM pin.
9//!
10//! Usage
11//! -----
12//!
13//! ```rust,ignore
14//! # use kernel::static_init;
15//!
16//! let mux_pwm = static_init!(
17//!     capsules_core::virtual_pwm::MuxPwm<'static, nrf52::pwm::Pwm>,
18//!     capsules_core::virtual_pwm::MuxPwm::new(&base_peripherals.pwm0)
19//! );
20//! let virtual_pwm_buzzer = static_init!(
21//!     capsules_core::virtual_pwm::PwmPinUser<'static, nrf52::pwm::Pwm>,
22//!     capsules_core::virtual_pwm::PwmPinUser::new(mux_pwm, nrf5x::pinmux::Pinmux::new(31))
23//! );
24//! virtual_pwm_buzzer.add_to_mux();
25//! ```
26
27use kernel::collections::list::{List, ListLink, ListNode};
28use kernel::hil;
29use kernel::utilities::cells::OptionalCell;
30use kernel::ErrorCode;
31
32pub struct MuxPwm<'a, P: hil::pwm::Pwm> {
33    pwm: &'a P,
34    devices: List<'a, PwmPinUser<'a, P>>,
35    inflight: OptionalCell<&'a PwmPinUser<'a, P>>,
36}
37
38impl<'a, P: hil::pwm::Pwm> MuxPwm<'a, P> {
39    pub const fn new(pwm: &'a P) -> MuxPwm<'a, P> {
40        MuxPwm {
41            pwm,
42            devices: List::new(),
43            inflight: OptionalCell::empty(),
44        }
45    }
46
47    /// If we are not currently doing anything, scan the list of devices for
48    /// one with an outstanding operation and run that.
49    fn do_next_op(&self) {
50        if self.inflight.is_none() {
51            let mnode = self.devices.iter().find(|node| node.operation.is_some());
52            mnode.map(|node| {
53                let started = node.operation.take().is_some_and(|operation| {
54                    match operation {
55                        Operation::Simple {
56                            frequency_hz,
57                            duty_cycle,
58                        } => {
59                            let _ = self.pwm.start(&node.pin, frequency_hz, duty_cycle);
60                            true
61                        }
62                        Operation::Stop => {
63                            // Can't stop if nothing is running
64                            false
65                        }
66                    }
67                });
68                if started {
69                    self.inflight.set(node);
70                } else {
71                    // Keep looking for something to do.
72                    self.do_next_op();
73                }
74            });
75        } else {
76            // We are running so we do whatever the inflight user wants, if
77            // there is some command there.
78            self.inflight.map(|node| {
79                node.operation.take().map(|operation| {
80                    match operation {
81                        Operation::Simple {
82                            frequency_hz,
83                            duty_cycle,
84                        } => {
85                            // Changed some parameter.
86                            let _ = self.pwm.start(&node.pin, frequency_hz, duty_cycle);
87                        }
88                        Operation::Stop => {
89                            // Ok we got a stop.
90                            let _ = self.pwm.stop(&node.pin);
91                            self.inflight.clear();
92                        }
93                    }
94                    // Recurse in case there is more to do.
95                    self.do_next_op();
96                });
97            });
98        }
99    }
100}
101
102#[derive(Copy, Clone, PartialEq)]
103enum Operation {
104    Simple {
105        frequency_hz: usize,
106        duty_cycle: usize,
107    },
108    Stop,
109}
110
111pub struct PwmPinUser<'a, P: hil::pwm::Pwm> {
112    mux: &'a MuxPwm<'a, P>,
113    pin: P::Pin,
114    operation: OptionalCell<Operation>,
115    next: ListLink<'a, PwmPinUser<'a, P>>,
116}
117
118impl<'a, P: hil::pwm::Pwm> PwmPinUser<'a, P> {
119    pub const fn new(mux: &'a MuxPwm<'a, P>, pin: P::Pin) -> PwmPinUser<'a, P> {
120        PwmPinUser {
121            mux,
122            pin,
123            operation: OptionalCell::empty(),
124            next: ListLink::empty(),
125        }
126    }
127
128    pub fn add_to_mux(&'a self) {
129        self.mux.devices.push_head(self);
130    }
131}
132
133impl<'a, P: hil::pwm::Pwm> ListNode<'a, PwmPinUser<'a, P>> for PwmPinUser<'a, P> {
134    fn next(&'a self) -> &'a ListLink<'a, PwmPinUser<'a, P>> {
135        &self.next
136    }
137}
138
139impl<P: hil::pwm::Pwm> hil::pwm::PwmPin for PwmPinUser<'_, P> {
140    fn start(&self, frequency_hz: usize, duty_cycle: usize) -> Result<(), ErrorCode> {
141        self.operation.set(Operation::Simple {
142            frequency_hz,
143            duty_cycle,
144        });
145        self.mux.do_next_op();
146        Ok(())
147    }
148
149    fn stop(&self) -> Result<(), ErrorCode> {
150        self.operation.set(Operation::Stop);
151        self.mux.do_next_op();
152        Ok(())
153    }
154
155    fn get_maximum_frequency_hz(&self) -> usize {
156        self.mux.pwm.get_maximum_frequency_hz()
157    }
158
159    fn get_maximum_duty_cycle(&self) -> usize {
160        self.mux.pwm.get_maximum_duty_cycle()
161    }
162}