Skip to content

Instantly share code, notes, and snippets.

@nullsub
Created August 2, 2012 17:37
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save nullsub/3238961 to your computer and use it in GitHub Desktop.
Save nullsub/3238961 to your computer and use it in GitHub Desktop.
stepper.c file
/*
config.h - compile time configuration
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
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 <http://www.gnu.org/licenses/>.
*/
#ifndef config_h
#define config_h
// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them.
#define BAUD_RATE 9600
// Updated default pin-assignments from 0.6 onwards
// (see bottom of file for a copy of the old config)
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0
//#define STEPPING_DDR DDRD
//#define STEPPING_PORT PORTD
#define X_STEP_BIT 2
#define Y_STEP_BIT 3
#define Z_STEP_BIT 4
#define X_DIRECTION_BIT 5
#define Y_DIRECTION_BIT 6
#define Z_DIRECTION_BIT 7
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define X_LIMIT_BIT 1
#define Y_LIMIT_BIT 2
#define Z_LIMIT_BIT 3
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
#define SPINDLE_ENABLE_BIT 4
#define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5
// This parameter sets the delay time before disabling the steppers after the final block of movement.
// A short delay ensures the steppers come to a complete stop and the residual inertial force in the
// CNC axes don't cause the axes to drift off position. This is particularly important when manually
// entering g-code into grbl, i.e. locating part zero or simple manual machining. If the axes drift,
// grbl has no way to know this has happened, since stepper motors are open-loop control. Depending
// on the machine, this parameter may need to be larger or smaller than the default time.
// NOTE: If defined 0, the delay will not be compiled.
#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer >= 0
// The temporal resolution of the acceleration management subsystem. Higher number give smoother
// acceleration but may impact performance.
// NOTE: Increasing this parameter will help any resolution related issues, especially with machines
// requiring very high accelerations and/or very fast feedrates. In general, this will reduce the
// error between how the planner plans the motions and how the stepper program actually performs them.
// However, at some point, the resolution can be high enough, where the errors related to numerical
// round-off can be great enough to cause problems and/or it's too fast for the Arduino. The correct
// value for this parameter is machine dependent, so it's advised to set this only as high as needed.
// Approximate successful values can range from 30L to 100L or more.
#define ACCELERATION_TICKS_PER_SECOND 50L
// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end
// of the buffer and all stops. This should not be much greater than zero and should only be changed
// if unwanted behavior is observed on a user's machine when running at very slow speeds.
#define MINIMUM_PLANNER_SPEED 0.0 // (mm/min)
// Minimum stepper rate. Sets the absolute minimum stepper rate in the stepper program and never runs
// slower than this value, except when sleeping. This parameter overrides the minimum planner speed.
// This is primarily used to guarantee that the end of a movement is always reached and not stop to
// never reach its target. This parameter should always be greater than zero.
#define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction. This parameter maybe decreased if there are issues with the accuracy of the arc
// generations. In general, the default value is more than enough for the intended CNC applications
// of grbl, and should be on the order or greater than the size of the buffer to help with the
// computational efficiency of generating arcs.
#define N_ARC_CORRECTION 25 // Integer (1-255)
#endif
// Pin-assignments from Grbl 0.5
// #define STEPPERS_DISABLE_DDR DDRD
// #define STEPPERS_DISABLE_PORT PORTD
// #define STEPPERS_DISABLE_BIT 2
//
// #define STEPPING_DDR DDRC
// #define STEPPING_PORT PORTC
// #define X_STEP_BIT 0
// #define Y_STEP_BIT 1
// #define Z_STEP_BIT 2
// #define X_DIRECTION_BIT 3
// #define Y_DIRECTION_BIT 4
// #define Z_DIRECTION_BIT 5
//
// #define LIMIT_DDR DDRD
// #define LIMIT_PORT PORTD
// #define X_LIMIT_BIT 3
// #define Y_LIMIT_BIT 4
// #define Z_LIMIT_BIT 5
//
// #define SPINDLE_ENABLE_DDR DDRD
// #define SPINDLE_ENABLE_PORT PORTD
// #define SPINDLE_ENABLE_BIT 6
//
// #define SPINDLE_DIRECTION_DDR DDRD
// #define SPINDLE_DIRECTION_PORT PORTD
// #define SPINDLE_DIRECTION_BIT 7
/*
limits.h - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
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 <http://www.gnu.org/licenses/>.
*/
#include <util/delay.h>
#include <avr/io.h>
#include "stepper.h"
#include "settings.h"
#include "nuts_bolts.h"
#include "config.h"
void limits_init() {
//LIMIT_DDR &= ~(LIMIT_MASK);
}
static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, bool reverse_direction, uint32_t microseconds_per_pulse) {
// First home the Z axis
/* NOT SUPPORTED
* uint32_t step_delay = microseconds_per_pulse - settings.pulse_microseconds;
uint8_t out_bits = DIRECTION_MASK;
uint8_t limit_bits;
if (x_axis) { out_bits |= (1<<X_STEP_BIT); }
if (y_axis) { out_bits |= (1<<Y_STEP_BIT); }
if (z_axis) { out_bits |= (1<<Z_STEP_BIT); }
// Invert direction bits if this is a reverse homing_cycle
if (reverse_direction) {
out_bits ^= DIRECTION_MASK;
}
// Apply the global invert mask
out_bits ^= settings.invert_mask;
// Set direction pins
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
for(;;) {
limit_bits = LIMIT_PIN;
if (reverse_direction) {
// Invert limit_bits if this is a reverse homing_cycle
limit_bits ^= LIMIT_MASK;
}
if (x_axis && !(LIMIT_PIN & (1<<X_LIMIT_BIT))) {
x_axis = false;
out_bits ^= (1<<X_STEP_BIT);
}
if (y_axis && !(LIMIT_PIN & (1<<Y_LIMIT_BIT))) {
y_axis = false;
out_bits ^= (1<<Y_STEP_BIT);
}
if (z_axis && !(LIMIT_PIN & (1<<Z_LIMIT_BIT))) {
z_axis = false;
out_bits ^= (1<<Z_STEP_BIT);
}
// Check if we are done
if(!(x_axis || y_axis || z_axis)) { return; }
STEPPING_PORT |= out_bits & STEP_MASK;
delay_us(settings.pulse_microseconds);
STEPPING_PORT ^= out_bits & STEP_MASK;
delay_us(step_delay);
}*/
return;
}
static void approach_limit_switch(bool x, bool y, bool z) {
homing_cycle(x, y, z, false, 100000);
}
static void leave_limit_switch(bool x, bool y, bool z) {
homing_cycle(x, y, z, true, 500000);
}
void limits_go_home() {
st_synchronize();
// Store the current limit switch state
uint8_t original_limit_state = LIMIT_PIN;
approach_limit_switch(false, false, true); // First home the z axis
approach_limit_switch(true, true, false); // Then home the x and y axis
// Xor previous and current limit switch state to determine which were high then but have become
// low now. These are the actual installed limit switches.
uint8_t limit_switches_present = (original_limit_state ^ LIMIT_PIN) & LIMIT_MASK;
// Now carefully leave the limit switches
leave_limit_switch(
limit_switches_present & (1<<X_LIMIT_BIT),
limit_switches_present & (1<<Y_LIMIT_BIT),
limit_switches_present & (1<<Z_LIMIT_BIT));
}
/*
stepper.c - stepper motor driver: executes motion plans using stepper motors
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
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 <http://www.gnu.org/licenses/>.
*/
/* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith
and Philipp Tiefenbacher. */
#include "stepper.h"
#include "config.h"
#include "settings.h"
#include <math.h>
#include <stdlib.h>
#include <util/delay.h>
#include "nuts_bolts.h"
#include <avr/interrupt.h>
#include "planner.h"
#include "limits.h"
// Some useful constants
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
#define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND)
static block_t *current_block; // A pointer to the block currently being traced
// Variables used by The Stepper Driver Interrupt
static uint8_t out_bits; // The next stepping-bits to be output
static int32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
counter_z;
static uint32_t step_events_completed; // The number of step events executed in the current block
static volatile uint8_t busy; // true when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler.
// Variables used by the trapezoid generation
static uint32_t cycles_per_step_event; // The number of machine cycles between each step event
static uint32_t trapezoid_tick_cycle_counter; // The cycles since last trapezoid_tick. Used to generate ticks at a steady
// pace without allocating a separate timer
static uint32_t trapezoid_adjusted_rate; // The current rate of step_events according to the trapezoid generator
static uint32_t min_safe_rate; // Minimum safe rate for full deceleration rate reduction step. Otherwise halves step_rate.
static uint8_t cycle_start; // Cycle start flag to indicate program start and block processing.
// __________________________
// /| |\ _________________ ^
// / | | \ /| |\ |
// / | | \ / | | \ 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
// during the first block->accelerate_until step_events_completed, then keeps going at constant speed until
// step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset.
// The slope of acceleration is always +/- block->rate_delta and is applied at a constant rate following the midpoint rule
// by the trapezoid generator, which is called ACCELERATION_TICKS_PER_SECOND times per second.
static void set_step_events_per_minute(uint32_t steps_per_minute);
// Stepper state initialization
static void st_wake_up()
{
// Enable stepper driver interrupt
TIMSK1 |= (1<<OCIE1A);
}
// Stepper shutdown
static void st_go_idle()
{
// Cycle finished. Set flag to false.
cycle_start = false;
// Disable stepper driver interrupt
TIMSK1 &= ~(1<<OCIE1A);
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
}
// Initializes the trapezoid generator from the current block. Called whenever a new
// block begins.
static void trapezoid_generator_reset()
{
trapezoid_adjusted_rate = current_block->initial_rate;
min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta
trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
set_step_events_per_minute(trapezoid_adjusted_rate); // Initialize cycles_per_step_event
}
// This function determines an acceleration velocity change every CYCLES_PER_ACCELERATION_TICK by
// keeping track of the number of elapsed cycles during a de/ac-celeration. The code assumes that
// step_events occur significantly more often than the acceleration velocity iterations.
static uint8_t iterate_trapezoid_cycle_counter()
{
trapezoid_tick_cycle_counter += cycles_per_step_event;
if(trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) {
trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK;
return(true);
} else {
return(false);
}
}
#define DIR_FORWARD (0)
#define DIR_BACKWARD (1)
#define STEPPER_X_A1 (0x01<<PINB0)
#define STEPPER_X_A2 (0x01<<PINB1)
#define STEPPER_X_B1 (0x01<<PINB2)
#define STEPPER_X_B2 (0x01<<PINB3)
#define STEPPER_Y_A1 (0x01<<PIND4)
#define STEPPER_Y_A2 (0x01<<PIND5)
#define STEPPER_Y_B1 (0x01<<PIND6)
#define STEPPER_Y_B2 (0x01<<PIND7)
#define STEPPER_Z_A1 (0x01<<PINC0)
#define STEPPER_Z_A2 (0x01<<PINC1)
#define STEPPER_Z_B1 (0x01<<PINC2)
#define STEPPER_Z_B2 (0x01<<PINC3)
#define STEPPING_PORT_Z PORTC
#define STEPPING_PORT_Y PORTD
#define STEPPING_PORT_X PORTB
#define STEPPING_DDR_Z DDRC
#define STEPPING_DDR_Y DDRD
#define STEPPING_DDR_X DDRB
//#define STEPPER_X_ENABLE (0x01<<25)
//#define STEPPER_Y_ENABLE (0x01<<26)
#define ALL_STEPPER_PINS_X (STEPPER_X_A1|STEPPER_X_A2|STEPPER_X_B1|STEPPER_X_B2)
#define ALL_STEPPER_PINS_Y (STEPPER_Y_A1|STEPPER_Y_A2|STEPPER_Y_B1|STEPPER_Y_B2)
#define ALL_STEPPER_PINS_Z (STEPPER_Z_A1 |STEPPER_Z_A2|STEPPER_Z_B1|STEPPER_Z_B2)
const int stepper_pins[3][4] = {
{STEPPER_X_A1, STEPPER_X_A2, STEPPER_X_B1, STEPPER_X_B2},
{STEPPER_Y_A1, STEPPER_Y_A2, STEPPER_Y_B1, STEPPER_Y_B2},
{STEPPER_Z_A1, STEPPER_Z_A2, STEPPER_Z_B1, STEPPER_Z_B2},
};
void do_full_step(int direction, int axis)
{
static unsigned int crrnt_step[3] = {0,0,0};
if(direction == DIR_FORWARD) {
crrnt_step[axis] ++;
if (crrnt_step[axis] >= 4)
crrnt_step[axis] = 0;
}
else{
if(crrnt_step[axis] == 0)
crrnt_step[axis] = 4;
crrnt_step[axis] --;
}
if(axis == X_AXIS) { // Z_AXIS is on a different I/O port
switch(crrnt_step[axis]) {
case 0:
STEPPING_PORT_X &= ~(stepper_pins[axis][0]|stepper_pins[axis][2]);
STEPPING_PORT_X |= stepper_pins[axis][3] | stepper_pins[axis][1];
break;
case 1:
STEPPING_PORT_X &= ~(stepper_pins[axis][1] | stepper_pins[axis][2]);
STEPPING_PORT_X |= stepper_pins[axis][0] | stepper_pins[axis][3];
break;
case 2:
STEPPING_PORT_X &= ~(stepper_pins[axis][1] | stepper_pins[axis][3]);
STEPPING_PORT_X |= stepper_pins[axis][2] | stepper_pins[axis][0];
break;
case 3:
STEPPING_PORT_X &= ~(stepper_pins[axis][0] | stepper_pins[axis][3]);
STEPPING_PORT_X |= (stepper_pins[axis][1] | stepper_pins[axis][2]);
break;
return;
}
}
if(axis == Y_AXIS) {
switch(crrnt_step[axis]) {
case 0:
STEPPING_PORT_Y &= ~(stepper_pins[axis][0]|stepper_pins[axis][2]);
STEPPING_PORT_Y |= stepper_pins[axis][3] | stepper_pins[axis][1];
break;
case 1:
STEPPING_PORT_Y &= ~(stepper_pins[axis][1] | stepper_pins[axis][2]);
STEPPING_PORT_Y |= stepper_pins[axis][0] | stepper_pins[axis][3];
break;
case 2:
STEPPING_PORT_Y &= ~(stepper_pins[axis][1] | stepper_pins[axis][3]);
STEPPING_PORT_Y |= stepper_pins[axis][2] | stepper_pins[axis][0];
break;
case 3:
STEPPING_PORT_Y &= ~(stepper_pins[axis][0] | stepper_pins[axis][3]);
STEPPING_PORT_Y |= (stepper_pins[axis][1] | stepper_pins[axis][2]);
break;
return;
}
}
if(axis == Z_AXIS) { // Z_AXIS is on a different I/O port
switch(crrnt_step[axis]) {
case 0:
STEPPING_PORT_Z &= ~(stepper_pins[axis][0]|stepper_pins[axis][2]);
STEPPING_PORT_Z |= stepper_pins[axis][3] | stepper_pins[axis][1];
break;
case 1:
STEPPING_PORT_Z &= ~(stepper_pins[axis][1] | stepper_pins[axis][2]);
STEPPING_PORT_Z |= stepper_pins[axis][0] | stepper_pins[axis][3];
break;
case 2:
STEPPING_PORT_Z &= ~(stepper_pins[axis][1] | stepper_pins[axis][3]);
STEPPING_PORT_Z |= stepper_pins[axis][2] | stepper_pins[axis][0];
break;
case 3:
STEPPING_PORT_Z &= ~(stepper_pins[axis][0] | stepper_pins[axis][3]);
STEPPING_PORT_Z |= (stepper_pins[axis][1] | stepper_pins[axis][2]);
break;
return;
}
}
}
// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is executed at the rate set with
// config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately.
// It 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 three stepper outputs simultaneously with these two interrupts.
ISR(TIMER1_COMPA_vect)
{
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
/*
// Set the direction pins a couple of nanoseconds before we step the steppers
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
// Then pulse the stepping pins
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
// Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
// exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
TCNT2 = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); // Reload timer counter
TCCR2B = (1<<CS21); // Begin timer2. Full speed, 1/8 prescaler
*/
if(out_bits & (1<<X_STEP_BIT)) {
if(out_bits & (1<<X_DIRECTION_BIT)) {
//do_half_step(DIR_FORWARD, X_AXIS);
do_full_step(DIR_FORWARD, X_AXIS);
} else {
// do_half_step(DIR_BACKWARD, X_AXIS);
do_full_step(DIR_BACKWARD, X_AXIS);
}
}
if(out_bits & (1<<Y_STEP_BIT)) {
if(out_bits & (1<<Y_DIRECTION_BIT)) {
// do_half_step(DIR_FORWARD, Y_AXIS);
do_full_step(DIR_FORWARD, Y_AXIS);
} else {
// do_half_step(DIR_BACKWARD, Y_AXIS);
do_full_step(DIR_BACKWARD, Y_AXIS);
}
}
if(out_bits & (1<<Z_STEP_BIT)) {
if(out_bits & (1<<Z_DIRECTION_BIT)) {
// do_half_step(DIR_FORWARD, Z_AXIS);
do_full_step(DIR_FORWARD, Z_AXIS);
} else {
// do_half_step(DIR_BACKWARD, Z_AXIS);
do_full_step(DIR_BACKWARD, Z_AXIS);
}
}
busy = true;
// Re-enable interrupts to allow ISR_TIMER2_OVERFLOW to trigger on-time and allow serial communications
// regardless of time in this handler. The following code prepares the stepper driver for the next
// step interrupt compare and will always finish before returning to the main program.
sei();
// If there is no current block, attempt to pop one from the buffer
if (current_block == NULL) {
// Anything in the buffer? If so, initialize next motion.
current_block = plan_get_current_block();
if (current_block != NULL) {
trapezoid_generator_reset();
counter_x = -(current_block->step_event_count >> 1);
counter_y = counter_x;
counter_z = counter_x;
step_events_completed = 0;
} else {
st_go_idle();
}
}
if (current_block != NULL) {
// Execute step displacement profile by bresenham line algorithm
out_bits = current_block->direction_bits;
counter_x += current_block->steps_x;
if (counter_x > 0) {
out_bits |= (1<<X_STEP_BIT);
counter_x -= current_block->step_event_count;
}
counter_y += current_block->steps_y;
if (counter_y > 0) {
out_bits |= (1<<Y_STEP_BIT);
counter_y -= current_block->step_event_count;
}
counter_z += current_block->steps_z;
if (counter_z > 0) {
out_bits |= (1<<Z_STEP_BIT);
counter_z -= current_block->step_event_count;
}
step_events_completed++; // Iterate step events
// While in block steps, check for de/ac-celeration events and execute them accordingly.
if (step_events_completed < current_block->step_event_count) {
// The trapezoid generator always checks step event location to ensure de/ac-celerations are
// executed and terminated at exactly the right time. This helps prevent over/under-shooting
// the target position and speed.
// NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the
// discrete velocity changes increase and accuracy can increase as well to a point. Numerical
// round-off errors can effect this, if set too high. This is important to note if a user has
// very high acceleration and/or feedrate requirements for their machine.
if (step_events_completed < current_block->accelerate_until) {
// Iterate cycle counter and check if speeds need to be increased.
if ( iterate_trapezoid_cycle_counter() ) {
trapezoid_adjusted_rate += current_block->rate_delta;
if (trapezoid_adjusted_rate >= current_block->nominal_rate) {
// Reached nominal rate a little early. Cruise at nominal rate until decelerate_after.
trapezoid_adjusted_rate = current_block->nominal_rate;
}
set_step_events_per_minute(trapezoid_adjusted_rate);
}
} else if (step_events_completed >= current_block->decelerate_after) {
// Reset trapezoid tick cycle counter to make sure that the deceleration is performed the
// same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for
// an accurate approximation of the deceleration curve.
if (step_events_completed == current_block-> decelerate_after) {
trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2;
} else {
// Iterate cycle counter and check if speeds need to be reduced.
if ( iterate_trapezoid_cycle_counter() ) {
// NOTE: We will only do a full speed reduction if the result is more than the minimum safe
// rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by
// half increments until finished. The half increments are guaranteed not to exceed the
// CNC acceleration limits, because they will never be greater than rate_delta. This catches
// small errors that might leave steps hanging after the last trapezoid tick or a very slow
// step rate at the end of a full stop deceleration in certain situations. The half rate
// reductions should only be called once or twice per block and create a nice smooth
// end deceleration.
if (trapezoid_adjusted_rate > min_safe_rate) {
trapezoid_adjusted_rate -= current_block->rate_delta;
} else {
trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2
}
if (trapezoid_adjusted_rate < current_block->final_rate) {
// Reached final rate a little early. Cruise to end of block at final rate.
trapezoid_adjusted_rate = current_block->final_rate;
}
set_step_events_per_minute(trapezoid_adjusted_rate);
}
}
} else {
// No accelerations. Make sure we cruise exactly at the nominal rate.
if (trapezoid_adjusted_rate != current_block->nominal_rate) {
trapezoid_adjusted_rate = current_block->nominal_rate;
set_step_events_per_minute(trapezoid_adjusted_rate);
}
}
} else {
// If current block is finished, reset pointer
current_block = NULL;
plan_discard_current_block();
}
}
// out_bits ^= settings.invert_mask; // Apply stepper invert mask
busy=false;
}
// This interrupt is set up by ISR_TIMER1_COMPAREA when it sets the motor port bits. It resets
// the motor port after a short period (settings.pulse_microseconds) completing one step cycle.
// TODO: It is possible for the serial interrupts to delay this interrupt by a few microseconds, if
// they execute right before this interrupt. Not a big deal, but could use some TLC at some point.
ISR(TIMER2_OVF_vect)
{
// Reset stepping pins (leave the direction pins)
// STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed.
}
// 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<<STEPPERS_DISABLE_BIT;
STEPPING_DDR_X |= ALL_STEPPER_PINS_X;
STEPPING_PORT_X |= ALL_STEPPER_PINS_X;
STEPPING_DDR_Y |= ALL_STEPPER_PINS_Y;
STEPPING_PORT_Y |= ALL_STEPPER_PINS_Y;
STEPPING_DDR_Z |= ALL_STEPPER_PINS_Z;
STEPPING_PORT_Z |= ALL_STEPPER_PINS_Z;
// waveform generation = 0100 = CTC
TCCR1B &= ~(1<<WGM13);
TCCR1B |= (1<<WGM12);
TCCR1A &= ~(1<<WGM11);
TCCR1A &= ~(1<<WGM10);
// output mode = 00 (disconnected)
TCCR1A &= ~(3<<COM1A0);
TCCR1A &= ~(3<<COM1B0);
// Configure Timer 2
TCCR2A = 0; // Normal operation
TCCR2B = 0; // Disable timer until needed.
TIMSK2 |= (1<<TOIE2); // Enable Timer2 interrupt flag
set_step_events_per_minute(MINIMUM_STEPS_PER_MINUTE);
trapezoid_tick_cycle_counter = 0;
current_block = NULL;
busy = false;
// Start in the idle state
st_go_idle();
}
// Block until all buffered steps are executed
void st_synchronize()
{
while(plan_get_current_block()) { sleep_mode(); }
}
// Configures the prescaler and ceiling of timer 1 to produce the given rate as accurately as possible.
// Returns the actual number of cycles per interrupt
static uint32_t config_step_timer(uint32_t cycles)
{
uint16_t ceiling;
uint16_t prescaler;
uint32_t actual_cycles;
if (cycles <= 0xffffL) {
ceiling = cycles;
prescaler = 0; // prescaler: 0
actual_cycles = ceiling;
} else if (cycles <= 0x7ffffL) {
ceiling = cycles >> 3;
prescaler = 1; // prescaler: 8
actual_cycles = ceiling * 8L;
} else if (cycles <= 0x3fffffL) {
ceiling = cycles >> 6;
prescaler = 2; // prescaler: 64
actual_cycles = ceiling * 64L;
} else if (cycles <= 0xffffffL) {
ceiling = (cycles >> 8);
prescaler = 3; // prescaler: 256
actual_cycles = ceiling * 256L;
} else if (cycles <= 0x3ffffffL) {
ceiling = (cycles >> 10);
prescaler = 4; // prescaler: 1024
actual_cycles = ceiling * 1024L;
} else {
// Okay, that was slower than we actually go. Just set the slowest speed
ceiling = 0xffff;
prescaler = 4;
actual_cycles = 0xffff * 1024;
}
// Set prescaler
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | ((prescaler+1)<<CS10);
// Set ceiling
OCR1A = ceiling;
return(actual_cycles);
}
static void set_step_events_per_minute(uint32_t steps_per_minute)
{
if (steps_per_minute < MINIMUM_STEPS_PER_MINUTE) { steps_per_minute = MINIMUM_STEPS_PER_MINUTE; }
cycles_per_step_event = config_step_timer((TICKS_PER_MICROSECOND*1000000*60)/steps_per_minute);
}
void st_go_home()
{
limits_go_home();
plan_set_current_position(0,0,0);
}
// Planner external interface to start stepper interrupt and execute the blocks in queue.
void st_cycle_start()
{
if (!cycle_start) {
cycle_start = true;
st_wake_up();
}
}
@exilaus
Copy link

