From e74587a1b4fea9f90a54d3293bbd98a4d58a4913 Mon Sep 17 00:00:00 2001 From: midopple Date: Sun, 5 Feb 2012 19:58:36 +0100 Subject: - Implement Plannercode from Marlin V1 --> big thanks to Erik - Stepper interrupt with Step loops - Stepperfrequenz 30 Khz - New Command * M202 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec * M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) im mm/sec^2 * M205 - advanced settings: minimum travel speed S=while printing T=travel only, X= maximum xy jerk, Z=maximum Z jerk - Remove unused Variables - Check Uart Puffer while circle processing (CMD: G2 / G3) - Fast Xfer Function --> move Text to Flash - Option to deaktivate ARC (G2/G3) function (save flash) - Removed modulo (%) operator, which uses an expensive divide --- Sprinter/Configuration.h | 68 ++- Sprinter/Sprinter.h | 31 +- Sprinter/Sprinter.pde | 1301 +++++++++++++++++++++++++++++----------------- 3 files changed, 905 insertions(+), 495 deletions(-) diff --git a/Sprinter/Configuration.h b/Sprinter/Configuration.h index 25b3be6..86b7115 100644 --- a/Sprinter/Configuration.h +++ b/Sprinter/Configuration.h @@ -56,8 +56,13 @@ const bool Z_ENDSTOP_INVERT = false; // Uncomment to make run init.g from SD on boot //#define SDINITFILE +//Only work with Atmega1284 you need +1 kb ram //#define SD_FAST_XFER_AKTIV +//Uncomment to aktivate the arc (circle) function (G2/G3 Command) +//Without SD function an ARC function the used Flash is smaller 31 kb +#define USE_ARC_FUNCTION + //----------------------------------------------------------------------- //// ADVANCED SETTINGS - to tweak parameters //----------------------------------------------------------------------- @@ -69,8 +74,9 @@ const bool Z_ENDSTOP_INVERT = false; #endif #endif - +//----------------------------------------------------------------------- // For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 +//----------------------------------------------------------------------- #define X_ENABLE_ON 0 #define Y_ENABLE_ON 0 #define Z_ENABLE_ON 0 @@ -79,40 +85,57 @@ const bool Z_ENDSTOP_INVERT = false; //Uncomment if you have problems with a stepper driver enabeling too late, this will also set how many microseconds delay there will be after enabeling the driver //#define DELAY_ENABLE 15 +//----------------------------------------------------------------------- // Disables axis when it's not being used. +//----------------------------------------------------------------------- const bool DISABLE_X = false; const bool DISABLE_Y = false; const bool DISABLE_Z = true; const bool DISABLE_E = false; +//----------------------------------------------------------------------- // Inverting axis direction +//----------------------------------------------------------------------- const bool INVERT_X_DIR = false; const bool INVERT_Y_DIR = false; const bool INVERT_Z_DIR = true; const bool INVERT_E_DIR = false; - +//----------------------------------------------------------------------- //// ENDSTOP SETTINGS: +//----------------------------------------------------------------------- // Sets direction of endstops when homing; 1=MAX, -1=MIN #define X_HOME_DIR -1 #define Y_HOME_DIR -1 #define Z_HOME_DIR -1 +//#define ENDSTOPS_ONLY_FOR_HOMING // If defined the endstops will only be used for homing const bool min_software_endstops = false; //If true, axis won't move to coordinates less than zero. const bool max_software_endstops = true; //If true, axis won't move to coordinates greater than the defined lengths below. + +//----------------------------------------------------------------------- //Max Length for Prusa Mendel, check the ways of your axis and set this Values +//----------------------------------------------------------------------- const int X_MAX_LENGTH = 200; const int Y_MAX_LENGTH = 200; const int Z_MAX_LENGTH = 100; +//----------------------------------------------------------------------- //// MOVEMENT SETTINGS +//----------------------------------------------------------------------- const int NUM_AXIS = 4; // The axis order in all axis related arrays is X, Y, Z, E -#define _MAX_FEEDRATE {200000, 200000, 240, 500000} -#define _HOMING_FEEDRATE {1500,1500,120} +#define _MAX_FEEDRATE {400, 400, 4, 45} // (mm/sec) +#define _HOMING_FEEDRATE {1500,1500,120} // (mm/min) !! #define _AXIS_RELATIVE_MODES {false, false, false, false} +#define MAX_STEP_FREQUENCY 30000 // Max step frequency + + +//----------------------------------------------------------------------- +//// Not used at the Moment +//----------------------------------------------------------------------- // Min step delay in microseconds. If you are experiencing missing steps, try to raise the delay microseconds, but be aware this // If you enable this, make sure STEP_DELAY_RATIO is disabled. @@ -128,23 +151,35 @@ const int NUM_AXIS = 4; // The axis order in all axis related arrays is X, Y, Z, long min_time_before_dir_change = 30; //milliseconds #endif -// Comment this to disable ramp acceleration -#define RAMP_ACCELERATION - +//----------------------------------------------------------------------- //// Acceleration settings -#ifdef RAMP_ACCELERATION +//----------------------------------------------------------------------- // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot. -#define _ACCELERATION 2000 // Normal acceleration mm/s^2 -#define _RETRACT_ACCELERATION 7000 // Normal acceleration mm/s^2 -#define _MAX_XY_JERK (20.0*60) -#define _MAX_Z_JERK (0.4*60) -#define _MAX_START_SPEED_UNITS_PER_SECOND {25.0,25.0,0.2,10.0} -#define _MAX_ACCELERATION_UNITS_PER_SQ_SECOND {500,500,50,500} // X, Y, Z and E max acceleration in mm/s^2 for printing moves or retracts -#define _MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND {500,500,50,500} // X, Y, Z max acceleration in mm/s^2 for travel moves -#endif +#define _ACCELERATION 1000 // Axis Normal acceleration mm/s^2 +#define _RETRACT_ACCELERATION 2000 // Extruder Normal acceleration mm/s^2 +#define _MAX_XY_JERK 20.0 +#define _MAX_Z_JERK 0.4 +//#define _MAX_START_SPEED_UNITS_PER_SECOND {25.0,25.0,0.2,10.0} +#define _MAX_ACCELERATION_UNITS_PER_SQ_SECOND {5000,5000,50,5000} // X, Y, Z and E max acceleration in mm/s^2 for printing moves or retracts +// 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 2.0 // (mm/sec) + +#define DEFAULT_MINIMUMFEEDRATE 0.0 // minimum feedrate +#define DEFAULT_MINTRAVELFEEDRATE 0.0 + +// If defined the movements slow down when the look ahead buffer is only half full +#define SLOWDOWN + + +const int dropsegments=5; //everything with less than this number of steps will be ignored as move and joined with the next movement + +//----------------------------------------------------------------------- // Machine UUID +//----------------------------------------------------------------------- // This may be useful if you have multiple machines and wish to identify them by using the M115 command. // By default we set it to zeros. #define _DEF_CHAR_UUID "00000000-0000-0000-0000-000000000000" @@ -290,7 +325,6 @@ long min_time_before_dir_change = 30; //milliseconds //#define DEBUG #ifdef DEBUG //#define DEBUG_PREPARE_MOVE //Enable this to debug prepare_move() function - //#define DEBUG_RAMP_ACCELERATION //Enable this to debug all constant acceleration info //#define DEBUG_MOVE_TIME //Enable this to time each move and print the result //#define DEBUG_HEAT_MGMT //Enable this to debug heat management. WARNING, this will cause axes to jitter! //#define DEBUG_DISABLE_CHECK_DURING_TRAVEL //Debug the namesake feature, see above in this file diff --git a/Sprinter/Sprinter.h b/Sprinter/Sprinter.h index 844fc34..b70d1fd 100644 --- a/Sprinter/Sprinter.h +++ b/Sprinter/Sprinter.h @@ -1,10 +1,18 @@ // Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. // Licence: GPL -#include + +//Check Version of Arduino and then include the right libraries +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include +#endif + #include "fastio.h" extern "C" void __cxa_pure_virtual(); +#define FORCE_INLINE __attribute__((always_inline)) inline #if X_ENABLE_PIN > -1 #define enable_x() WRITE(X_ENABLE_PIN, X_ENABLE_ON) @@ -47,10 +55,10 @@ typedef struct { // Fields used by the bresenham algorithm for tracing the line long steps_x, steps_y, steps_z, steps_e; // Step count along each axis - long step_event_count; // The number of step events required to complete this block - volatile long accelerate_until; // The index of the step event on which to stop acceleration - volatile long decelerate_after; // The index of the step event on which to start decelerating - volatile long acceleration_rate; // The acceleration rate used for acceleration calculation + unsigned long step_event_count; // The number of step events required to complete this block + long accelerate_until; // The index of the step event on which to stop acceleration + long decelerate_after; // The index of the step event on which to start decelerating + long acceleration_rate; // The acceleration rate used for acceleration calculation unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) #ifdef ADVANCE @@ -61,16 +69,20 @@ typedef struct { #endif // Fields used by the motion planner to manage acceleration - float speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis +// float speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis float nominal_speed; // The nominal speed for this block in mm/min + float entry_speed; // Entry speed at previous-current junction in mm/min + float max_entry_speed; // Maximum allowable junction entry speed in mm/min float millimeters; // The total travel of this block in mm - float entry_speed; float acceleration; // acceleration mm/sec^2 + unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction + unsigned char nominal_length_flag; // Planner flag for nominal speed always reached + // Settings for the trapezoid generator long nominal_rate; // The nominal step rate for this block in step_events/sec - volatile long initial_rate; // The jerk-adjusted step rate at start of block - volatile long final_rate; // The minimal rate at exit + long initial_rate; // The jerk-adjusted step rate at start of block + long final_rate; // The minimal rate at exit long acceleration_st; // acceleration steps/sec^2 volatile char busy; } block_t; @@ -87,7 +99,6 @@ void manage_inactivity(byte debug); void get_coordinates(); void prepare_move(); void prepare_arc_move(char isclockwise); -void plan_buffer_line(float x, float y, float z, float e, float feed_rate); void kill(byte debug); diff --git a/Sprinter/Sprinter.pde b/Sprinter/Sprinter.pde index 8cfe691..d33480f 100644 --- a/Sprinter/Sprinter.pde +++ b/Sprinter/Sprinter.pde @@ -1,6 +1,6 @@ /* Reprap firmware based on Sprinter - Optimize for Sanguinololu 1.2 and above + Optimize for Sanguinololu 1.2 and above, RAMPS This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,8 +27,7 @@ Parts of Marlin Firmware from ErikZalm https://github.com/ErikZalm/Marlin-non-gen6 - Sprinter V2 - + Sprinter Changelog - Look forward function --> calculate 16 Steps forward, get from Firmaware Marlin and Grbl - Stepper control with Timer 1 (Interrupt) - Extruder heating with PID use a Softpwm (Timer 2) with 500 hz to free Timer1 für Steppercontrol @@ -40,7 +39,19 @@ - move string to flash to free RAM vor forward planner - M203 Temperature monitor for Repetier - + + - Implement Plannercode from Marlin V1 big thanks to Erik + - Stepper interrupt with Step loops + - Stepperfrequenz 30 Khz + - New Command + * M202 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec + * M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) im mm/sec^2 + * M205 - advanced settings: minimum travel speed S=while printing T=travel only, X= maximum xy jerk, Z=maximum Z jerk + - Remove unused Variables + - Check Uart Puffer while circle processing (CMD: G2 / G3) + - Fast Xfer Function --> move Text to Flash + - Option to deaktivate ARC (G2/G3) function (save flash) + - Removed modulo (%) operator, which uses an expensive divide */ @@ -52,7 +63,9 @@ #include "pins.h" #include "Sprinter.h" #include "speed_lookuptable.h" -#include "arc_func.h" +#ifdef USE_ARC_FUNCTION + #include "arc_func.h" +#endif #include "heater.h" #ifdef SDSUPPORT @@ -115,9 +128,12 @@ void __cxa_pure_virtual(){}; // M119 - Show Endstopper State // M140 - Set bed target temp // M190 - Wait for bed current temp to reach target temp. -// M201 - Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000) -// M202 - Set max acceleration in units/s^2 for travel moves (M202 X1000 Y1000) +// M201 - Set maximum acceleration in units/s^2 for print moves (M201 X1000 Y1000) +// M202 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec // M203 - Set temperture monitor to Sx +// M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) im mm/sec^2 +// M205 - advanced settings: minimum travel speed S=while printing T=travel only, X= maximum xy jerk, Z=maximum Z jerk + // M220 - set speed factor override percentage S:factor in percent // Debug feature / Testing the PID for Hotend @@ -126,7 +142,7 @@ void __cxa_pure_virtual(){}; // M603 - Show Free Ram -#define _VERSION_TEXT "1.2.20T / 27.01.2012" +#define _VERSION_TEXT "1.3.04T / 04.02.2012" //Stepper Movement Variables char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'}; @@ -136,52 +152,61 @@ float max_feedrate[] = _MAX_FEEDRATE; float homing_feedrate[] = _HOMING_FEEDRATE; bool axis_relative_modes[] = _AXIS_RELATIVE_MODES; -bool move_direction[NUM_AXIS]; -unsigned long axis_previous_micros[NUM_AXIS]; -unsigned long previous_micros = 0; -unsigned long move_steps_to_take[NUM_AXIS]; - -#ifdef RAMP_ACCELERATION - float acceleration = _ACCELERATION; // Normal acceleration mm/s^2 - float retract_acceleration = _RETRACT_ACCELERATION; // Normal acceleration mm/s^2 - float max_xy_jerk = _MAX_XY_JERK; - float max_z_jerk = _MAX_Z_JERK; - float max_start_speed_units_per_second[] = _MAX_START_SPEED_UNITS_PER_SECOND; - long max_acceleration_units_per_sq_second[] = _MAX_ACCELERATION_UNITS_PER_SQ_SECOND; // X, Y, Z and E max acceleration in mm/s^2 for printing moves or retracts - long max_travel_acceleration_units_per_sq_second[] = _MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND; // X, Y, Z max acceleration in mm/s^2 for travel moves - - unsigned long axis_max_interval[NUM_AXIS]; - unsigned long axis_steps_per_sqr_second[NUM_AXIS]; - unsigned long axis_travel_steps_per_sqr_second[NUM_AXIS]; - unsigned long max_interval; - unsigned long steps_per_sqr_second, plateau_steps; -#endif +float move_acceleration = _ACCELERATION; // Normal acceleration mm/s^2 +float retract_acceleration = _RETRACT_ACCELERATION; // Normal acceleration mm/s^2 +float max_xy_jerk = _MAX_XY_JERK; +float max_z_jerk = _MAX_Z_JERK; + +long max_acceleration_units_per_sq_second[] = _MAX_ACCELERATION_UNITS_PER_SQ_SECOND; // X, Y, Z and E max acceleration in mm/s^2 for printing moves or retracts + +//float max_start_speed_units_per_second[] = _MAX_START_SPEED_UNITS_PER_SECOND; +//long max_travel_acceleration_units_per_sq_second[] = _MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND; // X, Y, Z max acceleration in mm/s^2 for travel moves + +float mintravelfeedrate = DEFAULT_MINTRAVELFEEDRATE; +float minimumfeedrate = DEFAULT_MINIMUMFEEDRATE; + +unsigned long axis_steps_per_sqr_second[NUM_AXIS]; +unsigned long plateau_steps; + +//unsigned long axis_max_interval[NUM_AXIS]; +//unsigned long axis_travel_steps_per_sqr_second[NUM_AXIS]; +//unsigned long max_interval; +//unsigned long steps_per_sqr_second; + //adjustable feed faktor for online tuning printerspeed volatile int feedmultiply=100; //100->original / 200-> Faktor 2 / 50 -> Faktor 0.5 int saved_feedmultiply; volatile bool feedmultiplychanged=false; -boolean acceleration_enabled = false, accelerating = false; -unsigned long interval; +//boolean acceleration_enabled = false, accelerating = false; +//unsigned long interval; float destination[NUM_AXIS] = {0.0, 0.0, 0.0, 0.0}; float current_position[NUM_AXIS] = {0.0, 0.0, 0.0, 0.0}; -unsigned long steps_taken[NUM_AXIS]; -long axis_interval[NUM_AXIS]; // for speed delay + + bool home_all_axis = true; +//unsigned ?? ToDo: Check int feedrate = 1500, next_feedrate, saved_feedrate; -float time_for_move; + long gcode_N, gcode_LastN; bool relative_mode = false; //Determines Absolute or Relative Coordinates -bool relative_mode_e = false; //Determines Absolute or Relative E Codes while in Absolute Coordinates mode. E is always relative in Relative Coordinates mode. -long timediff = 0; + +//unsigned long steps_taken[NUM_AXIS]; +//long axis_interval[NUM_AXIS]; // for speed delay +//float time_for_move; +//bool relative_mode_e = false; //Determines Absolute or Relative E Codes while in Absolute Coordinates mode. E is always relative in Relative Coordinates mode. +//long timediff = 0; //experimental feedrate calc //float d = 0; //float axis_diff[NUM_AXIS] = {0, 0, 0, 0}; + +#ifdef USE_ARC_FUNCTION //For arc centerpont, send bei Command G2/G3 float offset[3] = {0.0, 0.0, 0.0}; +#endif #ifdef STEP_DELAY_RATIO long long_step_delay_ratio = STEP_DELAY_RATIO * 100; @@ -195,9 +220,9 @@ float offset[3] = {0.0, 0.0, 0.0}; #endif // comm variables and Commandbuffer -// BUFSIZE is reduced from 8 to 5 to free more RAM for the PLANNER +// BUFSIZE is reduced from 8 to 6 to free more RAM for the PLANNER #define MAX_CMD_SIZE 96 -#define BUFSIZE 5 //8 +#define BUFSIZE 6 //8 char cmdbuffer[BUFSIZE][MAX_CMD_SIZE]; bool fromsd[BUFSIZE]; @@ -208,9 +233,9 @@ bool fromsd[BUFSIZE]; long xferbytes; #endif -int bufindr = 0; -int bufindw = 0; -int buflen = 0; +unsigned char bufindr = 0; +unsigned char bufindw = 0; +unsigned char buflen = 0; char serial_char; int serial_count = 0; boolean comment_mode = false; @@ -276,6 +301,11 @@ unsigned char manage_monitor = 255; } #ifdef SD_FAST_XFER_AKTIV + + #ifdef PIDTEMP + extern int g_heater_pwm_val; + #endif + void fast_xfer() { char *pstr; @@ -285,6 +315,8 @@ unsigned char manage_monitor = 255; if(HEATER_0_PIN > -1) WRITE(HEATER_0_PIN,LOW); if(HEATER_1_PIN > -1) WRITE(HEATER_1_PIN,LOW); + g_heater_pwm_val = 0; + lastxferchar = 1; xferbytes = 0; @@ -292,7 +324,7 @@ unsigned char manage_monitor = 255; if(pstr == NULL) { - Serial.println("invalid command"); + showString(PSTR("invalid command\r\n")); return; } @@ -301,24 +333,24 @@ unsigned char manage_monitor = 255; //check mode (currently only RAW is supported if(strcmp(strchr_pointer+4, "RAW") != 0) { - Serial.println("Invalid transfer codec"); + showString(PSTR("Invalid transfer codec\r\n")); return; }else{ - Serial.print("Selected codec: "); + showString(PSTR("Selected codec: ")); Serial.println(strchr_pointer+4); } if (!file.open(&root, pstr+1, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) { - Serial.print("open failed, File: "); + showString(PSTR("open failed, File: ")); Serial.print(pstr+1); - Serial.print("."); + showString(PSTR(".")); }else{ - Serial.print("Writing to file: "); + showString(PSTR("Writing to file: ")); Serial.println(pstr+1); } - Serial.println("ok"); + showString(PSTR("ok\r\n")); //RAW transfer codec //Host sends \0 then up to SD_FAST_XFER_CHUNK_SIZE then \0 @@ -331,15 +363,14 @@ unsigned char manage_monitor = 255; while(!Serial.available()) { } - if(Serial.peek() != 0) + if(Serial.read() != 0) { //host has failed, this isn't a RAW chunk, it's an actual command file.sync(); file.close(); return; } - //clear the initial 0 - Serial.read(); + for(int i=0;i- set speed factor override percentage { if(code_seen('S')) @@ -1563,7 +1633,7 @@ void ClearToSend() //Serial.println("ok"); } -inline void get_coordinates() +FORCE_INLINE void get_coordinates() { for(int i=0; i < NUM_AXIS; i++) { @@ -1578,12 +1648,14 @@ inline void get_coordinates() } } -inline void get_arc_coordinates() +#ifdef USE_ARC_FUNCTION +FORCE_INLINE void get_arc_coordinates() { get_coordinates(); if(code_seen('I')) offset[0] = code_value(); if(code_seen('J')) offset[1] = code_value(); } +#endif void prepare_move() @@ -1614,7 +1686,7 @@ void prepare_move() } - +#ifdef USE_ARC_FUNCTION void prepare_arc_move(char isclockwise) { @@ -1634,9 +1706,9 @@ void prepare_arc_move(char isclockwise) current_position[i] = destination[i]; } } +#endif - -inline void kill() +FORCE_INLINE void kill() { #if TEMP_0_PIN > -1 target_raw=0; @@ -1657,7 +1729,7 @@ inline void kill() } -inline void manage_inactivity(byte debug) +FORCE_INLINE void manage_inactivity(byte debug) { if( (millis()-previous_millis_cmd) > max_inactive_time ) if(max_inactive_time) kill(); @@ -1712,19 +1784,43 @@ static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for static volatile unsigned char block_buffer_head; // Index of the next block to be pushed static volatile unsigned char block_buffer_tail; // Index of the block to process now +//=========================================================================== +//=============================private variables ============================ +//=========================================================================== + +// Returns the index of the next block in the ring buffer +// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. +static int8_t next_block_index(int8_t block_index) { + block_index++; + if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } + return(block_index); +} + + +// Returns the index of the previous block in the ring buffer +static int8_t prev_block_index(int8_t block_index) { + if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } + block_index--; + return(block_index); +} + // The current position of the tool in absolute steps static long position[4]; +static float previous_speed[4]; // Speed of previous path line segment +static float previous_nominal_speed; // Nominal speed of previous path line segment -#define ONE_MINUTE_OF_MICROSECONDS 60000000.0 // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the // given acceleration: -inline long estimate_acceleration_distance(long initial_rate, long target_rate, long acceleration) +FORCE_INLINE float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) { - return( - (target_rate*target_rate-initial_rate*initial_rate)/ - (2L*acceleration) - ); + if (acceleration!=0) { + return((target_rate*target_rate-initial_rate*initial_rate)/ + (2.0*acceleration)); + } + else { + return 0.0; // acceleration was 0, set acceleration distance to 0 + } } // This function gives you the point at which you must start braking (at the rate of -acceleration) if @@ -1732,166 +1828,143 @@ inline long estimate_acceleration_distance(long initial_rate, long target_rate, // a total travel of distance. This can be used to compute the intersection point between acceleration and // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) -inline long intersection_distance(long initial_rate, long final_rate, long acceleration, long distance) +FORCE_INLINE float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) { - return( - (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/ - (4*acceleration) - ); + if (acceleration!=0) { + return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/ + (4.0*acceleration) ); + } + else { + return 0.0; // acceleration was 0, set intersection distance to 0 + } } // Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. -void calculate_trapezoid_for_block(block_t *block, float entry_speed, float exit_speed) -{ - if(block->busy == true) return; // If block is busy then bail out. - float entry_factor = entry_speed / block->nominal_speed; - float exit_factor = exit_speed / block->nominal_speed; - long initial_rate = ceil(block->nominal_rate*entry_factor); - long final_rate = ceil(block->nominal_rate*exit_factor); -#ifdef ADVANCE - long initial_advance = block->advance*entry_factor*entry_factor; - long final_advance = block->advance*exit_factor*exit_factor; -#endif // ADVANCE +void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) { + unsigned long initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) + unsigned long final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) // Limit minimal step rate (Otherwise the timer will overflow.) - if(initial_rate <120) initial_rate=120; - if(final_rate < 120) final_rate=120; + if(initial_rate <120) {initial_rate=120; } + if(final_rate < 120) {final_rate=120; } - // Calculate the acceleration steps long acceleration = block->acceleration_st; - long accelerate_steps = estimate_acceleration_distance(initial_rate, block->nominal_rate, acceleration); - long decelerate_steps = estimate_acceleration_distance(final_rate, block->nominal_rate, acceleration); - // Calculate the size of Plateau of Nominal Rate. - long plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; - + int32_t accelerate_steps = + ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration)); + int32_t decelerate_steps = + floor(estimate_acceleration_distance(block->nominal_rate, block->final_rate, -acceleration)); + + // Calculate the size of Plateau of Nominal Rate. + int32_t plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; + // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will - // have to use intersection_distance() to calculate when to abort acceleration and start braking + // have to use intersection_distance() to calculate when to abort acceleration and start braking // in order to reach the final_rate exactly at the end of this block. - if (plateau_steps < 0) { - accelerate_steps = intersection_distance(initial_rate, final_rate, acceleration, block->step_event_count); + if (plateau_steps < 0) { + accelerate_steps = ceil( + intersection_distance(block->initial_rate, block->final_rate, acceleration, block->step_event_count)); + accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off + accelerate_steps = min(accelerate_steps,block->step_event_count); plateau_steps = 0; - } - - long decelerate_after = accelerate_steps+plateau_steps; - long acceleration_rate = (long)((float)acceleration * 8.388608); + } + #ifdef ADVANCE + volatile long initial_advance = block->advance*entry_factor*entry_factor; + volatile long final_advance = block->advance*exit_factor*exit_factor; + #endif // ADVANCE + + // block->accelerate_until = accelerate_steps; + // block->decelerate_after = accelerate_steps+plateau_steps; CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section if(block->busy == false) { // Don't update variables if block is busy. block->accelerate_until = accelerate_steps; - block->decelerate_after = decelerate_after; - block->acceleration_rate = acceleration_rate; + block->decelerate_after = accelerate_steps+plateau_steps; block->initial_rate = initial_rate; block->final_rate = final_rate; -#ifdef ADVANCE - block->initial_advance = initial_advance; - block->final_advance = final_advance; -#endif ADVANCE + #ifdef ADVANCE + block->initial_advance = initial_advance; + block->final_advance = final_advance; + #endif //ADVANCE } CRITICAL_SECTION_END; } // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the // acceleration within the allotted distance. -inline float max_allowable_speed(float acceleration, float target_velocity, float distance) { - return( - sqrt(target_velocity*target_velocity-2*acceleration*60*60*distance) - ); +FORCE_INLINE float max_allowable_speed(float acceleration, float target_velocity, float distance) { + return sqrt(target_velocity*target_velocity-2*acceleration*distance); } // "Junction jerk" in this context is the immediate change in speed at the junction of two blocks. // This method will calculate the junction jerk as the euclidean distance between the nominal // velocities of the respective blocks. -inline float junction_jerk(block_t *before, block_t *after) { - return(sqrt( - pow((before->speed_x-after->speed_x), 2)+ - pow((before->speed_y-after->speed_y), 2))); -} +//inline float junction_jerk(block_t *before, block_t *after) { +// return sqrt( +// pow((before->speed_x-after->speed_x), 2)+pow((before->speed_y-after->speed_y), 2)); +//} + -// Return the safe speed which is max_jerk/2, e.g. the -// speed under which you cannot exceed max_jerk no matter what you do. -float safe_speed(block_t *block) { - float safe_speed; - safe_speed = max_xy_jerk/2; - if(abs(block->speed_z) > max_z_jerk/2) safe_speed = max_z_jerk/2; - if (safe_speed > block->nominal_speed) safe_speed = block->nominal_speed; - return safe_speed; -} // The kernel called by planner_recalculate() when scanning the plan from last to first entry. void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { - if(!current) { - return; - } - - float entry_speed = current->nominal_speed; - float exit_factor; - float exit_speed; - if (next) { - exit_speed = next->entry_speed; - } - else { - exit_speed = safe_speed(current); - } - - // Calculate the entry_factor for the current block. - if (previous) { - // Reduce speed so that junction_jerk is within the maximum allowed - float jerk = junction_jerk(previous, current); - if((previous->steps_x == 0) && (previous->steps_y == 0)) { - entry_speed = safe_speed(current); - } - else if (jerk > max_xy_jerk) { - entry_speed = (max_xy_jerk/jerk) * entry_speed; - } - if(abs(previous->speed_z - current->speed_z) > max_z_jerk) { - entry_speed = (max_z_jerk/abs(previous->speed_z - current->speed_z)) * entry_speed; - } - // If the required deceleration across the block is too rapid, reduce the entry_factor accordingly. - if (entry_speed > exit_speed) { - float max_entry_speed = max_allowable_speed(-current->acceleration,exit_speed, current->millimeters); - if (max_entry_speed < entry_speed) { - entry_speed = max_entry_speed; + if(!current) { return; } + + if (next) { + // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. + // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and + // check for maximum allowable speed reductions to ensure maximum possible planned speed. + if (current->entry_speed != current->max_entry_speed) { + + // If nominal length true, max junction speed is guaranteed to be reached. Only compute + // for max allowable speed if block is decelerating and nominal length is false. + if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) { + current->entry_speed = min( current->max_entry_speed, + max_allowable_speed(-current->acceleration,next->entry_speed,current->millimeters)); + } else { + current->entry_speed = current->max_entry_speed; } - } - } - else { - entry_speed = safe_speed(current); - } - // Store result - current->entry_speed = entry_speed; + current->recalculate_flag = true; + + } + } // Skip last block. Already initialized and set for recalculation. } // planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This // implements the reverse pass. void planner_reverse_pass() { - char block_index = block_buffer_head; - block_t *block[3] = { NULL, NULL, NULL }; - while(block_index != block_buffer_tail) { - block_index--; - if(block_index < 0) block_index = BLOCK_BUFFER_SIZE-1; - block[2]= block[1]; - block[1]= block[0]; - block[0] = &block_buffer[block_index]; - planner_reverse_pass_kernel(block[0], block[1], block[2]); - } - planner_reverse_pass_kernel(NULL, block[0], block[1]); + uint8_t block_index = block_buffer_head; + if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) { + block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1); + block_t *block[3] = { NULL, NULL, NULL }; + while(block_index != block_buffer_tail) { + block_index = prev_block_index(block_index); + block[2]= block[1]; + block[1]= block[0]; + block[0] = &block_buffer[block_index]; + planner_reverse_pass_kernel(block[0], block[1], block[2]); + } + } } + // The kernel called by planner_recalculate() when scanning the plan from first to last entry. void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { - if(!current) { - return; - } - if(previous) { - // If the previous block is an acceleration block, but it is not long enough to - // complete the full speed change within the block, we need to adjust out entry - // speed accordingly. Remember current->entry_factor equals the exit factor of - // the previous block. - if(previous->entry_speed < current->entry_speed) { - float max_entry_speed = max_allowable_speed(-previous->acceleration, previous->entry_speed, previous->millimeters); - if (max_entry_speed < current->entry_speed) { - current->entry_speed = max_entry_speed; + if(!previous) { return; } + + // If the previous block is an acceleration block, but it is not long enough to complete the + // full speed change within the block, we need to adjust the entry speed accordingly. Entry + // speeds have already been reset, maximized, and reverse planned by reverse planner. + // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. + if (!previous->nominal_length_flag) { + if (previous->entry_speed < current->entry_speed) { + double entry_speed = min( current->entry_speed, + max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->millimeters) ); + + // Check for junction speed change + if (current->entry_speed != entry_speed) { + current->entry_speed = entry_speed; + current->recalculate_flag = true; } } } @@ -1900,16 +1973,15 @@ void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *n // planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This // implements the forward pass. void planner_forward_pass() { - char block_index = block_buffer_tail; - block_t *block[3] = { - NULL, NULL, NULL }; + uint8_t block_index = block_buffer_tail; + block_t *block[3] = { NULL, NULL, NULL }; while(block_index != block_buffer_head) { block[0] = block[1]; block[1] = block[2]; block[2] = &block_buffer[block_index]; planner_forward_pass_kernel(block[0],block[1],block[2]); - block_index = (block_index+1) & BLOCK_BUFFER_MASK; + block_index = next_block_index(block_index); } planner_forward_pass_kernel(block[1], block[2], NULL); } @@ -1918,18 +1990,30 @@ void planner_forward_pass() { // entry_factor for each junction. Must be called by planner_recalculate() after // updating the blocks. void planner_recalculate_trapezoids() { - char block_index = block_buffer_tail; + int8_t block_index = block_buffer_tail; block_t *current; block_t *next = NULL; + while(block_index != block_buffer_head) { current = next; next = &block_buffer[block_index]; if (current) { - calculate_trapezoid_for_block(current, current->entry_speed, next->entry_speed); + // Recalculate if current block entry or exit junction speed has changed. + if (current->recalculate_flag || next->recalculate_flag) { + // NOTE: Entry and exit factors always > 0 by all previous logic operations. + calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_speed, + next->entry_speed/current->nominal_speed); + current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed + } } - block_index = (block_index+1) & BLOCK_BUFFER_MASK; + block_index = next_block_index( block_index ); + } + // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + if(next != NULL) { + calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, + MINIMUM_PLANNER_SPEED/next->nominal_speed); + next->recalculate_flag = false; } - calculate_trapezoid_for_block(next, next->entry_speed, safe_speed(next)); } // Recalculates the motion plan according to the following algorithm: @@ -1959,16 +2043,22 @@ void plan_init() { block_buffer_head = 0; block_buffer_tail = 0; memset(position, 0, sizeof(position)); // clear position + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; + previous_nominal_speed = 0.0; } -inline void plan_discard_current_block() { + +FORCE_INLINE void plan_discard_current_block() { if (block_buffer_head != block_buffer_tail) { block_buffer_tail = (block_buffer_tail + 1) & BLOCK_BUFFER_MASK; } } -inline block_t *plan_get_current_block() { +FORCE_INLINE block_t *plan_get_current_block() { if (block_buffer_head == block_buffer_tail) { return(NULL); } @@ -1977,6 +2067,16 @@ inline block_t *plan_get_current_block() { return(block); } +// Gets the current block. Returns NULL if buffer empty +FORCE_INLINE bool blocks_queued() +{ + if (block_buffer_head == block_buffer_tail) { + return false; + } + else + return true; +} + void check_axes_activity() { unsigned char x_active = 0; unsigned char y_active = 0; @@ -1985,14 +2085,14 @@ void check_axes_activity() { block_t *block; if(block_buffer_tail != block_buffer_head) { - char block_index = block_buffer_tail; + uint8_t block_index = block_buffer_tail; while(block_index != block_buffer_head) { block = &block_buffer[block_index]; if(block->steps_x != 0) x_active++; if(block->steps_y != 0) y_active++; if(block->steps_z != 0) z_active++; if(block->steps_e != 0) e_active++; - block_index = (block_index+1) & BLOCK_BUFFER_MASK; + block_index = (block_index+1) & (BLOCK_BUFFER_SIZE - 1); } } if((DISABLE_X) && (x_active == 0)) disable_x(); @@ -2001,21 +2101,15 @@ void check_axes_activity() { if((DISABLE_E) && (e_active == 0)) disable_e(); } + +float junction_deviation = 0.1; // Add a new linear movement to the buffer. steps_x, _y and _z is the absolute position in // mm. Microseconds specify how many microseconds the move should take to perform. To aid acceleration // calculation the caller must also provide the physical length of the line in millimeters. -void plan_buffer_line(float x, float y, float z, float e, float feed_rate) { - - // The target position of the tool in absolute steps - // Calculate target position in absolute steps - long target[4]; - target[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]); - target[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]); - target[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); - target[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); - +void plan_buffer_line(float x, float y, float z, float e, float feed_rate) +{ // Calculate the buffer head after we push this byte - int next_buffer_head = (block_buffer_head + 1) & BLOCK_BUFFER_MASK; + int next_buffer_head = next_block_index(block_buffer_head); // If the buffer is full: good! That means we are well ahead of the robot. // Rest here until there is room in the buffer. @@ -2023,12 +2117,16 @@ void plan_buffer_line(float x, float y, float z, float e, float feed_rate) { manage_heater(); manage_inactivity(1); } - - //showString(PSTR("X:")); - //Serial.print(x); - //showString(PSTR(" Y:")); - //Serial.println(y); + // The target position of the tool in absolute steps + // Calculate target position in absolute steps + //this should be done after the wait, because otherwise a M92 code within the gcode disrupts this calculation somehow + long target[4]; + target[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]); + target[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]); + target[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); + target[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + // Prepare to set up new block block_t *block = &block_buffer[block_buffer_head]; @@ -2043,11 +2141,16 @@ void plan_buffer_line(float x, float y, float z, float e, float feed_rate) { block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e))); // Bail if this is a zero-length block - if (block->step_event_count == 0) { - return; - }; - + if (block->step_event_count <=dropsegments) { return; }; + + // Compute direction bits for this block + block->direction_bits = 0; + if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<steps_x != 0) { @@ -2077,114 +2180,217 @@ void plan_buffer_line(float x, float y, float z, float e, float feed_rate) { if(block->steps_e != 0) enable_e(); #endif - float delta_x_mm = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; - float delta_y_mm = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; - float delta_z_mm = (target[Z_AXIS]-position[Z_AXIS])/axis_steps_per_unit[Z_AXIS]; - float delta_e_mm = (target[E_AXIS]-position[E_AXIS])/axis_steps_per_unit[E_AXIS]; - block->millimeters = sqrt(square(delta_x_mm) + square(delta_y_mm) + square(delta_z_mm) + square(delta_e_mm)); + float delta_mm[4]; + delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; + delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; + delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/axis_steps_per_unit[Z_AXIS]; + delta_mm[E_AXIS] = (target[E_AXIS]-position[E_AXIS])/axis_steps_per_unit[E_AXIS]; + block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) + + square(delta_mm[Z_AXIS]) + square(delta_mm[E_AXIS])); + float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides + + // Calculate speed in mm/second for each axis. No divide by zero due to previous checks. + float inverse_second = feed_rate * inverse_millimeters; + + block->nominal_speed = block->millimeters * inverse_second; // (mm/sec) Always > 0 + block->nominal_rate = ceil(block->step_event_count * inverse_second); // (step/sec) Always > 0 + + + + + if (block->steps_e == 0) { + if(feed_ratemillimeters/feed_rate)*1000000); +#ifdef SLOWDOWN + // slow down when de buffer starts to empty, rather than wait at the corner for a buffer refill + int moves_queued=(block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); - // Calculate speed in mm/minute for each axis - float multiplier = 60.0*1000000.0/microseconds; - block->speed_z = delta_z_mm * multiplier; - block->speed_x = delta_x_mm * multiplier; - block->speed_y = delta_y_mm * multiplier; - block->speed_e = delta_e_mm * multiplier; + if(moves_queued < (BLOCK_BUFFER_SIZE * 0.5) && moves_queued > 1) feed_rate = feed_rate*moves_queued / (BLOCK_BUFFER_SIZE * 0.5); +#endif + +/* + // segment time im micro seconds + long segment_time = lround(1000000.0/inverse_second); + if ((blockcount>0) && (blockcount < (BLOCK_BUFFER_SIZE - 4))) { + if (segment_timespeed_x) > max_feedrate[X_AXIS]) { - speed_factor = max_feedrate[X_AXIS] / abs(block->speed_x); - } - if(abs(block->speed_y) > max_feedrate[Y_AXIS]){ - tmp_speed_factor = max_feedrate[Y_AXIS] / abs(block->speed_y); - if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; - } - if(abs(block->speed_z) > max_feedrate[Z_AXIS]){ - tmp_speed_factor = max_feedrate[Z_AXIS] / abs(block->speed_z); - if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; - } - if(abs(block->speed_e) > max_feedrate[E_AXIS]){ - tmp_speed_factor = max_feedrate[E_AXIS] / abs(block->speed_e); - if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; - } - multiplier = multiplier * speed_factor; - block->speed_z = delta_z_mm * multiplier; - block->speed_x = delta_x_mm * multiplier; - block->speed_y = delta_y_mm * multiplier; - block->speed_e = delta_e_mm * multiplier; - block->nominal_speed = block->millimeters * multiplier; - block->nominal_rate = ceil(block->step_event_count * multiplier / 60); - - if(block->nominal_rate < 120) block->nominal_rate = 120; - block->entry_speed = safe_speed(block); - - // Compute the acceleration rate for the trapezoid generator. - float travel_per_step = block->millimeters/block->step_event_count; + float speed_factor = 1.0; //factor <=1 do decrease speed + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + } + + // Correct the speed + if( speed_factor < 1.0) { +// Serial.print("speed factor : "); Serial.println(speed_factor); + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + /* + if(speed_factor < 0.1) { + Serial.print("speed factor : "); Serial.println(speed_factor); + Serial.print("current_speed"); Serial.print(i); Serial.print(" : "); Serial.println(current_speed[i]); + } + */ + } + for(unsigned char i=0; i < 4; i++) { + current_speed[i] *= speed_factor; + } + block->nominal_speed *= speed_factor; + block->nominal_rate *= speed_factor; + } + + // Compute and limit the acceleration rate for the trapezoid generator. + float steps_per_mm = block->step_event_count/block->millimeters; if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) { - block->acceleration_st = ceil( (retract_acceleration)/travel_per_step); // convert to: acceleration steps/sec^2 + block->acceleration_st = ceil(retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 } else { - block->acceleration_st = ceil( (acceleration)/travel_per_step); // convert to: acceleration steps/sec^2 + block->acceleration_st = ceil(move_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 // Limit acceleration per axis - if((block->acceleration_st * block->steps_x / block->step_event_count) > axis_steps_per_sqr_second[X_AXIS]) + if(((float)block->acceleration_st * (float)block->steps_x / (float)block->step_event_count) > axis_steps_per_sqr_second[X_AXIS]) block->acceleration_st = axis_steps_per_sqr_second[X_AXIS]; - if((block->acceleration_st * block->steps_y / block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS]) + if(((float)block->acceleration_st * (float)block->steps_y / (float)block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS]) block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS]; - if((block->acceleration_st * block->steps_e / block->step_event_count) > axis_steps_per_sqr_second[E_AXIS]) + if(((float)block->acceleration_st * (float)block->steps_e / (float)block->step_event_count) > axis_steps_per_sqr_second[E_AXIS]) block->acceleration_st = axis_steps_per_sqr_second[E_AXIS]; - if(((block->acceleration_st / block->step_event_count) * block->steps_z ) > axis_steps_per_sqr_second[Z_AXIS]) + if(((float)block->acceleration_st * (float)block->steps_z / (float)block->step_event_count ) > axis_steps_per_sqr_second[Z_AXIS]) block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS]; } - block->acceleration = block->acceleration_st * travel_per_step; - -#ifdef ADVANCE - // Calculate advance rate - if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0)) { - block->advance_rate = 0; - block->advance = 0; + block->acceleration = block->acceleration_st / steps_per_mm; + block->acceleration_rate = (long)((float)block->acceleration_st * 8.388608); + +#if 0 // Use old jerk for now + // Compute path unit vector + double unit_vec[3]; + + unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; + unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; + unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; + + // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + // Let a circle be tangent to both previous and current path line segments, where the junction + // deviation is defined as the distance from the junction to the closest edge of the circle, + // colinear with the circle center. The circular segment joining the two paths represents the + // path of centripetal acceleration. Solve for max velocity based on max acceleration about the + // radius of the circle, defined indirectly by junction deviation. This may be also viewed as + // path width or max_jerk in the previous grbl version. This approach does not actually deviate + // from path, but used as a robust way to compute cornering speeds, as it takes into account the + // nonlinearities of both the junction angle and junction velocity. + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + + // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. + if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) + // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. + double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + + // Skip and use default max junction speed for 0 degree acute junction. + if (cos_theta < 0.95) { + vmax_junction = min(previous_nominal_speed,block->nominal_speed); + // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. + if (cos_theta > -0.95) { + // Compute maximum junction velocity based on maximum acceleration and junction deviation + double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + vmax_junction = min(vmax_junction, + sqrt(block->acceleration * junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); + } + } } - else { - long acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); - float advance = (STEPS_PER_CUBIC_MM_E * EXTRUDER_ADVANCE_K) * - (block->speed_e * block->speed_e * EXTRUTION_AREA * EXTRUTION_AREA / 3600.0)*65536; - block->advance = advance; - if(acc_dist == 0) { - block->advance_rate = 0; +#endif + // Start with a safe speed + float vmax_junction = max_xy_jerk/2; + if(abs(current_speed[Z_AXIS]) > max_z_jerk/2) + vmax_junction = max_z_jerk/2; + vmax_junction = min(vmax_junction, block->nominal_speed); + + if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + float jerk = sqrt(pow((current_speed[X_AXIS]-previous_speed[X_AXIS]), 2)+pow((current_speed[Y_AXIS]-previous_speed[Y_AXIS]), 2)); + if((previous_speed[X_AXIS] != 0.0) || (previous_speed[Y_AXIS] != 0.0)) { + vmax_junction = block->nominal_speed; + } + if (jerk > max_xy_jerk) { + vmax_junction *= (max_xy_jerk/jerk); } + if(abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]) > max_z_jerk) { + vmax_junction *= (max_z_jerk/abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS])); + } + } + block->max_entry_speed = vmax_junction; + + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = max_allowable_speed(-block->acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); + block->entry_speed = min(vmax_junction, v_allowable); + + // Initialize planner efficiency flags + // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. + // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then + // the current block and next block junction speeds are guaranteed to always be at their maximum + // junction speeds in deceleration and acceleration, respectively. This is due to how the current + // block nominal speed limits both the current and next maximum junction speeds. Hence, in both + // the reverse and forward planners, the corresponding block junction speed will always be at the + // the maximum junction speed and may always be ignored for any speed reduction checks. + if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } + else { block->nominal_length_flag = false; } + block->recalculate_flag = true; // Always calculate trapezoid for new block + + // Update previous path unit_vector and nominal speed + memcpy(previous_speed, current_speed, sizeof(previous_speed)); // previous_speed[] = current_speed[] + previous_nominal_speed = block->nominal_speed; + + #ifdef ADVANCE + // Calculate advance rate + if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0)) { + block->advance_rate = 0; + block->advance = 0; + } else { - block->advance_rate = advance / (float)acc_dist; + long acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); + float advance = (STEPS_PER_CUBIC_MM_E * EXTRUDER_ADVANCE_K) * + (current_speed[E_AXIS] * current_speed[E_AXIS] * EXTRUTION_AREA * EXTRUTION_AREA)*256; + block->advance = advance; + if(acc_dist == 0) { + block->advance_rate = 0; + } + else { + block->advance_rate = advance / (float)acc_dist; + } } - } -#endif // ADVANCE + #endif // ADVANCE - // compute a preliminary conservative acceleration trapezoid - float safespeed = safe_speed(block); - calculate_trapezoid_for_block(block, safespeed, safespeed); - // Compute direction bits for this block - block->direction_bits = 0; - if (target[X_AXIS] < position[X_AXIS]) { - block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<entry_speed/block->nominal_speed, + MINIMUM_PLANNER_SPEED/block->nominal_speed); + + // Move buffer head + block_buffer_head = next_buffer_head; + + // Update position memcpy(position, target, sizeof(target)); // position[] = target[] planner_recalculate(); @@ -2199,7 +2405,13 @@ void plan_set_position(float x, float y, float z, float e) position[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]); position[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]); position[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); - position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + st_set_position(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS]); + previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; } #ifdef AUTOTEMP @@ -2323,6 +2535,12 @@ asm volatile ( \ #define ENABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 |= (1< MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY; + + if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times + step_rate = (step_rate >> 2)&0x3fff; + step_loops = 4; + } + else if(step_rate > 10000) { // If steprate > 10kHz >> step 2 times + step_rate = (step_rate >> 1)&0x7fff; + step_loops = 2; + } + else { + step_loops = 1; + } + if(step_rate < 32) step_rate = 32; step_rate -= 32; // Correct for minimal speed - if(step_rate >= (8*256)) + if(step_rate >= (8*256)) // higher step rate { // higher step rate unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; unsigned char tmp_step_rate = (step_rate & 0x00ff); @@ -2385,48 +2641,36 @@ inline unsigned short calc_timer(unsigned short step_rate) timer = (unsigned short)pgm_read_word_near(table_address); timer -= (((unsigned short)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); } - - if(timer < 100) timer = 100; - + if(timer < 100) { timer = 100; }//(20kHz this should never happen) return timer; } // Initializes the trapezoid generator from the current block. Called whenever a new // block begins. -inline void trapezoid_generator_reset() +FORCE_INLINE void trapezoid_generator_reset() { - accelerate_until = current_block->accelerate_until; - decelerate_after = current_block->decelerate_after; - acceleration_rate = current_block->acceleration_rate; - initial_rate = current_block->initial_rate; - final_rate = current_block->final_rate; - nominal_rate = current_block->nominal_rate; - #ifdef ADVANCE advance = current_block->initial_advance; final_advance = current_block->final_advance; - advance_rate = current_block->advance_rate; + // Do E steps + advance steps + e_steps += ((advance >>8) - old_advance); + old_advance = advance >>8; #endif deceleration_time = 0; // step_rate to timer interval - acc_step_rate = initial_rate; + acc_step_rate = current_block->initial_rate; acceleration_time = calc_timer(acc_step_rate); OCR1A = acceleration_time; + OCR1A_nominal = calc_timer(current_block->nominal_rate); + } // "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. // It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. ISR(TIMER1_COMPA_vect) { - if(busy){ /*Serial.println("BUSY")*/; - return; - } // The busy-flag is used to avoid reentering this interrupt - - busy = true; - sei(); // Re enable interrupts (normally disabled while inside an interrupt handler) - // If there is no current block, attempt to pop one from the buffer if (current_block == NULL) { // Anything in the buffer? @@ -2438,10 +2682,12 @@ ISR(TIMER1_COMPA_vect) counter_z = counter_x; counter_e = counter_x; step_events_completed = 0; - e_steps = 0; +// #ifdef ADVANCE +// e_steps = 0; +// #endif } else { - DISABLE_STEPPER_DRIVER_INTERRUPT(); + OCR1A=2000; // 1kHz. } } @@ -2449,143 +2695,236 @@ ISR(TIMER1_COMPA_vect) // Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt out_bits = current_block->direction_bits; -#ifdef ADVANCE - // Calculate E early. - counter_e += current_block->steps_e; - if (counter_e > 0) { - counter_e -= current_block->step_event_count; - if ((out_bits & (1<> 16) - old_advance); - CRITICAL_SECTION_END; - old_advance = advance >> 16; -#endif //ADVANCE - // Set direction en check limit switches if ((out_bits & (1<step_event_count; + count_direction[X_AXIS]=-1; + CHECK_ENDSTOPS + { + #if X_MIN_PIN > -1 + bool x_min_endstop=(READ(X_MIN_PIN) != X_ENDSTOP_INVERT); + if(x_min_endstop && old_x_min_endstop && (current_block->steps_x > 0)) { + endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; + endstop_x_hit=true; + step_events_completed = current_block->step_event_count; + } + old_x_min_endstop = x_min_endstop; + #endif + } + } + else { // +direction + WRITE(X_DIR_PIN,!INVERT_X_DIR); + count_direction[X_AXIS]=1; + CHECK_ENDSTOPS + { + #if X_MAX_PIN > -1 + bool x_max_endstop=(READ(X_MAX_PIN) != X_ENDSTOP_INVERT); + if(x_max_endstop && old_x_max_endstop && (current_block->steps_x > 0)){ + endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; + endstop_x_hit=true; + step_events_completed = current_block->step_event_count; + } + old_x_max_endstop = x_max_endstop; + #endif } } - else // +direction - WRITE(X_DIR_PIN,!INVERT_X_DIR); if ((out_bits & (1<step_event_count; + count_direction[Y_AXIS]=-1; + CHECK_ENDSTOPS + { + #if Y_MIN_PIN > -1 + bool y_min_endstop=(READ(Y_MIN_PIN) != Y_ENDSTOP_INVERT); + if(y_min_endstop && old_y_min_endstop && (current_block->steps_y > 0)) { + endstops_trigsteps[Y_AXIS] = count_position[Y_AXIS]; + endstop_y_hit=true; + step_events_completed = current_block->step_event_count; + } + old_y_min_endstop = y_min_endstop; + #endif } } - else // +direction + else { // +direction WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + count_direction[Y_AXIS]=1; + CHECK_ENDSTOPS + { + #if Y_MAX_PIN > -1 + bool y_max_endstop=(READ(Y_MAX_PIN) != Y_ENDSTOP_INVERT); + if(y_max_endstop && old_y_max_endstop && (current_block->steps_y > 0)){ + endstops_trigsteps[Y_AXIS] = count_position[Y_AXIS]; + endstop_y_hit=true; + step_events_completed = current_block->step_event_count; + } + old_y_max_endstop = y_max_endstop; + #endif + } + } if ((out_bits & (1<step_event_count; + count_direction[Z_AXIS]=-1; + CHECK_ENDSTOPS + { + #if Z_MIN_PIN > -1 + bool z_min_endstop=(READ(Z_MIN_PIN) != Z_ENDSTOP_INVERT); + if(z_min_endstop && old_z_min_endstop && (current_block->steps_z > 0)) { + endstops_trigsteps[Z_AXIS] = count_position[Z_AXIS]; + endstop_z_hit=true; + step_events_completed = current_block->step_event_count; + } + old_z_min_endstop = z_min_endstop; + #endif } } - else // +direction - WRITE(Z_DIR_PIN,!INVERT_Z_DIR); - -#ifndef ADVANCE - if ((out_bits & (1<steps_x; - if (counter_x > 0) { - WRITE(X_STEP_PIN, HIGH); - counter_x -= current_block->step_event_count; - WRITE(X_STEP_PIN, LOW); + else { // +direction + WRITE(Z_DIR_PIN,!INVERT_Z_DIR); + count_direction[Z_AXIS]=1; + CHECK_ENDSTOPS + { + #if Z_MAX_PIN > -1 + bool z_max_endstop=(READ(Z_MAX_PIN) != Z_ENDSTOP_INVERT); + if(z_max_endstop && old_z_max_endstop && (current_block->steps_z > 0)) { + endstops_trigsteps[Z_AXIS] = count_position[Z_AXIS]; + endstop_z_hit=true; + step_events_completed = current_block->step_event_count; + } + old_z_max_endstop = z_max_endstop; + #endif + } } - counter_y += current_block->steps_y; - if (counter_y > 0) { - WRITE(Y_STEP_PIN, HIGH); - counter_y -= current_block->step_event_count; - WRITE(Y_STEP_PIN, LOW); - } + #ifndef ADVANCE + if ((out_bits & (1<steps_z; - if (counter_z > 0) { - WRITE(Z_STEP_PIN, HIGH); - counter_z -= current_block->step_event_count; - WRITE(Z_STEP_PIN, LOW); - } + + for(int8_t i=0; i < step_loops; i++) { // Take multiple steps per interrupt (For high speed moves) + //MSerial.checkRx(); // Check for serial chars. + + #ifdef ADVANCE + counter_e += current_block->steps_e; + if (counter_e > 0) { + counter_e -= current_block->step_event_count; + if ((out_bits & (1<steps_x; + if (counter_x > 0) { + WRITE(X_STEP_PIN, HIGH); + counter_x -= current_block->step_event_count; + WRITE(X_STEP_PIN, LOW); + count_position[X_AXIS]+=count_direction[X_AXIS]; + } -#ifndef ADVANCE - counter_e += current_block->steps_e; - if (counter_e > 0) { - WRITE(E_STEP_PIN, HIGH); - counter_e -= current_block->step_event_count; - WRITE(E_STEP_PIN, LOW); - } -#endif //!ADVANCE + counter_y += current_block->steps_y; + if (counter_y > 0) { + WRITE(Y_STEP_PIN, HIGH); + counter_y -= current_block->step_event_count; + WRITE(Y_STEP_PIN, LOW); + count_position[Y_AXIS]+=count_direction[Y_AXIS]; + } + + counter_z += current_block->steps_z; + if (counter_z > 0) { + WRITE(Z_STEP_PIN, HIGH); + counter_z -= current_block->step_event_count; + WRITE(Z_STEP_PIN, LOW); + count_position[Z_AXIS]+=count_direction[Z_AXIS]; + } + #ifndef ADVANCE + counter_e += current_block->steps_e; + if (counter_e > 0) { + WRITE(E_STEP_PIN, HIGH); + counter_e -= current_block->step_event_count; + WRITE(E_STEP_PIN, LOW); + count_position[E_AXIS]+=count_direction[E_AXIS]; + } + #endif //!ADVANCE + step_events_completed += 1; + if(step_events_completed >= current_block->step_event_count) break; + } // Calculare new timer value unsigned short timer; unsigned short step_rate; - if (step_events_completed < accelerate_until) { - MultiU24X24toH16(acc_step_rate, acceleration_time, acceleration_rate); - acc_step_rate += initial_rate; + if (step_events_completed <= (unsigned long int)current_block->accelerate_until) { + + MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate); + acc_step_rate += current_block->initial_rate; // upper limit - if(acc_step_rate > nominal_rate) - acc_step_rate = nominal_rate; + if(acc_step_rate > current_block->nominal_rate) + acc_step_rate = current_block->nominal_rate; // step_rate to timer interval timer = calc_timer(acc_step_rate); - advance += advance_rate; - acceleration_time += timer; OCR1A = timer; + acceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance += advance_rate; + } + //if(advance > current_block->advance) advance = current_block->advance; + // Do E steps + advance steps + e_steps += ((advance >>8) - old_advance); + old_advance = advance >>8; + + #endif } - else if (step_events_completed >= decelerate_after) { - MultiU24X24toH16(step_rate, deceleration_time, acceleration_rate); + else if (step_events_completed > (unsigned long int)current_block->decelerate_after) { + MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); if(step_rate > acc_step_rate) { // Check step_rate stays positive - step_rate = final_rate; + step_rate = current_block->final_rate; } else { step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. } // lower limit - if(step_rate < final_rate) - step_rate = final_rate; + if(step_rate < current_block->final_rate) + step_rate = current_block->final_rate; // step_rate to timer interval timer = calc_timer(step_rate); -#ifdef ADVANCE - advance -= advance_rate; - if(advance < final_advance) - advance = final_advance; -#endif //ADVANCE - deceleration_time += timer; OCR1A = timer; - } + deceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance -= advance_rate; + } + if(advance < final_advance) advance = final_advance; + // Do E steps + advance steps + e_steps += ((advance >>8) - old_advance); + old_advance = advance >>8; + #endif //ADVANCE + } + else { + OCR1A = OCR1A_nominal; + } + // If current block is finished, reset pointer - step_events_completed += 1; if (step_events_completed >= current_block->step_event_count) { current_block = NULL; plan_discard_current_block(); } } - busy=false; } #ifdef ADVANCE @@ -2595,23 +2934,27 @@ unsigned char old_OCR0A; // Timer 0 is shared with millies ISR(TIMER0_COMPA_vect) { - // Critical section needed because Timer 1 interrupt has higher priority. - // The pin set functions are placed on trategic position to comply with the stepper driver timing. - WRITE(E_STEP_PIN, LOW); + old_OCR0A += 52; // ~10kHz interrupt (250000 / 26 = 9615kHz) + OCR0A = old_OCR0A; // Set E direction (Depends on E direction + advance) - if (e_steps < 0) { - WRITE(E_DIR_PIN,INVERT_E_DIR); - e_steps++; - WRITE(E_STEP_PIN, HIGH); - } - if (e_steps > 0) { - WRITE(E_DIR_PIN,!INVERT_E_DIR); - e_steps--; - WRITE(E_STEP_PIN, HIGH); + for(unsigned char i=0; i<4;i++) + { + if (e_steps != 0) + { + WRITE(E0_STEP_PIN, LOW); + if (e_steps < 0) { + WRITE(E0_DIR_PIN, INVERT_E0_DIR); + e_steps++; + WRITE(E0_STEP_PIN, HIGH); + } + else if (e_steps > 0) { + WRITE(E0_DIR_PIN, !INVERT_E0_DIR); + e_steps--; + WRITE(E0_STEP_PIN, HIGH); + } + } + } } - old_OCR0A += 25; // 10kHz interrupt - OCR0A = old_OCR0A; -} #endif // ADVANCE void st_init() @@ -2628,24 +2971,46 @@ void st_init() TCCR1B = (TCCR1B & ~(0x07<