diff --git a/.gitignore b/.gitignore index c4509976609..9c3d7df58e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ app/bin/ app/pde.jar build/macosx/work/ core/bin/ -core/core.jar \ No newline at end of file +core/core.jar +/.cproject +/.project diff --git a/.project b/.project index 0b76a40ba80..a3f29214ef7 100644 --- a/.project +++ b/.project @@ -1,17 +1,85 @@ - - - processing-head - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + processing-head + + + Arduino_Uno + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/second eclipse test/Release} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + it.baeyens.arduinonature + + diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 1d540c3741c..8e347454298 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -98,7 +98,7 @@ public class Sketch { */ private String libraryPath; /** - * List of library folders. + * List of library folders. */ private ArrayList importedLibraries; @@ -365,8 +365,8 @@ protected void nameCode(String newName) { if (!validExtension(newExtension)) { Base.showWarning(_("Problem with rename"), I18n.format( - _("\".{0}\" is not a valid extension."), newExtension - ), null); + _("\".{0}\" is not a valid extension."), newExtension + ), null); return; } @@ -399,33 +399,33 @@ protected void nameCode(String newName) { if (newName.equalsIgnoreCase(c.getFileName())) { Base.showMessage(_("Nope"), I18n.format( - _("A file named \"{0}\" already exists in \"{1}\""), - c.getFileName(), - folder.getAbsolutePath() - )); + _("A file named \"{0}\" already exists in \"{1}\""), + c.getFileName(), + folder.getAbsolutePath() + )); return; } } - + // In Arduino, don't allow a .cpp file with the same name as the sketch, // because the sketch is concatenated into a file with that name as part - // of the build process. + // of the build process. if (newName.equals(getName() + ".cpp")) { Base.showMessage(_("Nope"), _("You can't have a .cpp file with the same name as the sketch.")); return; } - + if (renamingCode && currentIndex == 0) { for (int i = 1; i < codeCount; i++) { if (sanitaryName.equalsIgnoreCase(code[i].getPrettyName()) && code[i].getExtension().equalsIgnoreCase("cpp")) { Base.showMessage(_("Nope"), I18n.format( - _("You can't rename the sketch to \"{0}\"\n" + - "because the sketch already has a .cpp file with that name."), - sanitaryName - )); + _("You can't rename the sketch to \"{0}\"\n" + + "because the sketch already has a .cpp file with that name."), + sanitaryName + )); return; } } @@ -458,10 +458,10 @@ protected void nameCode(String newName) { if (newFolder.exists()) { Base.showWarning(_("Cannot Rename"), I18n.format( - _("Sorry, a sketch (or folder) named " + + _("Sorry, a sketch (or folder) named " + "\"{0}\" already exists."), - newName - ), null); + newName + ), null); return; } @@ -484,10 +484,10 @@ protected void nameCode(String newName) { if (!current.renameTo(newFile, newExtension)) { Base.showWarning(_("Error"), I18n.format( - _("Could not rename \"{0}\" to \"{1}\""), - current.getFileName(), - newFile.getName() - ), null); + _("Could not rename \"{0}\" to \"{1}\""), + current.getFileName(), + newFile.getName() + ), null); return; } @@ -529,10 +529,10 @@ protected void nameCode(String newName) { if (!current.renameTo(newFile, newExtension)) { Base.showWarning(_("Error"), I18n.format( - _("Could not rename \"{0}\" to \"{1}\""), - current.getFileName(), - newFile.getName() - ), null); + _("Could not rename \"{0}\" to \"{1}\""), + current.getFileName(), + newFile.getName() + ), null); return; } } @@ -545,11 +545,11 @@ protected void nameCode(String newName) { } } catch (IOException e) { Base.showWarning(_("Error"), - I18n.format( + I18n.format( "Could not create the file \"{0}\" in \"{1}\"", - newFile, - folder.getAbsolutePath() - ), e); + newFile, + folder.getAbsolutePath() + ), e); return; } SketchCode newCode = new SketchCode(newFile, newExtension); @@ -735,7 +735,7 @@ public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(".pde"); } }); - + if (pdeFiles != null && pdeFiles.length > 0) { if (Preferences.get("editor.update_extension") == null) { Object[] options = { _("OK"), _("Cancel") }; @@ -753,12 +753,12 @@ public boolean accept(File dir, String name) { null, options, options[0]); - + if (result != JOptionPane.OK_OPTION) return false; // save cancelled - + Preferences.setBoolean("editor.update_extension", true); } - + if (Preferences.getBoolean("editor.update_extension")) { // Do rename of all .pde files to new .ino extension for (File pdeFile : pdeFiles) @@ -768,14 +768,14 @@ public boolean accept(File dir, String name) { } for (int i = 0; i < codeCount; i++) { - if (code[i].isModified()) + if (code[i].isModified()) code[i].save(); } calcModified(); return true; } - + protected boolean renameCodeToInoExtension(File pdeFile) { for (SketchCode c : code) { if (!c.getFile().equals(pdeFile)) @@ -787,7 +787,7 @@ protected boolean renameCodeToInoExtension(File pdeFile) { } return false; } - + /** * Handles 'Save As' for a sketch. @@ -861,11 +861,11 @@ protected boolean saveAs() throws IOException { if (newName.equalsIgnoreCase(code[i].getPrettyName()) && code[i].getExtension().equalsIgnoreCase("cpp")) { Base.showMessage(_("Nope"), - I18n.format( + I18n.format( _("You can't save the sketch as \"{0}\"\n" + "because the sketch already has a .cpp file with that name."), - newName - )); + newName + )); return false; } } @@ -1068,7 +1068,7 @@ public boolean addFile(File sourceFile) { if (!muchSuccess) { Base.showWarning(_("Error adding file"), I18n.format(_("Could not delete the existing ''{0}'' file."), filename), - null); + null); return false; } } @@ -1091,7 +1091,7 @@ public boolean addFile(File sourceFile) { } catch (IOException e) { Base.showWarning(_("Error adding file"), I18n.format(_("Could not add ''{0}'' to the sketch."), filename), - e); + e); return false; } } @@ -1216,13 +1216,13 @@ protected void cleanup() { // need to be recompiled, or if the board does not // use setting build.dependency //Base.removeDir(tempBuildFolder); - + // note that we can't remove the builddir itself, otherwise // the next time we start up, internal runs using Runner won't // work because the build dir won't exist at startup, so the classloader // will ignore the fact that that dir is in the CLASSPATH in run.sh Base.removeDescendants(tempBuildFolder); - + deleteFilesOnNextBuild = false; } else { // delete only stale source files, from the previously @@ -1240,7 +1240,7 @@ protected void cleanup() { } } } - + // Create a fresh applet folder (needed before preproc is run below) //tempBuildFolder.mkdirs(); } @@ -1282,8 +1282,8 @@ public static void buildSettingChanged() { private static boolean deleteFilesOnNextBuild = true; /** - * When running from the editor, take care of preparations before running - * the build. + * When running from the editor, take care of preparations before running + * the build. */ public void prepare() { // make sure the user didn't hide the sketch folder @@ -1479,7 +1479,7 @@ public ArrayList getImportedLibraries() { return importedLibraries; } - + /** * Map an error from a set of processed .java files back to its location * in the actual sketch. @@ -1489,7 +1489,7 @@ public ArrayList getImportedLibraries() { * @return A RunnerException to be sent to the editor, or null if it wasn't * possible to place the exception to the sketch code. */ -// public RunnerException placeExceptionAlt(String message, +// public RunnerException placeExceptionAlt(String message, // String filename, int line) { // String appletJavaFile = appletClassName + ".java"; // SketchCode errorCode = null; @@ -1521,14 +1521,14 @@ public ArrayList getImportedLibraries() { // line--; // // // getMessage() will be what's shown in the editor -// RunnerException exception = +// RunnerException exception = // new RunnerException(message, codeIndex, line, -1); // exception.hideStackTrace(); // return exception; // } // return null; // } - + /** * Map an error from a set of processed .java files back to its location @@ -1539,8 +1539,8 @@ public ArrayList getImportedLibraries() { * @return A RunnerException to be sent to the editor, or null if it wasn't * possible to place the exception to the sketch code. */ - public RunnerException placeException(String message, - String dotJavaFilename, + public RunnerException placeException(String message, + String dotJavaFilename, int dotJavaLine) { int codeIndex = 0; //-1; int codeLine = -1; @@ -1582,7 +1582,7 @@ public RunnerException placeException(String message, } } // could not find a proper line number, so deal with this differently. - // but if it was in fact the .java file we're looking for, though, + // but if it was in fact the .java file we're looking for, though, // send the error message through. // this is necessary because 'import' statements will be at a line // that has a lower number than the preproc offset, for instance. @@ -1614,7 +1614,7 @@ public String build(boolean verbose) throws RunnerException { */ public String build(String buildPath, boolean verbose) throws RunnerException { - + // run the preprocessor editor.status.progressUpdate(20); String primaryClassName = preprocess(buildPath); @@ -1628,8 +1628,8 @@ public String build(String buildPath, boolean verbose) } return null; } - - + + protected boolean exportApplet(boolean usingProgrammer) throws Exception { return exportApplet(tempBuildFolder.getAbsolutePath(), usingProgrammer); } @@ -1642,7 +1642,7 @@ public boolean exportApplet(String appletPath, boolean usingProgrammer) throws RunnerException, IOException, SerialException { prepare(); - + // build the sketch editor.status.progressNotice(_("Compiling sketch...")); String foundName = build(appletPath, false); @@ -1664,34 +1664,46 @@ public boolean exportApplet(String appletPath, boolean usingProgrammer) return true; } - + public void setCompilingProgress(int percent) { editor.status.progressUpdate(percent); } - + protected void size(String buildPath, String suggestedClassName) - throws RunnerException { + throws RunnerException { long size = 0; String maxsizeString = Base.getBoardPreferences().get("upload.maximum_size"); if (maxsizeString == null) return; long maxsize = Integer.parseInt(maxsizeString); + + String maxramString = Base.getBoardPreferences().get("upload.maximum_ram_size"); + long maxram = maxramString == null ? 1024 : Integer.parseInt(maxramString); + + String warningramString = Base.getBoardPreferences().get("upload.warning_ram_size"); + long warningram = warningramString == null ? maxram/2 : Integer.parseInt(warningramString); + Sizer sizer = new Sizer(buildPath, suggestedClassName); - try { + try { size = sizer.computeSize(); - System.out.println( - I18n.format( - _("Binary sketch size: {0} bytes (of a {1} byte maximum)"), - size, maxsize - ) - ); + System.out.println("Binary sketch size: " + size + " bytes (of a " + + maxsize + " byte maximum)"); + System.out.println("Chip memory sram: " + sizer.data + + " bytes (of a " +maxram+ " byte maximum)"); } catch (RunnerException e) { - System.err.println(I18n.format(_("Couldn't determine program size: {0}"), e.getMessage())); + e.printStackTrace(); } + if (sizer.data > maxram) + throw new RunnerException( + "Allowable chip memory exceeded; see http://www.arduino.cc/en/Reference/PROGMEM to reduce ram size"); + + if (sizer.data > warningram) + System.err.println("Warning Large amount of chip memory used. Consider using PROGMEM, http://www.arduino.cc/en/Reference/PROGMEM, to reduce ram size "); + if (size > maxsize) throw new RunnerException( - _("Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.")); + "Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it."); } @@ -1766,9 +1778,9 @@ public boolean exportApplicationPrompt() throws IOException, RunnerException { return false; } - + /** - * Export to application via GUI. + * Export to application via GUI. */ protected boolean exportApplication() throws IOException, RunnerException { return false; @@ -1902,7 +1914,7 @@ public String getDefaultExtension() { public List getHiddenExtensions() { return hiddenExtensions; } - + /** * Returns a String[] array of proper extensions. */ diff --git a/app/src/processing/app/debug/Sizer.java b/app/src/processing/app/debug/Sizer.java index d67728a3c85..b6a544f9f3d 100644 --- a/app/src/processing/app/debug/Sizer.java +++ b/app/src/processing/app/debug/Sizer.java @@ -1,35 +1,10 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Sizer - computes the size of a .hex file - Part of the Arduino project - http://www.arduino.cc/ - - Copyright (c) 2006 David A. Mellis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - $Id$ -*/ - package processing.app.debug; -import processing.app.Base; - import java.io.*; import java.util.*; +import processing.app.*; + public class Sizer implements MessageConsumer { private String buildPath, sketchName; private String firstLine; @@ -40,34 +15,40 @@ public Sizer(String buildPath, String sketchName) { this.buildPath = buildPath; this.sketchName = sketchName; } - + public long computeSize() throws RunnerException { - String avrBasePath = Base.getAvrBasePath(); + String userdir = System.getProperty("user.dir") + File.separator; + String avrBasePath; + if(Base.isMacOS()) { + avrBasePath = new String("Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/"); + } + else if(Base.isLinux()) { + avrBasePath = new String(""); + } + else { + avrBasePath = new String(userdir + "hardware/tools/avr/bin/"); + } String commandSize[] = new String[] { - avrBasePath + "avr-size", - " " + avrBasePath + "avr-objdump", + "-h", + "" }; - - commandSize[1] = buildPath + File.separator + sketchName + ".hex"; - int r = 0; + commandSize[2] = buildPath + File.separator + sketchName + ".elf"; + try { exception = null; size = -1; firstLine = null; Process process = Runtime.getRuntime().exec(commandSize); - MessageSiphon in = new MessageSiphon(process.getInputStream(), this); - MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); - + MessageSiphon messageSiphon = new MessageSiphon(process.getInputStream(), this); boolean running = true; - while(running) { try { - if (in.thread != null) - in.thread.join(); - if (err.thread != null) - err.thread.join(); - r = process.waitFor(); + process.waitFor(); + if (messageSiphon.getThread() != null) { + Thread.sleep(100); + } running = false; } catch (InterruptedException intExc) { } } @@ -76,33 +57,40 @@ public long computeSize() throws RunnerException { // some sub-class has overridden it to do so, thus we need to check for // it. See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1166589459 exception = new RunnerException( - (e.toString() == null) ? e.getClass().getName() + r : e.toString() + r); + (e.toString() == null) ? e.getClass().getName() : e.toString()); } - + if (exception != null) throw exception; - + if (size == -1) throw new RunnerException(firstLine); - - return size; + + return size+1; } - + + public int data = 0; public void message(String s) { if (firstLine == null) firstLine = s; else { - StringTokenizer st = new StringTokenizer(s, " "); - try { - st.nextToken(); - st.nextToken(); - st.nextToken(); - size = (new Integer(st.nextToken().trim())).longValue(); - } catch (NoSuchElementException e) { - exception = new RunnerException(e.toString()); - } catch (NumberFormatException e) { - exception = new RunnerException(e.toString()); - } + size+=checkTag(s," .text "); + data+=checkTag(s," .data "); + data+=checkTag(s," .bss "); + // exception = new RunnerException(e.toString()); + + } + } + + private int checkTag(String s, String tag){ + int size=0; + int p = s.indexOf(tag); + if(p != -1){ + s=s.substring(p+6).trim(); + p=s.indexOf(" "); + if(p != -1) + size = Integer.parseInt(s.substring(0,p).trim(),16); } + return size; } } \ No newline at end of file diff --git a/libraries/Stepper/MultiStepper.cpp b/libraries/Stepper/MultiStepper.cpp new file mode 100644 index 00000000000..b09c875c2dd --- /dev/null +++ b/libraries/Stepper/MultiStepper.cpp @@ -0,0 +1,116 @@ +#include +#include + +#include +#include "Stepper.h" +#include "utility/vector.h" + +#include "MultiStepper.h" + +MultiStepper::MultiStepper() {} + +MultiStepper::~MultiStepper() {} + +uint8_t MultiStepper::registerStepper(Stepper* stepper) +{ + this->steppers.push_back(stepper); + return this->steppers.size() - 1; +} + +Stepper* MultiStepper::getStepper(uint8_t motor_id) +{ + return this->steppers[motor_id]; +} + +void MultiStepper::setSpeed(int speed) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setSpeed(speed); + } +} + +void MultiStepper::setDriveType(Stepper::DriveType drive_type) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setDriveType(drive_type); + } +} + +void MultiStepper::setHold(bool hold) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setHold(hold); + } +} + +void MultiStepper::setDirection(bool forward) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setDirection(forward); + } +} + +void MultiStepper::setStepsToMove(unsigned int steps_to_move) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setStepsToMove(steps_to_move); + } +} + +void MultiStepper::step(int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = steps_to_move; + } + step(steps_to_move_array); +} + +void MultiStepper::step(int steps_to_move[]) +{ + uint8_t num_motors = numMotors(); + + for (uint8_t i = 0; i < num_motors; i++) { + Stepper* stepper = this->steppers[i]; + + stepper->setStepsToMove(abs(steps_to_move[i])); + // determine direction based on whether steps_to_mode is + or -: + stepper->setDirection(steps_to_move[i] > 0); + } + + while(true) { + if (!stepSlice()) { + // no steps left for any motor. We're done! + break; + } + } +} + +bool MultiStepper::stepSlice() +{ + uint8_t num_motors = numMotors(); + + bool any_steps_left = false; + const unsigned long now = micros(); + for (uint8_t i = 0; i < num_motors; i++) { + Stepper* stepper = this->steppers[i]; + // if there are no steps left for any motors, we're done! + if (stepper->getStepsLeftToMove() > 0) { + any_steps_left = true; + + stepper->stepSlice(now); + } + } + + return any_steps_left; +} + +uint8_t MultiStepper::numMotors() +{ + return this->steppers.size(); +} diff --git a/libraries/Stepper/MultiStepper.h b/libraries/Stepper/MultiStepper.h new file mode 100644 index 00000000000..7a6f7c05732 --- /dev/null +++ b/libraries/Stepper/MultiStepper.h @@ -0,0 +1,77 @@ +/* + Multi.h - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerStepper() multiple times + You can move all of them in unison using the void step(int[]) or void step(int) functions. +*/ + +// ensure this library description is only included once +#ifndef MultiStepper_h +#define MultiStepper_h + +#include +#include "utility/Vector.h" +#include "Stepper.h" + +// library interface description +class MultiStepper { + public: + // constructors: + // Empty constructor + MultiStepper(); + + // destructor + ~MultiStepper(); + + // Registers an extra stepper. The return value is the motor's id, and should be used to + // reference it in the future. + uint8_t registerStepper(Stepper* stepper); + + Stepper* getStepper(uint8_t motor_id); + + // speed setter methods: + // Set the speed for all motors. in RPM + void setSpeed(int speed); + + // drive type setter methods: + // Set the drive type for all motors. + void setDriveType(Stepper::DriveType drive_type); + + void setDirection(bool forward); + + // sets a limit on how many steps should be moved + void setStepsToMove(unsigned int steps_to_move); + + // mover methods: + // Move all motors the number_of_steps + // Note that steps_to_move is an integer, with a max value of 2^15-1 + void step(int steps_to_move); + // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds + // to the number of steps the motor with motor_id being the index of that element should be + // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors + // to move, set those values to 0. + void step(int steps_to_move[]); + + bool stepSlice(); + + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); + + // Returns the number of motors registered. + uint8_t numMotors(void); + + private: + Vector steppers; +}; + +#endif diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 5d6b5e53664..0d289162ebb 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -1,220 +1,262 @@ -/* - Stepper.cpp - - Stepper library for Wiring/Arduino - Version 0.4 - - Original library (0.1) by Tom Igoe. - Two-wire modifications (0.2) by Sebastian Gassner - Combination version (0.3) by Tom Igoe and David Mellis - Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley - - Drives a unipolar or bipolar stepper motor using 2 wires or 4 wires - - When wiring multiple stepper motors to a microcontroller, - you quickly run out of output pins, with each motor requiring 4 connections. - - By making use of the fact that at any time two of the four motor - coils are the inverse of the other two, the number of - control connections can be reduced from 4 to 2. - - A slightly modified circuit around a Darlington transistor array or an L293 H-bridge - connects to only 2 microcontroler pins, inverts the signals received, - and delivers the 4 (2 plus 2 inverted ones) output signals required - for driving a stepper motor. - - The sequence of control signals for 4 control wires is as follows: - - Step C0 C1 C2 C3 - 1 1 0 1 0 - 2 0 1 1 0 - 3 0 1 0 1 - 4 1 0 0 1 - - The sequence of controls signals for 2 control wires is as follows - (columns C1 and C2 from above): - - Step C0 C1 - 1 0 1 - 2 1 1 - 3 1 0 - 4 0 0 - - The circuits can be found at - -http://www.arduino.cc/en/Tutorial/Stepper - - - */ +#include +#include +#include +#include "utility/Vector.h" -#include "Arduino.h" #include "Stepper.h" +// Only even rows are used for full-stepping. +// Only odd rows are used for wave-stepping. +// All rows are used for half-stepping. +const uint8_t Stepper::STEP_VALUES[8][4] = { + {HIGH, LOW, HIGH, LOW}, // not used for wave-stepping + {LOW, LOW, HIGH, LOW}, // not used for full-stepping + {LOW, HIGH, HIGH, LOW}, // not used for wave-stepping + {LOW, HIGH, LOW, LOW}, // not used for full-stepping + {LOW, HIGH, LOW, HIGH}, // not used for wave-stepping + {LOW, LOW, LOW, HIGH}, // not used for full-stepping + {HIGH, LOW, LOW, HIGH}, // not used for wave-stepping + {HIGH, LOW, LOW, LOW} // not used for full-stepping +}; + +// Pin values that turn off current to a motor so that it does not hold its position. +// This is only possible for 4-pin configurations +const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; + +Stepper::Stepper(const Stepper& copy) +{ + init(copy.steps_per_rev, copy.motor_pins, copy.num_pins); +} + +Stepper& Stepper::operator=(const Stepper& copy) { + free(this->motor_pins); + init(copy.steps_per_rev, copy.motor_pins, copy.num_pins); +} + /* * two-wire constructor. * Sets which wires should control the motor. */ -Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2) -{ - this->step_number = 0; // which step the motor is on - this->speed = 0; // the motor speed, in revolutions per minute - this->direction = 0; // motor direction - this->last_step_time = 0; // time stamp in ms of the last step taken - this->number_of_steps = number_of_steps; // total number of steps for this motor - - // Arduino pins for the motor control connection: - this->motor_pin_1 = motor_pin_1; - this->motor_pin_2 = motor_pin_2; - - // setup the pins on the microcontroller: - pinMode(this->motor_pin_1, OUTPUT); - pinMode(this->motor_pin_2, OUTPUT); - - // When there are only 2 pins, set the other two to 0: - this->motor_pin_3 = 0; - this->motor_pin_4 = 0; - - // pin_count is used by the stepMotor() method: - this->pin_count = 2; +Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) +{ + uint8_t motor_pins[] = {motor_pin_1, motor_pin_2}; + init(steps_per_rev, motor_pins, 2); } - /* * constructor for four-pin version * Sets which wires should control the motor. */ +Stepper::Stepper(int steps_per_rev, + uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) +{ + uint8_t motor_pins[] = {motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4}; + init(steps_per_rev, motor_pins, 4); +} + +Stepper::~Stepper() +{ + free(this->motor_pins); +} -Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4) +void Stepper::init(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) { - this->step_number = 0; // which step the motor is on - this->speed = 0; // the motor speed, in revolutions per minute - this->direction = 0; // motor direction - this->last_step_time = 0; // time stamp in ms of the last step taken - this->number_of_steps = number_of_steps; // total number of steps for this motor - - // Arduino pins for the motor control connection: - this->motor_pin_1 = motor_pin_1; - this->motor_pin_2 = motor_pin_2; - this->motor_pin_3 = motor_pin_3; - this->motor_pin_4 = motor_pin_4; + this->forward = true; + this->drive_type = FullStep; + this->last_step_time = 0UL; + + this->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); + for (uint8_t i = 0; i < num_pins; i++) { + this->motor_pins[i] = motor_pins[i]; + } - // setup the pins on the microcontroller: - pinMode(this->motor_pin_1, OUTPUT); - pinMode(this->motor_pin_2, OUTPUT); - pinMode(this->motor_pin_3, OUTPUT); - pinMode(this->motor_pin_4, OUTPUT); + this->num_pins = num_pins; + this->step_delay = 0UL; + this->step_number = 0; + this->steps_per_rev = steps_per_rev; + this->steps_to_move = 0; + this->should_hold = true; + + // Setup the pins on the microcontroller. + for (uint8_t i = 0; i < num_pins; i++) { + pinMode(motor_pins[i], OUTPUT); + } - // pin_count is used by the stepMotor() method: - this->pin_count = 4; + // Write LOW to each pin initially so the motor is not driven and current is not wasted. + // This only really makes sense for a 4-pin motor, but it's still better than letting the pins of + // a 2-pin motor have random values. + writeMotorPins(motor_pins, Stepper::OFF_VALUES, num_pins); } /* Sets the speed in revs per minute - */ -void Stepper::setSpeed(long whatSpeed) +void Stepper::setSpeed(unsigned int speed) { - this->step_delay = 60L * 1000L / this->number_of_steps / whatSpeed; + this->step_delay = 60UL * 1000000UL / this->steps_per_rev / speed; + this->step_delay = this->step_delay; } /* - Moves the motor steps_to_move steps. If the number is negative, + Sets the drive type + */ +void Stepper::setDriveType(DriveType drive_type) +{ + this->drive_type = drive_type; +} + +void Stepper::setHold(bool hold) +{ + this->should_hold = hold; + + // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a + // SEGFAULT would be really bad. + if (this->num_pins == 4) { + if (this->should_hold) { + // This will rewrite the latest pin values + stepMotor(); + } else { + writeMotorPins(this->motor_pins, Stepper::OFF_VALUES, this->num_pins); + } + } +} + +void Stepper::setDirection(bool forward) +{ + this->forward = forward; +} + +void Stepper::setStepsToMove(unsigned int steps_to_move) +{ + this->steps_to_move = steps_to_move; +} + +unsigned int Stepper::getStepsLeftToMove() +{ + return this->steps_to_move; +} + +/* + Moves the motor steps_to_move steps. If the number is negative, the motor moves in the reverse direction. */ -void Stepper::step(int steps_to_move) -{ - int steps_left = abs(steps_to_move); // how many steps to take - +void Stepper::step(int steps_to_move) { + setStepsToMove(abs(steps_to_move)); // determine direction based on whether steps_to_mode is + or -: - if (steps_to_move > 0) {this->direction = 1;} - if (steps_to_move < 0) {this->direction = 0;} - - - // decrement the number of steps, moving one step each time: - while(steps_left > 0) { + setDirection(steps_to_move > 0); + + while(this->steps_to_move > 0) { + stepSlice(); + } + + // TODO(smike): Since this happens right after the last step, it will effectively cancel it out. + // Add a delay so the motor has time to complete its last step. + setHold(this->should_hold); +} + +void Stepper::stepSlice() { + stepSlice(micros()); +} + +void Stepper::stepSlice(const unsigned long now) +{ + if (this->steps_to_move == 0) { + // we have nothing to do + return; + } + + int steps_per_rev; + unsigned long step_delay; + + steps_per_rev = this->steps_per_rev; + step_delay = this->step_delay; + if (this->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + steps_per_rev *= 2; + step_delay /= 2UL; + } + + unsigned long elapsed_time = now - this->last_step_time; + + // micros has rolled over. Assume that it has only rolled over once. + if (now < this->last_step_time) { + // 0xFFFF is 2^32-1, the maximum value of an unsigned long + elapsed_time += 0xFFFFUL; + } + // move only if the appropriate delay has passed: - if (millis() - this->last_step_time >= this->step_delay) { - // get the timeStamp of when you stepped: - this->last_step_time = millis(); - // increment or decrement the step number, - // depending on direction: - if (this->direction == 1) { - this->step_number++; - if (this->step_number == this->number_of_steps) { - this->step_number = 0; - } - } - else { - if (this->step_number == 0) { - this->step_number = this->number_of_steps; - } - this->step_number--; - } - // decrement the steps left: - steps_left--; - // step the motor to step number 0, 1, 2, or 3: - stepMotor(this->step_number % 4); - } + if (elapsed_time >= step_delay) { + // get the timeStamp of when you stepped: + this->last_step_time = now; + + stepNow(); + + // decrement the steps left: + this->steps_to_move--; + } +} + +void Stepper::stepNow() +{ + // increment or decrement the step number, + // depending on direction: + if (this->forward) { + this->step_number++; + } else { + this->step_number--; } + this->step_number %= steps_per_rev; + // step the motor: + stepMotor(); } /* * Moves the motor forward or backwards. */ -void Stepper::stepMotor(int thisStep) +void Stepper::stepMotor() { - if (this->pin_count == 2) { - switch (thisStep) { - case 0: /* 01 */ - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, HIGH); - break; - case 1: /* 11 */ - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, HIGH); - break; - case 2: /* 10 */ - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, LOW); - break; - case 3: /* 00 */ - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, LOW); - break; - } + // Only write out the values if the configuration is valid. + if (this->num_pins != 4 && + !(this->num_pins == 2 && this->drive_type == FullStep)) { + return; } - if (this->pin_count == 4) { - switch (thisStep) { - case 0: // 1010 - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, LOW); - digitalWrite(motor_pin_3, HIGH); - digitalWrite(motor_pin_4, LOW); - break; - case 1: // 0110 - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, HIGH); - digitalWrite(motor_pin_3, HIGH); - digitalWrite(motor_pin_4, LOW); + + const uint8_t* step_row; + switch(this->drive_type) { + case FullStep: + step_row = Stepper::STEP_VALUES[(this->step_number % 4) * 2]; + if (this->num_pins == 2) { + // For 2-wire configurations we take the middle two values, so we move the pointer up by + // one so that the first two values are really elements 1 and 2, instead of 0 and 1. + step_row++; + } break; - case 2: //0101 - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, HIGH); - digitalWrite(motor_pin_3, LOW); - digitalWrite(motor_pin_4, HIGH); + case HalfStep: + step_row = Stepper::STEP_VALUES[this->step_number % 8]; break; - case 3: //1001 - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, LOW); - digitalWrite(motor_pin_3, LOW); - digitalWrite(motor_pin_4, HIGH); + case Wave: + step_row = Stepper::STEP_VALUES[(this->step_number % 4) * 2 + 1]; break; - } + default: + // If the this is an unknown drive type do nothing. + return; + } + + writeMotorPins(this->motor_pins, step_row, this->num_pins); +} + +void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins) +{ + for (uint8_t i = 0; i < num_pins; i++) { + digitalWrite(motor_pins[i], values[i]); } } /* version() returns the version of the library: */ -int Stepper::version(void) +uint8_t Stepper::version(void) { - return 4; + return 5; } diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index 4094aee9077..5dff30eaf73 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -1,19 +1,19 @@ /* Stepper.h - - Stepper library for Wiring/Arduino - Version 0.4 - + Original library (0.1) by Tom Igoe. Two-wire modifications (0.2) by Sebastian Gassner Combination version (0.3) by Tom Igoe and David Mellis Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley - Drives a unipolar or bipolar stepper motor using 2 wires or 4 wires + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires When wiring multiple stepper motors to a microcontroller, - you quickly run out of output pins, with each motor requiring 4 connections. + you quickly run out of output pins, with each motor requiring 4 connections. By making use of the fact that at any time two of the four motor coils are the inverse of the other two, the number of - control connections can be reduced from 4 to 2. + control connections can be reduced from 4 to 2. A slightly modified circuit around a Darlington transistor array or an L293 H-bridge connects to only 2 microcontroler pins, inverts the signals received, @@ -37,47 +37,140 @@ 3 1 0 4 0 0 - The circuits can be found at - http://www.arduino.cc/en/Tutorial/Stepper + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms */ // ensure this library description is only included once #ifndef Stepper_h #define Stepper_h +#include +#include "utility/Vector.h" + +// forward declaration so it can be counted as a friend class. +class MultiStepper; + // library interface description class Stepper { public: + enum DriveType { FullStep, HalfStep, Wave }; + // constructors: - Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2); - Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4); + // The two constructors below will also add a motor as motor_id 0. + // steps_per_rev should be in full steps + Stepper() {} + Stepper(const Stepper& copy); + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // destructor + ~Stepper(); + + Stepper& operator=(const Stepper& copy); + + // speed setter methods: + // Set the speed of the motor, in RPM + void setSpeed(unsigned int speed); + + // drive type setter methods: + // Set the drive type of the motor. + void setDriveType(DriveType drive_type); + + // mover methods: + // Note that steps_to_move is an integer, with a max value of 2^15-1 + void step(int steps_to_move); + // Does a step without regard for speed or the steps left to move. After this method it will be + // just like no steps have happened with respect to speed and number of steps left. + void stepNow(); - // speed setter method: - void setSpeed(long whatSpeed); + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); - // mover method: - void step(int number_of_steps); + void setDirection(bool forward); - int version(void); + // sets a limit on how many steps should be moved + void setStepsToMove(unsigned int steps_to_move); + + // returns how many steps are left to move. + // This is scaled based on the drive type (1 FullStep = 2 HalfSteps) + unsigned int getStepsLeftToMove(); + + void stepSlice(); + + uint8_t version(void); + + friend class MultiStepper; + + protected: + // Helper method for stepSlice(). Takes the current time in microseconds. + // It is useful when needing to move multiple motors simultaneously. Having each motor calculate + // can make them out of sync, and slow since micros() take a long time to evaluate. + void stepSlice(const unsigned long now); private: - void stepMotor(int this_step); - - int direction; // Direction of rotation - int speed; // Speed in RPMs - unsigned long step_delay; // delay between steps, in ms, based on speed - int number_of_steps; // total number of steps this motor can take - int pin_count; // whether you're driving the motor with 2 or 4 pins - int step_number; // which step the motor is on - - // motor pin numbers: - int motor_pin_1; - int motor_pin_2; - int motor_pin_3; - int motor_pin_4; - - long last_step_time; // time stamp in ms of when the last step was taken + // initialization + void init(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); + + // moves the motor specified one step. + void stepMotor(); + + // writes the given values to the motor pins. + void writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins); + + // Only even rows are used for full-stepping. + // Only odd rows are used for wave-stepping. + // All rows are used for half-stepping. + static const uint8_t STEP_VALUES[8][4]; + + // Pin values that turn off current to a motor so that it does not hold its position. + // This is only possible for 4-pin configurations + static const uint8_t OFF_VALUES[4]; + + uint8_t* motor_pins; // motor pin numbers + uint8_t num_pins; + + Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. + unsigned long step_delay; // delay between steps, in micros, based on speed + int steps_per_rev; // total number of steps this motor can take + bool forward; // whether to move forward or backward + + unsigned int step_number; // which step the motor is on + unsigned int steps_to_move; // the number of steps left to take + unsigned long last_step_time; // time stamp in micros of when the last step was taken + + bool should_hold; // whether to hold the motor position when not stepping. }; #endif - diff --git a/libraries/Stepper/keywords.txt b/libraries/Stepper/keywords.txt index 19a0fadf2bd..1f98a38c724 100644 --- a/libraries/Stepper/keywords.txt +++ b/libraries/Stepper/keywords.txt @@ -6,23 +6,41 @@ # Datatypes (KEYWORD1) ####################################### -Stepper KEYWORD1 +MultiStepper KEYWORD1 +Stepper KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -step KEYWORD2 -setSpeed KEYWORD2 -version KEYWORD2 + +# Common betwen Stepper and MultiStepper +setDriveType KEYWORD2 +setDirection KEYWORD2 +setStepsToMove KEYWORD2 +setHold KEYWORD2 +setSpeed KEYWORD2 +step KEYWORD2 +stepSlice KEYWORD2 + +# Unique to Stepper +getStepsLeftToMove KEYWORD2 +stepNow KEYWORD2 +version KEYWORD2 + +# Unique to MultiStepper +getStepper KEYWORD2 +numMotors KEYWORD2 +registerStepper KEYWORD2 ###################################### # Instances (KEYWORD2) ####################################### -direction KEYWORD2 -speed KEYWORD2 - ####################################### # Constants (LITERAL1) ####################################### + +FullStep LITERAL1 +HalfStep LITERAL1 +Wave LITERAL1 \ No newline at end of file diff --git a/libraries/Stepper/utility/Vector.h b/libraries/Stepper/utility/Vector.h new file mode 100644 index 00000000000..3b10d1529be --- /dev/null +++ b/libraries/Stepper/utility/Vector.h @@ -0,0 +1,73 @@ +// Adapted from a post on the Arduino Forums by Gabb: +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289151491 + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +// Minimal class to replace std::vector +template +class Vector { + size_t d_size; // Stores no. of actually stored objects + size_t d_capacity; // Stores allocated capacity + Data *d_data; // Stores data +public: + // Default constructor + Vector() : d_size(0), d_capacity(0), d_data(0) {}; + // Copy constuctor + Vector(Vector const &other) : d_size(other.d_size), d_capacity(other.d_capacity), d_data(0) { + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + }; + + // Destructor + ~Vector() { + free(d_data); + }; + + // Needed for memory management + Vector &operator=(Vector const &other) { + free(d_data); + d_size = other.d_size; + d_capacity = other.d_capacity; + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + return *this; + }; + + // Const getter + Data const &operator[](size_t idx) const { + return d_data[idx]; + }; + // Changeable getter + Data &operator[](size_t idx) { + return d_data[idx]; + }; + + // Adds new value. If needed, allocates more space + void push_back(Data const &x) { + if (d_capacity == d_size) { + resize(); + } + d_data[d_size++] = x; + }; + + // Size getter + size_t size() const { + return d_size; + }; + +private: + // Allocates double the old space + void resize() { + d_capacity = d_capacity ? d_capacity * 2 : 1; + Data *newdata = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(newdata, d_data, d_size * sizeof(Data)); + free(d_data); + d_data = newdata; + }; +}; + +#endif diff --git a/libraries/Stepper3/Stepper3.cpp b/libraries/Stepper3/Stepper3.cpp new file mode 100644 index 00000000000..c6b26ec2781 --- /dev/null +++ b/libraries/Stepper3/Stepper3.cpp @@ -0,0 +1,402 @@ +/* + Stepper.cpp - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. + + When wiring multiple stepper motors to a microcontroller, + you quickly run out of output pins, with each motor requiring 4 connections. + + By making use of the fact that at any time two of the four motor + coils are the inverse of the other two, the number of + control connections can be reduced from 4 to 2. + + A slightly modified circuit around a Darlington transistor array or an L293 H-bridge + connects to only 2 microcontroler pins, inverts the signals received, + and delivers the 4 (2 plus 2 inverted ones) output signals required + for driving a stepper motor. + + The sequence of control signals for 4 control wires is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 1 1 0 + 3 0 1 0 1 + 4 1 0 0 1 + + The sequence of controls signals for 2 control wires is as follows + (columns C1 and C2 from above): + + Step C0 C1 + 1 0 1 + 2 1 1 + 3 1 0 + 4 0 0 + + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms +*/ + +#include +#include + +#include +#include "utility/vector.h" + +#include "Stepper3.h" + +// Only even rows are used for full-stepping. +// Only odd rows are used for wave-stepping. +// All rows are used for half-stepping. +const uint8_t Stepper::STEP_VALUES[8][4] = { + {HIGH, LOW, HIGH, LOW}, // not used for wave-stepping + {LOW, LOW, HIGH, LOW}, // not used for full-stepping + {LOW, HIGH, HIGH, LOW}, // not used for wave-stepping + {LOW, HIGH, LOW, LOW}, // not used for full-stepping + {LOW, HIGH, LOW, HIGH}, // not used for wave-stepping + {LOW, LOW, LOW, HIGH}, // not used for full-stepping + {HIGH, LOW, LOW, HIGH}, // not used for wave-stepping + {HIGH, LOW, LOW, LOW} // not used for full-stepping +}; + +// Pin values that turn off current to a motor so that it does not hold its position. +// This is only possible for 4-pin configurations +const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; + +Stepper::Stepper() : should_halt(false), callback(NULL) {} + +/* + * two-wire constructor. + * Sets which wires should control the motor. + */ +Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) + : should_halt(false), callback(NULL) +{ + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2); +} + +/* + * constructor for four-pin version + * Sets which wires should control the motor. + */ +Stepper::Stepper(int steps_per_rev, + uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) + : should_halt(false), callback(NULL) +{ + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4); +} + +Stepper::~Stepper() +{ + uint8_t num_motors = numMotors(); + for(uint8_t i = 0; i < num_motors; i++) { + free(motor_infos[i]->motor_pins); + delete motor_infos[i]; + } +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2 }; + return registerMotor(steps_per_rev, motor_pins, 2); +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4 }; + return registerMotor(steps_per_rev, motor_pins, 4); +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) +{ + MotorInfo* motor_info = new MotorInfo; + motor_info->drive_type = FullStep; + motor_info->last_step_time = 0UL; + + motor_info->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); + for (uint8_t i = 0; i < num_pins; i++) { + motor_info->motor_pins[i] = motor_pins[i]; + } + + motor_info->num_pins = num_pins; + motor_info->step_delay = 0UL; + motor_info->step_number = 0; + motor_info->steps_per_rev = steps_per_rev; + motor_info->should_hold = true; + + setupMotorPins(motor_pins, num_pins); + + this->motor_infos.push_back(motor_info); + return motor_infos.size() - 1; +} + +void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { + // Setup the pins on the microcontroller. + for (uint8_t i = 0; i < num_pins; i++) { + pinMode(motor_pins[i], OUTPUT); + } + + // Write LOW to each pin initially so the motor is not driven and current is not wasted. + // This only really makes sense for a 4-pin motor, but it's still better than letting the pins of + // a 2-pin motor have random values. + writeMotorPins(motor_pins, Stepper::OFF_VALUES, num_pins); +} + +/* + Sets the speed in revs per minute +*/ +void Stepper::setSpeed(int speed) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setSpeed(i, speed); + } +} + +void Stepper::setSpeed(uint8_t motor_id, int speed) +{ + MotorInfo* motor_info = this->motor_infos[motor_id]; + motor_info->step_delay = 60UL * 1000000UL / motor_info->steps_per_rev / speed; + motor_info->step_delay = abs(motor_info->step_delay); +} + +/* + Sets the drive type + */ +void Stepper::setDriveType(DriveType drive_type) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setDriveType(i, drive_type); + } +} + +void Stepper::setDriveType(uint8_t motor_id, DriveType drive_type) +{ + this->motor_infos[motor_id]->drive_type = drive_type; +} + +void Stepper::setHold(bool hold) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setHold(i, hold); + } +} + +void Stepper::setHold(uint8_t motor_id, bool hold) +{ + this->motor_infos[motor_id]->should_hold = hold; +} + +/* + Moves the motor steps_to_move steps. If the number is negative, + the motor moves in the reverse direction. + */ +void Stepper::step(int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = steps_to_move; + } + step(steps_to_move_array); +} + +void Stepper::step(uint8_t motor_id, int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = i == motor_id ? steps_to_move : 0; + } + step(steps_to_move_array); +} + +void Stepper::step(int steps_to_move[]) +{ + uint8_t num_motors = numMotors(); + + unsigned int steps_left[num_motors]; + bool direction_positive[num_motors]; + int steps_per_rev[num_motors]; + unsigned long step_delay[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + const MotorInfo* motor_info = this->motor_infos[i]; + steps_left[i] = abs(steps_to_move[i]); // how many steps to take + + // determine direction based on whether steps_to_mode is + or -: + direction_positive[i] = steps_to_move[i] > 0; + + steps_per_rev[i] = motor_info->steps_per_rev; + step_delay[i] = motor_info->step_delay; + if (motor_info->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + steps_per_rev[i] *= 2; + step_delay[i] /= 2UL; + } + } + + while(!this->should_halt) { + const unsigned long now = micros(); + bool any_steps_left = false; + // look at each motor to see if it should be moved now. + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + if (steps_left[i] > 0) { + any_steps_left = true; + + unsigned long elapsed_time = now - motor_info->last_step_time; + // micros has rolled over. Assume that it has only rolled over once. + if (now < motor_info->last_step_time) { + // 0xFFFF is 2^32-1, the maximum value of an unsigned long + elapsed_time += 0xFFFFUL; + } + + // move only if the appropriate delay has passed: + if (elapsed_time >= step_delay[i]) { + // get the timeStamp of when you stepped: + motor_info->last_step_time = now; + // increment or decrement the step number, + // depending on direction: + if (direction_positive[i]) { + motor_info->step_number++; + } else { + motor_info->step_number--; + } + motor_info->step_number %= steps_per_rev[i]; + // decrement the steps left: + steps_left[i]--; + // step the motor: + stepMotor(i); + } + } + } + + // if there are no steps left for any motors, we're done! + if (!any_steps_left) { + break; + } + + // if it is set, call the callback. + if (this->callback != NULL) { + this->callback(); + } + } + + // reset halt signal + this->should_halt = false; + + // TODO(smike): Since this happens right after the last step, it will effectively cancel it out. + // Add a delay so the motor has time to complete its last step. + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a + // SEGFAULT would be really bad. + if (!motor_info->should_hold && motor_info->num_pins == 4) { + writeMotorPins(motor_info->motor_pins, Stepper::OFF_VALUES, motor_info->num_pins); + } + } +} + +/* + * Moves the motor forward or backwards. + */ +void Stepper::stepMotor(uint8_t motor_id) +{ + const MotorInfo* motor_info = this->motor_infos[motor_id]; + uint8_t* motor_pins = motor_info->motor_pins; + + // Only write out the values if the configuration is valid. + if (motor_info->num_pins != 4 && + motor_info->num_pins != 2 && motor_info->drive_type != FullStep) { + return; + } + + const uint8_t* step_row; + switch(motor_info->drive_type) { + case FullStep: + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2]; + if (motor_info->num_pins == 2) { + // For 2-wire configurations we take the middle two values, so we move the pointer up by + // one so that the first two values are really elements 1 and 2, instead of 0 and 1. + step_row++; + } + break; + case HalfStep: + step_row = Stepper::STEP_VALUES[motor_info->step_number % 8]; + break; + case Wave: + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2 + 1]; + break; + default: + // If the this is an unknown drive type do nothing. + return; + } + + writeMotorPins(motor_pins, step_row, motor_info->num_pins); +} + +void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins) +{ + for (uint8_t i = 0; i < num_pins; i++) { + digitalWrite(motor_pins[i], values[i]); + } +} + +void Stepper::halt() +{ + this->should_halt = true; +} + +void Stepper::setCallback(void (*callback)()) +{ + this->callback = callback; +} + +uint8_t Stepper::numMotors() +{ + return this->motor_infos.size(); +} + +/* + version() returns the version of the library: +*/ +uint8_t Stepper::version(void) +{ + return 5; +} diff --git a/libraries/Stepper3/Stepper3.h b/libraries/Stepper3/Stepper3.h new file mode 100644 index 00000000000..b0c0cf3ed01 --- /dev/null +++ b/libraries/Stepper3/Stepper3.h @@ -0,0 +1,187 @@ +/* + Stepper.h - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. + + When wiring multiple stepper motors to a microcontroller, + you quickly run out of output pins, with each motor requiring 4 connections. + + By making use of the fact that at any time two of the four motor + coils are the inverse of the other two, the number of + control connections can be reduced from 4 to 2. + + A slightly modified circuit around a Darlington transistor array or an L293 H-bridge + connects to only 2 microcontroler pins, inverts the signals received, + and delivers the 4 (2 plus 2 inverted ones) output signals required + for driving a stepper motor. + + The sequence of control signals for 4 control wires is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 1 1 0 + 3 0 1 0 1 + 4 1 0 0 1 + + The sequence of controls signals for 2 control wires is as follows + (columns C1 and C2 from above): + + Step C0 C1 + 1 0 1 + 2 1 1 + 3 1 0 + 4 0 0 + + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms +*/ + +// ensure this library description is only included once +#ifndef Stepper_h +#define Stepper_h + +#include +#include "utility/vector.h" + +// library interface description +class Stepper { + public: + enum DriveType { FullStep, HalfStep, Wave }; + + // constructors: + // Empty constructor + Stepper(); + // The two constructors below will also add a motor as motor_id 0. + // steps_per_rev should be in full steps + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // destructor + ~Stepper(); + + // Registers an extra motor. The return value is the motor's id, and should be used to + // reference it in the future. + // steps_per_rev should be in full steps + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // speed setter methods: + // Set the speed for all motors. in RPM + void setSpeed(int speed); + // Set the speed for a specific motor, in RPM + void setSpeed(uint8_t motor_id, int speed); + + // drive type setter methods: + // Set the drive type for all motors. + void setDriveType(DriveType drive_type); + // Set the drive type for a specific motor + void setDriveType(uint8_t motor_id, DriveType drive_type); + + // mover methods: + // Move all motors the number_of_steps + // Note that steps_to_move is an integer, with a max value of 2^15-1 + void step(int steps_to_move); + // Move only the motor with motor_id number_of_steps + void step(uint8_t motor_id, int steps_to_move); + // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds + // to the number of steps the motor with motor_id being the index of that element should be + // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors + // to move, set those values to 0. + void step(int steps_to_move[]); + + // Aborts the step() loop once. + void halt(); + + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); + void setHold(uint8_t motor_id, bool hold); + + // Sets a callback to call after each iteration of the step() loop. Set to NULL to disable. + // Remember that the callback should execute quickly and not block. The longer the callback + // takes the slower steps can be taken. + void setCallback(void (*callback)()); + + // Returns the number of motors registered. + uint8_t numMotors(void); + + uint8_t version(void); + + private: + struct MotorInfo { + uint8_t* motor_pins; // motor pin numbers + uint8_t num_pins; + + Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. + unsigned long step_delay; // delay between steps, in micros, based on speed + int steps_per_rev; // total number of steps this motor can take + + unsigned int step_number; // which step the motor is on + unsigned long last_step_time; // time stamp in micros of when the last step was taken + + bool should_hold; // whether to hold the motor position when not stepping. + }; + + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); + void setupMotorPins(uint8_t motor_pins[], uint8_t num_pins); + + // moves the motor specified one step. + void stepMotor(uint8_t motor_id); + + // writes the given values to the motor pins. + void writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins); + + // Only even rows are used for full-stepping. + // Only odd rows are used for wave-stepping. + // All rows are used for half-stepping. + static const uint8_t STEP_VALUES[8][4]; + + // Pin values that turn off current to a motor so that it does not hold its position. + // This is only possible for 4-pin configurations + static const uint8_t OFF_VALUES[4]; + + Vector motor_infos; + bool should_halt; + void (*callback)(); +}; + +#endif diff --git a/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino b/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino new file mode 100644 index 00000000000..d428186342d --- /dev/null +++ b/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino @@ -0,0 +1,41 @@ +/* + * MotorKnob + * + * A stepper motor follows the turns of a potentiometer + * (or other sensor) on analog input 0. + * + * http://www.arduino.cc/en/Reference/Stepper + * This example code is in the public domain. + */ + +#include + +// change this to the number of steps on your motor +#define STEPS 100 + +// create an instance of the stepper class, specifying +// the number of steps of the motor and the pins it's +// attached to +Stepper stepper(STEPS, 8, 9, 10, 11); + +// the previous reading from the analog input +int previous = 0; + +void setup() +{ + // set the speed of the motor to 30 RPMs + stepper.setSpeed(30); +} + +void loop() +{ + // get the sensor value + int val = analogRead(0); + + // move a number of steps equal to the change in the + // sensor reading + stepper.step(val - previous); + + // remember the previous value of the sensor + previous = val; +} \ No newline at end of file diff --git a/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino b/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino new file mode 100644 index 00000000000..2dbb57d7af3 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino @@ -0,0 +1,44 @@ + +/* + Stepper Motor Control - one revolution + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + + The motor should revolve one revolution in one direction, then + one revolution in the other direction. + + + Created 11 Mar. 2007 + Modified 30 Nov. 2009 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution + // for your motor + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +void setup() { + // set the speed at 60 rpm: + myStepper.setSpeed(60); + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // step one revolution in one direction: + Serial.println("clockwise"); + myStepper.step(stepsPerRevolution); + delay(500); + + // step one revolution in the other direction: + Serial.println("counterclockwise"); + myStepper.step(-stepsPerRevolution); + delay(500); +} + diff --git a/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino b/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino new file mode 100644 index 00000000000..36d32991dc9 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino @@ -0,0 +1,44 @@ + +/* + Stepper Motor Control - one step at a time + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + + The motor will step one step at a time, very slowly. You can use this to + test that you've got the four wires of your stepper wired to the correct + pins. If wired correctly, all steps should be in the same direction. + + Use this also to count the number of steps per revolution of your motor, + if you don't know it. Then plug that number into the oneRevolution + example to see if you got it right. + + Created 30 Nov. 2009 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution + // for your motor + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +int stepCount = 0; // number of steps the motor has taken + +void setup() { + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // step one step: + myStepper.step(1); + Serial.print("steps:" ); + Serial.println(stepCount); + stepCount++; + delay(500); +} + diff --git a/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino b/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino new file mode 100644 index 00000000000..dbd0f7f02a8 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino @@ -0,0 +1,49 @@ + +/* + Stepper Motor Control - speed control + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + A potentiometer is connected to analog input 0. + + The motor will rotate in a clockwise direction. The higher the potentiometer value, + the faster the motor speed. Because setSpeed() sets the delay between steps, + you may notice the motor is less responsive to changes in the sensor value at + low speeds. + + Created 30 Nov. 2009 + Modified 28 Oct 2010 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution +// for your motor + + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +int stepCount = 0; // number of steps the motor has taken + +void setup() { + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // read the sensor value: + int sensorReading = analogRead(A0); + // map it to a range from 0 to 100: + int motorSpeed = map(sensorReading, 0, 1023, 0, 100); + // set the motor speed: + if (motorSpeed > 0) { + myStepper.setSpeed(motorSpeed); + // step 1/100 of a revolution: + myStepper.step(stepsPerRevolution/100); + } +} + + diff --git a/libraries/Stepper3/keywords.txt b/libraries/Stepper3/keywords.txt new file mode 100644 index 00000000000..6ffdb733643 --- /dev/null +++ b/libraries/Stepper3/keywords.txt @@ -0,0 +1,34 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Stepper KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +registerMotor KEYWORD2 +halt KEYWORD2 +numMotors KEYWORD2 +setCallback KEYWORD2 +setDriveType KEYWORD2 +setSpeed KEYWORD2 +step KEYWORD2 +version KEYWORD2 + +###################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + +FullStep LITERAL1 +HalfStep LITERAL1 +Wave LITERAL1 \ No newline at end of file diff --git a/libraries/Stepper3/utility/Vector.h b/libraries/Stepper3/utility/Vector.h new file mode 100644 index 00000000000..3b10d1529be --- /dev/null +++ b/libraries/Stepper3/utility/Vector.h @@ -0,0 +1,73 @@ +// Adapted from a post on the Arduino Forums by Gabb: +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289151491 + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +// Minimal class to replace std::vector +template +class Vector { + size_t d_size; // Stores no. of actually stored objects + size_t d_capacity; // Stores allocated capacity + Data *d_data; // Stores data +public: + // Default constructor + Vector() : d_size(0), d_capacity(0), d_data(0) {}; + // Copy constuctor + Vector(Vector const &other) : d_size(other.d_size), d_capacity(other.d_capacity), d_data(0) { + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + }; + + // Destructor + ~Vector() { + free(d_data); + }; + + // Needed for memory management + Vector &operator=(Vector const &other) { + free(d_data); + d_size = other.d_size; + d_capacity = other.d_capacity; + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + return *this; + }; + + // Const getter + Data const &operator[](size_t idx) const { + return d_data[idx]; + }; + // Changeable getter + Data &operator[](size_t idx) { + return d_data[idx]; + }; + + // Adds new value. If needed, allocates more space + void push_back(Data const &x) { + if (d_capacity == d_size) { + resize(); + } + d_data[d_size++] = x; + }; + + // Size getter + size_t size() const { + return d_size; + }; + +private: + // Allocates double the old space + void resize() { + d_capacity = d_capacity ? d_capacity * 2 : 1; + Data *newdata = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(newdata, d_data, d_size * sizeof(Data)); + free(d_data); + d_data = newdata; + }; +}; + +#endif