A lot of changes in the planner code

This commit is contained in:
Erik van der Zalm 2011-11-13 20:42:08 +01:00
parent 72ace55e6a
commit 65934eee9c
7 changed files with 2019 additions and 1877 deletions

View file

@ -6,6 +6,15 @@
#define MM_PER_ARC_SEGMENT 1 #define MM_PER_ARC_SEGMENT 1
#define N_ARC_CORRECTION 25 #define N_ARC_CORRECTION 25
// Frequency limit
// See nophead's blog for more info
#define XY_FREQUENCY_LIMIT 15
// 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)
// BASIC SETTINGS: select your board type, thermistor type, axis scaling, and endstop configuration // BASIC SETTINGS: select your board type, thermistor type, axis scaling, and endstop configuration
//// The following define selects which electronics board you have. Please choose the one that matches your setup //// The following define selects which electronics board you have. Please choose the one that matches your setup
@ -97,6 +106,11 @@ const int dropsegments=5; //everything with this number of steps will be ignore
#define DISABLE_E false #define DISABLE_E false
// Inverting axis direction // Inverting axis direction
//#define INVERT_X_DIR false // for Mendel set to false, for Orca set to true
//#define INVERT_Y_DIR true // for Mendel set to true, for Orca set to false
//#define INVERT_Z_DIR false // for Mendel set to false, for Orca set to true
//#define INVERT_E_DIR true // for direct drive extruder v9 set to true, for geared extruder set to false
#define INVERT_X_DIR true // for Mendel set to false, for Orca set to true #define INVERT_X_DIR true // for Mendel set to false, for Orca set to true
#define INVERT_Y_DIR false // for Mendel set to true, for Orca set to false #define INVERT_Y_DIR false // for Mendel set to true, for Orca set to false
#define INVERT_Z_DIR true // for Mendel set to false, for Orca set to true #define INVERT_Z_DIR true // for Mendel set to false, for Orca set to true
@ -117,7 +131,7 @@ const int dropsegments=5; //everything with this number of steps will be ignore
//// MOVEMENT SETTINGS //// MOVEMENT SETTINGS
#define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E #define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E
//note: on bernhards ultimaker 200 200 12 are working well. //note: on bernhards ultimaker 200 200 12 are working well.
#define HOMING_FEEDRATE {50*60, 50*60, 12*60, 0} // set the homing speeds #define HOMING_FEEDRATE {50*60, 50*60, 4*60, 0} // set the homing speeds (mm/min)
#define AXIS_RELATIVE_MODES {false, false, false, false} #define AXIS_RELATIVE_MODES {false, false, false, false}
@ -126,19 +140,20 @@ const int dropsegments=5; //everything with this number of steps will be ignore
// default settings // default settings
#define DEFAULT_AXIS_STEPS_PER_UNIT {79.87220447,79.87220447,200*8/3,14} // default steps per unit for ultimaker #define DEFAULT_AXIS_STEPS_PER_UNIT {79.87220447,79.87220447,200*8/3,14} // default steps per unit for ultimaker
#define DEFAULT_MAX_FEEDRATE {160*60, 160*60, 10*60, 500000} //#define DEFAULT_AXIS_STEPS_PER_UNIT {40, 40, 3333.92, 67}
#define DEFAULT_MAX_ACCELERATION {9000,9000,150,10000} // 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 DEFAULT_MAX_FEEDRATE {500, 500, 10, 500000} // (mm/min)
#define DEFAULT_MAX_ACCELERATION {9000,9000,100,10000} // 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 DEFAULT_ACCELERATION 3000 // X, Y, Z and E max acceleration in mm/s^2 for printing moves #define DEFAULT_ACCELERATION 3000 // X, Y, Z and E max acceleration in mm/s^2 for printing moves
#define DEFAULT_RETRACT_ACCELERATION 7000 // X, Y, Z and E max acceleration in mm/s^2 for r retracts #define DEFAULT_RETRACT_ACCELERATION 7000 // X, Y, Z and E max acceleration in mm/s^2 for r retracts
#define DEFAULT_MINIMUMFEEDRATE 10 // minimum feedrate #define DEFAULT_MINIMUMFEEDRATE 0 // minimum feedrate
#define DEFAULT_MINTRAVELFEEDRATE 10 #define DEFAULT_MINTRAVELFEEDRATE 0
// minimum time in microseconds that a movement needs to take if the buffer is emptied. Increase this number if you see blobs while printing high speed & high detail. It will slowdown on the detailed stuff. // minimum time in microseconds that a movement needs to take if the buffer is emptied. Increase this number if you see blobs while printing high speed & high detail. It will slowdown on the detailed stuff.
#define DEFAULT_MINSEGMENTTIME 20000 #define DEFAULT_MINSEGMENTTIME 20000
#define DEFAULT_XYJERK 30.0*60 #define DEFAULT_XYJERK 30.0 // (mm/sec)
#define DEFAULT_ZJERK 10.0*60 #define DEFAULT_ZJERK 0.4 // (mm/sec)
// The watchdog waits for the watchperiod in milliseconds whenever an M104 or M109 increases the target temperature // The watchdog waits for the watchperiod in milliseconds whenever an M104 or M109 increases the target temperature
@ -162,7 +177,7 @@ const int dropsegments=5; //everything with this number of steps will be ignore
//#define TEMP_HYSTERESIS 5 // (C°) range of +/- temperatures considered "close" to the target one //#define TEMP_HYSTERESIS 5 // (C°) range of +/- temperatures considered "close" to the target one
//// The minimal temperature defines the temperature below which the heater will not be enabled //// The minimal temperature defines the temperature below which the heater will not be enabled
#define HEATER_0_MINTEMP 5 //#define HEATER_0_MINTEMP 5
//#define HEATER_1_MINTEMP 5 //#define HEATER_1_MINTEMP 5
//#define BED_MINTEMP 5 //#define BED_MINTEMP 5
@ -170,7 +185,7 @@ const int dropsegments=5; //everything with this number of steps will be ignore
// When temperature exceeds max temp, your heater will be switched off. // When temperature exceeds max temp, your heater will be switched off.
// This feature exists to protect your hotend from overheating accidentally, but *NOT* from thermistor short/failure! // This feature exists to protect your hotend from overheating accidentally, but *NOT* from thermistor short/failure!
// You should use MINTEMP for thermistor short/failure protection. // You should use MINTEMP for thermistor short/failure protection.
#define HEATER_0_MAXTEMP 275 //#define HEATER_0_MAXTEMP 275
//#define_HEATER_1_MAXTEMP 275 //#define_HEATER_1_MAXTEMP 275
//#define BED_MAXTEMP 150 //#define BED_MAXTEMP 150
@ -246,9 +261,9 @@ const int dropsegments=5; //everything with this number of steps will be ignore
// The number of linear motions that can be in the plan at any give time. // The number of linear motions that can be in the plan at any give time.
// THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ringbuffering. // THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ringbuffering.
#if defined SDSUPPORT #if defined SDSUPPORT
#define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller #define BLOCK_BUFFER_SIZE 8 // SD,LCD,Buttons take more memory, block buffer needs to be smaller
#else #else
#define BLOCK_BUFFER_SIZE 16 // maximize block buffer #define BLOCK_BUFFER_SIZE 8 // maximize block buffer
#endif #endif
//The ASCII buffer for recieving from the serial: //The ASCII buffer for recieving from the serial:

View file

