// Copyright (C)2021-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // NOTE: This module bit-bangs the step signal to the A4988 driver. For more // accurate timing, you should generate the step signal from some kind of // dedicated real-time hardware, such as a microcontroller. using System; namespace IO.Devices.A4988 { /// /// Encapsulates the A4988 Stepper Motor Controller. /// public class Device : IO.Interfaces.Stepper.Output { private readonly int num_steps; private readonly IO.Interfaces.GPIO.Pin pin_step; private readonly IO.Interfaces.GPIO.Pin pin_dir; private readonly IO.Interfaces.GPIO.Pin pin_enable = null; private readonly IO.Interfaces.GPIO.Pin pin_reset = null; private readonly IO.Interfaces.GPIO.Pin pin_sleep = null; /// /// Constructor for a single A4988 device. /// /// The number of steps per rotation. /// This is a physical characteristic of the particular stepper motor /// being driven. /// GPIO pin object for the /// STEP signal. /// GPIO pin object for the /// DIR signal. /// GPIO pin object for the /// -ENABLE signal. /// GPIO pin object for the /// -RESET signal. /// GPIO pin object for the /// -SLEEP signal. public Device(int StepsPerRotation, IO.Interfaces.GPIO.Pin Step, IO.Interfaces.GPIO.Pin Dir, IO.Interfaces.GPIO.Pin Enable = null, IO.Interfaces.GPIO.Pin Reset = null, IO.Interfaces.GPIO.Pin Sleep = null) { this.num_steps = StepsPerRotation; this.pin_step = Step; this.pin_dir = Dir; this.pin_enable = Enable; this.pin_reset = Reset; this.pin_sleep = Sleep; this.Reset(); this.Disable(); this.Sleep(); this.Wakeup(); this.Enable(); } /// /// Move the stepper motor a specified number of steps at a /// specified rate. /// /// Number of steps to move. /// Negative values indicate reverse motion and positive values /// indicate forward motion. (The directions are nominal and depend on /// how the stepper motor coils are wired). Zero indicates the stepper /// motor should be stopped. /// The rate of motion, in steps per second. /// Negative values indicate reverse motion and positive values /// indicate forward motion. (The directions are nominal and depend on /// how the stepper motor coils are wired). Zero indicates the stepper /// motor should be stopped. /// This implementation supports a maximum rate of 500 steps /// per second. public void Move(int steps, float rate) { // Validate parameters if (steps == 0) return; if (rate == 0.0) return; int msecs = (int)(1000.0 / Math.Abs(rate) + 0.5); if (msecs < 2) throw new System.Exception("Invalid rate parameter."); // Set direction signal if (((steps < 0) && (rate > 0.0)) || ((steps > 0) && (rate < 0.0))) this.pin_dir.state = false; else this.pin_dir.state = true; steps = Math.Abs(steps); for (int i = 0; i < steps; i++) { this.pin_step.state = true; System.Threading.Thread.Sleep(1); this.pin_step.state = false; System.Threading.Thread.Sleep(msecs - 1); } } /// /// Spin (i.e. continuous rotation) the stepper moter at a specified rate. /// /// The rate of motion, in steps per second. /// Negative values indicate reverse motion and positive values /// indicate forward motion. (The directions are nominal and depend on /// how the stepper motor coils are wired). Zero indicates the stepper /// motor should be stopped. /// The A4988 stepper motor driver does not support continuous /// rotation and this method will always throw an exception. public void Spin(float rate) { throw new System.Exception("This driver does not support the Spin() method."); } /// /// Read-only property returning the number of steps a stepper motor /// has. /// public int StepsPerRotation { get { return this.num_steps; } } /// /// Enable the A4988 device. /// public void Enable() { if (this.pin_enable == null) return; this.pin_enable.state = false; // Enable is active low } /// /// Disable the A4988 device. /// public void Disable() { if (this.pin_enable == null) return; this.pin_enable.state = true; // Enable is active low } /// /// Reset the A4988 device. /// public void Reset() { if (this.pin_reset == null) return; this.pin_reset.state = false; // Reset is active low System.Threading.Thread.Sleep(1); this.pin_reset.state = true; // Reset is active low System.Threading.Thread.Sleep(1); } /// /// Put the A4988 device to sleep. /// public void Sleep() { if (this.pin_sleep == null) return; this.pin_sleep.state = false; // Sleep is active low } /// /// Wake up the A4988 device. /// public void Wakeup() { if (this.pin_sleep == null) return; this.pin_sleep.state = true; // Sleep is active low System.Threading.Thread.Sleep(1); } } }