|
@@ -47,7 +47,6 @@
|
47
|
47
|
#include "planner.h"
|
48
|
48
|
#include "stepper.h"
|
49
|
49
|
#include "temperature.h"
|
50
|
|
-#include "motion_control.h"
|
51
|
50
|
#include "cardreader.h"
|
52
|
51
|
#include "watchdog.h"
|
53
|
52
|
#include "configuration_store.h"
|
|
@@ -226,7 +225,7 @@ bool Running = true;
|
226
|
225
|
|
227
|
226
|
uint8_t marlin_debug_flags = DEBUG_INFO|DEBUG_ERRORS;
|
228
|
227
|
|
229
|
|
-static float feedrate = 1500.0, next_feedrate, saved_feedrate;
|
|
228
|
+static float feedrate = 1500.0, saved_feedrate;
|
230
|
229
|
float current_position[NUM_AXIS] = { 0.0 };
|
231
|
230
|
static float destination[NUM_AXIS] = { 0.0 };
|
232
|
231
|
bool axis_known_position[3] = { false };
|
|
@@ -258,7 +257,7 @@ const char errormagic[] PROGMEM = "Error:";
|
258
|
257
|
const char echomagic[] PROGMEM = "echo:";
|
259
|
258
|
const char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'};
|
260
|
259
|
|
261
|
|
-static float offset[3] = { 0 };
|
|
260
|
+static float arc_offset[3] = { 0 };
|
262
|
261
|
static bool relative_mode = false; //Determines Absolute or Relative Coordinates
|
263
|
262
|
static char serial_char;
|
264
|
263
|
static int serial_count = 0;
|
|
@@ -401,7 +400,6 @@ bool target_direction;
|
401
|
400
|
//================================ Functions ================================
|
402
|
401
|
//===========================================================================
|
403
|
402
|
|
404
|
|
-void get_arc_coordinates();
|
405
|
403
|
bool setTargetedHotend(int code);
|
406
|
404
|
|
407
|
405
|
void serial_echopair_P(const char *s_P, float v) { serialprintPGM(s_P); SERIAL_ECHO(v); }
|
|
@@ -1771,11 +1769,31 @@ static void homeaxis(AxisEnum axis) {
|
1771
|
1769
|
*/
|
1772
|
1770
|
|
1773
|
1771
|
/**
|
|
1772
|
+ * Set XYZE destination and feedrate from the current GCode command
|
|
1773
|
+ *
|
|
1774
|
+ * - Set destination from included axis codes
|
|
1775
|
+ * - Set to current for missing axis codes
|
|
1776
|
+ * - Set the feedrate, if included
|
|
1777
|
+ */
|
|
1778
|
+void gcode_get_destination() {
|
|
1779
|
+ for (int i = 0; i < NUM_AXIS; i++) {
|
|
1780
|
+ if (code_seen(axis_codes[i]))
|
|
1781
|
+ destination[i] = code_value() + (axis_relative_modes[i] || relative_mode ? current_position[i] : 0);
|
|
1782
|
+ else
|
|
1783
|
+ destination[i] = current_position[i];
|
|
1784
|
+ }
|
|
1785
|
+ if (code_seen('F')) {
|
|
1786
|
+ float next_feedrate = code_value();
|
|
1787
|
+ if (next_feedrate > 0.0) feedrate = next_feedrate;
|
|
1788
|
+ }
|
|
1789
|
+}
|
|
1790
|
+
|
|
1791
|
+/**
|
1774
|
1792
|
* G0, G1: Coordinated movement of X Y Z E axes
|
1775
|
1793
|
*/
|
1776
|
1794
|
inline void gcode_G0_G1() {
|
1777
|
1795
|
if (IsRunning()) {
|
1778
|
|
- get_coordinates(); // For X Y Z E F
|
|
1796
|
+ gcode_get_destination(); // For X Y Z E F
|
1779
|
1797
|
|
1780
|
1798
|
#ifdef FWRETRACT
|
1781
|
1799
|
|
|
@@ -1798,13 +1816,154 @@ inline void gcode_G0_G1() {
|
1798
|
1816
|
}
|
1799
|
1817
|
|
1800
|
1818
|
/**
|
|
1819
|
+ * Plan an arc in 2 dimensions
|
|
1820
|
+ *
|
|
1821
|
+ * The arc is approximated by generating many small linear segments.
|
|
1822
|
+ * The length of each segment is configured in MM_PER_ARC_SEGMENT (Default 1mm)
|
|
1823
|
+ * Arcs should only be made relatively large (over 5mm). Your slicer should have
|
|
1824
|
+ * options for G2/G3 arc generation.
|
|
1825
|
+ */
|
|
1826
|
+void plan_arc(
|
|
1827
|
+ float *target, // Destination position
|
|
1828
|
+ float *offset, // Center of rotation relative to current_position
|
|
1829
|
+ uint8_t clockwise // Clockwise?
|
|
1830
|
+) {
|
|
1831
|
+
|
|
1832
|
+ float radius = hypot(offset[X_AXIS], offset[Y_AXIS]),
|
|
1833
|
+ center_axis0 = current_position[X_AXIS] + offset[X_AXIS],
|
|
1834
|
+ center_axis1 = current_position[Y_AXIS] + offset[Y_AXIS],
|
|
1835
|
+ linear_travel = target[Z_AXIS] - current_position[Z_AXIS],
|
|
1836
|
+ extruder_travel = target[E_AXIS] - current_position[E_AXIS],
|
|
1837
|
+ r_axis0 = -offset[X_AXIS], // Radius vector from center to current location
|
|
1838
|
+ r_axis1 = -offset[Y_AXIS],
|
|
1839
|
+ rt_axis0 = target[X_AXIS] - center_axis0,
|
|
1840
|
+ rt_axis1 = target[Y_AXIS] - center_axis1;
|
|
1841
|
+
|
|
1842
|
+ // CCW angle of rotation between position and target from the circle center. Only one atan2() trig computation required.
|
|
1843
|
+ float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
|
|
1844
|
+ if (angular_travel < 0) { angular_travel += RADIANS(360); }
|
|
1845
|
+ if (clockwise) { angular_travel -= RADIANS(360); }
|
|
1846
|
+
|
|
1847
|
+ // Make a circle if the angular rotation is 0
|
|
1848
|
+ if (current_position[X_AXIS] == target[X_AXIS] && current_position[Y_AXIS] == target[Y_AXIS] && angular_travel == 0)
|
|
1849
|
+ angular_travel += RADIANS(360);
|
|
1850
|
+
|
|
1851
|
+ float mm_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
|
|
1852
|
+ if (mm_of_travel < 0.001) { return; }
|
|
1853
|
+ uint16_t segments = floor(mm_of_travel / MM_PER_ARC_SEGMENT);
|
|
1854
|
+ if (segments == 0) segments = 1;
|
|
1855
|
+
|
|
1856
|
+ float theta_per_segment = angular_travel/segments;
|
|
1857
|
+ float linear_per_segment = linear_travel/segments;
|
|
1858
|
+ float extruder_per_segment = extruder_travel/segments;
|
|
1859
|
+
|
|
1860
|
+ /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
|
|
1861
|
+ and phi is the angle of rotation. Based on the solution approach by Jens Geisler.
|
|
1862
|
+ r_T = [cos(phi) -sin(phi);
|
|
1863
|
+ sin(phi) cos(phi] * r ;
|
|
1864
|
+
|
|
1865
|
+ For arc generation, the center of the circle is the axis of rotation and the radius vector is
|
|
1866
|
+ defined from the circle center to the initial position. Each line segment is formed by successive
|
|
1867
|
+ vector rotations. This requires only two cos() and sin() computations to form the rotation
|
|
1868
|
+ matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since
|
|
1869
|
+ all double numbers are single precision on the Arduino. (True double precision will not have
|
|
1870
|
+ round off issues for CNC applications.) Single precision error can accumulate to be greater than
|
|
1871
|
+ tool precision in some cases. Therefore, arc path correction is implemented.
|
|
1872
|
+
|
|
1873
|
+ Small angle approximation may be used to reduce computation overhead further. This approximation
|
|
1874
|
+ holds for everything, but very small circles and large MM_PER_ARC_SEGMENT values. In other words,
|
|
1875
|
+ theta_per_segment would need to be greater than 0.1 rad and N_ARC_CORRECTION would need to be large
|
|
1876
|
+ to cause an appreciable drift error. N_ARC_CORRECTION~=25 is more than small enough to correct for
|
|
1877
|
+ numerical drift error. N_ARC_CORRECTION may be on the order a hundred(s) before error becomes an
|
|
1878
|
+ issue for CNC machines with the single precision Arduino calculations.
|
|
1879
|
+
|
|
1880
|
+ This approximation also allows plan_arc to immediately insert a line segment into the planner
|
|
1881
|
+ without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
|
|
1882
|
+ a correction, the planner should have caught up to the lag caused by the initial plan_arc overhead.
|
|
1883
|
+ This is important when there are successive arc motions.
|
|
1884
|
+ */
|
|
1885
|
+ // Vector rotation matrix values
|
|
1886
|
+ float cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation
|
|
1887
|
+ float sin_T = theta_per_segment;
|
|
1888
|
+
|
|
1889
|
+ float arc_target[4];
|
|
1890
|
+ float sin_Ti;
|
|
1891
|
+ float cos_Ti;
|
|
1892
|
+ float r_axisi;
|
|
1893
|
+ uint16_t i;
|
|
1894
|
+ int8_t count = 0;
|
|
1895
|
+
|
|
1896
|
+ // Initialize the linear axis
|
|
1897
|
+ arc_target[Z_AXIS] = current_position[Z_AXIS];
|
|
1898
|
+
|
|
1899
|
+ // Initialize the extruder axis
|
|
1900
|
+ arc_target[E_AXIS] = current_position[E_AXIS];
|
|
1901
|
+
|
|
1902
|
+ float feed_rate = feedrate*feedrate_multiplier/60/100.0;
|
|
1903
|
+
|
|
1904
|
+ for (i = 1; i < segments; i++) { // Increment (segments-1)
|
|
1905
|
+
|
|
1906
|
+ if (count < N_ARC_CORRECTION) {
|
|
1907
|
+ // Apply vector rotation matrix to previous r_axis0 / 1
|
|
1908
|
+ r_axisi = r_axis0*sin_T + r_axis1*cos_T;
|
|
1909
|
+ r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
|
|
1910
|
+ r_axis1 = r_axisi;
|
|
1911
|
+ count++;
|
|
1912
|
+ }
|
|
1913
|
+ else {
|
|
1914
|
+ // Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
|
|
1915
|
+ // Compute exact location by applying transformation matrix from initial radius vector(=-offset).
|
|
1916
|
+ cos_Ti = cos(i*theta_per_segment);
|
|
1917
|
+ sin_Ti = sin(i*theta_per_segment);
|
|
1918
|
+ r_axis0 = -offset[X_AXIS]*cos_Ti + offset[Y_AXIS]*sin_Ti;
|
|
1919
|
+ r_axis1 = -offset[X_AXIS]*sin_Ti - offset[Y_AXIS]*cos_Ti;
|
|
1920
|
+ count = 0;
|
|
1921
|
+ }
|
|
1922
|
+
|
|
1923
|
+ // Update arc_target location
|
|
1924
|
+ arc_target[X_AXIS] = center_axis0 + r_axis0;
|
|
1925
|
+ arc_target[Y_AXIS] = center_axis1 + r_axis1;
|
|
1926
|
+ arc_target[Z_AXIS] += linear_per_segment;
|
|
1927
|
+ arc_target[E_AXIS] += extruder_per_segment;
|
|
1928
|
+
|
|
1929
|
+ clamp_to_software_endstops(arc_target);
|
|
1930
|
+ plan_buffer_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], arc_target[E_AXIS], feed_rate, active_extruder);
|
|
1931
|
+ }
|
|
1932
|
+ // Ensure last segment arrives at target location.
|
|
1933
|
+ plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feed_rate, active_extruder);
|
|
1934
|
+
|
|
1935
|
+ // As far as the parser is concerned, the position is now == target. In reality the
|
|
1936
|
+ // motion control system might still be processing the action and the real tool position
|
|
1937
|
+ // in any intermediate location.
|
|
1938
|
+ set_current_to_destination();
|
|
1939
|
+}
|
|
1940
|
+
|
|
1941
|
+/**
|
1801
|
1942
|
* G2: Clockwise Arc
|
1802
|
1943
|
* G3: Counterclockwise Arc
|
1803
|
1944
|
*/
|
1804
|
1945
|
inline void gcode_G2_G3(bool clockwise) {
|
1805
|
1946
|
if (IsRunning()) {
|
1806
|
|
- get_arc_coordinates();
|
1807
|
|
- prepare_arc_move(clockwise);
|
|
1947
|
+
|
|
1948
|
+ #ifdef SF_ARC_FIX
|
|
1949
|
+ bool relative_mode_backup = relative_mode;
|
|
1950
|
+ relative_mode = true;
|
|
1951
|
+ #endif
|
|
1952
|
+
|
|
1953
|
+ gcode_get_destination();
|
|
1954
|
+
|
|
1955
|
+ #ifdef SF_ARC_FIX
|
|
1956
|
+ relative_mode = relative_mode_backup;
|
|
1957
|
+ #endif
|
|
1958
|
+
|
|
1959
|
+ // Center of arc as offset from current_position
|
|
1960
|
+ arc_offset[0] = code_seen('I') ? code_value() : 0;
|
|
1961
|
+ arc_offset[1] = code_seen('J') ? code_value() : 0;
|
|
1962
|
+
|
|
1963
|
+ // Send an arc to the planner
|
|
1964
|
+ plan_arc(destination, arc_offset, clockwise);
|
|
1965
|
+
|
|
1966
|
+ refresh_cmd_timeout();
|
1808
|
1967
|
}
|
1809
|
1968
|
}
|
1810
|
1969
|
|
|
@@ -4308,7 +4467,7 @@ inline void gcode_M303() {
|
4308
|
4467
|
//SoftEndsEnabled = false; // Ignore soft endstops during calibration
|
4309
|
4468
|
//SERIAL_ECHOLN(" Soft endstops disabled ");
|
4310
|
4469
|
if (IsRunning()) {
|
4311
|
|
- //get_coordinates(); // For X Y Z E F
|
|
4470
|
+ //gcode_get_destination(); // For X Y Z E F
|
4312
|
4471
|
delta[X_AXIS] = delta_x;
|
4313
|
4472
|
delta[Y_AXIS] = delta_y;
|
4314
|
4473
|
calculate_SCARA_forward_Transform(delta);
|
|
@@ -4932,7 +5091,7 @@ inline void gcode_T() {
|
4932
|
5091
|
make_move = true;
|
4933
|
5092
|
#endif
|
4934
|
5093
|
|
4935
|
|
- next_feedrate = code_value();
|
|
5094
|
+ float next_feedrate = code_value();
|
4936
|
5095
|
if (next_feedrate > 0.0) feedrate = next_feedrate;
|
4937
|
5096
|
}
|
4938
|
5097
|
#if EXTRUDERS > 1
|
|
@@ -5562,33 +5721,6 @@ void ok_to_send() {
|
5562
|
5721
|
SERIAL_EOL;
|
5563
|
5722
|
}
|
5564
|
5723
|
|
5565
|
|
-void get_coordinates() {
|
5566
|
|
- for (int i = 0; i < NUM_AXIS; i++) {
|
5567
|
|
- if (code_seen(axis_codes[i]))
|
5568
|
|
- destination[i] = code_value() + (axis_relative_modes[i] || relative_mode ? current_position[i] : 0);
|
5569
|
|
- else
|
5570
|
|
- destination[i] = current_position[i];
|
5571
|
|
- }
|
5572
|
|
- if (code_seen('F')) {
|
5573
|
|
- next_feedrate = code_value();
|
5574
|
|
- if (next_feedrate > 0.0) feedrate = next_feedrate;
|
5575
|
|
- }
|
5576
|
|
-}
|
5577
|
|
-
|
5578
|
|
-void get_arc_coordinates() {
|
5579
|
|
- #ifdef SF_ARC_FIX
|
5580
|
|
- bool relative_mode_backup = relative_mode;
|
5581
|
|
- relative_mode = true;
|
5582
|
|
- #endif
|
5583
|
|
- get_coordinates();
|
5584
|
|
- #ifdef SF_ARC_FIX
|
5585
|
|
- relative_mode = relative_mode_backup;
|
5586
|
|
- #endif
|
5587
|
|
-
|
5588
|
|
- offset[0] = code_seen('I') ? code_value() : 0;
|
5589
|
|
- offset[1] = code_seen('J') ? code_value() : 0;
|
5590
|
|
-}
|
5591
|
|
-
|
5592
|
5724
|
void clamp_to_software_endstops(float target[3]) {
|
5593
|
5725
|
if (min_software_endstops) {
|
5594
|
5726
|
NOLESS(target[X_AXIS], min_pos[X_AXIS]);
|
|
@@ -5912,19 +6044,6 @@ void prepare_move() {
|
5912
|
6044
|
set_current_to_destination();
|
5913
|
6045
|
}
|
5914
|
6046
|
|
5915
|
|
-void prepare_arc_move(char isclockwise) {
|
5916
|
|
- float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc
|
5917
|
|
-
|
5918
|
|
- // Trace the arc
|
5919
|
|
- mc_arc(current_position, destination, offset, X_AXIS, Y_AXIS, Z_AXIS, feedrate*feedrate_multiplier/60/100.0, r, isclockwise, active_extruder);
|
5920
|
|
-
|
5921
|
|
- // As far as the parser is concerned, the position is now == target. In reality the
|
5922
|
|
- // motion control system might still be processing the action and the real tool position
|
5923
|
|
- // in any intermediate location.
|
5924
|
|
- set_current_to_destination();
|
5925
|
|
- refresh_cmd_timeout();
|
5926
|
|
-}
|
5927
|
|
-
|
5928
|
6047
|
#if HAS_CONTROLLERFAN
|
5929
|
6048
|
|
5930
|
6049
|
void controllerFan() {
|