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}