stepper/
lib.rs

1//! Stepper - Universal Stepper Motor Interface
2//!
3//! Stepper aims to provide an interface that abstracts over stepper drivers and
4//! motion control chips, exposing high-level hardware features directly where
5//! available, or providing software fallbacks where hardware support is
6//! lacking.
7//!
8//! Right now, Stepper supports the following ICs:
9//!
10//! - [DRV8825](crate::drivers::drv8825::DRV8825)
11//! - [STSPIN220](crate::drivers::stspin220::STSPIN220)
12//! - [DQ542MA](crate::drivers::dq542ma::DQ542MA)
13//!
14//! Please check out the documentation of [`Stepper`], which is the main entry
15//! point to this API.
16//!
17//! # Example
18//!
19//! ``` rust
20//! # fn main()
21//! #     -> Result<
22//! #         (),
23//! #         stepper::Error<
24//! #             core::convert::Infallible,
25//! #             core::convert::Infallible,
26//! #             core::convert::Infallible,
27//! #             core::convert::Infallible,
28//! #         >
29//! #     > {
30//! #
31//! use stepper::{
32//!     fugit::NanosDurationU32 as Nanoseconds,
33//!     motion_control, ramp_maker,
34//!     Direction, Stepper,
35//! };
36//!
37//! # // Use a real driver to make things easy, without making the example seem
38//! # // too specific to one driver.
39//! # type MyDriver = stepper::drivers::drv8825::DRV8825<
40//! #     (), (), (), (), (), (), (), (), ()
41//! # >;
42//! #
43//! # struct Pin;
44//! # impl embedded_hal::digital::ErrorType for Pin {
45//! #     type Error = core::convert::Infallible;
46//! # }
47//! # impl stepper::embedded_hal::digital::blocking::OutputPin for Pin {
48//! #     fn set_low(&mut self) -> Result<(), Self::Error> { Ok(()) }
49//! #     fn set_high(&mut self) -> Result<(), Self::Error> { Ok(()) }
50//! # }
51//! #
52//! # struct Timer<const TIMER_HZ: u32>;
53//! #     impl<const TIMER_HZ: u32> Timer<TIMER_HZ> {
54//! #         pub fn new() -> Self {  Self {} }
55//! #     }
56//! #     impl<const TIMER_HZ: u32> fugit_timer::Timer<TIMER_HZ> for Timer<TIMER_HZ>{
57//! #         type Error = std::convert::Infallible;
58//! #         fn now(&mut self) -> fugit::TimerInstantU32<TIMER_HZ> {
59//! #             todo!()
60//! #         }
61//! #         fn start(&mut self, _duration: fugit::TimerDurationU32<TIMER_HZ>) -> Result<(), Self::Error> {
62//! #             Ok(())
63//! #         }
64//! #         fn cancel(&mut self) -> Result<(), Self::Error> {
65//! #             todo!()
66//! #         }
67//! #         fn wait(&mut self) -> nb::Result<(), Self::Error> {
68//! #             Ok(())
69//! #         }
70//! #     }
71//! #
72//! # fn delay_ns(_: Nanoseconds) {}
73//! #
74//! // We need some `embedded_hal::digital::OutputPin` implementations connected
75//! // to the STEP and DIR signals of our driver chip. How you acquire those
76//! // depends on the platform you run on. Here, we'll use a mock implementation
77//! // for the sake of demonstration.
78//! let step = Pin;
79//! let dir = Pin;
80//!
81//! // We also need a timer (that implements `embedded_hal::timer::CountDown`),
82//! // since there are time-critical aspects to communicating with the driver
83//! // chip. Again, how you acquire one depends on your target platform, and
84//! // again, we'll use a mock here for the sake of demonstration.
85//! let mut timer = Timer::<1_000_000>::new();
86//!
87//! // Define the numeric type we're going to use. We'll use a fixed-point type
88//! // here, as that's the most widely supported. If your target hardware has
89//! // support for floating point, it might be more convenient (and possibly
90//! // efficient) to use that instead.
91//! type Num = fixed::FixedI64<typenum::U32>;
92//!
93//! // Define the target acceleration and maximum speed using timer ticks as the
94//! // unit of time. We could also use seconds or any other unit of time
95//! // (Stepper doesn't care), but then we'd need to provide a conversion from
96//! // seconds to timer ticks. This way, we save that conversion.
97//! //
98//! // These values assume a 1 MHz timer, but that depends on the timer you're
99//! // using, of course.
100//! let target_accel = Num::from_num(0.001); // steps / tick^2; 1000 steps / s^2
101//! let max_speed = Num::from_num(0.001); // steps / tick; 1000 steps / s
102//!
103//! // We want to use the high-level motion control API (see below), but let's
104//! // assume the driver we use for this example doesn't provide hardware
105//! // support for that. Let's instantiate a motion profile from the RampMaker
106//! // library to provide a software fallback.
107//! let profile = ramp_maker::Trapezoidal::new(target_accel);
108//!
109//! // Now we need to initialize the stepper API. We do this by initializing a
110//! // driver (`MyDriver`), then wrapping that into the generic API (`Stepper`).
111//! // `MyDriver` is a placeholder. In a real use-case, you'd typically use one
112//! // of the drivers from the `stepper::drivers` module, but any driver that
113//! // implements the traits from `stepper::traits` will do.
114//! //
115//! // By default, drivers can't do anything after being initialized. This means
116//! // they also don't require any hardware resources, which makes them easier
117//! // to use when you don't need all features.
118//! let mut stepper = Stepper::from_driver(MyDriver::new())
119//!     // Enable direction control
120//!     .enable_direction_control(dir, Direction::Forward, &mut timer)?
121//!     // Enable step control
122//!     .enable_step_control(step)
123//!     // Enable motion control using the software fallback
124//!     .enable_motion_control((timer, profile, DelayToTicks));
125//!
126//! // Tell the motor to move 2000 steps (10 revolutions on a typical stepper
127//! // motor), while respecting the maximum speed. Since we selected a
128//! // trapezoidal motion profile above, this will result in a controlled
129//! // acceleration to the maximum speed, and a controlled deceleration after.
130//! let target_step = 2000;
131//! stepper
132//!     .move_to_position(max_speed, target_step)
133//!     .wait()?;
134//!
135//! // Here's the converter that Stepper is going to use internally, to convert
136//! // from the computed delay value to timer ticks. Since we chose to use timer
137//! // ticks as the unit of time for velocity and acceleration, this conversion
138//! // is pretty simple (and cheap).
139//! use num_traits::cast::ToPrimitive;
140//! pub struct DelayToTicks;
141//! impl<const TIMER_HZ: u32> motion_control::DelayToTicks<Num, TIMER_HZ> for DelayToTicks {
142//!     type Error = core::convert::Infallible;
143//!
144//!     fn delay_to_ticks(&self, delay: Num)
145//!         -> Result<fugit::TimerDurationU32<TIMER_HZ>, Self::Error>
146//!     {
147//!         Ok(fugit::TimerDurationU32::<TIMER_HZ>::from_ticks(Num::to_u32(&delay).expect("the delay to convert")))
148//!     }
149//! }
150//! #
151//! # Ok(())
152//! # }
153//! ```
154//!
155//! [RampMaker]: https://crates.io/crates/ramp-maker
156
157#![cfg_attr(not(test), no_std)]
158#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
159
160pub extern crate embedded_hal;
161pub extern crate fugit;
162pub extern crate ramp_maker;
163
164pub mod compat;
165pub mod drivers;
166pub mod motion_control;
167pub mod step_mode;
168pub mod traits;
169pub mod util;
170
171mod stepper;
172
173pub use self::stepper::*;
174
175/// Defines the direction in which to rotate the motor
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177pub enum Direction {
178    /// Rotate the motor forward
179    ///
180    /// This corresponds to whatever direction the motor rotates in when the
181    /// driver's DIR signal is set HIGH.
182    Forward = 1,
183
184    /// Rotate the motor backward
185    ///
186    /// This corresponds to whatever direction the motor rotates in when the
187    /// driver's DIR signal set is LOW.
188    Backward = -1,
189}