@ -114,7 +114,9 @@ extern float HeaterPower;
//=========================================================================== //===========================================================================
//=============================public variables============================= //=============================public variables=============================
//=========================================================================== //===========================================================================
#ifdef SDSUPPORT
CardReader card; CardReader card;
#endif
float homing_feedrate[] = HOMING_FEEDRATE; float homing_feedrate[] = HOMING_FEEDRATE;
bool axis_relative_modes[] = AXIS_RELATIVE_MODES; bool axis_relative_modes[] = AXIS_RELATIVE_MODES;
volatile int feedmultiply=100; //100->1 200->2 volatile int feedmultiply=100; //100->1 200->2
@ -215,7 +217,9 @@ void loop()
{ {
if(buflen<3) if(buflen<3)
get_command(); get_command();
#ifdef SDSUPPORT
card.checkautostart(false); card.checkautostart(false);
#endif
if(buflen) if(buflen)
{ {
#ifdef SDSUPPORT #ifdef SDSUPPORT
@ -933,7 +937,7 @@ inline void get_arc_coordinates()
void prepare_move() void prepare_move()
{ {
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate*feedmultiply/60.0/100.0); plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate*feedmultiply/60/100.0);
for(int8_t i=0; i < NUM_AXIS; i++) { for(int8_t i=0; i < NUM_AXIS; i++) {
current_position[i] = destination[i]; current_position[i] = destination[i];
} }
@ -943,7 +947,7 @@ void prepare_arc_move(char isclockwise) {
float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc
// Trace the arc // Trace the arc
mc_arc(current_position, destination, offset, X_AXIS, Y_AXIS, Z_AXIS, feedrate*feedmultiply/60.0/100.0, r, isclockwise); mc_arc(current_position, destination, offset, X_AXIS, Y_AXIS, Z_AXIS, feedrate*feedmultiply/60/100.0, r, isclockwise);
// As far as the parser is concerned, the position is now == target. In reality the // As far as the parser is concerned, the position is now == target. In reality the
// motion control system might still be processing the action and the real tool position // motion control system might still be processing the action and the real tool position

View file

@ -1,594 +1,705 @@
/* /*
planner.c - buffers movement commands and manages the acceleration profile plan planner.c - buffers movement commands and manages the acceleration profile plan
Part of Grbl Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Grbl is distributed in the hope that it will be useful, Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>. along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ /* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */
/* /*
Reasoning behind the mathematics in this module (in the key of 'Mathematica'): Reasoning behind the mathematics in this module (in the key of 'Mathematica'):
s == speed, a == acceleration, t == time, d == distance s == speed, a == acceleration, t == time, d == distance
Basic definitions: Basic definitions:
Speed[s_, a_, t_] := s + (a*t) Speed[s_, a_, t_] := s + (a*t)
Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t] Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t]
Distance to reach a specific speed with a constant acceleration: Distance to reach a specific speed with a constant acceleration:
Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t] Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t]
d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance() d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance()
Speed after a given distance of travel with constant acceleration: Speed after a given distance of travel with constant acceleration:
Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t] Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t]
m -> Sqrt[2 a d + s^2] m -> Sqrt[2 a d + s^2]
DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2] DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2]
When to start braking (di) to reach a specified destionation speed (s2) after accelerating When to start braking (di) to reach a specified destionation speed (s2) after accelerating
from initial speed s1 without ever stopping at a plateau: from initial speed s1 without ever stopping at a plateau:
Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di] Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di]
di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance() di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance()
IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a) IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a)
*/ */
//#include <inttypes.h> //#include <inttypes.h>
//#include <math.h> //#include <math.h>
//#include <stdlib.h> //#include <stdlib.h>
#include "Marlin.h" #include "Marlin.h"
#include "Configuration.h" #include "Configuration.h"
#include "pins.h" #include "pins.h"
#include "fastio.h" #include "fastio.h"
#include "planner.h" #include "planner.h"
#include "stepper.h" #include "stepper.h"
#include "temperature.h" #include "temperature.h"
#include "ultralcd.h" #include "ultralcd.h"
//=========================================================================== //===========================================================================
//=============================public variables ============================ //=============================public variables ============================
//=========================================================================== //===========================================================================
unsigned long minsegmenttime; unsigned long minsegmenttime;
float max_feedrate[4]; // set the max speeds float max_feedrate[4]; // set the max speeds
float axis_steps_per_unit[4]; float axis_steps_per_unit[4];
long max_acceleration_units_per_sq_second[4]; // Use M201 to override by software long max_acceleration_units_per_sq_second[4]; // Use M201 to override by software
float minimumfeedrate; float minimumfeedrate;
float acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX float acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX
float retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX float retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX
float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. float max_xy_jerk; //speed than can be stopped at once, if i understand correctly.
float max_z_jerk; float max_z_jerk;
float mintravelfeedrate; float mintravelfeedrate;
unsigned long axis_steps_per_sqr_second[NUM_AXIS]; unsigned long axis_steps_per_sqr_second[NUM_AXIS];
// The current position of the tool in absolute steps // The current position of the tool in absolute steps
long position[4]; //rescaled from extern when axis_steps_per_unit are changed by gcode long position[4]; //rescaled from extern when axis_steps_per_unit are changed by gcode
static float previous_speed[4]; // Speed of previous path line segment
static float previous_nominal_speed; // Nominal speed of previous path line segment
//===========================================================================
//=============================private variables ============================
//=========================================================================== //===========================================================================
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions //=============================private variables ============================
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 static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions
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
//=========================================================================== // Used for the frequency limit
//=============================functions ============================ static unsigned char old_direction_bits = 0; // Old direction bits. Used for speed calculations
//=========================================================================== static long x_segment_time[3]={0,0,0}; // Segment times (in us). Used for speed calculations
#define ONE_MINUTE_OF_MICROSECONDS 60000000.0 static long y_segment_time[3]={0,0,0};
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the // Returns the index of the next block in the ring buffer
// given acceleration: // NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
inline float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) { static int8_t next_block_index(int8_t block_index) {
if (acceleration!=0) { block_index++;
return((target_rate*target_rate-initial_rate*initial_rate)/ if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
(2.0*acceleration)); return(block_index);
} }
else {
return 0.0; // acceleration was 0, set acceleration distance to 0
} // 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; }
// This function gives you the point at which you must start braking (at the rate of -acceleration) if block_index--;
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after return(block_index);
// 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 float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) { //=============================functions ============================
if (acceleration!=0) { //===========================================================================
return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/
(4.0*acceleration) ); // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the
} // given acceleration:
else { inline float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) {
return 0.0; // acceleration was 0, set intersection distance to 0 if (acceleration!=0) {
} return((target_rate*target_rate-initial_rate*initial_rate)/
} (2.0*acceleration));
}
// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. else {
return 0.0; // acceleration was 0, set acceleration distance to 0
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; // This function gives you the point at which you must start braking (at the rate of -acceleration) if
long initial_rate = ceil(block->nominal_rate*entry_factor); // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
long final_rate = ceil(block->nominal_rate*exit_factor); // 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)
#ifdef ADVANCE
long initial_advance = block->advance*entry_factor*entry_factor; inline float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) {
long final_advance = block->advance*exit_factor*exit_factor; if (acceleration!=0) {
#endif // ADVANCE return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/
(4.0*acceleration) );
// Limit minimal step rate (Otherwise the timer will overflow.) }
if(initial_rate <120) initial_rate=120; else {
if(final_rate < 120) final_rate=120; return 0.0; // acceleration was 0, set intersection distance to 0
}
// Calculate the acceleration steps }
long acceleration = block->acceleration_st;
long accelerate_steps = estimate_acceleration_distance(initial_rate, block->nominal_rate, acceleration); // Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors.
long decelerate_steps = estimate_acceleration_distance(final_rate, block->nominal_rate, acceleration);
// Calculate the size of Plateau of Nominal Rate. void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) {
long plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; long initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min)
long final_rate = ceil(block->nominal_rate*exit_factor); // (step/min)
// 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 // Limit minimal step rate (Otherwise the timer will overflow.)
// in order to reach the final_rate exactly at the end of this block. if(initial_rate <120) {initial_rate=120; }
if (plateau_steps < 0) { if(final_rate < 120) {final_rate=120; }
accelerate_steps = intersection_distance(initial_rate, final_rate, acceleration, block->step_event_count);
plateau_steps = 0; long acceleration = block->acceleration_st;
} int32_t accelerate_steps =
ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration));
long decelerate_after = accelerate_steps+plateau_steps; int32_t decelerate_steps =
floor(estimate_acceleration_distance(block->nominal_rate, block->final_rate, -acceleration));
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. // Calculate the size of Plateau of Nominal Rate.
block->accelerate_until = accelerate_steps; int32_t plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps;
block->decelerate_after = decelerate_after;
block->initial_rate = initial_rate; // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will
block->final_rate = final_rate; // have to use intersection_distance() to calculate when to abort acceleration and start braking
#ifdef ADVANCE // in order to reach the final_rate exactly at the end of this block.
block->initial_advance = initial_advance; if (plateau_steps < 0) {
block->final_advance = final_advance; accelerate_steps = ceil(
#endif //ADVANCE 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
CRITICAL_SECTION_END; accelerate_steps = min(accelerate_steps,block->step_event_count);
} plateau_steps = 0;
}
// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
// acceleration within the allotted distance. #ifdef ADVANCE
inline float max_allowable_speed(float acceleration, float target_velocity, float distance) { long initial_advance = block->advance*entry_factor*entry_factor;
return sqrt(target_velocity*target_velocity-2*acceleration*60*60*distance); long final_advance = block->advance*exit_factor*exit_factor;
} #endif // ADVANCE
// "Junction jerk" in this context is the immediate change in speed at the junction of two blocks. // block->accelerate_until = accelerate_steps;
// This method will calculate the junction jerk as the euclidean distance between the nominal // block->decelerate_after = accelerate_steps+plateau_steps;
// velocities of the respective blocks.
inline float junction_jerk(block_t *before, block_t *after) { CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section
return sqrt( if(block->busy == false) { // Don't update variables if block is busy.
pow((before->speed_x-after->speed_x), 2)+pow((before->speed_y-after->speed_y), 2)); block->accelerate_until = accelerate_steps;
} block->decelerate_after = accelerate_steps+plateau_steps;
block->initial_rate = initial_rate;
// Return the safe speed which is max_jerk/2, e.g. the block->final_rate = final_rate;
// speed under which you cannot exceed max_jerk no matter what you do. #ifdef ADVANCE
float safe_speed(block_t *block) { block->initial_advance = initial_advance;
float safe_speed; block->final_advance = final_advance;
safe_speed = max_xy_jerk/2; #endif //ADVANCE
if(abs(block->speed_z) > max_z_jerk/2) }
safe_speed = max_z_jerk/2; CRITICAL_SECTION_END;
if (safe_speed > block->nominal_speed) }
safe_speed = block->nominal_speed;
return safe_speed; // 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) {
// The kernel called by planner_recalculate() when scanning the plan from last to first entry. return sqrt(target_velocity*target_velocity-2*acceleration*distance);
void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { }
if(!current) {
return; // "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.
float entry_speed = current->nominal_speed; //inline float junction_jerk(block_t *before, block_t *after) {
float exit_factor; // return sqrt(
float exit_speed; // pow((before->speed_x-after->speed_x), 2)+pow((before->speed_y-after->speed_y), 2));
if (next) { //}
exit_speed = next->entry_speed;
}
else { // The kernel called by planner_recalculate() when scanning the plan from last to first entry.
exit_speed = safe_speed(current); void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) {
} if(!current) { return; }
// Calculate the entry_factor for the current block. if (next) {
if (previous) { // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
// Reduce speed so that junction_jerk is within the maximum allowed // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
float jerk = junction_jerk(previous, current); // check for maximum allowable speed reductions to ensure maximum possible planned speed.
if((previous->steps_x == 0) && (previous->steps_y == 0)) { if (current->entry_speed != current->max_entry_speed) {
entry_speed = safe_speed(current);
} // If nominal length true, max junction speed is guaranteed to be reached. Only compute
else if (jerk > max_xy_jerk) { // for max allowable speed if block is decelerating and nominal length is false.
entry_speed = (max_xy_jerk/jerk) * entry_speed; if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) {
} current->entry_speed = min( current->max_entry_speed,
if(abs(previous->speed_z - current->speed_z) > max_z_jerk) { max_allowable_speed(-current->acceleration,next->entry_speed,current->millimeters));
entry_speed = (max_z_jerk/abs(previous->speed_z - current->speed_z)) * entry_speed; } else {
} current->entry_speed = current->max_entry_speed;
// If the required deceleration across the block is too rapid, reduce the entry_factor accordingly. }
if (entry_speed > exit_speed) { current->recalculate_flag = true;
float max_entry_speed = max_allowable_speed(-current->acceleration,exit_speed, current->millimeters);
if (max_entry_speed < entry_speed) { }
entry_speed = max_entry_speed; } // 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
else { // implements the reverse pass.
entry_speed = safe_speed(current); void planner_reverse_pass() {
} char block_index = block_buffer_head;
// Store result if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) {
current->entry_speed = entry_speed; block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1);
} block_t *block[3] = { NULL, NULL, NULL };
while(block_index != block_buffer_tail) {
// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This block_index = prev_block_index(block_index);
// implements the reverse pass. block[2]= block[1];
void planner_reverse_pass() { block[1]= block[0];
char block_index = block_buffer_head; block[0] = &block_buffer[block_index];
if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) { planner_reverse_pass_kernel(block[0], block[1], block[2]);
block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1); }
block_t *block[5] = { }
NULL, NULL, NULL, NULL, NULL }; }
while(block_index != block_buffer_tail) {
block_index = (block_index-1) & (BLOCK_BUFFER_SIZE -1); // The kernel called by planner_recalculate() when scanning the plan from first to last entry.
block[2]= block[1]; void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) {
block[1]= block[0]; if(!previous) { return; }
block[0] = &block_buffer[block_index];
planner_reverse_pass_kernel(block[0], block[1], block[2]); // 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
planner_reverse_pass_kernel(NULL, block[0], block[1]); // 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) {
// The kernel called by planner_recalculate() when scanning the plan from first to last entry. double entry_speed = min( current->entry_speed,
void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->millimeters) );
if(!current) {
return; // Check for junction speed change
} if (current->entry_speed != entry_speed) {
if(previous) { current->entry_speed = entry_speed;
// If the previous block is an acceleration block, but it is not long enough to current->recalculate_flag = true;
// 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) { // planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This
current->entry_speed = max_entry_speed; // implements the forward pass.
} void planner_forward_pass() {
} char block_index = block_buffer_tail;
} block_t *block[3] = { NULL, NULL, NULL };
}
while(block_index != block_buffer_head) {
// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This block[0] = block[1];
// implements the forward pass. block[1] = block[2];
void planner_forward_pass() { block[2] = &block_buffer[block_index];
char block_index = block_buffer_tail; planner_forward_pass_kernel(block[0],block[1],block[2]);
block_t *block[3] = { block_index = next_block_index(block_index);
NULL, NULL, NULL }; }
planner_forward_pass_kernel(block[1], block[2], NULL);
while(block_index != block_buffer_head) { }
block[0] = block[1];
block[1] = block[2]; // Recalculates the trapezoid speed profiles for all blocks in the plan according to the
block[2] = &block_buffer[block_index]; // entry_factor for each junction. Must be called by planner_recalculate() after
planner_forward_pass_kernel(block[0],block[1],block[2]); // updating the blocks.
block_index = (block_index+1) & (BLOCK_BUFFER_SIZE - 1); void planner_recalculate_trapezoids() {
} int8_t block_index = block_buffer_tail;
planner_forward_pass_kernel(block[1], block[2], NULL); block_t *current;
} block_t *next = NULL;
// Recalculates the trapezoid speed profiles for all blocks in the plan according to the while(block_index != block_buffer_head) {
// entry_factor for each junction. Must be called by planner_recalculate() after current = next;
// updating the blocks. next = &block_buffer[block_index];
void planner_recalculate_trapezoids() { if (current) {
char block_index = block_buffer_tail; // Recalculate if current block entry or exit junction speed has changed.
block_t *current; if (current->recalculate_flag || next->recalculate_flag) {
block_t *next = NULL; // NOTE: Entry and exit factors always > 0 by all previous logic operations.
while(block_index != block_buffer_head) { calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_speed,
current = next; next->entry_speed/current->nominal_speed);
next = &block_buffer[block_index]; current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
if (current) { }
calculate_trapezoid_for_block(current, current->entry_speed, next->entry_speed); }
} block_index = next_block_index( block_index );
block_index = (block_index+1) & (BLOCK_BUFFER_SIZE - 1); }
} // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
calculate_trapezoid_for_block(next, next->entry_speed, safe_speed(next)); if(next != NULL) {
} calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed,
MINIMUM_PLANNER_SPEED/next->nominal_speed);
// Recalculates the motion plan according to the following algorithm: next->recalculate_flag = false;
// }
// 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor) }
// so that:
// a. The junction jerk is within the set limit // Recalculates the motion plan according to the following algorithm:
// b. No speed reduction within one block requires faster deceleration than the one, true constant //
// acceleration. // 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor)
// 2. Go over every block in chronological order and dial down junction speed reduction values if // so that:
// a. The speed increase within one block would require faster accelleration than the one, true // a. The junction jerk is within the set limit
// constant acceleration. // b. No speed reduction within one block requires faster deceleration than the one, true constant
// // acceleration.
// When these stages are complete all blocks have an entry_factor that will allow all speed changes to // 2. Go over every block in chronological order and dial down junction speed reduction values if
// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than // a. The speed increase within one block would require faster accelleration than the one, true
// the set limit. Finally it will: // constant acceleration.
// //
// 3. Recalculate trapezoids for all blocks. // When these stages are complete all blocks have an entry_factor that will allow all speed changes to
// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than
void planner_recalculate() { // the set limit. Finally it will:
planner_reverse_pass(); //
planner_forward_pass(); // 3. Recalculate trapezoids for all blocks.
planner_recalculate_trapezoids();
} void planner_recalculate() {
planner_reverse_pass();
void plan_init() { planner_forward_pass();
block_buffer_head = 0; planner_recalculate_trapezoids();
block_buffer_tail = 0; }
memset(position, 0, sizeof(position)); // clear position
} void plan_init() {
block_buffer_head = 0;
block_buffer_tail = 0;
void plan_discard_current_block() { memset(position, 0, sizeof(position)); // clear position
if (block_buffer_head != block_buffer_tail) { previous_speed[0] = 0.0;
block_buffer_tail = (block_buffer_tail + 1) & (BLOCK_BUFFER_SIZE - 1); previous_speed[1] = 0.0;
} previous_speed[2] = 0.0;
} previous_speed[3] = 0.0;
previous_nominal_speed = 0.0;
block_t *plan_get_current_block() { }
if (block_buffer_head == block_buffer_tail) {
return(NULL);
} void plan_discard_current_block() {
block_t *block = &block_buffer[block_buffer_tail]; if (block_buffer_head != block_buffer_tail) {
block->busy = true; block_buffer_tail = (block_buffer_tail + 1) & (BLOCK_BUFFER_SIZE - 1);
return(block); }
} }
void check_axes_activity() { block_t *plan_get_current_block() {
unsigned char x_active = 0; if (block_buffer_head == block_buffer_tail) {
unsigned char y_active = 0; return(NULL);
unsigned char z_active = 0; }
unsigned char e_active = 0; block_t *block = &block_buffer[block_buffer_tail];
block_t *block; block->busy = true;
return(block);
if(block_buffer_tail != block_buffer_head) { }
char block_index = block_buffer_tail;
while(block_index != block_buffer_head) { void check_axes_activity() {
block = &block_buffer[block_index]; unsigned char x_active = 0;
if(block->steps_x != 0) x_active++; unsigned char y_active = 0;
if(block->steps_y != 0) y_active++; unsigned char z_active = 0;
if(block->steps_z != 0) z_active++; unsigned char e_active = 0;
if(block->steps_e != 0) e_active++; block_t *block;
block_index = (block_index+1) & (BLOCK_BUFFER_SIZE - 1);
} if(block_buffer_tail != block_buffer_head) {
} char block_index = block_buffer_tail;
if((DISABLE_X) && (x_active == 0)) disable_x(); while(block_index != block_buffer_head) {
if((DISABLE_Y) && (y_active == 0)) disable_y(); block = &block_buffer[block_index];
if((DISABLE_Z) && (z_active == 0)) disable_z(); if(block->steps_x != 0) x_active++;
if((DISABLE_E) && (e_active == 0)) disable_e(); if(block->steps_y != 0) y_active++;
} if(block->steps_z != 0) z_active++;
if(block->steps_e != 0) e_active++;
// Add a new linear movement to the buffer. steps_x, _y and _z is the absolute position in block_index = (block_index+1) & (BLOCK_BUFFER_SIZE - 1);
// 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(const float &x, const float &y, const float &z, const float &e, float feed_rate) if((DISABLE_X) && (x_active == 0)) disable_x();
{ if((DISABLE_Y) && (y_active == 0)) disable_y();
// Calculate the buffer head after we push this byte if((DISABLE_Z) && (z_active == 0)) disable_z();
int next_buffer_head = (block_buffer_head + 1) & (BLOCK_BUFFER_SIZE - 1); if((DISABLE_E) && (e_active == 0)) disable_e();
}
// If the buffer is full: good! That means we are well ahead of the robot.
// Rest here until there is room in the buffer.
while(block_buffer_tail == next_buffer_head) { float junction_deviation = 0.1;
manage_heater(); // Add a new linear movement to the buffer. steps_x, _y and _z is the absolute position in
manage_inactivity(1); // mm. Microseconds specify how many microseconds the move should take to perform. To aid acceleration
LCD_STATUS; // calculation the caller must also provide the physical length of the line in millimeters.
} void plan_buffer_line(const float &x, const float &y, const float &z, const float &e, float feed_rate)
{
// The target position of the tool in absolute steps // Calculate the buffer head after we push this byte
// Calculate target position in absolute steps int next_buffer_head = next_block_index(block_buffer_head);
//this should be done after the wait, because otherwise a M92 code within the gcode disrupts this calculation somehow
long target[4]; // If the buffer is full: good! That means we are well ahead of the robot.
target[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]); // Rest here until there is room in the buffer.
target[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]); while(block_buffer_tail == next_buffer_head) {
target[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); manage_heater();
target[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); manage_inactivity(1);
LCD_STATUS;
// Prepare to set up new block }
block_t *block = &block_buffer[block_buffer_head];
// The target position of the tool in absolute steps
// Mark block as not busy (Not executed by the stepper interrupt) // Calculate target position in absolute steps
block->busy = false; //this should be done after the wait, because otherwise a M92 code within the gcode disrupts this calculation somehow
long target[4];
// Number of steps for each axis target[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]);
block->steps_x = labs(target[X_AXIS]-position[X_AXIS]); target[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]);
block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]); target[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]);
block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]); target[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]);
block->steps_e = labs(target[E_AXIS]-position[E_AXIS]);
block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e))); // Prepare to set up new block
block_t *block = &block_buffer[block_buffer_head];
// Bail if this is a zero-length block
if (block->step_event_count <=dropsegments) { // Mark block as not busy (Not executed by the stepper interrupt)
return; block->busy = false;
};
// Number of steps for each axis
//enable active axes block->steps_x = labs(target[X_AXIS]-position[X_AXIS]);
if(block->steps_x != 0) enable_x(); block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]);
if(block->steps_y != 0) enable_y(); block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]);
if(block->steps_z != 0) enable_z(); block->steps_e = labs(target[E_AXIS]-position[E_AXIS]);
if(block->steps_e != 0) enable_e(); block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e)));
float delta_x_mm = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; // Bail if this is a zero-length block
float delta_y_mm = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; if (block->step_event_count <=dropsegments) { return; };
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]; // Compute direction bits for this block
block->millimeters = sqrt(square(delta_x_mm) + square(delta_y_mm) + square(delta_z_mm) + square(delta_e_mm)); block->direction_bits = 0;
if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<<X_AXIS); }
unsigned long microseconds; if (target[Y_AXIS] < position[Y_AXIS]) { block->direction_bits |= (1<<Y_AXIS); }
if (target[Z_AXIS] < position[Z_AXIS]) { block->direction_bits |= (1<<Z_AXIS); }
if (block->steps_e == 0) { if (target[E_AXIS] < position[E_AXIS]) { block->direction_bits |= (1<<E_AXIS); }
if(feed_rate<mintravelfeedrate) feed_rate=mintravelfeedrate;
} //enable active axes
else { if(block->steps_x != 0) enable_x();
if(feed_rate<minimumfeedrate) feed_rate=minimumfeedrate; if(block->steps_y != 0) enable_y();
} if(block->steps_z != 0) enable_z();
if(block->steps_e != 0) enable_e();
microseconds = lround((block->millimeters/feed_rate)*1000000);
float delta_mm[4];
// slow down when de buffer starts to empty, rather than wait at the corner for a buffer refill delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS];
// reduces/removes corner blobs as the machine won't come to a full stop. delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS];
int blockcount=(block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); 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];
if ((blockcount>0) && (blockcount < (BLOCK_BUFFER_SIZE - 4))) { block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) +
if (microseconds<minsegmenttime) { // buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more. square(delta_mm[Z_AXIS]));
microseconds=microseconds+lround(2*(minsegmenttime-microseconds)/blockcount); 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.
else { float inverse_second = feed_rate * inverse_millimeters;
if (microseconds<minsegmenttime) microseconds=minsegmenttime;
} block->nominal_speed = block->millimeters * inverse_second; // (mm/sec) Always > 0
// END OF SLOW DOWN SECTION block->nominal_rate = ceil(block->step_event_count * inverse_second); // (step/sec) Always > 0
// unsigned long microseconds;
// Calculate speed in mm/minute for each axis #if 0
float multiplier = 60.0*1000000.0/microseconds; if (block->steps_e == 0) {
block->speed_z = delta_z_mm * multiplier; if(feed_rate<mintravelfeedrate) feed_rate=mintravelfeedrate;
block->speed_x = delta_x_mm * multiplier; }
block->speed_y = delta_y_mm * multiplier; else {
block->speed_e = delta_e_mm * multiplier; if(feed_rate<minimumfeedrate) feed_rate=minimumfeedrate;
}
// Limit speed per axis microseconds = lround((block->millimeters/feed_rate)*1000000);
float speed_factor = 1; //factor <=1 do decrease speed
if(abs(block->speed_x) > max_feedrate[X_AXIS]) { // slow down when de buffer starts to empty, rather than wait at the corner for a buffer refill
speed_factor = max_feedrate[X_AXIS] / abs(block->speed_x); // reduces/removes corner blobs as the machine won't come to a full stop.
//if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; /is not need here because auf the init above int blockcount=(block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1);
}
if(abs(block->speed_y) > max_feedrate[Y_AXIS]){ if ((blockcount>0) && (blockcount < (BLOCK_BUFFER_SIZE - 4))) {
float tmp_speed_factor = max_feedrate[Y_AXIS] / abs(block->speed_y); if (microseconds<minsegmenttime) { // buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more.
if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; microseconds=microseconds+lround(2*(minsegmenttime-microseconds)/blockcount);
} }
if(abs(block->speed_z) > max_feedrate[Z_AXIS]){ }
float tmp_speed_factor = max_feedrate[Z_AXIS] / abs(block->speed_z); else {
if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor; if (microseconds<minsegmenttime) microseconds=minsegmenttime;
} }
if(abs(block->speed_e) > max_feedrate[E_AXIS]){ // END OF SLOW DOWN SECTION
float tmp_speed_factor = max_feedrate[E_AXIS] / abs(block->speed_e); #endif
if(speed_factor > tmp_speed_factor) speed_factor = tmp_speed_factor;
} // Calculate speed in mm/sec for each axis
multiplier = multiplier * speed_factor; float current_speed[4];
block->speed_z = delta_z_mm * multiplier; for(int i=0; i < 4; i++) {
block->speed_x = delta_x_mm * multiplier; current_speed[i] = delta_mm[i] * inverse_second;
block->speed_y = delta_y_mm * multiplier; }
block->speed_e = delta_e_mm * multiplier;
block->nominal_speed = block->millimeters * multiplier; // Limit speed per axis
block->nominal_rate = ceil(block->step_event_count * multiplier / 60); float speed_factor = 1.0; //factor <=1 do decrease speed
for(int i=0; i < 4; i++) {
if(block->nominal_rate < 120) if(abs(current_speed[i]) > max_feedrate[i])
block->nominal_rate = 120; speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i]));
block->entry_speed = safe_speed(block); }
// Compute the acceleration rate for the trapezoid generator. // Max segement time in us.
float travel_per_step = block->millimeters/block->step_event_count;
if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) { #ifdef XY_FREQUENCY_LIMIT
block->acceleration_st = ceil( (retract_acceleration)/travel_per_step); // convert to: acceleration steps/sec^2 #define MAX_FREQ_TIME (1000000.0/XY_FREQUENCY_LIMIT)
}
else { // Check and limit the xy direction change frequency
block->acceleration_st = ceil( (acceleration)/travel_per_step); // convert to: acceleration steps/sec^2 unsigned char direction_change = block->direction_bits ^ old_direction_bits;
float tmp_acceleration = (float)block->acceleration_st / (float)block->step_event_count; old_direction_bits = block->direction_bits;
// Limit acceleration per axis long segment_time = lround(1000000.0/inverse_second);
if((tmp_acceleration * block->steps_x) > axis_steps_per_sqr_second[X_AXIS]) { if((direction_change & (1<<X_AXIS)) == 0) {
block->acceleration_st = axis_steps_per_sqr_second[X_AXIS]; x_segment_time[0] += segment_time;
tmp_acceleration = (float)block->acceleration_st / (float)block->step_event_count; }
} else {
if((tmp_acceleration * block->steps_y) > axis_steps_per_sqr_second[Y_AXIS]) { x_segment_time[2] = x_segment_time[1];
block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS]; x_segment_time[1] = x_segment_time[0];
tmp_acceleration = (float)block->acceleration_st / (float)block->step_event_count; x_segment_time[0] = segment_time;
} }
if((tmp_acceleration * block->steps_e) > axis_steps_per_sqr_second[E_AXIS]) { if((direction_change & (1<<Y_AXIS)) == 0) {
block->acceleration_st = axis_steps_per_sqr_second[E_AXIS]; y_segment_time[0] += segment_time;
tmp_acceleration = (float)block->acceleration_st / (float)block->step_event_count; }
} else {
if((tmp_acceleration * block->steps_z) > axis_steps_per_sqr_second[Z_AXIS]) { y_segment_time[2] = y_segment_time[1];
block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS]; y_segment_time[1] = y_segment_time[0];
tmp_acceleration = (float)block->acceleration_st / (float)block->step_event_count; y_segment_time[0] = segment_time;
} }
} long max_x_segment_time = max(x_segment_time[0], max(x_segment_time[1], x_segment_time[2]));
block->acceleration = block->acceleration_st * travel_per_step; long max_y_segment_time = max(y_segment_time[0], max(y_segment_time[1], y_segment_time[2]));
block->acceleration_rate = (long)((float)block->acceleration_st * 8.388608); long min_xy_segment_time =min(max_x_segment_time, max_y_segment_time);
if(min_xy_segment_time < MAX_FREQ_TIME) speed_factor = min(speed_factor, (float)min_xy_segment_time / (float)MAX_FREQ_TIME);
#ifdef ADVANCE #endif
// Calculate advance rate
if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0)) {
block->advance_rate = 0; // Correct the speed
block->advance = 0; if( speed_factor < 1.0) {
} // Serial.print("speed factor : "); Serial.println(speed_factor);
else { for(int i=0; i < 4; i++) {
long acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); if(abs(current_speed[i]) > max_feedrate[i])
float advance = (STEPS_PER_CUBIC_MM_E * EXTRUDER_ADVANCE_K) * speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i]));
(block->speed_e * block->speed_e * EXTRUTION_AREA * EXTRUTION_AREA / 3600.0)*65536; // Serial.print("current_speed"); Serial.print(i); Serial.print(" : "); Serial.println(current_speed[i]);
block->advance = advance; }
if(acc_dist == 0) { for(unsigned char i=0; i < 4; i++) {
block->advance_rate = 0; current_speed[i] *= speed_factor;
} }
else { block->nominal_speed *= speed_factor;
block->advance_rate = advance / (float)acc_dist; block->nominal_rate *= speed_factor;
} }
}
#endif // ADVANCE // Compute and limit the acceleration rate for the trapezoid generator.
float steps_per_mm = block->step_event_count/block->millimeters;
// compute a preliminary conservative acceleration trapezoid if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) {
float safespeed = safe_speed(block); block->acceleration_st = ceil(retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2
calculate_trapezoid_for_block(block, safespeed, safespeed); }
else {
// Compute direction bits for this block block->acceleration_st = ceil(acceleration * steps_per_mm); // convert to: acceleration steps/sec^2
block->direction_bits = 0; // Limit acceleration per axis
if (target[X_AXIS] < position[X_AXIS]) { if(((float)block->acceleration_st * (float)block->steps_x / (float)block->step_event_count) > axis_steps_per_sqr_second[X_AXIS])
block->direction_bits |= (1<<X_AXIS); block->acceleration_st = axis_steps_per_sqr_second[X_AXIS];
} if(((float)block->acceleration_st * (float)block->steps_y / (float)block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS])
if (target[Y_AXIS] < position[Y_AXIS]) { block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS];
block->direction_bits |= (1<<Y_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 (target[Z_AXIS] < position[Z_AXIS]) { if(((float)block->acceleration_st * (float)block->steps_z / (float)block->step_event_count ) > axis_steps_per_sqr_second[Z_AXIS])
block->direction_bits |= (1<<Z_AXIS); block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS];
} }
if (target[E_AXIS] < position[E_AXIS]) { block->acceleration = block->acceleration_st / steps_per_mm;
block->direction_bits |= (1<<E_AXIS); block->acceleration_rate = (long)((float)block->acceleration_st * 8.388608);
}
#if 0 // Use old jerk for now
// Move buffer head // Compute path unit vector
block_buffer_head = next_buffer_head; double unit_vec[3];
// Update position unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters;
memcpy(position, target, sizeof(target)); // position[] = target[] unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters;
unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters;
planner_recalculate();
st_wake_up(); // 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,
void plan_set_position(const float &x, const float &y, const float &z, const float &e) // 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
position[X_AXIS] = lround(x*axis_steps_per_unit[X_AXIS]); // radius of the circle, defined indirectly by junction deviation. This may be also viewed as
position[Y_AXIS] = lround(y*axis_steps_per_unit[Y_AXIS]); // path width or max_jerk in the previous grbl version. This approach does not actually deviate
position[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); // from path, but used as a robust way to compute cornering speeds, as it takes into account the
position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); // 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)) );
}
}
}
#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 {
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;
}
else {
block->advance_rate = advance / (float)acc_dist;
}
}
#endif // ADVANCE
calculate_trapezoid_for_block(block, block->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();
st_wake_up();
}
void plan_set_position(const float &x, const float &y, const float &z, const 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]);
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;
}

