Fix static/extern linkage conflict for log2 timer-frequency variables - #372
Merged
Merged
Conversation
6 tasks
RampControl.cpp declared log2_timer_freq (and its two derived values) static, while RampGenerator.h declares them extern for use from RampGenerator.cpp, a separate translation unit. A static redeclaration cannot follow a non-static one in the same TU, so this fails to build for any platform whose TICKS_PER_S isn't exactly 16000000 or 21000000 (the two values RampCalculator.h has precomputed fast-path constants for) - e.g. AVR boards not running at 16/21 MHz.
ARDUTECH0
force-pushed
the
fix/log2-timer-freq-linkage
branch
from
September 21, 2026 10:44
f1b19d9 to
845d107
Compare
ARDUTECH0
added a commit
to ARDUTECH0/FastAccelStepper
that referenced
this pull request
Sep 21, 2026
New platform backend for the i.MX RT1062 (Teensy 4.0/4.1), using the chip's 4 QuadTimer (TMR) modules x 4 channels each for up to 16 independently timed steppers. Unlike the SAMD/SAM backends, the step pin is not muxed to the timer's own output pin: the ISR toggles it directly via digitalWriteFast(), so any digital pin works, the same flexibility ESP32 already has. Each step costs two hardware-timed compare-match interrupts (rising edge, falling edge after the pulse width), the same principle this library's AVR backend already uses to time each edge exactly regardless of interrupt latency. The register-level QuadTimer sequence is adapted from luni64/TeensyStep4 (MIT licensed), a stepper library already verified on real Teensy 4.x hardware - not guessed from the reference manual. Includes the same log2 timer-frequency linkage fix as gin66#372, since this backend's 9.375 MHz tick rate doesn't match either of the two existing fast-path TICKS_PER_S constants and needs that generic code path. STATUS: EXPERIMENTAL, NOT YET VERIFIED ON REAL HARDWARE. Written and reviewed without access to a Teensy 4.x board, a compiler for it, or an oscilloscope - see the warning at the top of pd_teensy/pd_config.h. Needs someone with the actual board to build it and check step timing.
ARDUTECH0
added a commit
to ARDUTECH0/FastAccelStepper
that referenced
this pull request
Sep 22, 2026
Verified on real Teensy 4.0 hardware for the first time (motor moves, ramps, and reaches target correctly at 2000 Hz / 4000 steps/s^2). Found and fixed two real bugs along the way: - RampCalculator.h/RampGenerator.h: the log2_timer_freq extern declarations lived in RampGenerator.h, included *after* RampCalculator.h - but RampCalculator.h's own inline methods (ramp_config_s::update() etc.) use log2_timer_freq as soon as that header is parsed, before RampGenerator.h's declarations are ever seen. Moved the declarations into RampCalculator.h itself, in the same branch that defines SUPPORT_LOG2_TIMER_FREQ_VARIABLES. This is what made the Teensy backend fail to compile at all (also affects any other platform hitting this fallback branch, e.g. AVR boards not running at 16/21 MHz) - same root cause as the static/extern issue in gin66#372, a second defect in the same never-before-exercised code path. - teensy_queue.cpp: the module-level ISR loop was missing a `dsb` memory barrier after clearing the QuadTimer CSCTRL.TCF1 flags. The Cortex-M7 write buffer does not guarantee that clear has reached the peripheral by the time the ISR returns, which showed up as the motor's step rate plateauing then hanging entirely as interrupt frequency increased during accel - exactly the failure mode observed on hardware. luni64/TeensyStep4's own module ISR carries the same barrier with the same reasoning; this backend had dropped it when adapting that code. Also, now that real hardware is available to tune against: - FAS_TEENSY_TMR_PRESCALE is now a documented, overridable macro (0..7) instead of a hardcoded prescaler, with TICKS_PER_S derived from it so the two can't drift apart. - Step-timing QuadTimer interrupts are now given a higher NVIC priority than the ~4 ms ramp tick (FAS_TEENSY_STEP_ISR_PRIORITY / FAS_TEENSY_RAMP_TICK_PRIORITY, both overridable), so a slow queue refill under load can't delay an already-scheduled step edge. Adds examples/TeensyBringUp, the bring-up sketch used for this testing: moves one stepper back and forth and prints position/speed/ ramp-state/queue-state/enable-pin diagnostics to Serial, useful for verifying this backend on your own board before relying on it. Enable pin behavior was also checked during this session and works as documented (LOW = enabled by default) - the driver board's own power LED, which some test rigs may be watching, is unrelated to the ENA signal and stays lit whenever the driver has power.
ARDUTECH0
added a commit
to ARDUTECH0/FastAccelStepper
that referenced
this pull request
Sep 22, 2026
…ardware) Found via real Teensy 4.0 bring-up: any move that starts or reverses from a genuine standstill (no setLinearAcceleration() in use) would "hang" for several minutes. Root cause: log2_timer_freq_div_sqrt_of_2 was computed with log2_multiply(log2_timer_freq, log2_timer_freq) - a copy-paste of the very next line's formula for log2_timer_freq_square_div_2 - squaring the tick frequency instead of dividing it by sqrt(2). calculate_ticks()'s near-standstill branch then produced multi-billion-tick results instead of a normal value. setLinearAcceleration() masks this entirely, since it routes small step counts through a different (cubic) branch that never touches this constant - which is exactly why it took a real motor, run without that option, to expose it. Shared root cause and now shared fix with gin66#372. Adds examples/TeensyMoveAndPause, the sketch that exposed this bug (move, sit genuinely idle for a few seconds so the driver actually disables, move again - unlike TeensyBringUp's back-to-back moves, which never let the queue fully drain and therefore never hit this path).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two real, related defects in the
SUPPORT_LOG2_TIMER_FREQ_VARIABLESfallback path (RampCalculator.h's#elsebranch, used whenever a platform'sTICKS_PER_Sisn't exactly 16000000 or 21000000). Every shippedpd_config.hcurrently uses one of those two values, so this path has never actually been exercised until a Teensy 4.x backend (in a sibling branch, PR #374) needed it for its 9.375 MHz tick rate - both bugs were found there and are fixed here too, standalone.RampControl.cppdeclareslog2_timer_freq(and its two derived values)static, whileRampGenerator.hdeclares the same namesexternfor use fromRampGenerator.cpp, a different translation unit. Astaticredeclaration cannot follow a non-static/externone in the same TU - ill-formed C++, fails to build the moment this path is active.externdeclarations lived inRampGenerator.h, included afterRampCalculator.h- butRampCalculator.h's own inline methods use them as soon as that header is parsed, beforeRampGenerator.h's declarations are ever seen. Moved intoRampCalculator.hitself.log2_timer_freq_div_sqrt_of_2was computed withlog2_multiply(log2_timer_freq, log2_timer_freq)- the same formula aslog2_timer_freq_square_div_2one line below, squaring the frequency instead of dividing it bysqrt(2). This produced absurdly large (multi-billion-tick) results fromcalculate_ticks()/calculate_ramp_steps()'s non-cubic branch, observed on hardware as the stepper "hanging" for minutes on every ramp start/reversal from standstill.setLinearAcceleration()masks it completely (steps belows_hroute through the separate, unaffected cubic branch), which is why a real motor moving under test still exposed this only intermittently depending on configuration.None of this changes behavior for any currently shipped platform (all use
TICKS_PER_Sof 16 MHz or 21 MHz, soSUPPORT_LOG2_TIMER_FREQ_VARIABLESis never defined for them today) - but it would affect, for example, an AVR board not running at 16/21 MHz (TICKS_PER_Sthere isF_CPU), and is required for the Teensy 4.x work in #374.Test plan