#include "grbl.h"
Go to the source code of this file.
Data Structures | |
struct | st_block_t |
Stores the planner block Bresenham algorithm execution data for the segments in the segment. More... | |
struct | segment_t |
Primary stepper segment ring buffer. Contains small, short line segments for the stepper. More... | |
struct | stepper_t |
Stepper ISR data struct. Contains the running data for the main stepper ISR. More... | |
struct | st_prep_t |
Segment preparation data struct. Contains all the necessary information to compute new segments. More... | |
Macros | |
#define | DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) |
Some useful constants. More... | |
#define | REQ_MM_INCREMENT_SCALAR 1.25 |
#define | RAMP_ACCEL 0 |
#define | RAMP_CRUISE 1 |
#define | RAMP_DECEL 2 |
#define | RAMP_DECEL_OVERRIDE 3 |
#define | PREP_FLAG_RECALCULATE bit(0) |
#define | PREP_FLAG_HOLD_PARTIAL_BLOCK bit(1) |
#define | PREP_FLAG_PARKING bit(2) |
#define | PREP_FLAG_DECEL_OVERRIDE bit(3) |
#define | MAX_AMASS_LEVEL 3 |
Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level. More... | |
#define | AMASS_LEVEL1 (F_CPU/8000) |
Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz) More... | |
#define | AMASS_LEVEL2 (F_CPU/4000) |
Over-drives ISR (x4) More... | |
#define | AMASS_LEVEL3 (F_CPU/2000) |
Over-drives ISR (x8) More... | |
Functions | |
void | st_wake_up () |
BLOCK VELOCITY PROFILE DEFINITION. More... | |
void | st_go_idle () |
Stepper shutdown. More... | |
ISR (TIMER1_COMPA_vect) | |
"The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. More... | |
ISR (TIMER0_OVF_vect) | |
The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step pulse. This should always trigger before the next Timer1 COMPA interrupt and independently finish, if Timer1 is disabled after completing a move. More... | |
void | st_generate_step_dir_invert_masks () |
Generates the step and direction port invert masks used in the Stepper Interrupt Driver. More... | |
void | st_reset () |
Reset and clear stepper subsystem variables. More... | |
void | stepper_init () |
Initialize and start the stepper motor subsystem. More... | |
void | st_update_plan_block_parameters () |
Called by planner_recalculate() when the executing block is updated by the new plan. More... | |
void | st_prep_buffer () |
Prepares step segment buffer. Continuously called from main program. More... | |
float | st_get_realtime_rate () |
Called by realtime status reporting to fetch the current speed being executed. This value. More... | |
struct st_block_t |
Stores the planner block Bresenham algorithm execution data for the segments in the segment.
Data Fields | ||
---|---|---|
uint8_t | direction_bits | |
uint8_t | is_pwm_rate_adjusted | Tracks motions that require constant laser power/rate. |
uint32_t | step_event_count | |
uint32_t | steps[N_AXIS] |
struct segment_t |
Primary stepper segment ring buffer. Contains small, short line segments for the stepper.
struct stepper_t |
Stepper ISR data struct. Contains the running data for the main stepper ISR.
Data Fields | ||
---|---|---|
uint32_t | counter_x | Counter variables for the bresenham line tracer. |
uint32_t | counter_y | |
uint32_t | counter_z | |
uint8_t | dir_outbits | |
st_block_t * | exec_block | Pointer to the block data for the segment being executed. |
uint8_t | exec_block_index | Tracks the current st_block index. Change indicates new block. |
segment_t * | exec_segment | Pointer to the segment being executed. |
uint8_t | execute_step | Flags step execution for each interrupt. |
uint16_t | step_count | Steps remaining in line segment motion. |
uint8_t | step_outbits | The next stepping-bits to be output. |
uint8_t | step_pulse_time | Step pulse reset time after step rise. |
uint32_t | steps[N_AXIS] |
struct st_prep_t |
Segment preparation data struct. Contains all the necessary information to compute new segments.
#define AMASS_LEVEL1 (F_CPU/8000) |
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) |
#define MAX_AMASS_LEVEL 3 |
Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level.
frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
ISR | ( | TIMER1_COMPA_vect | ) |
"The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl.
Grbl employs the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves. Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical round-off errors and only requires fast integer counters, meaning low computational overhead and maximizing the Arduino's capabilities.
However, the downside of the Bresenham algorithm is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but is usually not a physical problem at higher frequencies, although audible.
To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies, AMASS artificially increases the Bresenham resolution without effecting the algorithm's innate exactness. AMASS adapts its resolution levels automatically depending on the step frequency to be executed, meaning that for even lower step frequencies the step smoothing level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham step count for each AMASS level.
For example, for a Level 1 step smoothing, we bit shift the Bresenham step event count, effectively multiplying it by 2, while the axis step counts remain the same, and then double the stepper ISR frequency. In effect, we are allowing the non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is stepping every two ISR ticks, rather than every ISR tick in the traditional sense.
At AMASS Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but in fact, more efficiently utilizes unused CPU cycles overall throughout all configurations.
AMASS retains the Bresenham algorithm exactness by requiring that it always executes a full Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four intermediate steps must be completed such that baseline Bresenham (Level 0) count is always retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed. Although the AMASS Levels are in reality arbitrary, where the baseline Bresenham counts can be multiplied by any integer value, multiplication by powers of two are simply used to ease CPU overhead with bitshift integer operations.
This interrupt is simple and dumb by design. All the computational heavy-lifting, as in determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments, defined as constant velocity over n number of steps, from the step segment buffer and then executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. The bresenham line tracer algorithm controls all stepper outputs simultaneously with these two interrupts.
<
< Flag main program for cycle end
< Nothing to do but exit.
< Decrement step events count
< Apply step port invert mask
ISR | ( | TIMER0_OVF_vect | ) |
The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step pulse. This should always trigger before the next Timer1 COMPA interrupt and independently finish, if Timer1 is disabled after completing a move.
< Disable Timer0 to prevent re-entering this interrupt when it's not needed.
void st_generate_step_dir_invert_masks | ( | ) |
float st_get_realtime_rate | ( | ) |
void st_go_idle | ( | ) |
void st_prep_buffer | ( | ) |
Prepares step segment buffer. Continuously called from main program.
Reloads step segment buffer. Called continuously by realtime execution system.
The segment buffer is an intermediary buffer interface between the execution of steps by the stepper algorithm and the velocity profiles generated by the planner. The stepper algorithm only executes steps within the segment buffer and is filled by the main program when steps are "checked-out" from the first block in the planner buffer. This keeps the step execution and planning optimization processes atomic and protected from each other. The number of steps "checked-out" from the planner buffer and the number of segments in the segment buffer is sized and computed such that no operation in the main program takes longer than the time it takes the stepper algorithm to empty it before refilling it. Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
< Check if we need to fill the buffer.
< Reset for new segment block
< Default velocity profile complete at 0.0mm from end of block.
< [Forced Deceleration to Zero Velocity]
< End of feed hold.
< [Normal Operation]
< Initialize as acceleration ramp.
< Enforce stop at end of system motion.
< Only occurs during override reductions.
< Deceleration-only.
< Flag to load next block as deceleration override.
< Either trapezoid or triangle types
< Trapezoid type
< Triangle type
< Deceleration-only type
< Acceleration-only type
< Force update whenever updating block.
< Maximum segment time
< Initialize segment time
< Time worker variable
< mm-Distance worker variable
< Speed worker variable
< New segment distance from end of block.
< Guarantee at least one step.
<
< Mid-deceleration override ramp.
< End of acceleration ramp.
<
< Acceleration only.
< End of cruise.
<
< Cruising only.
< case RAMP_DECEL:
< Used as delta speed (mm/min)
< Check if at or below zero speed.
< (mm)
< Typical case. In deceleration ramp.
< Segment complete. Exit switch-case statement. Continue do-while loop.
< Add computed ramp time to total segment time.
< Check for very slow segments with zero steps.
< Complete Exit loop. Segment execution time maxed.
< Complete Exit loop. Profile complete.
< Reload segment PWM value
< Convert mm_remaining to steps
< Round-up current steps remaining
< Round-up last steps remaining
< Compute number of steps to execute.
< Segment not generated, but current step data still retained.
< Apply previous segment partial step execute time
< Compute adjusted step rate inverse
< (cycles/step)
< At end of forced-termination.
< Bail!
< End of planner block
< Set pointer to indicate check and load next planner block.
void st_reset | ( | ) |
void st_update_plan_block_parameters | ( | ) |
void st_wake_up | ( | ) |
BLOCK VELOCITY PROFILE DEFINITION.
Enable steppers, but cycle does not start unless called by motion control or realtime command.
__________________________ /| |\ _________________ ^ / | | \ /| |\ | / | | \ / | | \ s / | | | | | \ p / | | | | | \ e +-----+------------------------+---+--+---------------+----+ e | BLOCK 1 ^ BLOCK 2 | d | time -----> EXAMPLE: Block 2 entry speed is at max junction velocity
The planner block buffer is planned assuming constant acceleration velocity profiles and are continuously joined at block junctions as shown above. However, the planner only actively computes the block entry speeds for an optimal velocity plan, but does not compute the block internal velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise- deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and triangle(no cruise).
maximum_speed (< nominal_speed) -> + +--------+ <- maximum_speed (= nominal_speed) /|\ / \ / | \ current_speed -> + \ / | + <- exit_speed | + <- exit_speed / | | +-------------+ current_speed -> +----+--+ time --> ^ ^ ^ ^ | | | | decelerate_after(in mm) decelerate_after(in mm) ^ ^ ^ ^ | | | | accelerate_until(in mm) accelerate_until(in mm)
The step segment buffer computes the executing block velocity profile and tracks the critical parameters for the stepper algorithm to accurately trace the profile. These critical parameters are shown and defined in the above illustration.Stepper state initialization. Cycle should only start if the st.cycle_start flag is
< Normal operation
void stepper_init | ( | ) |
Initialize and start the stepper motor subsystem.
Initialize and setup the stepper motor subsystem.
< waveform generation = 0100 = CTC
< Disconnect OC1 output
< Disconnect OC0 outputs and OVF interrupt.
< Normal operation
< Disable Timer0 until needed
< Enable Timer0 overflow interrupt