Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
LaosMotion/grbl/stepper.cpp
- Committer:
- Michael J. Spencer
- Date:
- 2014-03-05
- Revision:
- 1:f5ac63519541
File content as of revision 1:f5ac63519541:
/*
stepper.c - stepper motor driver: executes motion plans using stepper motors
Part of Grbl
Taken from R2C2 project and modified for Laos by Peter Brier
stripped, included some Marlin changes from Erik van de Zalm
Copyright (c) 2009-2011 Simen Svale Skogsrud
Modifications Copyright (c) 2011 Sungeun K. Jeon
Modifications Copyright (c) 2011 Peter Brier
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, orc
(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 warrlaanty 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. */
/* The acceleration profiles are derived using the techniques descibed in the paper
"Generate stepper-motor speed profiles in real time" David Austin 2004
published at http://www.eetimes.com/design/embedded/4006438/Generate-stepper-motor-speed-profiles-in-real-time and others.
*/
#include <LaosMotion.h>
#include "pins.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "fixedpt.h"
#include "stepper.h"
#include "config.h"
#include "planner.h"
#define TICKS_PER_MICROSECOND (1) // Ticker uses 1usec units
// #define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND)
#define STEP_TIMER_FREQ 1000000 // 1 MHz
// types: ramp state
typedef enum {RAMP_UP, RAMP_MAX, RAMP_DOWN} tRamp;
// Prototypes
static void st_interrupt ();
static void set_step_timer (uint32_t cycles);
static void st_go_idle();
// Globals
volatile unsigned char busy = 0;
volatile int32_t actpos_x, actpos_y, actpos_z, actpos_e; // actual position
// Locals
static block_t *current_block; // A pointer to the block currently being traced
static Ticker timer; // the periodic timer used to step
static tFixedPt pwmofs; // the offset of the PWM value
static tFixedPt pwmscale; // the scaling of the PWM value
static volatile int running = 0; // stepper irq is running
static uint32_t direction_inv; // invert mask for direction bits
static uint32_t direction_bits; // all axes direction (different ports)
static uint32_t step_bits; // all axis step bits
static uint32_t step_inv; // invert mask for the stepper bits
static uint32_t nominal_rate; // [steps/min]
static int32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
counter_z;
static int32_t counter_e, counter_l, pos_l; // extruder and laser
static uint32_t step_events_completed; // The number of step events executed in the current block
// 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 tFixedPt c; // current clock cycle count [1/speed]
static int32_t c_min; // minimal clock cycle count [at vnominal for this block]
static int32_t n;
static int32_t decel_n;
static tRamp ramp; // state of state machine for ramping up/down
extern unsigned char bitmap_bpp;
extern unsigned long bitmap[], bitmap_width, bitmap_size;
// __________________________
// /| |\ _________________ ^
// / | | \ /| |\ |
// / | | \ / | | \ 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.
// Initialize and start the stepper motor subsystem
void st_init(void)
{
direction_inv =
(cfg->xscale<0 ? (1<<X_DIRECTION_BIT) : 0) |
(cfg->yscale<0 ? (1<<Y_DIRECTION_BIT) : 0) |
(cfg->zscale<0 ? (1<<Z_DIRECTION_BIT) : 0) |
(cfg->escale<0 ? (1<<E_DIRECTION_BIT) : 0);
step_inv =
(cfg->xinv ? (1<<X_STEP_BIT) : 0) |
(cfg->yinv ? (1<<Y_STEP_BIT) : 0) |
(cfg->zinv ? (1<<Z_STEP_BIT) : 0) |
(cfg->einv ? (1<<E_STEP_BIT) : 0);
printf("Direction: %d\n", direction_inv);
pwmofs = to_fixed(cfg->pwmmin) / 100; // offset (0 .. 1.0)
if ( cfg->pwmmin == cfg->pwmmax )
pwmscale = 0;
else
pwmscale = div_f(to_fixed(cfg->pwmmax - cfg->pwmmin), to_fixed(100) );
printf("ofs: %d, scale: %d\n", pwmofs, pwmscale);
actpos_x = actpos_y = actpos_z = actpos_e = 0;
st_wake_up();
trapezoid_tick_cycle_counter = 0;
st_go_idle(); // Start in the idle state
}
// output the direction bits to the appropriate output pins
static inline void set_direction_pins (void)
{
xdir = ( (direction_bits & (1<<X_DIRECTION_BIT))? 0 : 1 );
ydir = ( (direction_bits & (1<<Y_DIRECTION_BIT))? 0 : 1 );
zdir = ( (direction_bits & (1<<Z_DIRECTION_BIT))? 0 : 1 );
// edir = ( (direction_bits & (1<<E_DIRECTION_BIT))?0:1);
}
// output the step bits on the appropriate output pins
static inline void set_step_pins (uint32_t bits)
{
xstep = ( (bits & (1<<X_STEP_BIT))?1:0 );
ystep = ( (bits & (1<<Y_STEP_BIT))?1:0 );
zstep = ( (bits & (1<<Z_STEP_BIT))?1:0 );
// estep = ( (bits & (1<<E_STEP_BIT))?1:0 );
}
// unstep all stepper pins (output low)
static inline void clear_all_step_pins (void)
{
xstep =( (step_inv & (1<<X_STEP_BIT)) ? 1 : 0 );
ystep =( (step_inv & (1<<Y_STEP_BIT)) ? 1 : 0 );
zstep =( (step_inv & (1<<Z_STEP_BIT)) ? 1 : 0 );
// estep =( (step_inv & (1<<E_STEP_BIT)) ? 0 : 1 );
}
// check home sensor
int hit_home_stop_x(int axis)
{
return 1;
}
// check home sensor
int hit_home_stop_y(int axis)
{
return 1;
}
// check home sensor
int hit_home_stop_z(int axis)
{
return 1;
}
// Start stepper again from idle state, starts the step timer at a default rate
void st_wake_up()
{
if ( ! running )
{
running = 1;
set_step_timer(2000);
// printf("wake_up()..\n");
}
}
// When not stepping, go to idle mode. Steppers can be switched off, or set to reduced current
// (some delay might have to be implemented). Currently no motor switchoff is done.
static void st_go_idle()
{
timer.detach();
running = 0;
clear_all_step_pins();
*laser = LASEROFF;
pwm = cfg->pwmmax / 100.0; // set pwm to max;
// printf("idle()..\n");
}
// return number of steps to perform: n = (v^2) / (2*a)
// alpha is a adjustment factor for ?? (alsways 1! in this source)
static inline int32_t calc_n (float speed, float alpha, float accel)
{
return speed * speed / (2.0 * alpha * accel);
}
// Initializes the trapezoid generator from the current block. Called whenever a new
// block begins. Calculates the length ofc the block (in events), step rate, slopes and trigger positions (when to accel, decel, etc.)
static inline void trapezoid_generator_reset()
{
tFixedPt c0;
#define alpha (1.0)
// float alpha = 1.0;
float accel;
int32_t accel_until;
int32_t decel_after;
accel = current_block->rate_delta*ACCELERATION_TICKS_PER_SECOND / 60.0;
c0 = (float)STEP_TIMER_FREQ * sqrt (2.0*alpha/accel);
n = calc_n (current_block->initial_rate/60.0, alpha, accel);
if (n==0)
{
n = 1;
c = c0*0.676;
}
else
{
c = c0 * (sqrt(n+1.0)-sqrt((float)n));
}
ramp = RAMP_UP;
accel_until = calc_n (current_block->nominal_rate/60.0, alpha, accel);
c_min = c0 * (sqrt(accel_until+1.0)-sqrt((float)accel_until));
accel_until = accel_until - n;
decel_n = - calc_n (current_block->nominal_rate/60.0, alpha, accel);
decel_after = current_block->step_event_count + decel_n + calc_n (current_block->final_rate/60.0, alpha, accel);
if (decel_after < accel_until)
{
decel_after = (decel_after + accel_until) / 2;
decel_n = decel_after - current_block->step_event_count - calc_n (current_block->final_rate/60.0, alpha, accel);
}
current_block->decelerate_after = decel_after;
c = to_fixed(c);
c_min = to_fixed (c_min);
#undef alpha
}
// get step rate (steps/min) from time cycles
//static inline uint32_t get_step_rate (uint64_t cycles)
//{
// return (TICKS_PER_MICROSECOND*1000000*6) / cycles * 10;
//}
// Set the step timer. Note: this starts the ticker at an interval of "cycles"
static inline void set_step_timer (uint32_t cycles)
{
volatile static double p;
timer.attach_us(&st_interrupt,cycles);
// p = to_double(pwmofs + mul_f( pwmscale, ((power>>6) * c_min) / ((10000>>6)*cycles) ) );
// p = ( to_double(c_min) * current_block->power) / ( 10000.0 * (double)cycles);
// p = (60E6/nominal_rate) / cycles; // nom_rate is steps/minute,
//printf("%f,%f,%f\n\r", (float)(60E6/nominal_rate), (float)cycles, (float)p);
// printf("%d: %f %f\n\r", (int)current_block->power, (float)p, (float)c_min/(float(c) ));
p = (double)(cfg->pwmmin/100.0 + ((current_block->power/10000.0)*((cfg->pwmmax - cfg->pwmmin)/100.0)));
pwm = p;
}
// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is executed at the rate set with
// set_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.
static void st_interrupt (void)
{
// TODO: Check if the busy-flag can be eliminated by just disabeling this interrupt while we are in it
if(busy){ /*printf("busy!\n"); */ return; } // The busy-flag is used to avoid reentering this interrupt
busy = 1;
// Set the direction pins a cuple of nanoseconds before we step the steppers
//STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
// set_direction_pins (out_bits);
// Then pulse the stepping pins
//STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
// led2 = 1;
set_step_pins (step_bits ^ step_inv);
// If there is no current block, attempt to pop one from the buffer
if (current_block == NULL)
{
// Anything in the buffer?
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;
counter_e = counter_x;
counter_l = counter_x;
pos_l = 0; // reset laser bitmap counter
step_events_completed = 0;
direction_bits = current_block->direction_bits ^ direction_inv;
set_direction_pins ();
step_bits = 0;
}
else
{
st_go_idle();
}
}
// process the current block
if (current_block != NULL)
{
// this block is a bitmap engraving line, read laser on/off status from buffer
if ( current_block->options & OPT_BITMAP )
{
*laser = ! (bitmap[pos_l / 32] & (1 << (pos_l % 32)));
counter_l += bitmap_width;
// printf("%d %d %d: %d %d %c\n\r", bitmap_width, pos_l, counter_l, pos_l / 32, pos_l % 32, (*laser ? '1' : '0' ));
if (counter_l > 0)
{
counter_l -= current_block->step_event_count;
// putchar ( (*laser ? '1' : '0' ) );
pos_l++;
}
}
else
{
*laser = ( current_block->options & OPT_LASER_ON ? LASERON : LASEROFF);
}
if (current_block->action_type == AT_MOVE)
{
// Execute step displacement profile by bresenham line algorithm
step_bits = 0;
counter_x += current_block->steps_x;
if (counter_x > 0) {
actpos_x += ( (current_block->direction_bits & (1<<X_DIRECTION_BIT))? -1 : 1 );
step_bits |= (1<<X_STEP_BIT);
counter_x -= current_block->step_event_count;
}
counter_y += current_block->steps_y;
if (counter_y > 0) {
actpos_y += ( (current_block->direction_bits & (1<<Y_DIRECTION_BIT))? -1 : 1 );
step_bits |= (1<<Y_STEP_BIT);
counter_y -= current_block->step_event_count;
}
counter_z += current_block->steps_z;
if (counter_z > 0) {
actpos_z += ( (current_block->direction_bits & (1<<Z_DIRECTION_BIT))? -1 : 1 );
step_bits |= (1<<Z_STEP_BIT);
counter_z -= current_block->step_event_count;
}
counter_e += current_block->steps_e;
if (counter_e > 0) {
actpos_e += ( (current_block->direction_bits & (1<<E_DIRECTION_BIT))? -1 : 1 );
step_bits |= (1<<E_STEP_BIT);
counter_e -= current_block->step_event_count;
}
//clear_step_pins (); // clear the pins, assume that we spend enough CPU cycles in the previous statements for the steppers to react (>1usec)
step_events_completed++; // Iterate step events
// This is a homing block, keep moving until all end-stops are triggered
if (current_block->check_endstops)
{
if ( (current_block->steps_x && hit_home_stop_x (direction_bits & (1<<X_DIRECTION_BIT)) ) ||
(current_block->steps_y && hit_home_stop_y (direction_bits & (1<<Y_DIRECTION_BIT)) ) ||
(current_block->steps_z && hit_home_stop_z (direction_bits & (1<<Z_DIRECTION_BIT)) )
)
{
step_events_completed = current_block->step_event_count;
step_bits = 0;
}
}
// While in block steps, update acceleration profile
if (step_events_completed < current_block->step_event_count)
{
tFixedPt new_c;
switch (ramp)
{
case RAMP_UP:
{
new_c = c - (c<<1) / (4*n+1);
if (step_events_completed >= current_block->decelerate_after)
{
ramp = RAMP_DOWN;
n = decel_n;
}
else if (new_c <= c_min)
{
new_c = c_min;
ramp = RAMP_MAX;
}
if (to_int(new_c) != to_int(c))
{
set_step_timer (to_int(new_c));
}
c = new_c;
}
break;
case RAMP_MAX:
if (step_events_completed >= current_block->decelerate_after)
{
ramp = RAMP_DOWN;
n = decel_n;
}
break;
case RAMP_DOWN:
new_c = c - (c<<1) / (4*n+1);
if (to_int(new_c) != to_int(c))
{
set_step_timer (to_int(c));
}
c = new_c;
break;
}
n++;
} else {
// If current block is finished, reset pointer
current_block = NULL;
plan_discard_current_block();
}
}
}
else
{
// Still no block? Set the stepper pins to low before sleeping.
// printf("block == NULL\n");
step_bits = 0;
}
clear_all_step_pins (); // clear the pins, assume that we spend enough CPU cycles in the previous statements for the steppers to react (>1usec)
busy=0;
}
// Block until all buffered steps are executed
void st_synchronize()
{
while(plan_get_current_block()) { sleep_mode(); }
}