stepper_driver/
lib.rs

1//! A49xx, DRV88xx stepper motor driver.
2//!
3//! This is an implementation of the [`embedded-hal`] traits for the A49xx and DRV88xx families of
4//! stepper motor drivers.
5//!
6//! [`embedded-hal`]: https://github.com/japaric/embedded-hal
7//!
8//! # Examples
9//!
10//! ```rust,no-run
11//! #![deny(unsafe_code)]
12//! #![deny(warnings)]
13//! #![no_std]
14//!
15//! extern crate panic_abort;
16//! extern crate cortex_m;
17//! extern crate embedded_hal;
18//! extern crate tm4c123x_hal as hal;
19//! extern crate stepper_rs;
20//!
21//! use hal::delay::Delay;
22//! use hal::gpio::GpioExt;
23//! use hal::sysctl::SysctlExt;
24//!
25//! fn main() {
26//!     let p = hal::Peripherals::take().unwrap();
27//!     let sysctl = p.SYSCTL.constrain();
28//!     let portc = p.GPIO_PORTC.split(&sysctl.power_control);
29//!     let clocks = sysctl.clock_setup.freeze();
30//!
31//!     let cp = cortex_m::Peripherals::take().unwrap();
32//!     let mut driver = stepper_rs::MotorDriver::a4988(
33//!         Delay::new(cp.SYST, &clocks),
34//!         portc.pc6.into_push_pull_output(),
35//!         portc.pc7.into_push_pull_output(),
36//!         200,
37//!         16,
38//!         100f32
39//!     );
40//!
41//!     loop {
42//!         driver.set_speed(100f32);
43//!         driver.set_direction(true);
44//!         driver.move_instant(600);
45//!         driver.set_direction(false);
46//!         driver.move_instant(600);
47//!
48//!         driver.set_speed(300f32);
49//!         driver.set_direction(true);
50//!         driver.move_smooth(1600, 150, 150);
51//!         driver.set_direction(false);
52//!         driver.move_smooth(1600, 150, 150);
53//!     }
54//! }
55//! ```
56
57#![no_std]
58
59#![deny(missing_debug_implementations)]
60#![deny(missing_docs)]
61#![deny(warnings)]
62#![deny(trivial_casts)]
63#![deny(trivial_numeric_casts)]
64#![deny(unsafe_code)]
65#![deny(unstable_features)]
66#![deny(unused_import_braces)]
67#![deny(unused_qualifications)]
68
69extern crate embedded_hal as hal;
70
71use core::marker::PhantomData;
72use hal::digital::OutputPin;
73use hal::blocking::delay::DelayUs;
74
75// TODO: support EN pin
76//trait Enablable {
77//    pub fn enable();
78//    pub fn disable();
79//}
80
81/// Stepping mode (1:step_division)
82static STEP_DIVISION: [u8; 8] = [1,2,4,8,16,32,64,128];
83
84/// A stepper motor driver generic struct
85#[derive(Debug)]
86pub struct MotorDriver<D, DIR, STEP, CHIP>
87where
88    D: DelayUs<u32>,
89    DIR: OutputPin,
90    STEP: OutputPin,
91    CHIP: Params,
92{
93    delay: D,
94    dir_pin: DIR,
95    step_pin: STEP,
96//    TODO: support EN pin
97//    enable_pin: OutputPin,
98//    TODO: support driver specific stepping mode
99//    driver_impl: Some(),
100    _chip: PhantomData<CHIP>,
101
102    /// usually 200
103    number_of_steps: u16,
104    /// stepping mode (1:step_division) [1,2,4,8,16,32,64,128]
105    step_division: u8,
106    /// step pulse duration (microseconds)
107    step_interval: u32,
108}
109
110impl<D, DIR, STEP, CHIP> MotorDriver<D, DIR, STEP, CHIP>
111where
112    D: DelayUs<u32>,
113    DIR: OutputPin,
114    STEP: OutputPin,
115    CHIP: Params,
116{
117    /// Sets the speed in revolutions per minute (1-200 is a reasonable range)
118    pub fn set_speed(&mut self, rpm: f32) {
119        self.step_interval =
120            (60000000f32 / self.number_of_steps as f32 / rpm / self.step_division as f32) as u32;
121    }
122
123    /// Moves the motor steps_to_move steps
124    pub fn move_instant(&mut self, steps_to_move: u64) {
125        let steps_to_move = steps_to_move * self.step_division as u64;
126        (0..steps_to_move).for_each(|_| self.step(None));
127    }
128
129    /// Moves the motor smoothly `steps_to_move` steps.
130    /// Increasing speed during the `steps_acc` and decreasing during `steps_dec` steps.
131    pub fn move_smooth(&mut self,
132                       steps_to_move: u64,
133                       steps_acc: u64,
134                       steps_dec: u64) {
135        let steps_to_move = (steps_to_move - steps_acc - steps_dec) * self.step_division as u64;
136        let steps_acc = steps_acc * self.step_division as u64;
137        let steps_dec = steps_dec * self.step_division as u64;
138
139        (1..=steps_acc).for_each(|i| self.step(Some((i, steps_acc))));
140        (0..steps_to_move).for_each(|_| self.step(None));
141        (1..=steps_dec).rev().for_each(|i| self.step(Some((i, steps_dec))));
142    }
143
144    /// Set the direction
145    pub fn set_direction(&mut self, clock_work: bool) {
146        if clock_work {
147            self.dir_pin.set_low();
148        } else {
149            self.dir_pin.set_high();
150        }
151    }
152
153    /// Toggle step and yield to step control.
154    ///
155    /// !!!FIXME!!!
156    /// Super naive implementation due to limitaions of the `embedded-hal` crate.
157    /// One should use a timer instead of delay when `timer` and `time` API stabilize.
158    fn step(&mut self, s: Option<(u64, u64)>) {
159        self.step_pin.set_high();
160
161        let mut step_interval = self.step_interval;
162        if let Some((s1, s2)) = s {
163            let r1: f64 = s1 as f64 / s2 as f64;
164            let r2: f64 = 0.1 + 0.2*r1 + 2.2*r1*r1 - 1.5*r1*r1*r1;
165            step_interval = (self.step_interval as f64 / r2) as u32;
166        }
167
168        // Wait at least step_min_time
169        self.delay.delay_us(CHIP::STEP_MIN_TIME);
170        self.step_pin.set_low();
171
172        // Wait the rest of step_interval but at least step_min_time
173        let rest = if step_interval > CHIP::STEP_MIN_TIME {
174            step_interval - CHIP::STEP_MIN_TIME
175        } else {
176            CHIP::STEP_MIN_TIME
177        };
178        self.delay.delay_us(rest);
179    }
180
181    /// Generic version of constructor
182    fn new(delay: D,
183           mut dir_pin: DIR,
184           mut step_pin: STEP,
185           number_of_steps: u16,
186           step_division: u8,
187           rpm: f32) -> Self {
188        dir_pin.set_high();
189        step_pin.set_low();
190
191        MotorDriver {
192            delay,
193            dir_pin,
194            step_pin,
195            _chip: PhantomData,
196            number_of_steps,
197            step_division,
198            step_interval: (60000000f32 / number_of_steps as f32
199                / rpm / step_division as f32) as u32,
200        }
201    }
202}
203
204/// Trait for motor driver parameters.
205/// Currently only `STEP_MIN_TIME`.
206pub trait Params {
207    /// STEP high/low min value (microseconds)
208    const STEP_MIN_TIME: u32;
209}
210
211macro_rules! driver {
212    ($name:ident, $time:expr) => {
213        #[allow(non_camel_case_types)]
214        #[derive(Debug)]
215        struct $name;
216
217        impl Params for $name {
218            const STEP_MIN_TIME: u32 = $time;
219        }
220
221        impl<D, DIR, STEP> MotorDriver<D, DIR, STEP, $name>
222        where
223            D: DelayUs<u32>,
224            DIR: OutputPin,
225            STEP: OutputPin
226        {
227            /// Specialized constructor
228            pub fn $name(delay: D,
229                         dir_pin: DIR,
230                         step_pin: STEP,
231                         number_of_steps: u16,
232                         mut step_division: u8,
233                         rpm: f32) -> Self {
234                if !STEP_DIVISION.contains(&step_division) {
235                    step_division = 1;
236                }
237
238                Self::new(delay, dir_pin, step_pin, number_of_steps, step_division, rpm)
239            }
240        }
241    };
242}
243
244driver!(a4988, 1);
245driver!(drv8825, 2);
246driver!(drv8834, 2);
247driver!(drv8880, 1);