/* stepper.c - stepper motor driver: executes motion plans using stepper motors Part of Grbl Copyright (c) 2011-2013 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud Grbl 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 3 of the License, or (at your option) any later version. Grbl 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 Grbl. If not, see . */ #include #include "stepper.h" #include "config.h" #include "settings.h" #include "planner.h" // Some useful constants #define TICKS_PER_MICROSECOND (F_CPU/1000000) #define CRUISE_RAMP 0 #define ACCEL_RAMP 1 #define DECEL_RAMP 2 // Stepper state variable. Contains running data and trapezoid variables. typedef struct { // Used by the bresenham line algorithm int32_t counter[N_AXIS]; // Counter variables for the bresenham line tracer uint32_t event_count; // Total event count. Retained for feed holds. uint32_t step_events_remaining; // Steps remaining in motion // Used by Pramod Ranade inverse time algorithm int32_t delta_d; // Ranade distance traveled per interrupt tick int32_t d_counter; // Ranade distance traveled since last step event uint8_t ramp_count; // Acceleration interrupt tick counter. uint8_t ramp_type; // Ramp type variable. uint8_t execute_step; // Flags step execution for each interrupt. } stepper_t; static stepper_t st; static block_t *current_block; // A pointer to the block currently being traced // Used by the stepper driver interrupt static uint8_t step_pulse_time; // Step pulse reset time after step rise static uint8_t out_bits; // The next stepping-bits to be output // NOTE: If the main interrupt is guaranteed to be complete before the next interrupt, then // this blocking variable is no longer needed. Only here for safety reasons. static volatile uint8_t busy; // True when "Stepper Driver Interrupt" is being serviced. Used to avoid retriggering that handler. // __________________________ // /| |\ _________________ ^ // / | | \ /| |\ | // / | | \ / | | \ s // / | | | | | \ p // / | | | | | \ e // +-----+------------------------+---+--+---------------+----+ e // | BLOCK 1 | BLOCK 2 | d // // time -----> // // The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates by block->rate_delta // until reaching cruising speed block->nominal_rate, and/or until step_events_remaining reaches block->decelerate_after // after which it decelerates until the block is completed. The driver uses constant acceleration, which is applied as // +/- block->rate_delta velocity increments by the midpoint rule at each ACCELERATION_TICKS_PER_SECOND. // Stepper state initialization. Cycle should only start if the st.cycle_start flag is // enabled. Startup init and limits call this function but shouldn't start the cycle. void st_wake_up() { // Enable steppers by resetting the stepper disable port if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<> 3); // Enable stepper driver interrupt st.execute_step = false; TCNT0 = 0; // Clear Timer2 TIMSK0 |= (1<d_next; st.execute_step = true; // Configure next step out_bits = current_block->direction_bits; // Reset out_bits and reload direction bits // Execute step displacement profile by Bresenham line algorithm st.counter[X_AXIS] -= current_block->steps_x; // Doesn't change when set up. if (st.counter[X_AXIS] < 0) { out_bits |= (1<steps_y; if (st.counter[Y_AXIS] < 0) { out_bits |= (1<steps_z; if (st.counter[Z_AXIS] < 0) { out_bits |= (1<direction_bits ^ settings.invert_mask; st.execute_step = true; // Set flag to set direction bits. // Initialize Bresenham variables st.counter_x = (current_block->step_event_count >> 1); st.counter_y = st.counter_x; st.counter_z = st.counter_x; st.event_count = current_block->step_event_count; st.step_events_remaining = st.event_count; // During feed hold, do not update inverse time counter, rate, or ramp type. Keep decelerating. if (sys.state == STATE_CYCLE) { // Initialize Ranade variables st.d_counter = current_block->d_next; st.delta_d = current_block->initial_rate; st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; // Initialize ramp type. if (st.step_events_remaining == current_block->decelerate_after) { st.ramp_type = DECEL_RAMP; } else if (st.delta_d == current_block->nominal_rate) { st.ramp_type = CRUISE_RAMP; } else { st.ramp_type = ACCEL_RAMP; } } } else { st_go_idle(); bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end return; // Nothing to do but exit. } } // Adjust inverse time counter for ac/de-celerations if (st.ramp_type) { // Tick acceleration ramp counter st.ramp_count--; if (st.ramp_count == 0) { st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK; // Reload ramp counter if (st.ramp_type == ACCEL_RAMP) { // Adjust velocity for acceleration st.delta_d += current_block->rate_delta; if (st.delta_d >= current_block->nominal_rate) { // Reached cruise state. st.ramp_type = CRUISE_RAMP; st.delta_d = current_block->nominal_rate; // Set cruise velocity } } else if (st.ramp_type == DECEL_RAMP) { // Adjust velocity for deceleration if (st.delta_d > current_block->rate_delta) { st.delta_d -= current_block->rate_delta; } else { st.delta_d >>= 1; // Integer divide by 2 until complete. Also prevents overflow. } } } } // Check for feed hold state and execute accordingly. if (sys.state == STATE_HOLD) { if (st.ramp_type != DECEL_RAMP) { st.ramp_type = DECEL_RAMP; st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; } if (st.delta_d <= current_block->rate_delta) { st_go_idle(); bit_true(sys.execute,EXEC_CYCLE_STOP); return; } } if (st.ramp_type != DECEL_RAMP) { // Acceleration and cruise handled by ramping. Just check for deceleration. if (st.step_events_remaining <= current_block->decelerate_after) { st.ramp_type = DECEL_RAMP; if (st.step_events_remaining == current_block->decelerate_after) { if (st.delta_d == current_block->nominal_rate) { st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; // Set ramp counter for trapezoid } else { st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK-st.ramp_count; // Set ramp counter for triangle } } } } } else { busy = false; } // The Stepper Port Reset Interrupt: Timer2 OVF interrupt handles the falling edge of the // step pulse. This should always trigger before the next Timer0 COMPA interrupt and independently // finish, if Timer0 is disabled after completing a move. ISR(TIMER2_OVF_vect) { STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK); TCCR2B = 0; // Disable timer until needed. } // Reset and clear stepper subsystem variables void st_reset() { memset(&st, 0, sizeof(st)); current_block = NULL; busy = false; } // Initialize and start the stepper motor subsystem void st_init() { // Configure directions of interface pins STEPPING_DDR |= STEPPING_MASK; STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask; STEPPERS_DISABLE_DDR |= 1<