stepgen/lib.rs
1#![no_std]
2#![warn(missing_docs)]
3#![deny(warnings)]
4
5//! Stepper motor speed ramp generator.
6//!
7//! Given acceleration, target speed and target step to stop
8//! at, generates acceleration or deceleration profile for the stepper motor, in the form of delays
9//! between steps.
10//!
11//! Uses algorithm from "[Generate stepper-motor speed pro les in real time][1]" paper by David Austin.
12//!
13//! # Examples
14//! ```
15//! use stepgen::Stepgen;
16//!
17//! let mut stepper = Stepgen::new(1_000_000);
18//!
19//! stepper.set_acceleration(1000 << 8).unwrap(); // 1000 steps per second per second
20//! stepper.set_target_speed(800 << 8).unwrap(); // 800 steps per second (4 turns per second)
21//! stepper.set_target_step(1000).unwrap(); // stop at step 1000
22//!
23//! // Take 99 steps
24//! for _ in 0..99 {
25//! println!("{}", stepper.next().unwrap());
26//! }
27//!
28//! assert_eq!(99, stepper.current_step());
29//! assert_eq!(113621, stepper.current_speed());
30//! assert_eq!(2242, (stepper.next().unwrap() + 128) >> 8); // delay for 100th step, rounded to the nearest integer
31//! ```
32//! ## Note on numbers
33//!
34//! In few APIs, stepgen keeps numbers as fixed-point numbers, using least significant 8 bits
35//! for the fractional part and the remaining bits for the integral part.
36//!
37//! [1]: http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
38
39
40/// Error code for some of the stepgen operations.
41#[derive(Clone, Copy, PartialEq, Debug)]
42pub enum Error {
43 /// Requested parameter (acceleration or speed) is too slow -- delay is too long and does not
44 /// fit in 16.8 format.
45 TooSlow,
46
47 /// Requested speed is too fast -- delay is to short for the MCU to process it timely.
48 TooFast,
49
50 /// Speed or acceleration was not configured when step is set.
51 SpeedAccelerationNotSet
52}
53
54/// Result type for some of the stepgen operations.
55pub type Result = core::result::Result<(), Error>;
56
57// Smallest delay we can handle without significant rounding errors
58const FASTEST_DELAY: u32 = 30;
59
60/// State of the stepgen.
61#[derive(Debug)]
62pub struct Stepgen {
63 // Current step
64 current_step: u32,
65
66 // Amount of acceleration steps we've taken so far
67 speed: u32,
68 // Previously calculated delay, in 16.16 format
69 delay: u32,
70
71 // If slewing, this will be the slewing delay. Switched to this mode once
72 // we overshoot target speed. 16.16 format.
73 slewing_delay: u32,
74
75 // Timer frequency
76 ticks_per_second: u32,
77 // First step delay, in 16.16 format
78 first_delay: u32,
79 // Target step
80 target_step: u32,
81 // Target speed delay, in 16.16 format
82 target_delay: u32,
83}
84
85/// This function computes square root of an `u64` number.
86fn u64sqrt(x0: u64) -> u64 {
87 let mut x = x0;
88 let mut xr = 0; // result register
89 let mut q2 = 0x4000_0000_0000_0000u64; // scan-bit register, set to highest possible result bit
90 while q2 != 0 {
91 if (xr + q2) <= x {
92 x -= xr + q2;
93 xr >>= 1;
94 xr += q2; // test flag
95 } else {
96 xr >>= 1;
97 }
98 q2 >>= 2; // shift twice
99 }
100
101 // add for rounding, if necessary
102 if xr < x { xr + 1 } else { xr }
103}
104
105impl Stepgen {
106 /// Create new copy of stepgen. `ticks_per_second` defines size of each tick stepgen operates.
107 /// All settings (acceleration, speed) and current parameters (speed) are defined in terms of
108 /// these ticks.
109 pub const fn new(ticks_per_second: u32) -> Stepgen {
110 Stepgen {
111 current_step: 0,
112 speed: 0,
113 delay: 0,
114 slewing_delay: 0,
115 ticks_per_second,
116 first_delay: 0,
117 target_step: 0,
118 target_delay: 0,
119 }
120 }
121
122
123 // Configuration methods. Should not be called while motor is running.
124
125 /// Set stepper motor acceleration, in steps per second per second (in 16.8 format).
126 /// Note that this method is computation intensive, so it's best to set acceleration
127 /// once and never change it.
128 ///
129 /// # Examples
130 ///
131 /// ```
132 /// use stepgen::Stepgen;
133 /// let mut stepgen = Stepgen::new(1_000_000);
134 ///
135 /// stepgen.set_acceleration(1200 << 8).unwrap();
136 /// ```
137 ///
138 /// # Errors
139 ///
140 /// Returns an error if acceleration is too slow (first delay does not fit into 16.8).
141 ///
142 /// Too slow:
143 ///
144 /// ```
145 /// use stepgen::{Stepgen, Error};
146 ///
147 /// let mut stepper = Stepgen::new(1_000_000);
148 ///
149 /// // 1 step per second per second -- too slow!
150 /// assert_eq!(Error::TooSlow, stepper.set_acceleration(1 << 8).unwrap_err());
151 /// ```
152 pub fn set_acceleration(&mut self, acceleration: u32) -> Result {
153 // c0 = F*sqrt(2/a)*.676 = F*sqrt(2/a)*676/1000 =
154 // F*sqrt(2*676*676/a)/1000 =
155 // F*sqrt(2*676*676*1^16)/(1000*1^8)
156 // We bring as much as we can under square root, to increase accuracy of division
157 // sqrt(1 << 16) is (1 << 8), which is to convert to 24.8
158 // We shift 24 bits to the left to adjust for acceleration in 24.8 format plus to convert
159 // result into 24.8 format, so the resulting shift is 40 bits.
160 // 676 is used to correct for the first step (see the linked paper)
161 let c0long: u64 = ((2u64 * 676 * 676) << 40) / u64::from(acceleration);
162 let c0: u64 = (u64::from(self.ticks_per_second) * u64sqrt(c0long) / 1000) >> 8;
163 if (c0 >> 24) != 0 {
164 // Doesn't fit in 16.8 format, our timer is only 16 bit.
165 return Err(Error::TooSlow);
166 }
167 // Convert to 16.16 format. We only need this precision during intermediate calculations.
168 self.first_delay = (c0 as u32) << 8;
169 Ok(())
170 }
171
172 /// Set destination step for the stepper motor pulse generator. This is one of the two methods
173 /// to control the step generation (target step and target speed). If current step > target
174 /// step, stepper motor would slow down until stop if running or stay stopped if not running.
175 ///
176 /// # Errors
177 /// If speed or acceleration are not set, returns an error `Error::SpeedAccelerationNotSet`.
178 ///
179 /// # Notes
180 /// 1. Steps could only go in positive direction. Therefore, setting target step to 0 wil
181 /// always force step generation to decelerate and stop.
182 pub fn set_target_step(&mut self, target_step: u32) -> Result {
183 if self.target_delay == 0 || self.first_delay == 0 {
184 return Err(Error::SpeedAccelerationNotSet);
185 }
186 self.target_step = target_step;
187 Ok(())
188 }
189
190 /// Set slew speed (maximum speed stepper motor would run), in steps per second. Note that
191 /// stepper motor would only reach this speed if target step is far enough, so there is
192 /// enough space for acceleration/deceleration.
193 ///
194 /// # Errors
195 ///
196 /// Returns an error if target speed is either too slow (first delay does not fit into 16.8) or
197 /// too fast (first delay is shorter than `TICKS_PER_UPDATE`).
198 ///
199 /// Too slow:
200 ///
201 /// ```
202 /// use stepgen::{Stepgen, Error};
203 ///
204 /// let mut stepper = Stepgen::new(1_000_000);
205 ///
206 /// // 1 step per second -- too slow!
207 /// assert_eq!(Error::TooSlow, stepper.set_target_speed(1 << 8).unwrap_err());
208 /// ```
209 ///
210 /// Too fast:
211 ///
212 /// ```
213 /// use stepgen::{Stepgen, Error};
214 ///
215 /// let mut stepper = Stepgen::new(1_000_000);
216 ///
217 /// // 1_000_000 step per second per second -- too fast!
218 /// assert_eq!(Error::TooFast, stepper.set_target_speed(1_000_000 << 8).unwrap_err());
219 /// ```
220 pub fn set_target_speed(&mut self, target_speed: u32) -> Result {
221 if target_speed == 0 {
222 // Too slow, speed is zero
223 return Err(Error::TooSlow);
224 }
225 let delay = (u64::from(self.ticks_per_second) << 16) / u64::from(target_speed);
226 if (delay >> 24) != 0 {
227 // Too slow, doesn't fit in in 16.8 format, our timer is only 16 bit.
228 return Err(Error::TooSlow);
229 }
230 if delay <= u64::from(FASTEST_DELAY) * (1 << 8) {
231 // Too fast, less than 10 ticks of a timer. 10 is an arbitrary number,
232 // just to make sure we have enough time to calculate next delay.
233 return Err(Error::TooFast);
234 }
235 // Convert to 16.16 format. We only need this precision during intermediate calculations.
236 self.target_delay = (delay as u32) << 8;
237 Ok(())
238 }
239
240 /// Current step stepgen is at.
241 pub fn current_step(&self) -> u32 {
242 self.current_step
243 }
244
245 /// Target step stepgen should stop at. Note that if stepper is running too fast, it might not
246 /// be able to stop exactly at this step. This could happen when target step is updated after
247 /// stepper motor accelerated to certain speed.
248 pub fn target_step(&self) -> u32 {
249 self.target_step
250 }
251
252 /// Get estimated current speed, in 24.8 format
253 pub fn current_speed(&self) -> u32 {
254 let delay = if self.slewing_delay != 0 { self.slewing_delay } else { self.delay };
255 let delay = delay >> 8; // Convert to 16.8 format
256 if delay != 0 {
257 let speed = (u64::from(self.ticks_per_second) << 16) / u64::from(delay);
258 speed as u32
259 } else {
260 0
261 }
262 }
263
264 /// If we are running at target speed
265 pub fn is_at_speed(&self) -> bool {
266 self.slewing_delay != 0
267 }
268
269 /// Returns '0' if should stop. Otherwise, returns timer delay in 24.8 format
270 fn next_delay(&mut self) -> u32 {
271 let target_step = self.target_step;
272 let target_delay = self.target_delay;
273 let st = self.current_step;
274
275 // We are at the stop point and speed is zero -- return "stopped" (delay of 0)
276 if st >= target_step && self.speed <= 1 {
277 self.speed = 0;
278 return 0;
279 }
280
281 // Stop slewing if target delay was changed
282 if self.slewing_delay != 0 && self.slewing_delay != target_delay {
283 self.slewing_delay = 0;
284 }
285
286 // Steps made so far
287 self.current_step += 1;
288
289 if self.speed == 0 {
290 let d = if target_delay > self.first_delay {
291 // No acceleration is necessary -- just return the target delay
292 target_delay
293 } else {
294 // First step: load first delay, count as one acceleration step
295 self.delay = self.first_delay;
296 self.speed = 1;
297 self.delay
298 };
299 return d >> 8; // Convert to 16.8 format
300 }
301
302 // Calculate the projected step we would stop at if we start decelerating right now
303 let est_stop = st + self.speed;
304 if est_stop == target_step {
305 // We would stop one step earlier than we want, so let's just
306 // return the same delay as the current one and start deceleration
307 // on the next step.
308 } else if est_stop > target_step {
309 // We need to stop at target step, slow down
310 self.slowdown();
311
312 // We are not slewing even though we could have slowed down below the slewing speed
313 self.slewing_delay = 0;
314 } else if self.slewing_delay == 0 && self.delay < target_delay {
315 // Not slewing and running too fast, slow down
316 self.slowdown();
317
318 // Switch to slewing if we slowed down enough
319 if self.delay >= target_delay {
320 self.slewing_delay = target_delay;
321 }
322 } else if self.slewing_delay == 0 && self.delay > target_delay {
323 // Not slewing and running too slow, speed up
324 self.speedup();
325
326 // Switch to slewing if we have accelerated enough
327 if self.delay <= target_delay {
328 self.slewing_delay = target_delay;
329 }
330 }
331
332 // If slewing, return slew delay. delay should be close enough, but could
333 // be different due to the accumulated rounding errors
334 let d = if self.slewing_delay != 0 { self.slewing_delay } else { self.delay };
335 d >> 8 // Convert to 16.8 format
336 }
337
338
339 fn speedup(&mut self) {
340 let denom = 4 * self.speed + 1;
341 self.delay -= (2 * self.delay + denom / 2) / denom;
342 self.speed += 1;
343 }
344
345 fn slowdown(&mut self) {
346 self.speed -= 1;
347 let denom = 4 * self.speed - 1;
348 self.delay += (2 * self.delay + denom / 2) / denom;
349 }
350}
351
352impl Iterator for Stepgen {
353 type Item = u32;
354
355 fn next(&mut self) -> Option<Self::Item> {
356 match Stepgen::next_delay(self) {
357 0 => None,
358 v => Some(v)
359 }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 const FREQUENCY: u32 = 1_000_000; // Tests assume timer ticking at 1us (1Mhz)
368
369 fn round(delay: u32) -> u32 {
370 (delay + 128) >> 8
371 }
372
373 #[test]
374 fn sqrt_works() {
375 assert_eq!(0, u64sqrt(0));
376 assert_eq!(1, u64sqrt(1));
377 assert_eq!(2, u64sqrt(4));
378 assert_eq!(3, u64sqrt(10));
379 assert_eq!(4, u64sqrt(15));
380 assert_eq!(0x80_00_00_00u64, u64sqrt(0x4000_0000_0000_0000u64));
381 assert_eq!(0x1_00_00_00_00u64, u64sqrt(0xffff_ffff_ffff_ffffu64));
382 }
383
384 #[test]
385 fn acceleration_too_slow() {
386 let mut stepgen = Stepgen::new(FREQUENCY);
387 assert_eq!(Err(Error::TooSlow), stepgen.set_acceleration(1 << 8));
388 }
389
390 #[test]
391 fn too_slow() {
392 let mut stepgen = Stepgen::new(FREQUENCY);
393 assert_eq!(Err(Error::TooSlow), stepgen.set_target_speed(1 << 8));
394 }
395
396 #[test]
397 fn too_slow_2() {
398 let mut stepgen = Stepgen::new(FREQUENCY);
399 stepgen.set_target_speed(3907).unwrap();
400 assert_eq!(Err(Error::TooSlow), stepgen.set_target_speed(3906));
401 }
402
403 #[test]
404 fn too_slow_zero() {
405 let mut stepgen = Stepgen::new(FREQUENCY);
406 assert_eq!(Err(Error::TooSlow), stepgen.set_target_speed(0));
407 }
408
409 #[test]
410 fn slower_than_first_step_after_accel() {
411 // Setting very slow speed after acceleration is OK
412 let mut stepgen = Stepgen::new(FREQUENCY);
413 stepgen.set_acceleration(1000 << 8).unwrap();
414 stepgen.set_target_speed(5120).unwrap(); // 20 pulses per second = 50_000 delay
415 stepgen.set_target_step(3).unwrap();
416 assert!(stepgen.first_delay < stepgen.target_delay);
417
418 // Walk three steps
419 assert_eq!(50_000 << 8, stepgen.next().unwrap());
420 assert_eq!(0, stepgen.speed);
421 assert_eq!(50_000 << 8, stepgen.next().unwrap());
422 assert_eq!(0, stepgen.speed);
423 assert_eq!(50_000 << 8, stepgen.next().unwrap());
424 assert_eq!(0, stepgen.speed);
425 assert!(stepgen.next().is_none());
426 }
427
428 #[test]
429 fn slower_than_first_step_before_accel() {
430 // Setting acceleration after setting slow speed is also OK
431 let mut stepgen = Stepgen::new(FREQUENCY);
432 stepgen.set_target_speed(5120).unwrap();
433 stepgen.set_acceleration(1000 << 8).unwrap();
434 stepgen.set_target_step(3).unwrap();
435 assert!(stepgen.first_delay < stepgen.target_delay);
436
437 // Walk three steps
438 assert_eq!(50_000 << 8, stepgen.next().unwrap());
439 assert_eq!(0, stepgen.speed);
440 assert_eq!(50_000 << 8, stepgen.next().unwrap());
441 assert_eq!(0, stepgen.speed);
442 assert_eq!(50_000 << 8, stepgen.next().unwrap());
443 assert_eq!(0, stepgen.speed);
444 assert!(stepgen.next().is_none());
445 }
446
447 #[test]
448 fn too_fast() {
449 let mut stepgen = Stepgen::new(FREQUENCY);
450 assert_eq!(Err(Error::TooFast), stepgen.set_target_speed(1_000_000 << 8));
451 }
452
453 #[test]
454 fn slow_during_acceleration() {
455 let mut stepgen = Stepgen::new(FREQUENCY);
456 stepgen.set_target_speed(800 << 8).unwrap();
457 stepgen.set_acceleration(1000 << 8).unwrap();
458 stepgen.set_target_step(core::u32::MAX).unwrap();
459
460 assert_eq!(0, stepgen.current_speed());
461
462 assert_eq!(30232, round(stepgen.next().unwrap()));
463 assert_eq!(18139, round(stepgen.next().unwrap()));
464 assert_eq!(14108, round(stepgen.next().unwrap()));
465 assert_eq!(11938, round(stepgen.next().unwrap()));
466
467 // Update target speed, want to run slower
468 stepgen.set_target_speed(50 << 8).unwrap();
469 assert_eq!(14108, round(stepgen.next().unwrap()));
470 assert_eq!(18139, round(stepgen.next().unwrap()));
471 assert_eq!(20000, round(stepgen.next().unwrap())); // 20000 = 1_000_000 / 50
472 assert_eq!(50 << 8, stepgen.current_speed());
473
474 // Slow a little bit more
475 stepgen.set_target_speed(40 << 8).unwrap();
476 assert_eq!(25000, round(stepgen.next().unwrap())); // 25000 = 1_000_000 / 40
477 assert_eq!(40 << 8, stepgen.current_speed());
478 }
479
480 #[test]
481 fn no_speed_set() {
482 let mut stepgen = Stepgen::new(FREQUENCY);
483 stepgen.set_acceleration(1000 << 8).unwrap();
484 assert_eq!(Err(Error::SpeedAccelerationNotSet), stepgen.set_target_step(1_000_000_000));
485 }
486
487 #[test]
488 fn no_acceleration_set() {
489 let mut stepgen = Stepgen::new(FREQUENCY);
490 stepgen.set_target_speed(800 << 8).unwrap();
491 assert_eq!(Err(Error::SpeedAccelerationNotSet), stepgen.set_target_step(1_000_000_000));
492 }
493}