View file

@ -1,93 +1,96 @@
/* /*
planner.h - buffers movement commands and manages the acceleration profile plan planner.h - buffers movement commands and manages the acceleration profile plan
Part of Grbl Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Grbl is distributed in the hope that it will be useful, Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>. along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/ */
// This module is to be considered a sub-module of stepper.c. Please don't include // This module is to be considered a sub-module of stepper.c. Please don't include
// this file from any other module. // this file from any other module.
#ifndef planner_h #ifndef planner_h
#define planner_h #define planner_h
#include "Configuration.h" #include "Configuration.h"
// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in // This struct is used when buffering the setup for each linear movement "nominal" values are as specified in
// the source g-code and may never actually be reached if acceleration management is active. // the source g-code and may never actually be reached if acceleration management is active.
typedef struct { typedef struct {
// Fields used by the bresenham algorithm for tracing the line // 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 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 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 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 decelerate_after; // The index of the step event on which to start decelerating
volatile long acceleration_rate; // The acceleration rate used for acceleration calculation volatile 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) unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
#ifdef ADVANCE #ifdef ADVANCE
long advance_rate; // long advance_rate;
volatile long initial_advance; // volatile long initial_advance;
volatile long final_advance; // volatile long final_advance;
float advance; // float advance;
#endif #endif
// Fields used by the motion planner to manage acceleration // 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 nominal_speed; // The nominal speed for this block in mm/min
float millimeters; // The total travel of this block in mm float entry_speed; // Entry speed at previous-current junction in mm/min
float entry_speed; float max_entry_speed; // Maximum allowable junction entry speed in mm/min
float acceleration; // acceleration mm/sec^2 float millimeters; // The total travel of this block in mm
float acceleration; // acceleration mm/sec^2
// Settings for the trapezoid generator unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction
long nominal_rate; // The nominal step rate for this block in step_events/sec unsigned char nominal_length_flag; // Planner flag for nominal speed always reached
volatile long initial_rate; // The jerk-adjusted step rate at start of block
volatile long final_rate; // The minimal rate at exit // Settings for the trapezoid generator
long acceleration_st; // acceleration steps/sec^2 long nominal_rate; // The nominal step rate for this block in step_events/sec
volatile char busy; volatile long initial_rate; // The jerk-adjusted step rate at start of block
} block_t; volatile long final_rate; // The minimal rate at exit
long acceleration_st; // acceleration steps/sec^2
// Initialize the motion plan subsystem volatile char busy;
void plan_init(); } block_t;
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in // Initialize the motion plan subsystem
// millimaters. Feed rate specifies the speed of the motion. void plan_init();
void plan_buffer_line(const float &x, const float &y, const float &z, const float &e, float feed_rate);
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
// Set position. Used for G92 instructions. // millimaters. Feed rate specifies the speed of the motion.
void plan_set_position(const float &x, const float &y, const float &z, const float &e); void plan_buffer_line(const float &x, const float &y, const float &z, const float &e, float feed_rate);
// Set position. Used for G92 instructions.
// Called when the current block is no longer needed. Discards the block and makes the memory void plan_set_position(const float &x, const float &y, const float &z, const float &e);
// availible for new blocks.
void plan_discard_current_block();
// Called when the current block is no longer needed. Discards the block and makes the memory
// Gets the current block. Returns NULL if buffer empty // availible for new blocks.
block_t *plan_get_current_block(); void plan_discard_current_block();
void check_axes_activity(); // Gets the current block. Returns NULL if buffer empty
block_t *plan_get_current_block();
extern unsigned long minsegmenttime;
extern float max_feedrate[4]; // set the max speeds void check_axes_activity();
extern float axis_steps_per_unit[4];
extern long max_acceleration_units_per_sq_second[4]; // Use M201 to override by software extern unsigned long minsegmenttime;
extern float minimumfeedrate; extern float max_feedrate[4]; // set the max speeds
extern float acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX extern float axis_steps_per_unit[4];
extern float retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX extern long max_acceleration_units_per_sq_second[4]; // Use M201 to override by software
extern float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. extern float minimumfeedrate;
extern float max_z_jerk; extern float acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX
extern float mintravelfeedrate; extern float retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX
extern unsigned long axis_steps_per_sqr_second[NUM_AXIS]; extern float max_xy_jerk; //speed than can be stopped at once, if i understand correctly.
extern float max_z_jerk;
extern float mintravelfeedrate;
extern unsigned long axis_steps_per_sqr_second[NUM_AXIS];
#endif #endif

