stepper/stepper/
set_direction.rs

1use core::task::Poll;
2
3use embedded_hal::digital::blocking::OutputPin;
4use embedded_hal::digital::ErrorType;
5use fugit::TimerDurationU32 as TimerDuration;
6use fugit_timer::Timer as TimerTrait;
7
8use crate::{traits::SetDirection, Direction};
9
10use super::SignalError;
11
12/// The "future" returned by [`Stepper::set_direction`]
13///
14/// Please note that this type provides a custom API and does not implement
15/// [`core::future::Future`]. This might change, when using futures for embedded
16/// development becomes more practical.
17///
18/// [`Stepper::set_direction`]: crate::Stepper::set_direction
19#[must_use]
20pub struct SetDirectionFuture<Driver, Timer, const TIMER_HZ: u32> {
21    direction: Direction,
22    driver: Driver,
23    timer: Timer,
24    state: State,
25}
26
27impl<Driver, Timer, const TIMER_HZ: u32>
28    SetDirectionFuture<Driver, Timer, TIMER_HZ>
29where
30    Driver: SetDirection,
31    Timer: TimerTrait<TIMER_HZ>,
32{
33    /// Create new instance of `SetDirectionFuture`
34    ///
35    /// This constructor is public to provide maximum flexibility for
36    /// non-standard use cases. Most users can ignore this and just use
37    /// [`Stepper::set_direction`] instead.
38    ///
39    /// [`Stepper::set_direction`]: crate::Stepper::set_direction
40    pub fn new(direction: Direction, driver: Driver, timer: Timer) -> Self {
41        Self {
42            direction,
43            driver,
44            timer,
45            state: State::Initial,
46        }
47    }
48
49    /// Poll the future
50    ///
51    /// The future must be polled for the operation to make progress. The
52    /// operation won't start, until this method has been called once. Returns
53    /// [`Poll::Pending`], if the operation is not finished yet, or
54    /// [`Poll::Ready`], once it is.
55    ///
56    /// If this method returns [`Poll::Pending`], the user can opt to keep
57    /// calling it at a high frequency (see [`Self::wait`]) until the operation
58    /// completes, or set up an interrupt that fires once the timer finishes
59    /// counting down, and call this method again once it does.
60    pub fn poll(
61        &mut self,
62    ) -> Poll<
63        Result<
64            (),
65            SignalError<
66                Driver::Error,
67                <Driver::Dir as ErrorType>::Error,
68                Timer::Error,
69            >,
70        >,
71    > {
72        match self.state {
73            State::Initial => {
74                match self.direction {
75                    Direction::Forward => self
76                        .driver
77                        .dir()
78                        .map_err(|err| SignalError::PinUnavailable(err))?
79                        .set_high()
80                        .map_err(|err| SignalError::Pin(err))?,
81                    Direction::Backward => self
82                        .driver
83                        .dir()
84                        .map_err(|err| SignalError::PinUnavailable(err))?
85                        .set_low()
86                        .map_err(|err| SignalError::Pin(err))?,
87                }
88
89                let ticks: TimerDuration<TIMER_HZ> =
90                    Driver::SETUP_TIME.convert();
91                self.timer
92                    .start(ticks)
93                    .map_err(|err| SignalError::Timer(err))?;
94
95                self.state = State::DirectionSet;
96                Poll::Pending
97            }
98            State::DirectionSet => match self.timer.wait() {
99                Ok(()) => {
100                    self.state = State::Finished;
101                    Poll::Ready(Ok(()))
102                }
103                Err(nb::Error::Other(err)) => {
104                    self.state = State::Finished;
105                    Poll::Ready(Err(SignalError::Timer(err)))
106                }
107                Err(nb::Error::WouldBlock) => Poll::Pending,
108            },
109            State::Finished => Poll::Ready(Ok(())),
110        }
111    }
112
113    /// Wait until the operation completes
114    ///
115    /// This method will call [`Self::poll`] in a busy loop until the operation
116    /// has finished.
117    pub fn wait(
118        &mut self,
119    ) -> Result<
120        (),
121        SignalError<
122            Driver::Error,
123            <Driver::Dir as ErrorType>::Error,
124            Timer::Error,
125        >,
126    > {
127        loop {
128            if let Poll::Ready(result) = self.poll() {
129                return result;
130            }
131        }
132    }
133
134    /// Drop the future and release the resources that were moved into it
135    pub fn release(self) -> (Driver, Timer) {
136        (self.driver, self.timer)
137    }
138}
139
140enum State {
141    Initial,
142    DirectionSet,
143    Finished,
144}