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