View file

@ -1,612 +1,617 @@
/* /*
stepper.c - stepper motor driver: executes motion plans using stepper motors stepper.c - stepper motor driver: executes motion plans using stepper motors
Part of Grbl Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Grbl is distributed in the hope that it will be useful, Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>. 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 /* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith
and Philipp Tiefenbacher. */ and Philipp Tiefenbacher. */
#include "stepper.h" #include "stepper.h"
#include "Configuration.h" #include "Configuration.h"
#include "Marlin.h" #include "Marlin.h"
#include "planner.h" #include "planner.h"
#include "pins.h" #include "pins.h"
#include "fastio.h" #include "fastio.h"
#include "temperature.h" #include "temperature.h"
#include "ultralcd.h" #include "ultralcd.h"
#include "speed_lookuptable.h" #include "speed_lookuptable.h"
//=========================================================================== //===========================================================================
//=============================public variables ============================ //=============================public variables ============================
//=========================================================================== //===========================================================================
block_t *current_block; // A pointer to the block currently being traced block_t *current_block; // A pointer to the block currently being traced
//=========================================================================== //===========================================================================
//=============================private variables ============================ //=============================private variables ============================
//=========================================================================== //===========================================================================
//static makes it inpossible to be called from outside of this file by extern.! //static makes it inpossible to be called from outside of this file by extern.!
// Variables used by The Stepper Driver Interrupt // Variables used by The Stepper Driver Interrupt
static unsigned char out_bits; // The next stepping-bits to be output static unsigned char out_bits; // The next stepping-bits to be output
static long counter_x, // Counter variables for the bresenham line tracer static long counter_x, // Counter variables for the bresenham line tracer
counter_y, counter_y,
counter_z, counter_z,
counter_e; counter_e;
static unsigned long step_events_completed; // The number of step events executed in the current block static unsigned long step_events_completed; // The number of step events executed in the current block
#ifdef ADVANCE #ifdef ADVANCE
static long advance_rate, advance, final_advance = 0; static long advance_rate, advance, final_advance = 0;
static short old_advance = 0; static short old_advance = 0;
static short e_steps; static short e_steps;
#endif #endif
static unsigned char busy = false; // TRUE when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler. static unsigned char busy = false; // TRUE when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler.
static long acceleration_time, deceleration_time; static long acceleration_time, deceleration_time;
//static unsigned long accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate; //static unsigned long accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate;
static unsigned short acc_step_rate; // needed for deccelaration start point static unsigned short acc_step_rate; // needed for deccelaration start point
static char step_loops; static char step_loops;
// if DEBUG_STEPS is enabled, M114 can be used to compare two methods of determining the X,Y,Z position of the printer. // if DEBUG_STEPS is enabled, M114 can be used to compare two methods of determining the X,Y,Z position of the printer.
// for debugging purposes only, should be disabled by default // for debugging purposes only, should be disabled by default
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
volatile long count_position[NUM_AXIS] = { 0, 0, 0, 0}; volatile long count_position[NUM_AXIS] = { 0, 0, 0, 0};
volatile int count_direction[NUM_AXIS] = { 1, 1, 1, 1}; volatile int count_direction[NUM_AXIS] = { 1, 1, 1, 1};
#endif #endif
//=========================================================================== //===========================================================================
//=============================functions ============================ //=============================functions ============================
//=========================================================================== //===========================================================================
// intRes = intIn1 * intIn2 >> 16 // intRes = intIn1 * intIn2 >> 16
// uses: // uses:
// r26 to store 0 // r26 to store 0
// r27 to store the byte 1 of the 24 bit result // r27 to store the byte 1 of the 24 bit result
#define MultiU16X8toH16(intRes, charIn1, intIn2) \ #define MultiU16X8toH16(intRes, charIn1, intIn2) \
asm volatile ( \ asm volatile ( \
"clr r26 \n\t" \ "clr r26 \n\t" \
"mul %A1, %B2 \n\t" \ "mul %A1, %B2 \n\t" \
"movw %A0, r0 \n\t" \ "movw %A0, r0 \n\t" \
"mul %A1, %A2 \n\t" \ "mul %A1, %A2 \n\t" \
"add %A0, r1 \n\t" \ "add %A0, r1 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"lsr r0 \n\t" \ "lsr r0 \n\t" \
"adc %A0, r26 \n\t" \ "adc %A0, r26 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"clr r1 \n\t" \ "clr r1 \n\t" \
: \ : \
"=&r" (intRes) \ "=&r" (intRes) \
: \ : \
"d" (charIn1), \ "d" (charIn1), \
"d" (intIn2) \ "d" (intIn2) \
: \ : \
"r26" \ "r26" \
) )
// intRes = longIn1 * longIn2 >> 24 // intRes = longIn1 * longIn2 >> 24
// uses: // uses:
// r26 to store 0 // r26 to store 0
// r27 to store the byte 1 of the 48bit result // r27 to store the byte 1 of the 48bit result
#define MultiU24X24toH16(intRes, longIn1, longIn2) \ #define MultiU24X24toH16(intRes, longIn1, longIn2) \
asm volatile ( \ asm volatile ( \
"clr r26 \n\t" \ "clr r26 \n\t" \
"mul %A1, %B2 \n\t" \ "mul %A1, %B2 \n\t" \
"mov r27, r1 \n\t" \ "mov r27, r1 \n\t" \
"mul %B1, %C2 \n\t" \ "mul %B1, %C2 \n\t" \
"movw %A0, r0 \n\t" \ "movw %A0, r0 \n\t" \
"mul %C1, %C2 \n\t" \ "mul %C1, %C2 \n\t" \
"add %B0, r0 \n\t" \ "add %B0, r0 \n\t" \
"mul %C1, %B2 \n\t" \ "mul %C1, %B2 \n\t" \
"add %A0, r0 \n\t" \ "add %A0, r0 \n\t" \
"adc %B0, r1 \n\t" \ "adc %B0, r1 \n\t" \
"mul %A1, %C2 \n\t" \ "mul %A1, %C2 \n\t" \
"add r27, r0 \n\t" \ "add r27, r0 \n\t" \
"adc %A0, r1 \n\t" \ "adc %A0, r1 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"mul %B1, %B2 \n\t" \ "mul %B1, %B2 \n\t" \
"add r27, r0 \n\t" \ "add r27, r0 \n\t" \
"adc %A0, r1 \n\t" \ "adc %A0, r1 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"mul %C1, %A2 \n\t" \ "mul %C1, %A2 \n\t" \
"add r27, r0 \n\t" \ "add r27, r0 \n\t" \
"adc %A0, r1 \n\t" \ "adc %A0, r1 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"mul %B1, %A2 \n\t" \ "mul %B1, %A2 \n\t" \
"add r27, r1 \n\t" \ "add r27, r1 \n\t" \
"adc %A0, r26 \n\t" \ "adc %A0, r26 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"lsr r27 \n\t" \ "lsr r27 \n\t" \
"adc %A0, r26 \n\t" \ "adc %A0, r26 \n\t" \
"adc %B0, r26 \n\t" \ "adc %B0, r26 \n\t" \
"clr r1 \n\t" \ "clr r1 \n\t" \
: \ : \
"=&r" (intRes) \ "=&r" (intRes) \
: \ : \
"d" (longIn1), \ "d" (longIn1), \
"d" (longIn2) \ "d" (longIn2) \
: \ : \
"r26" , "r27" \ "r26" , "r27" \
) )
// Some useful constants // Some useful constants
#define ENABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 |= (1<<OCIE1A) #define ENABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 |= (1<<OCIE1A)
#define DISABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 &= ~(1<<OCIE1A) #define DISABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 &= ~(1<<OCIE1A)
// __________________________ // __________________________
// /| |\ _________________ ^ // /| |\ _________________ ^
// / | | \ /| |\ | // / | | \ /| |\ |
// / | | \ / | | \ s // / | | \ / | | \ s
// / | | | | | \ p // / | | | | | \ p
// / | | | | | \ e // / | | | | | \ e
// +-----+------------------------+---+--+---------------+----+ e // +-----+------------------------+---+--+---------------+----+ e
// | BLOCK 1 | BLOCK 2 | d // | BLOCK 1 | BLOCK 2 | d
// //
// time -----> // time ----->
// //
// The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates // The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates
// first block->accelerate_until step_events_completed, then keeps going at constant speed until // 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. // step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset.
// The slope of acceleration is calculated with the leib ramp alghorithm. // The slope of acceleration is calculated with the leib ramp alghorithm.
void st_wake_up() { void st_wake_up() {
// TCNT1 = 0; // TCNT1 = 0;
ENABLE_STEPPER_DRIVER_INTERRUPT(); if(busy == false)
} ENABLE_STEPPER_DRIVER_INTERRUPT();
}
inline unsigned short calc_timer(unsigned short step_rate) {
unsigned short timer; inline unsigned short calc_timer(unsigned short step_rate) {
if(step_rate > MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY; unsigned short timer;
if(step_rate > MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY;
if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times
step_rate = step_rate >> 2; if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times
step_loops = 4; step_rate = step_rate >> 2;
} step_loops = 4;
else if(step_rate > 10000) { // If steprate > 10kHz >> step 2 times }
step_rate = step_rate >> 1; else if(step_rate > 10000) { // If steprate > 10kHz >> step 2 times
step_loops = 2; step_rate = step_rate >> 1;
} step_loops = 2;
else { }
step_loops = 1; else {
} step_loops = 1;
}
if(step_rate < 32) step_rate = 32;
step_rate -= 32; // Correct for minimal speed if(step_rate < 32) step_rate = 32;
if(step_rate >= (8*256)){ // higher step rate step_rate -= 32; // Correct for minimal speed
unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; if(step_rate >= (8*256)){ // higher step rate
unsigned char tmp_step_rate = (step_rate & 0x00ff); unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0];
unsigned short gain = (unsigned short)pgm_read_word_near(table_address+2); unsigned char tmp_step_rate = (step_rate & 0x00ff);
MultiU16X8toH16(timer, tmp_step_rate, gain); unsigned short gain = (unsigned short)pgm_read_word_near(table_address+2);
timer = (unsigned short)pgm_read_word_near(table_address) - timer; MultiU16X8toH16(timer, tmp_step_rate, gain);
} timer = (unsigned short)pgm_read_word_near(table_address) - timer;
else { // lower step rates }
unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0]; else { // lower step rates
table_address += ((step_rate)>>1) & 0xfffc; unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0];
timer = (unsigned short)pgm_read_word_near(table_address); table_address += ((step_rate)>>1) & 0xfffc;
timer -= (((unsigned short)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); 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; }
return timer; //if(timer < 100) timer = 100;
} return timer;
}
// Initializes the trapezoid generator from the current block. Called whenever a new
// block begins. // Initializes the trapezoid generator from the current block. Called whenever a new
inline void trapezoid_generator_reset() { // block begins.
#ifdef ADVANCE inline void trapezoid_generator_reset() {
advance = current_block->initial_advance; #ifdef ADVANCE
final_advance = current_block->final_advance; advance = current_block->initial_advance;
#endif final_advance = current_block->final_advance;
deceleration_time = 0; #endif
// advance_rate = current_block->advance_rate; deceleration_time = 0;
// step_rate to timer interval // step_rate to timer interval
acc_step_rate = current_block->initial_rate; acc_step_rate = current_block->initial_rate;
acceleration_time = calc_timer(acc_step_rate); acceleration_time = calc_timer(acc_step_rate);
OCR1A = acceleration_time; OCR1A = acceleration_time;
} }
// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. // "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. // It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately.
ISR(TIMER1_COMPA_vect) ISR(TIMER1_COMPA_vect)
{ {
if(busy){ if(busy){
SERIAL_ERRORLN(*(unsigned short *)OCR1A<< " ISR overtaking itself."); /* SERIAL_ERRORLN(*(unsigned short *)OCR1A<< " ISR overtaking itself.");*/
return; return;
} // The busy-flag is used to avoid reentering this interrupt } // The busy-flag is used to avoid reentering this interrupt
busy = true; busy = true;
sei(); // Re enable interrupts (normally disabled while inside an interrupt handler) 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 there is no current block, attempt to pop one from the buffer
if (current_block == NULL) { if (current_block == NULL) {
// Anything in the buffer? // Anything in the buffer?
current_block = plan_get_current_block(); current_block = plan_get_current_block();
if (current_block != NULL) { if (current_block != NULL) {
trapezoid_generator_reset(); trapezoid_generator_reset();
counter_x = -(current_block->step_event_count >> 1); counter_x = -(current_block->step_event_count >> 1);
counter_y = counter_x; counter_y = counter_x;
counter_z = counter_x; counter_z = counter_x;
counter_e = counter_x; counter_e = counter_x;
step_events_completed = 0; step_events_completed = 0;
#ifdef ADVANCE #ifdef ADVANCE
e_steps = 0; e_steps = 0;
#endif #endif
} }
else { else {
// DISABLE_STEPPER_DRIVER_INTERRUPT(); // DISABLE_STEPPER_DRIVER_INTERRUPT();
} }
} }
if (current_block != NULL) { if (current_block != NULL) {
// Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt // Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt
out_bits = current_block->direction_bits; out_bits = current_block->direction_bits;
#ifdef ADVANCE #ifdef ADVANCE
// Calculate E early. // Calculate E early.
counter_e += current_block->steps_e; counter_e += current_block->steps_e;
if (counter_e > 0) { if (counter_e > 0) {
counter_e -= current_block->step_event_count; counter_e -= current_block->step_event_count;
if ((out_bits & (1<<E_AXIS)) != 0) { // - direction if ((out_bits & (1<<E_AXIS)) != 0) { // - direction
CRITICAL_SECTION_START; CRITICAL_SECTION_START;
e_steps--; e_steps--;
CRITICAL_SECTION_END; CRITICAL_SECTION_END;
} }
else { else {
CRITICAL_SECTION_START; CRITICAL_SECTION_START;
e_steps++; e_steps++;
CRITICAL_SECTION_END; CRITICAL_SECTION_END;
} }
} }
// Do E steps + advance steps // Do E steps + advance steps
CRITICAL_SECTION_START; CRITICAL_SECTION_START;
e_steps += ((advance >> 16) - old_advance); e_steps += ((advance >> 16) - old_advance);
CRITICAL_SECTION_END; CRITICAL_SECTION_END;
old_advance = advance >> 16; old_advance = advance >> 16;
#endif //ADVANCE #endif //ADVANCE
// Set direction en check limit switches // Set direction en check limit switches
if ((out_bits & (1<<X_AXIS)) != 0) { // -direction if ((out_bits & (1<<X_AXIS)) != 0) { // -direction
WRITE(X_DIR_PIN, INVERT_X_DIR); WRITE(X_DIR_PIN, INVERT_X_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[X_AXIS]=-1; count_direction[X_AXIS]=-1;
#endif #endif
#if X_MIN_PIN > -1 #if X_MIN_PIN > -1
if(READ(X_MIN_PIN) != ENDSTOPS_INVERTING) { if(READ(X_MIN_PIN) != ENDSTOPS_INVERTING) {
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
else { // +direction else { // +direction
WRITE(X_DIR_PIN,!INVERT_X_DIR); WRITE(X_DIR_PIN,!INVERT_X_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[X_AXIS]=1; count_direction[X_AXIS]=1;
#endif #endif
#if X_MAX_PIN > -1 #if X_MAX_PIN > -1
if((READ(X_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_x >0)){ if((READ(X_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_x >0)){
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
if ((out_bits & (1<<Y_AXIS)) != 0) { // -direction if ((out_bits & (1<<Y_AXIS)) != 0) { // -direction
WRITE(Y_DIR_PIN,INVERT_Y_DIR); WRITE(Y_DIR_PIN,INVERT_Y_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[Y_AXIS]=-1; count_direction[Y_AXIS]=-1;
#endif #endif
#if Y_MIN_PIN > -1 #if Y_MIN_PIN > -1
if(READ(Y_MIN_PIN) != ENDSTOPS_INVERTING) { if(READ(Y_MIN_PIN) != ENDSTOPS_INVERTING) {
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
else { // +direction else { // +direction
WRITE(Y_DIR_PIN,!INVERT_Y_DIR); WRITE(Y_DIR_PIN,!INVERT_Y_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[Y_AXIS]=1; count_direction[Y_AXIS]=1;
#endif #endif
#if Y_MAX_PIN > -1 #if Y_MAX_PIN > -1
if((READ(Y_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_y >0)){ if((READ(Y_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_y >0)){
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
if ((out_bits & (1<<Z_AXIS)) != 0) { // -direction if ((out_bits & (1<<Z_AXIS)) != 0) { // -direction
WRITE(Z_DIR_PIN,INVERT_Z_DIR); WRITE(Z_DIR_PIN,INVERT_Z_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[Z_AXIS]=-1; count_direction[Z_AXIS]=-1;
#endif #endif
#if Z_MIN_PIN > -1 #if Z_MIN_PIN > -1
if(READ(Z_MIN_PIN) != ENDSTOPS_INVERTING) { if(READ(Z_MIN_PIN) != ENDSTOPS_INVERTING) {
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
else { // +direction else { // +direction
WRITE(Z_DIR_PIN,!INVERT_Z_DIR); WRITE(Z_DIR_PIN,!INVERT_Z_DIR);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_direction[Z_AXIS]=1; count_direction[Z_AXIS]=1;
#endif #endif
#if Z_MAX_PIN > -1 #if Z_MAX_PIN > -1
if((READ(Z_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_z >0)){ if((READ(Z_MAX_PIN) != ENDSTOPS_INVERTING) && (current_block->steps_z >0)){
step_events_completed = current_block->step_event_count; step_events_completed = current_block->step_event_count;
} }
#endif #endif
} }
#ifndef ADVANCE #ifndef ADVANCE
if ((out_bits & (1<<E_AXIS)) != 0) // -direction if ((out_bits & (1<<E_AXIS)) != 0) // -direction
WRITE(E_DIR_PIN,INVERT_E_DIR); WRITE(E_DIR_PIN,INVERT_E_DIR);
else // +direction else // +direction
WRITE(E_DIR_PIN,!INVERT_E_DIR); WRITE(E_DIR_PIN,!INVERT_E_DIR);
#endif //!ADVANCE #endif //!ADVANCE
for(int8_t i=0; i < step_loops; i++) { // Take multiple steps per interrupt (For high speed moves) for(int8_t i=0; i < step_loops; i++) { // Take multiple steps per interrupt (For high speed moves)
counter_x += current_block->steps_x; counter_x += current_block->steps_x;
if (counter_x > 0) { if (counter_x > 0) {
WRITE(X_STEP_PIN, HIGH); WRITE(X_STEP_PIN, HIGH);
counter_x -= current_block->step_event_count; counter_x -= current_block->step_event_count;
WRITE(X_STEP_PIN, LOW); WRITE(X_STEP_PIN, LOW);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_position[X_AXIS]+=count_direction[X_AXIS]; count_position[X_AXIS]+=count_direction[X_AXIS];
#endif #endif
} }
counter_y += current_block->steps_y; counter_y += current_block->steps_y;
if (counter_y > 0) { if (counter_y > 0) {
WRITE(Y_STEP_PIN, HIGH); WRITE(Y_STEP_PIN, HIGH);
counter_y -= current_block->step_event_count; counter_y -= current_block->step_event_count;
WRITE(Y_STEP_PIN, LOW); WRITE(Y_STEP_PIN, LOW);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_position[Y_AXIS]+=count_direction[Y_AXIS]; count_position[Y_AXIS]+=count_direction[Y_AXIS];
#endif #endif
} }
counter_z += current_block->steps_z; counter_z += current_block->steps_z;
if (counter_z > 0) { if (counter_z > 0) {
WRITE(Z_STEP_PIN, HIGH); WRITE(Z_STEP_PIN, HIGH);
counter_z -= current_block->step_event_count; counter_z -= current_block->step_event_count;
WRITE(Z_STEP_PIN, LOW); WRITE(Z_STEP_PIN, LOW);
#ifdef DEBUG_STEPS #ifdef DEBUG_STEPS
count_position[Z_AXIS]+=count_direction[Z_AXIS]; count_position[Z_AXIS]+=count_direction[Z_AXIS];
#endif #endif
} }
#ifndef ADVANCE #ifndef ADVANCE
counter_e += current_block->steps_e; counter_e += current_block->steps_e;
if (counter_e > 0) { if (counter_e > 0) {
WRITE(E_STEP_PIN, HIGH); WRITE(E_STEP_PIN, HIGH);
counter_e -= current_block->step_event_count; counter_e -= current_block->step_event_count;
WRITE(E_STEP_PIN, LOW); WRITE(E_STEP_PIN, LOW);
} }
#endif //!ADVANCE #endif //!ADVANCE
step_events_completed += 1; step_events_completed += 1;
if(step_events_completed >= current_block->step_event_count) break; if(step_events_completed >= current_block->step_event_count) break;
} }
// Calculare new timer value // Calculare new timer value
unsigned short timer; unsigned short timer;
unsigned short step_rate; unsigned short step_rate;
if (step_events_completed <= current_block->accelerate_until) { if (step_events_completed <= current_block->accelerate_until) {
MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate); MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate);
acc_step_rate += current_block->initial_rate; acc_step_rate += current_block->initial_rate;
// upper limit // upper limit
if(acc_step_rate > current_block->nominal_rate) if(acc_step_rate > current_block->nominal_rate)
acc_step_rate = current_block->nominal_rate; acc_step_rate = current_block->nominal_rate;
// step_rate to timer interval // step_rate to timer interval
timer = calc_timer(acc_step_rate); timer = calc_timer(acc_step_rate);
#ifdef ADVANCE #ifdef ADVANCE
advance += advance_rate; advance += advance_rate;
#endif #endif
acceleration_time += timer; acceleration_time += timer;
OCR1A = timer; OCR1A = timer;
} }
else if (step_events_completed > current_block->decelerate_after) { else if (step_events_completed > current_block->decelerate_after) {
MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate);
if(step_rate > acc_step_rate) { // Check step_rate stays positive if(step_rate > acc_step_rate) { // Check step_rate stays positive
step_rate = current_block->final_rate; step_rate = current_block->final_rate;
} }
else { else {
step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point.
} }
// lower limit // lower limit
if(step_rate < current_block->final_rate) if(step_rate < current_block->final_rate)
step_rate = current_block->final_rate; step_rate = current_block->final_rate;
// step_rate to timer interval // step_rate to timer interval
timer = calc_timer(step_rate); timer = calc_timer(step_rate);
#ifdef ADVANCE #ifdef ADVANCE
advance -= advance_rate; advance -= advance_rate;
if(advance < final_advance) if(advance < final_advance)
advance = final_advance; advance = final_advance;
#endif //ADVANCE #endif //ADVANCE
deceleration_time += timer; deceleration_time += timer;
OCR1A = timer; OCR1A = timer;
} }
// If current block is finished, reset pointer else {
if (step_events_completed >= current_block->step_event_count) { timer = calc_timer(current_block->nominal_rate);
current_block = NULL; OCR1A = timer;
plan_discard_current_block(); }
}
} // If current block is finished, reset pointer
cli(); // disable interrupts if (step_events_completed >= current_block->step_event_count) {
busy=false; current_block = NULL;
} plan_discard_current_block();
}
#ifdef ADVANCE }
unsigned char old_OCR0A; cli(); // disable interrupts
// Timer interrupt for E. e_steps is set in the main routine; busy=false;
// Timer 0 is shared with millies }
ISR(TIMER0_COMPA_vect)
{ #ifdef ADVANCE
// Critical section needed because Timer 1 interrupt has higher priority. unsigned char old_OCR0A;
// The pin set functions are placed on trategic position to comply with the stepper driver timing. // Timer interrupt for E. e_steps is set in the main routine;
WRITE(E_STEP_PIN, LOW); // Timer 0 is shared with millies
// Set E direction (Depends on E direction + advance) ISR(TIMER0_COMPA_vect)
if (e_steps < 0) { {
WRITE(E_DIR_PIN,INVERT_E_DIR); // Critical section needed because Timer 1 interrupt has higher priority.
e_steps++; // The pin set functions are placed on trategic position to comply with the stepper driver timing.
WRITE(E_STEP_PIN, HIGH); WRITE(E_STEP_PIN, LOW);
} // Set E direction (Depends on E direction + advance)
if (e_steps > 0) { if (e_steps < 0) {
WRITE(E_DIR_PIN,!INVERT_E_DIR); WRITE(E_DIR_PIN,INVERT_E_DIR);
e_steps--; e_steps++;
WRITE(E_STEP_PIN, HIGH); WRITE(E_STEP_PIN, HIGH);
} }
old_OCR0A += 25; // 10kHz interrupt if (e_steps > 0) {
OCR0A = old_OCR0A; WRITE(E_DIR_PIN,!INVERT_E_DIR);
} e_steps--;
#endif // ADVANCE WRITE(E_STEP_PIN, HIGH);
}
void st_init() old_OCR0A += 25; // 10kHz interrupt
{ OCR0A = old_OCR0A;
//Initialize Dir Pins }
#if X_DIR_PIN > -1 #endif // ADVANCE
SET_OUTPUT(X_DIR_PIN);
#endif void st_init()
#if Y_DIR_PIN > -1 {
SET_OUTPUT(Y_DIR_PIN); //Initialize Dir Pins
#endif #if X_DIR_PIN > -1
#if Z_DIR_PIN > -1 SET_OUTPUT(X_DIR_PIN);
SET_OUTPUT(Z_DIR_PIN); #endif
#endif #if Y_DIR_PIN > -1
#if E_DIR_PIN > -1 SET_OUTPUT(Y_DIR_PIN);
SET_OUTPUT(E_DIR_PIN); #endif
#endif #if Z_DIR_PIN > -1
SET_OUTPUT(Z_DIR_PIN);
//Initialize Enable Pins - steppers default to disabled. #endif
#if E_DIR_PIN > -1
#if (X_ENABLE_PIN > -1) SET_OUTPUT(E_DIR_PIN);
SET_OUTPUT(X_ENABLE_PIN); #endif
if(!X_ENABLE_ON) WRITE(X_ENABLE_PIN,HIGH);
#endif //Initialize Enable Pins - steppers default to disabled.
#if (Y_ENABLE_PIN > -1)
SET_OUTPUT(Y_ENABLE_PIN); #if (X_ENABLE_PIN > -1)
if(!Y_ENABLE_ON) WRITE(Y_ENABLE_PIN,HIGH); SET_OUTPUT(X_ENABLE_PIN);
#endif if(!X_ENABLE_ON) WRITE(X_ENABLE_PIN,HIGH);
#if (Z_ENABLE_PIN > -1) #endif
SET_OUTPUT(Z_ENABLE_PIN); #if (Y_ENABLE_PIN > -1)
if(!Z_ENABLE_ON) WRITE(Z_ENABLE_PIN,HIGH); SET_OUTPUT(Y_ENABLE_PIN);
#endif if(!Y_ENABLE_ON) WRITE(Y_ENABLE_PIN,HIGH);
#if (E_ENABLE_PIN > -1) #endif
SET_OUTPUT(E_ENABLE_PIN); #if (Z_ENABLE_PIN > -1)
if(!E_ENABLE_ON) WRITE(E_ENABLE_PIN,HIGH); SET_OUTPUT(Z_ENABLE_PIN);
#endif if(!Z_ENABLE_ON) WRITE(Z_ENABLE_PIN,HIGH);
#endif
//endstops and pullups #if (E_ENABLE_PIN > -1)
#ifdef ENDSTOPPULLUPS SET_OUTPUT(E_ENABLE_PIN);
#if X_MIN_PIN > -1 if(!E_ENABLE_ON) WRITE(E_ENABLE_PIN,HIGH);
SET_INPUT(X_MIN_PIN); #endif
WRITE(X_MIN_PIN,HIGH);
#endif //endstops and pullups
#if X_MAX_PIN > -1 #ifdef ENDSTOPPULLUPS
SET_INPUT(X_MAX_PIN); #if X_MIN_PIN > -1
WRITE(X_MAX_PIN,HIGH); SET_INPUT(X_MIN_PIN);
#endif WRITE(X_MIN_PIN,HIGH);
#if Y_MIN_PIN > -1 #endif
SET_INPUT(Y_MIN_PIN); #if X_MAX_PIN > -1
WRITE(Y_MIN_PIN,HIGH); SET_INPUT(X_MAX_PIN);
#endif WRITE(X_MAX_PIN,HIGH);
#if Y_MAX_PIN > -1 #endif
SET_INPUT(Y_MAX_PIN); #if Y_MIN_PIN > -1
WRITE(Y_MAX_PIN,HIGH); SET_INPUT(Y_MIN_PIN);
#endif WRITE(Y_MIN_PIN,HIGH);
#if Z_MIN_PIN > -1 #endif
SET_INPUT(Z_MIN_PIN); #if Y_MAX_PIN > -1
WRITE(Z_MIN_PIN,HIGH); SET_INPUT(Y_MAX_PIN);
#endif WRITE(Y_MAX_PIN,HIGH);
#if Z_MAX_PIN > -1 #endif
SET_INPUT(Z_MAX_PIN); #if Z_MIN_PIN > -1
WRITE(Z_MAX_PIN,HIGH); SET_INPUT(Z_MIN_PIN);
#endif WRITE(Z_MIN_PIN,HIGH);
#else //ENDSTOPPULLUPS #endif
#if X_MIN_PIN > -1 #if Z_MAX_PIN > -1
SET_INPUT(X_MIN_PIN); SET_INPUT(Z_MAX_PIN);
#endif WRITE(Z_MAX_PIN,HIGH);
#if X_MAX_PIN > -1 #endif
SET_INPUT(X_MAX_PIN); #else //ENDSTOPPULLUPS
#endif #if X_MIN_PIN > -1
#if Y_MIN_PIN > -1 SET_INPUT(X_MIN_PIN);
SET_INPUT(Y_MIN_PIN); #endif
#endif #if X_MAX_PIN > -1
#if Y_MAX_PIN > -1 SET_INPUT(X_MAX_PIN);
SET_INPUT(Y_MAX_PIN); #endif
#endif #if Y_MIN_PIN > -1
#if Z_MIN_PIN > -1 SET_INPUT(Y_MIN_PIN);
SET_INPUT(Z_MIN_PIN); #endif
#endif #if Y_MAX_PIN > -1
#if Z_MAX_PIN > -1 SET_INPUT(Y_MAX_PIN);
SET_INPUT(Z_MAX_PIN); #endif
#endif #if Z_MIN_PIN > -1
#endif //ENDSTOPPULLUPS SET_INPUT(Z_MIN_PIN);
#endif
#if Z_MAX_PIN > -1
//Initialize Step Pins SET_INPUT(Z_MAX_PIN);
#if (X_STEP_PIN > -1) #endif
SET_OUTPUT(X_STEP_PIN); #endif //ENDSTOPPULLUPS
#endif
#if (Y_STEP_PIN > -1)
SET_OUTPUT(Y_STEP_PIN); //Initialize Step Pins
#endif #if (X_STEP_PIN > -1)
#if (Z_STEP_PIN > -1) SET_OUTPUT(X_STEP_PIN);
SET_OUTPUT(Z_STEP_PIN); #endif
#endif #if (Y_STEP_PIN > -1)
#if (E_STEP_PIN > -1) SET_OUTPUT(Y_STEP_PIN);
SET_OUTPUT(E_STEP_PIN); #endif
#endif #if (Z_STEP_PIN > -1)
SET_OUTPUT(Z_STEP_PIN);
// waveform generation = 0100 = CTC #endif
TCCR1B &= ~(1<<WGM13); #if (E_STEP_PIN > -1)
TCCR1B |= (1<<WGM12); SET_OUTPUT(E_STEP_PIN);
TCCR1A &= ~(1<<WGM11); #endif
TCCR1A &= ~(1<<WGM10);
// waveform generation = 0100 = CTC
// output mode = 00 (disconnected) TCCR1B &= ~(1<<WGM13);
TCCR1A &= ~(3<<COM1A0); TCCR1B |= (1<<WGM12);
TCCR1A &= ~(3<<COM1B0); TCCR1A &= ~(1<<WGM11);
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (2<<CS10); // 2MHz timer TCCR1A &= ~(1<<WGM10);
OCR1A = 0x4000; // output mode = 00 (disconnected)
DISABLE_STEPPER_DRIVER_INTERRUPT(); TCCR1A &= ~(3<<COM1A0);
TCCR1A &= ~(3<<COM1B0);
#ifdef ADVANCE TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (2<<CS10); // 2MHz timer
e_steps = 0;
TIMSK0 |= (1<<OCIE0A); OCR1A = 0x4000;
#endif //ADVANCE DISABLE_STEPPER_DRIVER_INTERRUPT();
sei();
} #ifdef ADVANCE
e_steps = 0;
// Block until all buffered steps are executed TIMSK0 |= (1<<OCIE0A);
void st_synchronize() #endif //ADVANCE
{ sei();
while(plan_get_current_block()) { }
manage_heater();
manage_inactivity(1); // Block until all buffered steps are executed
LCD_STATUS; void st_synchronize()
} {
while(plan_get_current_block()) {
manage_heater();
manage_inactivity(1);
LCD_STATUS;
}
} }

View file

@ -1,562 +1,564 @@
/* /*
temperature.c - temperature control temperature.c - temperature control
Part of Marlin Part of Marlin
Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* /*
This firmware is a mashup between Sprinter and grbl. This firmware is a mashup between Sprinter and grbl.
(https://github.com/kliment/Sprinter) (https://github.com/kliment/Sprinter)
(https://github.com/simen/grbl/tree) (https://github.com/simen/grbl/tree)
It has preliminary support for Matthew Roberts advance algorithm It has preliminary support for Matthew Roberts advance algorithm
http://reprap.org/pipermail/reprap-dev/2011-May/003323.html http://reprap.org/pipermail/reprap-dev/2011-May/003323.html
This firmware is optimized for gen6 electronics. This firmware is optimized for gen6 electronics.
*/ */
#include <avr/pgmspace.h>
#include "fastio.h"
#include "Configuration.h" #include "fastio.h"
#include "pins.h" #include "Configuration.h"
#include "Marlin.h" #include "pins.h"
#include "ultralcd.h" #include "Marlin.h"
#include "streaming.h" #include "ultralcd.h"
#include "temperature.h" #include "streaming.h"
#include "watchdog.h" #include "temperature.h"
#include "watchdog.h"
//===========================================================================
//=============================public variables============================ //===========================================================================
//=========================================================================== //=============================public variables============================
int target_raw[3] = {0, 0, 0}; //===========================================================================
int current_raw[3] = {0, 0, 0}; int target_raw[3] = {0, 0, 0};
int current_raw[3] = {0, 0, 0};
#ifdef PIDTEMP
#ifdef PIDTEMP
// probably used external
float HeaterPower; // probably used external
float pid_setpoint = 0.0; float HeaterPower;
float pid_setpoint = 0.0;
float Kp=DEFAULT_Kp;
float Ki=DEFAULT_Ki; float Kp=DEFAULT_Kp;
float Kd=DEFAULT_Kd; float Ki=DEFAULT_Ki;
float Kc=DEFAULT_Kc; float Kd=DEFAULT_Kd;
#endif //PIDTEMP #ifdef PID_ADD_EXTRUSION_RATE
float Kc=DEFAULT_Kc;
#endif
//=========================================================================== #endif //PIDTEMP
//=============================private variables============================
//===========================================================================
static bool temp_meas_ready = false; //===========================================================================
//=============================private variables============================
static unsigned long previous_millis_heater, previous_millis_bed_heater; //===========================================================================
static bool temp_meas_ready = false;
#ifdef PIDTEMP
//static cannot be external: static unsigned long previous_millis_heater, previous_millis_bed_heater;
static float temp_iState = 0;
static float temp_dState = 0; #ifdef PIDTEMP
static float pTerm; //static cannot be external:
static float iTerm; static float temp_iState = 0;
static float dTerm; static float temp_dState = 0;
//int output; static float pTerm;
static float pid_error; static float iTerm;
static float temp_iState_min; static float dTerm;
static float temp_iState_max; //int output;
static float pid_input; static float pid_error;
static float pid_output; static float temp_iState_min;
static bool pid_reset; static float temp_iState_max;
static float pid_input;
#endif //PIDTEMP static float pid_output;
static bool pid_reset;
#ifdef WATCHPERIOD
static int watch_raw[3] = {-1000,-1000,-1000}; #endif //PIDTEMP
static unsigned long watchmillis = 0;
#endif //WATCHPERIOD #ifdef WATCHPERIOD
static int watch_raw[3] = {-1000,-1000,-1000};
#ifdef HEATER_0_MINTEMP static unsigned long watchmillis = 0;
static int minttemp_0 = temp2analog(HEATER_0_MINTEMP); #endif //WATCHPERIOD
#endif //MINTEMP
#ifdef HEATER_0_MAXTEMP #ifdef HEATER_0_MINTEMP
static int maxttemp_0 = temp2analog(HEATER_0_MAXTEMP); static int minttemp_0 = temp2analog(HEATER_0_MINTEMP);
#endif //MAXTEMP #endif //MINTEMP
#ifdef HEATER_0_MAXTEMP
#ifdef HEATER_1_MINTEMP static int maxttemp_0 = temp2analog(HEATER_0_MAXTEMP);
static int minttemp_1 = temp2analog(HEATER_1_MINTEMP); #endif //MAXTEMP
#endif //MINTEMP
#ifdef HEATER_1_MAXTEMP #ifdef HEATER_1_MINTEMP
static int maxttemp_1 = temp2analog(HEATER_1_MAXTEMP); static int minttemp_1 = temp2analog(HEATER_1_MINTEMP);
#endif //MAXTEMP #endif //MINTEMP
#ifdef HEATER_1_MAXTEMP
#ifdef BED_MINTEMP static int maxttemp_1 = temp2analog(HEATER_1_MAXTEMP);
static int bed_minttemp = temp2analog(BED_MINTEMP); #endif //MAXTEMP
#endif //BED_MINTEMP
#ifdef BED_MAXTEMP #ifdef BED_MINTEMP
static int bed_maxttemp = temp2analog(BED_MAXTEMP); static int bed_minttemp = temp2analog(BED_MINTEMP);
#endif //BED_MAXTEMP #endif //BED_MINTEMP
#ifdef BED_MAXTEMP
//=========================================================================== static int bed_maxttemp = temp2analog(BED_MAXTEMP);
//=============================functions ============================ #endif //BED_MAXTEMP
//===========================================================================
//===========================================================================
void manage_heater() //=============================functions ============================
{ //===========================================================================
#ifdef USE_WATCHDOG
wd_reset(); void manage_heater()
#endif {
#ifdef USE_WATCHDOG
float pid_input; wd_reset();
float pid_output; #endif
if(temp_meas_ready != true) //better readability
return; float pid_input;
float pid_output;
CRITICAL_SECTION_START; if(temp_meas_ready != true) //better readability
temp_meas_ready = false; return;
CRITICAL_SECTION_END;
CRITICAL_SECTION_START;
#ifdef PIDTEMP temp_meas_ready = false;
pid_input = analog2temp(current_raw[TEMPSENSOR_HOTEND_0]); CRITICAL_SECTION_END;
#ifndef PID_OPENLOOP #ifdef PIDTEMP
pid_error = pid_setpoint - pid_input; pid_input = analog2temp(current_raw[TEMPSENSOR_HOTEND_0]);
if(pid_error > 10){
pid_output = PID_MAX; #ifndef PID_OPENLOOP
pid_reset = true; pid_error = pid_setpoint - pid_input;
} if(pid_error > 10){
else if(pid_error < -10) { pid_output = PID_MAX;
pid_output = 0; pid_reset = true;
pid_reset = true; }
} else if(pid_error < -10) {
else { pid_output = 0;
if(pid_reset == true) { pid_reset = true;
temp_iState = 0.0; }
pid_reset = false; else {
} if(pid_reset == true) {
pTerm = Kp * pid_error; temp_iState = 0.0;
temp_iState += pid_error; pid_reset = false;
temp_iState = constrain(temp_iState, temp_iState_min, temp_iState_max); }
iTerm = Ki * temp_iState; pTerm = Kp * pid_error;
//K1 defined in Configuration.h in the PID settings temp_iState += pid_error;
#define K2 (1.0-K1) temp_iState = constrain(temp_iState, temp_iState_min, temp_iState_max);
dTerm = (Kd * (pid_input - temp_dState))*K2 + (K1 * dTerm); iTerm = Ki * temp_iState;
temp_dState = pid_input; //K1 defined in Configuration.h in the PID settings
#ifdef PID_ADD_EXTRUSION_RATE #define K2 (1.0-K1)
pTerm+=Kc*current_block->speed_e; //additional heating if extrusion speed is high dTerm = (Kd * (pid_input - temp_dState))*K2 + (K1 * dTerm);
#endif temp_dState = pid_input;
pid_output = constrain(pTerm + iTerm - dTerm, 0, PID_MAX); // #ifdef PID_ADD_EXTRUSION_RATE
} // pTerm+=Kc*current_block->speed_e; //additional heating if extrusion speed is high
#endif //PID_OPENLOOP // #endif
#ifdef PID_DEBUG pid_output = constrain(pTerm + iTerm - dTerm, 0, PID_MAX);
SERIAL_ECHOLN(" PIDDEBUG Input "<<pid_input<<" Output "<<pid_output" pTerm "<<pTerm<<" iTerm "<<iTerm<<" dTerm "<<dTerm); }
#endif //PID_DEBUG #endif //PID_OPENLOOP
analogWrite(HEATER_0_PIN, pid_output); #ifdef PID_DEBUG
#endif //PIDTEMP SERIAL_ECHOLN(" PIDDEBUG Input "<<pid_input<<" Output "<<pid_output" pTerm "<<pTerm<<" iTerm "<<iTerm<<" dTerm "<<dTerm);
#endif //PID_DEBUG
#ifndef PIDTEMP analogWrite(HEATER_0_PIN, pid_output);
if(current_raw[0] >= target_raw[0]) #endif //PIDTEMP
{
WRITE(HEATER_0_PIN,LOW); #ifndef PIDTEMP
} if(current_raw[0] >= target_raw[0])
else {
{ WRITE(HEATER_0_PIN,LOW);
WRITE(HEATER_0_PIN,HIGH); }
} else
#endif {
WRITE(HEATER_0_PIN,HIGH);
if(millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) }
return; #endif
previous_millis_bed_heater = millis();
if(millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL)
#if TEMP_1_PIN > -1 return;
if(current_raw[TEMPSENSOR_BED] >= target_raw[TEMPSENSOR_BED]) previous_millis_bed_heater = millis();
{
WRITE(HEATER_1_PIN,LOW); #if TEMP_1_PIN > -1
} if(current_raw[TEMPSENSOR_BED] >= target_raw[TEMPSENSOR_BED])
else {
{ WRITE(HEATER_1_PIN,LOW);
WRITE(HEATER_1_PIN,HIGH); }
} else
#endif {
} WRITE(HEATER_1_PIN,HIGH);
}
// Takes hot end temperature value as input and returns corresponding raw value. #endif
// For a thermistor, it uses the RepRap thermistor temp table. }
// This is needed because PID in hydra firmware hovers around a given analog value, not a temp value.
// This function is derived from inversing the logic from a portion of getTemperature() in FiveD RepRap firmware. // Takes hot end temperature value as input and returns corresponding raw value.
int temp2analog(int celsius) { // For a thermistor, it uses the RepRap thermistor temp table.
#ifdef HEATER_0_USES_THERMISTOR // This is needed because PID in hydra firmware hovers around a given analog value, not a temp value.
int raw = 0; // This function is derived from inversing the logic from a portion of getTemperature() in FiveD RepRap firmware.
byte i; int temp2analog(int celsius) {
#ifdef HEATER_0_USES_THERMISTOR
for (i=1; i<NUMTEMPS_HEATER_0; i++) int raw = 0;
{ byte i;
if (heater_0_temptable[i][1] < celsius)
{ for (i=1; i<NUMTEMPS_HEATER_0; i++)
raw = heater_0_temptable[i-1][0] + {
(celsius - heater_0_temptable[i-1][1]) * if (pgm_read_word(&(heater_0_temptable[i][1])) < celsius)
(heater_0_temptable[i][0] - heater_0_temptable[i-1][0]) / {
(heater_0_temptable[i][1] - heater_0_temptable[i-1][1]); raw = pgm_read_word(&(heater_0_temptable[i-1][0])) +
break; (celsius - pgm_read_word(&(heater_0_temptable[i-1][1]))) *
} (pgm_read_word(&(heater_0_temptable[i][0])) - pgm_read_word(&(heater_0_temptable[i-1][0]))) /
} (pgm_read_word(&(heater_0_temptable[i][1])) - pgm_read_word(&(heater_0_temptable[i-1][1])));
break;
// Overflow: Set to last value in the table }
if (i == NUMTEMPS_HEATER_0) raw = heater_0_temptable[i-1][0]; }
return (1023 * OVERSAMPLENR) - raw; // Overflow: Set to last value in the table
#elif defined HEATER_0_USES_AD595 if (i == NUMTEMPS_HEATER_0) raw = pgm_read_word(&(heater_0_temptable[i-1][0]));
return celsius * (1024.0 / (5.0 * 100.0) ) * OVERSAMPLENR;
#endif return (1023 * OVERSAMPLENR) - raw;
} #elif defined HEATER_0_USES_AD595
return celsius * (1024.0 / (5.0 * 100.0) ) * OVERSAMPLENR;
// Takes bed temperature value as input and returns corresponding raw value. #endif
// For a thermistor, it uses the RepRap thermistor temp table. }
// This is needed because PID in hydra firmware hovers around a given analog value, not a temp value.
// This function is derived from inversing the logic from a portion of getTemperature() in FiveD RepRap firmware. // Takes bed temperature value as input and returns corresponding raw value.
int temp2analogBed(int celsius) { // For a thermistor, it uses the RepRap thermistor temp table.
#ifdef BED_USES_THERMISTOR // This is needed because PID in hydra firmware hovers around a given analog value, not a temp value.
// This function is derived from inversing the logic from a portion of getTemperature() in FiveD RepRap firmware.
int raw = 0; int temp2analogBed(int celsius) {
byte i; #ifdef BED_USES_THERMISTOR
for (i=1; i<BNUMTEMPS; i++) int raw = 0;
{ byte i;
if (bedtemptable[i][1] < celsius)
{ for (i=1; i<BNUMTEMPS; i++)
raw = bedtemptable[i-1][0] + {
(celsius - bedtemptable[i-1][1]) * if (pgm_read_word(&)bedtemptable[i][1])) < celsius)
(bedtemptable[i][0] - bedtemptable[i-1][0]) / {
(bedtemptable[i][1] - bedtemptable[i-1][1]); raw = pgm_read_word(&(bedtemptable[i-1][0])) +
(celsius - pgm_read_word(&(bedtemptable[i-1][1]))) *
break; (pgm_read_word(&(bedtemptable[i][0])) - pgm_read_word(&(bedtemptable[i-1][0]))) /
} (pgm_read_word(&(bedtemptable[i][1])) - pgm_read_word(&(bedtemptable[i-1][1])));
}
break;
// Overflow: Set to last value in the table }
if (i == BNUMTEMPS) raw = bedtemptable[i-1][0]; }
return (1023 * OVERSAMPLENR) - raw; // Overflow: Set to last value in the table
#elif defined BED_USES_AD595 if (i == BNUMTEMPS) raw = pgm_read_word(&(bedtemptable[i-1][0]));
return celsius * (1024.0 / (5.0 * 100.0) ) * OVERSAMPLENR;
#endif return (1023 * OVERSAMPLENR) - raw;
} #elif defined BED_USES_AD595
return celsius * (1024.0 / (5.0 * 100.0) ) * OVERSAMPLENR;
// Derived from RepRap FiveD extruder::getTemperature() #endif
// For hot end temperature measurement. }
float analog2temp(int raw) {
#ifdef HEATER_0_USES_THERMISTOR // Derived from RepRap FiveD extruder::getTemperature()
float celsius = 0; // For hot end temperature measurement.
byte i; float analog2temp(int raw) {
raw = (1023 * OVERSAMPLENR) - raw; #ifdef HEATER_0_USES_THERMISTOR
for (i=1; i<NUMTEMPS_HEATER_0; i++) float celsius = 0;
{ byte i;
if (heater_0_temptable[i][0] > raw) raw = (1023 * OVERSAMPLENR) - raw;
{ for (i=1; i<NUMTEMPS_HEATER_0; i++)
celsius = heater_0_temptable[i-1][1] + {
(raw - heater_0_temptable[i-1][0]) * if ((short)pgm_read_word(&heater_0_temptable[i][0]) > raw)
(float)(heater_0_temptable[i][1] - heater_0_temptable[i-1][1]) / {
(float)(heater_0_temptable[i][0] - heater_0_temptable[i-1][0]); celsius = (short)pgm_read_word(&heater_0_temptable[i-1][1]) +
(raw - (short)pgm_read_word(&heater_0_temptable[i-1][0])) *
break; (float)((short)pgm_read_word(&heater_0_temptable[i][1]) - (short)pgm_read_word(&heater_0_temptable[i-1][1])) /
} (float)((short)pgm_read_word(&heater_0_temptable[i][0]) - (short)pgm_read_word(&heater_0_temptable[i-1][0]));
} break;
}
// Overflow: Set to last value in the table }
if (i == NUMTEMPS_HEATER_0) celsius = heater_0_temptable[i-1][1];
// Overflow: Set to last value in the table
return celsius; if (i == NUMTEMPS_HEATER_0) celsius = (short)pgm_read_word(&(heater_0_temptable[i-1][1]));
#elif defined HEATER_0_USES_AD595
return raw * ((5.0 * 100.0) / 1024.0) / OVERSAMPLENR; return celsius;
#endif #elif defined HEATER_0_USES_AD595
} return raw * ((5.0 * 100.0) / 1024.0) / OVERSAMPLENR;
#endif
// Derived from RepRap FiveD extruder::getTemperature() }
// For bed temperature measurement.
float analog2tempBed(int raw) { // Derived from RepRap FiveD extruder::getTemperature()
#ifdef BED_USES_THERMISTOR // For bed temperature measurement.
int celsius = 0; float analog2tempBed(int raw) {
byte i; #ifdef BED_USES_THERMISTOR
int celsius = 0;
raw = (1023 * OVERSAMPLENR) - raw; byte i;
for (i=1; i<BNUMTEMPS; i++) raw = (1023 * OVERSAMPLENR) - raw;
{
if (bedtemptable[i][0] > raw) for (i=1; i<BNUMTEMPS; i++)
{ {
celsius = bedtemptable[i-1][1] + if (pgm_read_word(&(bedtemptable[i][0])) > raw)
(raw - bedtemptable[i-1][0]) * {
(bedtemptable[i][1] - bedtemptable[i-1][1]) / celsius = pgm_read_word(&(bedtemptable[i-1][1])) +
(bedtemptable[i][0] - bedtemptable[i-1][0]); (raw - pgm_read_word(&(bedtemptable[i-1][0]))) *
(pgm_read_word(&(bedtemptable[i][1])) - pgm_read_word(&(bedtemptable[i-1][1]))) /
break; (pgm_read_word(&(bedtemptable[i][0])) - pgm_read_word(&(bedtemptable[i-1][0])));
}
} break;
}
// Overflow: Set to last value in the table }
if (i == BNUMTEMPS) celsius = bedtemptable[i-1][1];
// Overflow: Set to last value in the table
return celsius; if (i == BNUMTEMPS) celsius = pgm_read_word(&(bedtemptable[i-1][1]));
#elif defined BED_USES_AD595 return celsius;
return raw * ((5.0 * 100.0) / 1024.0) / OVERSAMPLENR;
#endif #elif defined BED_USES_AD595
} return raw * ((5.0 * 100.0) / 1024.0) / OVERSAMPLENR;
#endif
void tp_init() }
{
#if (HEATER_0_PIN > -1) void tp_init()
SET_OUTPUT(HEATER_0_PIN); {
#endif #if (HEATER_0_PIN > -1)
#if (HEATER_1_PIN > -1) SET_OUTPUT(HEATER_0_PIN);
SET_OUTPUT(HEATER_1_PIN); #endif
#endif #if (HEATER_1_PIN > -1)
#if (HEATER_2_PIN > -1) SET_OUTPUT(HEATER_1_PIN);
SET_OUTPUT(HEATER_2_PIN); #endif
#endif #if (HEATER_2_PIN > -1)
SET_OUTPUT(HEATER_2_PIN);
#ifdef PIDTEMP #endif
temp_iState_min = 0.0;
temp_iState_max = PID_INTEGRAL_DRIVE_MAX / Ki; #ifdef PIDTEMP
#endif //PIDTEMP temp_iState_min = 0.0;
temp_iState_max = PID_INTEGRAL_DRIVE_MAX / Ki;
// Set analog inputs #endif //PIDTEMP
ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADIF | 0x07;
// Set analog inputs
// Use timer0 for temperature measurement ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADIF | 0x07;
// Interleave temperature interrupt with millies interrupt
OCR0B = 128; // Use timer0 for temperature measurement
TIMSK0 |= (1<<OCIE0B); // Interleave temperature interrupt with millies interrupt
} OCR0B = 128;
TIMSK0 |= (1<<OCIE0B);
}
void setWatch()
{
#ifdef WATCHPERIOD void setWatch()
if(isHeatingHotend0()) {
{ #ifdef WATCHPERIOD
watchmillis = max(1,millis()); if(isHeatingHotend0())
watch_raw[TEMPSENSOR_HOTEND_0] = current_raw[TEMPSENSOR_HOTEND_0]; {
} watchmillis = max(1,millis());
else watch_raw[TEMPSENSOR_HOTEND_0] = current_raw[TEMPSENSOR_HOTEND_0];
{ }
watchmillis = 0; else
} {
#endif watchmillis = 0;
} }
#endif
}
void disable_heater()
{
#if TEMP_0_PIN > -1 void disable_heater()
target_raw[0]=0; {
#if HEATER_0_PIN > -1 #if TEMP_0_PIN > -1
WRITE(HEATER_0_PIN,LOW); target_raw[0]=0;
#endif #if HEATER_0_PIN > -1
#endif WRITE(HEATER_0_PIN,LOW);
#endif
#if TEMP_1_PIN > -1 #endif
target_raw[1]=0;
#if HEATER_1_PIN > -1 #if TEMP_1_PIN > -1
WRITE(HEATER_1_PIN,LOW); target_raw[1]=0;
#endif #if HEATER_1_PIN > -1
#endif WRITE(HEATER_1_PIN,LOW);
#endif
#if TEMP_2_PIN > -1 #endif
target_raw[2]=0;
#if HEATER_2_PIN > -1 #if TEMP_2_PIN > -1
WRITE(HEATER_2_PIN,LOW); target_raw[2]=0;
#endif #if HEATER_2_PIN > -1
#endif WRITE(HEATER_2_PIN,LOW);
} #endif
#endif
// Timer 0 is shared with millies }
ISR(TIMER0_COMPB_vect)
{ // Timer 0 is shared with millies
//these variables are only accesible from the ISR, but static, so they don't loose their value ISR(TIMER0_COMPB_vect)
static unsigned char temp_count = 0; {
static unsigned long raw_temp_0_value = 0; //these variables are only accesible from the ISR, but static, so they don't loose their value
static unsigned long raw_temp_1_value = 0; static unsigned char temp_count = 0;
static unsigned long raw_temp_2_value = 0; static unsigned long raw_temp_0_value = 0;
static unsigned char temp_state = 0; static unsigned long raw_temp_1_value = 0;
static unsigned long raw_temp_2_value = 0;
switch(temp_state) { static unsigned char temp_state = 0;
case 0: // Prepare TEMP_0
#if (TEMP_0_PIN > -1) switch(temp_state) {
#if TEMP_0_PIN < 8 case 0: // Prepare TEMP_0
DIDR0 = 1 << TEMP_0_PIN; #if (TEMP_0_PIN > -1)
#else #if TEMP_0_PIN < 8
DIDR2 = 1<<(TEMP_0_PIN - 8); DIDR0 = 1 << TEMP_0_PIN;
ADCSRB = 1<<MUX5; #else
#endif DIDR2 = 1<<(TEMP_0_PIN - 8);
ADMUX = ((1 << REFS0) | (TEMP_0_PIN & 0x07)); ADCSRB = 1<<MUX5;
ADCSRA |= 1<<ADSC; // Start conversion #endif
#endif ADMUX = ((1 << REFS0) | (TEMP_0_PIN & 0x07));
#ifdef ULTIPANEL ADCSRA |= 1<<ADSC; // Start conversion
buttons_check(); #endif
#endif #ifdef ULTIPANEL
temp_state = 1; buttons_check();
break; #endif
case 1: // Measure TEMP_0 temp_state = 1;
#if (TEMP_0_PIN > -1) break;
raw_temp_0_value += ADC; case 1: // Measure TEMP_0
#endif #if (TEMP_0_PIN > -1)
temp_state = 2; raw_temp_0_value += ADC;
break; #endif
case 2: // Prepare TEMP_1 temp_state = 2;
#if (TEMP_1_PIN > -1) break;
#if TEMP_1_PIN < 7 case 2: // Prepare TEMP_1
DIDR0 = 1<<TEMP_1_PIN; #if (TEMP_1_PIN > -1)
#else #if TEMP_1_PIN < 7
DIDR2 = 1<<(TEMP_1_PIN - 8); DIDR0 = 1<<TEMP_1_PIN;
ADCSRB = 1<<MUX5; #else
#endif DIDR2 = 1<<(TEMP_1_PIN - 8);
ADMUX = ((1 << REFS0) | (TEMP_1_PIN & 0x07)); ADCSRB = 1<<MUX5;
ADCSRA |= 1<<ADSC; // Start conversion #endif
#endif ADMUX = ((1 << REFS0) | (TEMP_1_PIN & 0x07));
#ifdef ULTIPANEL ADCSRA |= 1<<ADSC; // Start conversion
buttons_check(); #endif
#endif #ifdef ULTIPANEL
temp_state = 3; buttons_check();
break; #endif
case 3: // Measure TEMP_1 temp_state = 3;
#if (TEMP_1_PIN > -1) break;
raw_temp_1_value += ADC; case 3: // Measure TEMP_1
#endif #if (TEMP_1_PIN > -1)
temp_state = 4; raw_temp_1_value += ADC;
break; #endif
case 4: // Prepare TEMP_2 temp_state = 4;
#if (TEMP_2_PIN > -1) break;
#if TEMP_2_PIN < 7 case 4: // Prepare TEMP_2
DIDR0 = 1 << TEMP_2_PIN; #if (TEMP_2_PIN > -1)
#else #if TEMP_2_PIN < 7
DIDR2 = 1<<(TEMP_2_PIN - 8); DIDR0 = 1 << TEMP_2_PIN;
ADCSRB = 1<<MUX5; #else
#endif DIDR2 = 1<<(TEMP_2_PIN - 8);
ADMUX = ((1 << REFS0) | (TEMP_2_PIN & 0x07)); ADCSRB = 1<<MUX5;
ADCSRA |= 1<<ADSC; // Start conversion #endif
#endif ADMUX = ((1 << REFS0) | (TEMP_2_PIN & 0x07));
#ifdef ULTIPANEL ADCSRA |= 1<<ADSC; // Start conversion
buttons_check(); #endif
#endif #ifdef ULTIPANEL
temp_state = 5; buttons_check();
break; #endif
case 5: // Measure TEMP_2 temp_state = 5;
#if (TEMP_2_PIN > -1) break;
raw_temp_2_value += ADC; case 5: // Measure TEMP_2
#endif #if (TEMP_2_PIN > -1)
temp_state = 0; raw_temp_2_value += ADC;
temp_count++; #endif
break; temp_state = 0;
default: temp_count++;
SERIAL_ERRORLN("Temp measurement error!"); break;
break; default:
} SERIAL_ERRORLN("Temp measurement error!");
break;
if(temp_count >= 16) // 6 ms * 16 = 96ms. }
{
#ifdef HEATER_0_USES_AD595 if(temp_count >= 16) // 6 ms * 16 = 96ms.
current_raw[0] = raw_temp_0_value; {
#else #ifdef HEATER_0_USES_AD595
current_raw[0] = 16383 - raw_temp_0_value; current_raw[0] = raw_temp_0_value;
#endif #else
current_raw[0] = 16383 - raw_temp_0_value;
#ifdef HEATER_1_USES_AD595 #endif
current_raw[2] = raw_temp_2_value;
#else #ifdef HEATER_1_USES_AD595
current_raw[2] = 16383 - raw_temp_2_value; current_raw[2] = raw_temp_2_value;
#endif #else
current_raw[2] = 16383 - raw_temp_2_value;
#ifdef BED_USES_AD595 #endif
current_raw[1] = raw_temp_1_value;
#else #ifdef BED_USES_AD595
current_raw[1] = 16383 - raw_temp_1_value; current_raw[1] = raw_temp_1_value;
#endif #else
current_raw[1] = 16383 - raw_temp_1_value;
temp_meas_ready = true; #endif
temp_count = 0;
raw_temp_0_value = 0; temp_meas_ready = true;
raw_temp_1_value = 0; temp_count = 0;
raw_temp_2_value = 0; raw_temp_0_value = 0;
#ifdef HEATER_0_MAXTEMP raw_temp_1_value = 0;
#if (HEATER_0_PIN > -1) raw_temp_2_value = 0;
if(current_raw[TEMPSENSOR_HOTEND_0] >= maxttemp_0) { #ifdef HEATER_0_MAXTEMP
target_raw[TEMPSENSOR_HOTEND_0] = 0; #if (HEATER_0_PIN > -1)
analogWrite(HEATER_0_PIN, 0); if(current_raw[TEMPSENSOR_HOTEND_0] >= maxttemp_0) {
SERIAL_ERRORLN("Temperature extruder 0 switched off. MAXTEMP triggered !!"); target_raw[TEMPSENSOR_HOTEND_0] = 0;
kill(); analogWrite(HEATER_0_PIN, 0);
} SERIAL_ERRORLN("Temperature extruder 0 switched off. MAXTEMP triggered !!");
#endif kill();
#endif }
#ifdef HEATER_1_MAXTEMP #endif
#if (HEATER_1_PIN > -1) #endif
if(current_raw[TEMPSENSOR_HOTEND_1] >= maxttemp_1) { #ifdef HEATER_1_MAXTEMP
target_raw[TEMPSENSOR_HOTEND_1] = 0; #if (HEATER_1_PIN > -1)
if(current_raw[2] >= maxttemp_1) { if(current_raw[TEMPSENSOR_HOTEND_1] >= maxttemp_1) {
analogWrite(HEATER_2_PIN, 0); target_raw[TEMPSENSOR_HOTEND_1] = 0;
SERIAL_ERRORLN("Temperature extruder 1 switched off. MAXTEMP triggered !!"); if(current_raw[2] >= maxttemp_1) {
kill() analogWrite(HEATER_2_PIN, 0);
} SERIAL_ERRORLN("Temperature extruder 1 switched off. MAXTEMP triggered !!");
#endif kill()
#endif //MAXTEMP }
#endif
#ifdef HEATER_0_MINTEMP #endif //MAXTEMP
#if (HEATER_0_PIN > -1)
if(current_raw[TEMPSENSOR_HOTEND_0] <= minttemp_0) { #ifdef HEATER_0_MINTEMP
target_raw[TEMPSENSOR_HOTEND_0] = 0; #if (HEATER_0_PIN > -1)
analogWrite(HEATER_0_PIN, 0); if(current_raw[TEMPSENSOR_HOTEND_0] <= minttemp_0) {
SERIAL_ERRORLN("Temperature extruder 0 switched off. MINTEMP triggered !!"); target_raw[TEMPSENSOR_HOTEND_0] = 0;
kill(); analogWrite(HEATER_0_PIN, 0);
} SERIAL_ERRORLN("Temperature extruder 0 switched off. MINTEMP triggered !!");
#endif kill();
#endif }
#endif
#ifdef HEATER_1_MINTEMP #endif
#if (HEATER_2_PIN > -1)
if(current_raw[TEMPSENSOR_HOTEND_1] <= minttemp_1) { #ifdef HEATER_1_MINTEMP
target_raw[TEMPSENSOR_HOTEND_1] = 0; #if (HEATER_2_PIN > -1)
analogWrite(HEATER_2_PIN, 0); if(current_raw[TEMPSENSOR_HOTEND_1] <= minttemp_1) {
SERIAL_ERRORLN("Temperature extruder 1 switched off. MINTEMP triggered !!"); target_raw[TEMPSENSOR_HOTEND_1] = 0;
kill(); analogWrite(HEATER_2_PIN, 0);
} SERIAL_ERRORLN("Temperature extruder 1 switched off. MINTEMP triggered !!");
#endif kill();
#endif //MAXTEMP }
#endif
#ifdef BED_MINTEMP #endif //MAXTEMP
#if (HEATER_1_PIN > -1)
if(current_raw[1] <= bed_minttemp) { #ifdef BED_MINTEMP
target_raw[1] = 0; #if (HEATER_1_PIN > -1)
WRITE(HEATER_1_PIN, 0); if(current_raw[1] <= bed_minttemp) {
SERIAL_ERRORLN("Temperatur heated bed switched off. MINTEMP triggered !!"); target_raw[1] = 0;
kill(); WRITE(HEATER_1_PIN, 0);
} SERIAL_ERRORLN("Temperatur heated bed switched off. MINTEMP triggered !!");
#endif kill();
#endif }
#endif
#ifdef BED_MAXTEMP #endif
#if (HEATER_1_PIN > -1)
if(current_raw[1] >= bed_maxttemp) { #ifdef BED_MAXTEMP
target_raw[1] = 0; #if (HEATER_1_PIN > -1)
WRITE(HEATER_1_PIN, 0); if(current_raw[1] >= bed_maxttemp) {
SERIAL_ERRORLN("Temperature heated bed switched off. MAXTEMP triggered !!"); target_raw[1] = 0;
kill(); WRITE(HEATER_1_PIN, 0);
} SERIAL_ERRORLN("Temperature heated bed switched off. MAXTEMP triggered !!");
#endif kill();
#endif }
} #endif
} #endif
}
}

View file

@ -1,12 +1,14 @@
#ifndef THERMISTORTABLES_H_ #ifndef THERMISTORTABLES_H_
#define THERMISTORTABLES_H_ #define THERMISTORTABLES_H_
#include <avr/pgmspace.h>
#define OVERSAMPLENR 16 #define OVERSAMPLENR 16
#if (THERMISTORHEATER_0 == 1) || (THERMISTORHEATER_1 == 1) || (THERMISTORBED == 1) //100k bed thermistor #if (THERMISTORHEATER_0 == 1) || (THERMISTORHEATER_1 == 1) || (THERMISTORBED == 1) //100k bed thermistor
#define NUMTEMPS_1 61 #define NUMTEMPS_1 61
const short temptable_1[NUMTEMPS_1][2] = { const short temptable_1[NUMTEMPS_1][2] PROGMEM = {
{ 23*OVERSAMPLENR , 300 }, { 23*OVERSAMPLENR , 300 },
{ 25*OVERSAMPLENR , 295 }, { 25*OVERSAMPLENR , 295 },
{ 27*OVERSAMPLENR , 290 }, { 27*OVERSAMPLENR , 290 },
@ -72,7 +74,7 @@ const short temptable_1[NUMTEMPS_1][2] = {
#endif #endif
#if (THERMISTORHEATER_0 == 2) || (THERMISTORHEATER_1 == 2) || (THERMISTORBED == 2) //200k bed thermistor #if (THERMISTORHEATER_0 == 2) || (THERMISTORHEATER_1 == 2) || (THERMISTORBED == 2) //200k bed thermistor
#define NUMTEMPS_2 21 #define NUMTEMPS_2 21
const short temptable_2[NUMTEMPS_2][2] = { const short temptable_2[NUMTEMPS_2][2] PROGMEM = {
{1*OVERSAMPLENR, 848}, {1*OVERSAMPLENR, 848},
{54*OVERSAMPLENR, 275}, {54*OVERSAMPLENR, 275},
{107*OVERSAMPLENR, 228}, {107*OVERSAMPLENR, 228},
@ -99,7 +101,7 @@ const short temptable_2[NUMTEMPS_2][2] = {
#endif #endif
#if (THERMISTORHEATER_0 == 3) || (THERMISTORHEATER_1 == 3) || (THERMISTORBED == 3) //mendel-parts #if (THERMISTORHEATER_0 == 3) || (THERMISTORHEATER_1 == 3) || (THERMISTORBED == 3) //mendel-parts
#define NUMTEMPS_3 28 #define NUMTEMPS_3 28
const short temptable_3[NUMTEMPS_3][2] = { const short temptable_3[NUMTEMPS_3][2] PROGMEM = {
{1*OVERSAMPLENR,864}, {1*OVERSAMPLENR,864},
{21*OVERSAMPLENR,300}, {21*OVERSAMPLENR,300},
{25*OVERSAMPLENR,290}, {25*OVERSAMPLENR,290},
@ -134,7 +136,7 @@ const short temptable_3[NUMTEMPS_3][2] = {
#if (THERMISTORHEATER_0 == 4) || (THERMISTORHEATER_1 == 4) || (THERMISTORBED == 4) //10k thermistor #if (THERMISTORHEATER_0 == 4) || (THERMISTORHEATER_1 == 4) || (THERMISTORBED == 4) //10k thermistor
#define NUMTEMPS_4 20 #define NUMTEMPS_4 20
short temptable_4[NUMTEMPS_4][2] = { const short temptable_4[NUMTEMPS_4][2] PROGMEM = {
{1*OVERSAMPLENR, 430}, {1*OVERSAMPLENR, 430},
{54*OVERSAMPLENR, 137}, {54*OVERSAMPLENR, 137},
{107*OVERSAMPLENR, 107}, {107*OVERSAMPLENR, 107},
@ -161,7 +163,7 @@ short temptable_4[NUMTEMPS_4][2] = {
#if (THERMISTORHEATER_0 == 5) || (THERMISTORHEATER_1 == 5) || (THERMISTORBED == 5) //100k ParCan thermistor (104GT-2) #if (THERMISTORHEATER_0 == 5) || (THERMISTORHEATER_1 == 5) || (THERMISTORBED == 5) //100k ParCan thermistor (104GT-2)
#define NUMTEMPS_5 61 #define NUMTEMPS_5 61
const short temptable_5[NUMTEMPS_5][2] = { const short temptable_5[NUMTEMPS_5][2] PROGMEM = {
{1*OVERSAMPLENR, 713}, {1*OVERSAMPLENR, 713},
{18*OVERSAMPLENR, 316}, {18*OVERSAMPLENR, 316},
{35*OVERSAMPLENR, 266}, {35*OVERSAMPLENR, 266},
@ -228,7 +230,7 @@ const short temptable_5[NUMTEMPS_5][2] = {
#if (THERMISTORHEATER_0 == 6) || (THERMISTORHEATER_1 == 6) || (THERMISTORBED == 6) // 100k Epcos thermistor #if (THERMISTORHEATER_0 == 6) || (THERMISTORHEATER_1 == 6) || (THERMISTORBED == 6) // 100k Epcos thermistor
#define NUMTEMPS_6 36 #define NUMTEMPS_6 36
const short temptable_6[NUMTEMPS_6][2] = { const short temptable_6[NUMTEMPS_6][2] PROGMEM = {
{28*OVERSAMPLENR, 250}, {28*OVERSAMPLENR, 250},
{31*OVERSAMPLENR, 245}, {31*OVERSAMPLENR, 245},
{35*OVERSAMPLENR, 240}, {35*OVERSAMPLENR, 240},
@ -270,7 +272,7 @@ const short temptable_6[NUMTEMPS_6][2] = {
#if (THERMISTORHEATER_0 == 7) || (THERMISTORHEATER_1 == 7) || (THERMISTORBED == 7) // 100k Honeywell 135-104LAG-J01 #if (THERMISTORHEATER_0 == 7) || (THERMISTORHEATER_1 == 7) || (THERMISTORBED == 7) // 100k Honeywell 135-104LAG-J01
#define NUMTEMPS_7 54 #define NUMTEMPS_7 54
const short temptable_7[NUMTEMPS_7][2] = { const short temptable_7[NUMTEMPS_7][2] PROGMEM = {
{46*OVERSAMPLENR, 270}, {46*OVERSAMPLENR, 270},
{50*OVERSAMPLENR, 265}, {50*OVERSAMPLENR, 265},
{54*OVERSAMPLENR, 260}, {54*OVERSAMPLENR, 260},