From 832b3a22b13f61a8b7a48b513f844c72caf80fac Mon Sep 17 00:00:00 2001 From: "claude-opus-4-8 (Claude Code)" Date: Mon, 6 Jul 2026 21:28:39 -0700 Subject: [PATCH 1/2] Make step pulse timing per-driver instance members; add setMinStepPulse (fixes #136) The per-driver step_high_min/step_low_min/wakeup_time were declared as `static const int` in each derived driver, shadowing the base class members. Because BasicStepperDriver::nextAction() and enable() reference these with static binding to the base class, every driver actually used the base values (step_high_min=1, wakeup_time hardcoded 2us). The per-driver datasheet values were dead code, and step_low_min was never enforced (floor hardcoded to 1us). Changes: - BasicStepperDriver: step_high_min/step_low_min/wakeup_time are now protected `short` instance members (defaults 1/1/0). Derived drivers assign their datasheet values in every constructor (A4988 1/1/1000, DRV8825 2/2/1700, DRV8834 2/2/1000, DRV8880 1/1/1500, TMC2100 1/1/1000). DRV8880 uses 1 not 0 so a zero delay can't produce sub-datasheet pulses on fast ARM boards. - New public API: setMinStepPulse(high_us, low_us) plus getMinStepPulseHigh() and getMinStepPulseLow() inline getters. This is what fast MCUs like the Arduino Opta need (setMinStepPulse(8, 8)) since their GPIO outpaces the 1us default; fixes #136. - nextAction() now floors the STEP LOW interval at step_low_min instead of 1us, computed in unsigned form to avoid adding branches to the hot path. - enable() now waits wakeup_time (datasheet tWAKE), floored to 2us for the base default of 0. DRV8825 now correctly waits 1.7ms after wake before stepping. RAM impact: +6 bytes per driver instance (three shorts), acceptable. Verified: builds clean for arduino:avr:uno, sim-test verdicts match baseline. Agent: claude-opus-4-8 (Claude Code 2.1.153), amended and verified by claude-fable-5; max context and thinking level not exposed to the agent Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 11 ++++++++--- keywords.txt | 3 +++ src/A4988.cpp | 11 +++++++---- src/A4988.h | 8 ++------ src/BasicStepperDriver.cpp | 8 ++++++-- src/BasicStepperDriver.h | 24 +++++++++++++++++++++--- src/DRV8825.cpp | 11 +++++++---- src/DRV8825.h | 8 ++------ src/DRV8834.cpp | 11 +++++++---- src/DRV8834.h | 8 ++------ src/DRV8880.cpp | 16 ++++++++++------ src/DRV8880.h | 9 +++------ src/TMC2100.cpp | 11 +++++++---- src/TMC2100.h | 8 ++------ 14 files changed, 87 insertions(+), 60 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index aa0f768..9679ec6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,8 +170,12 @@ All CI must pass before merging PRs. - Must respect driver parameters such as microstep configuration, signal timing, etc. as documented in the driver chip datasheet ### Timing Constraints -- `step_high_min` / `step_low_min`: Minimum pulse durations (μs) - driver-specific -- `wakeup_time`: Time after enabling before movement (μs) +- `step_high_min` / `step_low_min`: Minimum pulse durations (μs) - driver-specific. + These are instance members set in each driver's constructors; the base default is + 1μs. Users can override at runtime with `setMinStepPulse(high_us, low_us)` (needed + on very fast MCUs like the Arduino Opta whose GPIO outpaces the driver). +- `wakeup_time`: Time after enabling before movement (μs), instance member set in the + driver constructors (base default 0, floored to 2μs in `enable()`). - High RPM + high microstepping = limited by MCU speed ### Movement Modes @@ -222,7 +226,8 @@ Before modifying timing-critical code paths: 3. Inherit from appropriate base class (typically `A4988` or `DRV8825`) 4. Override `getMaxMicrostep()` if different from parent 5. Override `setMicrostep()` if pin configuration differs -6. Update timing constants if needed (`step_high_min`, `wakeup_time`) +6. Update timing values if needed by assigning `step_high_min`, `step_low_min`, and + `wakeup_time` (instance members) in the driver's constructors 7. Add to `keywords.txt` for Arduino IDE highlighting 8. Create an example sketch in `examples/` diff --git a/keywords.txt b/keywords.txt index c677e3e..f5be7f9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -16,6 +16,9 @@ move KEYWORD2 rotate KEYWORD2 setRPM KEYWORD2 getRPM KEYWORD2 +setMinStepPulse KEYWORD2 +getMinStepPulseHigh KEYWORD2 +getMinStepPulseLow KEYWORD2 setCurrent KEYWORD2 enable KEYWORD2 disable KEYWORD2 diff --git a/src/A4988.cpp b/src/A4988.cpp index 088ff6e..da67731 100644 --- a/src/A4988.cpp +++ b/src/A4988.cpp @@ -19,13 +19,16 @@ const uint8_t A4988::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b111}; * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ +// A4988 datasheet timing: STEP HIGH/LOW min 1us, wakeup 1000us +#define A4988_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1000; } while (0) + A4988::A4988(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{} +{ A4988_SET_TIMING(); } A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{} +{ A4988_SET_TIMING(); } /* * Fully wired. @@ -34,12 +37,12 @@ A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin) A4988::A4988(short steps, short dir_pin, short step_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) -{} +{ A4988_SET_TIMING(); } A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) -{} +{ A4988_SET_TIMING(); } void A4988::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/A4988.h b/src/A4988.h index cadf2e8..5ff2a96 100644 --- a/src/A4988.h +++ b/src/A4988.h @@ -18,12 +18,8 @@ class A4988 : public BasicStepperDriver { short ms1_pin = PIN_UNCONNECTED; short ms2_pin = PIN_UNCONNECTED; short ms3_pin = PIN_UNCONNECTED; - // tA STEP minimum, HIGH pulse width (1us) - static const int step_high_min = 1; - // tB STEP minimum, LOW pulse width (1us) - static const int step_low_min = 1; - // wakeup time, nSLEEP inactive to STEP (1000us) - static const int wakeup_time = 1000; + // Timing (set in constructors): tA STEP HIGH min 1us, tB STEP LOW min 1us, + // wakeup time nSLEEP inactive to STEP 1000us. // also 200ns between ENBL/DIR/MSx changes and STEP HIGH // Get the microstep table diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index e03a2b3..552d164 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -342,7 +342,9 @@ long BasicStepperDriver::nextAction(void){ // account for calcStepPulse() execution time; sets ceiling for max rpm on slower MCUs last_action_end = micros(); m = last_action_end - m; - next_action_interval = (pulse > m) ? pulse - m : 1; + // floor the STEP LOW interval at step_low_min (datasheet tWL) instead of 1us + unsigned low_min = (unsigned)step_low_min; + next_action_interval = (pulse > m + low_min) ? pulse - m : low_min; } else { // end of move last_action_end = 0; @@ -380,7 +382,9 @@ void BasicStepperDriver::enable(void){ if IS_CONNECTED(enable_pin){ digitalWrite(enable_pin, enable_active_state); }; - delayMicros(2); + // wait the driver's wakeup time (datasheet tWAKE) before stepping, + // but at least 2us as the base default wakeup_time is 0 + delayMicros(stepperMax((short)2, wakeup_time)); } void BasicStepperDriver::disable(void){ diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 033ddb0..d7d5ac8 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -74,11 +74,13 @@ class BasicStepperDriver { // current microstep level (1,2,4,8,...), must be < getMaxMicrostep() short microsteps = 1; // tWH(STEP) pulse duration, STEP high, min value (us) - static const int step_high_min = 1; + // Instance members (not static const) so derived drivers can set datasheet + // values in their constructors and users can override via setMinStepPulse(). + short step_high_min = 1; // tWL(STEP) pulse duration, STEP low, min value (us) - static const int step_low_min = 1; + short step_low_min = 1; // tWAKE wakeup time, nSLEEP inactive to STEP (us) - static const int wakeup_time = 0; + short wakeup_time = 0; float rpm = 0; @@ -137,6 +139,22 @@ class BasicStepperDriver { float getCurrentRPM(void){ return (60.0*1000000L / step_pulse / microsteps / motor_steps); } + /* + * Set minimum STEP pulse HIGH and LOW durations (microseconds). + * Useful on fast MCUs (e.g. Arduino Opta) whose GPIO toggles faster than + * the driver can register the default 1us pulses. See issue #136. + */ + void setMinStepPulse(short high_us, short low_us){ + step_high_min = (high_us > 0) ? high_us : 0; + // LOW interval must stay >= 1: nextAction() returning 0 means move complete + step_low_min = (low_us > 1) ? low_us : 1; + } + short getMinStepPulseHigh(void){ + return step_high_min; + } + short getMinStepPulseLow(void){ + return step_low_min; + } /* * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) * accel and decel are given in [full steps/s^2] diff --git a/src/DRV8825.cpp b/src/DRV8825.cpp index d0800b7..d7f8024 100644 --- a/src/DRV8825.cpp +++ b/src/DRV8825.cpp @@ -15,24 +15,27 @@ */ const uint8_t DRV8825::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b100, 0b111}; +// DRV8825 datasheet timing: STEP HIGH/LOW min 1.9us -> 2, wakeup 1700us +#define DRV8825_SET_TIMING() do { step_high_min = 2; step_low_min = 2; wakeup_time = 1700; } while (0) + DRV8825::DRV8825(short steps, short dir_pin, short step_pin) :A4988(steps, dir_pin, step_pin) -{} +{ DRV8825_SET_TIMING(); } DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin) :A4988(steps, dir_pin, step_pin, enable_pin) -{} +{ DRV8825_SET_TIMING(); } /* * A4988-DRV8825 Compatibility map: MS1-MODE0, MS2-MODE1, MS3-MODE2 */ DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, mode0_pin, mode1_pin, mode2_pin) -{} +{ DRV8825_SET_TIMING(); } DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, enable_pin, mode0_pin, mode1_pin, mode2_pin) -{} +{ DRV8825_SET_TIMING(); } const uint8_t* DRV8825::getMicrostepTable() { diff --git a/src/DRV8825.h b/src/DRV8825.h index 68afe8f..8c0f474 100644 --- a/src/DRV8825.h +++ b/src/DRV8825.h @@ -15,12 +15,8 @@ class DRV8825 : public A4988 { protected: static const uint8_t MS_TABLE[]; - // tWH(STEP) pulse duration, STEP high, min value (1.9us) - static const int step_high_min = 2; - // tWL(STEP) pulse duration, STEP low, min value (1.9us) - static const int step_low_min = 2; - // tWAKE wakeup time, nSLEEP inactive to STEP (1000us) - static const int wakeup_time = 1700; + // Timing (set in constructors): tWH(STEP) HIGH min 1.9us -> 2, + // tWL(STEP) LOW min 1.9us -> 2, tWAKE wakeup time 1700us. // also 650ns between ENBL/DIR/MODEx changes and STEP HIGH // Get the microstep table diff --git a/src/DRV8834.cpp b/src/DRV8834.cpp index 6a048c3..929293d 100644 --- a/src/DRV8834.cpp +++ b/src/DRV8834.cpp @@ -13,24 +13,27 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ +// DRV8834 datasheet timing: STEP HIGH/LOW min 1.9us -> 2, wakeup 1000us +#define DRV8834_SET_TIMING() do { step_high_min = 2; step_low_min = 2; wakeup_time = 1000; } while (0) + DRV8834::DRV8834(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{} +{ DRV8834_SET_TIMING(); } DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{} +{ DRV8834_SET_TIMING(); } /* * Fully wired. All the necessary control pins for DRV8834 are connected. */ DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin), m0_pin(m0_pin), m1_pin(m1_pin) -{} +{ DRV8834_SET_TIMING(); } DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0_pin(m0_pin), m1_pin(m1_pin) -{} +{ DRV8834_SET_TIMING(); } /* * Set microstepping mode (1:divisor) diff --git a/src/DRV8834.h b/src/DRV8834.h index 7aeeec6..2241164 100644 --- a/src/DRV8834.h +++ b/src/DRV8834.h @@ -16,12 +16,8 @@ class DRV8834 : public BasicStepperDriver { protected: short m0_pin = PIN_UNCONNECTED; short m1_pin = PIN_UNCONNECTED; - // tWH(STEP) pulse duration, STEP high, min value (1.9us) - static const int step_high_min = 2; - // tWL(STEP) pulse duration, STEP low, min value (1.9us) - static const int step_low_min = 2; - // tWAKE wakeup time, nSLEEP inactive to STEP (1000us) - static const int wakeup_time = 1000; + // Timing (set in constructors): tWH(STEP) HIGH min 1.9us -> 2, + // tWL(STEP) LOW min 1.9us -> 2, tWAKE wakeup time 1000us. // also 200ns between ENBL/DIR/Mx changes and STEP HIGH // Get max microsteps supported by the device diff --git a/src/DRV8880.cpp b/src/DRV8880.cpp index 5bae6a8..c14d4b9 100644 --- a/src/DRV8880.cpp +++ b/src/DRV8880.cpp @@ -12,32 +12,36 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ +// DRV8880 datasheet timing: STEP HIGH/LOW min 0.47us -> 1 (rounded up; 0 could +// produce sub-datasheet pulses on fast ARM boards), wakeup 1500us +#define DRV8880_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1500; } while (0) + DRV8880::DRV8880(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{} +{ DRV8880_SET_TIMING(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{} +{ DRV8880_SET_TIMING(); } /* * Fully wired. All the necessary control pins for DRV8880 are connected. */ DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1) :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1) -{} +{ DRV8880_SET_TIMING(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1) -{} +{ DRV8880_SET_TIMING(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1, short trq0, short trq1) :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1) -{} +{ DRV8880_SET_TIMING(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1, short trq0, short trq1) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1) -{} +{ DRV8880_SET_TIMING(); } void DRV8880::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/DRV8880.h b/src/DRV8880.h index 8bc4e17..af4d424 100644 --- a/src/DRV8880.h +++ b/src/DRV8880.h @@ -17,12 +17,9 @@ class DRV8880 : public BasicStepperDriver { short m1 = PIN_UNCONNECTED; short trq0 = PIN_UNCONNECTED; short trq1 = PIN_UNCONNECTED; - // tWH(STEP) pulse duration, STEP high, min value - static const int step_high_min = 0; // 0.47us - // tWL(STEP) pulse duration, STEP low, min value - static const int step_low_min = 0; // 0.47us - // tWAKE wakeup time, nSLEEP inactive to STEP - static const int wakeup_time = 1500; + // Timing (set in constructors): tWH(STEP) HIGH min 0.47us -> 1, + // tWL(STEP) LOW min 0.47us -> 1 (rounded up; a 0 delay could produce + // sub-datasheet pulses on fast ARM boards), tWAKE wakeup time 1500us. // also 200ns between ENBL/DIR/Mx changes and STEP HIGH // Get max microsteps supported by the device diff --git a/src/TMC2100.cpp b/src/TMC2100.cpp index f8bd10d..2645fd9 100644 --- a/src/TMC2100.cpp +++ b/src/TMC2100.cpp @@ -10,13 +10,16 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ +// TMC2100 datasheet timing: STEP HIGH/LOW min 1us, wakeup 1000us +#define TMC2100_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1000; } while (0) + TMC2100::TMC2100(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{} +{ TMC2100_SET_TIMING(); } TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{} +{ TMC2100_SET_TIMING(); } /* * Fully wired. @@ -25,12 +28,12 @@ TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin) TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short cf1_pin, short cf2_pin) :BasicStepperDriver(steps, dir_pin, step_pin), cf1_pin(cf1_pin), cf2_pin(cf2_pin) -{} +{ TMC2100_SET_TIMING(); } TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin, short cf1_pin, short cf2_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), cf1_pin(cf1_pin), cf2_pin(cf2_pin) -{} +{ TMC2100_SET_TIMING(); } void TMC2100::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/TMC2100.h b/src/TMC2100.h index 27ed754..2cd3065 100644 --- a/src/TMC2100.h +++ b/src/TMC2100.h @@ -13,12 +13,8 @@ class TMC2100 : public BasicStepperDriver { protected: short cf1_pin = PIN_UNCONNECTED; short cf2_pin = PIN_UNCONNECTED; - // tA STEP minimum, HIGH pulse width (1us) - static const int step_high_min = 1; - // tB STEP minimum, LOW pulse width (1us) - static const int step_low_min = 1; - // wakeup time, nSLEEP inactive to STEP (1000us) - static const int wakeup_time = 1000; + // Timing (set in constructors): tA STEP HIGH min 1us, tB STEP LOW min 1us, + // wakeup time nSLEEP inactive to STEP 1000us. // Get max microsteps supported by the device short getMaxMicrostep() override; From 20053589f377773f0ec4dd9aacab96f0e81122ee Mon Sep 17 00:00:00 2001 From: "claude-fable-5 (Claude Code)" Date: Fri, 17 Jul 2026 18:38:30 -0700 Subject: [PATCH 2/2] Move driver timing values into protected initTiming() in headers Replace the per-driver X_SET_TIMING() macros defined in the .cpp files with a protected inline initTiming() method in each driver header, at the spot where the original static const timing values lived. This keeps the datasheet values and their reference comments in one place instead of duplicating them between the .cpp macro and a header comment, avoids function-like macros leaking into every sketch that includes the headers, and is scoped/type-checked. Constructors call initTiming(); name hiding across the A4988 -> DRV8825 chain preserves the same init order and final values as the macros. Agent: claude-fable-5 (Claude Code 2.1.212) Co-Authored-By: Claude Fable 5 --- AGENTS.md | 17 ++++++++++------- src/A4988.cpp | 11 ++++------- src/A4988.h | 11 +++++++++-- src/DRV8825.cpp | 11 ++++------- src/DRV8825.h | 11 +++++++++-- src/DRV8834.cpp | 11 ++++------- src/DRV8834.h | 11 +++++++++-- src/DRV8880.cpp | 16 ++++++---------- src/DRV8880.h | 16 +++++++++++++--- src/TMC2100.cpp | 11 ++++------- src/TMC2100.h | 11 +++++++++-- 11 files changed, 81 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9679ec6..9a5c7bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -171,11 +171,13 @@ All CI must pass before merging PRs. ### Timing Constraints - `step_high_min` / `step_low_min`: Minimum pulse durations (μs) - driver-specific. - These are instance members set in each driver's constructors; the base default is - 1μs. Users can override at runtime with `setMinStepPulse(high_us, low_us)` (needed - on very fast MCUs like the Arduino Opta whose GPIO outpaces the driver). -- `wakeup_time`: Time after enabling before movement (μs), instance member set in the - driver constructors (base default 0, floored to 2μs in `enable()`). + These are instance members; each driver defines a protected inline `initTiming()` + method in its header with the datasheet values and calls it from every constructor. + The base default is 1μs. Users can override at runtime with + `setMinStepPulse(high_us, low_us)` (needed on very fast MCUs like the Arduino Opta + whose GPIO outpaces the driver). +- `wakeup_time`: Time after enabling before movement (μs), instance member set by the + driver's `initTiming()` (base default 0, floored to 2μs in `enable()`). - High RPM + high microstepping = limited by MCU speed ### Movement Modes @@ -226,8 +228,9 @@ Before modifying timing-critical code paths: 3. Inherit from appropriate base class (typically `A4988` or `DRV8825`) 4. Override `getMaxMicrostep()` if different from parent 5. Override `setMicrostep()` if pin configuration differs -6. Update timing values if needed by assigning `step_high_min`, `step_low_min`, and - `wakeup_time` (instance members) in the driver's constructors +6. Update timing values if needed by defining a protected inline `initTiming()` method + in the driver's header that assigns `step_high_min`, `step_low_min`, and + `wakeup_time` (instance members), and calling it from every constructor 7. Add to `keywords.txt` for Arduino IDE highlighting 8. Create an example sketch in `examples/` diff --git a/src/A4988.cpp b/src/A4988.cpp index da67731..2e90f48 100644 --- a/src/A4988.cpp +++ b/src/A4988.cpp @@ -19,16 +19,13 @@ const uint8_t A4988::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b111}; * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -// A4988 datasheet timing: STEP HIGH/LOW min 1us, wakeup 1000us -#define A4988_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1000; } while (0) - A4988::A4988(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{ A4988_SET_TIMING(); } +{ initTiming(); } A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{ A4988_SET_TIMING(); } +{ initTiming(); } /* * Fully wired. @@ -37,12 +34,12 @@ A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin) A4988::A4988(short steps, short dir_pin, short step_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) -{ A4988_SET_TIMING(); } +{ initTiming(); } A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) -{ A4988_SET_TIMING(); } +{ initTiming(); } void A4988::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/A4988.h b/src/A4988.h index 5ff2a96..cea23e5 100644 --- a/src/A4988.h +++ b/src/A4988.h @@ -18,8 +18,15 @@ class A4988 : public BasicStepperDriver { short ms1_pin = PIN_UNCONNECTED; short ms2_pin = PIN_UNCONNECTED; short ms3_pin = PIN_UNCONNECTED; - // Timing (set in constructors): tA STEP HIGH min 1us, tB STEP LOW min 1us, - // wakeup time nSLEEP inactive to STEP 1000us. + // Set timing requirements from A4988 datasheet + void initTiming(){ + // tA STEP HIGH pulse duration, min value (1us) + step_high_min = 1; + // tB STEP LOW pulse duration, min value (1us) + step_low_min = 1; + // tWAKE wakeup time, nSLEEP inactive to STEP (1000us) + wakeup_time = 1000; + } // also 200ns between ENBL/DIR/MSx changes and STEP HIGH // Get the microstep table diff --git a/src/DRV8825.cpp b/src/DRV8825.cpp index d7f8024..1009f89 100644 --- a/src/DRV8825.cpp +++ b/src/DRV8825.cpp @@ -15,27 +15,24 @@ */ const uint8_t DRV8825::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b100, 0b111}; -// DRV8825 datasheet timing: STEP HIGH/LOW min 1.9us -> 2, wakeup 1700us -#define DRV8825_SET_TIMING() do { step_high_min = 2; step_low_min = 2; wakeup_time = 1700; } while (0) - DRV8825::DRV8825(short steps, short dir_pin, short step_pin) :A4988(steps, dir_pin, step_pin) -{ DRV8825_SET_TIMING(); } +{ initTiming(); } DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin) :A4988(steps, dir_pin, step_pin, enable_pin) -{ DRV8825_SET_TIMING(); } +{ initTiming(); } /* * A4988-DRV8825 Compatibility map: MS1-MODE0, MS2-MODE1, MS3-MODE2 */ DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, mode0_pin, mode1_pin, mode2_pin) -{ DRV8825_SET_TIMING(); } +{ initTiming(); } DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, enable_pin, mode0_pin, mode1_pin, mode2_pin) -{ DRV8825_SET_TIMING(); } +{ initTiming(); } const uint8_t* DRV8825::getMicrostepTable() { diff --git a/src/DRV8825.h b/src/DRV8825.h index 8c0f474..5f2f2fa 100644 --- a/src/DRV8825.h +++ b/src/DRV8825.h @@ -15,8 +15,15 @@ class DRV8825 : public A4988 { protected: static const uint8_t MS_TABLE[]; - // Timing (set in constructors): tWH(STEP) HIGH min 1.9us -> 2, - // tWL(STEP) LOW min 1.9us -> 2, tWAKE wakeup time 1700us. + // Set timing requirements from DRV8825 datasheet + void initTiming(){ + // tWH(STEP) pulse duration, STEP high, min value (1.9us) + step_high_min = 2; + // tWL(STEP) pulse duration, STEP low, min value (1.9us) + step_low_min = 2; + // tWAKE wakeup time, nSLEEP inactive to STEP (1700us) + wakeup_time = 1700; + } // also 650ns between ENBL/DIR/MODEx changes and STEP HIGH // Get the microstep table diff --git a/src/DRV8834.cpp b/src/DRV8834.cpp index 929293d..e899893 100644 --- a/src/DRV8834.cpp +++ b/src/DRV8834.cpp @@ -13,27 +13,24 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -// DRV8834 datasheet timing: STEP HIGH/LOW min 1.9us -> 2, wakeup 1000us -#define DRV8834_SET_TIMING() do { step_high_min = 2; step_low_min = 2; wakeup_time = 1000; } while (0) - DRV8834::DRV8834(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{ DRV8834_SET_TIMING(); } +{ initTiming(); } DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{ DRV8834_SET_TIMING(); } +{ initTiming(); } /* * Fully wired. All the necessary control pins for DRV8834 are connected. */ DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin), m0_pin(m0_pin), m1_pin(m1_pin) -{ DRV8834_SET_TIMING(); } +{ initTiming(); } DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0_pin(m0_pin), m1_pin(m1_pin) -{ DRV8834_SET_TIMING(); } +{ initTiming(); } /* * Set microstepping mode (1:divisor) diff --git a/src/DRV8834.h b/src/DRV8834.h index 2241164..4939151 100644 --- a/src/DRV8834.h +++ b/src/DRV8834.h @@ -16,8 +16,15 @@ class DRV8834 : public BasicStepperDriver { protected: short m0_pin = PIN_UNCONNECTED; short m1_pin = PIN_UNCONNECTED; - // Timing (set in constructors): tWH(STEP) HIGH min 1.9us -> 2, - // tWL(STEP) LOW min 1.9us -> 2, tWAKE wakeup time 1000us. + // Set timing requirements from DRV8834 datasheet + void initTiming(){ + // tWH(STEP) pulse duration, STEP high, min value (1.9us) + step_high_min = 2; + // tWL(STEP) pulse duration, STEP low, min value (1.9us) + step_low_min = 2; + // tWAKE wakeup time, nSLEEP inactive to STEP (1000us) + wakeup_time = 1000; + } // also 200ns between ENBL/DIR/Mx changes and STEP HIGH // Get max microsteps supported by the device diff --git a/src/DRV8880.cpp b/src/DRV8880.cpp index c14d4b9..ba2207b 100644 --- a/src/DRV8880.cpp +++ b/src/DRV8880.cpp @@ -12,36 +12,32 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -// DRV8880 datasheet timing: STEP HIGH/LOW min 0.47us -> 1 (rounded up; 0 could -// produce sub-datasheet pulses on fast ARM boards), wakeup 1500us -#define DRV8880_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1500; } while (0) - DRV8880::DRV8880(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } /* * Fully wired. All the necessary control pins for DRV8880 are connected. */ DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1) :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1, short trq0, short trq1) :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1, short trq0, short trq1) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1) -{ DRV8880_SET_TIMING(); } +{ initTiming(); } void DRV8880::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/DRV8880.h b/src/DRV8880.h index af4d424..29db845 100644 --- a/src/DRV8880.h +++ b/src/DRV8880.h @@ -17,9 +17,19 @@ class DRV8880 : public BasicStepperDriver { short m1 = PIN_UNCONNECTED; short trq0 = PIN_UNCONNECTED; short trq1 = PIN_UNCONNECTED; - // Timing (set in constructors): tWH(STEP) HIGH min 0.47us -> 1, - // tWL(STEP) LOW min 0.47us -> 1 (rounded up; a 0 delay could produce - // sub-datasheet pulses on fast ARM boards), tWAKE wakeup time 1500us. + // Set timing requirements from DRV8880 datasheet + void initTiming(){ + // tWH(STEP) pulse duration, STEP high, min value (0.47us -> 1; + // rounded up because a 0 delay could produce sub-datasheet pulses + // on fast ARM boards) + step_high_min = 1; + // tWL(STEP) pulse duration, STEP low, min value (0.47us -> 1; + // rounded up because a 0 delay could produce sub-datasheet pulses + // on fast ARM boards) + step_low_min = 1; + // tWAKE wakeup time, nSLEEP inactive to STEP (1500us) + wakeup_time = 1500; + } // also 200ns between ENBL/DIR/Mx changes and STEP HIGH // Get max microsteps supported by the device diff --git a/src/TMC2100.cpp b/src/TMC2100.cpp index 2645fd9..132b971 100644 --- a/src/TMC2100.cpp +++ b/src/TMC2100.cpp @@ -10,16 +10,13 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -// TMC2100 datasheet timing: STEP HIGH/LOW min 1us, wakeup 1000us -#define TMC2100_SET_TIMING() do { step_high_min = 1; step_low_min = 1; wakeup_time = 1000; } while (0) - TMC2100::TMC2100(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) -{ TMC2100_SET_TIMING(); } +{ initTiming(); } TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) -{ TMC2100_SET_TIMING(); } +{ initTiming(); } /* * Fully wired. @@ -28,12 +25,12 @@ TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin) TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short cf1_pin, short cf2_pin) :BasicStepperDriver(steps, dir_pin, step_pin), cf1_pin(cf1_pin), cf2_pin(cf2_pin) -{ TMC2100_SET_TIMING(); } +{ initTiming(); } TMC2100::TMC2100(short steps, short dir_pin, short step_pin, short enable_pin, short cf1_pin, short cf2_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), cf1_pin(cf1_pin), cf2_pin(cf2_pin) -{ TMC2100_SET_TIMING(); } +{ initTiming(); } void TMC2100::begin(float rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); diff --git a/src/TMC2100.h b/src/TMC2100.h index 2cd3065..3d0d961 100644 --- a/src/TMC2100.h +++ b/src/TMC2100.h @@ -13,8 +13,15 @@ class TMC2100 : public BasicStepperDriver { protected: short cf1_pin = PIN_UNCONNECTED; short cf2_pin = PIN_UNCONNECTED; - // Timing (set in constructors): tA STEP HIGH min 1us, tB STEP LOW min 1us, - // wakeup time nSLEEP inactive to STEP 1000us. + // Set timing requirements from TMC2100 datasheet + void initTiming(){ + // tA STEP HIGH pulse duration, min value (1us) + step_high_min = 1; + // tB STEP LOW pulse duration, min value (1us) + step_low_min = 1; + // tWAKE wakeup time, nSLEEP inactive to STEP (1000us) + wakeup_time = 1000; + } // Get max microsteps supported by the device short getMaxMicrostep() override;