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);