exilaus commented Nov 21, 2012

Hi,
thanks too much for this code,but i have a question,
do you think possible modify this part of code for using using uln2003 two-wire configuration? http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/

Thanks

@exilaus
Copy link

exilaus commented Nov 29, 2012

HI Nullsub thanks too much for this configuration,
Do you think possible use this configuration http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/ for enable axis limit ?

@fjcns
Copy link

fjcns commented Feb 10, 2013

I'm trying to make a cnc milling machine using an arduino mega and H Bridge as stepper driver using windows. My problem is that I only understand a few things of arduino programming. I recognizing some code from arduino but not know what to do with the files ".h" and ". c" can you help me?

@offon
Copy link

offon commented Feb 12, 2013

put files in projects folder and remake .hex file by winAvr

@offon
Copy link

offon commented Feb 12, 2013

Have some problem when i try to make hex file, using ardunio2560:
can some body help me with this problem?

"make.exe" all
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c main.c -o main.o
main.c: In function 'main':
main.c:84: warning: array subscript is above array bounds
main.c:85: warning: array subscript is above array bounds
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c motion_control.c -o motion_control.o
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c gcode.c -o gcode.o
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c spindle_control.c -o spindle_control.o
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c serial.c -o serial.o
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c protocol.c -o protocol.o
protocol.c: In function 'protocol_status_report':
protocol.c:107: warning: array subscript is above array bounds
protocol.c:107: warning: array subscript is above array bounds
avr-gcc -Wall -Os -DF_CPU=20000000 -mmcu=atmega2560 -I. -ffunction-sections -c stepper.c -o stepper.o
stepper.c:37:1: warning: "STEP_MASK" redefined
In file included from stepper.c:25:
stepper.h:29:1: warning: this is the location of the previous definition
stepper.c:38:1: warning: "DIRECTION_MASK" redefined
stepper.h:30:1: warning: this is the location of the previous definition
stepper.c:89: error: static declaration of 'st_go_idle' follows non-static declaration
stepper.h:38: error: previous declaration of 'st_go_idle' was here
stepper.c: In function 'st_go_home':
stepper.c:505: error: too few arguments to function 'plan_set_current_position'
make.exe: *** [stepper.o] Error 1

Process Exit Code: 2
Time Taken: 00:01

@chimruoi
Copy link

chimruoi commented Sep 7, 2017

Can anybody give me the block diagram?
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment