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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Traits that can be implemented by Stepper drivers
//!
//! Users are generally not expected to use these traits directly, except to
//! specify trait bounds, where necessary. Please check out [`Stepper`], which
//! uses these traits to provide a unified API.
//!
//! There are two kinds of traits in this module:
//! 1. Those that provide a minimal and low-level interface over a specific
//!    capability (like controlling the microstepping mode).
//! 2. Those that provide an API for enabling these capabilities, taking
//!    ownership of the resources that are required to do so.
//!
//! When constructed, drivers usually do not provide access to any of their
//! capabilities. This means users can specifically enable the capabilities they
//! need, and do not have have to provide hardware resources (like output pins)
//! for capabilities that they are not going to use.
//!
//! This approach also provides a lot of flexibility for non-standard use cases,
//! for example if not all driver capabilities are controlled by software.
//!
//! [`Stepper`]: crate::Stepper

use embedded_hal::digital::blocking::OutputPin;
use fugit::NanosDurationU32 as Nanoseconds;

use crate::step_mode::StepMode;

/// Enable microstepping mode control for a driver
///
/// The `Resources` type parameter defines the hardware resources required for
/// controlling microstepping mode.
pub trait EnableStepModeControl<Resources> {
    /// The type of the driver after microstepping mode control has been enabled
    type WithStepModeControl: SetStepMode;

    /// Enable microstepping mode control
    fn enable_step_mode_control(
        self,
        res: Resources,
    ) -> Self::WithStepModeControl;
}

/// Implemented by drivers that support controlling the microstepping mode
pub trait SetStepMode {
    /// The time the mode signals need to be held before re-enabling the driver
    const SETUP_TIME: Nanoseconds;

    /// The time the mode signals need to be held after re-enabling the driver
    const HOLD_TIME: Nanoseconds;

    /// The error that can occur while using this trait
    type Error;

    /// The type that defines the microstepping mode
    ///
    /// This crate includes a number of enums that can be used for this purpose.
    type StepMode: StepMode;

    /// Apply the new step mode configuration
    ///
    /// Typically this puts the driver into reset and sets the mode pins
    /// according to the new step mode.
    fn apply_mode_config(
        &mut self,
        step_mode: Self::StepMode,
    ) -> Result<(), Self::Error>;

    /// Re-enable the driver after the mode has been set
    fn enable_driver(&mut self) -> Result<(), Self::Error>;
}

/// Enable direction control for a driver
///
/// The `Resources` type parameter defines the hardware resources required for
/// direction control.
pub trait EnableDirectionControl<Resources> {
    /// The type of the driver after direction control has been enabled
    type WithDirectionControl: SetDirection;

    /// Enable direction control
    fn enable_direction_control(
        self,
        res: Resources,
    ) -> Self::WithDirectionControl;
}

/// Implemented by drivers that support controlling the DIR signal
pub trait SetDirection {
    /// The time that the DIR signal must be held for a change to apply
    const SETUP_TIME: Nanoseconds;

    /// The type of the DIR pin
    type Dir: OutputPin;

    /// The error that can occur while accessing the DIR pin
    type Error;

    /// Provides access to the DIR pin
    fn dir(&mut self) -> Result<&mut Self::Dir, Self::Error>;
}

/// Enable step control for a driver
///
/// The `Resources` type parameter defines the hardware resources required for
/// step control.
pub trait EnableStepControl<Resources> {
    /// The type of the driver after step control has been enabled
    type WithStepControl: Step;

    /// Enable step control
    fn enable_step_control(self, res: Resources) -> Self::WithStepControl;
}

/// Implemented by drivers that support controlling the STEP signal
pub trait Step {
    /// The minimum length of a STEP pulse
    const PULSE_LENGTH: Nanoseconds;

    /// The type of the STEP pin
    type Step: OutputPin;

    /// The error that can occur while accessing the STEP pin
    type Error;

    /// Provides access to the STEP pin
    fn step(&mut self) -> Result<&mut Self::Step, Self::Error>;
}

/// Enable motion control for a driver
///
/// The `Resources` type parameter defines the hardware resources required for
/// motion control.
pub trait EnableMotionControl<Resources, const TIMER_HZ: u32> {
    /// The type of the driver after motion control has been enabled
    type WithMotionControl: MotionControl;

    /// Enable step control
    fn enable_motion_control(self, res: Resources) -> Self::WithMotionControl;
}

/// Implemented by drivers that have motion control capabilities
///
/// A software-based fallback implementation exists in the [`motion_control`]
/// module, for drivers that implement [SetDirection] and [Step].
///
/// [`motion_control`]: crate::motion_control
pub trait MotionControl {
    /// The type used by the driver to represent velocity
    type Velocity: Copy;

    /// The type error that can happen when using this trait
    type Error;

    /// Move to the given position
    ///
    /// This method must arrange for the motion to start, but must not block
    /// until it is completed. If more attention is required during the motion,
    /// this should be handled in [`MotionControl::update`].
    fn move_to_position(
        &mut self,
        max_velocity: Self::Velocity,
        target_step: i32,
    ) -> Result<(), Self::Error>;

    /// Reset internal position to the given value
    ///
    /// This method must not start a motion. Its only purpose is to change the
    /// driver's internal position value, for example for homing.
    fn reset_position(&mut self, step: i32) -> Result<(), Self::Error>;

    /// Update an ongoing motion
    ///
    /// This method may contain any code required to maintain an ongoing motion,
    /// if required, or it might just check whether a motion is still ongoing.
    ///
    /// Return `true`, if motion is ongoing, `false` otherwise. If `false` is
    /// returned, the caller may assume that this method doesn't need to be
    /// called again, until starting another motion.
    fn update(&mut self) -> Result<bool, Self::Error>;
}