From adb5b6455d496ed8101ebbc154fff56dacd01206 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 2 Jan 2018 16:31:25 +0100 Subject: [PATCH 001/101] First version of GRBL-Mega-5X ---------------------------------------------------------------------- 5-axis version for GRBL Mega with RAMPS 1.4 Since the RAMPS board has 5 stepper driver slots, I added the management of the 2 unmanaged stepper driver slots in the original version of gnea/grbl-Mega. ---------------------------------------------------------------------- The number of axes is parameterized using the variable N_AXIS in the file grbl / nutbolts.h, the accepted values are 3, 4 or 5. Great care has been taken to maintain compatibility with the original version gnea / grbl-Mega: By setting N_AXIS to 3, we get the same executable as the original GRBL with same other options. This 5-axis version is currently limited to the use of the RAMP card. The 2 additional axes are the rotary axes A and B. ---------------------------------------------------------------------- TODO: (without order priority) - Make the same 5 axis functionalities for other hardware (DEFAULTS_GENERIC + CPU_MAP_2560_INITIAL), perhaps add 6th axis. :-) - Possibility to choice which axe are enabled for 4, 5 (or 6) : A, B or C rotational axis or U, V, W linears axis. - ? --- doc/csv/setting_codes_en_US.csv | 8 ++ doc/markdown/interface.md | 17 +++- doc/markdown/settings.md | 76 ++++++++++++++---- doc/script/simple_stream.py | 0 doc/script/stream.py | 0 grbl/config.h | 27 +++++-- grbl/cpu_map.h | 64 ++++++++++++++- grbl/defaults.h | 20 ++++- grbl/gcode.c | 30 +++++-- grbl/gcode.h | 13 +++- grbl/limits.c | 72 ++++++++++++++++- grbl/motion_control.c | 20 +++++ grbl/motion_control.h | 7 +- grbl/nuts_bolts.h | 21 ++++- grbl/planner.c | 6 ++ grbl/report.c | 11 +++ grbl/settings.c | 36 +++++++++ grbl/settings.h | 2 +- grbl/stepper.c | 133 +++++++++++++++++++++++++++++++- grbl/system.c | 6 ++ 20 files changed, 525 insertions(+), 44 deletions(-) mode change 100644 => 100755 doc/csv/setting_codes_en_US.csv mode change 100644 => 100755 doc/markdown/interface.md mode change 100644 => 100755 doc/markdown/settings.md mode change 100755 => 100644 doc/script/simple_stream.py mode change 100755 => 100644 doc/script/stream.py mode change 100644 => 100755 grbl/cpu_map.h mode change 100644 => 100755 grbl/defaults.h mode change 100644 => 100755 grbl/gcode.c mode change 100644 => 100755 grbl/gcode.h mode change 100644 => 100755 grbl/motion_control.c mode change 100644 => 100755 grbl/nuts_bolts.h mode change 100644 => 100755 grbl/stepper.c mode change 100644 => 100755 grbl/system.c diff --git a/doc/csv/setting_codes_en_US.csv b/doc/csv/setting_codes_en_US.csv old mode 100644 new mode 100755 index fb97a3f3c..c0e3de22b --- a/doc/csv/setting_codes_en_US.csv +++ b/doc/csv/setting_codes_en_US.csv @@ -24,12 +24,20 @@ "100","X-axis travel resolution","step/mm","X-axis travel resolution in steps per millimeter." "101","Y-axis travel resolution","step/mm","Y-axis travel resolution in steps per millimeter." "102","Z-axis travel resolution","step/mm","Z-axis travel resolution in steps per millimeter." +"103","A-axis travel resolution","step/degre","A-Axis travel resolution in steps per degre" +"104","B-axis travel resolution","step/degre","B-Axis travel resolution in steps per degre" "110","X-axis maximum rate","mm/min","X-axis maximum rate. Used as G0 rapid rate." "111","Y-axis maximum rate","mm/min","Y-axis maximum rate. Used as G0 rapid rate." "112","Z-axis maximum rate","mm/min","Z-axis maximum rate. Used as G0 rapid rate." +"113","A-axis maximum rate","degre/min","A-axis maximum rate. Used as G0 rapid rate" +"114","B-axis maximum rate","degre/min","B-axis maximum rate. Used as G0 rapid rate" "120","X-axis acceleration","mm/sec^2","X-axis acceleration. Used for motion planning to not exceed motor torque and lose steps." "121","Y-axis acceleration","mm/sec^2","Y-axis acceleration. Used for motion planning to not exceed motor torque and lose steps." "122","Z-axis acceleration","mm/sec^2","Z-axis acceleration. Used for motion planning to not exceed motor torque and lose steps." +"123","A-Axis acceleration","degre/sec^2","A-axis acceleration. Used for motion planning to not exceed motor torque and lose steps." +"124","B-Axis acceleration","degre/sec^2","B-axis acceleration. Used for motion planning to not exceed motor torque and lose steps." "130","X-axis maximum travel","millimeters","Maximum X-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances." "131","Y-axis maximum travel","millimeters","Maximum Y-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances." "132","Z-axis maximum travel","millimeters","Maximum Z-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances." +"133","A-axis maximum travel","degres","Maximum A-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances." +"134","B-axis maximum travel","degres","Maximum B-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances." diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md old mode 100644 new mode 100755 index 184603e04..c454bed81 --- a/doc/markdown/interface.md +++ b/doc/markdown/interface.md @@ -266,15 +266,23 @@ $32=0 $100=250.000 $101=250.000 $102=250.000 +$103=8.888889 +$104=8.888889 $110=500.000 $111=500.000 $112=500.000 +$113=1440.000 +$114=1440.000 $120=10.000 $121=10.000 $122=10.000 +$123=1000.000 +$124=1000.000 $130=200.000 $131=200.000 $132=200.000 +$133=180.000 +$134=360.000 ok ``` @@ -305,16 +313,23 @@ ok | **`100`** | X-axis steps per millimeter | | **`101`** | Y-axis steps per millimeter | | **`102`** | Z-axis steps per millimeter | +| **`103`** | A-axis steps per degre | +| **`104`** | B-axis steps per degre | | **`110`** | X-axis maximum rate, mm/min | | **`111`** | Y-axis maximum rate, mm/min | | **`112`** | Z-axis maximum rate, mm/min | +| **`113`** | A-axis maximum rate, degres/min | +| **`114`** | B-axis maximum rate, degres/min | | **`120`** | X-axis acceleration, mm/sec^2 | | **`121`** | Y-axis acceleration, mm/sec^2 | | **`122`** | Z-axis acceleration, mm/sec^2 | +| **`123`** | A-axis acceleration, degres/sec^2 | +| **`124`** | B-axis acceleration, degres/sec^2 | | **`130`** | X-axis maximum travel, millimeters | | **`131`** | Y-axis maximum travel, millimeters | | **`132`** | Z-axis maximum travel, millimeters | - +| **`133`** | A-axis maximum travel, degres | +| **`134`** | B-axis maximum travel, degres | - The other `$Nx=line` message is the print-out of a user-defined startup line, where `x` denotes the startup line order and ranges from `0` to `1` by default. The `line` denotes the startup line to be executed by Grbl upon reset or power-up, except during an ALARM. diff --git a/doc/markdown/settings.md b/doc/markdown/settings.md old mode 100644 new mode 100755 index 4b54746f3..582729d96 --- a/doc/markdown/settings.md +++ b/doc/markdown/settings.md @@ -54,15 +54,23 @@ $32=0 $100=250.000 $101=250.000 $102=250.000 +$103=8.888889 +$104=8.888889 $110=500.000 $111=500.000 $112=500.000 +$113=1440.000 +$114=1440.000 $120=10.000 $121=10.000 $122=10.000 +$123=1000.000 +$124=1000.000 $130=200.000 $131=200.000 $132=200.000 +$133=180.000 +$134=360.000 ``` #### $x=val - Save Grbl setting @@ -98,16 +106,42 @@ This setting inverts the step pulse signal. By default, a step signal starts at This invert mask setting is a value which stores the axes to invert as bit flags. You really don't need to completely understand how it works. You simply need to enter the settings value for the axes you want to invert. For example, if you want to invert the X and Z axes, you'd send `$2=5` to Grbl and the setting should now read `$2=5 (step port invert mask:00000101)`. -| Setting Value | Mask |Invert X | Invert Y | Invert Z | -|:-------------:|:----:|:-------:|:--------:|:--------:| -| 0 | 00000000 |N | N | N | -| 1 | 00000001 |Y | N | N | -| 2 | 00000010 |N | Y | N | -| 3 | 00000011 |Y | Y | N | -| 4 | 00000100 |N | N | Y | -| 5 | 00000101 |Y | N | Y | -| 6 | 00000110 |N | Y | Y | -| 7 | 00000111 |Y | Y | Y | +|:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| +| Setting Value | Mask | Invert X | Invert Y | Invert Z | Invert A | Invert B | +|:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| +| 0 | 00000000 | N | N | N | N | N | +| 1 | 00000001 | Y | N | N | N | N | +| 2 | 00000010 | N | Y | N | N | N | +| 3 | 00000011 | Y | Y | N | N | N | +| 4 | 00000100 | N | N | Y | N | N | +| 5 | 00000101 | Y | N | Y | N | N | +| 6 | 00000110 | N | Y | Y | N | N | +| 7 | 00000111 | Y | Y | Y | N | N | +| 8 | 00001000 | N | N | N | Y | N | +| 9 | 00001001 | Y | N | N | Y | N | +| 10 | 00001010 | N | Y | N | Y | N | +| 11 | 00001011 | Y | Y | N | Y | N | +| 12 | 00001100 | N | N | Y | Y | N | +| 13 | 00001101 | Y | N | Y | Y | N | +| 14 | 00001110 | N | Y | Y | Y | N | +| 15 | 00001111 | Y | Y | Y | Y | N | +| 16 | 00010000 | N | N | N | N | Y | +| 17 | 00010001 | Y | N | N | N | Y | +| 18 | 00010010 | N | Y | N | N | Y | +| 19 | 00010011 | Y | Y | N | N | Y | +| 20 | 00010100 | N | N | Y | N | Y | +| 21 | 00010101 | Y | N | Y | N | Y | +| 22 | 00010110 | N | Y | Y | N | Y | +| 23 | 00010111 | Y | Y | Y | N | Y | +| 24 | 00011000 | N | N | N | Y | Y | +| 25 | 00011001 | Y | N | N | Y | Y | +| 26 | 00011010 | N | Y | N | Y | Y | +| 27 | 00011011 | Y | Y | N | Y | Y | +| 28 | 00011100 | N | N | Y | Y | Y | +| 29 | 00011101 | Y | N | Y | Y | Y | +| 30 | 00011110 | N | Y | Y | Y | Y | +| 31 | 00011111 | Y | Y | Y | Y | Y | +|:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| #### $3 – Direction port invert, mask @@ -145,10 +179,12 @@ To keep things simple and consistent, Grbl v1.1 has only two reporting options. Use the table below enables and disable reporting options. Simply add the values listed of what you'd like to enable, then save it by sending Grbl your setting value. For example, the default report with machine position and no buffer data reports setting is `$10=1`. If work position and buffer data are desired, the setting will be `$10=2`. -| Report Type | Value | Description | -|:-------------:|:----:|:----:| -| Position Type | 1 | Enabled `MPos:`. Disabled `WPos:`. | -| Buffer Data | 2 | Enabled `Buf:` field appears with planner and serial RX available buffer. +|:-------------:|:-----:|:-------------------------------------------------------------------------:| +| Report Type | Value | Description | +|:-------------:|:-----:|:-------------------------------------------------------------------------:| +| Position Type | 1 | Enabled `MPos:`. Disabled `WPos:`. | +| Buffer Data | 2 | Enabled `Buf:` field appears with planner and serial RX available buffer. | +|:-------------:|:-----:|:-------------------------------------------------------------------------:| #### $11 - Junction deviation, mm @@ -237,6 +273,9 @@ Grbl needs to know how far each step will take the tool in reality. To calculate The steps/mm can then be calculated like this: ```steps_per_mm = (steps_per_revolution*microsteps)/mm_per_rev``` +#### $103 AND $104 - [A,B] steps/degre +Idem than step/mm, but for rotational axis 4 and 5 + Compute this value for every axis and write these settings to Grbl. #### $110, $111 and $112 – [X,Y,Z] Max rate, mm/min @@ -247,13 +286,22 @@ The simplest way to determine these values is to test each axis one at a time by NOTE: This max rate setting also sets the G0 seek rates. +#### $113 and $114 - [A,B] Max rate, degre/min + +Maximum rate for rotational axis + + #### $120, $121, $122 – [X,Y,Z] Acceleration, mm/sec^2 This sets the axes acceleration parameters in mm/second/second. Simplistically, a lower value makes Grbl ease slower into motion, while a higher value yields tighter moves and reaches the desired feed rates much quicker. Much like the max rate setting, each axis has its own acceleration value and are independent of each other. This means that a multi-axis motion will only accelerate as quickly as the lowest contributing axis can. Again, like the max rate setting, the simplest way to determine the values for this setting is to individually test each axis with slowly increasing values until the motor stalls. Then finalize your acceleration setting with a value 10-20% below this absolute max value. This should account for wear, friction, and mass inertia. We highly recommend that you dry test some G-code programs with your new settings before committing to them. Sometimes the loading on your machine is different when moving in all axes together. +#### $123 and $124 - [A,B] Acceleration, degre/sec^2 + #### $130, $131, $132 – [X,Y,Z] Max travel, mm This sets the maximum travel from end to end for each axis in mm. This is only useful if you have soft limits (and homing) enabled, as this is only used by Grbl's soft limit feature to check if you have exceeded your machine limits with a motion command. + +#### $133, $134 - [A,B] Max travel, degres diff --git a/doc/script/simple_stream.py b/doc/script/simple_stream.py old mode 100755 new mode 100644 diff --git a/doc/script/stream.py b/doc/script/stream.py old mode 100755 new mode 100644 diff --git a/grbl/config.h b/grbl/config.h index bffc41fbc..ed64d4862 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -34,12 +34,12 @@ // NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only // one configuration file by placing their specific defaults and pin map at the bottom of this file. // If doing so, simply comment out these two defines and see instructions below. -#define DEFAULTS_GENERIC -#define CPU_MAP_2560_INITIAL +//#define DEFAULTS_GENERIC +//#define CPU_MAP_2560_INITIAL // To use with RAMPS 1.4 Board, comment out the above defines and uncomment the next two defines -//#define DEFAULTS_RAMPS_BOARD -//#define CPU_MAP_2560_RAMPS_BOARD +#define DEFAULTS_RAMPS_BOARD +#define CPU_MAP_2560_RAMPS_BOARD // Serial baud rate // #define BAUD_RATE 230400 @@ -107,9 +107,22 @@ // will not be affected by pin sharing. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. #ifdef DEFAULTS_RAMPS_BOARD - #define HOMING_CYCLE_0 (1< 3 + #define STEP_PORT_3 A // Axis number 4 (Ramps E0) + #endif + #if N_AXIS > 4 + #define STEP_PORT_4 C // Axis number 5 (Ramps E1) + #endif #define STEP_BIT_0 0 // X Step - Pin A0 #define STEP_BIT_1 6 // Y Step - Pin A6 #define STEP_BIT_2 3 // Z Step - Pin D46 + #if N_AXIS > 3 + #define STEP_BIT_3 4 // Axis number 4 Step - Pin D26 + #endif + #if N_AXIS > 4 + #define STEP_BIT_4 1 // Axis number 5 Step - Pin D36 + #endif #define _STEP_BIT(i) STEP_BIT_##i #define STEP_BIT(i) _STEP_BIT(i) #define STEP_DDR(i) _DDR(STEP_PORT_##i) @@ -166,9 +178,21 @@ #define DIRECTION_PORT_0 F #define DIRECTION_PORT_1 F #define DIRECTION_PORT_2 L + #if N_AXIS > 3 + #define DIRECTION_PORT_3 A // Axis number 4 (Ramps E0) + #endif + #if N_AXIS > 4 + #define DIRECTION_PORT_4 C // Axis number 5 (Ramps E1) + #endif #define DIRECTION_BIT_0 1 // X Dir - Pin A1 #define DIRECTION_BIT_1 7 // Y Dir - Pin A7 #define DIRECTION_BIT_2 1 // Z Dir - Pin D48 + #if N_AXIS > 3 + #define DIRECTION_BIT_3 6 // Axis number 4 Step - Pin D28 + #endif + #if N_AXIS > 4 + #define DIRECTION_BIT_4 3 // Axis number 5 Step - Pin D34 + #endif #define _DIRECTION_BIT(i) DIRECTION_BIT_##i #define DIRECTION_BIT(i) _DIRECTION_BIT(i) #define DIRECTION_DDR(i) _DDR(DIRECTION_PORT_##i) @@ -180,9 +204,21 @@ #define STEPPER_DISABLE_PORT_0 D #define STEPPER_DISABLE_PORT_1 F #define STEPPER_DISABLE_PORT_2 K + #if N_AXIS > 3 + #define STEPPER_DISABLE_PORT_3 A // Axis number 4 (Ramps E0) + #endif + #if N_AXIS > 4 + #define STEPPER_DISABLE_PORT_4 C // Axis number 5 (Ramps E1) + #endif #define STEPPER_DISABLE_BIT_0 7 // X Enable - Pin D38 #define STEPPER_DISABLE_BIT_1 2 // Y Enable - Pin A2 #define STEPPER_DISABLE_BIT_2 0 // Z Enable - Pin A8 + #if N_AXIS > 3 + #define STEPPER_DISABLE_BIT_3 2 // Axis number 4 Step - Pin D24 + #endif + #if N_AXIS > 4 + #define STEPPER_DISABLE_BIT_4 7 // Axis number 5 Step - Pin D30 + #endif #define STEPPER_DISABLE_BIT(i) STEPPER_DISABLE_BIT_##i #define STEPPER_DISABLE_DDR(i) _DDR(STEPPER_DISABLE_PORT_##i) #define STEPPER_DISABLE_PORT(i) _PORT(STEPPER_DISABLE_PORT_##i) @@ -192,9 +228,21 @@ #define MIN_LIMIT_PORT_0 E #define MIN_LIMIT_PORT_1 J #define MIN_LIMIT_PORT_2 D + #if N_AXIS > 3 + #define MIN_LIMIT_PORT_3 L + #endif + #if N_AXIS > 4 + #define MIN_LIMIT_PORT_4 L + #endif #define MIN_LIMIT_BIT_0 5 // X Limit Min - Pin D3 #define MIN_LIMIT_BIT_1 1 // Y Limit Min - Pin D14 #define MIN_LIMIT_BIT_2 3 // Z Limit Min - Pin D18 + #if N_AXIS > 3 + #define MIN_LIMIT_BIT_3 7 // Axis number 4 : RAMPS AUX2 pin D42 + #endif + #if N_AXIS > 4 + #define MIN_LIMIT_BIT_4 5 // Axis number 5 : RAMPS AUX2 pin D44 + #endif #define _MIN_LIMIT_BIT(i) MIN_LIMIT_BIT_##i #define MIN_LIMIT_BIT(i) _MIN_LIMIT_BIT(i) #define MIN_LIMIT_DDR(i) _DDR(MIN_LIMIT_PORT_##i) @@ -204,9 +252,21 @@ #define MAX_LIMIT_PORT_0 E #define MAX_LIMIT_PORT_1 J #define MAX_LIMIT_PORT_2 D + #if N_AXIS > 3 + #define MAX_LIMIT_PORT_3 G + #endif + #if N_AXIS > 4 + #define MAX_LIMIT_PORT_4 F + #endif #define MAX_LIMIT_BIT_0 4 // X Limit Max - Pin D2 #define MAX_LIMIT_BIT_1 0 // Y Limit Max - Pin D15 #define MAX_LIMIT_BIT_2 2 // Z Limit Max - Pin D19 + #if N_AXIS > 3 + #define MAX_LIMIT_BIT_3 1 // Axis number 4 : RAMPS AUX2 pin D40 + #endif + #if N_AXIS > 4 + #define MAX_LIMIT_BIT_4 5 // Axis number 5 : RAMPS AUX2 pin D59 + #endif #define _MAX_LIMIT_BIT(i) MAX_LIMIT_BIT_##i #define MAX_LIMIT_BIT(i) _MAX_LIMIT_BIT(i) #define MAX_LIMIT_DDR(i) _DDR(MAX_LIMIT_PORT_##i) @@ -222,10 +282,10 @@ // Define spindle enable and spindle direction output pins. #define SPINDLE_ENABLE_DDR DDRG #define SPINDLE_ENABLE_PORT PORTG - #define SPINDLE_ENABLE_BIT 5 // MEGA2560 Digital Pin 4 - Ramps 1.4 Servo 4 Signal pin + #define SPINDLE_ENABLE_BIT 5 // MEGA2560 Digital Pin 4 - Ramps 1.4 Servo 4 Signal pin (D4) #define SPINDLE_DIRECTION_DDR DDRE #define SPINDLE_DIRECTION_PORT PORTE - #define SPINDLE_DIRECTION_BIT 3 // MEGA2560 Digital Pin 5 - Ramps 1.4 Servo 3 Signal pin + #define SPINDLE_DIRECTION_BIT 3 // MEGA2560 Digital Pin 5 - Ramps 1.4 Servo 3 Signal pin (D5) // Define flood and mist coolant enable output pins. #define COOLANT_FLOOD_DDR DDRB diff --git a/grbl/defaults.h b/grbl/defaults.h old mode 100644 new mode 100755 index ae80ae0a1..96db2fd3b --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -459,12 +459,24 @@ #define DEFAULT_X_MAX_RATE 18000.0 // mm/min #define DEFAULT_Y_MAX_RATE 18000.0 // mm/min #define DEFAULT_Z_MAX_RATE 300.0 // mm/min - #define DEFAULT_X_ACCELERATION 3000 - #define DEFAULT_Y_ACCELERATION 3000 - #define DEFAULT_Z_ACCELERATION 100 + #define DEFAULT_X_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_Y_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_Z_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 200.0 // mm #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm + #if N_AXIS > 3 + #define DEFAULT_A_STEPS_PER_DEGRE 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_A_MAX_RATE 1440 // °/mn + #define DEFAULT_A_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_A_MAX_TRAVEL 360.0 // ° + #endif + #if N_AXIS > 4 + #define DEFAULT_B_STEPS_PER_DEGRE 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_B_MAX_RATE 1440 // °/mn + #define DEFAULT_B_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_B_MAX_TRAVEL 180.0 // ° + #endif #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -489,4 +501,4 @@ #define DEFAULT_HOMING_PULLOFF 5.0 // mm #endif -#endif \ No newline at end of file +#endif diff --git a/grbl/gcode.c b/grbl/gcode.c old mode 100644 new mode 100755 index 3b323700e..d1f03368b --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -288,8 +288,16 @@ uint8_t gc_execute_line(char *line) legal g-code words and stores their value. Error-checking is performed later since some words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ switch(letter){ - // case 'A': // Not supported - // case 'B': // Not supported + // case 'A': // Not supported (supported in 4 or 5 axes mode) + // case 'B': // Not supported (supported in 4 or 5 axes mode) + #if N_AXIS > 3 + #ifdef A_AXIS + case 'A': word_bit = WORD_A; gc_block.values.xyz[A_AXIS] = value; axis_words |= (1< 3 + for (idx=0; idx 3 + for (idx=0; idx 3 + if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_A)|bit(WORD_B))); } // Remove axis words. +#else if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words. +#endif if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words] /* ------------------------------------------------------------------------------------- @@ -1125,7 +1145,7 @@ uint8_t gc_execute_line(char *line) - Canned cycles - Tool radius compensation - - A,B,C-axes + - A,B,C-axes // A & B Supported in Ramps 1.4 grbl-Mega-5X version if N_AXIS > 3 - Evaluation of expressions - Variables - Override control (TBD) diff --git a/grbl/gcode.h b/grbl/gcode.h old mode 100644 new mode 100755 index 6cdc61b45..ae413f3e2 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -149,7 +149,10 @@ #define WORD_X 10 #define WORD_Y 11 #define WORD_Z 12 - +#if N_AXIS > 3 + #define WORD_A 13 + #define WORD_B 14 +#endif // Define g-code parser position updating flags #define GC_UPDATE_POS_TARGET 0 // Must be zero #define GC_UPDATE_POS_SYSTEM 1 @@ -198,7 +201,11 @@ typedef struct { typedef struct { float f; // Feed +#if N_AXIS > 3 + float ijk[N_AXIS]; // axes offsets for XYZ & AB(C) +#else float ijk[3]; // I,J,K Axis arc offsets +#endif uint8_t l; // G10 or canned cycles parameters int32_t n; // Line number float p; // G10 or dwell parameters @@ -206,7 +213,11 @@ typedef struct { float r; // Arc radius float s; // Spindle speed uint8_t t; // Tool selection +#if N_AXIS > 3 + float xyz[N_AXIS]; // X,Y,Z Translational axes & A,B,(C) +#else float xyz[3]; // X,Y,Z Translational axes +#endif } gc_values_t; diff --git a/grbl/limits.c b/grbl/limits.c index ebe540526..feca8e707 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -37,24 +37,60 @@ void limits_init() MIN_LIMIT_DDR(0) &= ~(1< 3 + MIN_LIMIT_DDR(3) &= ~(1< 4 + MIN_LIMIT_DDR(4) &= ~(1< 3 + MAX_LIMIT_DDR(3) &= ~(1< 4 + MAX_LIMIT_DDR(4) &= ~(1< 3 + MIN_LIMIT_PORT(3) &= ~(1< 4 + MIN_LIMIT_PORT(4) &= ~(1< 3 + MAX_LIMIT_PORT(3) &= ~(1< 4 + MAX_LIMIT_PORT(4) &= ~(1< 3 + MIN_LIMIT_PORT(3) |= (1< 4 + MIN_LIMIT_PORT(4) |= (1< 3 + MAX_LIMIT_PORT(3) |= (1< 4 + MAX_LIMIT_PORT(4) |= (1< 3 + else if (idx==A_AXIS) { axislock[idx] &= ~(step_pin[A_AXIS]); } + #endif + #if N_AXIS > 4 + else if (idx==B_AXIS) { axislock[idx] &= ~(step_pin[B_AXIS]); } + #endif else { axislock[idx] &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else axislock[idx] &= ~(step_pin[idx]); @@ -410,7 +464,11 @@ void limits_go_home(uint8_t cycle_mask) int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position); sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position; } else { + #if N_AXIS > 3 + sys_position[idx] = 0; + #else sys_position[Z_AXIS] = 0; + #endif } #else sys_position[idx] = 0; @@ -448,6 +506,12 @@ void limits_go_home(uint8_t cycle_mask) if (limit_state & (1 << idx)) { #ifdef COREXY if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); } + #if N_AXIS > 3 + else if (idx==A_AXIS) { axislock &= ~(step_pin[A_AXIS]); } + #endif + #if N_AXIS > 4 + else if (idx==B_AXIS) { axislock &= ~(step_pin[B_AXIS]); } + #endif else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else axislock &= ~(step_pin[idx]); diff --git a/grbl/motion_control.c b/grbl/motion_control.c old mode 100644 new mode 100755 index 6e11e35cb..8b660260f --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -120,6 +120,12 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of float theta_per_segment = angular_travel/segments; float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; + #if N_AXIS >3 + float axis_a_per_segment = (target[A_AXIS] - position[A_AXIS])/segments; + #endif + #if N_AXIS >4 + float axis_b_per_segment = (target[B_AXIS] - position[B_AXIS])/segments; + #endif /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. @@ -180,6 +186,12 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of position[axis_1] = center_axis1 + r_axis1; position[axis_linear] += linear_per_segment; + #if N_AXIS > 3 + position[A_AXIS] += axis_a_per_segment; + #endif + #if N_AXIS > 4 + position[B_AXIS] += axis_b_per_segment; + #endif mc_line(position, pl_data); // Bail mid-circle on system abort. Runtime command check already performed by mc_line. @@ -234,6 +246,14 @@ void mc_homing_cycle(uint8_t cycle_mask) #ifdef HOMING_CYCLE_2 limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 #endif + #if N_AXIS > 3 + #ifdef HOMING_CYCLE_3 + limits_go_home(HOMING_CYCLE_3); // Homing cycle 2 + #endif + #ifdef HOMING_CYCLE_4 + limits_go_home(HOMING_CYCLE_4); // Homing cycle 2 + #endif + #endif } protocol_execute_realtime(); // Check for reset and set system abort. diff --git a/grbl/motion_control.h b/grbl/motion_control.h index 442ab2e9f..c0b6e179e 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -31,7 +31,12 @@ #define HOMING_CYCLE_X bit(X_AXIS) #define HOMING_CYCLE_Y bit(Y_AXIS) #define HOMING_CYCLE_Z bit(Z_AXIS) - +#if N_AXIS > 3 + #define HOMING_CYCLE_A bit(A_AXIS) +#endif +#if N_AXIS > 4 + #define HOMING_CYCLE_B bit(B_AXIS) +#endif // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h old mode 100644 new mode 100755 index c936f2be2..b7d097afb --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -28,12 +28,27 @@ #define SOME_LARGE_VALUE 1.0E+38 // Axis array index values. Must start with 0 and be continuous. -#define N_AXIS 3 // Number of axes +#ifdef DEFAULTS_RAMPS_BOARD + // 5 axis support only for RAMPS 1.4 (for the moment :-)...) + // TODO: 5 (or 6) axis support for other hardwares. + #define N_AXIS 5 // Number of axes + #define N_AXIS_LINEAR 3 // Number of linears axis +#else + #define N_AXIS 3 // Number of axes +#endif #define X_AXIS 0 // Axis indexing value. #define Y_AXIS 1 #define Z_AXIS 2 -// #define A_AXIS 3 - +#if N_AXIS > 3 + #define A_AXIS 3 +#endif +#if N_AXIS > 4 + #define B_AXIS 4 +#endif +#if N_AXIS > 5 + #error "N_AXIS must be <= 5. N_AXIS > 5 is not implemented." + // TODO: 6 axis support for other hardwares.#endif +#endif // CoreXY motor assignments. DO NOT ALTER. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. #ifdef COREXY diff --git a/grbl/planner.c b/grbl/planner.c index fcec8ebc7..011592c92 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -332,6 +332,12 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position); position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position); position_steps[Z_AXIS] = sys_position[Z_AXIS]; + #if N_AXIS > 3 + position_steps[A_AXIS] = sys_position[A_AXIS]; + #endif + #if N_AXIS > 4 + position_steps[B_AXIS] = sys_position[B_AXIS]; + #endif #else memcpy(position_steps, sys_position, sizeof(sys_position)); #endif diff --git a/grbl/report.c b/grbl/report.c index 07598494a..ffedc968c 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -361,6 +361,11 @@ void report_build_info(char *line) printPgmString(PSTR("[VER:" GRBL_VERSION "." GRBL_VERSION_BUILD ":")); printString(line); report_util_feedback_line_feed(); + #if N_AXIS > 3 + printPgmString(PSTR("[N_AXIS=")); + print_uint8_base10(N_AXIS); + report_util_feedback_line_feed(); + #endif printPgmString(PSTR("[OPT:")); // Generate compile-time build option list serial_write('V'); serial_write('N'); @@ -546,6 +551,12 @@ void report_realtime_status() if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); } if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); } if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); } + #if N_AXIS > 3 + if (bit_istrue(lim_pin_state,bit(A_AXIS))) { serial_write('A'); } + #endif + #if N_AXIS > 4 + if (bit_istrue(lim_pin_state,bit(A_AXIS))) { serial_write('B'); } + #endif } if (ctrl_pin_state) { #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN diff --git a/grbl/settings.c b/grbl/settings.c index 91337a4c3..95da4d034 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -106,6 +106,18 @@ void settings_restore(uint8_t restore_flag) { settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL); settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); + #if N_AXIS > 3 + settings.steps_per_mm[A_AXIS] = DEFAULT_A_STEPS_PER_DEGRE; + settings.max_rate[A_AXIS] = DEFAULT_A_MAX_RATE; + settings.acceleration[A_AXIS] = DEFAULT_A_ACCELERATION; + settings.max_travel[A_AXIS] = (-DEFAULT_A_MAX_TRAVEL); + #endif + #if N_AXIS > 4 + settings.steps_per_mm[B_AXIS] = DEFAULT_B_STEPS_PER_DEGRE; + settings.max_rate[B_AXIS] = DEFAULT_B_MAX_RATE; + settings.acceleration[B_AXIS] = DEFAULT_B_ACCELERATION; + settings.max_travel[B_AXIS] = (-DEFAULT_B_MAX_TRAVEL); + #endif write_global_settings(); } @@ -318,6 +330,12 @@ uint8_t get_step_pin_mask(uint8_t axis_idx) #ifdef DEFAULTS_RAMPS_BOARD if ( axis_idx == X_AXIS ) { return((1< 3 + if ( axis_idx == A_AXIS ) { return((1< 4 + if ( axis_idx == B_AXIS ) { return((1< 3 + if ( axis_idx == A_AXIS ) { return((1< 4 + if ( axis_idx == B_AXIS ) { return((1< 3 + if ( axis_idx == A_AXIS ) { return((1< 4 + if ( axis_idx == B_AXIS ) { return((1< 3 + if ( axis_idx == A_AXIS ) { return((1< 4 + if ( axis_idx == B_AXIS ) { return((1< steps_per_degre for rotationals axis (A_AXIS and B_AXIS) float max_rate[N_AXIS]; float acceleration[N_AXIS]; float max_travel[N_AXIS]; diff --git a/grbl/stepper.c b/grbl/stepper.c old mode 100644 new mode 100755 index d0fc3aa71..dd3cc095c --- a/grbl/stepper.c +++ b/grbl/stepper.c @@ -97,7 +97,16 @@ typedef struct { // Used by the bresenham line algorithm uint32_t counter_x, // Counter variables for the bresenham line tracer counter_y, + #if N_AXIS == 4 + counter_z, + counter_a; + #elif N_AXIS == 5 + counter_z, + counter_a, + counter_b; + #else counter_z; + #endif #ifdef STEP_PULSE_DELAY #ifdef DEFAULTS_RAMPS_BOARD uint8_t step_bits[N_AXIS]; // Stores out_bits output to complete the step pulse delay @@ -239,10 +248,22 @@ void st_wake_up() STEPPER_DISABLE_PORT(0) |= (1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) |= (1 << STEPPER_DISABLE_BIT(1)); STEPPER_DISABLE_PORT(2) |= (1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); + #endif } else { STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); + #endif } // Initialize stepper output bits to ensure first ISR call does not step. for (idx = 0; idx < N_AXIS; idx++) { @@ -293,10 +314,22 @@ void st_go_idle() STEPPER_DISABLE_PORT(0) |= (1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) |= (1 << STEPPER_DISABLE_BIT(1)); STEPPER_DISABLE_PORT(2) |= (1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); + #endif } else { STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); + #endif } #else if (pin_state) { STEPPERS_DISABLE_PORT |= (1< 3 + DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | st.dir_outbits[3]; + #endif + #if N_AXIS > 4 + DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; + #endif #else DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK); #endif // Ramps Boafd @@ -376,10 +415,22 @@ ISR(TIMER1_COMPA_vect) st.step_bits[0] = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; // Store out_bits to prevent overwriting. st.step_bits[1] = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; // Store out_bits to prevent overwriting. st.step_bits[2] = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; // Store out_bits to prevent overwriting. + #if N_AXIS > 3 + st.step_bits[3] = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; // Store out_bits to prevent overwriting. + #endif + #if N_AXIS > 4 + st.step_bits[4] = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; // Store out_bits to prevent overwriting. + #endif #else STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; + #endif + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; + #endif #endif #else #ifdef STEP_PULSE_DELAY @@ -420,7 +471,13 @@ ISR(TIMER1_COMPA_vect) st.exec_block = &st_block_buffer[st.exec_block_index]; // Initialize Bresenham line and distance counters - st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); + #if N_AXIS == 4 + st.counter_x = st.counter_y = st.counter_z = st.counter_a = (st.exec_block->step_event_count >> 1); + #elif N_AXIS == 5 + st.counter_x = st.counter_y = st.counter_z = st.counter_a = st.counter_b = (st.exec_block->step_event_count >> 1); + #else + st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); + #endif } #ifdef DEFAULTS_RAMPS_BOARD for (i = 0; i < N_AXIS; i++) @@ -434,6 +491,12 @@ ISR(TIMER1_COMPA_vect) st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level; st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level; st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level; + #if N_AXIS > 3 + st.steps[A_AXIS] = st.exec_block->steps[A_AXIS] >> st.exec_segment->amass_level; + #endif + #if N_AXIS > 4 + st.steps[B_AXIS] = st.exec_block->steps[B_AXIS] >> st.exec_segment->amass_level; + #endif #endif // Set real-time spindle output as segment is loaded, just prior to the first step. @@ -523,6 +586,36 @@ ISR(TIMER1_COMPA_vect) else { sys_position[Z_AXIS]++; } } #endif // Ramps Board + #if N_AXIS > 3 + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_a += st.steps[A_AXIS]; + #else + st.counter_a += st.exec_block->steps[A_AXIS]; + #endif + #ifdef DEFAULTS_RAMPS_BOARD + if (st.counter_a > st.exec_block->step_event_count) { + st.step_outbits[A_AXIS] |= (1<step_event_count; + if (st.exec_block->direction_bits[A_AXIS] & (1< 3 + #if N_AXIS > 4 + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_b += st.steps[B_AXIS]; + #else + st.counter_b += st.exec_block->steps[B_AXIS]; + #endif + #ifdef DEFAULTS_RAMPS_BOARD + if (st.counter_b > st.exec_block->step_event_count) { + st.step_outbits[B_AXIS] |= (1<step_event_count; + if (st.exec_block->direction_bits[B_AXIS] & (1< 4 // During a homing cycle, lock out and prevent desired axes from moving. #ifdef DEFAULTS_RAMPS_BOARD @@ -565,6 +658,12 @@ ISR(TIMER0_OVF_vect) STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | step_port_invert_mask[0]; STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | step_port_invert_mask[1]; STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | step_port_invert_mask[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; + #endif + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; + #endif #else STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK); #endif // Ramps Board @@ -582,6 +681,12 @@ ISR(TIMER0_OVF_vect) STEP_PORT(0) = st.step_bits[0]; // Begin step pulse. STEP_PORT(1) = st.step_bits[1]; // Begin step pulse. STEP_PORT(2) = st.step_bits[2]; // Begin step pulse. + #if N_AXIS > 3 + STEP_PORT(3) = st.step_bits[3]; // Begin step pulse. + #endif + #if N_AXIS > 4 + STEP_PORT(4) = st.step_bits[4]; // Begin step pulse. + #endif #else STEP_PORT = st.step_bits; // Begin step pulse. #endif // Ramps Board @@ -643,6 +748,14 @@ void st_reset() STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | step_port_invert_mask[2]; DIRECTION_PORT(2) = (DIRECTION_PORT(2) & ~(1 << DIRECTION_BIT(2))) | dir_port_invert_mask[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; + DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | dir_port_invert_mask[3]; + #endif + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; + DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | dir_port_invert_mask[4]; + #endif #else st.dir_outbits = dir_port_invert_mask; // Initialize direction bits to default. @@ -661,14 +774,32 @@ void stepper_init() STEP_DDR(0) |= 1< 3 + STEP_DDR(3) |= 1< 4 + STEP_DDR(4) |= 1< 3 + STEPPER_DISABLE_DDR(3) |= 1< 4 + STEPPER_DISABLE_DDR(4) |= 1< 3 + DIRECTION_DDR(3) |= 1< 4 + DIRECTION_DDR(4) |= 1< 3 + case 'A': mc_homing_cycle(HOMING_CYCLE_A); break; + #endif + #if N_AXIS > 4 + case 'B': mc_homing_cycle(HOMING_CYCLE_B); break; + #endif default: return(STATUS_INVALID_STATEMENT); } #endif From 1a47bffb65ddbaba438788ba6236dd8c633e2399 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 23 Apr 2018 08:39:30 +0200 Subject: [PATCH 002/101] Correction COPYING & typos --- COPYING | 1 + Makefile | 3 ++- grbl/config.h | 1 + grbl/cpu_map.h | 1 + grbl/defaults.h | 41 +++++++++++++++++++++-------------------- grbl/gcode.c | 1 + grbl/gcode.h | 1 + grbl/grbl.h | 2 +- grbl/limits.c | 1 + grbl/motion_control.c | 1 + grbl/motion_control.h | 1 + grbl/nuts_bolts.c | 1 + grbl/nuts_bolts.h | 1 + grbl/planner.c | 1 + grbl/report.c | 29 +++++++++++++++-------------- grbl/serial.c | 37 ++++++++++++++++++++++++++++++++++--- grbl/serial.h | 3 +++ grbl/settings.c | 1 + grbl/settings.h | 1 + grbl/stepper.c | 1 + grbl/system.c | 1 + 21 files changed, 91 insertions(+), 39 deletions(-) diff --git a/COPYING b/COPYING index de86f6fb5..93fa11bd1 100644 --- a/COPYING +++ b/COPYING @@ -4,6 +4,7 @@ COPYRIGHT NOTICE FOR GRBL: Grbl - Embedded CNC g-code interpreter and motion-controller +Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Jens Geisler diff --git a/Makefile b/Makefile index 6d97edfba..a95f53c6c 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,8 @@ DEVICE ?= atmega2560 CLOCK = 16000000L -PROGRAMMER ?= -c avrisp2 -P usb +###PROGRAMMER ?= -c avrisp2 -P usb +PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB0 SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c serial.c \ protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ print.c probe.c report.c system.c sleep.c jog.c diff --git a/grbl/config.h b/grbl/config.h index ed64d4862..8b2612320 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -2,6 +2,7 @@ config.h - compile time configuration Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 1193b83d2..b386d1370 100755 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -2,6 +2,7 @@ cpu_map.h - CPU and pin mapping configuration file Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/defaults.h b/grbl/defaults.h index 96db2fd3b..08d978329 100755 --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -2,6 +2,7 @@ defaults.h - defaults settings configuration file Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -19,7 +20,7 @@ */ /* The defaults.h file serves as a central default settings selector for different machine - types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings + types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings files listed here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. @@ -84,10 +85,10 @@ #define DEFAULT_Y_MAX_TRAVEL 125.0 // mm NOTE: Must be a positive value. #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 2800.0 // rpm - #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm + #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 - #define DEFAULT_DIRECTION_INVERT_MASK ((1< 4 #define DEFAULT_B_STEPS_PER_DEGRE 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° diff --git a/grbl/gcode.c b/grbl/gcode.c index d1f03368b..2c4a6accc 100755 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -2,6 +2,7 @@ gcode.c - rs274/ngc parser. Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/gcode.h b/grbl/gcode.h index ae413f3e2..4803fd393 100755 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -2,6 +2,7 @@ gcode.h - rs274/ngc parser. Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/grbl.h b/grbl/grbl.h index fc8b8884a..e96d23316 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,7 +23,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1f" -#define GRBL_VERSION_BUILD "20170802" +#define GRBL_VERSION_BUILD "20180223" // Define standard libraries used by Grbl. #include diff --git a/grbl/limits.c b/grbl/limits.c index feca8e707..cdbbbff15 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -2,6 +2,7 @@ limits.c - code pertaining to limit-switches and performing the homing cycle Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/motion_control.c b/grbl/motion_control.c index 8b660260f..2094ddce4 100755 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -2,6 +2,7 @@ motion_control.c - high level interface for issuing motion commands Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/motion_control.h b/grbl/motion_control.h index c0b6e179e..cce8846bf 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -2,6 +2,7 @@ motion_control.h - high level interface for issuing motion commands Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/nuts_bolts.c b/grbl/nuts_bolts.c index 9d89a8d06..40fed7378 100644 --- a/grbl/nuts_bolts.c +++ b/grbl/nuts_bolts.c @@ -2,6 +2,7 @@ nuts_bolts.c - Shared functions Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index b7d097afb..356aec197 100755 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -2,6 +2,7 @@ nuts_bolts.h - Header file for shared definitions, variables, and functions Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/planner.c b/grbl/planner.c index 011592c92..96ab21af0 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -2,6 +2,7 @@ planner.c - buffers movement commands and manages the acceleration profile plan Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Jens Geisler diff --git a/grbl/report.c b/grbl/report.c index ffedc968c..914ab7221 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -2,6 +2,7 @@ report.c - reporting and messaging methods Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -50,7 +51,7 @@ static void report_util_setting_string(uint8_t n) { serial_write('('); switch(n) { case 0: printPgmString(PSTR("stp pulse")); break; - case 1: printPgmString(PSTR("idl delay")); break; + case 1: printPgmString(PSTR("idl delay")); break; case 2: printPgmString(PSTR("stp inv")); break; case 3: printPgmString(PSTR("dir inv")); break; case 4: printPgmString(PSTR("stp en inv")); break; @@ -91,13 +92,13 @@ static void report_util_setting_string(uint8_t n) { } */ -static void report_util_uint8_setting(uint8_t n, int val) { - report_util_setting_prefix(n); - print_uint8_base10(val); - report_util_line_feed(); // report_util_setting_string(n); +static void report_util_uint8_setting(uint8_t n, int val) { + report_util_setting_prefix(n); + print_uint8_base10(val); + report_util_line_feed(); // report_util_setting_string(n); } -static void report_util_float_setting(uint8_t n, float val, uint8_t n_decimal) { - report_util_setting_prefix(n); +static void report_util_float_setting(uint8_t n, float val, uint8_t n_decimal) { + report_util_setting_prefix(n); printFloat(val,n_decimal); report_util_line_feed(); // report_util_setting_string(n); } @@ -174,7 +175,7 @@ void report_init_message() // Grbl help message void report_grbl_help() { - printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n")); + printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n")); } @@ -298,8 +299,8 @@ void report_gcode_modes() switch (gc_state.modal.program_flow) { case PROGRAM_FLOW_PAUSED : serial_write('0'); break; // case PROGRAM_FLOW_OPTIONAL_STOP : serial_write('1'); break; // M1 is ignored and not supported. - case PROGRAM_FLOW_COMPLETED_M2 : - case PROGRAM_FLOW_COMPLETED_M30 : + case PROGRAM_FLOW_COMPLETED_M2 : + case PROGRAM_FLOW_COMPLETED_M30 : print_uint8_base10(gc_state.modal.program_flow); break; } @@ -319,12 +320,12 @@ void report_gcode_modes() } else { serial_write('9'); } #ifdef ENABLE_PARKING_OVERRIDE_CONTROL - if (sys.override_ctrl == OVERRIDE_PARKING_MOTION) { + if (sys.override_ctrl == OVERRIDE_PARKING_MOTION) { report_util_gcode_modes_M(); print_uint8_base10(56); } #endif - + printPgmString(PSTR(" T")); print_uint8_base10(gc_state.tool); @@ -362,7 +363,7 @@ void report_build_info(char *line) printString(line); report_util_feedback_line_feed(); #if N_AXIS > 3 - printPgmString(PSTR("[N_AXIS=")); + printPgmString(PSTR("[AXS:")); print_uint8_base10(N_AXIS); report_util_feedback_line_feed(); #endif @@ -604,7 +605,7 @@ void report_realtime_status() } if (cl_state & COOLANT_STATE_FLOOD) { serial_write('F'); } if (cl_state & COOLANT_STATE_MIST) { serial_write('M'); } - } + } } #endif diff --git a/grbl/serial.c b/grbl/serial.c index 381812972..fe03f106f 100644 --- a/grbl/serial.c +++ b/grbl/serial.c @@ -2,6 +2,7 @@ serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -156,11 +157,30 @@ ISR(SERIAL_RX) if (data > 0x7F) { // Real-time control characters are extended ACSII only. switch(data) { case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true - case CMD_JOG_CANCEL: + case CMD_JOG_CANCEL: if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel. - system_set_exec_state_flag(EXEC_MOTION_CANCEL); + system_set_exec_state_flag(EXEC_MOTION_CANCEL); + /* Pour debug + serial_write('*'); + serial_write('J'); + serial_write('o'); + serial_write('g'); + serial_write(' '); + serial_write('C'); + serial_write('a'); + serial_write('n'); + serial_write('c'); + serial_write('e'); + serial_write('l'); + serial_write('\r'); + serial_write('\n'); + */ + serial_reset_read_buffer(); // Vide un reste éventuel de données dans le buffer + /* Pour debug + serial_putstring("Buffer remis a zero\r\n"); + */ } - break; + break; #ifdef DEBUG case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break; #endif @@ -200,3 +220,14 @@ void serial_reset_read_buffer() { serial_rx_buffer_tail = serial_rx_buffer_head; } + + +void serial_putstring(char* StringPtr) +{ + int i; + int len = strlen(StringPtr); + for(i=0; i Date: Mon, 23 Apr 2018 10:27:48 +0200 Subject: [PATCH 003/101] Choice of 4th & 5th axis name(s) --- grbl/config.h | 80 ++++++++--------- grbl/defaults.h | 70 +++++++-------- grbl/gcode.c | 72 ++++++++-------- grbl/grbl.h | 5 +- grbl/limits.c | 64 +++++++------- grbl/motion_control.c | 20 ++--- grbl/motion_control.h | 6 +- grbl/nuts_bolts.h | 11 ++- grbl/planner.c | 12 +-- grbl/report.c | 6 +- grbl/serial.c | 18 ---- grbl/settings.c | 42 ++++----- grbl/settings.h | 6 +- grbl/stepper.c | 196 +++++++++++++++++++++--------------------- grbl/system.c | 8 +- 15 files changed, 301 insertions(+), 315 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 8b2612320..3d5f848de 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -108,21 +108,21 @@ // will not be affected by pin sharing. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. #ifdef DEFAULTS_RAMPS_BOARD - #if N_AXIS == 4 // 4 axis : homing - #define HOMING_CYCLE_0 (1< 3 - #define DEFAULT_A_STEPS_PER_DEGRE 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_A_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° #define DEFAULT_A_MAX_RATE 1440 // °/mn #define DEFAULT_A_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_A_MAX_TRAVEL 360.0 // ° #endif #if N_AXIS > 4 - #define DEFAULT_B_STEPS_PER_DEGRE 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_B_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° #define DEFAULT_B_MAX_RATE 1440 // °/mn #define DEFAULT_B_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_B_MAX_TRAVEL 180.0 // ° diff --git a/grbl/gcode.c b/grbl/gcode.c index 2c4a6accc..3045df363 100755 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -157,7 +157,7 @@ uint8_t gc_execute_line(char *line) if (!((mantissa == 0) || (mantissa == 10))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } gc_block.non_modal_command += mantissa; mantissa = 0; // Set to zero to indicate valid non-integer G command. - } + } break; case 0: case 1: case 2: case 3: case 38: // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block. @@ -174,7 +174,7 @@ uint8_t gc_execute_line(char *line) } gc_block.modal.motion += (mantissa/10)+100; mantissa = 0; // Set to zero to indicate valid non-integer G command. - } + } break; case 17: case 18: case 19: word_bit = MODAL_GROUP_G2; @@ -258,10 +258,10 @@ uint8_t gc_execute_line(char *line) case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break; case 5: gc_block.modal.spindle = SPINDLE_DISABLE; break; } - break; + break; case 7: case 8: case 9: - word_bit = MODAL_GROUP_M8; - switch(int_value) { + word_bit = MODAL_GROUP_M8; + switch(int_value) { case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break; case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break; case 9: gc_block.modal.coolant = COOLANT_DISABLE; break; @@ -289,17 +289,15 @@ uint8_t gc_execute_line(char *line) legal g-code words and stores their value. Error-checking is performed later since some words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ switch(letter){ - // case 'A': // Not supported (supported in 4 or 5 axes mode) - // case 'B': // Not supported (supported in 4 or 5 axes mode) + // case 'A', 'B' or 'C' depending of AXIS_4_NAME and AXIS_5_NAME supported in 4 or 5 axes mode #if N_AXIS > 3 - #ifdef A_AXIS - case 'A': word_bit = WORD_A; gc_block.values.xyz[A_AXIS] = value; axis_words |= (1< MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } - gc_block.values.t = int_value; - break; + case 'T': word_bit = WORD_T; + if (value > MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } + gc_block.values.t = int_value; + break; case 'X': word_bit = WORD_X; gc_block.values.xyz[X_AXIS] = value; axis_words |= (1< 3 - for (idx=0; idx 3 + for (idx=0; idx 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. else { coord_select = gc_block.modal.coord_select; } // Index P0 as the active coordinate system - + // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command. if (!settings_read_coord_data(coord_select,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail] @@ -674,7 +672,7 @@ uint8_t gc_execute_line(char *line) // Axis words are optional. If missing, set axis command flag to ignore execution. if (!axis_words) { axis_command = AXIS_COMMAND_NONE; } break; - case MOTION_MODE_CW_ARC: + case MOTION_MODE_CW_ARC: gc_parser_flags |= GC_PARSER_ARC_IS_CLOCKWISE; // No break intentional. case MOTION_MODE_CCW_ARC: // [G2/3 Errors All-Modes]: Feed rate undefined. @@ -818,7 +816,7 @@ uint8_t gc_execute_line(char *line) case MOTION_MODE_PROBE_TOWARD_NO_ERROR: case MOTION_MODE_PROBE_AWAY_NO_ERROR: gc_parser_flags |= GC_PARSER_PROBE_IS_NO_ERROR; // No break intentional. case MOTION_MODE_PROBE_TOWARD: case MOTION_MODE_PROBE_AWAY: - if ((gc_block.modal.motion == MOTION_MODE_PROBE_AWAY) || + if ((gc_block.modal.motion == MOTION_MODE_PROBE_AWAY) || (gc_block.modal.motion == MOTION_MODE_PROBE_AWAY_NO_ERROR)) { gc_parser_flags |= GC_PARSER_PROBE_IS_AWAY; } // [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate // is undefined. Probe is triggered. NOTE: Probe check moved to probe cycle. Instead of returning @@ -877,34 +875,34 @@ uint8_t gc_execute_line(char *line) if (status == STATUS_OK) { memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); } return(status); } - + // If in laser mode, setup laser power based on current and past parser conditions. if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) { - if ( !((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) + if ( !((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) || (gc_block.modal.motion == MOTION_MODE_CCW_ARC)) ) { gc_parser_flags |= GC_PARSER_LASER_DISABLE; } - // Any motion mode with axis words is allowed to be passed from a spindle speed update. + // Any motion mode with axis words is allowed to be passed from a spindle speed update. // NOTE: G1 and G0 without axis words sets axis_command to none. G28/30 are intentionally omitted. // TODO: Check sync conditions for M3 enabled motions that don't enter the planner. (zero length). - if (axis_words && (axis_command == AXIS_COMMAND_MOTION_MODE)) { - gc_parser_flags |= GC_PARSER_LASER_ISMOTION; + if (axis_words && (axis_command == AXIS_COMMAND_MOTION_MODE)) { + gc_parser_flags |= GC_PARSER_LASER_ISMOTION; } else { // M3 constant power laser requires planner syncs to update the laser when changing between // a G1/2/3 motion mode state and vice versa when there is no motion in the line. if (gc_state.modal.spindle == SPINDLE_ENABLE_CW) { - if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC) + if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC) || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) { - if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; // Change from G1/2/3 motion mode. } } else { // When changing to a G1 motion mode without axis words from a non-G1/2/3 motion mode. - if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; } - } + } } } } @@ -926,7 +924,7 @@ uint8_t gc_execute_line(char *line) // [4. Set spindle speed ]: if ((gc_state.spindle_speed != gc_block.values.s) || bit_istrue(gc_parser_flags,GC_PARSER_LASER_FORCE_SYNC)) { - if (gc_state.modal.spindle != SPINDLE_DISABLE) { + if (gc_state.modal.spindle != SPINDLE_DISABLE) { if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_ISMOTION)) { if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { spindle_sync(gc_state.modal.spindle, 0.0); @@ -937,9 +935,9 @@ uint8_t gc_execute_line(char *line) } // NOTE: Pass zero spindle speed for all restricted laser motions. if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { - pl_data->spindle_speed = gc_state.spindle_speed; // Record data for planner use. + pl_data->spindle_speed = gc_state.spindle_speed; // Record data for planner use. } // else { pl_data->spindle_speed = 0.0; } // Initialized as zero already. - + // [5. Select tool ]: NOT SUPPORTED. Only tracks tool value. gc_state.tool = gc_block.values.t; @@ -1072,8 +1070,8 @@ uint8_t gc_execute_line(char *line) pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE; #endif gc_update_pos = mc_probe_cycle(gc_block.values.xyz, pl_data, gc_parser_flags); - } - + } + // 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 // in any intermediate location. @@ -1082,7 +1080,7 @@ uint8_t gc_execute_line(char *line) } else if (gc_update_pos == GC_UPDATE_POS_SYSTEM) { gc_sync_position(); // gc_state.position[] = sys_position } // == GC_UPDATE_POS_NONE - } + } } // [21. Program flow ]: diff --git a/grbl/grbl.h b/grbl/grbl.h index e96d23316..595d264da 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -2,6 +2,7 @@ grbl.h - main Grbl include file Part of Grbl + Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2015-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -22,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1f" -#define GRBL_VERSION_BUILD "20180223" +#define GRBL_VERSION "1.1g" +#define GRBL_VERSION_BUILD "20180423" // Define standard libraries used by Grbl. #include diff --git a/grbl/limits.c b/grbl/limits.c index cdbbbff15..1f423047e 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -5,7 +5,7 @@ Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - + Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with Grbl. If not, see . */ - + #include "grbl.h" @@ -39,19 +39,19 @@ void limits_init() MIN_LIMIT_DDR(1) &= ~(1< 3 - MIN_LIMIT_DDR(3) &= ~(1< 4 - MIN_LIMIT_DDR(4) &= ~(1< 3 - MAX_LIMIT_DDR(3) &= ~(1< 4 - MAX_LIMIT_DDR(4) &= ~(1<feed_rate = homing_rate; // Set current homing rate. @@ -388,10 +388,10 @@ void limits_go_home(uint8_t cycle_mask) #ifdef COREXY if (idx==Z_AXIS) { axislock[idx] &= ~(step_pin[Z_AXIS]); } #if N_AXIS > 3 - else if (idx==A_AXIS) { axislock[idx] &= ~(step_pin[A_AXIS]); } + else if (idx==AXIS_4) { axislock[idx] &= ~(step_pin[AXIS_4]); } #endif #if N_AXIS > 4 - else if (idx==B_AXIS) { axislock[idx] &= ~(step_pin[B_AXIS]); } + else if (idx==AXIS_5) { axislock[idx] &= ~(step_pin[AXIS_5]); } #endif else { axislock[idx] &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else @@ -508,10 +508,10 @@ void limits_go_home(uint8_t cycle_mask) #ifdef COREXY if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); } #if N_AXIS > 3 - else if (idx==A_AXIS) { axislock &= ~(step_pin[A_AXIS]); } + else if (idx==AXIS_4) { axislock &= ~(step_pin[AXIS_4]); } #endif #if N_AXIS > 4 - else if (idx==B_AXIS) { axislock &= ~(step_pin[B_AXIS]); } + else if (idx==AXIS_5) { axislock &= ~(step_pin[AXIS_5]); } #endif else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else diff --git a/grbl/motion_control.c b/grbl/motion_control.c index 2094ddce4..27982bfc9 100755 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -114,18 +114,18 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of // Multiply inverse feed_rate to compensate for the fact that this movement is approximated // by a number of discrete segments. The inverse feed_rate should be correct for the sum of // all segments. - if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) { - pl_data->feed_rate *= segments; + if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) { + pl_data->feed_rate *= segments; bit_false(pl_data->condition,PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments. } - + float theta_per_segment = angular_travel/segments; float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; #if N_AXIS >3 - float axis_a_per_segment = (target[A_AXIS] - position[A_AXIS])/segments; + float axis_a_per_segment = (target[AXIS_4] - position[AXIS_4])/segments; #endif #if N_AXIS >4 - float axis_b_per_segment = (target[B_AXIS] - position[B_AXIS])/segments; + float axis_b_per_segment = (target[AXIS_5] - position[AXIS_5])/segments; #endif /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, @@ -188,10 +188,10 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of position[axis_linear] += linear_per_segment; #if N_AXIS > 3 - position[A_AXIS] += axis_a_per_segment; + position[AXIS_4] += axis_a_per_segment; #endif #if N_AXIS > 4 - position[B_AXIS] += axis_b_per_segment; + position[AXIS_5] += axis_b_per_segment; #endif mc_line(position, pl_data); @@ -233,7 +233,7 @@ void mc_homing_cycle(uint8_t cycle_mask) // ------------------------------------------------------------------------------------- // Perform homing routine. NOTE: Special motion case. Only system reset works. - + #ifdef HOMING_SINGLE_AXIS_COMMANDS if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask. else @@ -399,8 +399,8 @@ void mc_reset() // the steppers enabled by avoiding the go_idle call altogether, unless the motion state is // violated, by which, all bets are off. if ((sys.state & (STATE_CYCLE | STATE_HOMING | STATE_JOG)) || - (sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) { - if (sys.state == STATE_HOMING) { + (sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) { + if (sys.state == STATE_HOMING) { if (!sys_rt_exec_alarm) {system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); } } else { system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); } st_go_idle(); // Force kill steppers. Position has likely been lost. diff --git a/grbl/motion_control.h b/grbl/motion_control.h index cce8846bf..1c6d368c3 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -5,7 +5,7 @@ Copyright (c) 2017-2018 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - + Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -33,10 +33,10 @@ #define HOMING_CYCLE_Y bit(Y_AXIS) #define HOMING_CYCLE_Z bit(Z_AXIS) #if N_AXIS > 3 - #define HOMING_CYCLE_A bit(A_AXIS) + #define HOMING_CYCLE_4 bit(AXIS_4) #endif #if N_AXIS > 4 - #define HOMING_CYCLE_B bit(B_AXIS) + #define HOMING_CYCLE_5 bit(AXIS_5) #endif // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index 356aec197..8bbc7b1d3 100755 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -32,8 +32,9 @@ #ifdef DEFAULTS_RAMPS_BOARD // 5 axis support only for RAMPS 1.4 (for the moment :-)...) // TODO: 5 (or 6) axis support for other hardwares. - #define N_AXIS 5 // Number of axes - #define N_AXIS_LINEAR 3 // Number of linears axis + #define N_AXIS 5 // Number of axes + #define AXIS_NAMES "XYZAB" // Letters (names) of axis + #define N_AXIS_LINEAR 3 // Number of linears axis #else #define N_AXIS 3 // Number of axes #endif @@ -41,10 +42,12 @@ #define Y_AXIS 1 #define Z_AXIS 2 #if N_AXIS > 3 - #define A_AXIS 3 + #define AXIS_4 3 + #define AXIS_4_NAME 'A' // Letter of axis number 4 #endif #if N_AXIS > 4 - #define B_AXIS 4 + #define AXIS_5 4 + #define AXIS_5_NAME 'B' // Letter of axis number 5 #endif #if N_AXIS > 5 #error "N_AXIS must be <= 5. N_AXIS > 5 is not implemented." diff --git a/grbl/planner.c b/grbl/planner.c index 96ab21af0..644929008 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -328,19 +328,19 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) uint8_t idx; // Copy position data based on type of motion being planned. - if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) { + if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) { #ifdef COREXY position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position); position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position); position_steps[Z_AXIS] = sys_position[Z_AXIS]; #if N_AXIS > 3 - position_steps[A_AXIS] = sys_position[A_AXIS]; + position_steps[AXIS_4] = sys_position[AXIS_4]; #endif #if N_AXIS > 4 - position_steps[B_AXIS] = sys_position[B_AXIS]; + position_steps[AXIS_5] = sys_position[AXIS_5]; #endif #else - memcpy(position_steps, sys_position, sizeof(sys_position)); + memcpy(position_steps, sys_position, sizeof(sys_position)); #endif } else { memcpy(position_steps, pl.position, sizeof(pl.position)); } @@ -373,7 +373,7 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) block->steps[idx] = labs(target_steps[idx]-position_steps[idx]); block->step_event_count = max(block->step_event_count, block->steps[idx]); delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx]; - #endif + #endif unit_vec[idx] = delta_mm; // Store unit vector numerator // Set direction bits. Bit enabled always means direction is negative. @@ -397,7 +397,7 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) // Store programmed rate. if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; } - else { + else { block->programmed_rate = pl_data->feed_rate; if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; } } diff --git a/grbl/report.c b/grbl/report.c index 914ab7221..6b7e30596 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -365,6 +365,8 @@ void report_build_info(char *line) #if N_AXIS > 3 printPgmString(PSTR("[AXS:")); print_uint8_base10(N_AXIS); + printPgmString(PSTR(":")); + printPgmString(PSTR(AXIS_NAMES)); report_util_feedback_line_feed(); #endif printPgmString(PSTR("[OPT:")); // Generate compile-time build option list @@ -553,10 +555,10 @@ void report_realtime_status() if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); } if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); } #if N_AXIS > 3 - if (bit_istrue(lim_pin_state,bit(A_AXIS))) { serial_write('A'); } + if (bit_istrue(lim_pin_state,bit(AXIS_4))) { serial_write(AXIS_4_NAME); } #endif #if N_AXIS > 4 - if (bit_istrue(lim_pin_state,bit(A_AXIS))) { serial_write('B'); } + if (bit_istrue(lim_pin_state,bit(AXIS_4))) { serial_write(AXIS_5_NAME); } #endif } if (ctrl_pin_state) { diff --git a/grbl/serial.c b/grbl/serial.c index fe03f106f..099e1f9ea 100644 --- a/grbl/serial.c +++ b/grbl/serial.c @@ -160,25 +160,7 @@ ISR(SERIAL_RX) case CMD_JOG_CANCEL: if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel. system_set_exec_state_flag(EXEC_MOTION_CANCEL); - /* Pour debug - serial_write('*'); - serial_write('J'); - serial_write('o'); - serial_write('g'); - serial_write(' '); - serial_write('C'); - serial_write('a'); - serial_write('n'); - serial_write('c'); - serial_write('e'); - serial_write('l'); - serial_write('\r'); - serial_write('\n'); - */ serial_reset_read_buffer(); // Vide un reste éventuel de données dans le buffer - /* Pour debug - serial_putstring("Buffer remis a zero\r\n"); - */ } break; #ifdef DEBUG diff --git a/grbl/settings.c b/grbl/settings.c index ea50a453d..6a5713b9c 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -1,5 +1,5 @@ /* - settings.c - eeprom configuration handling + settings.c - eeprom configuration handling Part of Grbl Copyright (c) 2017-2018 Gauthier Briere @@ -29,7 +29,7 @@ settings_t settings; void settings_store_startup_line(uint8_t n, char *line) { #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE - protocol_buffer_synchronize(); // A startup line may contain a motion and be executing. + protocol_buffer_synchronize(); // A startup line may contain a motion and be executing. #endif uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); @@ -95,9 +95,9 @@ void settings_restore(uint8_t restore_flag) { if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } if (DEFAULT_INVERT_PROBE_PIN) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_UNIT; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_UNIT; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_UNIT; settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; @@ -108,16 +108,16 @@ void settings_restore(uint8_t restore_flag) { settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); #if N_AXIS > 3 - settings.steps_per_mm[A_AXIS] = DEFAULT_A_STEPS_PER_DEGRE; - settings.max_rate[A_AXIS] = DEFAULT_A_MAX_RATE; - settings.acceleration[A_AXIS] = DEFAULT_A_ACCELERATION; - settings.max_travel[A_AXIS] = (-DEFAULT_A_MAX_TRAVEL); + settings.steps_per_mm[AXIS_4] = DEFAULT_A_STEPS_PER_UNIT; + settings.max_rate[AXIS_4] = DEFAULT_A_MAX_RATE; + settings.acceleration[AXIS_4] = DEFAULT_A_ACCELERATION; + settings.max_travel[AXIS_4] = (-DEFAULT_A_MAX_TRAVEL); #endif #if N_AXIS > 4 - settings.steps_per_mm[B_AXIS] = DEFAULT_B_STEPS_PER_DEGRE; - settings.max_rate[B_AXIS] = DEFAULT_B_MAX_RATE; - settings.acceleration[B_AXIS] = DEFAULT_B_ACCELERATION; - settings.max_travel[B_AXIS] = (-DEFAULT_B_MAX_TRAVEL); + settings.steps_per_mm[AXIS_5] = DEFAULT_B_STEPS_PER_UNIT; + settings.max_rate[AXIS_5] = DEFAULT_B_MAX_RATE; + settings.acceleration[AXIS_5] = DEFAULT_B_ACCELERATION; + settings.max_travel[AXIS_5] = (-DEFAULT_B_MAX_TRAVEL); #endif write_global_settings(); @@ -332,10 +332,10 @@ uint8_t get_step_pin_mask(uint8_t axis_idx) if ( axis_idx == X_AXIS ) { return((1< 3 - if ( axis_idx == A_AXIS ) { return((1< 4 - if ( axis_idx == B_AXIS ) { return((1< 3 - if ( axis_idx == A_AXIS ) { return((1< 4 - if ( axis_idx == B_AXIS ) { return((1< 3 - if ( axis_idx == A_AXIS ) { return((1< 4 - if ( axis_idx == B_AXIS ) { return((1< 3 - if ( axis_idx == A_AXIS ) { return((1< 4 - if ( axis_idx == B_AXIS ) { return((1< steps_per_degre for rotationals axis (A_AXIS and B_AXIS) + float steps_per_mm[N_AXIS]; // Steps per units => steps_per_degre for rotationals axis (AXIS_4 and AXIS_5) float max_rate[N_AXIS]; float acceleration[N_AXIS]; float max_travel[N_AXIS]; @@ -91,10 +91,10 @@ typedef struct { uint8_t status_report_mask; // Mask to indicate desired report data. float junction_deviation; float arc_tolerance; - + float rpm_max; float rpm_min; - + uint8_t flags; // Contains default boolean settings uint8_t homing_dir_mask; diff --git a/grbl/stepper.c b/grbl/stepper.c index c1f8d0291..c82a39fde 100755 --- a/grbl/stepper.c +++ b/grbl/stepper.c @@ -100,11 +100,11 @@ typedef struct { counter_y, #if N_AXIS == 4 counter_z, - counter_a; + counter_4; #elif N_AXIS == 5 counter_z, - counter_a, - counter_b; + counter_4, + counter_5; #else counter_z; #endif @@ -186,7 +186,7 @@ typedef struct { float decelerate_after; // Deceleration ramp start measured from end of block (mm) float inv_rate; // Used by PWM laser mode to speed up segment calculations. - uint16_t current_spindle_pwm; + uint16_t current_spindle_pwm; } st_prep_t; static st_prep_t prep; @@ -242,7 +242,7 @@ void st_wake_up() #ifdef DEFAULTS_RAMPS_BOARD int idx; #endif // Ramps Board - + // Enable stepper drivers. #ifdef DEFAULTS_RAMPS_BOARD if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { @@ -401,10 +401,10 @@ ISR(TIMER1_COMPA_vect) DIRECTION_PORT(1) = (DIRECTION_PORT(1) & ~(1 << DIRECTION_BIT(1))) | st.dir_outbits[1]; DIRECTION_PORT(2) = (DIRECTION_PORT(2) & ~(1 << DIRECTION_BIT(2))) | st.dir_outbits[2]; #if N_AXIS > 3 - DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | st.dir_outbits[3]; + DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | st.dir_outbits[3]; #endif #if N_AXIS > 4 - DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; + DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; #endif #else DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK); @@ -433,7 +433,7 @@ ISR(TIMER1_COMPA_vect) STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; #endif #endif - #else + #else #ifdef STEP_PULSE_DELAY st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting. #else // Normal operation @@ -473,9 +473,9 @@ ISR(TIMER1_COMPA_vect) // Initialize Bresenham line and distance counters #if N_AXIS == 4 - st.counter_x = st.counter_y = st.counter_z = st.counter_a = (st.exec_block->step_event_count >> 1); + st.counter_x = st.counter_y = st.counter_z = st.counter_4 = (st.exec_block->step_event_count >> 1); #elif N_AXIS == 5 - st.counter_x = st.counter_y = st.counter_z = st.counter_a = st.counter_b = (st.exec_block->step_event_count >> 1); + st.counter_x = st.counter_y = st.counter_z = st.counter_4 = st.counter_5 = (st.exec_block->step_event_count >> 1); #else st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); #endif @@ -493,10 +493,10 @@ ISR(TIMER1_COMPA_vect) st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level; st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level; #if N_AXIS > 3 - st.steps[A_AXIS] = st.exec_block->steps[A_AXIS] >> st.exec_segment->amass_level; + st.steps[AXIS_4] = st.exec_block->steps[AXIS_4] >> st.exec_segment->amass_level; #endif #if N_AXIS > 4 - st.steps[B_AXIS] = st.exec_block->steps[B_AXIS] >> st.exec_segment->amass_level; + st.steps[AXIS_5] = st.exec_block->steps[AXIS_5] >> st.exec_segment->amass_level; #endif #endif @@ -589,31 +589,31 @@ ISR(TIMER1_COMPA_vect) #endif // Ramps Board #if N_AXIS > 3 #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING - st.counter_a += st.steps[A_AXIS]; + st.counter_4 += st.steps[AXIS_4]; #else - st.counter_a += st.exec_block->steps[A_AXIS]; + st.counter_4 += st.exec_block->steps[AXIS_4]; #endif #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_a > st.exec_block->step_event_count) { - st.step_outbits[A_AXIS] |= (1<step_event_count; - if (st.exec_block->direction_bits[A_AXIS] & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_4] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_4] & (1< 3 #if N_AXIS > 4 #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING - st.counter_b += st.steps[B_AXIS]; + st.counter_5 += st.steps[AXIS_5]; #else - st.counter_b += st.exec_block->steps[B_AXIS]; + st.counter_5 += st.exec_block->steps[AXIS_5]; #endif #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_b > st.exec_block->step_event_count) { - st.step_outbits[B_AXIS] |= (1<step_event_count; - if (st.exec_block->direction_bits[B_AXIS] & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_5] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_5] & (1< 4 @@ -740,13 +740,13 @@ void st_reset() for (idx=0; idx 3 @@ -781,7 +781,7 @@ void stepper_init() #if N_AXIS > 4 STEP_DDR(4) |= 1< 4 STEPPER_DISABLE_DDR(4) |= 1<entry_speed_sqr); } - + // Setup laser mode variables. PWM rate adjusted motions will always complete a motion with the - // spindle off. + // spindle off. st_prep_block->is_pwm_rate_adjusted = false; if (settings.flags & BITFLAG_LASER_MODE) { - if (pl_block->condition & PL_COND_FLAG_SPINDLE_CCW) { + if (pl_block->condition & PL_COND_FLAG_SPINDLE_CCW) { // Pre-compute inverse programmed rate to speed up PWM updating per step segment. prep.inv_rate = 1.0/pl_block->programmed_rate; - st_prep_block->is_pwm_rate_adjusted = true; + st_prep_block->is_pwm_rate_adjusted = true; } } } - /* --------------------------------------------------------------------------------- - Compute the velocity profile of a new planner block based on its entry and exit - speeds, or recompute the profile of a partially-completed planner block if the - planner has updated it. For a commanded forced-deceleration, such as from a feed - hold, override the planner velocities and decelerate to the target exit speed. - */ - prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block. - float inv_2_accel = 0.5/pl_block->acceleration; - if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { // [Forced Deceleration to Zero Velocity] - // Compute velocity profile parameters for a feed hold in-progress. This profile overrides - // the planner block profile, enforcing a deceleration to zero speed. - prep.ramp_type = RAMP_DECEL; - // Compute decelerate distance relative to end of block. - float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr; - if (decel_dist < 0.0) { - // Deceleration through entire planner block. End of feed hold is not in this block. - prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters); - } else { - prep.mm_complete = decel_dist; // End of feed hold. - prep.exit_speed = 0.0; - } - } else { // [Normal Operation] - // Compute or recompute velocity profile parameters of the prepped planner block. - prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp. - prep.accelerate_until = pl_block->millimeters; - - float exit_speed_sqr; - float nominal_speed; + /* --------------------------------------------------------------------------------- + Compute the velocity profile of a new planner block based on its entry and exit + speeds, or recompute the profile of a partially-completed planner block if the + planner has updated it. For a commanded forced-deceleration, such as from a feed + hold, override the planner velocities and decelerate to the target exit speed. + */ + prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block. + float inv_2_accel = 0.5/pl_block->acceleration; + if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { // [Forced Deceleration to Zero Velocity] + // Compute velocity profile parameters for a feed hold in-progress. This profile overrides + // the planner block profile, enforcing a deceleration to zero speed. + prep.ramp_type = RAMP_DECEL; + // Compute decelerate distance relative to end of block. + float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr; + if (decel_dist < 0.0) { + // Deceleration through entire planner block. End of feed hold is not in this block. + prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters); + } else { + prep.mm_complete = decel_dist; // End of feed hold. + prep.exit_speed = 0.0; + } + } else { // [Normal Operation] + // Compute or recompute velocity profile parameters of the prepped planner block. + prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp. + prep.accelerate_until = pl_block->millimeters; + + float exit_speed_sqr; + float nominal_speed; if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) { prep.exit_speed = exit_speed_sqr = 0.0; // Enforce stop at end of system motion. } else { @@ -1014,9 +1014,9 @@ void st_prep_buffer() } nominal_speed = plan_compute_profile_nominal_speed(pl_block); - float nominal_speed_sqr = nominal_speed*nominal_speed; - float intersect_distance = - 0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr)); + float nominal_speed_sqr = nominal_speed*nominal_speed; + float intersect_distance = + 0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr)); if (pl_block->entry_speed_sqr > nominal_speed_sqr) { // Only occurs during override reductions. prep.accelerate_until = pl_block->millimeters - inv_2_accel*(pl_block->entry_speed_sqr-nominal_speed_sqr); @@ -1039,39 +1039,39 @@ void st_prep_buffer() prep.maximum_speed = nominal_speed; prep.ramp_type = RAMP_DECEL_OVERRIDE; } - } else if (intersect_distance > 0.0) { - if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types - // NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0. - prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr); - if (prep.decelerate_after < intersect_distance) { // Trapezoid type - prep.maximum_speed = nominal_speed; - if (pl_block->entry_speed_sqr == nominal_speed_sqr) { - // Cruise-deceleration or cruise-only type. - prep.ramp_type = RAMP_CRUISE; - } else { - // Full-trapezoid or acceleration-cruise types - prep.accelerate_until -= inv_2_accel*(nominal_speed_sqr-pl_block->entry_speed_sqr); - } - } else { // Triangle type - prep.accelerate_until = intersect_distance; - prep.decelerate_after = intersect_distance; - prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr); - } - } else { // Deceleration-only type + } else if (intersect_distance > 0.0) { + if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types + // NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0. + prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr); + if (prep.decelerate_after < intersect_distance) { // Trapezoid type + prep.maximum_speed = nominal_speed; + if (pl_block->entry_speed_sqr == nominal_speed_sqr) { + // Cruise-deceleration or cruise-only type. + prep.ramp_type = RAMP_CRUISE; + } else { + // Full-trapezoid or acceleration-cruise types + prep.accelerate_until -= inv_2_accel*(nominal_speed_sqr-pl_block->entry_speed_sqr); + } + } else { // Triangle type + prep.accelerate_until = intersect_distance; + prep.decelerate_after = intersect_distance; + prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr); + } + } else { // Deceleration-only type prep.ramp_type = RAMP_DECEL; // prep.decelerate_after = pl_block->millimeters; // prep.maximum_speed = prep.current_speed; - } - } else { // Acceleration-only type - prep.accelerate_until = 0.0; - // prep.decelerate_after = 0.0; - prep.maximum_speed = prep.exit_speed; - } - } - + } + } else { // Acceleration-only type + prep.accelerate_until = 0.0; + // prep.decelerate_after = 0.0; + prep.maximum_speed = prep.exit_speed; + } + } + bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); // Force update whenever updating block. } - + // Initialize new segment segment_t *prep_segment = &segment_buffer[segment_buffer_head]; @@ -1180,16 +1180,16 @@ void st_prep_buffer() /* ----------------------------------------------------------------------------------- Compute spindle speed PWM output for step segment */ - + if (st_prep_block->is_pwm_rate_adjusted || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) { if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) { float rpm = pl_block->spindle_speed; - // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate. + // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate. if (st_prep_block->is_pwm_rate_adjusted) { rpm *= (prep.current_speed * prep.inv_rate); } // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE) // but this would be instantaneous only and during a motion. May not matter at all. prep.current_spindle_pwm = spindle_compute_pwm_value(rpm); - } else { + } else { sys.spindle_speed = 0.0; prep.current_spindle_pwm = SPINDLE_PWM_OFF_VALUE; } @@ -1197,7 +1197,7 @@ void st_prep_buffer() } prep_segment->spindle_pwm = prep.current_spindle_pwm; // Reload segment PWM value - + /* ----------------------------------------------------------------------------------- Compute segment step rate, steps to execute, and apply necessary rate corrections. NOTE: Steps are computed by direct scalar conversion of the millimeter distance diff --git a/grbl/system.c b/grbl/system.c index 34d9a34b0..f676d9909 100755 --- a/grbl/system.c +++ b/grbl/system.c @@ -68,10 +68,10 @@ ISR(CONTROL_INT_vect) } else if (bit_istrue(pin,CONTROL_PIN_INDEX_CYCLE_START)) { bit_true(sys_rt_exec_state, EXEC_CYCLE_START); } else if (bit_istrue(pin,CONTROL_PIN_INDEX_FEED_HOLD)) { - bit_true(sys_rt_exec_state, EXEC_FEED_HOLD); + bit_true(sys_rt_exec_state, EXEC_FEED_HOLD); } else if (bit_istrue(pin,CONTROL_PIN_INDEX_SAFETY_DOOR)) { bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR); - } + } } } @@ -178,10 +178,10 @@ uint8_t system_execute_line(char *line) case 'Y': mc_homing_cycle(HOMING_CYCLE_Y); break; case 'Z': mc_homing_cycle(HOMING_CYCLE_Z); break; #if N_AXIS > 3 - case 'A': mc_homing_cycle(HOMING_CYCLE_A); break; + case AXIS_4_NAME: mc_homing_cycle(HOMING_CYCLE_4); break; #endif #if N_AXIS > 4 - case 'B': mc_homing_cycle(HOMING_CYCLE_B); break; + case AXIS_5_NAME: mc_homing_cycle(HOMING_CYCLE_5); break; #endif default: return(STATUS_INVALID_STATEMENT); } From 8fff54e98ba2af45cfa6a80eef6904f180a5e3cb Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 26 Apr 2018 11:29:49 +0200 Subject: [PATCH 004/101] Corrected bug on REPORT_FIELD_PIN_STATE for axis number 5 --- grbl/report.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grbl/report.c b/grbl/report.c index 6b7e30596..be6dd67b6 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -558,7 +558,7 @@ void report_realtime_status() if (bit_istrue(lim_pin_state,bit(AXIS_4))) { serial_write(AXIS_4_NAME); } #endif #if N_AXIS > 4 - if (bit_istrue(lim_pin_state,bit(AXIS_4))) { serial_write(AXIS_5_NAME); } + if (bit_istrue(lim_pin_state,bit(AXIS_5))) { serial_write(AXIS_5_NAME); } #endif } if (ctrl_pin_state) { From 87f726a34da35fa58fb5cee7eecb7f8a76c39fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sat, 28 Apr 2018 08:47:48 +0200 Subject: [PATCH 005/101] Update settings.md --- doc/markdown/settings.md | 76 +++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/doc/markdown/settings.md b/doc/markdown/settings.md index 582729d96..1c5c26856 100755 --- a/doc/markdown/settings.md +++ b/doc/markdown/settings.md @@ -106,42 +106,40 @@ This setting inverts the step pulse signal. By default, a step signal starts at This invert mask setting is a value which stores the axes to invert as bit flags. You really don't need to completely understand how it works. You simply need to enter the settings value for the axes you want to invert. For example, if you want to invert the X and Z axes, you'd send `$2=5` to Grbl and the setting should now read `$2=5 (step port invert mask:00000101)`. +| Setting Value | Mask | Invert X | Invert Y | Invert Z | Invert A | Invert B | |:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| -| Setting Value | Mask | Invert X | Invert Y | Invert Z | Invert A | Invert B | -|:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| -| 0 | 00000000 | N | N | N | N | N | -| 1 | 00000001 | Y | N | N | N | N | -| 2 | 00000010 | N | Y | N | N | N | -| 3 | 00000011 | Y | Y | N | N | N | -| 4 | 00000100 | N | N | Y | N | N | -| 5 | 00000101 | Y | N | Y | N | N | -| 6 | 00000110 | N | Y | Y | N | N | -| 7 | 00000111 | Y | Y | Y | N | N | -| 8 | 00001000 | N | N | N | Y | N | -| 9 | 00001001 | Y | N | N | Y | N | -| 10 | 00001010 | N | Y | N | Y | N | -| 11 | 00001011 | Y | Y | N | Y | N | -| 12 | 00001100 | N | N | Y | Y | N | -| 13 | 00001101 | Y | N | Y | Y | N | -| 14 | 00001110 | N | Y | Y | Y | N | -| 15 | 00001111 | Y | Y | Y | Y | N | -| 16 | 00010000 | N | N | N | N | Y | -| 17 | 00010001 | Y | N | N | N | Y | -| 18 | 00010010 | N | Y | N | N | Y | -| 19 | 00010011 | Y | Y | N | N | Y | -| 20 | 00010100 | N | N | Y | N | Y | -| 21 | 00010101 | Y | N | Y | N | Y | -| 22 | 00010110 | N | Y | Y | N | Y | -| 23 | 00010111 | Y | Y | Y | N | Y | -| 24 | 00011000 | N | N | N | Y | Y | -| 25 | 00011001 | Y | N | N | Y | Y | -| 26 | 00011010 | N | Y | N | Y | Y | -| 27 | 00011011 | Y | Y | N | Y | Y | -| 28 | 00011100 | N | N | Y | Y | Y | -| 29 | 00011101 | Y | N | Y | Y | Y | -| 30 | 00011110 | N | Y | Y | Y | Y | -| 31 | 00011111 | Y | Y | Y | Y | Y | -|:-------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| +| 0 | 00000000 | N | N | N | N | N | +| 1 | 00000001 | Y | N | N | N | N | +| 2 | 00000010 | N | Y | N | N | N | +| 3 | 00000011 | Y | Y | N | N | N | +| 4 | 00000100 | N | N | Y | N | N | +| 5 | 00000101 | Y | N | Y | N | N | +| 6 | 00000110 | N | Y | Y | N | N | +| 7 | 00000111 | Y | Y | Y | N | N | +| 8 | 00001000 | N | N | N | Y | N | +| 9 | 00001001 | Y | N | N | Y | N | +| 10 | 00001010 | N | Y | N | Y | N | +| 11 | 00001011 | Y | Y | N | Y | N | +| 12 | 00001100 | N | N | Y | Y | N | +| 13 | 00001101 | Y | N | Y | Y | N | +| 14 | 00001110 | N | Y | Y | Y | N | +| 15 | 00001111 | Y | Y | Y | Y | N | +| 16 | 00010000 | N | N | N | N | Y | +| 17 | 00010001 | Y | N | N | N | Y | +| 18 | 00010010 | N | Y | N | N | Y | +| 19 | 00010011 | Y | Y | N | N | Y | +| 20 | 00010100 | N | N | Y | N | Y | +| 21 | 00010101 | Y | N | Y | N | Y | +| 22 | 00010110 | N | Y | Y | N | Y | +| 23 | 00010111 | Y | Y | Y | N | Y | +| 24 | 00011000 | N | N | N | Y | Y | +| 25 | 00011001 | Y | N | N | Y | Y | +| 26 | 00011010 | N | Y | N | Y | Y | +| 27 | 00011011 | Y | Y | N | Y | Y | +| 28 | 00011100 | N | N | Y | Y | Y | +| 29 | 00011101 | Y | N | Y | Y | Y | +| 30 | 00011110 | N | Y | Y | Y | Y | +| 31 | 00011111 | Y | Y | Y | Y | Y | #### $3 – Direction port invert, mask @@ -179,12 +177,10 @@ To keep things simple and consistent, Grbl v1.1 has only two reporting options. Use the table below enables and disable reporting options. Simply add the values listed of what you'd like to enable, then save it by sending Grbl your setting value. For example, the default report with machine position and no buffer data reports setting is `$10=1`. If work position and buffer data are desired, the setting will be `$10=2`. +| Report Type | Value | Description | |:-------------:|:-----:|:-------------------------------------------------------------------------:| -| Report Type | Value | Description | -|:-------------:|:-----:|:-------------------------------------------------------------------------:| -| Position Type | 1 | Enabled `MPos:`. Disabled `WPos:`. | -| Buffer Data | 2 | Enabled `Buf:` field appears with planner and serial RX available buffer. | -|:-------------:|:-----:|:-------------------------------------------------------------------------:| +| Position Type | 1 | Enabled `MPos:`. Disabled `WPos:`. | +| Buffer Data | 2 | Enabled `Buf:` field appears with planner and serial RX available buffer. | #### $11 - Junction deviation, mm From 339e91a8e7da2ff0675e3bd91b9b28f35c333da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sat, 28 Apr 2018 08:57:34 +0200 Subject: [PATCH 006/101] Update interface.md --- doc/markdown/interface.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md index c454bed81..215c90027 100755 --- a/doc/markdown/interface.md +++ b/doc/markdown/interface.md @@ -136,7 +136,7 @@ Every G-code block sent to Grbl and Grbl `$` system command that is terminated w * **`error:X`**: Something went wrong! Grbl did not recognize the command and did not execute anything inside that message. The `X` is given as a numeric error code to tell you exactly what happened. The table below decribes every one of them. - | ID | Error Code Description | +| ID | Error Code Description | |:-------------:|----| | **`1`** | G-code words consist of a letter and a value. Letter was not found. | | **`2`** | Numeric value format is not valid or missing an expected value. | @@ -286,7 +286,7 @@ $134=360.000 ok ``` - | `$x` Code | Setting Description, Units | +| `$x` Code | Setting Description, Units | |:-------------:|----| | **`0`** | Step pulse time, microseconds | | **`1`** | Step idle delay, milliseconds | @@ -433,7 +433,7 @@ Feedback messages provide non-critical information on what Grbl is doing, what i - A string may appear after the second `:` colon. It is a stored EEPROM string a user via a `$I=line` command or OEM can place there for personal use or tracking purposes. - The `[OPT:]` line follows immediately after and contains character codes for compile-time options that were either enabled or disabled and two values separated by commas, which indicates the total usable planner blocks and serial RX buffer bytes, respectively. The codes are defined below and a CSV file is also provided for quick parsing. This is generally only used for quickly diagnosing firmware bugs or compatibility issues. - | `OPT` Code | Setting Description, Units | +| `OPT` Code | Setting Description, Units | |:-------------:|----| | **`V`** | Variable spindle enabled | | **`N`** | Line numbers enabled | From 9c55dbc528b317b0912242bc9a77af642addb82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Wed, 2 May 2018 06:53:35 +0200 Subject: [PATCH 007/101] Update README.md --- README.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index aa6a772b3..6d9678598 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ![GitHub Logo](https://github.com/gnea/gnea-Media/blob/master/Grbl%20Logo/Grbl%20Logo%20250px.png?raw=true) -*** -_Click the `Release` tab to download pre-compiled `.hex` files or just [click here](https://github.com/gnea/grbl-Mega/releases)_ + *** Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. This version of Grbl runs on an Arduino Mega2560 only. @@ -11,19 +10,13 @@ It accepts standards-compliant g-code and has been tested with the output of sev Grbl includes full acceleration management with look ahead. That means the controller will look up to 24 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. -* [Licensing](https://github.com/gnea/grbl/wiki/Licensing): Grbl is free software, released under the GPLv3 license. +* [Licensing](https://github.com/fra589/grbl-Mega-5X/blob/edge/COPYING): Grbl is free software, released under the GPLv3 license. * For more information and help, check out our **[Wiki pages!](https://github.com/gnea/grbl/wiki)** If you find that the information is out-dated, please to help us keep it updated by editing it or notifying our community! Thanks! -* Lead Developer: Sungeun "Sonny" Jeon, Ph.D. (USA) aka @chamnit - -* Built on the wonderful Grbl v0.6 (2011) firmware written by Simen Svale Skogsrud (Norway). - -*** - -### Official Supporters of the Grbl CNC Project -![Official Supporters](https://github.com/gnea/gnea-Media/blob/master/Contributors.png?raw=true) +* Lead Developer: Gauthier Brière (France) aka @fra589 +* Built on the wonderful Grbl v1.1f (2017) firmware originally written by Simen Svale Skogsrud (Norway) and maintained by Sungeun "Sonny" Jeon, Ph.D. (USA) aka @chamnit *** @@ -58,8 +51,6 @@ Grbl includes full acceleration management with look ahead. That means the contr - Lots of minor bug fixes and refactoring to make the code more efficient and flexible. - - ``` List of Supported G-Codes in Grbl v1.1: - Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G92, G92.1 @@ -76,10 +67,17 @@ List of Supported G-Codes in Grbl v1.1: - Program Flow: M0, M1, M2, M30* - Coolant Control: M7*, M8, M9 - Spindle Control: M3, M4, M5 - - Valid Non-Command Words: F, I, J, K, L, N, P, R, S, T, X, Y, Z + - Valid Non-Command Words: F, I, J, K, L, N, P, R, S, T, X, Y, Z, A, B, C ``` ------------- -Grbl is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! +Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! + +
+ + + + +
-[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CUGXJHXA36BYW) From 08a2ab6a4e30596b5782825943382dfa308dcb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Wed, 2 May 2018 06:56:13 +0200 Subject: [PATCH 008/101] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6d9678598..21e25f4fd 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! +[![Donate] +
- - - - -
+[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CZZN52UPPVHCW) From 929cae61cba67dec7e8efa00b8d053ce6640e0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Wed, 2 May 2018 07:11:49 +0200 Subject: [PATCH 010/101] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74a978211..16c888f95 100644 --- a/README.md +++ b/README.md @@ -73,5 +73,5 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CZZN52UPPVHCW) +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/pools/c/842hNSm2It) From 7ac6c92921c59d058fdddcae6b2ff47b5010c74a Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 2 May 2018 18:40:21 +0200 Subject: [PATCH 011/101] ENABLE_SAFETY_DOOR_INPUT_PIN in config.h and add doc/images/grbl-Mega-5X_Wiring.svg --- doc/images/grbl-Mega-5X_Wiring.svg | 2616 ++++++++++++++++++++++++++++ grbl/config.h | 2 +- 2 files changed, 2617 insertions(+), 1 deletion(-) create mode 100644 doc/images/grbl-Mega-5X_Wiring.svg diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg new file mode 100644 index 000000000..d4c198c6e --- /dev/null +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -0,0 +1,2616 @@ + + + + + grbl-Mega-5X pinout + + + + + + + + image/svg+xml + + grbl-Mega-5X pinout + + 2018-05-02 + + + Gauthie Brière + + + + + + + + + + + + + + + + + + + X motor + Y motor + 1 or 2 Z motor(s) + + + + + D10 + D09 + D08 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + axe 4 motor + axe 5 motor + + + + + + + X min end stop (D3) + X max end stop (D2) + Y min end stop (D14) + Y max end stop (D15) + Z min end stop (D18) + Z max end stop (D19) + Aux-2 + Aux-1 + Aux-3 + Servos + + + + + Axe 5 max end stop (D59) + Axe 4 max end stop (D40) + Axe 4 min end stop (D42) + Axe 5 min end stop (D59) + Spindle enable (D4) + + + Spindle direction (D5) + + + Coolant flood + Coolant mist + + + + + + + + + + + + + + + + + + + + + + Control feed hold (A10) + + Control reset (A9) + + Control cycle start (A11) + + Control safety door (A12) + + + Spindle PWM + Probe switch (A15) + + grbl-Mega-5X pinout + Copyright (C) Gauthier Brière - 2018 - Licence Creative Commons (BY-NC-ND) + + diff --git a/grbl/config.h b/grbl/config.h index 3d5f848de..b1bf87dac 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -187,7 +187,7 @@ // immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until // the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the // previous tool path, as if nothing happened. -// #define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable. +#define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable. // After the safety door switch has been toggled and restored, this setting sets the power-up delay // between restoring the spindle and coolant and resuming the cycle. From ae52d813aee64afdc16193efc37ef0313ceb15a3 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 2 May 2018 18:46:28 +0200 Subject: [PATCH 012/101] Some labels corrections --- doc/images/grbl-Mega-5X_Wiring.svg | 4884 ++++++++++++++-------------- 1 file changed, 2444 insertions(+), 2440 deletions(-) diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg index d4c198c6e..c92b1297d 100644 --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -29,7 +29,7 @@ inkscape:pageshadow="2" inkscape:zoom="0.812" inkscape:cx="500" - inkscape:cy="500" + inkscape:cy="499.99993" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -87,2510 +87,2514 @@ height="1000" x="0" y="91.250053" /> + + X motor + Y motor + 1 or 2 Z motor(s) + + + + + D10 + D09 + D08 - X motor - Y motor - 1 or 2 Z motor(s) - - + y="525.24994" + x="945.5" + height="7" + width="7" + id="rect953" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + y="525.25" + x="961.16669" + height="7" + width="7" + id="rect953-3" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - D10 - D09 - D08 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + y="525.24994" + x="976.83331" + height="7" + width="7" + id="rect953-9" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + id="g1202"> - - - - - - - - - - - - - - - - - - - - + id="g1143" + transform="translate(9.666687,117.00001)"> + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + id="g1143-7" + transform="translate(9.666687,196.00001)"> + + + + + + + + + + + - + id="g1202-2"> + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + id="g1143-7-8" + transform="translate(9.666687,196.00001)"> + + + + + + + + + + + - + id="g1202-27"> - - - - - - - - - - - - - - - - - - - - + id="g1143-66" + transform="translate(9.666687,117.00001)"> + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + id="g1143-7-6" + transform="translate(9.666687,196.00001)"> + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="g1202-4"> + + + + + + + + + + + + + + + + + + + + + + + - + id="g1202-22"> - - - - - - - - - - + id="g1143-2" + transform="translate(9.666687,117.00001)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + transform="translate(0,2.50001)" + id="g1739"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + id="g2207"> - - - + + + + + + + - + id="g2210-4"> - - - - - - - - - - + style="fill:#7f7f7f;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect2181-6" + width="45" + height="44.999985" + x="900" + y="586.25" /> - - - - - - - - - - + id="g2207-1"> + + + + + + + + + + - + id="g2210-7"> + height="44.999985" + x="900" + y="586.25" /> - - - - - - - - - - - - - - - - - - + id="g2207-0"> + + + + + + + + + + - - - - + id="g2210-46"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="fill:#7f7f7f;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect2181-99" + width="45" + height="44.999985" + x="900" + y="586.25" /> - - - - - - - - - - - - - + id="g2207-7"> + + + + + + + + - - - - - - axe 4 motor - axe 5 motor - - - - - - - X min end stop (D3) - X max end stop (D2) - Y min end stop (D14) - Y max end stop (D15) - Z min end stop (D18) - Z max end stop (D19) - Aux-2 - Aux-1 - Aux-3 - Servos - - - - - Axe 5 max end stop (D59) - Axe 4 max end stop (D40) - Axe 4 min end stop (D42) - Axe 5 min end stop (D59) - Spindle enable (D4) - - - Spindle direction (D5) - - - Coolant flood - Coolant mist + + - - - - + id="g2210-5"> - - - + style="fill:#7f7f7f;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect2181-2" + width="45" + height="44.999985" + x="900" + y="586.25" /> - - - - - - - - + id="g2207-8"> + + + + + + + + - - Control feed hold (A10) - - Control reset (A9) - - Control cycle start (A11) - - Control safety door (A12) - - - Spindle PWM - Probe switch (A15) + + + + + + axe 4 motor + axe 5 motor + + + + + + + X min end stop (D3) + X max end stop (D2) + Y min end stop (D14) + Y max end stop (D15) + Z min end stop (D18) + Z max end stop (D19) + Aux-2 + Aux-1 + Aux-3 + Servos + + + + + Axe 5 max end stop (D59) + Axe 4 max end stop (D40) + Axe 4 min end stop (D42) + Axe 5 min end stop (D59) + Spindle enable (D4) + + + Spindle direction (D5) + + + Coolant flood + Coolant mist + + + + + + + + + + + + + + + + + + + + + + Feed hold switch (A10) + + Reset switch (A9) + + Cycle start switch (A11) + + Control safety door (A12) + + + Spindle PWM + Probe switch (A15) Date: Wed, 2 May 2018 19:16:59 +0200 Subject: [PATCH 013/101] Mistakes corrections :-/ --- doc/images/grbl-Mega-5X_Wiring.svg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg index c92b1297d..444ea609f 100644 --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -29,7 +29,7 @@ inkscape:pageshadow="2" inkscape:zoom="0.812" inkscape:cx="500" - inkscape:cy="499.99993" + inkscape:cy="500" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -2307,7 +2307,7 @@ id="tspan2437-27-5" x="463.14359" y="998.19922" - style="font-size:14.66666698px">Axe 5 min end stop (D59) + style="font-size:14.66666698px">Axe 5 min end stop (D44) Control safety door (A12) + style="font-size:14.66666698px">Safety door switch (A12) Date: Thu, 3 May 2018 18:20:05 +0200 Subject: [PATCH 014/101] add new grbl-Mega-5X logo --- doc/images/Mega-5X-logo.svg | 152 ++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 doc/images/Mega-5X-logo.svg diff --git a/doc/images/Mega-5X-logo.svg b/doc/images/Mega-5X-logo.svg new file mode 100644 index 000000000..e850c4f67 --- /dev/null +++ b/doc/images/Mega-5X-logo.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + From baebad51eb7582c7f2223f5c9ba4a0e8b9f709bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Thu, 3 May 2018 18:22:20 +0200 Subject: [PATCH 015/101] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16c888f95..620d2604d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![GitHub Logo](https://github.com/gnea/gnea-Media/blob/master/Grbl%20Logo/Grbl%20Logo%20250px.png?raw=true) +![GitHub Logo](https://github.com/fra589/grbl-Mega-5X/blob/edge/doc/images/Mega-5X-logo.svg) *** From adce7f3614fb9040c8d98e352fbdf29d8c90b56d Mon Sep 17 00:00:00 2001 From: "gauthier@snoopy" Date: Sun, 10 Jun 2018 18:19:16 +0200 Subject: [PATCH 016/101] =?UTF-8?q?Premiere=20version=206=20axes=20-=20non?= =?UTF-8?q?=20test=C3=A9e...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grbl/config.h | 7 +++++ grbl/cpu_map.h | 68 +++++++++++++++++++++++++++++++------------ grbl/defaults.h | 6 ++++ grbl/gcode.c | 9 ++++-- grbl/gcode.h | 7 +++-- grbl/limits.c | 29 ++++++++++++++++++ grbl/motion_control.c | 13 +++++++-- grbl/motion_control.h | 3 ++ grbl/nuts_bolts.h | 19 +++++++----- grbl/planner.c | 3 ++ grbl/report.c | 3 ++ grbl/settings.c | 18 ++++++++++++ grbl/stepper.c | 65 +++++++++++++++++++++++++++++++++++++++++ grbl/system.c | 3 ++ 14 files changed, 219 insertions(+), 34 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index b1bf87dac..a9448bedc 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -119,6 +119,13 @@ #define HOMING_CYCLE_2 (1<. */ -/* The cpu_map.h files serve as a central pin mapping selection file for different - processor types or alternative pin layouts. This version of Grbl supports only the +/* The cpu_map.h files serve as a central pin mapping selection file for different + processor types or alternative pin layouts. This version of Grbl supports only the Arduino Mega2560. */ #ifndef cpu_map_h @@ -57,7 +57,7 @@ #define STEPPERS_DISABLE_BIT 7 // MEGA2560 Digital Pin 13 #define STEPPERS_DISABLE_MASK (1< 4 #define STEP_PORT_4 C // Axis number 5 (Ramps E1) #endif + #if N_AXIS > 5 + #define STEP_PORT_5 L // Axis number 6 (Ramps Aux-3 D49) + #endif #define STEP_BIT_0 0 // X Step - Pin A0 #define STEP_BIT_1 6 // Y Step - Pin A6 #define STEP_BIT_2 3 // Z Step - Pin D46 @@ -168,6 +171,9 @@ #if N_AXIS > 4 #define STEP_BIT_4 1 // Axis number 5 Step - Pin D36 #endif + #if N_AXIS > 5 + #define STEP_BIT_5 0 // Axis number 6 Step - Pin D49 + #endif #define _STEP_BIT(i) STEP_BIT_##i #define STEP_BIT(i) _STEP_BIT(i) #define STEP_DDR(i) _DDR(STEP_PORT_##i) @@ -185,6 +191,9 @@ #if N_AXIS > 4 #define DIRECTION_PORT_4 C // Axis number 5 (Ramps E1) #endif + #if N_AXIS > 5 + #define DIRECTION_PORT_5 B // Axis number 6 (Ramps Aux-3 D51) + #endif #define DIRECTION_BIT_0 1 // X Dir - Pin A1 #define DIRECTION_BIT_1 7 // Y Dir - Pin A7 #define DIRECTION_BIT_2 1 // Z Dir - Pin D48 @@ -194,6 +203,9 @@ #if N_AXIS > 4 #define DIRECTION_BIT_4 3 // Axis number 5 Step - Pin D34 #endif + #if N_AXIS > 5 + #define DIRECTION_BIT_5 2 // Axis number 6 Step - Pin D51 + #endif #define _DIRECTION_BIT(i) DIRECTION_BIT_##i #define DIRECTION_BIT(i) _DIRECTION_BIT(i) #define DIRECTION_DDR(i) _DDR(DIRECTION_PORT_##i) @@ -211,6 +223,9 @@ #if N_AXIS > 4 #define STEPPER_DISABLE_PORT_4 C // Axis number 5 (Ramps E1) #endif + #if N_AXIS > 5 + #define STEPPER_DISABLE_PORT_5 B // Axis number 5 (Ramps Aux-3 D53) + #endif #define STEPPER_DISABLE_BIT_0 7 // X Enable - Pin D38 #define STEPPER_DISABLE_BIT_1 2 // Y Enable - Pin A2 #define STEPPER_DISABLE_BIT_2 0 // Z Enable - Pin A8 @@ -220,12 +235,15 @@ #if N_AXIS > 4 #define STEPPER_DISABLE_BIT_4 7 // Axis number 5 Step - Pin D30 #endif + #if N_AXIS > 5 + #define STEPPER_DISABLE_BIT_5 0 // Axis number 5 Step - Pin D53 + #endif #define STEPPER_DISABLE_BIT(i) STEPPER_DISABLE_BIT_##i #define STEPPER_DISABLE_DDR(i) _DDR(STEPPER_DISABLE_PORT_##i) #define STEPPER_DISABLE_PORT(i) _PORT(STEPPER_DISABLE_PORT_##i) #define STEPPER_DISABLE_PIN(i) _PIN(STEPPER_DISABLE_PORT_##i) - // Define homing/hard limit switch input pins and limit interrupt vectors. + // Define homing/hard limit switch input pins and limit interrupt vectors. #define MIN_LIMIT_PORT_0 E #define MIN_LIMIT_PORT_1 J #define MIN_LIMIT_PORT_2 D @@ -235,6 +253,9 @@ #if N_AXIS > 4 #define MIN_LIMIT_PORT_4 L #endif + #if N_AXIS > 5 + #define MIN_LIMIT_PORT_5 F // (Ramps Aux-1 D57) + #endif #define MIN_LIMIT_BIT_0 5 // X Limit Min - Pin D3 #define MIN_LIMIT_BIT_1 1 // Y Limit Min - Pin D14 #define MIN_LIMIT_BIT_2 3 // Z Limit Min - Pin D18 @@ -244,6 +265,9 @@ #if N_AXIS > 4 #define MIN_LIMIT_BIT_4 5 // Axis number 5 : RAMPS AUX2 pin D44 #endif + #if N_AXIS > 5 + #define MIN_LIMIT_BIT_5 3 // Axis number 6 : RAMPS AUX2 pin D57 + #endif #define _MIN_LIMIT_BIT(i) MIN_LIMIT_BIT_##i #define MIN_LIMIT_BIT(i) _MIN_LIMIT_BIT(i) #define MIN_LIMIT_DDR(i) _DDR(MIN_LIMIT_PORT_##i) @@ -259,6 +283,9 @@ #if N_AXIS > 4 #define MAX_LIMIT_PORT_4 F #endif + #if N_AXIS > 5 + #define MAX_LIMIT_PORT_5 F // (Ramps Aux-3 D58) + #endif #define MAX_LIMIT_BIT_0 4 // X Limit Max - Pin D2 #define MAX_LIMIT_BIT_1 0 // Y Limit Max - Pin D15 #define MAX_LIMIT_BIT_2 2 // Z Limit Max - Pin D19 @@ -268,6 +295,9 @@ #if N_AXIS > 4 #define MAX_LIMIT_BIT_4 5 // Axis number 5 : RAMPS AUX2 pin D59 #endif + #if N_AXIS > 5 + #define MAX_LIMIT_BIT_5 4 // Axis number 6 : RAMPS AUX2 pin D58 + #endif #define _MAX_LIMIT_BIT(i) MAX_LIMIT_BIT_##i #define MAX_LIMIT_BIT(i) _MAX_LIMIT_BIT(i) #define MAX_LIMIT_DDR(i) _DDR(MAX_LIMIT_PORT_##i) @@ -275,7 +305,7 @@ #define MAX_LIMIT_PIN(i) _PIN(MAX_LIMIT_PORT_##i) // #define LIMIT_INT PCIE0 // Pin change interrupt enable pin - // #define LIMIT_INT_vect PCINT0_vect + // #define LIMIT_INT_vect PCINT0_vect // #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register // #define LIMIT_MASK ((1< 5 + #define DEFAULT_C_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_C_MAX_RATE 1440 // °/mn + #define DEFAULT_C_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_C_MAX_TRAVEL 180.0 // ° + #endif #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 diff --git a/grbl/gcode.c b/grbl/gcode.c index 3045df363..90b8afad2 100755 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -289,7 +289,7 @@ uint8_t gc_execute_line(char *line) legal g-code words and stores their value. Error-checking is performed later since some words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ switch(letter){ - // case 'A', 'B' or 'C' depending of AXIS_4_NAME and AXIS_5_NAME supported in 4 or 5 axes mode + // case 'A', 'B' or 'C' depending of AXIS_4_NAME, AXIS_5_NAME and AXIS_6_NAME supported in 4, 5 or 6 axes mode #if N_AXIS > 3 #ifdef AXIS_4 case AXIS_4_NAME: word_bit = WORD_A; gc_block.values.xyz[AXIS_4] = value; axis_words |= (1< 3 - if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_A)|bit(WORD_B))); } // Remove axis words. + if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_A)|bit(WORD_B)|bit(WORD_C))); } // Remove axis words. #else if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words. #endif @@ -1144,7 +1147,7 @@ uint8_t gc_execute_line(char *line) - Canned cycles - Tool radius compensation - - A,B,C-axes // A & B Supported in Ramps 1.4 grbl-Mega-5X version if N_AXIS > 3 + - A,B,C-axes // A, B & C Supported in Ramps 1.4 grbl-Mega-5X version if N_AXIS > 3 - Evaluation of expressions - Variables - Override control (TBD) diff --git a/grbl/gcode.h b/grbl/gcode.h index 4803fd393..42af3c15e 100755 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -49,9 +49,9 @@ // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. -// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing +// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing // compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not -// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c +// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c // to see how they are used, if you need to alter them. // Modal Group G0: Non-modal actions @@ -153,6 +153,7 @@ #if N_AXIS > 3 #define WORD_A 13 #define WORD_B 14 + #define WORD_C 15 #endif // Define g-code parser position updating flags #define GC_UPDATE_POS_TARGET 0 // Must be zero @@ -165,7 +166,7 @@ #define GC_PROBE_FAIL_INIT GC_UPDATE_POS_NONE #define GC_PROBE_FAIL_END GC_UPDATE_POS_TARGET #ifdef SET_CHECK_MODE_PROBE_TO_START - #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE + #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE #else #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_TARGET #endif diff --git a/grbl/limits.c b/grbl/limits.c index 1f423047e..21ded11f0 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -44,6 +44,9 @@ void limits_init() #if N_AXIS > 4 MIN_LIMIT_DDR(4) &= ~(1< 5 + MIN_LIMIT_DDR(5) &= ~(1< 4 MAX_LIMIT_DDR(4) &= ~(1< 5 + MAX_LIMIT_DDR(5) &= ~(1< 4 MIN_LIMIT_PORT(4) &= ~(1< 5 + MIN_LIMIT_PORT(5) &= ~(1< 4 MAX_LIMIT_PORT(4) &= ~(1< 5 + MAX_LIMIT_PORT(5) &= ~(1< 4 MIN_LIMIT_PORT(4) |= (1< 5 + MIN_LIMIT_PORT(5) |= (1< 4 MAX_LIMIT_PORT(4) |= (1< 5 + MAX_LIMIT_PORT(5) |= (1< 4 else if (idx==AXIS_5) { axislock[idx] &= ~(step_pin[AXIS_5]); } #endif + #if N_AXIS > 5 + else if (idx==AXIS_6) { axislock[idx] &= ~(step_pin[AXIS_6]); } + #endif else { axislock[idx] &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else axislock[idx] &= ~(step_pin[idx]); @@ -513,6 +539,9 @@ void limits_go_home(uint8_t cycle_mask) #if N_AXIS > 4 else if (idx==AXIS_5) { axislock &= ~(step_pin[AXIS_5]); } #endif + #if N_AXIS > 5 + else if (idx==AXIS_6) { axislock &= ~(step_pin[AXIS_6]); } + #endif else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } #else axislock &= ~(step_pin[idx]); diff --git a/grbl/motion_control.c b/grbl/motion_control.c index 27982bfc9..c50dad19a 100755 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -127,6 +127,9 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of #if N_AXIS >4 float axis_b_per_segment = (target[AXIS_5] - position[AXIS_5])/segments; #endif + #if N_AXIS >5 + float axis_c_per_segment = (target[AXIS_6] - position[AXIS_6])/segments; + #endif /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. @@ -193,6 +196,9 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of #if N_AXIS > 4 position[AXIS_5] += axis_b_per_segment; #endif + #if N_AXIS > 5 + position[AXIS_6] += axis_c_per_segment; + #endif mc_line(position, pl_data); // Bail mid-circle on system abort. Runtime command check already performed by mc_line. @@ -249,10 +255,13 @@ void mc_homing_cycle(uint8_t cycle_mask) #endif #if N_AXIS > 3 #ifdef HOMING_CYCLE_3 - limits_go_home(HOMING_CYCLE_3); // Homing cycle 2 + limits_go_home(HOMING_CYCLE_3); // Homing cycle 3 #endif #ifdef HOMING_CYCLE_4 - limits_go_home(HOMING_CYCLE_4); // Homing cycle 2 + limits_go_home(HOMING_CYCLE_4); // Homing cycle 4 + #endif + #ifdef HOMING_CYCLE_5 + limits_go_home(HOMING_CYCLE_5); // Homing cycle 5 #endif #endif } diff --git a/grbl/motion_control.h b/grbl/motion_control.h index 1c6d368c3..62635d904 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -38,6 +38,9 @@ #if N_AXIS > 4 #define HOMING_CYCLE_5 bit(AXIS_5) #endif +#if N_AXIS > 5 + #define HOMING_CYCLE_6 bit(AXIS_6) +#endif // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index 8bbc7b1d3..91255c0dd 100755 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -30,17 +30,19 @@ // Axis array index values. Must start with 0 and be continuous. #ifdef DEFAULTS_RAMPS_BOARD - // 5 axis support only for RAMPS 1.4 (for the moment :-)...) - // TODO: 5 (or 6) axis support for other hardwares. - #define N_AXIS 5 // Number of axes - #define AXIS_NAMES "XYZAB" // Letters (names) of axis - #define N_AXIS_LINEAR 3 // Number of linears axis + // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) + #define N_AXIS 6 // Number of axes + #define AXIS_NAMES "XYZABC" // Letters (names) of axis + #define N_AXIS_LINEAR 3 // Number of linears axis #else #define N_AXIS 3 // Number of axes #endif #define X_AXIS 0 // Axis indexing value. #define Y_AXIS 1 #define Z_AXIS 2 +#if N_AXIS <3 + #error "N_AXIS must be >= 3. N_AXIS < 3 is not implemented." +#endif #if N_AXIS > 3 #define AXIS_4 3 #define AXIS_4_NAME 'A' // Letter of axis number 4 @@ -50,8 +52,11 @@ #define AXIS_5_NAME 'B' // Letter of axis number 5 #endif #if N_AXIS > 5 - #error "N_AXIS must be <= 5. N_AXIS > 5 is not implemented." - // TODO: 6 axis support for other hardwares.#endif + #define AXIS_6 5 + #define AXIS_6_NAME 'C' // Letter of axis number 6 +#endif +#if N_AXIS > 6 + #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif // CoreXY motor assignments. DO NOT ALTER. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. diff --git a/grbl/planner.c b/grbl/planner.c index 644929008..76ad71562 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -339,6 +339,9 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) #if N_AXIS > 4 position_steps[AXIS_5] = sys_position[AXIS_5]; #endif + #if N_AXIS > 5 + position_steps[AXIS_6] = sys_position[AXIS_6]; + #endif #else memcpy(position_steps, sys_position, sizeof(sys_position)); #endif diff --git a/grbl/report.c b/grbl/report.c index be6dd67b6..652fda528 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -560,6 +560,9 @@ void report_realtime_status() #if N_AXIS > 4 if (bit_istrue(lim_pin_state,bit(AXIS_5))) { serial_write(AXIS_5_NAME); } #endif + #if N_AXIS > 5 + if (bit_istrue(lim_pin_state,bit(AXIS_6))) { serial_write(AXIS_6_NAME); } + #endif } if (ctrl_pin_state) { #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN diff --git a/grbl/settings.c b/grbl/settings.c index 6a5713b9c..20608ce56 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -119,6 +119,12 @@ void settings_restore(uint8_t restore_flag) { settings.acceleration[AXIS_5] = DEFAULT_B_ACCELERATION; settings.max_travel[AXIS_5] = (-DEFAULT_B_MAX_TRAVEL); #endif + #if N_AXIS > 5 + settings.steps_per_mm[AXIS_6] = DEFAULT_C_STEPS_PER_UNIT; + settings.max_rate[AXIS_6] = DEFAULT_C_MAX_RATE; + settings.acceleration[AXIS_6] = DEFAULT_C_ACCELERATION; + settings.max_travel[AXIS_6] = (-DEFAULT_C_MAX_TRAVEL); + #endif write_global_settings(); } @@ -337,6 +343,9 @@ uint8_t get_step_pin_mask(uint8_t axis_idx) #if N_AXIS > 4 if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 4 if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 4 if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 4 if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 4 STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); + #endif } else { STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); @@ -265,6 +273,9 @@ void st_wake_up() #if N_AXIS > 4 STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); + #endif } // Initialize stepper output bits to ensure first ISR call does not step. for (idx = 0; idx < N_AXIS; idx++) { @@ -321,6 +332,9 @@ void st_go_idle() #if N_AXIS > 4 STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); + #endif } else { STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); @@ -331,6 +345,9 @@ void st_go_idle() #if N_AXIS > 4 STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); + #endif } #else if (pin_state) { STEPPERS_DISABLE_PORT |= (1< 4 DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; #endif + #if N_AXIS > 5 + DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | st.dir_outbits[5]; + #endif #else DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK); #endif // Ramps Boafd @@ -422,6 +442,9 @@ ISR(TIMER1_COMPA_vect) #if N_AXIS > 4 st.step_bits[4] = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; // Store out_bits to prevent overwriting. #endif + #if N_AXIS > 5 + st.step_bits[5] = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; // Store out_bits to prevent overwriting. + #endif #else STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; @@ -432,6 +455,9 @@ ISR(TIMER1_COMPA_vect) #if N_AXIS > 4 STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; + #endif #endif #else #ifdef STEP_PULSE_DELAY @@ -476,6 +502,8 @@ ISR(TIMER1_COMPA_vect) st.counter_x = st.counter_y = st.counter_z = st.counter_4 = (st.exec_block->step_event_count >> 1); #elif N_AXIS == 5 st.counter_x = st.counter_y = st.counter_z = st.counter_4 = st.counter_5 = (st.exec_block->step_event_count >> 1); + #elif N_AXIS == 6 + st.counter_x = st.counter_y = st.counter_z = st.counter_4 = st.counter_5 = st.counter_6 = (st.exec_block->step_event_count >> 1); #else st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); #endif @@ -498,6 +526,9 @@ ISR(TIMER1_COMPA_vect) #if N_AXIS > 4 st.steps[AXIS_5] = st.exec_block->steps[AXIS_5] >> st.exec_segment->amass_level; #endif + #if N_AXIS > 5 + st.steps[AXIS_6] = st.exec_block->steps[AXIS_6] >> st.exec_segment->amass_level; + #endif #endif // Set real-time spindle output as segment is loaded, just prior to the first step. @@ -617,6 +648,21 @@ ISR(TIMER1_COMPA_vect) } #endif // Ramps Board #endif // N_AXIS > 4 + #if N_AXIS > 5 + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_6 += st.steps[AXIS_6]; + #else + st.counter_6 += st.exec_block->steps[AXIS_6]; + #endif + #ifdef DEFAULTS_RAMPS_BOARD + if (st.counter_6 > st.exec_block->step_event_count) { + st.step_outbits[AXIS_6] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_6] & (1< 5 // During a homing cycle, lock out and prevent desired axes from moving. #ifdef DEFAULTS_RAMPS_BOARD @@ -665,6 +711,9 @@ ISR(TIMER0_OVF_vect) #if N_AXIS > 4 STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; + #endif #else STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK); #endif // Ramps Board @@ -688,6 +737,9 @@ ISR(TIMER0_OVF_vect) #if N_AXIS > 4 STEP_PORT(4) = st.step_bits[4]; // Begin step pulse. #endif + #if N_AXIS > 5 + STEP_PORT(5) = st.step_bits[5]; // Begin step pulse. + #endif #else STEP_PORT = st.step_bits; // Begin step pulse. #endif // Ramps Board @@ -757,6 +809,10 @@ void st_reset() STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | dir_port_invert_mask[4]; #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; + DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | dir_port_invert_mask[5]; + #endif #else st.dir_outbits = dir_port_invert_mask; // Initialize direction bits to default. @@ -781,6 +837,9 @@ void stepper_init() #if N_AXIS > 4 STEP_DDR(4) |= 1< 5 + STEP_DDR(5) |= 1< 4 STEPPER_DISABLE_DDR(4) |= 1< 5 + STEPPER_DISABLE_DDR(5) |= 1< 4 DIRECTION_DDR(4) |= 1< 5 + DIRECTION_DDR(5) |= 1< 4 case AXIS_5_NAME: mc_homing_cycle(HOMING_CYCLE_5); break; + #endif + #if N_AXIS > 5 + case AXIS_6_NAME: mc_homing_cycle(HOMING_CYCLE_6); break; #endif default: return(STATUS_INVALID_STATEMENT); } From 1ea028693972a6871cf0a346c5bcb439498fd1c2 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 11 Jul 2018 10:03:28 +0200 Subject: [PATCH 017/101] Move N_AXIS definition in config.h to allow homing cycles definition for N_AXIS != 3 --- grbl/config.h | 31 +++++++++++++++++++++++++++++++ grbl/motion_control.h | 6 +++--- grbl/nuts_bolts.h | 30 ------------------------------ 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index a9448bedc..b1a929a60 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -42,6 +42,37 @@ #define DEFAULTS_RAMPS_BOARD #define CPU_MAP_2560_RAMPS_BOARD +// Axis array index values. Must start with 0 and be continuous. +#ifdef DEFAULTS_RAMPS_BOARD + // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) + #define N_AXIS 6 // Number of axes + #define AXIS_NAMES "XYZABC" // Letters (names) of axis + #define N_AXIS_LINEAR 3 // Number of linears axis +#else + #define N_AXIS 3 // Number of axes +#endif +#define X_AXIS 0 // Axis indexing value. +#define Y_AXIS 1 +#define Z_AXIS 2 +#if N_AXIS <3 + #error "N_AXIS must be >= 3. N_AXIS < 3 is not implemented." +#endif +#if N_AXIS > 3 + #define AXIS_4 3 + #define AXIS_4_NAME 'A' // Letter of axis number 4 +#endif +#if N_AXIS > 4 + #define AXIS_5 4 + #define AXIS_5_NAME 'B' // Letter of axis number 5 +#endif +#if N_AXIS > 5 + #define AXIS_6 5 + #define AXIS_6_NAME 'C' // Letter of axis number 6 +#endif +#if N_AXIS > 6 + #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." +#endif + // Serial baud rate // #define BAUD_RATE 230400 #define BAUD_RATE 115200 diff --git a/grbl/motion_control.h b/grbl/motion_control.h index 62635d904..c8e416eb4 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -33,13 +33,13 @@ #define HOMING_CYCLE_Y bit(Y_AXIS) #define HOMING_CYCLE_Z bit(Z_AXIS) #if N_AXIS > 3 - #define HOMING_CYCLE_4 bit(AXIS_4) + #define HOMING_CYCLE_A bit(AXIS_4) #endif #if N_AXIS > 4 - #define HOMING_CYCLE_5 bit(AXIS_5) + #define HOMING_CYCLE_B bit(AXIS_5) #endif #if N_AXIS > 5 - #define HOMING_CYCLE_6 bit(AXIS_6) + #define HOMING_CYCLE_C bit(AXIS_6) #endif // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index 91255c0dd..6e72eb7a5 100755 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -28,36 +28,6 @@ #define SOME_LARGE_VALUE 1.0E+38 -// Axis array index values. Must start with 0 and be continuous. -#ifdef DEFAULTS_RAMPS_BOARD - // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) - #define N_AXIS 6 // Number of axes - #define AXIS_NAMES "XYZABC" // Letters (names) of axis - #define N_AXIS_LINEAR 3 // Number of linears axis -#else - #define N_AXIS 3 // Number of axes -#endif -#define X_AXIS 0 // Axis indexing value. -#define Y_AXIS 1 -#define Z_AXIS 2 -#if N_AXIS <3 - #error "N_AXIS must be >= 3. N_AXIS < 3 is not implemented." -#endif -#if N_AXIS > 3 - #define AXIS_4 3 - #define AXIS_4_NAME 'A' // Letter of axis number 4 -#endif -#if N_AXIS > 4 - #define AXIS_5 4 - #define AXIS_5_NAME 'B' // Letter of axis number 5 -#endif -#if N_AXIS > 5 - #define AXIS_6 5 - #define AXIS_6_NAME 'C' // Letter of axis number 6 -#endif -#if N_AXIS > 6 - #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." -#endif // CoreXY motor assignments. DO NOT ALTER. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. #ifdef COREXY From d9549d500f1063562d366d0d94e884174e7a3334 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 11 Jul 2018 10:04:37 +0200 Subject: [PATCH 018/101] update GRBL_VERSION_BUILD --- grbl/grbl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index 595d264da..4f8ff2601 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1g" -#define GRBL_VERSION_BUILD "20180423" +#define GRBL_VERSION_BUILD "20180711" // Define standard libraries used by Grbl. #include From 77fb28d71867f592690d16a044cbcbff32a67c93 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 12 Jul 2018 13:56:54 +0200 Subject: [PATCH 019/101] Axis renaming and cloning. - Add the possibility of renaming axis independently of their positions, - This add the possibility of moving axis without modification of the cpu_map.h file, - Cloning axis is also possible by using the same letter name for more than one axis. --- grbl/config.h | 64 +++++++++++++++++++-------------- grbl/defaults.h | 14 ++++---- grbl/gcode.c | 82 ++++++++++++++++++++++++++++--------------- grbl/limits.c | 24 ++++++------- grbl/motion_control.h | 6 ++-- grbl/nuts_bolts.h | 4 +-- grbl/planner.c | 22 ++++++------ grbl/report.c | 25 ++++++++----- grbl/settings.c | 60 +++++++++++++++---------------- grbl/stepper.c | 48 ++++++++++++------------- grbl/system.c | 4 +-- 11 files changed, 199 insertions(+), 154 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index b1a929a60..223e1d80d 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -45,25 +45,33 @@ // Axis array index values. Must start with 0 and be continuous. #ifdef DEFAULTS_RAMPS_BOARD // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) - #define N_AXIS 6 // Number of axes - #define AXIS_NAMES "XYZABC" // Letters (names) of axis - #define N_AXIS_LINEAR 3 // Number of linears axis + #define N_AXIS 5 // Number of axes + #define N_AXIS_LINEAR 4 // Number of linears axis #else #define N_AXIS 3 // Number of axes #endif +/* #define X_AXIS 0 // Axis indexing value. #define Y_AXIS 1 #define Z_AXIS 2 +*/ +#define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. +#define AXIS_1_NAME 'X' +#define AXIS_2 1 +#define AXIS_2_NAME 'Y' +#define AXIS_3 2 +#define AXIS_3_NAME 'Z' + #if N_AXIS <3 #error "N_AXIS must be >= 3. N_AXIS < 3 is not implemented." #endif #if N_AXIS > 3 #define AXIS_4 3 - #define AXIS_4_NAME 'A' // Letter of axis number 4 + #define AXIS_4_NAME 'Y' // Letter of axis number 4 #endif #if N_AXIS > 4 #define AXIS_5 4 - #define AXIS_5_NAME 'B' // Letter of axis number 5 + #define AXIS_5_NAME 'A' // Letter of axis number 5 #endif #if N_AXIS > 5 #define AXIS_6 5 @@ -73,6 +81,8 @@ #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif +//#define AXIS_NAMES "XYZABC" // Letters (names) of axis + // Serial baud rate // #define BAUD_RATE 230400 #define BAUD_RATE 115200 @@ -141,38 +151,38 @@ #ifdef DEFAULTS_RAMPS_BOARD #if N_AXIS == 4 // 4 axis : homing #define HOMING_CYCLE_0 (1< 3 - #ifdef AXIS_4 - case AXIS_4_NAME: word_bit = WORD_A; gc_block.values.xyz[AXIS_4] = value; axis_words |= (1< MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } gc_block.values.t = int_value; break; - case 'X': word_bit = WORD_X; gc_block.values.xyz[X_AXIS] = value; axis_words |= (1< 3 else if (idx==AXIS_4) { axislock[idx] &= ~(step_pin[AXIS_4]); } #endif @@ -483,18 +483,18 @@ void limits_go_home(uint8_t cycle_mask) if (bit_istrue(cycle_mask,bit(idx))) { n_active_axis++; #ifdef COREXY - if (idx == X_AXIS) { + if (idx == AXIS_1) { int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position); sys_position[A_MOTOR] = axis_position; sys_position[B_MOTOR] = -axis_position; - } else if (idx == Y_AXIS) { + } else if (idx == AXIS_2) { int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position); sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position; } else { #if N_AXIS > 3 sys_position[idx] = 0; #else - sys_position[Z_AXIS] = 0; + sys_position[AXIS_3] = 0; #endif } #else @@ -532,7 +532,7 @@ void limits_go_home(uint8_t cycle_mask) if (axislock & step_pin[idx]) { if (limit_state & (1 << idx)) { #ifdef COREXY - if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); } + if (idx==AXIS_3) { axislock &= ~(step_pin[AXIS_3]); } #if N_AXIS > 3 else if (idx==AXIS_4) { axislock &= ~(step_pin[AXIS_4]); } #endif @@ -616,11 +616,11 @@ void limits_go_home(uint8_t cycle_mask) #endif #ifdef COREXY - if (idx==X_AXIS) { + if (idx==AXIS_1) { int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position); sys_position[A_MOTOR] = set_axis_position + off_axis_position; sys_position[B_MOTOR] = set_axis_position - off_axis_position; - } else if (idx==Y_AXIS) { + } else if (idx==AXIS_2) { int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position); sys_position[A_MOTOR] = off_axis_position + set_axis_position; sys_position[B_MOTOR] = off_axis_position - set_axis_position; diff --git a/grbl/motion_control.h b/grbl/motion_control.h index c8e416eb4..00fb9cbc9 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -29,9 +29,9 @@ #define PARKING_MOTION_LINE_NUMBER 0 #define HOMING_CYCLE_ALL 0 // Must be zero. -#define HOMING_CYCLE_X bit(X_AXIS) -#define HOMING_CYCLE_Y bit(Y_AXIS) -#define HOMING_CYCLE_Z bit(Z_AXIS) +#define HOMING_CYCLE_X bit(AXIS_1) +#define HOMING_CYCLE_Y bit(AXIS_2) +#define HOMING_CYCLE_Z bit(AXIS_3) #if N_AXIS > 3 #define HOMING_CYCLE_A bit(AXIS_4) #endif diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index 6e72eb7a5..e274e443b 100755 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -31,8 +31,8 @@ // CoreXY motor assignments. DO NOT ALTER. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. #ifdef COREXY - #define A_MOTOR X_AXIS // Must be X_AXIS - #define B_MOTOR Y_AXIS // Must be Y_AXIS + #define A_MOTOR AXIS_1 // Must be AXIS_1 (X) + #define B_MOTOR AXIS_2 // Must be AXIS_2 (Y) #endif // Conversions diff --git a/grbl/planner.c b/grbl/planner.c index 76ad71562..f5c749c38 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -330,9 +330,9 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) // Copy position data based on type of motion being planned. if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) { #ifdef COREXY - position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position); - position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position); - position_steps[Z_AXIS] = sys_position[Z_AXIS]; + position_steps[AXIS_1] = system_convert_corexy_to_x_axis_steps(sys_position); + position_steps[AXIS_2] = system_convert_corexy_to_y_axis_steps(sys_position); + position_steps[AXIS_3] = sys_position[AXIS_3]; #if N_AXIS > 3 position_steps[AXIS_4] = sys_position[AXIS_4]; #endif @@ -350,8 +350,8 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) #ifdef COREXY target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]); target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]); - block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS])); - block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS])); + block->steps[A_MOTOR] = labs((target_steps[AXIS_1]-position_steps[AXIS_1]) + (target_steps[AXIS_2]-position_steps[AXIS_2])); + block->steps[B_MOTOR] = labs((target_steps[AXIS_1]-position_steps[AXIS_1]) - (target_steps[AXIS_2]-position_steps[AXIS_2])); #endif for (idx=0; idxstep_event_count = max(block->step_event_count, block->steps[idx]); if (idx == A_MOTOR) { - delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx]; + delta_mm = (target_steps[AXIS_1]-position_steps[AXIS_1] + target_steps[AXIS_2]-position_steps[AXIS_2])/settings.steps_per_mm[idx]; } else if (idx == B_MOTOR) { - delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx]; + delta_mm = (target_steps[AXIS_1]-position_steps[AXIS_1] - target_steps[AXIS_2]+position_steps[AXIS_2])/settings.steps_per_mm[idx]; } else { delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx]; } @@ -490,10 +490,10 @@ void plan_sync_position() uint8_t idx; for (idx=0; idx 3 - printPgmString(PSTR("[AXS:")); - print_uint8_base10(N_AXIS); - printPgmString(PSTR(":")); - printPgmString(PSTR(AXIS_NAMES)); - report_util_feedback_line_feed(); + serial_write(AXIS_4_NAME); + #endif + #if N_AXIS > 4 + serial_write(AXIS_5_NAME); #endif + #if N_AXIS > 5 + serial_write(AXIS_6_NAME); + #endif + report_util_feedback_line_feed(); printPgmString(PSTR("[OPT:")); // Generate compile-time build option list serial_write('V'); serial_write('N'); @@ -551,9 +560,9 @@ void report_realtime_status() printPgmString(PSTR("|Pn:")); if (prb_pin_state) { serial_write('P'); } if (lim_pin_state) { - if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); } - if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); } - if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); } + if (bit_istrue(lim_pin_state,bit(AXIS_1))) { serial_write(AXIS_1_NAME); } + if (bit_istrue(lim_pin_state,bit(AXIS_2))) { serial_write(AXIS_2_NAME); } + if (bit_istrue(lim_pin_state,bit(AXIS_3))) { serial_write(AXIS_3_NAME); } #if N_AXIS > 3 if (bit_istrue(lim_pin_state,bit(AXIS_4))) { serial_write(AXIS_4_NAME); } #endif diff --git a/grbl/settings.c b/grbl/settings.c index 20608ce56..e7a1c0749 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -95,18 +95,18 @@ void settings_restore(uint8_t restore_flag) { if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } if (DEFAULT_INVERT_PROBE_PIN) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_UNIT; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_UNIT; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_UNIT; - settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; - settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; - settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; - settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION; - settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION; - settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION; - settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL); - settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); - settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); + settings.steps_per_mm[AXIS_1] = DEFAULT_X_STEPS_PER_UNIT; + settings.steps_per_mm[AXIS_2] = DEFAULT_Y_STEPS_PER_UNIT; + settings.steps_per_mm[AXIS_3] = DEFAULT_Z_STEPS_PER_UNIT; + settings.max_rate[AXIS_1] = DEFAULT_X_MAX_RATE; + settings.max_rate[AXIS_2] = DEFAULT_Y_MAX_RATE; + settings.max_rate[AXIS_3] = DEFAULT_Z_MAX_RATE; + settings.acceleration[AXIS_1] = DEFAULT_X_ACCELERATION; + settings.acceleration[AXIS_2] = DEFAULT_Y_ACCELERATION; + settings.acceleration[AXIS_3] = DEFAULT_Z_ACCELERATION; + settings.max_travel[AXIS_1] = (-DEFAULT_X_MAX_TRAVEL); + settings.max_travel[AXIS_2] = (-DEFAULT_Y_MAX_TRAVEL); + settings.max_travel[AXIS_3] = (-DEFAULT_Z_MAX_TRAVEL); #if N_AXIS > 3 settings.steps_per_mm[AXIS_4] = DEFAULT_A_STEPS_PER_UNIT; settings.max_rate[AXIS_4] = DEFAULT_A_MAX_RATE; @@ -335,8 +335,8 @@ void settings_init() { uint8_t get_step_pin_mask(uint8_t axis_idx) { #ifdef DEFAULTS_RAMPS_BOARD - if ( axis_idx == X_AXIS ) { return((1< 3 if ( axis_idx == AXIS_4 ) { return((1< 5 if ( axis_idx == AXIS_6 ) { return((1< 3 if ( axis_idx == AXIS_4 ) { return((1< 5 if ( axis_idx == AXIS_6 ) { return((1< 3 if ( axis_idx == AXIS_4 ) { return((1< 5 if ( axis_idx == AXIS_6 ) { return((1< 3 if ( axis_idx == AXIS_4 ) { return((1< 5 if ( axis_idx == AXIS_6 ) { return((1<steps[X_AXIS] >> st.exec_segment->amass_level; - st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level; - st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level; + st.steps[AXIS_1] = st.exec_block->steps[AXIS_1] >> st.exec_segment->amass_level; + st.steps[AXIS_2] = st.exec_block->steps[AXIS_2] >> st.exec_segment->amass_level; + st.steps[AXIS_3] = st.exec_block->steps[AXIS_3] >> st.exec_segment->amass_level; #if N_AXIS > 3 st.steps[AXIS_4] = st.exec_block->steps[AXIS_4] >> st.exec_segment->amass_level; #endif @@ -558,64 +558,64 @@ ISR(TIMER1_COMPA_vect) // Execute step displacement profile by Bresenham line algorithm #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING - st.counter_x += st.steps[X_AXIS]; + st.counter_x += st.steps[AXIS_1]; #else - st.counter_x += st.exec_block->steps[X_AXIS]; + st.counter_x += st.exec_block->steps[AXIS_1]; #endif #ifdef DEFAULTS_RAMPS_BOARD if (st.counter_x > st.exec_block->step_event_count) { - st.step_outbits[X_AXIS] |= (1<step_event_count; - if (st.exec_block->direction_bits[X_AXIS] & (1<direction_bits[AXIS_1] & (1< st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1<direction_bits & (1<steps[Y_AXIS]; + st.counter_y += st.exec_block->steps[AXIS_2]; #endif #ifdef DEFAULTS_RAMPS_BOARD if (st.counter_y > st.exec_block->step_event_count) { - st.step_outbits[Y_AXIS] |= (1<step_event_count; - if (st.exec_block->direction_bits[Y_AXIS] & (1<direction_bits[AXIS_2] & (1< st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1<direction_bits & (1<steps[Z_AXIS]; + st.counter_z += st.exec_block->steps[AXIS_3]; #endif #ifdef DEFAULTS_RAMPS_BOARD if (st.counter_z > st.exec_block->step_event_count) { - st.step_outbits[Z_AXIS] |= (1<step_event_count; - if (st.exec_block->direction_bits[Z_AXIS] & (1<direction_bits[AXIS_3] & (1< st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1<direction_bits & (1< 3 diff --git a/grbl/system.c b/grbl/system.c index 2ef9dbdfa..56b90facf 100755 --- a/grbl/system.c +++ b/grbl/system.c @@ -292,9 +292,9 @@ float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx) { float pos; #ifdef COREXY - if (idx==X_AXIS) { + if (idx==AXIS_1) { pos = (float)system_convert_corexy_to_x_axis_steps(steps) / settings.steps_per_mm[idx]; - } else if (idx==Y_AXIS) { + } else if (idx==AXIS_2) { pos = (float)system_convert_corexy_to_y_axis_steps(steps) / settings.steps_per_mm[idx]; } else { pos = steps[idx]/settings.steps_per_mm[idx]; From fac4be5de795954f37f2655a2dfce106b505b3d9 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 12 Jul 2018 14:26:41 +0200 Subject: [PATCH 020/101] Axis renaming and cloning. - Renaming constantes and variables from *[XYZ]* to *AXIS[123]* --- grbl/config.h | 22 ++-- grbl/defaults.h | 288 ++++++++++++++++++++++++------------------------ grbl/settings.c | 48 ++++---- 3 files changed, 176 insertions(+), 182 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 223e1d80d..6b6672f3e 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -42,19 +42,19 @@ #define DEFAULTS_RAMPS_BOARD #define CPU_MAP_2560_RAMPS_BOARD +// Serial baud rate +// #define BAUD_RATE 230400 +#define BAUD_RATE 115200 + // Axis array index values. Must start with 0 and be continuous. #ifdef DEFAULTS_RAMPS_BOARD // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) #define N_AXIS 5 // Number of axes #define N_AXIS_LINEAR 4 // Number of linears axis #else - #define N_AXIS 3 // Number of axes + #define N_AXIS 3 // Number of axes = 3 if not DEFAULTS_RAMPS_BOARD #endif -/* -#define X_AXIS 0 // Axis indexing value. -#define Y_AXIS 1 -#define Z_AXIS 2 -*/ + #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. #define AXIS_1_NAME 'X' #define AXIS_2 1 @@ -67,11 +67,11 @@ #endif #if N_AXIS > 3 #define AXIS_4 3 - #define AXIS_4_NAME 'Y' // Letter of axis number 4 + #define AXIS_4_NAME 'A' // Letter of axis number 4 #endif #if N_AXIS > 4 #define AXIS_5 4 - #define AXIS_5_NAME 'A' // Letter of axis number 5 + #define AXIS_5_NAME 'B' // Letter of axis number 5 #endif #if N_AXIS > 5 #define AXIS_6 5 @@ -81,12 +81,6 @@ #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif -//#define AXIS_NAMES "XYZABC" // Letters (names) of axis - -// Serial baud rate -// #define BAUD_RATE 230400 -#define BAUD_RATE 115200 - // Define realtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be diff --git a/grbl/defaults.h b/grbl/defaults.h index 407992409..da0cb79a3 100755 --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -30,18 +30,18 @@ #ifdef DEFAULTS_GENERIC // Grbl generic default settings. Should work across different machines. - #define DEFAULT_X_STEPS_PER_UNIT 250.0 - #define DEFAULT_Y_STEPS_PER_UNIT 250.0 - #define DEFAULT_Z_STEPS_PER_UNIT 250.0 - #define DEFAULT_X_MAX_RATE 500.0 // mm/min - #define DEFAULT_Y_MAX_RATE 500.0 // mm/min - #define DEFAULT_Z_MAX_RATE 500.0 // mm/min - #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT 250.0 + #define DEFAULT_AXIS2_STEPS_PER_UNIT 250.0 + #define DEFAULT_AXIS3_STEPS_PER_UNIT 250.0 + #define DEFAULT_AXIS1_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -72,18 +72,18 @@ #define MICROSTEPS 2 #define STEPS_PER_REV 200.0 #define MM_PER_REV (0.050*MM_PER_INCH) // 0.050 inch/rev leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_Y_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_Z_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_X_MAX_RATE 635.0 // mm/min (25 ipm) - #define DEFAULT_Y_MAX_RATE 635.0 // mm/min - #define DEFAULT_Z_MAX_RATE 635.0 // mm/min - #define DEFAULT_X_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 225.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 125.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS1_MAX_RATE 635.0 // mm/min (25 ipm) + #define DEFAULT_AXIS2_MAX_RATE 635.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 635.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 225.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 125.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 170.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 2800.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -117,18 +117,18 @@ #define MICROSTEPS_Z 2 #define STEP_REVS_Z 400 #define MM_PER_REV_Z 1.250 // 1.25 mm/rev leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Y_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Z_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) - #define DEFAULT_X_MAX_RATE 1000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 1000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 1000.0 // mm/min - #define DEFAULT_X_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) + #define DEFAULT_AXIS1_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -162,18 +162,18 @@ #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 1.250 // 1.25 mm/rev leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Y_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Z_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) - #define DEFAULT_X_MAX_RATE 5000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 500.0 // mm/min - #define DEFAULT_X_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) + #define DEFAULT_AXIS1_MAX_RATE 5000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 5000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -206,18 +206,18 @@ #define MICROSTEPS_Z 8 #define STEP_REVS_Z 200 #define MM_PER_REV_Z (2.0*20) // 2mm belt pitch, 20 pulley teeth - #define DEFAULT_X_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Y_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Z_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) - #define DEFAULT_X_MAX_RATE 5000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 5000.0 // mm/min - #define DEFAULT_X_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 425.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 465.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) + #define DEFAULT_AXIS1_MAX_RATE 5000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 5000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 5000.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 425.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 465.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 80.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -251,18 +251,18 @@ #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 2.117 // ACME 3/8-12 Leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Y_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Z_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) - #define DEFAULT_X_MAX_RATE 8000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 500.0 // mm/min - #define DEFAULT_X_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) + #define DEFAULT_AXIS1_MAX_RATE 8000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 8000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 290.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -296,18 +296,18 @@ #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 2.117 // ACME 3/8-12 Leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Y_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) - #define DEFAULT_Z_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) - #define DEFAULT_X_MAX_RATE 8000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 500.0 // mm/min - #define DEFAULT_X_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 740.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 790.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) + #define DEFAULT_AXIS1_MAX_RATE 8000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 8000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 740.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 790.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -339,18 +339,18 @@ #define MICROSTEPS 8 #define STEPS_PER_REV 200.0 #define MM_PER_REV 8.0 // 8 mm/rev leadscrew - #define DEFAULT_X_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_Y_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_Z_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) - #define DEFAULT_X_MAX_RATE 6000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 6000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 6000.0 // mm/min - #define DEFAULT_X_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 190.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 180.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 150.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS2_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS3_STEPS_PER_UNIT (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) + #define DEFAULT_AXIS1_MAX_RATE 6000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 6000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 6000.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 190.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 180.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 150.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -378,18 +378,18 @@ #ifdef DEFAULTS_OXCNC // Grbl settings for OpenBuilds OX CNC Machine // http://www.openbuilds.com/builds/openbuilds-ox-cnc-machine.341/ - #define DEFAULT_X_STEPS_PER_UNIT 26.670 - #define DEFAULT_Y_STEPS_PER_UNIT 26.670 - #define DEFAULT_Z_STEPS_PER_UNIT 50 - #define DEFAULT_X_MAX_RATE 500.0 // mm/min - #define DEFAULT_Y_MAX_RATE 500.0 // mm/min - #define DEFAULT_Z_MAX_RATE 500.0 // mm/min - #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 750.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT 26.670 + #define DEFAULT_AXIS2_STEPS_PER_UNIT 26.670 + #define DEFAULT_AXIS3_STEPS_PER_UNIT 50 + #define DEFAULT_AXIS1_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 500.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 750.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 80.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -417,18 +417,18 @@ #ifdef DEFAULTS_SIMULATOR // Settings only for Grbl Simulator (www.github.com/grbl/grbl-sim) // Grbl generic default settings. Should work across different machines. - #define DEFAULT_X_STEPS_PER_UNIT 1000.0 - #define DEFAULT_Y_STEPS_PER_UNIT 1000.0 - #define DEFAULT_Z_STEPS_PER_UNIT 1000.0 - #define DEFAULT_X_MAX_RATE 1000.0 // mm/min - #define DEFAULT_Y_MAX_RATE 1000.0 // mm/min - #define DEFAULT_Z_MAX_RATE 1000.0 // mm/min - #define DEFAULT_X_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Y_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. - #define DEFAULT_Z_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS1_STEPS_PER_UNIT 1000.0 + #define DEFAULT_AXIS2_STEPS_PER_UNIT 1000.0 + #define DEFAULT_AXIS3_STEPS_PER_UNIT 1000.0 + #define DEFAULT_AXIS1_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS2_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS3_MAX_RATE 1000.0 // mm/min + #define DEFAULT_AXIS1_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS2_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. + #define DEFAULT_AXIS3_MAX_TRAVEL 1000.0 // mm NOTE: Must be a positive value. #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 @@ -454,35 +454,35 @@ #endif #ifdef DEFAULTS_RAMPS_BOARD - #define DEFAULT_X_STEPS_PER_UNIT 80 - #define DEFAULT_Y_STEPS_PER_UNIT 80 - #define DEFAULT_Z_STEPS_PER_UNIT 4000 - #define DEFAULT_X_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec - #define DEFAULT_Y_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec - #define DEFAULT_Z_MAX_RATE 300.0 // 300 mm/min = 300/60 = 5 mm/sec - #define DEFAULT_X_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 - #define DEFAULT_Y_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 - #define DEFAULT_Z_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_X_MAX_TRAVEL 200.0 // mm - #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm - #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm + #define DEFAULT_AXIS1_STEPS_PER_UNIT 80 + #define DEFAULT_AXIS2_STEPS_PER_UNIT 80 + #define DEFAULT_AXIS3_STEPS_PER_UNIT 4000 + #define DEFAULT_AXIS1_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec + #define DEFAULT_AXIS2_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec + #define DEFAULT_AXIS3_MAX_RATE 300.0 // 300 mm/min = 300/60 = 5 mm/sec + #define DEFAULT_AXIS1_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (300.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 200.0 // mm + #define DEFAULT_AXIS2_MAX_TRAVEL 200.0 // mm + #define DEFAULT_AXIS3_MAX_TRAVEL 200.0 // mm #if N_AXIS > 3 - #define DEFAULT_A_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_A_MAX_RATE 1440 // °/mn - #define DEFAULT_A_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_A_MAX_TRAVEL 360.0 // ° + #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS4_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° #endif #if N_AXIS > 4 - #define DEFAULT_B_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_B_MAX_RATE 1440 // °/mn - #define DEFAULT_B_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_B_MAX_TRAVEL 180.0 // ° + #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS5_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° #endif #if N_AXIS > 5 - #define DEFAULT_C_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_C_MAX_RATE 1440 // °/mn - #define DEFAULT_C_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_C_MAX_TRAVEL 180.0 // ° + #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS6_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° #endif #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm diff --git a/grbl/settings.c b/grbl/settings.c index e7a1c0749..8247bbf5b 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -95,35 +95,35 @@ void settings_restore(uint8_t restore_flag) { if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } if (DEFAULT_INVERT_PROBE_PIN) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } - settings.steps_per_mm[AXIS_1] = DEFAULT_X_STEPS_PER_UNIT; - settings.steps_per_mm[AXIS_2] = DEFAULT_Y_STEPS_PER_UNIT; - settings.steps_per_mm[AXIS_3] = DEFAULT_Z_STEPS_PER_UNIT; - settings.max_rate[AXIS_1] = DEFAULT_X_MAX_RATE; - settings.max_rate[AXIS_2] = DEFAULT_Y_MAX_RATE; - settings.max_rate[AXIS_3] = DEFAULT_Z_MAX_RATE; - settings.acceleration[AXIS_1] = DEFAULT_X_ACCELERATION; - settings.acceleration[AXIS_2] = DEFAULT_Y_ACCELERATION; - settings.acceleration[AXIS_3] = DEFAULT_Z_ACCELERATION; - settings.max_travel[AXIS_1] = (-DEFAULT_X_MAX_TRAVEL); - settings.max_travel[AXIS_2] = (-DEFAULT_Y_MAX_TRAVEL); - settings.max_travel[AXIS_3] = (-DEFAULT_Z_MAX_TRAVEL); + settings.steps_per_mm[AXIS_1] = DEFAULT_AXIS1_STEPS_PER_UNIT; + settings.steps_per_mm[AXIS_2] = DEFAULT_AXIS2_STEPS_PER_UNIT; + settings.steps_per_mm[AXIS_3] = DEFAULT_AXIS3_STEPS_PER_UNIT; + settings.max_rate[AXIS_1] = DEFAULT_AXIS1_MAX_RATE; + settings.max_rate[AXIS_2] = DEFAULT_AXIS2_MAX_RATE; + settings.max_rate[AXIS_3] = DEFAULT_AXIS3_MAX_RATE; + settings.acceleration[AXIS_1] = DEFAULT_AXIS1_ACCELERATION; + settings.acceleration[AXIS_2] = DEFAULT_AXIS2_ACCELERATION; + settings.acceleration[AXIS_3] = DEFAULT_AXIS3_ACCELERATION; + settings.max_travel[AXIS_1] = (-DEFAULT_AXIS1_MAX_TRAVEL); + settings.max_travel[AXIS_2] = (-DEFAULT_AXIS2_MAX_TRAVEL); + settings.max_travel[AXIS_3] = (-DEFAULT_AXIS3_MAX_TRAVEL); #if N_AXIS > 3 - settings.steps_per_mm[AXIS_4] = DEFAULT_A_STEPS_PER_UNIT; - settings.max_rate[AXIS_4] = DEFAULT_A_MAX_RATE; - settings.acceleration[AXIS_4] = DEFAULT_A_ACCELERATION; - settings.max_travel[AXIS_4] = (-DEFAULT_A_MAX_TRAVEL); + settings.steps_per_mm[AXIS_4] = DEFAULT_AXIS4_STEPS_PER_UNIT; + settings.max_rate[AXIS_4] = DEFAULT_AXIS4_MAX_RATE; + settings.acceleration[AXIS_4] = DEFAULT_AXIS4_ACCELERATION; + settings.max_travel[AXIS_4] = (-DEFAULT_AXIS4_MAX_TRAVEL); #endif #if N_AXIS > 4 - settings.steps_per_mm[AXIS_5] = DEFAULT_B_STEPS_PER_UNIT; - settings.max_rate[AXIS_5] = DEFAULT_B_MAX_RATE; - settings.acceleration[AXIS_5] = DEFAULT_B_ACCELERATION; - settings.max_travel[AXIS_5] = (-DEFAULT_B_MAX_TRAVEL); + settings.steps_per_mm[AXIS_5] = DEFAULT_AXIS5_STEPS_PER_UNIT; + settings.max_rate[AXIS_5] = DEFAULT_AXIS5_MAX_RATE; + settings.acceleration[AXIS_5] = DEFAULT_AXIS5_ACCELERATION; + settings.max_travel[AXIS_5] = (-DEFAULT_AXIS5_MAX_TRAVEL); #endif #if N_AXIS > 5 - settings.steps_per_mm[AXIS_6] = DEFAULT_C_STEPS_PER_UNIT; - settings.max_rate[AXIS_6] = DEFAULT_C_MAX_RATE; - settings.acceleration[AXIS_6] = DEFAULT_C_ACCELERATION; - settings.max_travel[AXIS_6] = (-DEFAULT_C_MAX_TRAVEL); + settings.steps_per_mm[AXIS_6] = DEFAULT_AXIS6_STEPS_PER_UNIT; + settings.max_rate[AXIS_6] = DEFAULT_AXIS6_MAX_RATE; + settings.acceleration[AXIS_6] = DEFAULT_AXIS6_ACCELERATION; + settings.max_travel[AXIS_6] = (-DEFAULT_AXIS6_MAX_TRAVEL); #endif write_global_settings(); From 54676eff2b2b30afd61b7105bf79b6c36969293d Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 12 Jul 2018 14:43:33 +0200 Subject: [PATCH 021/101] Axis renaming and cloning. - Commentaire sur l'edition de config.h --- grbl/config.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 6b6672f3e..bed4b6c61 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -20,8 +20,9 @@ along with Grbl. If not, see . */ -// This file contains compile-time configurations for Grbl's internal system. For the most part, -// users will not need to directly modify these, but they are here for specific needs, i.e. +// This file contains compile-time configurations for Grbl's internal system. +// This file is used to define the number of axix and their names, +// define homing sequences or specific needs, i.e. // performance tuning or adjusting to non-typical machines. // IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. From 5efd4bb1875a9d17ad2ec959457871b8476a9b3f Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 12 Jul 2018 14:49:58 +0200 Subject: [PATCH 022/101] Axis renaming and cloning. - Update GRBL_VERSION & GRBL_VERSION_BUILD --- grbl/grbl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index 4f8ff2601..4d957bcec 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1g" -#define GRBL_VERSION_BUILD "20180711" +#define GRBL_VERSION "1.1h" +#define GRBL_VERSION_BUILD "20180712" // Define standard libraries used by Grbl. #include From ec537621d4176be6b12f2a28c20b2589a73581eb Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 12 Jul 2018 16:42:59 +0200 Subject: [PATCH 023/101] Correct the default standard N_AXIS_LINEAR to 3 --- grbl/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grbl/config.h b/grbl/config.h index bed4b6c61..c10bb36ab 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -51,7 +51,7 @@ #ifdef DEFAULTS_RAMPS_BOARD // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) #define N_AXIS 5 // Number of axes - #define N_AXIS_LINEAR 4 // Number of linears axis + #define N_AXIS_LINEAR 3 // Number of linears axis #else #define N_AXIS 3 // Number of axes = 3 if not DEFAULTS_RAMPS_BOARD #endif From b124dc955840e7826ee92754f029f93f6b4a959e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 3 Dec 2018 17:30:37 +0100 Subject: [PATCH 024/101] 6 axis wiring svg image update --- doc/images/grbl-Mega-5X_Wiring.svg | 739 ++++++++++++++++++++++++++--- 1 file changed, 669 insertions(+), 70 deletions(-) mode change 100644 => 100755 doc/images/grbl-Mega-5X_Wiring.svg diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg old mode 100644 new mode 100755 index 444ea609f..3c3b637ed --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -27,13 +27,19 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.812" + inkscape:zoom="0.813" inkscape:cx="500" inkscape:cy="500" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" - units="px"> + units="px" + showguides="false" + inkscape:window-width="1366" + inkscape:window-height="708" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1"> @@ -97,33 +103,33 @@ X motor + x="217.75493" + y="778.27551">axe 1 motor Y motor + x="368.5809" + y="857.5473">axe 2 motor 1 or 2 Z motor(s) + x="251.71283" + y="883.62683" + id="tspan1448">1 or 2 axe 3 motor(s) - Servos Axe 5 max end stop (D59) Axe 4 max end stop (D40) Axe 4 min end stop (D42) Axe 5 min end stop (D44) Feed hold switch (A10) Reset switch (A9) Cycle start switch (A11) - Safety door switch (A12) Copyright (C) Gauthier Brière - 2018 - Licence Creative Commons (BY-NC-ND) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E + D + S + for arduino control + 5V + GND + Vin + + axe 6 motor + + + + + + From 7c5bfb025ec1da00f45d21d4ce630776330314b3 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 4 Dec 2018 18:57:41 +0100 Subject: [PATCH 025/101] IJK & G10, G28, G30 problem with cloned axis --- .gitignore | 1 + doc/csv/setting_codes_en_US.csv | 0 doc/images/grbl-Mega-5X_Wiring.svg | 0 doc/markdown/interface.md | 0 doc/markdown/settings.md | 0 grbl/cpu_map.h | 0 grbl/defaults.h | 0 grbl/gcode.c | 324 +++++++++++++++++++++++++++-- grbl/gcode.h | 0 grbl/grbl.h | 4 +- grbl/motion_control.c | 99 +++++++-- grbl/motion_control.h | 4 +- grbl/nuts_bolts.h | 0 grbl/stepper.c | 0 grbl/system.c | 0 15 files changed, 404 insertions(+), 28 deletions(-) mode change 100755 => 100644 doc/csv/setting_codes_en_US.csv mode change 100755 => 100644 doc/images/grbl-Mega-5X_Wiring.svg mode change 100755 => 100644 doc/markdown/interface.md mode change 100755 => 100644 doc/markdown/settings.md mode change 100755 => 100644 grbl/cpu_map.h mode change 100755 => 100644 grbl/defaults.h mode change 100755 => 100644 grbl/gcode.c mode change 100755 => 100644 grbl/gcode.h mode change 100755 => 100644 grbl/motion_control.c mode change 100755 => 100644 grbl/nuts_bolts.h mode change 100755 => 100644 grbl/stepper.c mode change 100755 => 100644 grbl/system.c diff --git a/.gitignore b/.gitignore index b61325d28..474d49cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.hex +*.zip *.o *.elf *.DS_Store diff --git a/doc/csv/setting_codes_en_US.csv b/doc/csv/setting_codes_en_US.csv old mode 100755 new mode 100644 diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg old mode 100755 new mode 100644 diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md old mode 100755 new mode 100644 diff --git a/doc/markdown/settings.md b/doc/markdown/settings.md old mode 100755 new mode 100644 diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h old mode 100755 new mode 100644 diff --git a/grbl/defaults.h b/grbl/defaults.h old mode 100755 new mode 100644 diff --git a/grbl/gcode.c b/grbl/gcode.c old mode 100755 new mode 100644 index eb2c35caf..ee737a38d --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -78,6 +78,13 @@ uint8_t gc_execute_line(char *line) uint8_t axis_command = AXIS_COMMAND_NONE; uint8_t axis_0, axis_1, axis_linear; + uint8_t axis_a, axis_b, axis_c; + uint8_t axis_0_mask = 0; + uint8_t axis_1_mask = 0; + uint8_t axis_linear_mask = 0; + uint8_t axis_a_mask = 0; + uint8_t axis_b_mask = 0; + uint8_t axis_c_mask = 0; uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution // Initialize bitflag tracking variables for axis indices compatible operations. @@ -292,9 +299,51 @@ uint8_t gc_execute_line(char *line) // case 'D': // Not supported case 'F': word_bit = WORD_F; gc_block.values.f = value; break; // case 'H': // Not supported - case 'I': word_bit = WORD_I; gc_block.values.ijk[AXIS_1] = value; ijk_words |= (1<condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag. mc_line(gc_block.values.xyz, pl_data); } else if ((gc_state.modal.motion == MOTION_MODE_CW_ARC) || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) { + // GBGB TODO : Revoir mc_arc() pour le clonage et le renomage des axes... mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r, - axis_0, axis_1, axis_linear, bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE)); + axis_0, axis_1, axis_linear, axis_0_mask, axis_1_mask, axis_linear_mask, + axis_a, axis_b, axis_c, axis_a_mask, axis_b_mask, axis_c_mask, + bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE)); } else { // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So // upon a successful probing cycle, the machine position and the returned value should be the same. diff --git a/grbl/gcode.h b/grbl/gcode.h old mode 100755 new mode 100644 diff --git a/grbl/grbl.h b/grbl/grbl.h index 4d957bcec..efe879f29 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1h" -#define GRBL_VERSION_BUILD "20180712" +#define GRBL_VERSION "1.1i" +#define GRBL_VERSION_BUILD "20181204" // Define standard libraries used by Grbl. #include diff --git a/grbl/motion_control.c b/grbl/motion_control.c old mode 100755 new mode 100644 index c50dad19a..1ec3365c4 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -86,7 +86,9 @@ void mc_line(float *target, plan_line_data_t *pl_data) // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // distance from segment to the circle when the end points both lie on the circle. void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, - uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) + uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t axis_0_mask, uint8_t axis_1_mask, uint8_t axis_linear_mask, + uint8_t axis_a, uint8_t axis_b, uint8_t axis_c, uint8_t axis_a_mask, uint8_t axis_b_mask, uint8_t axis_c_mask, + uint8_t is_clockwise_arc) { float center_axis0 = position[axis_0] + offset[axis_0]; float center_axis1 = position[axis_1] + offset[axis_1]; @@ -94,6 +96,7 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of float r_axis1 = -offset[axis_1]; float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1; + float a_per_segment, b_per_segment, c_per_segment; // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); @@ -121,15 +124,9 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of float theta_per_segment = angular_travel/segments; float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; - #if N_AXIS >3 - float axis_a_per_segment = (target[AXIS_4] - position[AXIS_4])/segments; - #endif - #if N_AXIS >4 - float axis_b_per_segment = (target[AXIS_5] - position[AXIS_5])/segments; - #endif - #if N_AXIS >5 - float axis_c_per_segment = (target[AXIS_6] - position[AXIS_6])/segments; - #endif + if ( axis_a_mask ) a_per_segment = (target[axis_a] - position[axis_a])/segments; else a_per_segment = 0; + if ( axis_b_mask ) b_per_segment = (target[axis_b] - position[axis_b])/segments; else b_per_segment = 0; + if ( axis_c_mask ) c_per_segment = (target[axis_c] - position[axis_c])/segments; else c_per_segment = 0; /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. @@ -186,19 +183,95 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of } // Update arc_target location + /* position[axis_0] = center_axis0 + r_axis0; position[axis_1] = center_axis1 + r_axis1; position[axis_linear] += linear_per_segment; + * */ + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 - position[AXIS_4] += axis_a_per_segment; + if (bit_istrue((1< 4 - position[AXIS_5] += axis_b_per_segment; + if (bit_istrue((1< 5 - position[AXIS_6] += axis_c_per_segment; + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< Date: Thu, 13 Dec 2018 14:37:34 +0100 Subject: [PATCH 026/101] Fix HOMING_SINGLE_AXIS_COMMANDS bug #25 --- grbl/main.c | 84 +++++++++++++++++++++++++++++++++++++++++-- grbl/motion_control.h | 12 ------- grbl/system.c | 36 ++++++++++++------- grbl/system.h | 9 +++-- 4 files changed, 113 insertions(+), 28 deletions(-) diff --git a/grbl/main.c b/grbl/main.c index 2b5438720..dce157671 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -31,6 +31,12 @@ volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variab volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides. volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides. +uint8_t axis_X_mask = 0; // Global mask for axis X bits +uint8_t axis_Y_mask = 0; // Global mask for axis Y bits +uint8_t axis_Z_mask = 0; // Global mask for axis Z bits +uint8_t axis_A_mask = 0; // Global mask for axis A bits +uint8_t axis_B_mask = 0; // Global mask for axis B bits +uint8_t axis_C_mask = 0; // Global mask for axis C bits #ifdef DEBUG volatile uint8_t sys_rt_exec_debug; #endif @@ -44,6 +50,80 @@ int main(void) stepper_init(); // Configure stepper pins and interrupt timers system_init(); // Configure pinout pins and pin-change interrupt + // Initialize axis mask bits (ability to axis renaming ans cloning) + if (AXIS_1_NAME == 'X') axis_X_mask |= (1< 3 - #define HOMING_CYCLE_A bit(AXIS_4) -#endif -#if N_AXIS > 4 - #define HOMING_CYCLE_B bit(AXIS_5) -#endif -#if N_AXIS > 5 - #define HOMING_CYCLE_C bit(AXIS_6) -#endif // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in diff --git a/grbl/system.c b/grbl/system.c index 56b90facf..2a27d23cb 100644 --- a/grbl/system.c +++ b/grbl/system.c @@ -174,18 +174,30 @@ uint8_t system_execute_line(char *line) #ifdef HOMING_SINGLE_AXIS_COMMANDS } else if (line[3] == 0) { switch (line[2]) { - case 'X': mc_homing_cycle(HOMING_CYCLE_X); break; - case 'Y': mc_homing_cycle(HOMING_CYCLE_Y); break; - case 'Z': mc_homing_cycle(HOMING_CYCLE_Z); break; - #if N_AXIS > 3 - case AXIS_4_NAME: mc_homing_cycle(HOMING_CYCLE_4); break; - #endif - #if N_AXIS > 4 - case AXIS_5_NAME: mc_homing_cycle(HOMING_CYCLE_5); break; - #endif - #if N_AXIS > 5 - case AXIS_6_NAME: mc_homing_cycle(HOMING_CYCLE_6); break; - #endif + case 'X': mc_homing_cycle(axis_X_mask); break; + case 'Y': mc_homing_cycle(axis_Y_mask); break; + case 'Z': mc_homing_cycle(axis_Z_mask); break; + case 'A': + if (axis_A_mask != 0) { + mc_homing_cycle(axis_A_mask); + } else { + return(STATUS_INVALID_STATEMENT); + } + break; + case 'B': + if (axis_B_mask != 0) { + mc_homing_cycle(axis_B_mask); + } else { + return(STATUS_INVALID_STATEMENT); + } + break; + case 'C': + if (axis_C_mask != 0) { + mc_homing_cycle(axis_C_mask); + } else { + return(STATUS_INVALID_STATEMENT); + } + break; default: return(STATUS_INVALID_STATEMENT); } #endif diff --git a/grbl/system.h b/grbl/system.h index 7060f2147..c7803c8ab 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -118,7 +118,7 @@ // Define global system variables typedef struct { uint8_t state; // Tracks the current system state of Grbl. - uint8_t abort; // System abort flag. Forces exit back to main loop for reset. + uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door. uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean) uint8_t step_control; // Governs the step segment generator depending on system state. @@ -150,7 +150,12 @@ extern volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. extern volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides. extern volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides. - +extern uint8_t axis_X_mask; // Global mask for axis X bits +extern uint8_t axis_Y_mask; // Global mask for axis Y bits +extern uint8_t axis_Z_mask; // Global mask for axis Z bits +extern uint8_t axis_A_mask; // Global mask for axis A bits +extern uint8_t axis_B_mask; // Global mask for axis B bits +extern uint8_t axis_C_mask; // Global mask for axis C bits #ifdef DEBUG #define EXEC_DEBUG_REPORT bit(0) extern volatile uint8_t sys_rt_exec_debug; From 05d0757feb0b9be73b82c00511af3e87202834d6 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 9 Jan 2019 13:19:29 +0100 Subject: [PATCH 027/101] Z homing first correction --- grbl/config.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index c10bb36ab..50e82db31 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -145,27 +145,27 @@ // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. #ifdef DEFAULTS_RAMPS_BOARD #if N_AXIS == 4 // 4 axis : homing - #define HOMING_CYCLE_0 (1< Date: Thu, 7 Feb 2019 16:04:34 +0100 Subject: [PATCH 028/101] Adding U, V & W axis name capability --- grbl/config.h | 2 +- grbl/gcode.c | 242 ++++++++++++++++++++++++------------------ grbl/gcode.h | 39 +++---- grbl/grbl.h | 4 +- grbl/main.c | 49 ++++++++- grbl/motion_control.c | 50 +++++++++ grbl/motion_control.h | 1 + grbl/nuts_bolts.h | 1 + grbl/system.c | 21 ++++ grbl/system.h | 3 + 10 files changed, 285 insertions(+), 127 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 50e82db31..0021da0cb 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -57,7 +57,7 @@ #endif #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. -#define AXIS_1_NAME 'X' +#define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V & W. #define AXIS_2 1 #define AXIS_2_NAME 'Y' #define AXIS_3 2 diff --git a/grbl/gcode.c b/grbl/gcode.c index ee737a38d..e3bab3734 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -79,21 +79,25 @@ uint8_t gc_execute_line(char *line) uint8_t axis_command = AXIS_COMMAND_NONE; uint8_t axis_0, axis_1, axis_linear; uint8_t axis_a, axis_b, axis_c; + uint8_t axis_u, axis_v, axis_w; uint8_t axis_0_mask = 0; uint8_t axis_1_mask = 0; uint8_t axis_linear_mask = 0; uint8_t axis_a_mask = 0; uint8_t axis_b_mask = 0; uint8_t axis_c_mask = 0; + uint8_t axis_u_mask = 0; + uint8_t axis_v_mask = 0; + uint8_t axis_w_mask = 0; uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution // Initialize bitflag tracking variables for axis indices compatible operations. - uint8_t axis_words = 0; // XYZ tracking + uint32_t axis_dwords = 0; // XYZ tracking uint8_t ijk_words = 0; // IJK tracking // Initialize command and value words and parser flags variables. - uint16_t command_words = 0; // Tracks G and M command words. Also used for modal group violations. - uint16_t value_words = 0; // Tracks value words. + uint32_t command_dwords = 0; // Tracks G and M command words. Also used for modal group violations. + uint32_t value_dwords = 0; // Tracks value words. uint8_t gc_parser_flags = GC_PARSER_NONE; // Determine if the line is a jogging motion or a normal g-code block. @@ -111,7 +115,7 @@ uint8_t gc_execute_line(char *line) perform initial error-checks for command word modal group violations, for any repeated words, and for negative values set for the value words F, N, P, T, and S. */ - uint8_t word_bit; // Bit-value for assigning tracking variables + uint32_t dword_bit; // Bit-value for assigning tracking variables uint8_t char_counter; char letter; float value; @@ -158,7 +162,7 @@ uint8_t gc_execute_line(char *line) } // No break. Continues to next line. case 4: case 53: - word_bit = MODAL_GROUP_G0; + dword_bit = MODAL_GROUP_G0; gc_block.non_modal_command = int_value; if ((int_value == 28) || (int_value == 30) || (int_value == 92)) { if (!((mantissa == 0) || (mantissa == 10))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } @@ -173,7 +177,7 @@ uint8_t gc_execute_line(char *line) axis_command = AXIS_COMMAND_MOTION_MODE; // No break. Continues to next line. case 80: - word_bit = MODAL_GROUP_G1; + dword_bit = MODAL_GROUP_G1; gc_block.modal.motion = int_value; if (int_value == 38){ if (!((mantissa == 20) || (mantissa == 30) || (mantissa == 40) || (mantissa == 50))) { @@ -184,36 +188,36 @@ uint8_t gc_execute_line(char *line) } break; case 17: case 18: case 19: - word_bit = MODAL_GROUP_G2; + dword_bit = MODAL_GROUP_G2; gc_block.modal.plane_select = int_value - 17; break; case 90: case 91: if (mantissa == 0) { - word_bit = MODAL_GROUP_G3; + dword_bit = MODAL_GROUP_G3; gc_block.modal.distance = int_value - 90; } else { - word_bit = MODAL_GROUP_G4; + dword_bit = MODAL_GROUP_G4; if ((mantissa != 10) || (int_value == 90)) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G90.1 not supported] mantissa = 0; // Set to zero to indicate valid non-integer G command. // Otherwise, arc IJK incremental mode is default. G91.1 does nothing. } break; case 93: case 94: - word_bit = MODAL_GROUP_G5; + dword_bit = MODAL_GROUP_G5; gc_block.modal.feed_rate = 94 - int_value; break; case 20: case 21: - word_bit = MODAL_GROUP_G6; + dword_bit = MODAL_GROUP_G6; gc_block.modal.units = 21 - int_value; break; case 40: - word_bit = MODAL_GROUP_G7; + dword_bit = MODAL_GROUP_G7; // NOTE: Not required since cutter radius compensation is always disabled. Only here // to support G40 commands that often appear in g-code program headers to setup defaults. // gc_block.modal.cutter_comp = CUTTER_COMP_DISABLE; // G40 break; case 43: case 49: - word_bit = MODAL_GROUP_G8; + dword_bit = MODAL_GROUP_G8; // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. @@ -228,11 +232,11 @@ uint8_t gc_execute_line(char *line) break; case 54: case 55: case 56: case 57: case 58: case 59: // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) - word_bit = MODAL_GROUP_G12; + dword_bit = MODAL_GROUP_G12; gc_block.modal.coord_select = int_value - 54; // Shift to array indexing. break; case 61: - word_bit = MODAL_GROUP_G13; + dword_bit = MODAL_GROUP_G13; if (mantissa != 0) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G61.1 not supported] // gc_block.modal.control = CONTROL_MODE_EXACT_PATH; // G61 break; @@ -240,9 +244,9 @@ uint8_t gc_execute_line(char *line) } if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command] // Check for more than one command per modal group violations in the current block - // NOTE: Variable 'word_bit' is always assigned, if the command is valid. - if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } - command_words |= bit(word_bit); + // NOTE: Variable 'dword_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_dwords,dwbit(dword_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } + command_dwords |= dwbit(dword_bit); break; case 'M': @@ -251,7 +255,7 @@ uint8_t gc_execute_line(char *line) if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands] switch(int_value) { case 0: case 1: case 2: case 30: - word_bit = MODAL_GROUP_M4; + dword_bit = MODAL_GROUP_M4; switch(int_value) { case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause case 1: break; // Optional stop not supported. Ignore. @@ -259,7 +263,7 @@ uint8_t gc_execute_line(char *line) } break; case 3: case 4: case 5: - word_bit = MODAL_GROUP_M7; + dword_bit = MODAL_GROUP_M7; switch(int_value) { case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break; case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break; @@ -267,7 +271,7 @@ uint8_t gc_execute_line(char *line) } break; case 7: case 8: case 9: - word_bit = MODAL_GROUP_M8; + dword_bit = MODAL_GROUP_M8; switch(int_value) { case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break; case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break; @@ -276,7 +280,7 @@ uint8_t gc_execute_line(char *line) break; #ifdef ENABLE_PARKING_OVERRIDE_CONTROL case 56: - word_bit = MODAL_GROUP_M9; + dword_bit = MODAL_GROUP_M9; gc_block.modal.override = OVERRIDE_PARKING_MOTION; break; #endif @@ -284,9 +288,9 @@ uint8_t gc_execute_line(char *line) } // Check for more than one command per modal group violations in the current block - // NOTE: Variable 'word_bit' is always assigned, if the command is valid. - if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } - command_words |= bit(word_bit); + // NOTE: Variable 'dword_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_dwords,dwbit(dword_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } + command_dwords |= dwbit(dword_bit); break; // NOTE: All remaining letters assign values. @@ -297,10 +301,10 @@ uint8_t gc_execute_line(char *line) words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ switch(letter){ // case 'D': // Not supported - case 'F': word_bit = WORD_F; gc_block.values.f = value; break; + case 'F': dword_bit = DWORD_F; gc_block.values.f = value; break; // case 'H': // Not supported case 'I': - word_bit = WORD_I; /* gc_block.values.ijk[AXIS_1] = value; ijk_words |= (1< MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } gc_block.values.t = int_value; break; - // case 'X', 'Y', 'Z', 'A', 'B' or 'C' depending of AXIS_*_NAME. - // case imposible because same name can be used for axis cloning + // case 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V' or 'W' depending of AXIS_*_NAME. + // case imposible because same name can be used more than one for axis cloning // case AXIS_1_NAME: case AXIS_2_NAME: case AXIS_3_NAME: case AXIS_4_NAME: case AXIS_5_NAME: case AXIS_6_NAME: default: if (letter == AXIS_1_NAME) { - word_bit = WORD_X; gc_block.values.xyz[AXIS_1] = value; axis_words |= (1< MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number] } - // bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking. + // bit_false(value_dwords,dwbit(DWORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking. // Track for unused words at the end of error-checking. // NOTE: Single-meaning value words are removed all at once at the end of error-checking, because @@ -467,14 +471,14 @@ uint8_t gc_execute_line(char *line) // is not defined after switching to G94 from G93. // NOTE: For jogging, ignore prior feed rate mode. Enforce G94 and check for required F word. if (gc_parser_flags & GC_PARSER_JOG_MOTION) { - if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } + if (bit_isfalse(value_dwords,dwbit(DWORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; } } else { if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93 // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here. if (axis_command == AXIS_COMMAND_MOTION_MODE) { if ((gc_block.modal.motion != MOTION_MODE_NONE) && (gc_block.modal.motion != MOTION_MODE_SEEK)) { - if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing] + if (bit_isfalse(value_dwords,dwbit(DWORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing] } } // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would @@ -492,7 +496,7 @@ uint8_t gc_execute_line(char *line) } else { // = G94 // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value. if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94 - if (bit_istrue(value_words,bit(WORD_F))) { + if (bit_istrue(value_dwords,dwbit(DWORD_F))) { if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; } } else { gc_block.values.f = gc_state.feed_rate; // Push last state feed rate @@ -500,40 +504,37 @@ uint8_t gc_execute_line(char *line) } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value. } } - // bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking. + // bit_false(value_dwords,dwbit(DWORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking. // [4. Set spindle speed ]: S is negative (done.) - if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; } - // bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. + if (bit_isfalse(value_dwords,dwbit(DWORD_S))) { gc_block.values.s = gc_state.spindle_speed; } + // bit_false(value_dwords,dwbit(DWORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. - // bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. + // bit_false(value_dwords,dwbit(DWORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. // [6. Change tool ]: N/A // [7. Spindle control ]: N/A // [8. Coolant control ]: N/A // [9. Override control ]: Not supported except for a Grbl-only parking motion override control. #ifdef ENABLE_PARKING_OVERRIDE_CONTROL - if (bit_istrue(command_words,bit(MODAL_GROUP_M9))) { // Already set as enabled in parser. - if (bit_istrue(value_words,bit(WORD_P))) { + if (bit_istrue(command_dwords,dwbit(MODAL_GROUP_M9))) { // Already set as enabled in parser. + if (bit_istrue(value_dwords,dwbit(DWORD_P))) { if (gc_block.values.p == 0.0) { gc_block.modal.override = OVERRIDE_DISABLED; } - bit_false(value_words,bit(WORD_P)); + bit_false(value_dwords,dwbit(DWORD_P)); } } #endif // [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below. if (gc_block.non_modal_command == NON_MODAL_DWELL) { - if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] - bit_false(value_words,bit(WORD_P)); + if (bit_isfalse(value_dwords,dwbit(DWORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] + bit_false(value_dwords,dwbit(DWORD_P)); } - // [11. Set active plane ]: N/A + // [11. Set active plane ]: switch (gc_block.modal.plane_select) { - case PLANE_SELECT_XY: - /*axis_0 = AXIS_1; - axis_1 = AXIS_2; - axis_linear = AXIS_3;*/ + case PLANE_SELECT_XY: /* axis_0 = X axis, axis_1 = Y axis, axis_linear = Z axis */ if (AXIS_1_NAME == 'X') { axis_0_mask |= (1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] if (gc_state.modal.coord_select != gc_block.modal.coord_select) { if (!(settings_read_coord_data(gc_block.modal.coord_select,block_coord_system))) { FAIL(STATUS_SETTING_READ_FAIL); } @@ -771,16 +802,16 @@ uint8_t gc_execute_line(char *line) // [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.) // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing. // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing. - if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words] - if (bit_isfalse(value_words,((1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] if (gc_block.values.l != 20) { if (gc_block.values.l == 2) { - if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported] + if (bit_istrue(value_dwords,dwbit(DWORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported] } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L] } - bit_false(value_words,(bit(WORD_L)|bit(WORD_P))); + bit_false(value_dwords,(dwbit(DWORD_L)|dwbit(DWORD_P))); // Determine coordinate system to change and try to load from EEPROM. if (coord_select > 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. @@ -792,7 +823,7 @@ uint8_t gc_execute_line(char *line) // Pre-calculate the coordinate data changes. for (idx=0; idx WCS = MPos - G92 - TLO - WPos @@ -807,12 +838,12 @@ uint8_t gc_execute_line(char *line) break; case NON_MODAL_SET_COORDINATE_OFFSET: // [G92 Errors]: No axis words. - if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words] + if (!axis_dwords) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words] // Update axes defined only in block. Offsets current system to defined value. Does not update when // active coordinate system is selected, but is still active unless G92.1 disables it. for (idx=0; idx G92 = MPos - WCS - TLO - WPos gc_block.values.xyz[idx] = gc_state.position[idx]-block_coord_system[idx]-gc_block.values.xyz[idx]; if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.xyz[idx] -= gc_state.tool_length_offset; } @@ -829,9 +860,9 @@ uint8_t gc_execute_line(char *line) // modes applied. This includes the motion mode commands. We can now pre-compute the target position. // NOTE: Tool offsets may be appended to these conversions when/if this feature is added. if (axis_command != AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // TLO block any axis command. - if (axis_words) { + if (axis_dwords) { for (idx=0; idx 3 - if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_A)|bit(WORD_B)|bit(WORD_C))); } // Remove axis words. + if (axis_command) { bit_false(value_dwords,(dwbit(DWORD_X)|dwbit(DWORD_Y)|dwbit(DWORD_Z)|dwbit(DWORD_A)|dwbit(DWORD_B)|dwbit(DWORD_C)|dwbit(DWORD_U)|dwbit(DWORD_V)|dwbit(DWORD_W))); } // Remove axis words. #else - if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words. + if (axis_command) { bit_false(value_dwords,(dwbit(DWORD_X)|dwbit(DWORD_Y)|dwbit(DWORD_Z))); } // Remove axis words. #endif - if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words] + if (value_dwords) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words] /* ------------------------------------------------------------------------------------- STEP 4: EXECUTE!! @@ -1190,7 +1221,7 @@ uint8_t gc_execute_line(char *line) if (gc_parser_flags & GC_PARSER_JOG_MOTION) { // Only distance and unit modal commands and G53 absolute override command are allowed. // NOTE: Feed rate word and axis word checks have already been performed in STEP 3. - if (command_words & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6 | bit(MODAL_GROUP_G0))) ) { FAIL(STATUS_INVALID_JOG_COMMAND) }; + if (command_dwords & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6 | bit(MODAL_GROUP_G0))) ) { FAIL(STATUS_INVALID_JOG_COMMAND) }; if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE || gc_block.non_modal_command == NON_MODAL_NO_ACTION)) { FAIL(STATUS_INVALID_JOG_COMMAND); } // Initialize planner data to current spindle and coolant modal state. @@ -1212,7 +1243,7 @@ uint8_t gc_execute_line(char *line) // Any motion mode with axis words is allowed to be passed from a spindle speed update. // NOTE: G1 and G0 without axis words sets axis_command to none. G28/30 are intentionally omitted. // TODO: Check sync conditions for M3 enabled motions that don't enter the planner. (zero length). - if (axis_words && (axis_command == AXIS_COMMAND_MOTION_MODE)) { + if (axis_dwords && (axis_command == AXIS_COMMAND_MOTION_MODE)) { gc_parser_flags |= GC_PARSER_LASER_ISMOTION; } else { // M3 constant power laser requires planner syncs to update the laser when changing between @@ -1391,6 +1422,7 @@ uint8_t gc_execute_line(char *line) mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r, axis_0, axis_1, axis_linear, axis_0_mask, axis_1_mask, axis_linear_mask, axis_a, axis_b, axis_c, axis_a_mask, axis_b_mask, axis_c_mask, + axis_u, axis_v, axis_w, axis_u_mask, axis_v_mask, axis_w_mask, bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE)); } else { // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So diff --git a/grbl/gcode.h b/grbl/gcode.h index 42af3c15e..2fb054260 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -137,24 +137,27 @@ // N/A: Stores coordinate system value (54-59) to change to. // Define parameter word mapping. -#define WORD_F 0 -#define WORD_I 1 -#define WORD_J 2 -#define WORD_K 3 -#define WORD_L 4 -#define WORD_N 5 -#define WORD_P 6 -#define WORD_R 7 -#define WORD_S 8 -#define WORD_T 9 -#define WORD_X 10 -#define WORD_Y 11 -#define WORD_Z 12 -#if N_AXIS > 3 - #define WORD_A 13 - #define WORD_B 14 - #define WORD_C 15 -#endif +// Updated to 32 bits tou support more than 16 values... Needed for new axis U, V & W +#define DWORD_F 0 +#define DWORD_I 1 +#define DWORD_J 2 +#define DWORD_K 3 +#define DWORD_L 4 +#define DWORD_N 5 +#define DWORD_P 6 +#define DWORD_R 7 +#define DWORD_S 8 +#define DWORD_T 9 +#define DWORD_X 10 +#define DWORD_Y 11 +#define DWORD_Z 12 +#define DWORD_A 13 +#define DWORD_B 14 +#define DWORD_C 15 +#define DWORD_U 16 +#define DWORD_V 17 +#define DWORD_W 18 + // Define g-code parser position updating flags #define GC_UPDATE_POS_TARGET 0 // Must be zero #define GC_UPDATE_POS_SYSTEM 1 diff --git a/grbl/grbl.h b/grbl/grbl.h index efe879f29..c69a8f05a 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1i" -#define GRBL_VERSION_BUILD "20181204" +#define GRBL_VERSION "1.1j" +#define GRBL_VERSION_BUILD "20190207" // Define standard libraries used by Grbl. #include diff --git a/grbl/main.c b/grbl/main.c index dce157671..ae47a1010 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -37,6 +37,9 @@ uint8_t axis_Z_mask = 0; // Global mask for axis Z bits uint8_t axis_A_mask = 0; // Global mask for axis A bits uint8_t axis_B_mask = 0; // Global mask for axis B bits uint8_t axis_C_mask = 0; // Global mask for axis C bits +uint8_t axis_U_mask = 0; // Global mask for axis C bits +uint8_t axis_V_mask = 0; // Global mask for axis C bits +uint8_t axis_W_mask = 0; // Global mask for axis C bits #ifdef DEBUG volatile uint8_t sys_rt_exec_debug; #endif @@ -50,7 +53,7 @@ int main(void) stepper_init(); // Configure stepper pins and interrupt timers system_init(); // Configure pinout pins and pin-change interrupt - // Initialize axis mask bits (ability to axis renaming ans cloning) + // Initialize axis mask bits (ability to axis renaming and cloning) if (AXIS_1_NAME == 'X') axis_X_mask |= (1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< Date: Wed, 27 Mar 2019 18:19:16 +0100 Subject: [PATCH 029/101] Ignore soft limit if AXIS_MAX_TRAVEL == 0 (parameter 30 to 35) --- grbl/system.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/grbl/system.c b/grbl/system.c index b4954fcc8..eaab8b047 100644 --- a/grbl/system.c +++ b/grbl/system.c @@ -367,18 +367,21 @@ uint8_t system_check_travel_limits(float *target) { uint8_t idx; for (idx=0; idx -settings.max_travel[idx]) { return(true); } - } else { + // Ignore soft limit if AXIS_MAX_TRAVEL == 0 (parameter $130 to $135) + if (settings.max_travel[idx] != 0) { + #ifdef HOMING_FORCE_SET_ORIGIN + // When homing forced set origin is enabled, soft limits checks need to account for directionality. + // NOTE: max_travel is stored as negative + if (bit_istrue(settings.homing_dir_mask,bit(idx))) { + if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { return(true); } + } else { + if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); } + } + #else + // NOTE: max_travel is stored as negative if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); } - } - #else - // NOTE: max_travel is stored as negative - if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); } - #endif + #endif + } } return(false); } From 34c718308c53d51f948440ee308d841414fc06ac Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 14 May 2019 20:23:07 +0200 Subject: [PATCH 030/101] Correct bug Invert limit pin problem --- grbl/config.h | 2 +- grbl/cpu_map.h | 2 +- grbl/grbl.h | 4 ++-- grbl/limits.c | 20 +++++++++++--------- grbl/protocol.c | 32 ++++++++++++++++---------------- grbl/report.c | 2 ++ 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 0021da0cb..8eb35e882 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -294,7 +294,7 @@ // ADVANCED CONFIGURATION OPTIONS: // Enables code for debugging purposes. Not for general use and always in constant flux. -// #define DEBUG // Uncomment to enable. Default disabled. +//#define DEBUG // Uncomment to enable. Default disabled. // Configure rapid, feed, and spindle override settings. These values define the max and min // allowable override values and the coarse and fine increments per command received. Please diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 5c7c5d18a..a9c37bafd 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -308,7 +308,7 @@ // #define LIMIT_INT_vect PCINT0_vect // #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register // #define LIMIT_MASK ((1< diff --git a/grbl/limits.c b/grbl/limits.c index 179d27a1b..74ba34aeb 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -22,7 +22,6 @@ #include "grbl.h" - // Homing axis search distance multiplier. Computed by this value times the cycle travel. #ifndef HOMING_AXIS_SEARCH_SCALAR #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged. @@ -111,7 +110,7 @@ void limits_init() MAX_LIMIT_PORT(5) |= (1<spindle_speed; } #ifdef DISABLE_LASER_DURING_HOLD - if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) { + if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) { system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); } #endif @@ -540,10 +540,10 @@ static void protocol_exec_rt_suspend() // Block until initial hold is complete and the machine has stopped motion. if (sys.suspend & SUSPEND_HOLD_COMPLETE) { - // Parking manager. Handles de/re-energizing, switch state checks, and parking motions for + // Parking manager. Handles de/re-energizing, switch state checks, and parking motions for // the safety door and sleep states. if (sys.state & (STATE_SAFETY_DOOR | STATE_SLEEP)) { - + // Handles retraction motions and de-energizing. if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) { @@ -556,7 +556,7 @@ static void protocol_exec_rt_suspend() coolant_set_state(COOLANT_DISABLE); // De-energize #else - + // Get current position and store restore location and spindle retract waypoint. system_convert_array_steps_to_mpos(parking_target,sys_position); if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) { @@ -617,7 +617,7 @@ static void protocol_exec_rt_suspend() } else { - + if (sys.state == STATE_SLEEP) { report_feedback_message(MESSAGE_SLEEP_MODE); // Spindle and coolant should already be stopped, but do it again just to be sure. @@ -626,8 +626,8 @@ static void protocol_exec_rt_suspend() st_go_idle(); // Disable steppers while (!(sys.abort)) { protocol_exec_rt_system(); } // Do nothing until reset. return; // Abort received. Return to re-initialize. - } - + } + // Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume. if (sys.state == STATE_SAFETY_DOOR) { if (!(system_check_safety_door_ajar())) { @@ -692,8 +692,8 @@ static void protocol_exec_rt_suspend() // restore parking motion should logically be valid, either by returning to the // original position through valid machine space or by not moving at all. pl_data->feed_rate = PARKING_PULLOUT_RATE; - pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state - pl_data->spindle_speed = restore_spindle_speed; + pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state + pl_data->spindle_speed = restore_spindle_speed; mc_parking_motion(restore_target, pl_data); } } @@ -748,7 +748,7 @@ static void protocol_exec_rt_suspend() } } - + #ifdef SLEEP_ENABLE // Check for sleep conditions and execute auto-park, if timeout duration elapses. // Sleep is valid for both hold and door states, if the spindle or coolant are on or diff --git a/grbl/report.c b/grbl/report.c index aa8ac41fd..416517048 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -439,6 +439,8 @@ void report_build_info(char *line) print_uint8_base10(BLOCK_BUFFER_SIZE-1); serial_write(','); print_uint8_base10(RX_BUFFER_SIZE); + serial_write(','); + print_uint8_base10(settings.flags); report_util_feedback_line_feed(); } From 485ce1df27ad383d37ca7ac40e762b5a0859775c Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 15 May 2019 21:59:56 +0200 Subject: [PATCH 031/101] Adding SORT_REPORT_BY_AXIS_NAME and REPORT_VALUE_FOR_AXIS_NAME_ONCE options in config.h --- grbl/config.h | 25 ++++++++ grbl/grbl.h | 4 +- grbl/main.c | 34 ++++++++++- grbl/report.c | 160 +++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 204 insertions(+), 19 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 8eb35e882..b0fbe9e20 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -82,6 +82,31 @@ #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif +// Renaming axis doesn't change their number. By default, the status report give axis values in +// the order of their number. Some graphical interface are not able to affect axis values reported +// by Grbl to the corect axis name. +// Uncomment to enable sorting of axis values by axis_names rather than by axis number. Default disabled. +// If this option is enabled, the sorting order will be X, Y, Z, U, V, W, A, B and C as defined below. +//#define SORT_REPORT_BY_AXIS_NAME +//#define AXIS_NAME_SORT_ORDER {'X', 'Y', 'Z', 'U', 'V', 'W', 'A', 'B', 'C'} + +#ifdef SORT_REPORT_BY_AXIS_NAME + #ifndef AXIS_NAME_SORT_ORDER + #error You must define AXIS_NAME_SORT_ORDER to use SORT_REPORT_BY_AXIS_NAME + #endif +#endif + +// By default, Grbl report all values of each axis. When cloning axis with more than one axis with +// the same name, Grbl reports the values several times for the same axis_name if it is cloned. +// Uncomment to enable report of axis values only one time by axis_names in case of clones axis. +//#define REPORT_VALUE_FOR_AXIS_NAME_ONCE + +#ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + #ifndef SORT_REPORT_BY_AXIS_NAME + #error You must define SORT_REPORT_BY_AXIS_NAME to use REPORT_VALUE_FOR_AXIS_NAME_ONCE + #endif +#endif + // Define realtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be diff --git a/grbl/grbl.h b/grbl/grbl.h index 6c226316a..f62200ce4 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1k" -#define GRBL_VERSION_BUILD "20190514" +#define GRBL_VERSION "1.1l" +#define GRBL_VERSION_BUILD "20190515" // Define standard libraries used by Grbl. #include diff --git a/grbl/main.c b/grbl/main.c index ae47a1010..5688bd42e 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -43,7 +43,9 @@ uint8_t axis_W_mask = 0; // Global mask for axis C bits #ifdef DEBUG volatile uint8_t sys_rt_exec_debug; #endif - +#ifdef SORT_REPORT_BY_AXIS_NAME + uint8_t n_axis_report; +#endif int main(void) { @@ -171,6 +173,36 @@ int main(void) if (AXIS_6_NAME == 'W') axis_W_mask |= (1< 3 + if ((AXIS_4_NAME != AXIS_3_NAME) && (AXIS_4_NAME != AXIS_2_NAME) && (AXIS_4_NAME != AXIS_1_NAME)) { + n_axis_report++; + } + #endif + #if N_AXIS > 4 + if ((AXIS_5_NAME != AXIS_4_NAME) && (AXIS_5_NAME != AXIS_3_NAME) && (AXIS_5_NAME != AXIS_2_NAME) && (AXIS_5_NAME != AXIS_1_NAME)) { + n_axis_report++; + } + #endif + #if N_AXIS > 5 + if ((AXIS_6_NAME != AXIS_5_NAME) && (AXIS_6_NAME != AXIS_4_NAME) && (AXIS_6_NAME != AXIS_3_NAME) && (AXIS_6_NAME != AXIS_2_NAME) && (AXIS_6_NAME != AXIS_1_NAME)) { + n_axis_report++; + } + #endif + #else + n_axis_report = N_AXIS; + #endif + #endif + memset(sys_position,0,sizeof(sys_position)); // Clear machine position. sei(); // Enable interrupts diff --git a/grbl/report.c b/grbl/report.c index 416517048..ae87d7b73 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -37,12 +37,84 @@ static void report_util_feedback_line_feed() { serial_write(']'); report_util_li static void report_util_gcode_modes_G() { printPgmString(PSTR(" G")); } static void report_util_gcode_modes_M() { printPgmString(PSTR(" M")); } // static void report_util_comment_line_feed() { serial_write(')'); report_util_line_feed(); } + static void report_util_axis_values(float *axis_value) { - uint8_t idx; - for (idx=0; idx3 + if (AXIS_4_NAME == axis_name_order[i]) { + printFloat_CoordValue(axis_value[3]); + n_report++; + if (n_report < (n_axis_report)) { serial_write(','); } + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + #if N_AXIS >4 + if (AXIS_5_NAME == axis_name_order[i]) { + printFloat_CoordValue(axis_value[4]); + n_report++; + if (n_report < (n_axis_report)) { serial_write(','); } + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + #if N_AXIS >5 + if (AXIS_6_NAME == axis_name_order[i]) { + printFloat_CoordValue(axis_value[5]); + n_report++; + if (n_report < (n_axis_report)) { serial_write(','); } + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + } + #else // #ifdef SORT_REPORT_BY_AXIS_NAME + for (uint8_t idx=0; idx 3 - serial_write(AXIS_4_NAME); - #endif - #if N_AXIS > 4 - serial_write(AXIS_5_NAME); - #endif - #if N_AXIS > 5 - serial_write(AXIS_6_NAME); + + // If this option is enabled, the sorting order will be X, Y, Z, U, V, W, A, B and C. + // defined by AXIS_NAME_SORT_ORDER + #ifdef SORT_REPORT_BY_AXIS_NAME + // Output axis_names in order of AXIS_NAME_SORT_ORDER et traiter REPORT_VALUE_FOR_AXIS_NAME_ONCE + for (int i=0; i<9; i++) { + if (AXIS_1_NAME == axis_name_order[i]) { + serial_write(AXIS_1_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + if (AXIS_2_NAME == axis_name_order[i]) { + serial_write(AXIS_2_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + if (AXIS_3_NAME == axis_name_order[i]) { + serial_write(AXIS_3_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #if N_AXIS > 3 + if (AXIS_4_NAME == axis_name_order[i]) { + serial_write(AXIS_4_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + #if N_AXIS > 4 + if (AXIS_5_NAME == axis_name_order[i]) { + serial_write(AXIS_5_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + #if N_AXIS > 5 + if (AXIS_6_NAME == axis_name_order[i]) { + serial_write(AXIS_6_NAME); + #ifdef REPORT_VALUE_FOR_AXIS_NAME_ONCE + axis_name_order[i] = '\0'; + #endif + } + #endif + } + #else + // Output axis_names in order of axis number + serial_write(AXIS_1_NAME); + serial_write(AXIS_2_NAME); + serial_write(AXIS_3_NAME); + #if N_AXIS > 3 + serial_write(AXIS_4_NAME); + #endif + #if N_AXIS > 4 + serial_write(AXIS_5_NAME); + #endif + #if N_AXIS > 5 + serial_write(AXIS_6_NAME); + #endif #endif report_util_feedback_line_feed(); printPgmString(PSTR("[OPT:")); // Generate compile-time build option list From f00edee4875363a4a82c9f4fa4c24784f457ef7f Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 5 Jun 2019 11:36:40 +0200 Subject: [PATCH 032/101] Issue #55 corrected --- grbl/config.h | 2 +- grbl/gcode.c | 31 ------ grbl/grbl.h | 2 +- grbl/main.c | 272 +++++++++++++++++++++++++++++++++++++--------- grbl/nuts_bolts.c | 38 ++++--- grbl/report.c | 6 +- grbl/report.h | 2 +- grbl/system.h | 1 + 8 files changed, 251 insertions(+), 103 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index b0fbe9e20..c7ba0aa7b 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -84,7 +84,7 @@ // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported -// by Grbl to the corect axis name. +// by Grbl to the correct axis name. // Uncomment to enable sorting of axis values by axis_names rather than by axis number. Default disabled. // If this option is enabled, the sorting order will be X, Y, Z, U, V, W, A, B and C as defined below. //#define SORT_REPORT_BY_AXIS_NAME diff --git a/grbl/gcode.c b/grbl/gcode.c index e3bab3734..61f146ec1 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -712,37 +712,6 @@ uint8_t gc_execute_line(char *line) } - // Pour debug - #ifdef DEBUG - printPgmString(PSTR("axis_0_mask = ")); - print_uint8_base2_ndigit(axis_0_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_1_mask = ")); - print_uint8_base2_ndigit(axis_1_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_linear_mask = ")); - print_uint8_base2_ndigit(axis_linear_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_a_mask = ")); - print_uint8_base2_ndigit(axis_a_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_b_mask = ")); - print_uint8_base2_ndigit(axis_b_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_c_mask = ")); - print_uint8_base2_ndigit(axis_c_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_u_mask = ")); - print_uint8_base2_ndigit(axis_u_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_v_mask = ")); - print_uint8_base2_ndigit(axis_v_mask, 8); - printPgmString(PSTR("\r\n")); - printPgmString(PSTR("axis_w_mask = ")); - print_uint8_base2_ndigit(axis_w_mask, 8); - printPgmString(PSTR("\r\n")); - #endif - // [12. Set length units ]: N/A // Pre-convert XYZ coordinate values to millimeters, if applicable. uint8_t idx; diff --git a/grbl/grbl.h b/grbl/grbl.h index f62200ce4..e14e5fdf6 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1l" -#define GRBL_VERSION_BUILD "20190515" +#define GRBL_VERSION_BUILD "20190605" // Define standard libraries used by Grbl. #include diff --git a/grbl/main.c b/grbl/main.c index 5688bd42e..1b879cdd3 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -40,6 +40,7 @@ uint8_t axis_C_mask = 0; // Global mask for axis C bits uint8_t axis_U_mask = 0; // Global mask for axis C bits uint8_t axis_V_mask = 0; // Global mask for axis C bits uint8_t axis_W_mask = 0; // Global mask for axis C bits +unsigned char axis_name[N_AXIS]; // Global table of axis names #ifdef DEBUG volatile uint8_t sys_rt_exec_debug; #endif @@ -56,121 +57,284 @@ int main(void) system_init(); // Configure pinout pins and pin-change interrupt // Initialize axis mask bits (ability to axis renaming and cloning) - if (AXIS_1_NAME == 'X') axis_X_mask |= (1< 0) { - if (sys.abort) { return; } - if (mode == DELAY_MODE_DWELL) { - protocol_execute_realtime(); - } else { // DELAY_MODE_SYS_SUSPEND - // Execute rt_system() only to avoid nesting suspend loops. - protocol_exec_rt_system(); - if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens. - } - _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment - } + uint16_t i = ceil(1000/DWELL_TIME_STEP*seconds); + while (i-- > 0) { + if (sys.abort) { return; } + if (mode == DELAY_MODE_DWELL) { + protocol_execute_realtime(); + } else { // DELAY_MODE_SYS_SUSPEND + // Execute rt_system() only to avoid nesting suspend loops. + protocol_exec_rt_system(); + if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens. + } + _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment + } } @@ -164,11 +164,21 @@ float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); } float convert_delta_vector_to_unit_vector(float *vector) { - uint8_t idx; + uint8_t idx, j; + bool isclone; float magnitude = 0.0; for (idx=0; idx Date: Sun, 24 Nov 2019 09:48:38 +0100 Subject: [PATCH 033/101] Add files via upload --- doc/images/arduino-mega-pinout-diagram.png | Bin 0 -> 179425 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/arduino-mega-pinout-diagram.png diff --git a/doc/images/arduino-mega-pinout-diagram.png b/doc/images/arduino-mega-pinout-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..b4d3f719bc27b356bcb0cfb7efe3cff85a93396b GIT binary patch literal 179425 zcmcG$XIN9)^9G8da+HG{yL1~yx{8!Q07Vo53DSj76r}eA2%)No6+sC_Ab?7f7NmxR zW}}2IEkJ+>gc=ecG$91;4jg~ye?Q(2*XQvOvUk?3J?ovBcgps~9jG?n0l@=YTwHuQ zx3rA7xb|M+;^Hp;Yd7%8Ifs3tz(0GOuIXRn;wp;f-MGIO_{Bp|v zmXSUeSKxUru7^*!xYmJ>9!_y_`Ca7Vnz_ftrSgu8>x4IkUZ4UZ`rV_?B3xaa%KLs_Y;TSo_ev*fh-y2>F zRQ>1LKl1t!UlcFy5=uI}=cv|27n3jhl8;{dVRC5q{@q%8OpXejJ8i16@Qe}c(tviB>|h_{CR(L1LV1D=VvbH!JQu- zqPhS7{^S?+UkU${_5b0M{@=D5n*mE5(G<~B&*9H&L&8d@y~4(V_A7%qtD0zpv1~Oq z=35gqgf}s#nn0lt<}u5Ie=cf>sy*Lz*x_VD$vD@)pW)GAL0s&!O_xkJhm?ZVc7b>} z>tHaSzV)qsZDUQeC0+KsoujEuSr|Bv5%z|2u6`lWA`b8 z|JA%Q`0X>a6v-ost{GL(jeDS)%P2XnfiS9EsmW7)hHC*s|5YIubh!9*b$;wr--qi2 zG>kxwOP4aqkp)F(bvcY>jemu~1FDZ%N29rS@bnNpMm3-chkFdLUp<&Ga?5Nm#z2xZ z%>Tx_SE!^-LiYSw_ZRh~-%m`HYkj~XPmpmV0qshnXWLfFo;H8b|3BftO$ty^G0AJ@ zxr35@nf*HURXPLKTZ^-rQGWy~gP~FAuww-fBqEiF^WgIDuc%d5Igzx_&lq3ncJI-j z`#2lh+l$3H|DyhjLTHpB4h*WL77xVmu2l#i{yW8nPvm$s1w}rZ;_6a1_$2shE$@F9 zK?85lQl2LbNjX_rrN+fNjmWC<{U9EUFb5~z%y$7BUr}lVU)h3kKP-5;1~2c;{MfMU z=O>QY^HfGf^kON?-3S10 z1i$H*f1X1bb#mE2eK?)wB7PKZ+ngOK6pl8hob>91*9UO_*+c+HEclxCWPP;8<;GU! zkmlBK_srviO{r>7TwMCr#V-ZHE_A$QI&Q;5V@;2B?*MJ!!=%_SYjs^Cvnh55Xw7FWR&q+F;*>RTXt?6LzuxfwhdspQ=oLM9TOqB^# z-D^!nqVzbmv)sm?9$#SLG2|Tf{YA-%cVC+8$Ei$2LHA?D?#F^W^#57P0UtOeGab6w zFdd%RE5S(dsRnC6hnt|Xh4D~I0PXKr=RgVWH0!YI1#jcY%>26ciU*ZsE584oViJui zsg1lb9ZDxiwwK-Xn});7Vlk{JdXmK3R0g#?Ort9bjmttee;RK;klAEy9r%l?o6$@N z;WyVSD0PrRk_4m3%0M)%o0S-MrY#fc^uh+uMW zevI7fPiobrn%E;9E`v(Q<4~I)6@h&hMFIyS!I{UHHq|WO+#HVgmJ^h~zKhL$9gnDd zqX-k5Ub_4{R_y}yzWXCx-D^^`&6FcpH!WqVm9v4?OUJ&9&+v80#y0-GTIbRNd#o8YoYj zTf*r%;J;GE9xd&YmGP5qAu4EDtcMoUd`nA*Go$pzsYRya608Js9^_evzi`g>HI9I+ zH-%_izjqtv5{tga=4ZNI!cu)n7IXoLO&IhBd?<6`@U04~ODB&(6jPFKJnbS=GrO)C z;uc2^tDs6BpK<{OK7JE&mDRVhB)M}W_P?VF8WXXrLX-KoNN@Z?$BbUzdxqvViAM9R zJBjySOEv517DqDQeG+8 zAR8-$q?<;i4&Qy6mM}j?1=MqJxAhhFL9+=mQl)N%*B>ta(mp5~SB&Nv`X+yHw;1G3 zW2Xk>_g^2Y7dWeVGcy>yo$&tU@e}-p6s9!0y{B`)X<7^$=%-*7>v{`?5q_VGfn2b{ z2~JOa^7<+86W-)~FRm4133)SO1V(ig=1qn3@ad@TpE%cknHJEw%;B?uwF~JbNROQ7 zgu2OVrIg|zsj1o&#QBn`)NnN|)yzpiQ$b3z7Bp4V#a|dom+DySZGj<~W^E1=e27lC zA1R`07FIG1!tDk?0+IT8h%^Md7B+zyOI$O4m1m-QQe4(wvur{c??zdNiXJ`mJ0U&< zWU)hnKF;tn@jdZ_3sYnRz`_*=4+-@Nf^Ci9ANoyNZ1#m-Z52Nb$^9FyXSkM zNC(pUp9Cx4>6gcrg?iQIc-L}G`Ou)-FBHunzrTFXGcQ~yvm(1XxZ8^uR(6hYDFlU0 zl7s)@5Fm>eX4fy!PRmuMv`T`Q#6E71N;DU;7;UIsV_9sYs(X zp!;Z{hnZ?Yt9Y0%m296i?v_STzQ1v9vd^+t@W`xK=rqy z;L_=YNtW0no22->T>z;B92U>oajdqQ;?Sq`$AWaz)C4pS{~=8JK8dN=;`#T2kVeRf zU({=os+J>W-3+W2K~M1{WFaS5h;2!LL3#Le8!A=OGdlEu z*+LXRe>u*;f1DTITyuNk%FLHX>1rQKR8ie7*6NUo<1c(>FV0yCC?oFGMSxLPlv2Bu z12jLxurfxb(z69c{*@v35-kQfIgq{{4+5_}ZS}gTv6p80XYD%`sW!SIzduL`w!3s2 zzCGBYk&brDyXwq75;d`rUQ#&i@CF*)+j(Mw`qS;Ut8mD{kFQ>u4QR_LIvI%o58w^Q z41!S|8dcMimP-JCd&GKORsb$Pe~TA0sFK$_pZ={72iyaA{`?=uvDu~k?q>UkXK#v3 zIo!D`I(ym@T)1MG5T)W;`o*Rb>tVeXQkn}ZUTfaKX@j(zmNu~0a$Oaqsw>4u31>4O;!A&Ya- z&`{A9r+<}uzL7?#LLSyZElWd1hkE5~<=lU*_IdUlCPfmGAK>?@qKq@DG%|}0rmz=w zY3}$ie-P4gxTC@_%j4%~e?IP0dBN|Fug5eavifVdHkX7vX7Z?sNAeX}OJmLK1?MO=mn)SM%Bap;UVGD=+zSIZ z(8EB6i2BW96?4}#VxA1TpgPpe8dKui*T#fvi)>uQcWf!nd4!w5-A#7W_sra+cgg80olc;$=*yj@gYe~@hu~-cSPnX@m=c`6^c(QfVuRx1rO}{v z(o-l_!Lkq;z7o&2*__S&LX_baV;?PLRxIwgEzSbKSCvshNO(^g4^qc(LIR4_3hb~6 zx&>#R%JFpGK>!EIz0?e~EjFjWPr_SHWAp)52G#`xK%&=(a`-7DG`(+)$bGst@OK#y&e=Db!`k9`7SR-l|Kk*{ zZ7}?Av$%r0rdxwRpuF`$znP?7YZy`|bFQ!EO|+m-b?Mqy9#KXQ+bVl}J4l15#S6!z z(qLt|UtQLKfUetYZ6EQ(3hSwf9m0B8H8*go5&y0HL3w?-rp!$+^GDYBk$M9oq|XUx zBgo)gG2z1ktzBc_GXq5|FCZDz7M1&H$_j1GvynYEyknliPO1VSL$5=Fs=IXsYND`1 zObZUvr(#2YTCEIQehZR*LT5dyi69;&2IkgFxLC~Nqk|i zsGSh)xs+M=+3oQ2zjK6<;Bi?E^)o$gm^Z321}?Kt1~W&?F6XdUlql2*j*0zG)SjIu zIs!<(y3CyV*pQ&Mc002nWL0BnZPJ$MGD`iwcZgrIpUD6ItTt!qP5fNng1NdrrpnU3 z!7bTQKgDQ^IwnB&vMh0Js<^bCn7L5H??0@BjMwBHbmjQ`s+6kom6uB^+&l**uO1Cr znN2Bf2a-3l@F_F-IgItUY)SOUHHJ8z)$oB4+gQi;(ry9xkAW$N}0HVzSueDuXTz`+z|O2Gtf_ zQLQfOoDL0g7{iZnV*Fa7E0ixj|M)LV6DXmV7&qExH@2OZKC+zJ{Im!geXc7CHw~X&5H5Tx8=z8qa3r? zVNZpYzT+*3a63r;xV${l6T98&;H?fPQM;9-1r zxkhH`k&2l;y2=ME%T!Q9b==}qxefBxY7b?LA|6RoDb>SsbB@OaDjU*TY`SuQzGTsH z0KxpYp>#?hetZIQDVcsdb9vLEh@l}9x|Dc&-bz@c;^)n^-1}hAx668{d^1X&mNT@?Htcxh#E&)of z!d)IInBTf%G!odE{YE{Ile}=?({UJz{Si)MINf-xufgAu=fI2Xke1kksT#y=jPSO5 zi#2s12p$Z-nB(Oso5nTl72e9N4_^En-7?|uw?8fA{rjTITH_JS*yhL(l@86fCR%Sg zf2ujnq!t3Z7x#xCoj zVxTPe5RHpLJ5AfAuup$tpSAIXrgqi=j(hh$sGPFhtH1L1eGMC)MK5ZAYXAUq%=3mX zoQ*&HK)U72`0#qNq&J)<`Yt3&6pYtMM$)ujsi1U5Bhlm&b;LL!^KVwX@$y^0D4RkY z^-6(J?#X=^k9d`tyE$z&IH2qnn@Zj5`=tz;waIXWR3UCCe9IR>p;E`IpUkE5D1swks2MJHXb|MAkk>ar{} zsAqSnV^82qKiFJ;#)Htd7gK*=aP%iJZplBKwZ0EqYr5q0!r>=C`QbJwrcn*ot z2KNQ<-t?8{COsZzlKRq6{h-!5Vsk4|e1yKWY&%q3Y9+^e!HQE+lq*K-nV~NauT4BE zOUr;x6pT^V4bJ$dsTB1dWoGak-;~kMEiy=Z9`)bZLVlxfhy7YROHg22wR+ACxECM0 z#882XeyRvL)ATaWb;!(eE%1H>jbk!h^$lJU z`}E*}px`g&072kjx&mqww+H~V#Q-=dunJ0dpQ?-ITIv|5d&v z*1LL|WW9ujPwVG6to-LU)+FfUEn4liH9>#Hq6gZORQ7`8?EbF)wZRK@t7UDihv`aD z`MWo^!SFT(k`sh5LK)?MudE8Z*?t?&oYgXrBNDq*U2xEmK;Nse872BT2gd&RJ_ePg z-j@Tx2mhv`2eu}{HnYxsxK7*S3I#g=VG6)N{TMH!Ge@^MPDakOg)u*;!s9T@ar>`k zsYPW|Kyk20g^rO=KZbjPZg8ASPEv~xGCV*^cS`oI3a0`6@kMV$0FrVx|wGOlIfOb`8%5OF@{8@&{Rn9`7Tx<7DGjST& zNw@iKG96Rmim7@iI;W!q${9T+QIyub3$XC37DdZF<|j1Poxwb5hqeiemI8RK8sRu? zB8Sgk$+5PLldNxjmyS5uF}HgsI750bvh%&%A1hgEizFMarpPP48or5v1dRj+8mYOQ zQ33YmP`9;`wuMOKvE)!7Ww4<>RQ)q$Z{{(8;%=*Ojw;}!^(y5qIn7?p! z+S!alv*?2(Bsm~-1FnJdU5<~NpmuF7X_>@4vr^p1@TYsMX{Nk~^VT z^HCw%b9i|5o9*U>W%5R>O;Yt2vs7QZ@>96`S2(p9*bgH5{6+qmPxji*tK`@YVjMQw z!=kT(wx)t=T5<%cp2u`Rn2ZGgeB0ENV@eG~`;_2erk|0sOk-wQzJ|!M@m$nN)t}PV zt**42+Dhi!5kV4O3Gr^%UfYv7`^3efLEwTkZUg2D=Ij>-Bs2b98e~AKMjQjl@vgpW zz-cc60lQ(FI`}X@oz}yP_b3gkeAkp)?wVfe^bnhAzco#G0HYyP@Vtq5#qDx&61pMY zlm{vbOJI-;v)dn=!9md}KMN%ALH<4f;=QXc+C7T^Fug<6I5`Sb)Q@TLD($U?6m@t# z-B7|uN(;$%u1_~v>J?UT69(}0lpiXbZU*WRR5Y(C^dQ8+UsfqAFyDV)jR^U?Yn{vp zRfiS1@)D?uXCK9$xXn7EhWfvIhYb*=&ZziL{X?UgRbYQqbX%s>qcMZ=Wj1mS6HRH^ z%6J8Y$F|y=i0RX`zJ35ls*@aJr@|VE#*Q1Z?}OC}<)uA=PzDCv^A2m@Cj0!~bh|wI zF;-C`1=jpmhIN9vflR9`FXO{DxrcQS?VD>0RMY^kJoVQI;Cx~QYSA|@%f+Kl;Jk-w z=>QV|Kx2+Y8=TbGOxvPtt+b?VMFK<|T53fnZ0h=Mx4bX1LEti*m$*a=%7Bc6%g zeJ836J~}~_I$rhJcS10@tW^b{`IQ?)GRP@o@5;IC7o_ArP;xKRgYoQz8b{}cFc8H@ z!hXzi)g~4JZ5AC(Ux^BIihG4O~(Eue4%})k1@SJe6vpE{74m zTx>UzSD24#$5<(<&)Pa#hcK;wyTaPgxX^9iF-k4svjYw43Vu*afA4704@ATafo?uN z$J84!i)!C9zAg6^b=jYteiD9p4`8MMSZsWt*nrg(t9{*T zgzE8+PK8}vwa~Z*zd^1NXv?+TCbmpQc`XCQt0zm1_6XtUFjZ%$Xe&Is!a3IY*<@^n zPaYVhI{=oGX%BcR5kV`8hA?NMM18|2cQ6_^MLoWENiv}&Pc^Vf8)L^*M)j!TQ59ft z8O`GyR#Y+HUcc4aK+W9p^juG+L3WZL$LWkJo=MKPRczgzylpgUhG1s2Egn2p z)Sw=DVfJrhpvyK-X~6_{6+SEbBZ)@sEviDcr#2<(m6y0PpAl3%`~tJ1l5GZW(N&Z; zi^tP1O-jeU)N2GC3&fI4gWW&7F0(@kI5l(yRG{yYP`TT4H`bTuNyWH4s(rAw`CWTlzUEbHE6 z2*OEUKq>1FM9;0xKiL%q-%HnhcNVL)pBp%5$gn7Sj{=cc@|Q;p!VzM&Vnefd z9z{#FF|~=UObUH2J-#527GCi{-4^_?e2fZ8>y}m99I4$j$5b6o8N3a8If9S+6m2dhR>R6XJSHoqKGp=v=C{p?IFvF`y}42O=@ci~v@wOIUO`$OpOk)q z*3M@7w_z4Ffi9p<1WjiWeYi;eyO5`&9{^*I1|mW<#ccu@}g&puUe{yxmRy&pjTH7giCKhLtAr@QfgR>CUO#sK2&C$`Ch2E;6^wn zJW2HtjgR^)#5hc=8VFWHa@>3Y8roDg72NqOSnBsx0M>+{y}se?c%>cbnS-*!ed|d& z6}*@#c*@{YqXZWgcdlMDde+hs8#R@C&uP%_`5B=Ew1&4{xI>#lJ{GXxw77U03^U8`=jZcp6$q z?}cq9O{I$CJS|>3lo*0Yl`1dggGIXO`UhsDhaeVi7;thcp}K$VQ}s9NS6!jmy@AcP zWA2o;!IXCbHAn6h7`}Uwtf-x0l?)3?$*?wbpNd)w>NRtn3;QQf0P{iBiCttWs zbo7afjVRs26%wQtvW21q0o?$!>S^h`9}&^~H*24#XmX9yqJf@WUUiGSq5&hHjD+jqS?dKMGtX-n>rgCi z$!Jcp^j^@xgZBr-&J~pzCGUB**xIi<#dg|N1Z7a3%tAfe4?S*87kh0Tu@%WN&MH9+ zBqV-rDG_I1>5VHzXD_{urY~WSyfL8(hL^-Wa?5P0>$%z*GpjP5cq8!8HH4$q&Qk@? z-#9VteHmfGe|qI_JYwe6=30aS1uLF!=lZ4ck!o=jWQ@(|#yx zTagful3{6D4Z8BM+w>>fI*%BEN+-Q!soTGRc7z;zvFJfrSl~dINjLj|HyJOZ3|e(# zrf84@$!0t~u=B%=7Z1JK%>MQ|HWAu$H6P7maJ(~RaH#(MqdY8*XIC4sz~~_aNH-j2 zsC%V8W?WHxdHI#HVgA8Mt~*1}&DyOYibVpmeT8n^_th+V1xuc;v>H&??p zohCqqTA+j{+8XB(v(ivcQHEfiHlY)!fs?6yI7;*^;VN`<1MRgHFzbCNqG|jd`*!{u z_58~=wlrPX4_;!tR)AnmrONbU_w~Ld`>m{yl*{}zMO#N$;Y6_7LCj#PNX1IIx;K3B ze&uZhe9_}X8#evegnzoUu?MWblJU>LIm_p%CH_N0_0D=h&FyG;h6<;i0a*MmYHjgU zZ>&G2Y9nRviPb*Yw`PG>Bb`GvF02m=ed5DdA$-!>ORR&!7b5vQOMPQN1s#T%?~B&1 zkz~x;$+k<6BSMqwh~YjY_O9>ttL{V_B}U-h0)zh|!|AifozhvTXk;ZL z+SPN6fIm`Fj6h7>^Y!&`Ai5^x_($(A#S4^$Nh4puqbeDcGbD*qjW2id06?7&(a(I? zpA2=K(}ImE9pm%eH2Csnh7QT-gfjX=yqUVb@cxQ31DRIE1GThQM@krtn0=V3Z7L;m?|U8VDX^N0S}3#zOQ}k>8@ClRaU%|0W^$RPJAR6bz6u~ zDz1V)vOWCt3=sGXC7vAUJlw9~y=ctA78i5Ne|DWEiY0hIXwDO@<< zbHSOhB^RK&K9XcS7ms#RDHV$r(cHZspg?d82I#pIGB)Hfs+`mI(|031jS zdQCfjIw77m&QA$`vFEVEu`t#`P)+tu7Qsb*tu@|XpU|F6I2ZI_w%ht{Av5dA20p^H zWO7gkH&BGKKJ-=|h{U;?21$liF7tgq2TYPYuNQ)Dd@ZAyLxVmAGiLY}(?Srepbe?m zL*^A&e8&oX?EnbWbfKny3nx}HrgvaF!Q;M)0^{kFddFtk%>y|pP=ZFbc3aZQJpvq4#m8LJ<`Kht&<)OF!}KD6VczY-QAU+}U#|gckn~>Q zZaNs~K4q1>&neQdvx+|lkYk{FwYd0=c$)WYrR*EDn{4TIo+!8yr?Lr5OMD@gJdGLF z2i$B9cdL%fG3N1E&mTUBC&nd3*va zO#ef!Uvj)vdZh^XcA+&;%jKM}H}f%OuyZ0@j?idCk+8=r0276mXANZ26;G4id-iHT z959LkmHK+J_9O$DO4;%C=?!6!m+IXZt8p4@Rb;{w7?R~Q!mi@M_?aH-V+12gQ;?I` zJL~g>=kV~^0n_8?22SNDZcs5H-klYidPRGna<0mrE|am4lsR}4Y1~%z`5MWGdEkZ` zQ748QD0FhVJ;P9}=vc9Htx`pN+4&q0>f$kcl1{Do$mUfaOUFpuO9cD{4qsX|fb-OQ z*w7Z?$aii&*HEtS3lI;lipmOsbU3w2&SDWQ5xu^4Km2;~CWU>5G}jQeV;+daDidJT z3wTiBG5t}QhPHmMs!VQBq4G*tV8GW3)5Gu{EMi;doNgLFs2%ADr%*G;bz_|5Jb|a_ zCw3hfl5}ezo{kS^)%KWB6+vt9_11sm{wH(^ggZELcvd=!o|b%>Iuns<{uF=*`OVqerYSAywUws2$CS|;SZNPXz2emO7ZGrLY4P~iLp?Vj)@>f{{$aN(veKNijF}L< zQ4vlS&(zgE>}g7yxeb83;xrdm+$6OUIXB&3f)fK87lhRs9)989D$f6SZ1tAYf3me! zH5THDo@m2#uX)_za+}u2eC*}2GV?v4X-%&ia*_kLPh@sA!oLyxVTBQ~tQDZIQF@(T z9#Xk-KxV45^zsTe_feaC#k{n3_jymyPgv}xESJekv|fQnfMRx6sO>@|`ZX}3DKy*a z(O3ag>!3Ev&eBRkZq`M9Y=)eMgR>IX!zN#N^1wiUdUfoBq<9!Ng*fEi7t?u9^;FhYB7xr-~0Nmw+;G3;fl^p?*#Kd95odxpkEE*XjO1!e?dfZEi0I zX+Gq~JyZ@fHC{DU1)Tu#e6j)|jo%aV!Wa5ldk~IQaZvMbkdw>$Zrf#*Lc4>(K3YSK zLw9L`M73R9pYDN>u9Ex-N=Xka2NZjWt;5GcRn&4TveGZ#b<0WvdOaSZG{u%t>hwe! zmAYAOsrc6aS=3P6@MR@MgQ#QHII$ry(pBCXFYUS9R9k2LrTv=ghnDwzRcq_;3HK;d zTJcun7hv?y-VFlqg9fk=&9Oa5*P$m2(0?qf&X8_fPTck?e7bg^#RDeWmj2hR7YzVzW z@)08GYfRrf$aNkSBp%spBO8Gr&8AmFE=@BoZ1hwI_|4}%6R68#28je3A?Z9^`vUD;Eu58m#NSA zg_k2Xrj#f?FfOj#R-#9yxFb-p(aCyY=c{4HC8l~gQEm~KINkhUZ`_YPMX_xwp2Q#( zru((m4;3_2gcA0y#*g=i)8lU-+06H^jC9m7tkTC6@G+-SkYrxy35ut5xsUTXXDh<_Me* z8}){p%LI>ZUdaDKcCmShcDkKy{u0fjMy0D$g<-=uPbX5*7?%87v$FRxQ376;kP zQ;l^f9N%CaKOFXHWI}I@weQYPDAs3ylW+G|Oel6e-si5DIGjKIL)uwFj??}J72Y{F ztQ{R(7+b+NnH4}SFVhDRViyg7mw=$D%FPLJ@3pg!%LkQ4Vl9)N-_%xJm9chn|2}nh zbgwEN`X%*H#quWBN_(M?LA$oU8dbc^kEDfPew6W|ckuLO(M#U&pp8R~K>75}UMTi- ze-iW@!J6bF)0dR-VPMC}nF&!kE1F;Scql7nHYkj$Z2iTFd@kI%$>KLjs+4k^`!!d= zbBQt`9$twR^}Wv~X;kd9Dh>Y>y+&5$<$~8)OUYID`!?dTnizyZa5hD}_ROiVL-cwT z(CS<^8byxNOXj{>2gdj0Nl9T%P;bCM&sawP#*XFrZSf+4g_qA#G`6VZ81E7v7rfE9 zWzH|#dt47Ck?@k=Cn&$8L#1=&i*oCQx= z9_M&IK!;-ogRG>+XMk5Y)(!(T=lR)fu4jH=Mky3126TEQ2$>TJFMG;jmXx`!CVRs_ zNk3q{oOP2RU!25C&I6zJ}zLtLcxgE>SBXd$Hwv~_D(`ArMfwJr-K#3#Q7 zW=*ec_Ur|M^)9aW3Ye-VkG5}=LQH-6`JE?@ zggn}%`FoH2Am2xzxpvbc0gFD#VH&__4@f%$xU=eN21QW8TBB^E{#DR9vZNM2b3_rI z-0PMqk5`f0VKY}v!T5cSBokMC(0LO*Bj4?YLUQ!_6IJf5(Xs3C`=jH$Cr65Xmrt7q zjkhN{avID$Tn}^MaQ%<|?Spv#K*8?TQY*>L`d(5gM!jw)M(BLn!$uGBWKe7|CwE&D zJ;uMpjmJOAV?r#W7@A%=uPvLtBtvgQJa+?g;hb)}NisUD+$$~R-QQN%^QB@g zZCW6;$#^in&Pwk_hIdr zeV5w9lMuA!f)2BpOAO|)<8lSSO*tUpxL@(p$#L=K*EalT(le{kq*fSsi`XjXKHxXi z0YEa^a@DJUSs{Bk#R%bv@2dP2LDk|j) z3n#QVc+<*F#SnqbQ0j^X5c6;Mf6rU(NLb&gV;a8mecE$#B$Ee*{0UF;jI6!--@8Sk zws*DtEJw6TMn?sjMJ+ayibsniW%HGa>Ky=o9=3BZ!uR38k5{u9wK-)}%F zB!BRVk5zpqI>-d@l-b&oW>wt;R4x${oR{c0C&nMK~FUY*p4L5eIU^D-Pfmm@Hpm(tP<9xWvR^QP^E4^!gG6`?T`mUhRPG26`lWQrvO;49X&mvfwyp&>3tSF`u@iH z`j0l1|JP;tD@lanQ6rz^M^#{PfYAT9HPH`=?Wyl7f9Cco>`ih!@3pDU8Jho}uHjG4 zgN^M5b9*`2t}TU~f1$GOe;fCKKW;o!SY2c;$;jkfNg*{`1a0$UiFJR3i4gg~+UB zS(j)-WI&|$(c{=Z%L1wUKZz=6DcELX6KQ83oE1UO%?^ZU=gY6fFV988`gV+J+Xk+c z-n{hd}~5mzVQ$fiS2uM#HhE4MN*mS8W6O71Y|*+iT=j;=t8< zIQ0ap7ymBGg;K`PeuSkh%GS;@4K&uy^=Y|b#nzso4d|!rwaL#VQxmOf>55b9fP*Nw zG`zXaaFU=0@l})}vZ1fULawaKbm>|I)(-sq7YcisjcLazR~#`xU>BwbkaDh5(FVL^ za*L`HD+p1wP!AiRtIi$WGAJbwXukV{qu%rowV~`*uh0^pm8IYOUPTX6!~jw+dQU_V(<;yCN$6MP}? z>H`P=ckCuwYbh?t8)jKa*!1F2%&^V7FOwP116`g9SK=}@P&Qg~G455%Y~yEAPSY}s zLhU#(N?*SrQq%L+uQ-s_$aBe84tc{S)r2GPxI(e@^-l)|!X1a&gx<(sXQaF1^JIah zJAkBTMs^=h>JP8$PBZIe6BuVQ55scBuK0g+Q!Nef^s|S0EFwR)Ox9@(lCtkP-DRC_TgRL#IWjd=;gpnbrk_t>1))Md9m1?I@zf6)?fp zH5hm0mkiM5P4r*JK5m73EB6l+45@7Wv+_bqkAK#T3_7;N`+O64$5K|u<_k1K{DQDy z-1n;L`O!+y=2r+I|31uja2X-H9nitG`aXl}k~?!*eHAM-RrzHE)KD z^J)#mwmVqZ8+=!YuOf1D&n@GYhvfe8O9=J{dzaXe2Q}`v^E>JwrO@%o{i74R{eHtS1 z=B)wJgstI8jBVd4wky$TWZNTNm$>xerCBsfEsfiYKUOZqnSbW})@OxVxxM}`7N*i4 zWj%S@XLX}g^?t%her%oGKl97#1Q@v~qUwrM+RBvm0nc=r;I9)Ovsd^2HAo&J-)rBr zeyNVwPe@?ddGSlw1qtjCdqe4zT(jmjPshgj^V1=0waNwo@#%ZEhoI{7O+t$@R!ZGl zR@JVTu6_jGcCJrZ^#!LpH6&qM|E50}=uQmJh8!0DXj{7(5i)nhYkS&T85}!Iw14$F z#VF|^{y`R`9L!y{mNOc4QZ!CUe9sYsH0ETv#AAPsXvX2r2_-)Ag2No zb_@9Xr_{}hJPIPWE*xQ9vs6u5vrI9t@v?pbA*%uD**ulXaJ{?8+8MV0aUZ;}xE zs0svfp>X=vy>o9aof% zYV4I9@}RKXt!dire4+B=kF~IrqQO9Q7U^SsXqcDVtkdUKO^-WD3gt7jrJ|0HP|-ng z4K{=1UYXZe3_}Vkp2vuMb4VVthLh}ow%nC1iOAkb;Rf~cS{&ZDi6Zx=Nt5_KDd;)YkID2_rT1D;qTpZAJQm?ECfN zd5l53!a9wjFM6$~!v+1iNVE2J+H)~!p{re_ugWvi6LVO@)eibawk*Dq4&=P7c;iO& zdJq}$&z7eW+e-h}nNQC16!>9L7O;EcN~nqPry~KUsD-u5j5eftFiUPuDW7~eZyeeP zbQQbrChxIScm1*4jopc#Tr#87$c39GiD-?Lz9Z+UK`RVIEmpfm&CzoB^F+k15FS+! zkOA&r`x;K!U{<+M5;WXU*Sm=VV|+Wj;zANy{)C`m2vjzG`14xpFC~8xBRX1jZ9u;U zowr(HCV5@-%5bq?b}wz{6X(tyZgl51XpU8xws(jSKDI5DZ$t@wU_0m z4JJ;guA$Cm`_KK9ADajXTqZdG(E9t$r}vxG8CfiJY~~F1(2%?PO6(?e;7+m|sEAFJ zfzEY$__3R$DJK+Efw=2={m=R)u^=T7>D#lpP?zSzevI3PFdI+ewp7~YNmpq)M?UnQqQ{>mO$@y7kE2p^kGze~O9)s;dNj%4esf+4 z-{w0#@5hBhBz^esK@OlOpAy|(&yIF?sVs@9@~ob1lAma??ii0uEm}l3 z96)t$P;1^uW$C9R`u?!tmhCal?R$cMNmlip`h?#XuKzD7d;jfDCgE?Zg{Gv{+s%dQ zYgnD@qI!pza_TCdwAMkD{LW-H>!4eI)6nT5qRfLKrxnQL$FB*_D&w!7ZJ24agA2W$ z$+y`}$bW1Z%N2;$$9N~dD2=p?o1FQ_^Ml~>-P1@*>^^LfqOYcU= zK&pN8#;{5@gNt6nL}2P{dO96%;-fY`-MtnO{k-8BIOClk{u^k4=9ReSK+C{Zqm5NSL_2?ZU4-7->!&{#zB{ z_kz#~6h^L@SpA)LrS^iQ`cmh>0#Y60d-NF5ZDS?!($q@Bzr6naM^>#D-G)v3h!bUCKqI3Qv)c>n`4L-1@b{Y~&&BB# z3hFfwUr0s?f!g0fl_*372ulQU;=IU>tdQk-x01i`Nv{dCcHrgnRv|@9f6gM24wXFR z3RUwPpc2&x7>mA%mpl_gB5Ij(wZHA|)NbybD@o)G%YJn7+p1d2RLK;zuAH$S)mVRw zUrj}cxT(ZaOK9C6xYI;{bgE;bW$^Gs5*qlQB-}=3j2TA9)y5{6uC%+dmHxYHgBni6 z_~UVZTbVM97TloKs;<*Ov+bf@*d`H2p=?@oy^y61wEsLy0rCMu^$#un^l|e<6CO|Z z@3nO|uT0a8#+3{zK|sL;)n=^OsyjukCirPAQ;lT>E@zbc-?h24#B5eU&idZWPHGc@ z2BGLO8ls5_URJ6b6#-j*lWo6Z9f#DdX><6sm3GCduBH#+GaX?VMKxvdl#Je0I>J96 zn8&z|ZWB?#P%8Pi>P|dWzZ~LU9k_~erv(|%*K-xdfd2r7cGtM8K+jx#FOY@3=R18& z>3aFs;n3o7S>Bh$2-;o7NgKgeq#q77?peG}In3V#f?oTg>0l@9%m3c`nzLzg#&v z=e)=3zF+r!zYFr`VbU&Pbw|C8<|vSRf8-39Ff*X1%*=&c`n-P_IX0YrdyqGnnjvR~0x6m$=~X1tWMk`2%yQ z!;T4!cfOll6`ENpwJd-*sLhh4A&j(gkvI=J@LCmzM6 zeE;q55vGc+55`qi{`4n3v&MGuu-=+$M_ z+od|Mfgm1}GLMna3eU_@^=#SO&5&0>+tJSpz?Bb1)HWBnCd7=a71s?Oee_s&@-)!V z^_c08RU;8>sDKghWvrAJ`L*KTLUc~z=c3W#O~BEIDmhBG0-Vy>Gde1p1x3K-DPLPU zYVronl01H241?UReIo<6bwA;Gi@iP`X-x zM~aCM^S0^X-^Qrn{kK1B>TZUkMWmd;u)#K4!2R=A#F0m6Q$BL5ReX>bFK%}0X1!+* zrGXfG=g59#?29tq9ibVUj21BvuOA(l&#`H%NZ)$$2W?uj^EguxU+d?(#hB%}2J~z{ zI|wS`<3@1s6%Mk`zaSe*>7=wPFio;JHh#35>}y#p9Lq>vD7e(D#$C@mulO1;ecbrm z{(5YGFKedIkJ%@#yC+I!&F4XSC#kltwx4%apu5NH2S;g=suL#GN~ zSFKRC{~Y8#dQt8LJ0J!Pdy3Ump+Z(DJ`UFrwX%+b7n$*GU)zY*A=kl~nwAjMs6I)% z);rJFxEM`6Wz}rmIZiYjD2j6w(NCjLObq-9!(_m5X3(BCMS1)qgJG=nciA0iT_Kwq z0!yw^YeU5x+thf%3L;Al;z|0ECf5;S^Ep@E8mH8X{8-<%h;TApe(r5+gLiVT-RN!F zy4oOTIAx+b|WiwhKf^-R1^=eI5kQ*i&^r4c) z`9FP@dmbHr#wHA&kK-|bJ}F@zN_USS)1s!ZvIb-??MvPX4W(^bSvj4c{Ddm}uhU3dip+(+hg3g51*f>&Pk$=3j0--G?c zQ-I=m%1>Wa;5zzNbwR0PyNzDfYmHo6y%)q}BHlN*tAl+wgILd+5nrkbzCvTQV>|si zZ8(ZGr`C?xTNB0?$^qFK z&;+@sgMCjvpx}XZdQ3Nn(VXW8K=R##U^^%!xhqD3QRiypBnr{tZ4z`RO`!4n7*2AU zkT29Jg0ZFejljz`2L6DYU$m@E=T6VyIBUW9skLzy&rE{sI5AwGfIS+jNWcHK08N1U zTsa_`#w-D9yS{v=e_Lk%k{E@8L3vRQ!L_n>Kj7!w5jek%c{abmcW6WIx+0}Sk2sp3 zp_pa9+)H)Yc=zSIV;$T3Tn_N;u9xLw%R{^!Vmm_iZ7-8KOgL0_?HjIVtDOFiiy&iE zZ;dDBRmjhk?@eHyK3~OS+h)cpCOQnl@(*@6X{BA+gu?@7xjqBh{iWvzZOmI&+D8c7 z+|X>B*#qqjZ0!XU16j~n{@2mf)*RM)F_aD)&uG9`O*}hpkLb=`ZEE8y@*AMdUzMQ8 z<&RL@mD;i4)rUa&r-J?Ispt(DqJ0xzcJzWl%=8*2GkIpi!ROm+ z(RWAm%E^F8K)Ea+u%_HExZ&Au$EnDGC$Qiq<6`3+mS{{YAcfPc<3@Ndld<^8g~ zA|w28`MzO9?WN(CQ3dQfd{?%1T92Qgi>d}BO)3S=$H}r~4op?8wFAg-oN=Gjqu|Aere_?WG?3|F#iy>#k*kv2Q^71m) zZJMG8c-uXX6ChUKTJ1l+Uoa}V9ru1ujRwba?e|o$=1Sq0OaVKeJR*1&ZW8jNd|ICJ=H zNS`2y!}v#b%{2ZcA8OKKe$oErt0C;oalmYwt-n4)ZXLcm z*Ti*^RZuWbcD5tn*DuqjDwg}-gmC29TgQPDdtwzCSJoV4=9>6^D|&a_D@1QE4jlBM zb%Y+Y-KC_0yp%HawC7lJYHPOQPJY-Qo;=7B=}!9#6!!ls^#?c+ysttBm`~+=L-+Y; zs~TRuNqWlw*tr{z-UVA?QbWAq{$lZ;kp;O616lAMywi^7b7d2P^XiYZpprzn`1ZA< zu=P+K$gZ{Oy`%erNIG{L3Xcq`-;dRiw#HpGxg-by+;Y-jxN>xNVHX;C^w#)?*UK#j zq4^Cykkeww0}TpU0#0&#W0JY~JXzjOSQZGSoJu%s@zvV^`FwuAb|QSu7@U&JN? z)py2mdxo5?3s>qE;uRD;3l7mqD&m`HN3Lm#S9`3bfc2}<$&C4RxOc@@kw21id~GfF z98urE_4kNEvUV51k{bk*m`-tjwGY5WMG$>zFfMv&wE1%7P{3C{zEOPE zP@zSdqR%l+u*3fRQ@$bo3~F5+z70y5(+A@CuJm&O1nSc4Dmqz$!ZFlZ7U_i4WVobhlnFCT>bz5sO}PCvg(VV~PxKz-%%vtN=4T=&@=AaJIR z6vaMb>d&y|H43Ks;9U4q{f-7c0f1Fl2OIVAX!e}#&z+>MO? zjIKcKm!s;w;bS~&{i4jkE+$v4*k;WE(16c0l7ew^Gr-X;0{NL z>Q@?nDAcjqwWxKLv`Fs7Es(CH(HRQ~{=2XNr&2yt#qkUI{R7Z4=*>be(~t`FvweiS ziy-r(J9fiB12#_@Ab~8z3o$z2vL5ZwSzCdqenerPsk74kbh+8 zpB9*e*6#oys}knIE@nsG4D7bcx!|2EpoWgi3GjND2Lz*N!}yZ#NZfQ5fc^hh4kMeM z!LE6-m$VUOvfF{mU)Xx7iunFBQ23WF#4f|-9kqLaIF){_hzgDLnarn9%oL@^Mf(Zg zADuey>1IS4W`Fl(CM&P9ncMWhpPFgbTJl=Y>^C~mcWZu2=jd-#7<#zEw8p;lr|!I^VvhB229F`Le!aiKb*au_c1fr6ozm6H+`2)GbcIbRryHTi=4wN2_0|MV)M4dMzGhv!p5<1zJ4G5R@fSo@M$2~1eSRJ?y zxF)aEUuaq69*h6bX}S}lc6u4u)31QU057Q75=H-U&iNY>IlAL9^VdFI&*3rTPJS;r z=YZ?zJ4^E`CBK%}&(v^H>}*P1uZ~eIS|ZH^Jn$x#QT1N)ekM>}~wSlLQZZkLbB z_~1L_6sX1;6@aZX+g8j%jK%T<4|{7IJ^$Zd?Mn-~BwBhRp( zP4J@1l{+wxwpkdR#a%&OO^r=0+~Qbj8Q5cD!48Y7w^QN{g9itrwxknmx}_;V%+5M9 zSBuq0e;j7sf?Xi4a;!sgCS;I6qg2=-=PGm~JUwZLMpL5|efoR+<@Nl)cfx32HVj-b z)+i><^}Iup#H=OlFA0!gSe)O}*yUryFmdX3AEY`dWNh!>{W+Wd5j~JaS{X zrS_mfK0hshJ@_{8yfc#ynht|BAXrhKuD5d_{Y49o_0F7@>o5ROcvsyyPGl{`Qt&_H))9!*2Y+g2=pcIQ zZaV`U!XSG~iJh12{P-w)qx z;vKiI%pS^|^zE<`{Je&LME};V|BKc$+Z!U@U%af&%AX&AYVlk*xP^GEuSAb4w6q#( zC0VDEC7|*JezhMU&Y2pVqeMed^E4Fn`u~N(Vt^hue1?K>iD$!gP&zu-+tHkD#7*doz3{(H?BiIc1nYEgYP30uxs-IuO6^ z3T0OgUmg6-*%TTh0+Z<^u3oWTZYNHwG*ZN2GUeJf!S+YhPy^_8?@{c3sTX_du@io} zJ)<%&TN0awctL-|Grc$s21p#9Fk{s{*1nzr)Cy8 z9k{k;Qo9ttCZ+iGCr!hCvGMIUA8{Bd`b<}wD+0sjjEDzPrL!s8C2HuIix2KY1y#;^ z>!U_lXJo1_=seKSVFz-DpEM6|HUNy6Tw8TC&VIRCdeYfLK3M5!+>0ni>!!W0}2R3EkZyv%7mM}1Q>a9WsE5`1#T*#u;&InR(dCGHBKfc zE9$#{ovB`{UbGs&!t^bJT6x^#pbF@9K5LulZ;i-*eQ=y}_P~?7(g~otonvI0e(v#o z0bt*CLQ-ytT#4H-25tnl-c2=5*~jnhT71G)i(vbnEw%F_VeCsUbHw-sj~vcYnLR6e z`f(y5EZn-Hg7JvGAx*S9U_3L^SeuF@I3GP3HZ6f*Z30C8hmz0VC1y<>R#6Sw0jM0$ zjpLg&ZY9x|P6$ev7nqHT5g;|CUp6dp&;W~}NcU#}WC;@UkU=$HcaP+Dk6d}GQQJ4O z0rB;PZEvp_hud!n2ZKKG6WYTcy^{iN9y$mPRlqN%L>b#J85qp1tPD8&I;v9zimWTg zD?nQzRp{Zl7>n{rp;GUBiC&91t$B(emp^8_1y&yL!xi$4e}X#xk6E9^MQa zFSgk`pX?=@dW{LfSVjo+s(tm4Fvf)|W@x!BEKcc^>rZsR`$U=kuC@jm4)ozU`RZmC zxS8Zl!@cXsF&Uu41^7kPXm-D0zx!K0alu$2DB|av!OZloHVM6eHRIk8P5}yLCAB|O zbIwTc$p7d^7UCbR@9VK(;>z^i)@qu*%CGkWGFG8U0^;`p7*|ito!dEno9d!J=9@1w zp17wYh{Jb%xokdAti7-*M)eZXr=1&)RWp3JCy4R$pPOQ_=X?3^W%fLDRs_cI&R&Ky9F_Tc*JLe5?l^9y5$bA~eou@VJ7G~uJ^IAXE0ph}b>!0US)XFYd-k>{G~x= z`u^SL+AGHy4c3~OGZA$FGKqj~9gYx{F!(ndU zg*Ri(nO*~W|8@|+u@uGD+1zsnDP^4WO4QRWA)*mu9I#?P|LA6WkVc)=BX zSQ{KSn>TE^YeNNBt+2~qaBAQkcted0UB@HOp zYDljJBe`Jh`L_pLhB6k@?8zSEc_|}f+Z#_$L(DEgu)FTec%@KI$!60|+9)Ay?4cUA zWAgN;)oinQX^zVuyS@&d_2x8J{Yy*@HvxN)A-r#Pt^zvidz|6~$M4#V*&h^H(qOF9SYW(^ z9q7h6oc`kX-`)CZUOZOo`{nU(v1>@)e>VhlX-55~W89h9wom0X6UAN?Ve>ruvH5|i z*xCM9!QwZ^4@skyqQId5Emu&CS2pcmeMaZsnjkil$mp}RKn~UN`p*zWxCW z*~tmmQ9me;e-^@?ogKW9xdy9mHp1LUOK^UlXP>P+8H@EPs==Z9g_f z1Eghy412)*`(OZrJc56lIwwY!*~S_PV!T^efS%$u0Q}0ApfgRIG8?6KQJ5v0!Qz_$ z0;G=BA`joD@BpbSSwoirCTx4%{Eem2sB5qy{^$5(2#qK0*qDAI_6Li6di^v~vFzK~C z?C=mQCPGVr#-XOa08R^_J)OsJusM4k-$W+Ny4$6jq>@+Oq}|V_xA?Mk_1C0Ww>Gkr z%K1t&IlR_JATjyJ$637L#VG~o}@w_=JDi>5d#+9 zq)Fz}GlJ9?-k4Q-BZ9$MiDZ2`R1Ht~>co>&Q`Vua%(iF`3cn2T=^0j4MUKtPI_4WhHQ*U0WMs z?;EgJ|AAIeFTXsvutt$Po5J0vEpc$iJrRW0FC6J*g5`D$^<$}GU=g#l3hH(z#-&wi zKJd^Swfe-;eCv)Nj$HK$dY*rI6fRPc9^XCe+5a7s|FV({AiUgP3Ki5BCOvQxzxOKY zK+QxN3a%(v^E4#TIR3p}VZP+k+F*Wqua%fGuKHy#HG76DDs_+1=@)VIhHDEUiC>vx zq#qhg6GrBw*ZQetJ3UH&B11vl>nyN+V{QFYw#>D*KLr3usTk_VAa?}!>#_C|b5W&c z@6hlsn?QZA*7CNut$u0nWg3vhT9XX9P~zN(H*P_|c|f$?=_vB+1vl;mZA*ywYURuE zuhun_lotXJ8r!n1qI;p6F!rm5r*wRNOC$jh9|0~ZsXXJG|E<4Q!G6licJv)u+PKSwFE{nK|`;*iy@d?Ws8>msh7n^g+}*hIj| z0PJ+yH&(aoRM9Es?#pML&%Xk4NGk8L8LNpUE9M_31@vjAhP)XSvX{^|;)KWZAroEe zL-EXc&5C6kRd)<##s>Q8qZ%@ zizkis;2Rwj6wAxe$O1*DtyJVQDraZrU4`cOeV3I?QQ$HKx@<*bKUj{7O~UVmY;~|AU7{?h$WPn-n}0b~guR5x zJtsHa-?&lk+_0&b^Z-V)FX)fAS@JaHE)pT+w!DH0a4s4<{cGZoQ?8E`bzRY_>+~GQ z#dvc>_bdzd&Nhb$*Fn;O*KdIDO(pHR*(|?EQv%{tUqpWQgH6}Qju_Vh;`fdeccn#i z0P1OJYF(p_D*ctOd(3#!YroeMpKEbzw}JP+27n13PbXNHj6tS8Owl8uyhag1n~Kt@ zxt0oQXD_cWcHlg{d%^bUpO6KRkczBuQ^AS!mrXE9iKhR7kGM zty=jW^G4G{C&@2)0WO(os;gwO?1X;M2b+be_i;t270JGF5LtfMP(wcg`??(3Jn>#R zI^^8WS&`Vrrl8%O2Q-u!Op~4_Wdu7vnAPCAc$-B*((5)uQnxLiT96jgb@sY@R+nWb z9{1fo6T#VXvHLqupt&qj5i&P>hl=VnAsepCMMddXS^MoyV8{3#b_kuT^qyfCx+DOuTBCWOcwc=zK*>wv-d zvpOrHZY)yxESl*}4xM!w6|z+ul~#6P8J5%bsMN!O+Pry*c|VDG@6gG(+Gg~Dv&BNoj5$!hEZeZijAXc(OG~ZXT5Q6$`?y$HS!p?2=^m;j;dI+J&M*cQr3-xv zJC}31`5pGt*x#}u0+TCj)j^ zm${Q54U2|iY*0gKI&EvSZfqMJ5v|3?M=ah?Oc^~bH{z7&moDXPP-_3<FR#ao66ncs+i2T)9Do?qAVfaKS2ogDz zq&&$uHZ9$>*VQ5Zz=O05juVcqRUyCV{m zm%3acDC-;YdwDmki1~mb7z!XYO}f%ltp(wTbDxke`hTvF%~niU%7I{ z8gT_J&G|0$^IDHoEJ!Vmr1b0pcc!WuC?64H@09q1(YnDgx-uO`TV?38n?~IytbYxy zZH%M;?vS(&4W$z%tapp}gx?bdBX~)?iR8|%!~Xbyj8BTT?$LO|!F>r)*)qJ8ciNMQ z?=w$TA#22Zni5M^f!?sU;m6c9mwZ;0Q6cm?AzjsbLa@x;6x3QoOi<<*n4=vGjBt{V zB88(mQoNbMU;Wot^rV$)3;f!DKluvUnpLZ@ivYySc-uW zrnko5zEmk+f%Z0a4m(=xbzByg*ljSo1!{C&pR{!Tq*}FQI#&7(KO{x}Xx&mRfd++o z6-@C5?4OS9IV-`I)DAuhf0E3Np39b$D`tsfoGjNrjNps)a@Xn*tvZ}fTo(<~0`XfQ zP`q%%!mS9(h@4A?jE;aHtIckosdrl(MIDCHGV3o}T<@%ZR}G6gyWh8v<1a90&?%bU zF`=J7@`XeK4CKn6Sq?*TO%iCh>01jI9g*T`` z`FNl9NgU|Rkp=V{PI3&N4$sBl+l?oR%w&G=Es$@KVjmHt8$rl-YaD8=B&<=QWV@*{ z8g1k6Z<@?@jeB_D8<7H$`bbcAeVhO)!-vZWYV0Ltc3!vOMhh0TvN|yNNG7+vqRHUn zO3%L2`ALZ`Dp`At=y_CybRxO2==X46Wtn3OSR&Gm1RAIaw`*|M>Ca z@duF<*ura6<<@JkC#>~c+bY3NN~5wUvDN?X6+(@>o-M*ou0=S~35c;r(eHB|_3YkB z&2xg>Lf&?=UVF=K%{aCOL7u`MN}~6cza;XUm58!WD&E3Y-@J(q$K7ZLSS&b?_HF<&WK6~f#~%|3p&k|nqdR(9FG>tHKJ6k^<_3mMW-@!azetje_ppB6%biHk0evL1bVLvteU>&vEGpH0`JZfYo8Qsap;I8o?p zU@iN%pjlmJ06&h+T&X|G@aG2ZFj%`MvP~(HT2(ati{>{hgkVjnUmrf2LZ_j&a2&4A zTC`UWr$1Nf7VVwt=-d2VmMS&aiKf5=n|617>th2mY&xK~R7F>ORJCX?pzzbu)p$O! z)7@e#)fz)>oaTNXhqo<`FC7#9U^-+5xE?I$h5S+fs$BYrAbtUWk)=Bg;hwEl%#UXDUdC}WAJN-YH`K^Et*W!*48zx>4Ux#LUDR!=e?`+)6JK0yrBjf~XyK_u?ktDG$PK zYW>%diks+~%)Eij);B}V=r_-wJZuj}E`jb#3CK$cU5LZbXU@+6EnWXZv}r%g>4@q%zPA*x4*y5kkA`zb5ZgWy{V`tBwXm9<+>;$L4RmwK zLPNwKU_Hx7sL_)ZVEa$EwXs>{5U<|a3TZ1SPSKG6y1M5@Tc$Vi-P3|33o&qNF|p)@ z;v7CWO%(5L147i(lyt(#7k|vZ@#}4g*R6|=QVFe|$;0!0CoPA0>V+piA59#{dRCSH zlt$WR4L1iXXQZHJAB9dOv;tPsqcQitEbXd)Uo=y^l&`U${+(s*YpB0wZF~V8#V;Fn zW3X8Fwv!D!zmuY{eL0ZYr+DH@pe90eZZ|&*SZkMku1yU5)JWjV#tI4q%9~~>aWTwY zec5()j%3xLah9p^(ONU*_;2AiurPA9MDhEt|NRo1S_=2&lF~4K04H~3td0`Au;yj< zjf=*G+vLCw0(?KDUz>Z_gq2_QY+`2JU7js34uq(Z;gPM3dA((sf7ZWyrI%i0Z)2Dq za-X$F{o)t|6AyF3`%f!zmHZB+JHFLYza1l*Sm+xlO(6@kVzkNsSw!_q? zd?bdSrp$i@^kAdP zk7RP1{e94ss6Ja_$@xefDOczDHVd$TpHTe6D4 zY4tZ|$+FsS9C>7wY;1B8J9rHjrcSpv0b+5LL#DGPkESK*N6OXkxop#Vzr8&}@DA9^ zIq8DK(fmMk`9em$f7`jY$cc;hjDIo?7swF^`{=p%}$DSKW@D zhN+0%aEqmDAiACo#!~13Z9bLn>T_hXJ5KzzB?>3*>#<+7d`hnF=m*YE0Q`5tlugy| z$I8Ro)@?FUbS$%{_*RshN?S`mywYLkMu6AP2?m$#Jyn$rf_@sGaYVIop)Ru)#7`A- zFXIvF0^0iW5#ak2O{#B@kCE3VX!oc6tfQi&=F~e=m@03&Z-TwIw59M9Jc|R*0@&>| zb7W3Oo@>Nwo(+}W`|U3g-IQAFuCFIzJq=8kU`hHuXl2TFMDnfc^fwLTh56{)AcF$# z29J&cH=QF#2Lqp}-#>>{YpK*TA3i>*qh>CKs#YJo0H&1;9ZO;>$Y1VdTC{dZ8m@DY z2|cCzv_{Lk>(5t0QWAtG;GVXM$I*d5{LPM>bSgp0(^E&>IHoibZ@?*exlNsg*Wj0X zOMc4E_zeLXD;DLtp2@_0(>1q~(qkAPWqUB(bfMYgkZRb;IMSn8E$js z%|(7QKM7!50~O=hOj{>y{xL_6nKO`^KD4n(6m9MVT@Zsxs#En`d_}1z4mX8cPDhQ5 z9Kium^zph;Ff%$uR=kq*unSgqyvhpQUrHNYS1A%Pi>#Y{pckZ0J z&g|X)YNB%zgT$G3n#W9@Z})=`uciLdmyh}^_d`k;MR^@8wy$$H*y=f-F#WrI*(?=z z!ctE;ZIMC1@Td`7JCA%S;H&wYGO-=ly}6ik^M?A-a&^GpMDq1!<2D>ReC&2&jgine z!6Zkgw&H3+$_c~`s20|w>#+FR_~yxGwBQH#rAebEy2TzxZ%Tod@cVrk&n9V1C! zN-TR;aq$d7$pwRecPFO!syROp=av2re!4?}5lZo!N~2{PP$?o^7vK4G&(=E>;xpX9;%%I);( zI+a5@lk!);75<}Wx#67IkkBn{+Xga0FlhAz{=2Eu)0l{}j|Ih33yq)L|2MM5zu?B| z{tiy(JUikbQm$3o^k!e`_#x2KarG`uB5*EvszZ*|5tJ51R;YA)`t$E3lA+0J?VY)g zvlqg84QqkmWTBC}ZTRdAY}coDu}G6S9!i{@@8H|WctXkuqaSO3&^((m>TsXi(9D;5 zZga4p%Y-QWpyTvMrISoGqarsqUGZ-&aIJ^@mQt)ge_6bs9@oya=9DboODxJgmFTza z52~2&kMGR2^M+GFoT$tWUlWsCi8UF(G{umB?4*68k(q6pdy%Rjf~!H2h=4}LXiqb?x){^#yuZa=vLX7knV<}6=6?ZWU(la z70+LTuL_t3W6n=eWYi#1C5E`Z3S+ADANPa~1<%ZdK#S(x)?xJ~(sfjY?;;i44bxv+ znkb*6Zl}c9ti7`XHM^f|W0qi3PXq1I^E>@dG$1G16WOjYHV16{u>0~repl>a?8?)3 z<#_wpy&Ha~FI!%6H{R9iD6*cb^uiFgm&h7+X~2H4G;b-to;k~8_CSECi8MBH#y0>s zd(u?iB@JY^rEZAbpIU91UZ$K_t*dRbmt3y;k>;}Upq;bQzI&iO+;3gs0rtmf%e5Rn zKdYaE9}CaND+(ZYf7ea(H1BoRb@ywr_6d2w#}7gufYP1&7v(Uu3vQ3q!J7TVu!75H z_gW)}$R+Z6d-8v&zVnK{TZUHz$WuOPev8-TEqr>l&UAe*;#ZrVm}GL;G3^q*aJ~Q- z1Ocetr^D^NIgch71nfA@GBxp=;y9eX6LP5uHNEbk4@Nztd!!VX-(Cf9PF0e%AlSoV z@5l!~p!nSyIIvVvZaut{Wv{ug)*r9NWr_Gixu{^^W_U@Pb@_j&qv_I|8O>Em?}N9| z7X}D$Al9YAeEt0?J8`8{uq5u`Ls0h4?+7MD%@b+wG{vzLcr{NqOqt%i8e}6J^=H-K zr536pU2&{b;F(I{$XAXx~iVoZH zDu3oQpYp7=FT7A9Qa-)ce7>3XJ1XNpZ;?CWo(P`DtdXzRev^#ol8Orz>8pV|9k16k zInkNdh%yllRk`K~0K92~u%ZLD7MwXO6_F1hf{gQ==+LN~x}ydjKN6c0_@XUhsI#mB z?S5i1Gi*}n@D#ncIK_6dS}zBa9h%F{*w($mb&=QUjD>&GRNj+SC}%E%0H*~;fv&f8 zNMKX7sPv?_zVlCpoP|0ah)J7iVIc<0MNgs>#_iVruHNq(m~Eik`(2A%xa>H6-vY5I zsn4c2tdUS-?z!^!dI(!C9HXciT#t< zYXas9NO7Cam_D`q2b=W$Oihd>xJ8o2h`(CDT^m(vbqyN$pd&ovHv@w}%V+GJvqs(F zO_b-Zqn=u*orZ@Q3M@;MHJBaOU)Xjc^wk5#MbhnL(<+YXF3m`zvkh1Col3Dug|FuW3&z!1~B%bUUnxV(rU~R+phW zMd;mQ_xT z(Pf~TA%|WKp@O#{eBTh!L0-P|LF3c4t4kC+enq|hX&>dma4W%WDbsIEXc?`?f-YV; zz)dqJLX)RXSS)?ef^Z*s-B1o8zTbKmUGvThA@o2z2=6%ky=F*u#8WGcLVhw#oS8AZ zD8RM@^&%S#Fd1WGyVn17jX5PBhh(|6fJcaT<!pC&RtyJ>T~;MsLbO2Cq(0Mk#Hm;6<(7KpfNDAAaUUk7-1 zbE#7M{>A^)z<#f^SW`wG zW&h(}G&t0_w#+d{r>^E)y8ejoExBOD*SaJI%|F;}Mzm{9P%gUQ3gQb`n_=rXn7xuy zPamycFH8$KY}_8(r$`)a9@Wwh>-RMM{paKBYi)XOHxnI%AF!I(2|IE?!cT^;-sDurkhRE$l}v{I{JaK#3YwaaA71V$1~Z98w4-sYi|RNxNL2q6dXWJLHXCK>3(Wn5C3gD7_KFxJHWyz;yMcUAh3No*RM<%h z2jr0xbSrAsFwG77b8za zxFl)SyBl+U#)&5iB9ZwIoc}%jn3z7c0$vqLb7#kq^$mi?Ii;1Y!~KsT+P!TYVqY;W zU1UBpc%HB4+oqKy^m-$FFWa-3G8#zaHAJU#8`=q-E1ot09Z}+o4bI@U^rRR3cr9 zdEnly=RVshBOAlKX&e&vkw+{iDHdA*(**LKvY@Ky#jdp5A$n?@jAS?ezAxnXKet@z z(WfSI=Ch_Z@5MtnKgfy#pj>yaH6BGr$*2rPkasC9X(l|ZgJK;TrGWVR;`NwkA28ez zoNt$YMjf0awQerIGIG{()=HB`-%hy5*`6F9=x=kXfv_ORFm_@{{YCr)kgIYlFKPGO2jqc%4y9e}mSej}9SP4j#C-c2~= z5`SHp|7M8qN|G<>!$VJhZWp{az~N9yqSby2urA&kcHdO^lm3m>Z>^N3lZbLRkMU;$ zE-CkkEspWKzhjqSW5drQt$gZs*Usy+A(VD!&lEgTl<^LA%RQBla$^vbY`edeiafbV zBwDe5*NNo4*7s0I>WOJ;3#9%lG()XECl~F%sa&#md(&QbM{^RigxOHg;*S%(K+$S#Iw(nB8 zR3k55?`Ps|{<~pE?srb~D%uSce6()PF2d;~CM}Sc$1C!qk6I^h_Y7RRO%gR7@W*%; zql3tIUgb>2Uk~kBxcLs+<;)iPzpC!;4@!PKDwW0aXH|wFsvnc~z^cbRDgu6utW(Ce zoUGOPX+#~*Ai8PSkCs7$JZ*d(PAuL}INi~x1sS&Vdj9Z63|oFg%$K*+*(dksGnTgr z(>2scA)q32p}J*hCWwfK5Q$e=J`}mY*CQwZAOD?F+DWmP6LC3YU6*K>*7~L9!I7~x z6Y|9P9H#g7b5b#{pjnsX-CqXWe!2nKqC?G;>}HCwMz8F)iSoNDyiL{>0j=+K8Kj2c z$i-{R+_upRVj?PTuPvQ0V-@~yewJ%UU#j%pri}29qV56cJ-VbN?@ScJQD}}QtBW`E zm%}*>K~cu8#GVm3JA>=Oyu|AI38*`q7~96_aG>{~EQq7m%dZ32eeU-d8gQ1(SqsMN@|@it z^B;%AUi$ee^5k5(jWSR}$@(7TK0o!bLI3XWO-Z2UJo`&xkjG4Gk=9j#}mlquM?GDoNhPdh*cZgBqLs@>*Z)Ue5W#f zo}1lUZDD$7rB`-8 zO*V$dB6&q!lKPC!bb^0Hpqp6iJhS6E+K(G_zrJ}S?ziEv09*f@t?aX$Q}gdIzG5_? z1Cpo=Ew9;Tsr{)XuH#9^h7)JHj!VVty1i;^L^<9&#A0Nq(XcQ@Gf2pYw!NoiW^R}q zu&}xCaw|^U3Za&r!JEDF6*Sl^OxwfAWT{*8B$i!$!A8jCMkjLw6IU1h7CjPw)nVax zBG?+SCOU6;<;7*2_S$Jkez0vtSIptfr=BKaSsk@W5@pQXJoixjRG#9_@O<}d|Cmp?jJ+-cjI-zJU4Ox^B=RcqBz)OW3g>6 zp}+8t2EdPwWr1WepO`7EDe0zu61st2F{;l*T@3A-@G;>O?~OG2 zauQv?xfL7|lCgx%v+G|Ch4icghrgDKLN~Ob{r&kHIw^yzN1h8kHnxeVb$0EPZyo0> z*N{lZquH`39?wHTQ@i_ee6R{#O@}UgRu+CSR7G{D_Iy?eVr%r6IQ>VS-u*?H50;S! zUG2Pjg*7gGgfNVRbx-@CnjEnWYg((6la73RIx2N9b@G43tv3MEqs#>Z5TfU&2;wC5 zFJ~L>(BgwPVF7DP$&q#d^$7{iz4dqPFRW#4MNj_5+?d4Qm|G2$uq=Q{mKB(*ab*aa zasKp)@;rEA>{b)dImT$EX?3h5nS1=?bRq&C{_Cu&=?Wi(an55rsM`OJu=9><>Rq=y zh=@wFA<`8z^jgt=@5FK7k_8Y z+;cy3XZ%wUG}$}vv!3-`YokK(-pLQ{xzc&Cb>oz;#|Q?WQ*2LEzbs8&h*a2`xO%lg zumA*idyq8#F@#Lafg64&7gcNg)acT`k88 zdqR7WiQ~IwztiTH2F=w`ZtoYpi5YeyFXz=o85F-(1)Prv4q^K?>00Y-ebm7J$p&VL zKHO7ha*)(arU{}zVT^7vUkh{^eNy;&1#-G8`nxasU_`D;{u_*jBd^DIx!(_W>yRMWO??YaNxTbMhiIUb|_N8rltB| zg3Q)9K8I?cWd6mAAJxhY@A18O9vA-N3%%9b`%E#O|Fe|+_cfRLlW<3mlAo79>WO{n zg!VSyB?dJIG3^`*6mz54RjljB?tBU#m=AO?E1j$mF*^@HtfScrfBNBor?w!MKHa#@$E8I2F2EpyoKxeOJBF2+6ZCpJ-ase(G0D-MkWx-1f?VE_RyDXDO% zt-oJDKW~wx)bAqXrn+-(|Wx_OnE5WPaD>uQb#hNqT+(#DaNEI_@&ncMkRhq-U zA4;+dy@l+4TOPFb0FsVOb-I6U1G{D#e}*7}YLPhoJ_QW~<;yoQ(P65A`zYI0XXCk{ ze?BrkvPW3CV1Ja|^5>&yQl*{$Ui!WOF%eyyZ5;1Dn)0?bcU0*5lOH7d=;vkJ$ z8F<6JD8IhQMq$Lgekhk=KuelxI8rSHPbY^B^KX{ox)}x2yHQ6t1qH?}f4DlfB;(6e z+Y&t}fZD&v46%dr5t<_}KYrDkW0tjrVnR+AVf`4M!l`D#f}uNI9JIpm+d)DaYAx9D zIX>$IQM<9At>GXaY1m3BBl$Pzhkx{CxHWN)@>3|Mfv&fK=zkHIU|%aXla+JqzB00> zt}aS1=s;dy_P96Kwc+mpm;YMVdW-CUt{E!I`{PH3e6F1;L8}e3{S_lQEaJO8MeYta zOZ3n=AV%OfbW454lDeVa{XM{PDu`%SaK;2_YiKZWX zivp|VR|?c_ZNHi`1y1}UZAqUg_lnRChSL@Ga1nW0AubYM>*`|?=dqU;JoTjcf!|OB zzPk~=bXp5%cQLt+usP8G&s-8}SmjyYyeZE1Rmc0Ca9IWbP!>>!#SQo%%scH<6ejN) zs={G^85%+;^@3+b(C}Z3TOBFp7kOVhl&a>Km1X{VRa!Y{7R#2WVN(ToCs`h>`E8z zp9RO>`M^D8QuuuACmt)}0|=X(pA>grU1TZ2%DgK-*92{Hx1I;9aql)2D{@hdyn);! zGfG)R=*r8o=7mYiOvkn~lRzo~@C1%#qB^Z(==MC{(K7&s+-WBmY`nXojiSBLSLNVt z`SrCBja96Mf?Fu`Az$cy5TLHy|tXqLHemMk8Ea*ICJ4 z^L}9V=@;rb8~{uL48{|4>Q*-pgkc)`JYvzg&%ioe>~S(PJ6)~P$1ggDJ%g`5Khkpa zGQ^9N(ImW;S1z?JTElgB^{pR~novD1A%@KZ%@6K4dLi4^waaC9l76iop}W)5HJw|X zxAud`G?;yF62D*Xa23#g)giBtgu8h)8hE6<&06Y=GV)hiKAV3Tc@KY3+Gdp+b@(N& zH6f(3oJ+OHtDV``^AUS<291-|j!K>K0p{f*yQhBMKKQr(C2F1zb(W}vS|ilI%SaM@ z7*9M$6*@Z#7bp}`?Dk51sO=f$zl)$Ts*4oeVM;55a z9G(2&55!G(6eqY9%KfL)*7#!sMw+a$|CrwHl&U3IczgX`)=As`k>S12lMx$?54C8t zl;0m&9#uQ35VrdJ_gLueNjl{=rr&<0DsAfqo5}RNF4}e2`}aKz#~0)}krA$*qfM#Z zY}!2*LuINiO#7q?m$|Avd^M9OQuatdrq}5uKJn-GSI!UUj$P&3Rg zM=YV<{lk*2&HVGyhGq)6538M^k(RLvgnL&Z>CeK)9UERsev%&zt0 zbVqK=v1ODzBBy7ZjW?YobZtB-V?*J$|vhjl2w10AWEIw6%Wte^;05`s)C4NT# zxsRI-eTjhxvLn$$^%-o17)@!Pcu>w)IcYg*v84T;)1WR@G0GA;Cf_8JGOW!-T@rR_v2L?G?3@xN>#^W64HC(GkAg&?Ht-(x|M)i9nMhB zsq$<0a2c(Dc3!n0zTLm;v*qpIkpy&)X~3#{+ZP~xZ2Q=Pll(2!NYQ?c`z%|*1Ag~l zn(-og#;)e}_BWd6G8c?I4nHF|{f_tO`6=nkI@D;74q3WS0Xt(YD{24NT%wRbMB}!r zKvc}v_7m&H@P>x3S^CeosijK_#SD&e8#!@^&tm%RRl_J*{Jm`LjE&NFwm#e4I${Pa zQL#_G$k?plNbSoh@2fAGQ&OTY{FeUg);Rfce4Cn~qcih-?rx=ebEVJ$M3a)%`0!J()U?9gcZdt;fV!l=+=gI&C4>(wwm z*=H^Ef)!`6fXVE6*^zEw8b(t(ZI1??2UD8C50~k|nn_7H#%b_)3!_vWu17x!GM+2X zQ`8cY3hV;KEr0T3=kV!C&&kpx+pI|<_V;gz(Xa+YzpWn((vo0TE-@-2`Q4{g zrmiealjpd_DE2`sjM9MwEi^8C=E1($Sx3XQq8$X_tG3mv;Y zBFFUA{3V`ljj#HJLgdg~Z;ep)pVUz2NB8Go`_Hl_b>%YURt#upY2A+!XVE(bJ?BjG zC0RZUrI{|{SkYG1rLf316_2Y#(T80?pSn*xg2U{>%@BeBuhD-q@Iwq>}-~Qsw zH8)Kq$Q((2*#2boM_Nh?$LkOUk9`Lp)HxNUX?dh5i40W2Q-6MwR#=-1CQu{)oS42e zgJAQkaatsPBt4KwjKpqrG2=melUjPPvAxyU0w^ z4^(9p%cm_od{<)h>vNcLoxY>QqEo+*@#insT$CkN+;PJOX{$yh7AFT6DjYkt{cj!5 zX;-Q59OWjWrY}@fS&}3xts1=&8~Hj>>z zh*UnhO_1+ypcNGtheTbTIa-j0gw))9&d3?5@cNC3+&Xx*zLJJD9$;}vL<=wv3L_3A z+aQKqGZ$ycPDoviK=!$GS6&Eu&{q#GK(Q194TWg}7DxQ|fqqM1Ou-LD#TyR;%7AHM7oyxw@# zi}EXiZAg5$C%}UwXm#*Mdy~^Lf#MmK_hR%Zy19r57WNiH0!*!~B>G^e%Es~AYIz2j z`we!1bUgOZTbB3#sh9C#+@VfU;{jAr9?|YJ#AaGH6=f$ehA>@NA`~Vh*rm8!j&nzu zj4hD^pI#Bt(T9*jCd!5nf~#{3aV-jA-qDOmBzu{9RRrqn#dJQ(8lE0*RQ+5H{17!# zpqy=(RA<_-MpnevUzlBG6Y#o8c#V|>B&Tpc-n~yQ_VfaLQ6{zN;U++&;xM}Ln5>hl z+OdSCHo^7{uH9Xx`@LmZchk2el+N&F#dC`N^&Rhc-n?n=YSER91y^11G+&MM&>GYI zK%Gd_H=Dw2JWHD^%<9M#C<+8s*^Y&$T*<;(Ha*b%l zLgic>(kM?D)aM1Ln@f@DA7IhH5U`|cL->t(ewwS-Rb8+`9e z-g72HH)xLKSiXm#O}NK-RT+MQ$izTXn1ZRZKpvzu~G-=q@PE}5aTypYOJ7n2sbL@Au!L!vD7vG9!J#b2lgpIpT99$48J80SmujL2PiYfsX zSnK-P_Q)zT`Y3|g)azFq-f1F$>JjHps8|$GNY4Bf z;wxRi=F zw9`?fo2J2>*R=VS?B!G2G&2xZV{FuCI_7c5HeP8-3M(^Q>NG37vIzZeGP!+QKJc!4 z?FRM$MibCIouAzj#d57*TV<--1vZv(7G5#$&DZsW)$1(vdpOyJdg>TJ5Hz19z89EM zk>MEC*j^pQ*|xI)zE!V*v zCvZmcwU){6eYccU7TNomHkhiPjCm0BbToQTq4^hK^>AA$@7d=FW@)hx_;`&^y4m{# zTFDZ_NJ)jp1eEL{m%!6_Y4es`XP8FpBT;7awGps)|MsF@^MO9!^TDQ>&(55wzW5Kg z)g_^c9+T?h23Ch>ixF~ztKSnA!tooHaAu#xqfQ%|Rk60x42L~o@AM}o5Vps|Iol0Y zWtQG`6Sct<+dr1O4?db>+ew?|S{WPLB$i@(ik+ZGI0GjbTT}9AV7}`rBh5{>2hHCl z5q&H`V46|%(1!1-G0M8#v@p5GSDlr?O&oDHFNr)pDwz95OA!3=M9Cf zUh$j8?*4~0-PYi8=Fw^DS*QCeXj!#{)xXh*hxHdOk8T`n@;!=_gtx5gg7Fi8Y?^H~ zw_!9vGVMX$-%Rhyr`hCe>|lH*GWyM`y=D|n4*Mf1ezhdad#^lQthEcrQ?jelvSemp ztMbxp6;FxIe>#Ct`&%l<|R zfa?KdNE_i-E$c{iGL>GyPo6Es*+_hl+he=l&*=z^SCGofE_L;3kG}2X_YKvDL|&=G zeb)X1ZX7CA$GU1c2JHX#&bx`HxH`MMX%o)PaWU0ba8qng{axZZbP10}szmYXZf!Yz zG*umX!mXr}eJ?X7>%Fyww2iW0Sh4HSHE-q1zd`-+(|$S^PJ_r2)B`xhaO8mAt42LR z&o4d>j~-BkWr;-zm;IBD@#ft3tX{>bZqH0FbWqQGy*nvcJPuNZWd*E}`S8Tyg$rQG zKD;i`vFgpq_@FfartGQ!9I^yhgvrzDVv(#eM`1*REX!w%IlgSZrxyr4woV8D8D4H= z4eoCzo52fPK*b|~b??DLyYyjUkP6|}n(xczqDXRC zKl)RdChR?+5&7_NKBjkb>C@v9ix+a#r-Nkmc5C>Ld}Bk%&ZJ*=p6JfW;hj2-?gwwa zKP05J&~A98i>+j>d|x#^CSzR{ZeK&kc{USFDy970NO7#MMBMf#d?LMP2&luR+It9z zw(~ulc5xcH6+H{cuYa{%o`J z$F&7UG#$=yzL;SVrr#1Chm*~hR+?t6{fYzE(A5v*VOZ%GA~cpjGG!Ql z3k{Y`^D=n}pU&AcFdArh5KAUwlIWtk>zd;CkG!qU5Zn}NAxj)Iexa#~BxS2^XY>)olug&pzLjJ0kUescT zJbbo@v;DVJ;gO<&C{z)9vhTmccBX}t2m*k+qLo1GGm(XDP^il~(kbP&dzbO}`iRP7XF^f``03BC zcHmf^o$tQ2MuHTjd>uR$^~3nRM9-)-Y>XSZXgNSfH|H(V&}%~8T5$35++L?&KiF|S zdUB{u5YGN{(@2O^V##yT&knP`Q|-X)s;u?f=GvYR-#Jm)U!6Msv7ZsVE$IEi!RuC>0aK#h|w zUP5Hlu1K6>&PCZ-XFNFaSEWvhIYmma!KY}KX2MydBsJSN3F_UEHhm{qC^ky=F%PQ< ze%d!3R)y$yNydvaHLXLgQP{G|xwGa9?}~Zs58>Dp_78^ztJZHdIGe&dcQ@xn;VIA4 zcgqW6BC~c*js%Hym#y0cMz&){{QB|1H|c35-%=*1Jt=K3zv*+ji4fNZ@AiTT*Ls66 zAfRmE>45uuaQyotudpY)kzn}KhBE}OaPUI%eJ-Zpya+|pJYQ)dGkFCIvTkl;^^>O6 zQP>tsj?;C4MQzC3>W4~_flqYsZjLpY_g3(<0eoIwx6Y;i5sme{AqX+x%-VI#8^qee zuMx&@FD?{6XA~;)L1l(;gDB+fG zTm2k>*{xRS<@4@^=I_5%-PdC%CASa%a1Y+7C0l%^fUMwn{k%(&%p~@ly-bYQBF)>k zY`0eTx6WuE=CF`2Ew^(m3z@O$giHo1EW~$tj;xrO{bA~i9J$`pF_G&YG@O0ifq%zG z2_{h7kE1ebO8lH;^*%6JdkPj=UCt`_(v4mZgc9Y#cuFc3@u}SviwB;o>k$En4irNZ zPQZ8y04?qIBVs0#u=v5%z>~|@BGP?6bgsX?IN{Pa{^b9 z2&c8BocpuuU^KQ8?f6A-dL7`*)B3L^LM!h8x>pDiMK8y&AS`}t z)Lvp5TFKGKHH4w&am5IYeQ!K{8&=QIn2S!j3|v~Svzt(5RYB-_u)IKYn#Yd~klS)h zn%w4t^n0=wWbeg{1z41|_UymlA$%W32kKB36dov9zv_!ST5YW4SW~dw1+TssZzH^n z*4l?Glfy1|%zvRTTl0s}Xwg=4jRK41uuR?4vE10~#M9j>_o>L`e|%njNW@OSqXmQk z?3vxa90s)Mx-94>5z6+y&!rH87E5d8x1NxqF=~uC9gF47TuNfrm?87&AfRVnW#5$)10A)jW~*)YG_mm8 z9d;k?VXL4VGMJ^kRr$=~-LUC{>Jx26cJm!}k-bBCx%VLL1nB7fQ41Jr8=*Ag;5ytq zaK8!P#O(TNL!jBkd1yJ7dHx2rn-p%;UiPh@*&q8Dh82SMUgJ_5_XwYCUb+5Q8Flp; z>nbiO6hHdqnqR-eRTT{(Z|U`a>tIfwk;#&R=!3!0Uvtmu)&Li6w1-vu{JeNDV^!KY zHpUVH9tWFhI*D6BjVMQTTl7TgnVI|Vx*&90_bD!5rb}0xrt{H_DOxlzf$wSmoA8~&{|4+4AV_vcYYzik@&yz; zADgH#glgi1y@`zqNw!u3oT=20v^NbRzc89?irqVP=55%89mZ?~1cFEC1h%VWtq+F8 zBkEi=%;(e-J{9~1Yuc;Ggikz_()0uS)CoFPaHVF2#-^_<;C9X%6>HU- zePoZuJBuNKkPh>w@QOQHjf64>(q+P%t`U^%o9-TNB;xB6d(&?X1T6EDiZ|TG2h?Io z8C}u;_>XAHxMZ96*CVRDz4oQ8hCr0c3f_(FbdJKzoM{oQ_tAfpFA2|`Yb8LltcZi6 zc0n_fhP2W~x))*c9?h{MFHa5UU&v&sd?7msoA^@N1Bm&67|syZjs>Z-Dy#ro;A_A8 zWn}CfqdMstbno%1G%SpGf*Pkf&voSAVHSpUs=CP6Sjuv5cHj%UD6cZ3udY$s%fD{V zw=3jAvq4|e3_PL&S7Oq}-2C-&7PIy@GX?slH4-IG@-%Y0ylFLx=+kWn(*{O&;I1Dv41ahRae)_J;bJ^HMy7g*PYwn=}1D#M;GXL zR{D>YKeB}fN}T&_sw7qG3F{TBM`j6K;^5Ey=U+edTA_a-(=0;{cd#EExF6urOC2}O z{{jCHo8%lRsiTvAjj9nXTQm+|%%?Ny0BTtT5_PwV)ZfuvZNN;&6g;ZQ=%|Jin(n8N zYiF<+_r(T%!+E3=iHLhsx6P?X+SIEK1A?hv|1ieoT?(HAqr0yM@?)9?=GSJT%T4+8E+pYDTEfwd z3oZzSj( z+%n%?uJ0E#6NZ49K7__{<=+c|$iZe_f3Nw8v8m~Jpkn>( ztLH0!EF{D>mfmVQYYV{YbgW~S(7 zcZx{FFhtyf7m1fKVP2GGT}soe6c2SYmqps~6f@hx=^~ZpR(wX@^=t;En@uk;%9h?@ zxmjJMs``Y_^BIhlgY4PC-Tt#7bJ5d3EY~J#$qr^2G+B$|O8AY zH;0&B%Y-SFRh)m>imMN#o*bzU;|z4@<;nG1$3W>0dd`MrJvI7JxFBF>4DIa{Kb3n! zqZCSgzVeKu^epS5pG79=LNULo-|77tsL>zN8XALlNd z1Kl@yj;`=-$6V>aB{4+@M$Fj6Bjj}W#cE0oPu^Ob+O}6>r)Et9OUqK9ZeVeZx~xxs zd$q=CB~m<5Y5v|WS|H~M3{n(Bkr4FE8{E zR0dD4wc?IMS^{#+C|fKksoKwOP}%bTmdDMG%{7@jI`~bnkm^(#by^fZ)?yI6DzXQ- zLCYi01~)0pJdln=@pC9lt>4}zsLkcf$WKr}THT`X@Wgo4GPIg|Zy;h{NC^C}KI%|7 z*+SbTt3}{fhs)yMdlTc3RBN;+|&!LZV+<&|s zA#nIsd8e@O``jB*^AkGIINH9{v`?Vk3a;|h{4=<*n7Z_&zGGa<*S1e^t@OWpBBCdkwM+JeVwK;F`atmg{EqpZmSnYNbJ>FR z^bDMk_XqIzR(5A+c7OfHQ)+@7B6k#)P|pe4E^bzndlNTd(xqwEel0 z>@@q-VMUb&#bIeCzx>o`a2Tyr5!t2Xq8&sxI(+~7!}@KWjqkysBVWhp|7-=xI90NA zR`uir_hc%xd?`7=}Q`9 zIa^lgG7?1#o!#_fmDVOt@}wOPI?4+BTn8|WNaV!Pw1WTGe2`5G;dJkSwZ__^WM@BY zqcP@GH=^SFfZUxRN zRn0^rFU)QUawV7ax>LB={;`BJG_;9~%+qMfj>|`PpG9YLtl_thX+?MM9VI{^L0B3@Out8l$7_np75V7y+S`={+oLxWrf;g#Pw;;{J+D2 z8>CSdyQ6N>m96%b3x>_MBLD3LQ6+^hUpw%YN&*otjp-#y<6i`gUU1Haa}!~JdIxF%GM^AA~n#+>BJeX1)8#O_-L<-3{>&l+>M*kB`KD)|t6+1flT5op> z>*`qW#6AtZS=ka;0|tv^x6{D_4+aY!DWnyM$t@*h|BUqzwGmi)25A&Zq5;3zXp_io zwg?0(5RdstnPIOQjpOY-&%MI#0styt1}NJJRZlq;z`92o28=(}?dQS0d+z-Y#t|$b zf`wfulmEy+I>OBE%apMeZ&mOM_JoCf6$7^lH{0b_V~rLka-J3AQ5=_|nQ8BPJIYho ztvb)vVm4EQ%wGn2TuEAwwYPtg*X-f3+mzQ6P)`hc1h;_BnQ{tnc*~wTo>@RM3?pPnBY|qYM_@pl?gvF8rd`8J$lb9J5wsaSmHB#di9R#nS_@= z)X#VN&7P!GLzJI?Ha=DF-%0bB&yWa2Q8 z;~9sLOTwGlh2V&@-M=zFmpBkC|~O7Y|FI*lNZBtM;^nIH)IZ>t&D zp2axCsNH|d7J6C{l`d4p;Yaxy@a}vr3GmCQmZ9UP(OTG-nHd8!{+5nYI4QatKi!s8@)u2;vIe~vdZ8tlvpmPYW4Z59P(LzPrD9MzzHLSH zLz#%tgpmGM4qK}2ZBCpo1uKxSYX1Gxy~0rF<+?rk$A*b#9DtK|>K9Nq zC8Ui5e&gV+Q5oqTTTUT>2_vkDY!Qxp*O3K_4gh6dS)qUO1NQ{ZwtzsS8vYr-m{ULAftHw}hyXX2Lr+(PO>o7A3kaINq1dEiz-0tFyVe>zB7RV%c)Q|fdC@DrAbzH>2`n9HRP+8dNL3{4@%fD>wRjx@(roLl#{n27M4b%`!Aqi-5$G`q{enUhdm!#Xp z-|l3q*H6TOi}xnt6c<18I;~5a6BpMbcBjBApaq`R3m$_F;>#8>3jDjAPuh73| zKPVUWw&5}80bAWGSH!FBsisN1+cgAu26Q$xmr;IGsJ#1ruQvocp#eSLwVVDWha|Va z@jH)6b6IaR9eH98pa5N`F^>rb#`rg~0pFgsGtkzE>T$?YosXxL?Od7ausWRlTXz8a zf@ZEf+D_XUKgq2?V^Vao$J=kvge+1{nse3=sAk9C9D{O$+nRBEqR@*$)x_D=^L_P7 zG|^f2%SfN4a&JGwayj*12xXM?Krz=zWrg1m=AnQzLQYQ4p`B2#?oCkcn7L6h{fjbD z1dyPK!$YCFYk?xIAL4^R&E^w;@ zy7LkQ@ki-4v|I63p<7{@&b=MD_EdSJ~f36_}DJ;kWec7;h$^VDvm305G)%m6ml`>AmAsjI|6Z7MSVou)5W#QT4;$4nXSGh*gr{uN`-w64*jmn7e9P5n;2I#7va*v<-oH z>Nc5~@2ERqF{=iaGSG_=A!ugGy)40G%J6V&IJ&vpj@X|h)u@5U9$)VX*6g&9V1e{! znZ@^=h4d#pO*YRw1%kOU8z)yw_r*qMP`L$_@KHZAcF5m7Q$$E z9*$LA|C|4KC0quZvQM4ysuP&(n<_9rGWLR=Wo=q-cmQBfPNblV3u#j=oK48BO}XGH zv1y?b)d$KBVHsDCTMIgD$O+JE3hqAR?KF~?c)lR!-`~O-QU`Z{Qk0RDi*y|yUgJ3; z2m!KX8*1r6=H;jnhZ=&AG0m1oyF<(}74=Y^Sudqfd`ojg9P}KW-!)*vaf`P;NGE!lC zm%A+q^f|V=j1(5(ME4NY8$0@xKYCDp08i?<6~cAqVwRwT5sD0HG@vB1p6^Nfu~-BCIVF3wJEaKiJ_Sr zx5Uu0T@OHDa1fv9EbGJ>9{@^pP<|lh4OSqn)1}#>^^S?>uuJno-Vm8GO_>08?Hvgt zvlTi3<0Ai)3cUTQjH%U=Yo&yPg7=<`AGQKzsfG0@i9HKPtDI^b6w0rSKze5G)(yUR z{P%?BmsS1;n+tl^_;Rod^##gz;-L|iL=ACc2!)e1Pspo@FJ8k|-u9)(#ofE9P+GOp z?pLd(o=S)KtDhR_9!3^O&vf3O9efH6AbPdf`KtKYQDeHM(aiR9Up!n`&=n&(Yo-Ws znhV?=bIW$X7sWqdoyP&g*yv%)PXp_zQqD@@F3wG&+(hOrsXBV z6SZ~yhZ;I1yQ@vLh|&Ls@!ince}S&LWhYQ@1G2+ECX%44glnB|zLA~@^upg;@%c}8 z%K@EAoYh{^vW(ZodH*_URJM-(wv5$K;iIZA0Av;&4pfoggXgODDOqI@YaTM_Z}&|% zTM`wH*T>5+SEj^zn~c=TZpFTp9zN@8{lN-h{jp^`+^zTd5Z!D) zxOr|mk|w$Fb?m(*edIr@cM_#&sG)|)w)XdT+jzF)L)ZA~`wlOpZ}hM44^=6LzFRaf z^|w$3=8QZ{fWm?*)Q}~Q*Y*rk3X7x#ED|=2G>u0lDV+E~-n2c9o0a7Y@cU^AT}pJE zUyHFa8pJ=OAE-W2v_((VCVfoA_~$i_Jc?|Q9dnp1CM=*Z`F2%rWFP0ITtoSdpoD?= z=;793T-YyiMSWz7t|qqh7Ww;twfkuV<~@GU<2JH?yDZO;RQ)XX3l0dIj=|1zUtcYh zAE+x(M=~zg=Dd6r!)#A6YNJ~s=KA+SD{BG$+K(Q2HevX3>ZRX;YN*Czit(Yp?XL%m z8_y+o8a`7WWnxeI7pry#Pj+!)TV04MnPLPTt)k(41m4{iKJqdzqD7V!Phh;%j%eE@ zo+BP?%e(DlL(dcw`;<2VTZ>*VOG9;eYZH{3d)=^053eO0F4E@^+&#T`Z!>vSB+Bys zW3{O3xXyS=`Vg8L9wPI*E?kA^j(0j*EJtM1#98roJkn$~Iv)*?zR%iL6VY6QO%kg~ zeg|KNY5z{6bu~fvrfyysRfQJECKhv9;cto0#E9Hf*MgGdG9x`eT?qn%GsP`sY*)u)h1Hr7rW`F&jZBJT zFNRxRyGoYhu)FIQv3H(U6g=uWS4T#;51zmUPwFjSTKPRUt`Q*Z)){SUO!)(YVjim4 z*q-NjOVxIlGb;JzyjNRT-$N@^LP~eKMzgi|Sha5l|JQ~wk!P?4>0%J;YPvV|nAA#k zpt~P*)ts*`<1&J-*N{bUs7TPT#S};0D6<_D@g3vMwqR|nH+9aHt#bSH&(yYvAgRlh!X zv@cCNCE7SKdIsMUacf0uOFBh^nuZsfK{CsaT2J)yDaxb$Q4z4Yc> zT!{kUxZn?Bu81b)i)~@FWP8cD z`_SYqNKxqF$$GMU$K=_dsq(c)1pny2deP;TE7aCmEGb#fml=mhXNrAe;w<{%uLyf_ z3*C~C$n88)k*nAJAP=09JQYiU=dr*vN>Tm;airPFugtuNoc8SVs8(9lw>H$O!O`uynXF>1?dW@@6KW5U+mI zxd{z(LKQuXOL7?SU9HsoS$vyGH&U`_FvaqpQ-Qu2ZC>@V$i&utCP>C@ja1qjqESkI zDYEugRsnlfK@9NgyRiGUwC&Votf`;dg^QtlHWbQ7T4ol~=+3At1_cr;K&J&>e~=I{D%J=0O0^D+6c zuby$Kcx=6^@$VH<&g8Tv`uX+G#6%vHi(N&&A6R$FfsJq7X={tbXUrgJkQC=-JEz0v z2;Cp1^0Bv#ZQ>A>a1@w<2ehVpqc|ek?#o(#qp>ED8~&lznjx5)7u#Q!9R5FLSJvlwBN-(&hc?s1TU=Tm z(F@uy89ukaw;NrmzO`%`j0GjK&C;0xTn7;qn&1n=$!3pcha54OPa4(wib*O6QsU59 zB}k9=Yb3`|G6k7<3N$z?Ie7%|1q2p zW;A0;kHo=TCz)M^$`mV~qGfqQE#XTJw@qn#lsK*)fJV52Hk`nnB@>rA5^bX|Udl6G zOmgtnUl)+qCC2OYVr3a+d68bLd;;mWiW6{sNvN0*+Y`h4s*&RIMJXxFa4GJhLsQ!287Ce(s>r$xvp9aQ^zwvoO@`*jUGSFYf6|8(ld z%M(!9;7FcFs>O*#GMeI}j18L-t9>erm~fKCFB&*$FLP4JTo_(P_x=6&W}-A2A^Zy1 zN2`IZPJA~It@)v->{dH1m7yZ7(ty=_KS-1H%S z{$G5(bySpb_b+OKN-3yx2@C?#Ii!e+%FrMvLxTvCLk=m5ARsx!kRu=0V1Ku9YcX8UuJM)Q;_@(;MXafIgbMR zm1}jU9{S(0*n^_5NR@a2(T63!CbT}tBMX@R4aLeIwQ4T<{d9e5KKDnLuQY}v#o%>f zp6~D+VgjSVz-KjO2VoJsP{K-Nra3L}fH4g@z32{@f1uke6&G!vqI-=8a4!CRCnk*Q z9AM~+i}xwlql9raks=`-LFr6yZwl)pEp7eX5{#%YxHz$TrJX=gYEf( zU!m?*l=JhF5I-xE`@n&3FB59t-^H8BMd!V7wpElTqVy}CkW@Tv3K*U zKU&V4x9>z@FW&ilb&~`w{3sC31&ynEpH>A#sr1h%H9KCf$EN)J6%r})siC}dmP?XFC4CUEo@U&y+Z(O#Tf-04^ zADzeL$ag=warxH}?UhCeknj)VUhx9;5^9%0dQ{pwLYe0;#XURfG9G2=$IPNg;rn00 zmSv)`T(+w8(?Y*nbY6=7y;@=6=fPN-l|p8W_gDsNeyH3y)%?yX>fle6R$CN^F*#AX7O}TMAZrv*B<}lLj@Aj4{w&ZEELD)gw z-)0GkjJXB`1bA($K%Tb`D*|lFa-kijA^-r>5d1Z!Jy(YBtOk=g(a*yt)_oqtw~M1b zcoTrIS0s~UIzU|R0>s_h23$oc@CN_oLQJ@gOJVlYpk07%p!CON0;vJvEYwx#SM_i# z5Dfj4nCx8TKltK%_{T~iMqD8F^0~j#2n!$b0S?z?r&ojXEtDbecsK~wK)x7T9G?|q z_-Z;HGBT}yC1gwz7}fxG#+Yf90k$!IdTv9;49f#ybl-Wu%F>!GKH0VNab22S6Y!)y zm7K-Cx{66h`N*aHtH3epTthZlE~}=XY)UW$FiU5e9E{80e;wk0agZ5nUheaV(I}f$t66Q823CQTpQkC>`|2lMTaI@~R2p zE(?`EbGx4E-h!iIk)Jha^_eL5Qs)sm07^XUGBHOxORRqzd<2x8!G07PB7d&Vs7{+}?Mr-0$_){j^CrNYPXmpnjf>VPcS zwG@GR18>iND<=N#vf45fy#5c*Eiy5MvwFd5-D7x{poJx6XR@d*YE*8)S_0E36o^y* zyQw6h&$n@9OwtZ1W)Gp120VVCc2Ns)A7Ve?#K-7; zP*OFuXZ>`|{*GCW9_Di~rzfOloZMxyU_`?i>)G;_ta=Y=ynMO{yh#N7({iHn$=1UB z*HyhKGCr2!l6RfNC+MY+LCE)D^c_-S`rG07$?B^2VMDi-${i+?&(k?evh5CtW=l7W zv@E3EAIH)@5lz9g-O{sV;&!_!?FS58C5Kr7+nfGlkp%vN7*TtkAHN>J@%ya*-H=@X z_QV7x#wP<%oad_LMMDYR$n(_R<~er_!->DSMM0Fy_xX*6YFYc1wUzS$U9c=f@Uz3aoPER9N5OrZ*)@=AFg&+^<8q6H(@rW)Iqg#+W&Gx zk&dHxFwfX<`b?Ixh|4N&|qTk|8@|2l1EAx7F0MOp4=&(`}eet&@3P$1!Ux*k36 z!2ursdeLXAjqp;NIcl!J70$EZv8w%x$zi`kdBI~Y5^rO3hyw!chO*UC%r<76t-zBZ z0?41bUR7+zYp|-ET(lfBFtm>Z{+!QC#{Wu%ACkvnr7f*{b5ULgL*DFkSnI`yb6PKF zUfap-LxM%m7gpSrEDt~w_Kir{e;9ZKXmr=lqpUUGL#3Uzk(+4XA2J0S0}z&SXamgu zTUPiHGKQ{8)-5v@Vx2!NAN|L1F)njw% z&bNkdAgB27b$n8S6s-RMWt;M6*u!Pu1%IaEVjQprnss;IVoxBZxoyN9pksNV&kB1z zKrOE548H5h9m~QAW8)@FL&CL-8{`iEc{jCQLnbQ@Zfz!!8~o@524KQRfbdo!&_>S*>#-M$TCg&({QZkEiL@l|4a|TB?LDk&L~JDbpQomml@n zwJ-JB!^>li#1{EUtDr7S+Of>G}EGO`AA2|#Kq3F3@1RL^Y)O_`W;^h#f9Iv-92sLGEO=KYIsXljfgWJf?wWs2NDvdZ=kN# z#brNSTNYiUpV35-99+POyn2ePyEf(R7Tk^te`5-}xHiY__nX~!XT(Z&U4Oum;2`>a z>Gf$0QV=5WaV>q<#;_l>-DU1D^@a>FHl=RqV!IVMLr1-!H0Lc&wRT@_t}&}WCp&A)^#=t-^NfZRnhn8uA!@AgQV@ZM0+h}qndV4YdzuMX4+Gv1FU~MgMVEeIH>V`f z;rc9h3uLYG{6%QbDYF{tTGoDdub4KRr7KSNH`y;wU1iR^?col3`@>3QDkkJ%5AK45 z`RfUSdCQN((L@usV||ast|+X}PkPal_Y|@vwi~Z}zF98vP!(mE63NTQP*XL#UA2dr zP}25H5wy`IRgsPpQTRrne$&E&odN|4%k!piC84Bf{#~+{FU&GeTH5!lVb4Iqj<}29 zo2p1c9lKBA6OAM-+q1Zqfy7czd~4peDRHuE^6d#u`VCE+{Q>1x=@7~@sOZQ72IrZ) z@MRr=U8b3$43Gg#Z%$<|$g7&>Q+XB8bmupM5N1x@DWFb)IC9u;etv0j_4ZJ9=bk7Y zA`!e)`L#xQ0c{^6w zC^G14V6(udsI$alg^^HDwgifap+#NoC0YplnMF+=EJjHZ+F~$|Kzvo)t+?b$Ru}4? za=k4S&s>8SMM0fPVNNY!hS?FqeNk9Nr{}7RQNGed>>Whjnv=BbfM^GOqk%o}b~Nl1 zKu0D$mn&-OZPT2n+$#$SPp9^=6o{lZukm8zKQ0`ol!t$EP@bf}(vEp4&>iw=*2a7! zYxmh*n=x#S=98i&RW9UjPsWN4FYAz-3GNzVY@XvFleO^B>RiOI zx>~XrBo*aLgomjUO4(Co|9HNs+%~;x{8G1Pe)W!99R~;xz3N+f#rAak`SN?*DI5FOG(*LPDhsTX2x&) z!jo6Nhk7N~xkiqqV+uvV{;O@0l$Bn2MWiM$@=p>^tHdV*x)T;!aW=H_%qQEz;Qh|e zTCd`zfQhRz5d_k$0XIqUYtXq>j);u~{=d%hE)LZA+bVjHWalsF8(4C2)K}1MV{}$m ztV+ozY{qLYTVhP`ZXU&>9)bs{08VDUzO9sxl?>V%?<4AuWqpSX{_u=N-MMvmPxV3K^ZRrh$QUpr+={Ah$*?a&=H#Wo ztHVDntbI-vFqi(!QTh-1_Wm`$ao`+6998kE(qhne$Xy_EiOgVZKhABfHuBCnMrAMW zp_X{kg?qxGOhS2t^lhH1muUw4twSsOh@8jhVR&CY|pIhji6` ztDnGd95@X39$y)~xa&lMvb71h(%PLO-}z^K(SG8mrFdrMSzfJzH{0S@rb$PnGu)C44u3y)zXYb}fO6B;%y;qE5o8u0mKp*nJ~mSs)Bk!1 z67^Si^IjOfDcYC zpBKkZHu9AAZd`nf)M?QSe|@t@yHZ&q=4|fg6Jc&uin`rrl;vV{jGlX4)c@o5yl+$w zZ>KHmPk^!^R6ish3jE%z;}$g!Y3wf8Ga*Jt3+Lp1McAvvU|-!G4C9Jsyn7no6op;i z$e#AUbr-Z45OPC!zrd^M>JYX8INM}jBcZ$`!r>Pewc6u-_+Eq}jCBin8Z2Os22ELV zRvt8U%L)%&y4*L`%1k{lfEPZzOHJ}4|itK*b z!J#GtdW{^%bGnBb(&}=ye-=WM6UDMloZ-&7$Z_?T7HtVk z3;xZw^6$#;eViAnG9~8!=*mp-w#rgXVqrkEj1wKaO^~u&6h9LsF;1|7#vqUmmrbkD zDRGd$r_y%?cbG%PWfNAiGLfrR-a`rI$>eqf*u$b&4s@;-vvr@3U=Mw z2$1X)LR)89N+Ra^2L$Duf>$AG#>Y6D$HM4dH%WRfK$8@R)pow+Cn@IHhQkIAy65NRF&@-??iDWPjrbPB{d$V9pVrh^ zNQ==+;rw!(X-E2~Ec;EzLQmC2nUiSwu;O4p3R8yK;VP~lu=!Cs%gx%HjnD>9kE|JYwD zj!LYw2f{CQmd$e@GsLb#E?(LrlBU?D@vB?PS<`7N^}>uwtawS;f1cEN!NcWIAd?+C zWsB71(YY%nBCLqooJ}5s{Gvp0omvIz0P}gXYDE zoh6PJQc7Xp4I*-Q;NG8&1#@14YT1jx_@!@$=%^HO#2M^JVn-rN zy|CuABMwYKp^U*;@G+A}mX}YQWgOLvl+tAMiJ$TOMHztUyiaS8B?+7Xdo&W|PQr0r zf`im{CpSD_|9(30jhI@*mDXSqmj^i_dw*K6C{C!Te&ixj^V1a@>Oy*{R%g3KVrznI zAw8%Y@X#3awa~uV_i3nB^u&*I2VniTEZ$dD$-b0c-9$4+iK5SjT{Fu};!U1KW3O9- z8b+*yKbb%TN?A_!u`_lT9ilRiC(1E&x1`GHR_GjM#M$~9ak5Wmxi^d^GHTXkPw<$H z7)T~)gmSgPO!fqp=r)r+@4-|&)*|g>-tO`&|cr?C-IL7GVXK36IsvY(VQrA7SSDV7)^!! z`E7ya24A|N4@7BL&}iPZfR&h~4d%b5S8=gLQWJUQTAa9MFp*6`PNpS9uW#|hr1 z@5tq4c8xzLV-Mb>drn7%M%jauIq&J-5r{S=wh_$NAe6B7&m0I9if64*F*M)p2&`mZU+!CFcW#GB2p1mG<>P~}mvy+=!^kF^*_Wmm3!OTy0Y z+5@;We236Ug|c^uXve|AmozYcs4)VL0=k?J09a}TD<0%F0E+XqDj!1i_&sUaPaWzg zumiz#(jbght?im)m+{3deo^fvTw^9psKBR3v6CU6sxv+Bhi?N#ps8?62bnA{;Dm&} zhcYdYzi1JHj|Z|Ob>Vz@L$)sCqwW_v3>=J$oqrK=WMNB$XE#Cy2X!1KhU5A3Zs#S+ zl=DP?%1x4PQZ;s;kLJ3VyKfeA#yni(Gga4R^2xinG=+}>0o0cJc}djHdk6k2c@DK7 z9ia1ZG;QtL4^y1(Os%%u^f<$XP63au(j!gzrq{LN)jOG)A)nSX8|4k4VUjLs{Of*bo0PT%jfc;=%pnc^kserzT z(E*W`$hizP$#uQEgp+5V`giTj6ub*(_Y->AX>T`4eK(&U=&u$`O<5hMtgwD$+j2C;GL_@j?lz;)ajv`Mes6{Ef0U8_kEcG9{490dd!($!D zEP%$OVZd`uI@Qr$0^D4el7=k4it%m(kPqQpXJHEuTDKF36b8={&?8i1%}D((1+Jj!h zh+HD?4ez-Z&BH|!ZyTR#RDN{V+02r~Cct)|sguC66ruUy?qT)03T{gL-jpt7}J+;fhx^| z+J3UJAQRgxqQ3P(IDa$oz$Zo~GK`@(pzUOCGHhF$|#Ys$IXc!}zn~z1riM z@@81a>7n1LC@_K$9pfm1z4DR!s)(Y?^F3*AON~Y6U}^L4m^+ zzC~Ps<=nXvR12^Y%S+w>s=p`mK-~X(U*6LgLX4suI{*RoT)$*Pyn9r&CKV8v>o3v5 zVD=iFFcCcE(Ymroj-rrKZx>e z_}%?;KcA?zS$B;L(PFRSF#4F4t(K;pJNe_o;xGGFFqUMa0JI!73US?dd@3a5AW?)^W3j zh6qNc%%bfn$egJ^V};W}Dw>$gl78qg#(=&pr1W4y*e&IHW4Tw1 zz>sm69AE6rd7 z*{@5T*5BCJG>$wl-iXQD5&ko$X#ov{Oq2vzyyqY1vE>C`*lQOiX3YNz3mcvKt&y?pHsx(+YGr(`{uCp5}s`g>oE8>SVeC#xnC0Jr&<1 zUU6-}`9~hto>|n1$2Sa}tzwcbU>^#1+BKlF(uh7<1$0+(un4@cxv=EHVej*OPvC`% zY)%Tu?_*Ci{4h+2D_0lwG+NROj?% z)w}DfA4tLDKFNh;DMfe}?h>w4Ts#?;o@4Lf;;XJ`#Mi@t=Lu|hiwz83>*MUHs4jW^z=o&B3zSn&Oz$ud(V z`39tVm}HJRWr)gv6U&5=26aDNamIP$&;|hH?)Kl97#%wan>$dT{xs=AD{l*GLb^Z# z5c)>AfVc`dV)ZF?P43ZehTP4an@I18L>3Kb)hw_s-ULP>8h}LyM4LQ9xqhimU;<03 zJQ0h@ltU(aYHG>b91+@CN;Ak2R#5}0IT7%J4c>At3r+x}Hiz$b)4TvFPoE$JeGy@h z$$>wnw1%|i+MuOE(hIOp^Rb!chmvBXG=`+@?J6P6u0yOvcAsCkrznlN4vFs8TIl?A zK331U&+poAh!VBd(y3fEfv2t(9`$>C9JpwE-8iTt&AmL~N42M7Rsz%cBSjw9)`<2e z&OuJgYv)-Ip^VVI1U?^E9)a8sKwIHf?`nB(I8_fK5CV{y0%=$Ng0+nNu8K=Aa#u;F z1(Ewihk7J5;!TUl3wLHV()RVHxC!L(aKbyG2s%Ld0Gpf(JE`Zw4pjiRaRO5)dVXr3 z4aoIwJL+8b0}CLuuh}Vezg9VT_(icT&C-isOcy0v{w~dO%@2DmLTz6+Yv7US4))|- zmEx#~s`oU@cGt_+Wa|z|@$U0JOmZPc>vZU>OAE9yiT>Tsz2{sSWgV+$O02A`tfMVL zaK|k^^da&fK$=10#lu7*AbS?C1}B;lA!Q#w2KnyK>^UhWF1g4(WloU1H%xQm6|K@l zO40lvjh;vlHU~6)a-sw2GA7;o@tNz7lFGi9X{1^-Bpf61Jk6R4v{-mAJ+=d0SuH1k zpy}t}ez*U_lzQT$=lpghkI4N!rU9n4w)swLGa}H>Mfg~;*a6=}G32y;eXL*7^bohQ z+HITA*W>sLqf*vu>gen>cA`+Zkz^<%EN!@j5`o}bE&_zJs`yvt;Wt_CkEQW0a7ArP zfH*OQnS+~f>RlEsXb*v5MgBg__BH{qF>>TY5&N#>-`0UeU+Te~Vwf0REHq zi9`7|?HwrQm-L0m656A}^)%mH`7+k^;cCLrW^L>*(gy)-KHmR{6N#Gd`<98occ zBkMpR_@4h3YU1>8^r)7I#o1GnKNkUy+`mUXQ_^e-Xb6)O>+ahdAV-E~3yc$nRwx%$3&etv#>H;2!{ zk}i2)C(glkEn{kM_?o`~GCBL9)cw=8LsxyvWse4&F9VVy#N6qCenbMx8B`8@N3<3U zEHI~7#&K~7fZ+7;Lf~(bmEUhlG}AEt4CCd!&qq(Y-^-5!#p-DWQ~9My;Suh4`uQp3 zlG39%>WNO~Eg+j>-k+A12yUJCgU~>b&6+M$pYC-U?Rj@A2lNg30t7%lX|c3^&-5Vv znp?o&r{& z=)$&IKL~?&GLnDbxoi#lrQz)E#M+wZdJ(WYqf>qvJo zAA!(bZ}}X6LW2(KeCHh^c%6U)Dh(wqzhus5P*Zosc6N7$ALxwOUNluj0A7d#6{R6d z-(Qff^$CbM4E)OFGq7H5$HR5TSv&~2D<}__n1@V`jf4gzhnvEjqLrri@J<_Y@41;a z%kJB%2&wF?uz&?CQOWfczC_OdBV0qXg7nlJzK?OZr6l-X8M^?ZGCOArftUVO2QG4; z3azSna%MC>8vW$rRgWExS6r9qH+yAj^3W0TEc=#Yi`{@8lpNB}lLyf&5lyN3s_>5{yI##^N5> z9_$|)be!!Dl`plVoVlow6(y?#R9ShQnTx*h^s@m8@pmV91H28Lv7HT-f~x1!x$SEz z*_@**S!%(Ygj8}j;xIE`(Qcs;z;0+*!qHG^!MBAJk?LJW#cg?vO1V`w$nkyPC0{!3 z>5Q;H$oFn8m7j#o4#ZxN@p*bIgQYDiBc!Yi`>`t_nb5AntxPC%(st!1tU)T$N{|)F ziO6FJ!nRgr24kH*w%E6gGZzbillpOD%Z1#_+}#wQMM>odL)y)<#zGay5wpRXCnu!v zXUp=YPPdTNO$Xpfp6OFQ^UC&Xn7LQkWyb(4XS`OWxSu$*6EWy;NCSdp)pcT^htz%z*yM+@(pz#aEf8LbY!6+h-{lgKo;)n!hHp89uuuT>Hzdx)??Hxh(0?6*3IUs zrw)|%IPTR`$GvFZB?6Z zzQ6sadnA!tz#Oz8r}_;LUevN0O!zbgq^E0n2?0Q5%HxtOBKG5}okl{IJX~Z^;87Tz z7B^FZ_Y)r>qIp5!~wBn+aOL*XDG|i0AulJ!+EtO zK0c?Oc1lLa(11hTvgp`?SidFAzLZeH8kPOGInl1&+f!&vrjm+z>FOdx9L>?TIj#3O zVg@2=qneDrMSNBY?yY&j_`ESP+5&c~V+($9u^#v1ihw-3qh(6F-m{Q=4g}3?=yN%% z@`uI^xDm7S1||?_VC3xkrCDF~FpE%WAfS5-)!WUaBkeeOd*{bk++0Y@?Fa^D^7iLg z9%k(;dS|E$DxH7Gk7&%4q)T$$LIdX=nFF@+fqd|81p zt4dT-Et@yn*(-N>Oq} z$M?p8=^s8kUX^)76lOijR}Kli!S^Brz?RprC0;7dh!g&c3G;I=LdZf{Qgvv4I%!a* z@Lzoq&MI>kkt4{@^+gs!996AS zJ;qVTha<*0P)vIjysiQ(PJteOTSG{yAcXQc;5eBlGgyHL*kVCczXP7liGUM&N3`a- zG(n3cCI3Z|QX=S+liRXFzklP3F|A1we@I(58f~gcOwVCtVSa&KxEb*;kQ^X9;w^v_ z-W&F3jVT5?_|l2fV~P5|HFJ?e<%@L4>QW_E%7?2C-{J`nDr6(lj-x{G!`IWf2-rtK z$6)1+qmNl;Z(nyCM!)GYB$0T=_#pnX%RaD3&Ei=Sc=qxhlBbBrfA#6y-;&pWJ7~h) z;4y`H&q*Uh)lj!OI_oKW7r7C67`mS?{ytLLy^7?QDOk&Zx4W+IJZGB=Lq*5s?~xQz zu}VOi3bO;Sj8<~g^1#%xmV6ry_nZE{5w{{%bCB_IdflmXT$u*-qA$T z>1k9ImsvkEJyKEP2{BIp#@$B3IKOR3{wN<72M?dGsCn{&IV3#kD6B8Z8nPSyOs^=1 zY2jS<1o}5%5joTQ{vzf7>VVSDE9kz*Ws3#PjP2=yDFH=C&^Vm<asWD2O{kFd9s?| zM>zNBqvLi~DWZYH?)+;7Dgjmmy!|~r-)VhzUl)Lpg&F{iWFcGbmqL&`L+gDlI$%hw ztCiF{fYChr$%)17H#~n(e!p@CTq`Sf8;LD~PUiPdXr*HSrR_|*c@h%d=INjwQAijZ z_%l;LAlcQBx>2|Lof){WtpW#CR^y0sisNJ7Ql84L)WgCpbOs$J!|AL3mw7bft4`eK zLT#R&V()1H5&>n9-*qMgpgI^1JJZ0UJDkKJ6nRIO3vODimG5~pwswR%^!zY$B))1_z!%FJw6~tDI z;%TBtnLN0hVaM8c1u=y|h}hD_V)+%!qN6h+9LI#`cd!msp1RfYTy5F_PbwL#J!($B zvqW{MwUk7e^zH1JjC=j+)o&8})mI5R-M~hqYiG*b#;9YX^=<6H5NbpuJI$YZKKCW~ zW762zeVszyIQLG!wtjBOPJq=qS3t9)u^9Kl&$(;6b6c#~dmsg3mx&IUE;y|t3JYXB zFjYiu;DYDd?}`I1ruLeII^1o8GOyzwr=12uRAJ?hJB8pRqkB_6>Sm_UxGW+)80T&A zvyI;(Rl^cSJ#-+^HS8^tfvf`TZ`Tj^*LH_=_JIiDW7mh%p{a>QXVnj^4I9}sxdFgt z-^-a<0C>u&cg%w?!z^GI9azBbwMTBW9G4ak3!b(=vq^xC1j}`AvB+2NZ}lLd#gY-Z z5d^)j5lRb=6{DZzIpo!~FM#wUFJ-A0#Y;YX`NLQHq3U$XcXyAoA4ezSAb`xD8Xi`z zEhxTqsay!G4(xt8j@?=P(bh}&OG^vEwIGvc$9yV7c19KHj2Y*6;{f5j2)Np8WkzV_ ztpvvUyJpWdm^)=6q;#`)@eELhL;l`JFrvxa>`GKYa@ImE`N7Vt2$z^&UQv1qR$vA_b>Q z14-yk+{9Swn8J2=jYx~ae)OH`KSBp7m{1j0FXJ+3SB!L}4`kuYr!(^$do>(LE=#Pa*v zQi0sX9Js;C@u#WPq*8bF!%^cRs_A(6xefSQ6p>6~SAXB}Crj)~o*++IkU&HTqJEfeB5On*TvEW;@DP)+8z&#f zi!%VLN96iEO-l*>^vQl+fT7oFK@3W!bRV#bUi%^A^$AT3#D<~9?MX?g2os;ldhZnq z2(M^mLV445drD889-+9$9B;E!o9w>Fsy)Hp%X zN9J?>dYmCF$yhF<2@z8LtFN3>vFmS;&_zh{Kyfaw=00{c>y7G;TA>sH5yYy&=1UyN zlV<8R)P*}L9E>Zo78Mm-U*j&K`X?ycm#=O>xdBg)C2alSnCi&Dt@)5COgcJs z9S8$8yUDUEuPRTfOLJBgVn~Z~16)MVMT#I+Od%3#8PPYBlWN+DJI}FJ{O5@tqT5Q z)#!Iej9eJ4a7<8VPu_kEtbob>#@cKoe}Zd8YwmU!$qT{5Bw^m4Gp>{4CLt_C&w-2O zuUuWpk-}32wXseK&m6 z=b>F`|K)zHYp^+KlO>;Mu(c#4mDn7y#h0y?Cw?@BUP)mHyhf5x>uLJtNCNN=bTA*J(6)^M@G?gsQ$|XOPC4ciu}>8m7cWa-J)&Q-S?bvSD~tPni0LkON;kmsd3)y)5f8E8T25e>aJ#Vra` zFQH7zf|?oi1twv^Q=k|_T7`C9klt(i@IO}qvDJX}jCVdawam!EV1RqW$G$q(po>ji zm%@32cO3m11ufE2vxJqtkdNnb^dTB{)05a;)Q*ts73(P)XUv z2)Qko(O>PwyL0m>ko5DT@_4?8cTArfnC0C$E+>p<^MFta<@68Ar<%IwTMI%gD5Wakj>JH#RQS_;ET$c0aSS1jeO3>i4y>7))Bl3L5PU`}GzOx^9~?#X<=XrC3vj>$Zmvvw_rLV6;M?%|hB3dGGW+4y{lc!AP5S2KgP0V%~@Yw~q#KIqso&Acm2RTh<`8QWz}YbanmG7Ssx7z%Pb|o1*

g2N6u9iF0$9L!Sj!4#sv-fu2{K4P0eA0Qwfit1_+;GjdIyf&e&pBlm`Hphkz{f6 zHVSPEkjt{be|f3t1vdacjVA}QT%6WXe3~G4ntUKmQ#jq*pB3iR*MhrV4}N$`OhR3` z46|h7Qb!&iZPakr%ZP!vol-VtzI1X^AcEd|1AygjI9=3tdrQK1O<{0HcVbm%RJy))=oq^9H11|eK8m_wCx|uo`iPlLqP7k5fLOWR=xrLSu-ISu6ny|N9j`NR zOu5cp%XQa3Q-5e=-UGj>Q{wt`pfQr)&5f7aA~eJf7bXU~2(z6!ZFr;j_cnc;4Uea^ zKfqPurJ1pS6;5~COHBlwD&3Xw_=qea=@2KM0AA%j_B*%c0^RmjR z>8jQC^rzR48&Q+G+!1REH+|V5@}FA_W+{}lQu{uNUKaFy%@_Cvpu9SXg1)MWy<2`a@oUakudbm-eF@1Cz+xZQ8A-r60a5EI^)(Gqfm*>1XSww_ z?jp!-RB&#oLf~^)S98tj-1@ib819uU<<`C&*XjoNRYW3&T_ybIN4juY-3KLO#x#wo zlwp$d{6XDVe!YX4fGnA=paD4{MoG-u0`v5GeAxW6pGT&|>dRhvtofZ!K@P^7VpBJ6 z1Om24Cj8of0WdrB_I^1~yq8nHqpDoihV{QY8hANrnwluU0SD3P0^CVa(e+6t0dZu#bjHUr^~ortEd`rqz`(2$I+F3`z2NLegTVNQ1#C&PDU+r#{G*;Q`aRdj z3UBi!FOmDSffy>=u_Tvs4NrM9q>& zK4eHmg4_EszEl|WVcx!S^;YF4Q4RN#rZTq-3`=?1py@?eht6Zs^TKa`>vhYfKSmgB z0v0PAQphMuUHE~QR)e*lke%e15-Uo3dacj}gt$j4t?Kx`+X&JU@|;hD#75b+)Md5v z9|vHI(IQhNlxnP`kfj6nblyX9^Q@b7nauzzx6sHxpg!4xgU z^5`Yw@f{^{npZEx)7vc^gQBi~`IaLOZ<&pSH3CvoL%zDaJDz5xp|{rOh-~@C^TWqv zV>DtGtAL$SPhCz8(Ouv3T4V?7hxSQ&9kh(dD^e)2R8wmBgBKWi5meu~Znt-~RO8^b z-niuQ7v8D|BgQFlbg9lIQi0f#KrB!RnMV8JjI0#^(+_Po1#z39=A+pzNypuD48#Hx zOv;}UODVK}e#Vxktc6;{hF6I`8>~LB-m+jJgnFV-jnkl#e*&;)B@kjl#Omr+20`ib z!xegD+~n8+bIM$Lg^1TN*xvc1mTZYAXT0eqv1M#N86vYXF)?)gCuu7^BpOSIqQp;z9hY|R#{*3! z+j)l*iEimm1l$IbvM}nM#9MK*;dXY;y^T0Yqw3d^uF?_QyRzNv{~w4<`wf7|fJ^nv z<+)IG4FDF!gwd6^>NIf_w(vJee^Iibuwu2Hs}g&`|7CJAHF|KM`YBuT81SsXe%Ri5 z-td%-+*hms{alZmy;I9bDsz$1cdsTZ)fBHuruzS|b>`tv{{7omDM=BsWK9^^_ZVww zkxYby?7NUsa!9bbs&rxqrXs>AyN0M_lH* zuFv~?pXclJViJsX#YkYS66h;MU>a=$D*atgZU!ij3&Vuk4H zGBPlR#)5OgT&P4_9=0}rSj@ywhhAtyTcS!W{URCuz_V4L&}poa3l51bq+k?5g!C)1 z4>12&^i13_uo<`ToR%3;;zJ&$I0rQ8Spdgx(W=Fs2a=cF<%?u?v8r&eF+mhab&*pswM#5-W|Hk;V0L|gog3UCL z^uJB_a#2#n@KDGCF2<_hDXD(slJ=8#h~4xldbVDqs)EcnQGx^z$ggJQr1+rJK6OFt z2cn^^_?dl1q1{5S%Td@$VqLh6Iq5tF{~cjCS^0U|=b z4!1rl-Csuolm1`tFOUGi>U_s@=Uj~O!ljWZT{1{GJS>PEi7MEdHbDap&ZGuBM&wA* zm&Qe?2vtp%_?*szCSgOvT^Z^{?>9-;SMaq`_STA=W*Ba9mJZa(gE_VPqx(e!->AFm zcDi~Jbdl~H?-ZloyiLU4lgl!8CNa)Y9&sjP=80bG11CwW3Dx)OkDc9zJxXT@aGv=M zOvX!f4g&j$#H;WQhk$yiVce3RDH$~5V;onqtDU@@=)*YP;@F#h%?=6l+~+P!AzuS` z=9`CgL6SGo7fF^0*SmM4;r_z%nj|DmwB+cW>s%AC?jFl$NIzXw3RT+e4q%PFb@{|B zr8aB9s0@9WnCSM%$ww|3^$sqyPT;o!v(?uYHl$Ij2Lj3^iIrN1c`d3ZR_~2~)8YRf z#)}g;^89XY4GTP4=W|VmD_E$`e72$Yncyl+Ajc^C=*dn55F#&JxN!e1vy7)> z24l58zhH_&`V2u1;1F_eKGBj8krD#BXN4f9#McZ`;X5(Wk#1Ya70|tVifaoz=Yw_u z`0)`BCu7%6#N288WWUNAU$+2OwB~_l2@cgQXyu=cCS{0Td1RtrFwGDPR9%& zYTGctG1~ZD7>!EI9`V@d$sh5EW0g1w0~u+)!|BldKXW|)A(JTooDDztCG#Sd(@fx* z>8|ihkkcmK^fXQk+m0W*oDrQfb+SgJ*txETXnkN|yCJi?{j>09Mu%ilNB$LqGUw|q z>I&4aO)!5XOS{>;@`HcvrBaRgWsSQ?6Q`*`GA;6ZVH<7^m*I9YQnho9 zFuXQOz3NmrZyRI$iv2T|%@Q6Ny6OMX%-asuJ>6|m%P_d0t529FV~UjvTAmyvcZV>& z_b{G)p|*Ve9Aelw39OnAi+$~C(#u5LToR~Id>wetiS1Ge60p#2t!azNYNiw9iVALq z_26eX^jMq|*hF5!mz*DwmV6MWEQWR30VkIGp|$Z0;KYu0z$|_OT{cfEdi)Kr=k=V0 zBz<(J?AgiH6p?U-^&jEM^0ehohUUnv<2~!C9k#3UZ&@MrZhBs|z6+7L?!%nu<2S}$ z6R{N63iJt{WwexWQLggew7hCD7?C2vK9F|}-HEVWr-a0;zDkzzF+)WL?>3i$BLNmPs$e`Nqcg!%|HSy%&J4f0b; zSQETLxb!3&SR+{wE4OhArBZG_Rhx6Rixonqxgz@2<(|0E$%UaU1 z*qFTFxzHD~V9EQ=wmaZ1=T?bO&@y6H^?D8Bk!j%e`@MJ*d5?!aiD^#GUSBjZ3%b|u zI$ljL9KI9@H6&bsSf2;a)eP`x-Y} zUQt*Zg<#Xe8e9PH(++%Z@_#!J)4M|I#~+e4h58IrgCT zcXKU;7{C2Pn`h%o*Jf*qj+F45VWK%B$WoU+eSRn*#1oG zI|0nmKF6((2E=!+&M$ojXBDv9sU7)Bfl#j$O|0{SKOPsEwozl!6h`FRk&LWC!xy`( zcWJS!h8-*>nJod1Eq)pKH^gZoxtMOo5B;kQ%(lf)CnlVU)6-;I)A#sV@60`WVv+o3 z6YbEQzi38*S!}`@D8KOA=|Ed^H%2a+gh*d1BflM-QPG$Z7^2U7!=37m61OK0c8N zimPeBDmE$d_C7>mA$ZvpiBgqEFSW8P3NCa0-B!XaHwO&Qf`Dgpd zhrOe_uF3Rebqj;b7k;dUwXL2{ecQ@Zg%_0f|I!pVp@C+5^%+X(iV8@9IfHbB?q_$t?QHT^A$7?@qXGf zB+|f)K*jJq;B@#7_PWz*|Jmy{W_*p}gs9>aZoOwxg)kZDrPi9m0|>~tIL|j@$HRBe z49|(Y{-UkYIn#cEv;eMW0B#_-lFrKkRD_pb8zyQi-a3@-c2oGJkipSRNuPVqT_1|9 zso1~=io|Auz}f0!C9B@SXOl#beycWAN^&3meE+q8bMh^v!@7}LC~EUHuE~lhAIKC* zWpq;lnem<@Z6vqD?LNatjxYCg#ii7&8`ce<pVYO-sTr#V@CtkTxeEq0Q#Vx3Zq0pUOxgma`W6@4~O0rG4r{+oyA zm;4wgPK}D@@k~5BtL=9eeAi54#*edMG@3vGlD!dL9&Tl8!^*~NcBxbSVYcRH%2B#n z$2aAQ(iKPN>?{qoA#1Vg?Bg}`JsWh@;M zNXa)42ocOoAEiaxMt(Bt5s3J|*;IhM-Wq>-K;UY_+?jnRR@R2m8F->=vVp_Lgiwk@ zeP)GXcwN4MYck|bZ&;BX-V84?;w;tM+U~(uW$}2Xs(78o=Mwj@<1j9Sv%<~LQdUne z-ZYH3&oBmRnEJzOW|W$SXC<(TF6`V*3bx69yMn_mzyg5R7GgREF<>zyYR~QW2mCr2 zi}U-rxh%`b2UL7eg;%Yt2EStjtQ7_I8d{ksG+(1NfePrZ*_QXE67{N;KXhY5Ri>V_ zGZy!vZKwcM{bfBdN@eoPsGGk@SI&?u;de#`Og~&x`3Qw}PrzOJdU8D=u@2yNBbWm- zWwO!uuc{uS#r&p)3qp${Eh9~~APG&CPaM#i{jQt{5|%RzdVNlbkAt_wvAJKCZ;9Mq z@tSGDgm>Y+{%}w&xqX#EeEO;}Drr2s8oz(;-AJv+aQ~HR5Q?3B5-Z)v?oq_OeHMIx*$S#jj72njRDr3g3^~3rvqlmT4WKmdZI`;j>|3N%gJUJ z7>{8bXk7mbtTB~vSYjOdM7{1=+Q+{lx9(w!64n{Rh2SU+c5_K61;p%`oWd?Jr{@`_ zWd^3uIqWdqHW876oVX3|vfY3r)sS7My&>BMc`tFz?Fpsa{mkp^>=K8yH112|^`TvO zzl|H=X>#hX%BUoLnm+!r+s+cZ%NbH67k|>;m zeRh>`JYBy%i7TN?C*nEpx{kf@=K`zDhxfQ3xwO?T+S%eztq0kEkSA7#lWSmz^aIAj z+{Y#5=8@b8Rec}b#ys}In~5*%WB(5MvKXtkNA{MjrE(V1XCz6XUlUplXux`4=Q}yX zbZCh1)bZ*9;EnLv15iO&{=IHscd|+zb7f(Y!8oIxyS__K)f_#sQ_>H5VLUYX^|4BN zVl5Bav^bwwEdLXiWI{>betdkq7U<`_Ia|Po;i1GQ6A_!INHwoyyaa#8Ni{@s83;!F zeDE6*pH;+J_}tW7213y&j3IW<2{MEEpY$X9Tlz6eIQZY0H{44f6>bA z3$}~ARYw=dOdj|U#IYLl7YfvBuUt`OS1XMKZlEQPn-OqHT(ZjA+i&pg;3ky1=;)(P`_zQtT26bOzs|(OzTC7W6wsy99iNz|`%B-w z$j_Wul4Rw6;_xKHuP_LnUeT7h0Ry)mn2q|LHJ#G1t7?f|y-{VOXPPu|WCs29BK8@L zsViXnG^j_x)4p)X=7m&*;S`&9EwgeR z*N>M&vGP`n85PJVh3+tbk0FyYPpqr$Plp7b z1I$LQyhaSGAK@H6xr+U=5`ro~esbVijB&eBtTEqjmK)pDWA}d*l$VQGL5>FtkM)WM zznubK>(ra`r@l?`lO2++@4=vGT6nlo%p7es28daia_sTndM$?;P-B1n+y5kb za|Imv>;W=RWtN9YV%G+~&WZ-NLdpB*dt<_)yi3al=JYdjh%qE8*TdYI3Sl|Jm2~>iemBBuDI`hpj8gJ|~ZfC2*<4n0$YuJA4ms=Z= z_+f>^F;%hK7$`=v%15w}aCYOVL>0Tqdp~Agb}GJ7IHa?aLqtW52wya;FXMs{RA*Nk z4b7=)fUZTJ52KEAiwcUJpWiM2Rzjvwy@#DZ;9w#fkASbWIPvVJ)|bqKKZzc=?JLBg;a3odx{<8yhAqnfvQmIXvh?&A|Q6 z6uo+mk=@Ci1&pA)+q7G2h`&_;-DjV3al0?ZEnfZeYk!SP9RoIaGN@YT^a8?Jev~3{ ztZA6{Twj_khla!RfQ*dH0j2KMklg0=2NqskJ&vinJa&q}8;~GH>lOY%+%;I+@w0(n1efDKpd|MJKL8lh z07T2B4naVXleqrc*(iIlYPsm88HA#Srrt$I)^94_^cN3j)nS-ISYJKqJ16~7&5bIp z&5PIcq#Yhf{`*4}ZeyPgHPM%)wlNbf1>qot!myjzybf)Y(ZyE%$vRm&NO5=yv!8Kpn$Q4$(a^eMrU9AQ9Ke)Q2(%A)1y}GWsac=oCre{~Ixd)p`g~UyH zZ`!{al-*kztdwEVzZ~}6{fX&QiR9LlS$-Ew_#cxSzBc@vZ^YY8A3-Fs^>8}NNB+c% z=5QS>)e}{d;{~Gpn zLKFs_W|R^tU`pNr6?Lb9XZm03{*MnYr`*6t$ramOQP(o%5PsMa`8ROO`bi?rI<(IE zt`Y2NBi?&wr6uvRex0R*vK65nuYK82i23Zn2j@q*GS$TRk5Khoqv z&=`ZvYOZXcT-_5~iUko9L)}IUVom{ifP;g5@G0oD@;@oKGei*6H8s&*X~)M7-@My` zXOoLc_5(-McrsEO=Y)_bKMvUS<1R3vy5=2tmjnAwWtnBNwmWxj#8)>8&t(_J~Z z+|FdZ#<|7elQjdj-uUq%Pw`pSlmuuS!XCNjPLfAdDH6#WO(7lB|pq%MRMTk!D{|a`f>OE zhWCD#&&~Mgq1vhi5Q5q(bcq$#+XW}Q|1xN+?wyIGI2SDwLtIl|3VKvmqP#Cn`tgyOaLTovK=LZ*iKdVKkZR}O6Q@IuEop~V zOt2a1{0>LL<5Doq*Q907D(`c`Wz6ohCvl5wUlXG4JJu98t0(Ubg?DL)Jf z-I+~{qm{ZVqN%oV!;_+OBazR3kiLx@Vcrv(q(A}?oq*?KL2=Q*i-7gKWYsd)9E|;5 zGj>T)@vx9`^n9{_h`@#V0Q;iAz)w+;m3u~#MQAe6hU~xnQsZ&z!S-Nhp?}`2jOJCp z-F=z;Yw@d|fwFq0hf5FpDw+N7%x2F?AgB_(3iRvjNy-=ZuJlt(cR2_8N0AhqOzJhJ zGChB&dOtFO%DggiTq`+pRhDioeZnrThdcx>`E|rBF-+J4()Q8)=7zqp;iok^7KD2K zmoEtlyysB?2Q$2S0XsL3>0Ye^oxL}rL-VhbA%48*d=T#H_}Az3{;v)Ricx`=Wv?cK zr(>JDBy>TKzjwcZSP3KC&C6a6eBf?Rzu@l0_E+twbk&}bg!++jK-Q#)|ke= z)A(S&4k7)Rg0WuFTit&C*(^`=>xjrZ4tj-ph2Kz$)jM=`F$jep&OZTA=2C{vm;#rm zTwBII&=?*hvst8Ho4Z4wcbb{x#ng>5m@Eg<#GRloEYq+yuR%~Y)HJBWiNR1#WW#?u zVo|@wgXU0S>#NfrK07UloMCm|j=vf^Fa*0a#gRgK)0RpVrFYn2oFvOX$Eb(37QO8n zaC7n2{6|^kkaKq*b^1fR-u5%9yC)f4Ux)1tU_1V&Hyyw>_dEe0oByh1;T5{K0RN}3 zAcpOgO*Qp%p%2GCeyIX>77^z#oyHdpvO*L8L+k>(52G0(tq^Gckr#1SE?%ZfB8X>1 z9({ENA_h^{{1CM~AOXFX$K%KTNJ!@P7d%pd^wwP&rjY+e^^U;KpGCo(aFamwM@nuxGd0;cR&cN3eUF|M+;^MED7JkUQ7tsW80&-Rt22tx|iy=62hp?jLdH@&tXJ}h;eb)K>3UD zUs@h?HzBVT91XDoU3myx1(4ddzdU28+jbsLX3&6pY+5Y0K1vmoZ<2A|9yfzIon(4W z21iYK1igEKY2FOoFcJkARuf-8g{knufrBNdN7!(&lN{VSV7OI4oaCq%qb_?HoR-8= zFYv?(UejBgn+$+Y2s&N;A@is(B|!zE`di5gqL&>B&+2t?%_m<2$!eukJS`(n+tXdA zB%LC+0})M^)lHo%!aMclk|EJa#rsSu^E755!tn`0gh0zW=!WvBkq!4H4$ z8lCOx@Z<42or=l7tIUb-O5dv|u9rN1`s{<9e_I}9ao-FA@`U){%FBO20p6_Y0}#O= zoAG0PW3909X8;N3qc=KpieNB^tM)H7Z_TsrY?R`;CkvePqwoo7l2e(UazyTDX>9{2 zA>khIvq2@C&q@6Ap7t4mQ7h(Ex(AQA9tuF1imWa#K-tseAWxzSWl=g7C@WufHHil-5eCsUmlt;@Hg9OBSq&f@Kzq-m$0@m$qIELb}9L6?(xgor5 z+9t~m!!UJd6hH+|0?|})Fp<}$ige&0(coM}`kucQxVc(?ymq`d9aUIYyET7_D$(%O z;RI_%a>T?wUOg?XertzBRj8Sd(L*rAnR*`?&l}Zmxcv#B;gEw=Iep&W=3W?&$Mu+2 zUa5TGeN)Uw4I=6DqdgudEe%qWm%{~FWmvAM76`v;9ji;FV$0XqasLt=?VK7Q;};}D z-I4Bd#=sSN`|1zAln>W`cmwLz#<;cF>=dUnLqXXCsY#pfY0K#wJKf4TF@QB1)^W+| z!#}S3BILgnmOJkwB^Q5djV^uWc@mDphkh1>*s@LSDO(ina-5IG0mp?;Bk#L!fF)r= z90sInqf<~w#&RB*%G2@$>VPF&57;L^eLcy-VK&METr^U+TAVa0xTc=&$Z<3uts<2$1{V?8C3bl>rHF)M<&p7#j|n8i&=r+%^}X{`ftU4 zAf*|rFm7g_j(lc^L&<~Vl2a%CncV>N(|JG|3*4PR|MwLDWD=ScKAIdQEN~(G8Q=^2 zVh({3^c_h6*cAiQpoR{-Bf^MywaH8ESNY_O@6NV!?B&Xtm$ukzdyCbFa)9Pe6 z;)I}~)dqEvvz>e^oG%xEoxCZ0qTYO+Ib|Om2z>fWDGJul6x~Le$)y`Zzis0@PJ@H- zS?&rY5{5M;kIk)1*^NA`Pd)?^MmS>}PgnCB|Nt z@HTO(6#(2c$>jnc5Z?Up%|H|fWT)b)BQ43KJUDLRtR_pCAhXXb6t6G0p=}tt0V1TYSg+ zilrrvr`)zZ#*gV)QsyOsH14&K%N@{gq<^Xp{&{xdsZR$A9Eb=ndVV0szA}Gm(z)$= zBehP9^zHMowM58?&!2YMGudxClJE_taWF3v$n%`-^pqE#2}nQ4S>+t(^*&&OKNho3 z(W-NJ(FV7U#BDb)(GYa1dxvE{8*kB?=-B7Xz18|DOvoXu8K*Q}a53n>`+BZg8GM7+ z%yF^aA|#XD4;Fv&*44aiNG!U_NPNdr`xa8BBH~ej@#Y-eMSX?FBz5FD6gwi#^TO7#Z^f>jRH3Nh>_@;d+u6kFYSiXm60dULSjxM>hwqe(;=!P7bU9$@bN?7a*)XH!GwjwktxN zm*uM2blaGXYV$soXUiRiy>zz;F&y~z;|xU}pAz%OIOp$~;n$XC+o*LCWFB)(@vpG{ z)RU?}vW0ryT?WSAnSDHaF&ooMiNPQA7Q|r_c`A)4sVX_st+OSgc^c%wOHAQ^?pcii zc7?Jvq+NJRBaieoM}7cbTTOBHSGxcFA>c1*I6^g?*Vb_A%09eKs~aE)#z$rRyY|bM z)4Sg7Fp(WVWHUaVYN}ri$8D5uUU$dT9t+BpYsw(*u;BE}FK?cMH%lminBCYA;L-sq zTb|!1j%w;~%eU9!PQ^uuf@a0P4i&JwQ5jm!)@aM*e2eor^ZD7BdkQOJcDz+h#0o69 ze#z9Vw&GVr0zineczN{eL;5_(euQzG8sOYr(^_tvWQ4Re-=PjK;l9<)b{`Bf01po% zfHg#-j#ZT(=?ua~?fw zmt_Xb>2ok7eLe<8U@jw+)F4?C!ymIh0xf?5t6M$|@V@%ncC^N^r$~#Nc|}Nl!_NNT zxs3j9tN7qE?VnvRJ$EyY!k9E79~_7(9li^m#z(g4x;90DK!S%nsZX%MJ`H_N!m=HN{jWnlUnV^P7Y3-ig>FDL>8 z;z&Ei{VOrd(5j+qx42AU7Q=U%i06@PjkIl$(hwO5r`AFXIJ#O1S z(Ava5T|f+aFB@?{5lk^cB0|i|cK6QCYJ)g(Rs^fgJ@$TEa_1awf8Hg&3VGa_U!|5? zzE#F^k+`#+u^j&KJTBtLm51gmLQDm1|D+^fPZq8 z*77UZ%~dT6%mARsKeJ5FhkvVTAD-D=)u&pW{#+K*lE|beU0;#Ir%S>o@Ljxn`_YAN zcJ>s?ImD;_c|?|vL2Jv3#LCjPN65|{OQO_hcY(Ybw^#qO3Yj%O`1L{VLT|oj%cuMN ze8lF4Y!r}<^DE1#=IO~O?XzOoTEA=hlfY-I^Or=&fJk{}*O08x_^$*ih}-3tqFC3C zP;9JkV`LC+XS4}I@bH9#n0@0`@KShszsF)`Ha`b=LL^A2#N?Uw?j&^9)K|ass@Bz? zKY8h2q79YnYG9mCxE1Dw5j1Hazjkwn+rIhlk~%?Hi^rya*wnu)?^6gZhbYY@@kUUI z$!DjN+7I$#`uBM3gm=Kd>^<(AffBQ!N$^E)!CKQ@LNaYnhf>{glPg}$`$zN&<7az&EYCt zaoy4XfXV?g9^?xN-XstKnJo6H21zxL(@(|)!5BvFbkW?8IRDk*N+Q@fP_C$aiv#?(9YcXy*F;wAx9(VH^^Z2 z(w^92)Pu8Fw9duRlW?K$_HerFmPCyS{PLkBR7B}80PqZ*U?K#k zA)2{L*a>Vc@O;ELUlT#dzi!ZJ1rIkrKefT$u;-`$@Iqdn!V z3^UP|XvIAI5Qnb$`t<@!j@!WwlabHn8}aj@UWJ4p+^GABuy`~Rk^*>}Kj_XrPYm<0 zjKIGRJGKUF{DT92Z?jNP*0QNx&U6KII?qgIgXkG>(jc7qEgr}r^xiS@72aCT%}u(2 zkdJ%-Z10&97WmKuwV|@+e1xV37a$8{zHIs1h;qE$b0`K?SVOFXLmE$jjLYe^^<)|@ zP?E0j*&ld54Ghnzqws}BaFGpK@y!{_0xSx;51Bn7di_`8mqLIvCG@6Bv*GA`vQZ=X z#ghcaf0Na-*BY<2`hh5l9oSM`Yl{-G66vpG;O%cThkG#HdCLEnxIe>@6dQK~VVD`a z!mTa!fD_nJ4xqzej^UrZXw3a}PRGl5(J}|X*GgRu$kj2GZR(@k)2noZn5<##Zv3xZ zO;f19`ET3yf~X&=5)-tYKzm^_sdrh-``|}toCEsQj|F#;RY!x$cFhe*bxS51h_bJE zwAPu+mAtQ2P79vaV+UmmARc>Cnsee_g?jT>y-Ox~`0n8VNo9Od;Jq9ta24HGZ@8F@ zT9yQqtYp+jhocNenZj=TR*R++7wjCR7hg8}ncEws3tWE%rQ(FYkd$Q?>Ttd#K0O=G zt4fZ^Z+{6d%zo5D4FBmI$3C1N=buH|Y%6N_`z)jd+iLc zWuE-_C!(PLBIe*r*u0`LSWq5>fRlz9+%*Kirm_mDbp?!NLYC=Cvo7uI1!914&1x ziJxo2W2Wb9;;YR{>h-%wBR(tR#!IJe=Dp#I?9HpeVy0g28lH=Pu^Z4(ns|pEqP)Fi zz zgE9_mT=ky(ek@(_e2BZ{)BO`764^;wl}$3Q=s)b((J236Pmx#@&TDOa?x`b*k541} z1WuvbX9Ryeaf>Rqe1Nqr<&toe!7qifs^#$RI%gm0`xzEQ8s&WjE=avQX(C^5>QovH z2T>c@dX*qM4R$b!uh@=u%2Z7?r?l6%SsF>WSKyfU zBAM=w$|*d65v)3^<;@4rAwU&~)=5c1BP}<`nKt*b#Cm~^y7|-Z+h)YdL>@$lAs!{j z65dtY9|9X(DW|o(CbK4uU+#<+^AG*;%0@p%J zUq3$ky?Zhydp}Exl z$r`SRW~<1_wHGYO`NY?Zuk4*oyrUCDcql`CxI(&{zQ{n-FEZ)%Oea|1D&RF*FX{1D24z;*uRfx@FoH=gJw+l z0AR-(80sjN*}&VM%6N28snW>T@#+^ZVnn?5T5HKv$!uH#HpkcXD1Cu4F!ntvXmu=~ zPie0=H1BS7?fH1wfEAacfu)lY4yVDD6lWO6Ew2d+#s}r(RTR{+3m?gG;r#2_!zw!Z z9A6k&GL|_54+VcAmu6yY(OYn-L0+6uGU^MwlPy-o?v9>QC>iFZeTdVmzolCz?Q0hi z3qlC`mj#X8K#1E^ywdLW9!E*uTRZa_Cwys2RPa`|)I(>klxED>L_x1;&@yvx`;b^c z;4Ea4j(=_^?8E<%l-?NgrA!3m{Q4bqrnn}A2!A(8Cw+Y~OF>ra_kyWOe6HGx9N&!v zq|p+BZ)u&pKHdkkg2k(u`fh#w6llowBsN8tU{eKVm7wL3ndk#11?&TJQdm0r)R@JV z$2C)IMt-$JtpbD4-?t`=p_Iu);c9+Pr8%fjq%x*2!!!@B%DKF%VsG#45STpJ8$OX> zm$fg8aFNG28_f)A`Rlv`P_(}VYbWoTB;>fbG5|%PE)x{G_<&QK2_TWw{{;uvjIPMY zm6|-~wKo5I$F+{H^JW?7y11FwZZLc48V#f>M#9KvG1*FbQsS1E|DB$nrS>)a%FwX> zYa;rB1d+>SmcQg&oZ8?OIHh6~(}47(#|vniAsK%g+}p6I#$k>n5*hJ>>GT3EOJK(C zH$+O{c)dq>BV3yPlMtzxyQitgNgDUO8Et6B+Jm)@F?~0{IVnTlBk4aBvGJZg> zz5Tz>Es)|+e^M^dSTYT3?ot5mQhTKvCVIPK!-61q8oInpHCUA!t=o7Jut{XfdRO*V zW$oTFQDL7=(7#o0J|Z!lUk{JwfTSNwcnB! zL8vZsqHbqm6aL-xVMV-PlF-rb+1SMqQ zXE?sPth6kT=uRcBD6akH(4!`anbRzp)O;<(7K~Y_S5XU7(xeB+@g$QeW&=??(A}GA_xtoJM7BhC#%Afqk%cx z6o$FhFRnHILlg6RLS!egDFCh{ivG}p2|f=~!UjSDH)`*!-^{FaUzIS22f^atBSq%D zd((m;a_D7 zZP5_MjtOIWi++BaO<*^a(Znh10)j{XKn(&Imq0pA+M+_STCXD;;7gmyBfphoqU>bU ze2!r;dTfV7sd(Z*JkF!(;F2TgY~*s`U_kncVR_w+c!mI=V=`|sQQv*1Wt?qxzpJ#P zAg8I@q-G`gI>C4vhNh|V?P>;Lp!Y+uuYSJ*vWit2R}@MZNW@K*KUy+8<8v8e*s$q2 zIWK2I&8nH07znH*&jZyD?}Jg{obPvh^qm7zd{-#rfw?Guw0-(HigRD*svLSSL0Vz= z;=gDcs6BrY5d?N0Jq8d8X>50q-sBEO(+Q2H*~S^%KxhI>!@scwVgOrk@gZo@7%Y>H zIgsd&AM`j`XwSMZB)s{r@eAd^A%1RmMbQlT3#=lAwIH?z`Z9^_UqPh(KWlAJMD^Wy zv@}BLmd5eojwgrfd&_Uwx6RgY4nRl-J%cPtjwA0oQ^Ig3ja2n_U7ie31>quYKrT!O z{}~rHPc{a8zP(c2QAv2?Jty{SNv}NRoWj@FeGe)e$>A%j4r|1g6110d-_X%gd%aIr zCXqzY!(<)k{Gw^o1gSQ);5+j|@NzNWye0k<)*_3(m?@|p`GZ2Z5|o{ZMPCev&uxD0 zqkiPidUa}WbT>st`L7RtvdY}@sgZ_)k)(&a`82jaNAZbAzm?`qX=qyYMaOaT=>m=1 z4|}t$=(@wvwZ_sD=aH`d`9#)-QeaSF*~A?U@C|R`Ho-EE_pea%4#%L~h?n*DfFy}| z+%F>2Aa05KtSD_wp6L+hp!^ri4kwHAm%vuG&2j&R#einX(ai8d`Bu#AIWM+ifOcue z2XuSLC65Eiq&Gtd+dXXt?bZZ$CXL=rKZ5HQp5k#3p=;~7K_6IoRHHs8sSG2m@`U61 z1vXog?iN1l*?lO3-t||pBwD{%eIPs{O?%Y{xW3?kRmw#cCn!|NraTX-pBkhj{&4!P z{b6r;Kj-_0dvp1VbbZ%2)_CL`56`XBwPNQeg(gSN1s#$h^(=32onkEa9#y#5-YW5{ zQR?Pn&m`lKk=8mw#$ipX?p*eYp>7%>FU=!oIJLSeZ@B_V3L?EJbePhijR$`|zkw~H z%JjY<_%!ZX-OtqCh^XE53YjZ-6g~YWGd;DseA}^|qurYAgUn<43VO@1Cqy#%XmCu- zdRcyBYl@?jeK$w@rA}^cP)ht=(&sD@hZt6gs)zuVdx!KaOL*^(+Oig2f&|o`gfpHO z!m%2~`w=(Hi0ustH$L>wq{)S>U(q05VR61J8^2@$SHSP#24i;-Dfjkq#(X}=;$)kJe5+S)LPJ85ya zVrhq$tv$MFMQ;?@lLkq237#6g)kDwFS&+MoTC?h%p|2>?!cTX_9>YQ1$7Jl*mpd*E zM~G?OkY6vQ#e9AZzdipk7Gpa6Y;N9+oP2TG(hPnuZg9l`Pmx!!?HfTKP$pu+Y!Nyo zb)auw+B?$NtG0NZ_<{&e^43C*1J+cYU4Yw zG^RL9b-8N$FkE#NBZI}fo)VT}Z}q40%V`Y1SBcf?k0|BzweH25z)|Efy2M^Xz54!B z%wlvKya!+B1L?_7!Y=P>XrspqCKNv}A*yp4C4DY}kcZPrm)GnsX^1UD{hy07d+hnb zhmHOG+SzRpxWMi00tw6EPFr}wIuy#-EJCfhz$5w|=lU9<4>+9RU8H`EWKE79JO)t% zgYbFB#(7}ibk{T+@tUin%P$XKhrgad0w{t{)jl4sbEiduXS7l7Mc7*;FSKj=_|~+P zbxzVVyn^Vm@}`L##LJe^8osh0opRNWtxkYUGH5w0nN78-E&!%u?$*lnf`cvsH~O@n znX;+=AsKloC9linU0BBL9MCRs9(CL6j!iHj4d=fJOJhfDcHYx=CzB?XN&I=+ z#Ygj?`tzHgp*?&RJ8kD9waH7H*H3Huo**So*xe@gk;AV>1Ow5t6)} z8L)2Wq$8KB2-UhT@6c@WzAL;*xO8{a!UzxOBrdzHpBK}AP%<%b1~DBtc)IqD^?NvJ0M1WDmN>LLu&Kl5Q1Vd!h+uW5KNzKM>t=|Q$(*m4U~>byQGci}BP9;F2; zF9e#?)9$p^vd?7U1@8}Fo7X6LZk-UJPrS7L&m<@|p>l#wA zl0I7b=MiTfVBRDdynmO5d4BMtEXA&u$txV*R*NVw?0% zU-9xdb%w^N5hfxYm;!+SbVcmjd6(T=6q?m?^a;2K0@hzOLz`08yPaXrQaCOFgun7N zk)nux*ZR01g-uEvUcyP@s#+s{%R}`83OA=~kx$u!mu;eaobW8ZJ2|RZ3$Kx?SWRZPFDk4M>6O~R)ZQV^S0!!y>4N{G?wwytW$We4%B@G zYFYm!o_t2pwXQuO#Zq(WwpwQsReaee4pDuj<4Iq2>GHgko3+JG`^?W~H$;An(Z;WMD?}J?Tv23X2Fd%EMy{^78ES@IjIpNru=z;(BahZ|6Ln)wm zIs53QkHzq643&)tOSOh+z%~!881(rPUNJjWNZ3tpo4VHnda zGW#lSa|lh37X@Dt;pJh(2Ruw;cAnpI>wWr~?(e!wfzkm)qer5;-!M6UL-4Sm0p$Bx& zS#)%tkwzBI$yOs1d!DchEDYvw{P?&i7n>3eiBf`b6mCF9MDToG{%b#*r6xx_EgWk2 z?-`l6-YarwzCI|u8u(yg>&0gA^2zJ$1ForR`EBz6pV#mQ$8+`W^mR%;_y{8S7&w>p zDIhe>e0OVUPPeo=GpW;hotdyqu36joPTp!uUr4Z#x(p*&_`J%`>bHT142Ab>OG}p1 zP++2P+`C-?9bdv;KeZsiOlUFc1@Y4oK63dV(KXuh4(X5bz?LxC7Cxnb&`ZWOJZC_j z|Cs&5<`s@%nm#!e^*YT!!wkYVe4`7GlJjMrMvlu&*~j3<3<>m>JjFy;!N=4;4YR)N zj!daWVko{g=!ij~OM%A?f!*^ucoFjC=C?SRFKf~7?pej@4t~QgN=X!@l{vKY)o&5Y z2ViwBlc)!fsi6`sQOH8CbTfV!WAZ%;$K>ulZ~-ABBYiyaeF|V0*oOZ;9tFGb@z47R zX0LGEn>`Z^5gmA^7=Qdn@V)!xLm@o?MXZ2qW(d*e?|APUB_&b2VH19K(x~dsQcB>T zYQ4fM9ejMhjU`qH&%XAn>GaIefBxuisTsB@aGMdR7Iu zT+>(blNDTO{}b{S&7u&e_%Bv?ZUm=&Ze=<~gh?o{G$))sto7kGnzzm@!Kag>6MDTZ zl{L#^IR!k;I|sepEPrf-&TfHUe_Q(k z4C{3|+Bb~y4O}~-DZ9|-tCry;=5~!zL1Ml}dmE>SXp$$dBsfmjFy5S!VV;hMCX=(t7{SK`KDmXw5WP$}NB$QV-`25y=dgk6 zMuc`rqhr*!>Gfx$`!f(tIZvk+5C`+%dB$UtmDfBiA$UtMpe+l6%{^#J2_$Qob`gip zGNHv-{f0B+_-rTD{8rJQd^f~6<%&pd^4w|a_^qsoYLGWzxoxQA= z?Oe9TPer7L2ln|7$AUoE(|`L(=l`MWt)rrh+P+aK5fM-%q*Y*0IwS@O6;Y7^6eOg3 z1f+&;5fEu%q)Sm+1`vs%8-|t|I*0B-y3R(Q=l$OAoORaX4_J#eYt6pzz4!I2>&m5t z&=aM~*c0BusCMd(Es342&PJF_3^2tCqz}R4n;%-OluGUt^YqQ+>(PoW6`9)><2_rz z-6PtJgBU`ZDfep|VEhK1I*q#}fEQ`MZiXD%fcS1Cp$k zyS%UaDZ=`E4w4NnH-%va>U>V8ifnu87W?lY4QlMwLMZ}A=fooR5=5m@Er~J@S7rAj zeAv&RBED_~x;l41CO_Nv%J+<0?|e6-Ym+M%Gc$v4GUTb8+ns7|L1k_h1AD@fUQTMI z+aT`&=5Alyh)T7|cJs3?-60tP0a<7uiVt*6Q*+?=#)nl^730~~Fuo5bHY>y3hL14t z{sDtZySm9&`}6UY^&mJl%TYQKN)Q4X0)0NnA!3M=1+(2QaoxE<&K2|Dul6nSWiMr@ zh%l@B>V9*OVx}Jx^Uyr!#UxvwExq3o`<<6^-6Lu+un=>&H=ksO?8z$m1IB2lrnKBh z3Vuk<7ADsB01VRIdL@x~zf^t1wi(CAXPxt;dS)}aABH05>ny?XuaES)<{}_9*8?UV zAY3npm6pWB#AJK`d6b68D)a)9YK`%HpAFqOn#Y!U%DBPQWDasfR~)XnZXcW%{%rIR zQsJ#bZUf2CIU|VXZkgj0%*Lx;1X_6EvPO$5>ovRoc~JJ``DKZEFav_Md5ye}@smu; z7tu|lLfiIV{Z=kOudU8&=JOq?$TH>PVA^FKXwIPzc9O&Mkvb2@!S%#bs(~iN+JCLt z+rpYPKfbeH`Mr@iH{b~+Ix&)8zLs0zt~ex9*e!Yml}Uu(C>&csEnv%>(HGo0mr%2} zx^u54VD-Q?cHnAJ`C6fD8c$GgIzwmFkp^;2Z>!@4iZ{%yU9jKXXxvM{Lz!WnUT0yC?mA@@yXZ zqb@x?&DsbvA!@jGxTfed6&tY`u&ecy>;x%7XE0W=Ck5|R(5oCE>SaYtAEH_(jI&6972|{VYG{N$4 zYobE8_B-r0k7n~v$?aEr?rWXmt!y*eIRX)14U%wAes2nX)al)Ey54GHjCI4fC`9)T z$p&T+b>x04AfLFv-ZA}E<;KyD;Kv5EJ+u4h52aSzgZ=eWZ>$34ClM_fWQ5FFWp1&? zHBk4}pm!oZxfveE%LT@<-EfIN{_GLmt~MJ zD_#%6>@j=zb{$q*-(bZkhaTs=$&nF50+0_cI%qkf_X4HFXmt15{D0$)W;aOkZ@BTt+>GQxSU&&1ofOaqt zl{kh8kJXK%?#funK;0`pKJ7d>cEjiL3SNxfdew4-q4=HGKrB>ZRKoO(?O^|%5+Sfb ztd_^=DvDI`A~CUijO{JWR}Y_u%@III(G6344GO0|r|mV3`HFeF2Fu5NE_T~O%0Gc{@6W23uM+U+TXCc8DPakFYu-^>vr2EzJ2GuaJ{ z->Kdf(YP`p4Gs^fOk7S~bmzcw2-p3CP9_K=w;|(W-0{s13o5R%ZojY3o!2T-$XFZ~ zX3g=~^bKfWZ0p71s;~G!zZxBS(vOr|Jq}TkH9A=EIlC^v!0}#>lmZYkDy$#%G96~WHAKR?xCuuH z5C^?Yxaqw<$=vMQF#gd$iy5l%N#ZZE9qvzPe8fcV)G6=_N5BzethB}mX!mfCzXuUa zp9h{TEtWcFjJ1#Q8)IzT>HMfQZiaw)k-ypdS4jF|X8BZIq?;kme7|mNpC2)zEjVc^ z6uk*eabCokbvhn1_q-S}x3yG(Q98Ar>2BY%^_!u7tGR=Jd&Rrrbh>-;Xlhn<&E>3x z%@A>2Srb{y#MybeHE27A!OAl0gimJP+|+sd`;%53@@>dT<$&Q}N z+_Z}y6V$KhjKriv^z0#QiyPv*^ZWrfon2@@D-(O%g2bju8S*3hd@*Fk^r;bS$i(q7 z&y#hwF{gt+O#m3BJ$}dBtlWzG<7-Vzc3A{1FC)zLMO`W~j(PWDJ>MM`V5|JLA&fqp z=Yr?0=$#*-a6ky9V63w31$O$U)^&fp@MNFUQh44m$Jaz$Q^*}#+L(RkRjSxdv04`# z8e5(8`8PhExmQ_rfQ$!;`N{%q9{*t|6t^-`!n9P#F0 z`6xBovgRzdP{)p=Z|zZHEtBuchSz?;{ey8T85S8ajluAp@Fn>0o+&zD>-6yHv2X+$Mj z5C2r&&->(5cQ7DM^-3yL>H|hWl#K`Pa&Y)PSkEP7iY81iGeSrt3S+HF*C^v_g3Ti` z32A=aD-NOe<@qum8avYVcSA@cJs|n%%{8Q54*+5gEZsAGyW`Xc$^rQ_7I`ICO&#&w zEt0NYR;}vyv#iptg2yqtlz)mWxA0w|-Mw;58V+LzEY{Pdj|<+6|FY=EC;MzvrH}mb z$OyOA5l_~xK5~}U$|+Pg@zOJIG6e3#>C)v-JXu;OMYK@+kARDxh`K+PD~#|3^|=3L zp(axa95??kmmPmxwPHfNt^tYllq_zk+o{EEnIPtn6<@FZjCwybp4@0vxx`7TT6j-- z*tePCB`)BZ{xt^QK$*jf!fJOHD;wiFJ_r@A8$n{*%5%3$y!y;9wqspq#lOP%FI!c< z{tlXfPU-$$wcILSA%JMUD_<%nal~>GYF>j-W&QY8$4|qyedBD3U(4&~95ONfkxpoH z;(n4vB+RCI&yHX!s0H^z9Ey8>Eml+a+A3q=Gbgi%(T}Fn=XVfrti^jal&74?$&tO> z{+*ICFg`B$r2c>_ocVQ;ag)Ch-d|ogUJMg|To=uA0T1|z)BRC$hS5SE(prR;I9`ar zpjCG{do!?aq@P*lCB=>t&mceMl-KA!rS+V@|Av^DH2%(m6jaPcPBag^D~l*k_Q=pS zn7jI|=*4Zn`&_h09vAF)^xH}p+9?V5JyMgKh`B^_Us7WZcDnAtmQ`LRI8Tq(7OjHxZR5149A?$s+I=RAt}=xf@O7}RJge-COWM0i$7?uRn_|uJ(@vWMsTQcq zz2d9=2GG8vg=SW?gO7MHp_sncBqz9NVFPmOp97DWY|l>+IR3KW!Kkcv-dEK6YKXv0 zU${6J>b!A!LL2iZqzj(f;kh=Zc-;?UzrQV*wwT$x*@FLWA}n$cC9g-?wbn@@Ds)`y zSCMk*tYs$ED*SAO{meut4*R;E&asPp(wev27BpOISDmbMo*4bma(}0DC9c8K&|@9U z$1Pr4oQCU0q=u@h+%L7^NyRA1@l5T0POZFii8fj%FqO7!y}WqZQlGkwzgVm&svaw% zco)!yY{#qST8<}2BfoEP9h3>92RI^(meTIAq61a;L{R3iY9lX!)6D_`92LL8YYJG` z^#;cp3#l(4^uKsI0e(GOGYBsc6MZ6lAQuw5(1aUx7wf-@G^lVFjXfLL*VIl(0IYRl zV#3+_{03l}uG{MVTP9fz;$wa3J?dI?zmWF_)AgGU7hLCkj!*Bq*2F%srj9#XeoTN^ zH8?xac~$||Nes1O^b?MEy{U^8&meTihb=7bt$B_;`tHm&oJZCmASIn?hN3uKY+kHy z(sRcgE41UjZOHg={d!xh50T}|2K^|MgNZEo2vRQ}am+=Z$Ye6Zo6W*gvhsg{qX2ik~rUw+Uc62>a}?E8Zztgc&6-0#LfafUR4&XI-9N4s&57LhdQl*GFsc#r z<2xz;G^u!iF*es16QW&x4(_$gl`iv=wgxdJh~)ZCF2^d{zzA^U3!O%zc9H`id%@uI zwkcuN_qcz`x6m<%h}3Gv`Mt5LtIN+8#5>&so7Xv@vB5LGj;!3L^L!rQgA{HgG0~DO zo)^qK*E9KeoRDO4Q|kC^E^cJa?m#=xT~8XTS&R`6As=PT+GyS|_d6ain`Cqv(O&Nr zf|8jN%H!LfNg?OCdj%N5Y}c@i*ymDe7ZcX%#J*l(l~>}kppd&lSA2sd{U@voS|_2H z)DWetXbIt(R3(ea8RK!|sDrYjq*UKwC@MMKIUt%(En+f!!HA|WWz3L;3}WaVo2i~) z@PhhoX6~*!CF7n_vc$Yq=Rhj%B-9$-xbhOkETrI#U^qE@sj~w5M@gx?K`AmbmTp;^ z<86Jm1Mm85`?1xWUe}$Aa=X8WC#m*^cUMtU>UQ6B*4x5KS(0&hbNz^c+iyhKiR5{A zftt=kHcm3!Evg!adE_L7#D)xm?4BtIS!kf^H5G!??2Y z^yw4JxyQ*eqP;IPQ`>_GTSczF-#Q1hDS$B0jKZW=ZC>$4=vEh`x8vT!GzuF}R%{5f zL>Tb?&!!Lhy%8U^-${E;2@YLRuHa?Suoiv%^t0%T-~A=Vnu6%CXJleu=+y2)7Jk`H zNXWYzMjrqAZjMi_K7eS2GYjo2u|ePomL%D=w$zYVMcq}UN#fuoAir{xMK^#BfG2ie zFBr%mp9VjpH%7c*Hl=6Z{v#P?B;?)x;DkVcM}uL@n z`qDIHcMB3j>j~T%9JfE+zVt{G1KRCsl6}HnMNU?$DZfvxpn(iMt8#H^sRK)UNc1Vk_dHpqej{Ni@hx(ZfboUo8QI+k$r(l zx3=xZwBKQd{o@1Tqy!O5Z7VsBIqAD+5|EvL#;j2C&M9kp-AOC5V>Op1smsi2-Y zF+SkmE7NZlK?hLZG1W6#4mJ^oVDDMbU$5>e$?FW8lPwAC76Eh#N_Ev;>q%~clH?jL zn8|5Ynag)r8jmsD%2x6wQDxAi6@BtZG~-F?UaSM_(P z!i1m?u{NvX{PwY!rFIn?_U7fXk3pU0w7QIJ6+k$X7E;i zIKRbcV}wkF$Fc+xqXKpnX{FGcBu+{Pm*}+N*%O?(Ak>dQ7Rrjy&k>MylaKRUh~Z-p zf!;wq?>Elm4%MTI<4#A1+h3v494>k`?(}*`@Sr$X_Q-XyKbGoBg4c1M3upQ1YUF|z zbzRrqa>Lo5qW4m=DX{S2J_gV6bji|jPs9L6u6bA~Hykn3trm-XV`X8H@>)e(0yBO0 z-bCx81^TLvFHN@p?_Hs?Ym@u49Vu( z_u!0`d6_N@{k=4QWr0~6_Ash1O49kHgnez^z3I1igAZ%g98)(gMupPb>aopsFb?UU z);luB%jFi;+5zx^r3w%c|NJbUDNxH^@6k2Jj@vorWHf0C=Yz>J?~aT3NcC>E*c>-d z#4&Rnq{;8iva%gf4ErpGBu<)l<7i*nlEhtIdr@Nx~b8XBNbhwkDZMD?L&gGiu>^k4;}ZZ zh0~;*5Aj(d_1cRpkKr6&Txwy`%0KrhA%cYIR)J*kNwS}&zuwvt8vNbREvN-SUf+Ji zN%A8}_%{KB>w4h#L`8Z7QHZ!)6@J&iU*vYYE)^Z8?^keRNLjwxS80krXF6&(POU3K zF+O{z0*fLuC%Eb+5wzkw?+&87klugBL3?D$YOOVq?dN4e_zRXNT1~K?@xQpGB^eXw zyv~8na((@=yqrFQ#Z0Dsa9Bxxp{Se1%S%0^uV34sbaR3>uXAg#&n;Em%%G%zC$hS~ zp5GwVvuT7AZ8v|={d9C>VSS=lCJvhR-S!yS4McV=K=lND?Qd z`u;2Em+5F3m)?voNFDQ>C4f*_nbH^|&~3mycD+5T?4O=&f^W|hc(epm&{D_$mrxwj z=hC9i|F6E}jq|)x02lQ-UG|XoY4q`2<&8TzZ~D1^SK#Ypz7~6 zcxlGNP%oUDP({3=&dK7RG@Tr6XN}gH@R$Y|rFn$9EOhtLJB(0iH?e)iUtKFg0BNf` z>R`F~pnh`8TAE42#f9sk8iR-a;IMiCta@YX=^-3;PDg5YOk^=s|ku09BNtyz@%!PjnGH$WX;-e+V5o zf1IyZ>^VGxqH$iTj}Sfzo^QoTa8IY&ji3>Jv)=;L7LW}?s7Cjfx!RC3$0;qKf}3^V zasv&Zf|UYqXTNWO!&5$w=l%GJvykfI=-j&RGuQOU_I%cFT;d>pQHAP=(-rHPj8MIJ zZ8acH*Bh}N#`OmoaUJPEV?46dfNSt;IIrcn;E1GY;;iC(g5{^(nbXOsf0a@y$>+4pW3V7$1W)Nx>M2M3onbrwx*Sg_OD!Z1vPW z*cFphEpS|j^dm(;UrLeR;ZW`>9zETkz5BK$CNIhJ?c|j5qaHOab7j0|d?g3{n{;C= z@NrnCu=t!5Er;p>3F8EI+Is3BC+CssvHQ+S_Sncr3>NN?nB{cQ*ACrAf{P@yotEy2 z^^kIbWQNI{9q%a^VV3WHW~k@_{h8CKw+dI%g%4aCL7Ub;7;bu`-y+y4EhsKHo-0L? zml3Wo8JBeJrf#^s6i$9%{4Z=D6G6lA%htg@kWglHai~(^iV^REIqItoud|n7OL*v- z?;MY-Vk8Q`mMC6$&V8(^&=jt0-ygY55<(sDS1vEM*n@#ylX+il4z;u;EUh_5a6x+X zHogxCgsh*m!SRm@S3Z_nTy1i4aOZOL(u{7_0aUd9mfF)mJ*FEsT0!N02ldKsrrXy6 z-MOUbE`9aO*DMP893Ge)x4j|M4`YIjlz&Y8o7UjQ*n!-0+Ez*| zt}R|UX_$UkC-%{H zagSv=-AJ!|2zxsAD|wEzIyzM)p@oS~gmX^#@quu?u!t8k1}eJI9dZ;zf^y*9_v z5Z|Pyx8&XD+CMZXwfzsQOIg0Pumm2HKstf$5HN@gjwfrzi7YQGSK9LdTZpVDSO|a` z|FUB05Cbe$@m@bhJmuSD>9&1H#E6$^DdbAc=AWb(w3x)<`ZhVe%MxcUgb(S!y8bva zuOyc2ykh0b>6?*?goHDbi5#SidpRp*53YRh`uY4;vVo7ABXByvZmt&P#3j8M75VlJ zj3F%}VDJ^X9nYMZRyo#qc(D;B(#5qQeVRdCFLX%zP+(@lI<*)~C(1rQXsQ(#e+{c8 z#`$l8?UOavj%!uHn~b6jA7&SGlvh+JU@U&5cFh~Ql_RLbUefY^=87=pFI8Y*BAx5` zmigpwk!mvRwzFe+0LCX#rqm>*TaO>zGss+*4>?ia>;xO(U-_2rwIf_fVIf;+8yKhi zi7ZVeUYMlh_;=XL*lr<+pb||R^X{i`40iVi{hgAMk{X9K=_g%D_kH$0SBSP=G8e>{ zR;{uPb;4re0BA-Z3PYB0EI;~6fjY+?GL=vCm@P3%#nYXs8$boq2=}WR04Yarv zH3oNaBFR73mh~z!cN?f9!RjzWMgg9B+{a5l^okj8WhLvBeC`H9OAz~0J(tNyxj;@G zT5|-lK8*-jVT_H;(3%nS5J`Jx0`NrxF8z7FPod2QDF1WiAzb*R%Jm19M0GR++Z5Tf zkU*{0HgtX!djQ{(_MeGS+r1r>LCIqEtCE6GtBXA-Utr$g(;JGevVY^FB3s93P&B}! zA+WCfxk|bW;S?WCRekWEUE>*dJWSp_#YO+5{5<^m!#L;&JMJG#==!mr8JxV8*%szy zsvm{W7d(JrZhbv{>Q~_}tE(Ot3Ve>nU5xRy#A}x!E>BHYnMdGqLT=xq+i*#wT}+wJ zsCc7NW1c~%YEqZ>U=)G}DwCeqFFg&v!O3wL$u1wC#JL*Qki_XFULsP9JBNsx0dAId zE(b8yySG^|IJ`%@G%JcGxiBm+wKB&TJI5XP>$>nkAlygSPFB4G=b8M%4~$maqpy!HRJ~bo@#C%T5oZd!^Oe}roqI~f$~Yz( zSGYDInpJb{+?ntf?X3x+Z*jsX!76l>dDbA#5-^06TIGN z>2{G|DH`3cP0g}qlHNq`7|N&i897wwb};k-?2b)KF<5>>+1Iuqx)Nu*u}2dgla{}( zNq5EO!5cDKO7`Fu628slmL8XM?%5Srty;8D=mhpHp`k?1bucLYhAg^$BKTFRt2d(k zsL9U!(Y(A#lzMbq=*PXSK|O&$OfH}Ec!AUtKNjN%;IWfMZtF41{`SQ*<97Noii>}J z?{k%7wpm~O&_BnaFNqfV**N>+?1JI(>0?w_Pt4`X$_>@&%@Cjy&Ygr1M<#Nd+Tbtg#Vn^ z;I0rxwo|Mhb|@LJ$cT5ndjD$)MUEA^Afu1@!2xNpi>`2g8_8%Zh!*Z&^osDd!zDF#em$uY z8?Vsy^0qkNK}>XE9e4ZH=9}BWZ(N#MP^3BEED-O0(Prz<6|T@0TACxOuS9K8h4Or_ z57QH`^C6HjVx-&ZqK5@s_^j?LfB7n&R{=x69t%~N`XB`Lp`TVI$>IaomFrzSres=AgwfP?1r-^vX@%PvZ2X}zRs+A8z32XH7OKL$|C**Ld(8#LCsp*_ z&+p7u02&AOOqCUnfE&67kIAdZ7_0L6Lzo+`e@q6(7FP7Toe9qu1d}luIs7$918FSY zGwuBTO&L*HUqQkf&e?8;FXTLdA13pU4{=j5MpSXHUgzzklM2X<2p`fK;Ym}y8g`fB zr9{F?>xwp>1aFgGJfOR4Tiw>0?3gk73#R?{0!0nQh!>echf^JAh)xA2j8#9 zNjd+ZK^ojY(*Pj)WYn1fnRv2(VlZ~%R}nXLPRw0FZa{YllXtvE1tlwKg5{R}G~s{& zISX?or{UuTR>%J(|NQ`GxcvVZL_uhD<1YxzEv!Br8OTLnIQS+lou||}eSP4CL8bXCb;UC2cTbVC(&d{5KpE{|aLT?eFl-5vx3Gh+!%mMeYSVXghgV|r z!96vbvvpBcheq-wSPm(*o~cH|2Lld zTRtLHtjTV6Pi%go=iU<5xZ-SR_r~z12NMg!UB@)#RLS`EpppAM2G1^qS;eLW3p_b6 zP?q_Se_#HVK}p+{)@?Jqeeu>N61ZwtIhw;mz51IjTe`1JuKg8Dn6gQ~=B;asxridQKOPVjr zlcxWe{n3MHPO7#MAR1i%MHP@3fz)c(?K~A=&Tjd2ocd|FW5gKmb2@nzi;o{WSxv!2 zFiRDbSxu**NEmeX(z-Ma4qoDP-qT`#=oWvsdf8m^%j#M;;cbGrI*O{5B3Z>DBA!@! zcUK|A@qWGA-UN2yWlkaOE$UeQ<2S=;0qu0-n&o_^I^8&#RIrJ(%=Ct*G(@_c;#Shp z#am(R+2M#p`J5Iaw>S(^0uv&9&<^j4VXKfiJl@Xh>^xRXPkFQ~yUPKlG`19!>wF#B zc?5=9GO@pk&2p7-<3MIQM1jS_Fjv^H+#ZsjB+2+fx5zJQW{cMwW{9JC%0G438fx9O*5A%TzR@>m(KlL{#QU z0r)$Z#pelOF)(n#_T3i)Jav$s?9oh!Kg8VPDDZ=;B9n(l1J`ooj`RxT}fg7^E$xG&k|p4SL{JT&umt9fXvs#by1V zl7-@e1-=A*)G&5hMt$2dxB6-y?evT8-8K#=XL>C<`gb8%=_e1&q?6BI3R81LRwB;M zb84Peleppu%#7u+OY~64^ZavPj)+@8yzaZsC<8Z-eZ~e{s0hW=@=m}fmVbX}6@sZr zIjAAPNrPtICv2y3H@ZMv;b}?Fh4`E(kof=sS%IaWH^RiW2K()fVVvVIzSnCW6jCGbgA70cs1y}8?s<@k>!QzqOmH(1LEQufzdY11_q*02ik8$^~fnQ4Q>b;I@Q~o#7R96zMXb?5Sl1evK!ytgSdDx zUUPxLZXCN`wWaO1s2~AV6oJV4tj|4)C{#{m%)SUQ7#oOQ7Y{>FTX%|x@A50}CGHNB zdN6%U@qXV(u5%w0P4c_vQUE(UBkU6yIR)lo7dSZM2+?XG8cYz zIFY^g)QUSjPbHo0ru}hIp4+__G3%>MwCatgAv?=@&>nun5{3-)~D+<^WwK9GC z>CbVOipe4IbD@0?Z;`)edq$`A3{~DbCQ^j7+n}qrWZf$vzwg_M#|bAuly02y%=r?b zYceWk93L=3my*bM`3jkdUVt|@=#@AP*LxO57xhVyk}#sZ&nJ^AbqgsMWygN=zY6UY zGKEL8vgJaIH?1`uPIcUaz;&Js4HVI*7zrnSZp2x643g96=!{OHcUjrtUCA{bZR3ol z#3FH~KlOL1R?Q)b9lM5!W5>(%iDI@4?X8k7`ouO4P2q~Ar>{CdW3g)@^< z1cH6tdr;`0ehqRqKPbyx5yfe@jxzRt)d48QDZbzALTJh7Mg9ZwOC0*aqwPirj+O5u z#czMlJc`0&;wwLWbi4&2O(mXw^5TmGyWU^L*a z#};|Ei4epkl~^4)dylCO9g?!n?BRy$PTJ{Q(hI65QtYgiMYCMjg4Ns0>EO2c$v#b! zeJTab*ww~Mue#dzm`SR^|ElJuKlXSr||>P%q_aRwXE4& z8OT{~l*c(QL@5u(D1Rf@0oPKLX|(J8%CAWa%OT4T)k;WqwP?-p85!}@&u=y32v?_x z0tw9RM$9ZgT*L3f;+J9|Vm_>RkFmxzhehaBibEoP`NO&_rYr4($4Kdt9@v+qmLOm{!iuupN zBv7saPk@PzCrY|+QCVkm&*L3tE07f5&3R3;uej&twr^A37Nf1FRuf_E^kceEv>ZL3 z{Ys=O1b80go;PV$!1!D8ADT-cMFOQ;Uouk~sDL&-5vezPI~u;u6F}hC&FPHso0pI^ znk~{o#)T`{@nh#YFE}OSi$q#}5^>k#Zmj0R88Ct9E z@^Q?XZlZ$FQyAgMOB{8oCK$VR>D(yp?Yd1x7tZf73$L*+9`ssuUuU#M{Yi-D1}9TbkgRA0O1I!&e00)3D)^z|J)bRH+eIfIRDC$cJBnPs#2i+zklhe;9+BxywAlIq^2 z>Yh3<-Q{v-p%jP6x)+qSo9V_U^%x)e|4@`>63&nE#P`{HF+A9MSGB$5ViX0+-uz~r zp`0*`$-N1!XuwCRB*v5{5zp8;z{NkOZ)u36lgaEqXgLoK>>KL zmV(DH{O2E@6cz6yfnFQ#X{K2Yfi|Gt^5^e|MNsP0ctTpPH|epMrVzJ+&rtK&Eq@~< zb&m!NKQ6m@Ilm0yHEl;+epN44Ra5lp=a9hyU^bU6x(t^X=yXsHEO1t7LMCCx6D;L{ z2kmfzMW^J0ucwR8*=pL_;zs6o95Z2{*lX=e<-YE> zAaJr@Oh~LT-@O;iR8aMvD_yU9Ba9F-KfR7GWOD;HLAucGDBA<1Femg?k_T=J_79QjK3)I^W(y`G!^sj*2C zAC?9cO7sJ7T+gkG2Ua=Ckf-c&uc7YHo=FwvPxD3GQJ6KZ5XZz2`VWFf2ajmvdZWc! zyf5%H;Uq4Ubru})vbKUcjSs}R zw+Yt#+Qk8eNdF8R~>_0^0i(${p zT6cG?wpMij>mV{+`fPmXS-gAxR>tmB8?E)4L${Aif;6190`aBV=5WIIbkm2IzYS2t zz-~vv!osJd^uLh2Cn$X*g=J|@zp|dYDAg`Nbs|fHGOpeIeD}J*O6JC2J%hd&kT+^- zEn`<512X)^qtSu?1XqeJAPz9De+=VH)vw2 z8kW}_SNVTMLbqY8&L2qYyT+@{f+}Y52oU}mxBCEXFQ80f%IKN~q(^X3zaACbQKa7W zCe!U&93r}5VEq>kwmxrM_9|5xx$@ewDV~|Ig(n;gUr4&b@`R=W)f)i{{}dz1R--Z? zA#KM8nqXV}FtjoIUW{0S$38#d2;E7ET0I=4AR66=Y(#mqgE*}=oR|wx%t9-F5z{}K zqXSpE@fph5FX$mRnR~T=8o}UodH^DzUyTdOmw@Tpo}1fyP2wQQG*sCve;l+#CJa@3 zOCloIb+N;>B%~Sjl)Lb$Fj_tbVAwIyi3f8)^$XUHZ8N+K32Ydq+s76ff1a@HS5Xdi zpL(F$`RU0;SUA1(gbKd{7!R?+#Z{X0#yQ%XnG4b)jS7X*wh09dhsGZ_cgr@Zgf|^w z&h}em`Ck8AjfY2oXdE>}^G$hZbvtg^L-xNKRO%&IwbwS}cwqQUC!tU3K64prcO-zE z_|pQu8`0UOK7IJBeAd%&4#Zk}kHbs;QUO6DoiA6<2|nXOPPBV>y|L5rD4lS z!?W+kh-m%@7kN5k#P^XapoVn_q7T*sO%3#90cQggF$F>u&?SM>(sT9#lzqgn6V`=` z(LqId=dgeB$52sioLPmHHq0#`ucL|X7}IuhwRET`KTUBEf_`L8RRKF&r`u@0A@ z`*3{cK^W6Y1>*w-AY|j(XhArE5zHHCD%v`~fXZWTrzSnN7qXs8<4(R3k2?wAsa>wp|~sYNFM!rsY#gI)*+8H z$$kkU73Tp&p%{4N5XAVVrQO;wue!o|>WEI_sz9FU1v-P~ZZNL*dXkLjj4*V7nBj-^ zI3Sty7NF{^BnxEmy5@woC~6N9Cv3E|%|&04_scr#NV9^ zaU~+`_{0ApT`QXz%U9eKVvWATpDv?jIl5U}^>`yHNigRfLV%0$!$0#0 zWvkit=)w;@Cj-lIr;@yj<2$X-+iw`JKBbaXji?!qeQjsED z`kZ(HI>{S*`~xq<>Al;Ev_E|8!hpoOg5;`%rQBcND z^Wuxl0mqMk!M#oP{k^dctx&`q@O=}tgMJE+(Yoq+o6pkYq?#{VRHB{QiRNRZi!9bl z^k?(hK;IZ)6a4mqez<37Kq0pago}Y|=UX$7VL=`CJ>;bBuTD|l^x~|xelQ%EOCtXh zmc4Wq8yYIyp0Tqd6YBhqE6g#Bh6<&3Ha!ctSdi!YKN2;g34qd7#1E4_XQ78)_t{Dd zvc{h}Th(upt8`n$uTcIpCGPXbR=dAu`k5pJlK~Q~%lj4$dPy=^4|%-PvHQj{A;babP}}GVP`24 z<!GW72O!U5#hCoB^%J4Xd4TPd3p?+DMK^UDxrgqI}t}DVO zkk|k+PpP=1O$70%u7u0Z(AW(p@$O1y_A*dJJiOOVJ?AO@%7R(Wg`%^Bd+QUH4(Z3U z!sfbIy&F#Rq46Sdg+u!+AZKIi2T|JEm)=FNN|ZTHr=^ou2$#w;m-mJCzucMt9V z=Kr%Wv4idU?`cYnWIFHPRI3Mxoh`j;0SrKWEfEy)fpVQh zur5dkZzIn48gv5mI(|zIPAl9ljU4>038dxpc*CbRI+}TF?S5|~BN+`vaHyVUtK^im z{i->mqX`yh@(Q1WEn_k2$5%SVcmiXHqj{PZCWJ-4ZM{=h`6-tt=+>Rwv)Dyq^6&xY zYZsS2AQp&;b{TGeJMwtx&V%8vs?00&mQ-hLXna4L>?KoTO!3#I5Z1w@De4-!_vDg# zjPswVuZ3kR>bcSu-)$&%cKTJ_5V(%I?bxDUc^;|rBTfrBl#Bmy*GeN}YjMPS6}KkK z8ucKV0zh0%%8r);>cE|F()+iNBG)^b835k+YQLK;cc-kXW5`K4#ABJJTK^hEg}Q>$ zJ6GbWq7?3t4ff+}JHOE~71PJx}4)P-2UZLJwGw~87|Dkyf>diuKClu$P`YTJ@-*kOdJHBkmjVsymg@ms8it@LjJQak z8xWG<3plN-6#@A`)+;@`z1EE11<|Yv=&@NrsAozNkZnPq36Z3Q1l^2grY<_%`=|?U zFaM>dKHzk&Ak}(b56@nuIcbf2dvC%xZmd}`0LX^JiPcP5)WpqqZ*)mCuIJQcXO(%; z=24Z8vfp~?1)CGu)5*aERF@h-GOs42n3B9*X_m>kv9J9GTrc(QI0bf5| z1GgcuOk+FoiPe&xHc1LCv?j63iT7`VSpohYmVd?D_tt;K+o*A8-fVoQEt!+I zliT6DNWAnN0X_8#w=J0Qa9V>`-`pWZ;xPf$4r_`$XIT^Mz2#N7X0A?22GzeddrlE=p*R zvqcgOma6^hhoM92EC2G-#D}2;ga60WqyU)&#!R>9aql+`%a*wTyPf&Aw8rOAw0Wnr zk&%Tb!qB^)vss>#&lkTq%^X}of)2UF?Rs43)w($z&_<{cmAM^|VTd3}L#lUCJDg0* zi%#rT;Uh!l2qYpnJoC3=s(jnh-n@vn{TS*k;jDLXEmSMbKZQ7`fdHXFFm(7cfQq<5 zgn<{#c~DS3bt7@-FvUMwVg+*WfSQ)hlb`k+NMz9V@0}HXWKrlIa@0>gMzzflqsV#} zZx~4$`RCyl6B~;;u?-h*7LdNzj&;UEVrnOvaW^H|KS7&sUKid(hE%=QW;+_dmX6uq ztjx=6;5=ZGJ^QTwI84v$NUs~`CUlTuBGB`CvXtUmgW^|-&&mcc8T@=;LdO*7wN#4_ z%B1{12%@+R2YQ-m8i=0&0eIM9K3q-*dhnEz6~xnwj~gS|L4EQ*oRZfwZT?>8#ZZSj zJgrr%-J|+!zeH9y8YOnsI{;rYBs%qF{2V<`H6^>Efw{&7B4BwS4E;_K#^g=Z>mFY~ zm!M?m;6F1YR=Z%DLp05w1WGsEE8281q@iy=zB4CazriA(7}7l~c6El8Xo%2zXS_As z^`SqQ+We}eP1Ex;iT4knpVQv-S;lWSc%9C8 z^fvLn%R`TqWRHqnRf?Oo)(Oe46{1JX5N{Ytcm2AneOq+Gt@O*SX#4c<{1}fL3-HWh zqIHSw0P_!9etNmT>Z)c=n0wemwcwfy)1C_Xehr%pPC8n#ZpAeYNUmok=+vRg(}lZa z^vTQb>XUbv0mGdekXU#eWtUwn#?8X(VTB`4OT>9cG%{G%L)xAcU?d%`6+_`&foWxC zm{T!^mIA&h<_$aby7+KX3<%JV^F(fIfJG{*{(AH<#--}*=AZ zcKo(vJz;L%k?lIc*RXsGBg4s#lI=g6E!KdGBGqr^If;uCb{c*`=ZP#CvSM_GgGvON zI3y#@1*fa9xwT?M-h!$CsdjDp=1h>%4!oYUz*I3YGWNFr+SzuBl=DfZZ8bxjAuAXh z^h}s^WYz?0213{3vQRGt^H;jFon{E^CbEb-cF1X_cMEAE1lkomIBk(V&0aLae-}a( zt~`*KUSxdvf1%eRbG*;Vf^96{Dq%_N1lFvipjd96^8E~xhx-sND2!>TBKPfe7k}VF z-!ME=Z^c#Sc0rYRRx)RZBE;tfkq7z79ziNB`=Qe+or)P=AQg#>$K1*Vu+L4JZ=xhy z0IwGo9*WzEv5S5xEa>N-zkrX)N_-zHgCB`hO>r;vuihA{6xx}$(#+e0$tahvY{4i; zNol5ZAt3oaoOreT6M75h>xpps=+RPReR(aEr%dlUAabV?1KBPk%wPyC!(ZpbQd(8QqhzpA{Yc03pQ55Ii*~5 zq}gco#RHet5l}(oQCZUiSR*+0aPPiwtA)N{tR&^+8BgY>+m9$a@^tZF4R(UGET?k zcK5DDpPrB1!L{K@vt4{bX|__RS~yNDiyWTv9^Fi>;j#tOCnI-2>RyStu>LMxTuuT#Id|0`PlH!kz?!W+gQI5}{!gFgvX-H1uk>0^t&<2V@b)F}9vJj(~fL;bYrD>ir^=dsqBl z?QkgrJ%ic9jAlfWPominZ?ec@{YmGq!5Qh9iOesj7y2hK%(YSfrs$tSAnE&X9vKXD zT|Wrp*8hCiaqWVF;?8$0bO-=l>(;82JezGr-X@{pA`XAL%{B`sDIKhWKU z-oGYm)s8X>6LfOP0FLu-%a(DOjr{_+KURNG_AedXLAhRSlghR9Qco*Z3wx5=iM?nm zz};TvHYLFV(QF?KZ`k419a0ZMXU@ba-{<*&4*Vf>QG@|#>CqZb1)!KtnXkN6l^jQm z>2SR;oEVBMdEi2}HoXc>zbi?ftLG!2kyVdTB2Q?4i*keTjy(%wUTml4N&~b5w$Tak zYYv?>yhg;a;U_XNMwd=Q0uy*~*Xr5uALv(>UmL@R{>rZ-f2QxF-I~QKtj=}1u|yH! zlH{~*8053%-I2rMp8}E$!jeB~r4?56>!RkQpnQLLegCthbdW3Ao5;Ce0o-~J8r71V z>!zJDE*nijp)_5i`GWp^{&$I*@XT+)}z=IF_9~ zZ!zEsSY;c&dSdYCAB>ked11@lB+!rcFU(-|w9Kqox31cX9##0W9sJtmuKVAd23S#y zK&v&L@k(VU7IFMc)|3_5IZuw26M!c4zgAoa*4isyfVK92WW;cyvN>BbDM9OAV2_HpgK#NaMN#t$68c3L>0hcGVec zS|aY>W|eYDK$TY&d#bysB?_A2-;KpG^x2FJkNzo`V-QIbZd|rGD094DVExD)HUD~l zFE$kDQ*&59)oR2u9KV6wu4h&REwy|h~Dm{F(()9-TzJ1Ns*6#fsSe&{M;_vxbnB7`U2_~ z`As_-y+cG`dOQLwcL^K2S>3wV_@OdA-Vd9k%6VjNKTF#-Nyo{RDjLn#2o z*^|FZNY}Q@j%zgO*=-gMAZu6LJ-`A1 zC9|X*-!b+*%e|%!%B(Xr;UM3~e)FjhRi&Norq|ROW2PpL_1e%_c1-0nbM>MLgUg`o zs7Y{q-|9!9&$#lLhM-OG8{Bl==~@oHHL9-Wr|`kb!FUdeQRh4Cbk?Dl8VqfpACN!- zsIC7GWrySON{3Li_T?!4Pg}_^X@BTJ+KFM(s+0K{BTfksHE&nN?%=F0$AZ#59!-@V zrdN%3e`7p6`H7(COF&@_tcO^>0H>k4dbn2qyrvj+GF7m{?ohWzVkF|hPT3lH2nm2o-leu*&T95>Y)7*OS|4*ts zn^P|LqA;!nyD$_s?CMt?;|8$GS%0z=CvM!u{%@lEIhz(IUxa*+%YwYOW@>qP@pGmA zPalv4C=8S3J0ab2Zy~2n6@>L*IVPZGG-vJlw&Q8AocmBKAN1zvMXM*1e>g*vD}S=r*u^-H(mE`>7=OO4iCR<6+UrXYPc*rA;gZLczd0wE)xqc z5_vZ3hYdwPprqwdf5%^G+&Pq!uaw%C2Z&$+!kGVVOzUsK$Sp4RqR+}D@O_WDD0>#k zYiuVI0~@g+XA5yhNYCY7sgcg+t00~G&7Kxf-q+TAQ@~B$6qeePlR-^?>jqJXrEPt! zld(Za8w1CR07!@x7yz(@tt%|@g1*#qkpFmeOc2^pU4-VWN)YsmC>wBG%l>7S9A-|| zAI^2*-N=R9jw0nvhYMBDM*Mcc3wTg7%}>o^56rF`$j793|6ITTexRE$niB~NvjOc; zD=ff!ZGS7ij#Ris_?W~G&LDE(i<96&ePR>V;-E&5T>LT%Gs90Lcw)OL7R$<3mRF&>O`K^HCBeVl z&PqVSKU@ZM&a?msa$dhTYsYrhP`61FI8SO%@bj)OzwqwuP8~f1yqSH9P9LKcO%625 zIl*%PN9Oir0YH1?1VGjrhI)4l)Xo9%=h|ksly5(Ft0G|-;3Tqu81NOc9SGJHg5qQ#8AZ|d5NLB|%&NR*x zqjIckUKk-TGST~PXjq-o_^zbK$7}p--(>seG~7(l7^vI;Ah8w zTGOW4vk4pi7E0WtVB|WSjGg7QkYVXN<_`$55)2E>G2%X71)$6t6sfc$9MFwxX@@;ciNr;-3tO9VTfwe-fPjc5C%)SdAelp|m*~wEqgtMXB*tER+Hi zqnCi2eipAh>=8m4zPTJxCD05&on@xh4c~F5N#iObmmG&7+a%xbm^5JBV|DBy?9Wof z!rs27MUwntOS{pF#gh&+Qp^ZIdGn9l1`p%k*igc?wE&&CJ z2ZPm)KYCO$Hb7+nu|G>nhQ~k*K$1QGaj65nsEz!LOz79=5Hl&Ln!9ON{e7~25$kLK zcRqB}~CUcf5iM%qlM9Ch;BoSR)i`cd5(*4?HwQEZpJOeui6-yoM&jlIOi0~Nn zFF1mJwFEM|88NpURni#qxaY&zgj~L+AhITKUe!eq!p_dPS1H@aW-Y_cSjWR6va7*U z-FI6c9O_5}&=Pq*2I@6+QeQlce~mZ=iMI8A90AEb_sc%fWbY8b`K7llsyJwlf!TQK z1CFR_@rsD|=>u;>-dz|@-WX*(+|A-}aS#aBnfLY$V)7NK>u`U=esGoVGIOqUZgK{1 zf?jX@DFYp{<^){grQEGTO@Utm7cXt^17oHA!bH~SXpPT2d;3#nbOR7)J8U8=9tweC zdpc!U8mh`%1Heh;)(4>^1~t1?ODE?HAYIWy$?~-1L~@LhzV^LUc$iI4!h^&VwJlfr z_KchD8uQq%9w2jMi|Ek_NE|Se(zg+1q&K@XRf%s-@cTvKUl=?>cdNJb@!AUyf*wi> zl<$g7+oz@6c4k%0P}$~yo$TGtaGDkDHT3}-yV+rL3b%w*v{M9RFRhoZ3u3*e$0 z?P35ZjTM71uMxUbXzcc6K9DqZVx76|qWoqHaMfQ&iQytsl>%Kz z|H6MFm!E=AHFHNrxmabFl!UPf;RM#|mBT?IfC&1fcV1-HcbRPcn~@U$GDx@?o@$Tx z3kIRMQb2P}PEKmFA&H!Kl02F`dHiA8WBtd)D^>CQ(9CP!Sgv|s$pe~kvBlm(BFMUS z4P>X6{s0@NkBL1rvz|dgBVl#2vw3EZ8q^$YU@3FPF6XEn1Iq{W<`W67%2^GGcd_nJr+Rq|G<< zLcrxsG%eP?FTSjd@uz)lZQ_N{OqRWqvS4HsGa-j#%+Eb zmYeT2$?$2vt?x??fGplF2xTSKX&zBp9lsND%jEqKfp8HStlG0cT?Qfubnzp^xi8+} z%OzJX`{w7bt;0RSBLx?GIBCd@F9|CDe~RqMNriHy!!8MLBY!CkfP+dlOlIr(qK{bS zGe!vZ1irM*jEA$mzxCU^UoPrFm>I&i!kcZM?FKS8U74OT!!GR*@SxIrrIj$|Cd=MH z4dp|@($0nxLlbw$oYSdr!qhrF0M~_p=e<2xb&p~pfOvMym zz%q)dRuQsjK5%_MQu^?1^4#5`S^0ZT^2uIW_FX@znDSzWP{6MH8q;Sq!^FDsSdb1nqPCtUd%l;MrR|8e2mJ=rA3@~O zX}QkE^L6D*GuAb3zUaYb`La@>8z^4>!%W&`PsU<+{?o1BNMfAi6Mss+(1`_$q9hEs z*jo;9O&Q#A?pynE{!6>sb*(5i3@Ai@XC#xJIxYv3YYtjW5DltK(_gDsj1UVW)*>eW z$(eljsK}Y<{7C_rtfrpxuCoYfLL{s}bS-{l=UipJoB~>c6ta&OW|X%Gkfp?eKvhC| zt9^=Rs%B#S!nwNKcf~#EOR3r?G}V>?;9c#X!0OE`-l2=nMdWpRU#%txlD80_>wQjc zkM07kagajXcBDLIjf0w^ay{Q)E3}1PjfGPijEAHMY4PH+NdfTlgPH_CvYDflLG`p~ z+Hh*TKDXT5c{wdT?%B&{Qunlm%0&xxhC0P)S4jPpP51bF0=LrN!yUZr2 zy#PXKmw$zbHAeLl`T}BiE=(-~4duptFAZlrxat?#q2&jRryh)cz=^=eo+)8hHOaaK z05B)Hk?;U80!UPIx&4wcB3D`cQ}sU=@+iG-?!Pm1VuQ5b%9^H9ZX=Pd+YOB#b~SU%>4x39u7XJ6|e5PNvvP9R!LLL{bS1TV6QlewV;_ zHaCh|-=d!Oq-|8MJXMM|!}B5i`r3lPkeq!X2#ucLr>UfuqT6Co=yK%q^S(I`C4weY zkG&-&zXAUo0;ck~b&&A8?LT4rAK9~~WE7f2a`^e}8k<3GdWt*_`WmKBJStLEN9|v4 zhV*~nVs}n4L7bcjLp)5=E+9Ma%Rg0o(2K=DfDkz7?W7Y?q)8#rJKd780g}*vn&e_` zlcR)Hud9Y}FDEOVyA`|t{;S^k959c|4*mch;rndDDe&*Lx#KdR+vF}78my0MPm$x%Fi4;%v593I6oZf;feNpaU>(vxzGTl`!OLwsufB$M-wa@;|_66b^Gyo)1!KmS`h4Q?*#-t&A4qR?NW z?1eZnktZ7_qr!gw>fK@$VE~o%%zF-M&Gc14NMlExMSRv6hdK%OQ!hjeqh{JqtnYN= zxXaHNA0OFRu2&bf;{R#Nv*#(jEVk!B`Y;c;uU@9QlMSiT6L`jSHp7(1%C+~mSQP{M zvh|D=8QP@C+?(+NvV?Vo#9i1+Q#l(+^FQ4oK+~Qu>Yo=N%oH1eRse!9M-8^}zWX^` zF*^i?d|IE-Pp1VULqgxdfa6ftqQ-TNq>fd@*2}DckpK~O&y}gA;yHz18+P0FSe1@4 ztjX**SAX$O z@8;LUo9KXs^lVdmxn5d9GW}zN+jX>JbmozH251`tcO@Wfzjiy=eoDSsv*%+E`p6*! z%K$)9T+yfFwW?__g=qKP`0<0h`)rQ+j8%iVhF`11suyzyjGYlU0Hkv2pd{a^ls#S# z(0Ipv`kO#5c42b@i0o!he;ZG`LMk}q95);%zO2xFTAH8K&jBx;U@T=%On7e@o(=U5k2~YKHl@hkD}`B z>koq07u{g1?$t#m;NdYMy>+GIAoYol=f>mP}U+pt(0zf}zeThq1sBw}Lc3{TXT0-JL+E9LLn zZ>4oM?z>v#5f$aVp=wNG$J+xTytw5T7qHgc+=nExL-)&R`-}aK9L(@v0f0j{7Gfr9 zKy`7jSyZw5lBIoMaF>0^$IMe;}nDsIom7mQrmsVO-%2^X6e&~>9MCtT(2om zy}^UrQe{GDY<4iQi13QRnC=Klgr+)wGxSAU4d99aAhjD3UCXAA+_q(h7B%p2G zQyR>YV^XXg&u0)`{oBj5N*b*g6s)SC7dXZQ6u~za?>b~{Iz`pg0K({{niajY2H5F< z$P_8#Cr?EF*Pr*~7i0iJ+okk391>>?zjw2PPwwgEgyiQ=UANIGXxroCSI1Z_VA|;c zQ#x>e@9M;5+nb&9?Cj`&C^)%c=)atWd{wOi)`nGX$QoZ^#Vtw%TG32@-9n<^_IPX+ z5GC4TW{XjMG(=oxRCoSlwiSHzMZJjkJT@ABGc)2uTz@%(v$+_Mmf5|+)xT*XC_ekw z1uY_tfzXBDsREyHo@VkvZ%<601D3bPrP5GhM#J459PAYmjNSjJg53nFs17!OVTHRa z=%Fa32$#y}w@+`Z0GqAXI`MX(GsqHO#o$9iC7i>KfI51n!aqAv9qVM~^Hmh*$EoQ* zb7e?+;LryTwSX#@RfqrnUTkxiNP0$~L(&dd}$Ja@9IqBf>1=bovg#H*u9C|X+ zzq}8qi9rfIUm*)i?K|f{zY)T47=1s%&UqzBn42uH7v}wK?VQ7)MGU-2W!}#ltA8@G z*4AWTDmoVi&^VX<;sq4C60==t{&-J=v)vEX=1--C!tcsaB zJGF*xCf_fD_VV$uxWTdX+4@-=+iIpq&!?R{y~k2j0hC!q0b2`C1P*?rlDY9iKZcp$ zhZYvNC_i1CaY|kJ`5M2FwNPJkPy^TxEeykd175c`@yW`+wL{5nQHNE0I6T~iIv65i z$plSrrA$4vG@Ts$PWb`7C$U*EfU7doAD`G)niR_pG{4u&w7Y}82ekuv<}c;{n1H#Z zEcPNN6ZycrK?j|MYCkTz#)%UNfLp5h?O&+$SGnx3s<|=`e8G98?foe~?Cj%GFfTyX zn?o6Dl>anwME&{KhC=gumqEjL_Ev^=_3N}eSwi$OZ*^oEsCQ0_oJyiR3kKF(BGa|L z9AYn#a!!auLTZX$MD~k2bo1r*m?0NJ{)m zlX3$y#KT`T-k&+k&KfOE`!n8=W`G}izM@%}N(<8$vDRri`@=#O4h5Z^JJ+KwFcL60(r(Rh*^}=?z4}-M z>(O>%c2f&NU$AA<`oOtY5+qO$0*2|(!^kJ$E!gGz-Kwqh@ya)(p?i@4c;s$jx0m^C z@I)0%!I>HwUKB%D*o@pC%>jNYuQ;qO!8jRd7Dr5p4~Wl`u&_FdB|qtdj|Oj)=013W zCi?(dd#ljL_N-tiYxMi&V&F@lJMbvav$O}~<@1!|{obFBEw@@W`1TB(V=Ag*Ap4o= z;O|AXwoHtH_<5)79h#;$Elonr>9lt5>t>t4jHe9_)c4WsN~d;SQI;s6Wz?d7+cJOd z@3oOY-|Q#1y}wv>XeXXjO8G;v{a;tF`E`1uOPz!^)_-(>vA6yNC(iF?`2H%eXtCTj zwdjQzHiI?F4hZjhfRGko?~8}`K494m5`^m8A2iqRZ&Mx$zjQLVu9vz6Fmu%{JpSpv ztiH40PZA36K@c|qhs;0U{6KHq8BS%QX~-g-m_CPAIha9TR_eN zeJ@c6#l-t_=K){}*gPjzYYyc>c5bf7n%;qk?75O6(@mJXqZv#Qhb#||8OenJ^dyO| z^g6Or#{PH>U(1l|aMc+9T!FT-Ae&Z9;WeaU@zyp7b2g3=yn6>X!0!hdT=$3AK3|q$aT;om4BRX{V@P1x<1!?r$3BQ>~Q9Y z`OrfSkgdAU9irzasm~4Lp(&vImt+i*ym&O#1WqS@26#k;FSXiE?2Ddjj4_xpHwCZ) zwiCC%^j@6*>4kpQN3b`d``o#P2;O(cAUx^}wt zUMkZJLVq_;=0)b#-BJiB6nx$Uz=TELpV4ILz@1|=*l>yJXLku6X&?IEDZ-M{Br0vl z8Y-LKBydM#;OY+M@g5kS_EVo-%){y&4!w~Rr*^uz)2hAp6Z?AG3HzR^y%75zmmcdn z;}|klUjpKYi2Ik`KS~^lkm1UtjJQZCL;8Y}>v8X+>r^)$4c_bDlMFH>eBMA^9DOe{ zQ^Q>QO-lF?D{tM)VI2>%^_Z0MQWuO1$~e4yF)?@4VO?_Ab736zzOC2Cv-MdhDFHyFBm_xW;hX8@Tq8Yv8m|J{uR{G?v&hyLUF|`{u zEjai+<1h~so-kQuYOW-idGHz`!mOLjXscZDB=w2_Gyr!;bwJWgHy$ofkZTQx6H9!s zr)hfCgFS9{Dr~6QzNx9ZrAJ|c5VkX5k7B$S$Kqf^%VJPpQ1^IVkG)k)esY4*w%PW! zEM!*;#^bDGM}E~MKW_nyQ9uUo=q~ckmW8wSH-F=~)vbcdF~n2tj|&ztZh>J3^fRI8 z=A~7$-P`V4gba|xcyQ1nv)`d#n?D;Tt$AcmXYqK<%95U)0;$kV=&}3x#d?Y_+ML>+ zO_|Du9}?b-O#x=qAB(QR2rqn#pZ0MMEcMq4vXmSO?lM~C-LdBHbS+z`MwrNk?x0Yh zUziQV*%%Mb(fW`4t95hc{Ohz=Be;NMSC8icmI3)uI3$}hsXFld;CFKts4`V)V^eQ_ zqSv;|2c7S$&c2F5xj1z{1Z8Z z37!_s8D(YtW5XId9AGj8ex^c$wNCV_5BF4EfMgEG(MMB6*xFT?l+!Hh>Wug04h9+t z`gRvdZL)4GWvp$!myzb@63)j7@F}Acaw}$gvFnCI1c+hIJNA&_SHz3J1C0$_*|?na ziB{U_^c4fmf7T9ky2`^wK6LrQ^ioE8vC%cZGW;Wh+dY5?+tZF-E~eK8R)8Nlb}J*$ z3M-c|n*RzP3s1a6`%3C_hQEPRi&#ZnszUf}y*b-&;&P;B!~O}zUv0n5dV5tb&L75o z)2n-XqvBHR)}o@uR*h4fPv1ylqqu=Pi}w8eFg7lUH5ex62Pe{Y#BFw>hDIr+X20i3 zn)m)VXPVx@^4^qZ@ae|z8R;hf{0L|L8DX~URGnR*AhY+JtRcQMI``0@y%n4K$HtmD zkiaEjqPZ8mRCy}w2Qw{(JUCLcfLq=}gm#ucX`bg&(f&7E=^>LFbN{~SYaLjYOj#kK zja+WudA=%B_btAv>op9@9A+KvknBhBIPYNgz0SO)@_9d^Cxw1jV)*ZY#$ukf*Z61r zoh9XW6H4`01M2DZQer>k+-G)O?fcU3Aty;7)2BY_y4dH8TNwtmo=biB3y(XG*b50w zd%p#*vX6asiZf^>+n25j=^@+i1yiKbPPZl3Hs<5%>k;$84UTWE2Q%VP>Qheq zHo1zr5F*|) zMQv-6_qmIlPwsl=y4VN%X$=IbiW7Noa@9Yr{G}+QT?7-JqFjAYaMawIhOz(I)T#&r zM+iFI6jA?v{zcLcY8^(W?u8W4lkPJ;TlDsNUO`>FFu&l~*-iFDoN>4+SKhc?F-=V9 zMXj+`sh|5v2;Hf!5s|Onr3Sf)7cQYyDJvLNRYuW-CLUDZK@xkep6znfc)8`?MDj5z zdBMtBoqJ58H$Pj+WXb(ddtb7#yw8Zgii^I` zu$M|GOfOzy$W4qQNRZc9VrYFAkZYv{iz+PT!#L}fu2VXa$u4%GJ)S;|?#x*C#^J@|} zhVcEeLXctPN;2a1gm`*|v+`lSC^=Yd}Ab;#@flDqT_ z8LHvE*c4~(hYKvqQjo&3@C-LWh-PtlpA~mmh(1Yv*|+%PJDr)OW%js8f*k46Q{H!}{MTOQgJ6P&gCzz=XW>m^53_jesua}&ym zlD@xD7U4fXAnTuDVQ%mQc=xo;dwX3HMPxxRrSJU}J>svl$aXWt^P6QKXniC-att$M zAfLQXc7_SZm6-MCAH8snA!B-?7>9Ne_fJ9PxR+fwR8gs46L(g@w;(NTQK!!yWL#2z zkx_!mPmw|G>-^-CJ z7N2f@yDRPb;5E%SSo(&eZS=FT*DDKdP1usn`S~{mZpn!66Vwp)HX$wC)@HRG!BzaVW%K1Pl$i^}>v%@OtH$OkDGL z5BB1S!vZ%!#-q2NZgB0zm-|{gPcyV4X}H#coPH*i2=^(ffK|e z;LpO*Y|kDe?z?*LiUzU*;r#nq+Ag;@<17I0j zl*{ChtYr+E3Y-GzG!HU(Mub|+Jzk;?DGbwB?t5IXs4k5xT{Ch3b8W6$fd571^KFls z5ljrWJnYQ-38f=EPMg#`6MXMAGrq7sWVr#^n^Njg)H}X?k*4`)Qp2J?#d{XMmXf?2iQ)vqN`|5#p6MDvHI_lXuR%iZ(kNL0TQmI9}OWcL}fyM$sNnBdJR*`J}oXdAjal#%Oa z$BW^-KKy&5Cz}ay)z5zO{(su}<4I~)5-qAP)U=7^>uPH}ppXzWlTb0? z#)Y7U52t^|X>DCC@lUETW%#~e{7Xs*ZBTSDZQgWcb@;8(`2j#`=Rc+fEK6C*-IuGh z-Z6mE3u5n1B3sJWh`>VfwHh9dj>(*LF?jdC!FQEbhh6}-b*UBv=AO>}I zp_fL^C0VB~-@(?bpS>rJt0KLdY}_b#_1b8gNPNxxmqOa+64c1j{i?5`g<9Fh{8f3M zAe~?Q8k02?E+)Y9My)#7WzsimuzL*xmkCeJmw%Wy0b@%7FWYK zmE;w|e@t`()X@C!da`&n%;sS#hyA_YR)N)DS{)oaU*L~iRIZD~rq?uZ$Gow2z_c}4 z%{Uj)5yk#2s2x)}N)AdIWcZ9=lyk}FVs~-axkCRtK|I3ab6pqY{>O2h*?+?<_6bAg}zgg4CR{ z&o;aB@K@DNX>qJ3DWm(sn&9pe*ojDgN#0m>N_JTNiTC@_P7`9W5h}Z<#T?*06v}`T zFiEwK!RAH7Hr1xf>?+m5n2CG8emItR;gJt9BTp;$^}(W4kcJ zQ$pnbei3?QA=1?z9yY@ThET#yQ5_YPU)e=`yzJRagT>xnhee9=$B#wXI~os4{Qy4g z�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 literal 0 HcmV?d00001 From 0190fdd8736ce4da7f408a2844abfc0d4805d187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sun, 24 Nov 2019 10:35:15 +0100 Subject: [PATCH 034/101] Add files via upload --- doc/images/arduino-mega-pinout-detail.png | Bin 0 -> 13588 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/arduino-mega-pinout-detail.png diff --git a/doc/images/arduino-mega-pinout-detail.png b/doc/images/arduino-mega-pinout-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5978a8e36a94835795f50f177a86c50c0e792c GIT binary patch literal 13588 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 R18#Ey$xEw9RZEx#{~zwpET#Ye literal 0 HcmV?d00001 From 2a4348748d76969cc1316cee37c77b49196e0121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sun, 24 Nov 2019 10:38:02 +0100 Subject: [PATCH 035/101] Add files via upload --- doc/images/arduino-mega-pinout-detail.png | Bin 13588 -> 22161 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/images/arduino-mega-pinout-detail.png b/doc/images/arduino-mega-pinout-detail.png index 5f5978a8e36a94835795f50f177a86c50c0e792c..47306d47fb0748514fee34d99da120dd555a014e 100644 GIT binary patch literal 22161 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%0D|;9l$F%Q+ zw&(x)wH;Wz3>N_q5g9C8@+YUK_fgVoVXArjm>n0FPn)9U=J}XW(wml?s*E4*>*^&G zgP*&H_7JQX#CQLYV=+NsF z`i6>s^hi|(cY5ADp&0zOg@@n0akC$hf1L3sX=&+k4EB$V)YQ__kEP{Q;gY}alHT5$ z`o0;5EQYHnF!@8g2 zvLlgCmCvvG=z40O_39bct7njDf*V+~-Vyxb|Z|`)I$D{ z(~65rEzB&?uK<9YllZD%r*+~vK7MX9I^>J|IX;fcX>Qv7;>EqnE=Ra~l@*SUqt52% zO+FVy``SVK+A;ZD5F+^D4m=~jf`pj!OyjtBrCUOta~S7)(TWwrhY#;wA}%3eKX+bV zSL_K1tClWXwRD*;?<75lr1yPRHeLCi-Mcq$-n4o1CN|d+_O2lj3Ck@OZ67j4|^a{TDw zx;h#v=`AQOLY;XKN$rE-Uooff)Cq&-+`9A zeVc`z;OV&^0BkKR*ROK~fT$-i>(@DU{{{4tU*^t7zD7NX;ccT`*y@tC*IflKfAslF zdedDuNpD(RT^}XAJX`jgX%0j5zjoS+@5^gBd`&o9Ny&EdWb`Ye&rfebrBDd&?uf9j zu(}~(tA2Eh^eF()$=DMry7nPZDC>Nw0?b$Vt!6keLDIEmo8sS zNK7K^++lA&{ouiarsn1+Po4PoucaifU+1`d#Y+2`U6#xI9AZLZR|@7Oix(_eyx^aJ zKmhp7WitRAIdat4#KhbCfT!pFDu(ygjD6QDRo}NQ6$uOL#j}}TPFfs~l~9qB)=`pin{TQhAqK+0>y(u4l3sq}@X@1zf#+5(Th8Vj zze88`4E)0GrJOj{6j&6h4)sN3Tid2o`x*@T~F;3 z*s`Bp_s-h}Chv?wY!%(=wVcnCcC{{;Zesg*E z=8dg|<-0d;9M-PlZM$ta6JT`6mrS8LkLlVzrxkoi$sqv%D(NLvROWspD`GGWeQ+G$ z8Tng3x2NQiucQ?OCge(ri^@rf3x)jSEiA;v1u^c>8tRb+|9($T6Vvf}I=bwa{o$dD zEKQ}55)zR>A`*Ojow^2i6;TqAke0Hip4Q}lmM?DY`J^1j1*>0Oiw1z`wC9593slnE zwJ7T*=|#^$CB2A}XQZZ9)_dZ;+qe9@y+&$kQL8`kWaXcqL$tN9JnHMaWt%I9muPE` zY^bjFDWeD|WqMmXeU-u1y3Z>AiJ3 z%*@If5&t}ndg|r*C;OE@qokLy?~r9b^0~38Nq*SykdUj$0-g=*XU{o%+Fy>DAJLoF z>9!LmX^$LXJ8@FN3;My7vF`?qq?b!GTS>LKuJYfjYyluXvxF1MzK?sMY0pSaRaH}4#1LRJDk7X#R|owMjFMjVMt%gGNP79FTyM2obQ~Qp%xY$8YPoY%lT}DR5Cnn2#RMIQ>Dj||yJhPXAKP9~^ z_70||FO!lxIy$)X?U$F6Nl7o>yyY=BzWwneIT;y7XG}9o^YEz0?*3jv@$E0CeA>*g z_MS0kPT<+Vp!30nd3l0Q*Of&i+D)3ocxd`myYNSk+;{yU6yJUcadF1Jn=*SgBIy;1 zZ+}@G&3XF*CPNY{N$**QNqJ?p9J=dpj)5ZS1%PvB&J-0Ea&e)ooV?SP&GAp4=H#HNInF2Lg>BwbdUAjn>UZ0JgJNyF8D}3aj7qlojfTrNQ5o}65>w@ z0K$soGk+M?u0OgzKf2G{QH!@stiu> zk$j>^c|jL10f6I{E&YZ3YgaFkVd0tF*4ip`i@-Y~QC&lwyBL0Ax;oVrAGwzFs;gq$ z_C+zuLP_s)MkuTA-%Jp^u+PTC+TH&E>3t6UeljHmI>FCR2$J3zbLL26F+t~p84qP; zXHWfglG)sGf`4pe6>xV>+-K~&^p`J%CFz~0p|LmmX>vj$AIUEzI#^vs8u`pgod4$L zX3?QT7-c+H&Yv$Qll#Nu&mHptKtfVNWRM7lbH1n)b61n+bLTQGzxgnf)lXHuzmK5{6VhR&D-D2={m>3W^wE8#zxSe+ul@NUwm3Q^TW%SU|Cx_1dez}%fgB(5k z`(?YuQ-*2Er9{4oy&tvFW0USgennF2FIjrIL$BTr7>^rYU*FK3{77NqiE$u`=jyB0HnnD!*K(Mpm*=2TDCkl!_W6rg zA!Tu7G{|q<6w|R+V9b$ z2mlDa5E2^`jgUVkHh$w~m$1+~oK&8RM4hGCWMwTy0LV$n(-_A*zywYa~+6#3@rWHPtmW)hAAwQeIvb`S9UdhjlXDVgV<&3S zVBzAfQ9CBu!otjO^ytS?Px@zC7WkBT;1EnM@XVXO%S~&x-{J&`sf~_j@}Zzi@^*MgkbdE>!X_x zB^WJQwCJJ}j6P9@XyZN4`~P@8?Q`wxoW0h))^AsxXxdtXTVHMsOL`vL9Uyu*iqO%D zbs0KTPZ6HaQ6pJmDD}|=^XdMi5g)JF*|XvGh;2Gng^dr*Pd@ zC-_u#oss+Q?Q?#vR3`Ci*^0}L86|1bYEcxatepI^c6AAvL0aIlB0cz_zsf-OwT#q5 z5dB;S;gXM$HvT;vXxNGv&;jA==Or3BM(HAAcS5Fq3W^CJmZBI@1xH_}7}sig?}Zf8qt3*e z8i8u*H3}=)vXPD)1OCXq-{|ntO9MS>zY_;G=kBKvthnaA_JeZzx`aV>2meOPo7ScnSwx@iFso3 zpHi({B9ODVgpg(9K=_eOUq4we;(|%;=#ki?cL=Z>Jv z`l{%DPBB3Bhnq9(1L|0sQbq+LlJmCdO0RU?Qu`-#g)_>&W?(+h_35RFZ8qmI?%x}K zyJasYmSchd>X52CPpUUtkJ5D#moFQ~u6Ypd35l;Y2ieceBDfMQ4i(Pp$M3v+kw-?L~k z3=necMc_GvIExCCGm4E2PD!F(K{SSx^z=I4!1PTn#wwMP#*40~FRmmFYoP&r8>E28 ziCkOL91511CG52BV6{+rB^DePm{3+F5i@Rz^1=NmJMu)BOUU}Rkz3yS*SfmJv<9bs zcn@O)5o=6HBSu~N6o=?UNYEZT-)4ohxI(u?U7YdRIZ6fw%%gMupJ%HAXl`BH4W=}~ zU;?>89W`z$Y7?}dP!h*ZS#cU|As3frS;_fsV*CBJ^i;NaZ0`kE!D?-kVE&!dh| zWtV?y2uqI3vBJ5OaMefMrT@@H_SihrcpNrKAiq6bsk5H<6^<9&Ml(q@L_@c{*T3r= zdq=OB!v%Qx*LCOo523o2Vi!w#`jd_CG`?qq$p7|q@F~Lec6xh?Sf0$u|Iw4ceuYO zy4M{|M!L+3IxDE~=sHv&DA|G4d{ZEmBMk-%}-l*2N zYtBD~O*Tf)kVfW&VD)FrPGC`9DR-I6@AF|KaiDXiX`tpTkAT2AhaBRdFV<%humkSS z?+z%(bS=~60{fB=glC}3M%tPdsR3_;4ZoQZ$bV|ux}y+q74pYYTP1aeV+0-Lad`)= z0i&)$O)(TzQ)QTbYQFpu`lX_x05y*KaG$57WV~A>Pp;N;{tq^#GT1U?6SH+ZIPF+9 zo<6AmB=*I##ri<;$%kUDUU=hVWROGh>DET$1#>`dYBmAL@8wgX?7G+gLvDV4mu>4E zT8shTCjkbG^IvtX*p8W7`ddP3LgNYNLf*5&ViypV>X?RC>k-7agYg%p?V(S1QUJ0Eme}j2hD-yKH-%vrHdq z@P%PbfoYRgS=q}z?ZMSX)bGWE>njPQ?+PUsknMYGyP1Ql0Vl3VzZ5J_(sFzUudx%Z z2KyhTaaTrb3b<+;Aj^lb{tn)#R17frgy9p0M`}-KG+`W*XGqCpOg{V#2&`UORr+&! zd~&wSNR20h*f=|s; zax=cPua9qX|0AtgBCUg_-XCcn+=Qs23eG|?_N}RFQ z1{R7nuKP!C*g#N81UzZ4@sJJ;OydAqpr(!+Togj~x8hNs5jo>vb8Lb|A2FcVxFcWE zmg8=$E-R?Y{Fclo=@j4SDk;a^e%;wAieriuV}B2GUD%I_U-VrtWME*9O0M@fWS&}W zn(eVgW-X{`3B459YiDKM{=x>Dar0QO{+#dyu<+#`*sA8B|X%^r(6{O~i7! zJ#c|7H?fw5<-8*&*VoRn@c-`fofK41a9wo>^ezt zn1O$W>}(6O>EGbr(Ae;p>G4J|uW_rergo#hS$gV>2(VsisVze_ZI*dcyQbzT`n`gQ z#p}*$J?-j5D*BcEZ`J)N+8x`iC7hj*-vY2*On>Yi3j3EpESUc6uO4E%HE#pU05A%y z2-umJ|IcAJ$ws=-9CM4o*vZi5zq*mrl{4=1-FEfg`qqNo;&d66%oHc(o`p;+nDVm9 zevw#&*WSd~0b{$ws&9U(%EO?P@4dYuv4YvE%BuGnZ@3He79~-U=H=T_@%STlr=TN#WyY%tzdas9QfHQ#_qOr(C3%>C#eV=4d{-$@J z7sMC2^X*g>sSU?D?TxZ5t(*V}(HOH_=tGxMe?zMuLF8|I&*eIJ;x%+2_Zk!7XSQ7z z-nn~5mCq_JjWb9}zPCiFK}z+~@H4b#4p*t>i|q%B?jy}>jy=~SS?1`LemwoZOyhlz z=+m@;(-brCT*>xBVSY35$nt#q*C1CxEA<qf9XZK=5e)IOsL0$Y1eQSs5xulm10J;VhzC~8Zo)Vc8 z_Jo9hw6t($O+9gJV9jd$ceun72 z_)zLftH1j}rl>+jwxk{o7)FR0V1mPfUS1H;>RRLH+TYYCdRb!?vKEK`HQ!vcCZF~A zXLHGf*b%Hz9|3FSwD(pf?X)mmcInV!8(fNdCGP4ad=r2Ecc&hGEqlD)*?pSju42jE zj$Gk>&vr#-a^~$RnWcsoNE19bsMX4G!fak(y2HtSW&d7l9O`kUmwH*haD9HPOqf$a zIe1sIY*$fS(7|xVFm=mc5u0UV;yrpTs}2*-CXr9QxxZh!I6Ooc(hx@3(5I3^^4?(j zTt=h=T^^7-`1qf@=jBTGk5Xwy@)r1TLT67*yE!|oxZ4LB@)9z-wz9XcSP!&9>-QbA zWu4hr&~Nnm;{0&Q*0&ol z5&-rG1Pve$ES|7HMlCdy;FCt}?as?PXdFPJ3}rTCbW~ zdMxvJKhu%d)>y$$FW!dk-Fvf=w&BWfK;>PvM!MH+KveYfWTX@`oBUdLt}Z@~G>kd9 z#~dS;*Xvm8{a>)$1+nwXrUsLeYuDNRu9bPWJ36XeQ-4}xUhi-*!J7I2V5J#jBX;;m zVD3QZRJf~HP*EDuWrf43UIYX#ePiic+gMSBh_KuXsnB6VCM7Z70c}bBU{*PTLQ z%>;rDtv;lxCDBg;$T%6O!*vBx*101sp@+Kx9M7i0GZ3KWU*WZ_F%RN^kZ50t$m-7? z1-jz$eJZM7Nzd;~fvLs<<~mwRtoHI}445g$@MZ8cJuBnP)YPL}&Ns~rfr2B${c+-J zm&Zuuiv{zlGHuM0LWZTiPW*6?-Bt%2)A*AP=CGyd22Jpw|Kzb59TuFUDoTN0zyO})Gj@4uU6n!$XIt)l@Q z&&kPYfC+ooH;CUAHfM#2tiMD2zV2$zUE;c6w<@us1tmVDnPaAzsl=m!K%Iyt{Kq*M zQ5%1OWB$KyeYu56tf>)D?NL+{A|-o!i=m`&N?JVT z|5aP`1T$AOL+TT&RiXBwG~y@w9I?5n77pAE>~ElS>B${tOuL` z%~K|27F3Ubvo6mhPF}p5HLO)SbhW0in0rpLBOGdi8_Jf{baO9c{|~=Et*= zo1s{d-*45HSCR*MASyqT*{VbBRM|O%h5uNw3IQ19msg_pi$r>Sk1$^;Hg=s@?!LZn zb75?D!Yi|WJuLvV6UObZM~l#ubd2# zisZfXud_inr_U8y(rIQ((Sm^TYhc)h#@Sd&i71kVbeP81kN!{Lh0MUl8IOR-tbu;K z8k%NZF@4!IdZ7fj6KUkWj3#Z1Kb>fbXqL#TY)(48Bk$?Cb6lcPKAkuBal~HHz2I41 z@CRbOiO;mQaOYI?4U45Ttv!$Hl%%T?lW$u~jTggLJu?%x*b<7TX;n+jM`yS_U^*#4 ztQAkn_hk;`=IrWcf~}W z!b&>n;6OJ|&v%}ld@!FA2YR?TYoJAo7k76Jt|5H;{{H_? z_9UBo&L*49z0b@uGaIR|tSDRsT!j=C>6b6@r4LNK2#yww9GO_sv}b3! zh%H?gq+fbC?bFZ@R#rIZ;N$%Is>O;cC(ev(kNhnzScFr?rhbr)F1dL8;pvQ~^>%C| zbYleS2D{HL;H8Bl%f0Z}$*DgX4ZE6i@_nYh*S4F?QW&vj`7Qv;>nI-@A8&5UHxEiJ zTKTnMds1PeIy^ia{bp@BFvTvfV##IsM|c0NTxx3C19JRVPyStv1E=N|L17GX=W(O= z?q>-T7BUzOwJUW#JSDY%Mi)E=>xyho%J3L-Een5|tPv16>K^gmvysXzj>}{PIL9THK;p+`*BqFKiXATAoJJqahVV}@zo9xG**co= zxS+(DFv4qk1e}dfKxne*|7NuEqcU4;1pT;WzL(+>KYZ{qlQ1+q9-OZaG{JY*8gp1A z3=BkN*C|n5i_K~t$rY&R%)x7;JwEJHef9|LlLo078VU!V8hR@8UGVMkImz1Eep z5yOO-Sy(jw-5b5jo~(c0_hhs^j>)23R&F0sQE_GsTWwZFSa4n}Osoc6mg30P<;K-D zWR#U)fpl0|oy7tV1oNIlx6$TwIM9M%@f2Cdr-xwMUuRQdEX+dL>4CdLRykj+esp0_ zaxnN;#x&5LictOw=N;%#NH}-n86I|k#r>O>ji11!r!SfTKe>B92gpOZ^QJ7~DXOZ? zl&^jW4h$Zp+J%nxDKRNKHt)7`L#;kF`0*=4TEh;Wq7WKpc5jyI9m+<8uT$PlW+cb;rdiKI&Snc#@ z-PJu_UHQPMLQLd(rnb=T6^TWWnMqnOij0gb6GsvvhSPNSNJd6RixvD#H85M{L6aC# zSLX@#Q_^^zv&by0o}FLC1nFI0gYpWh*at(oVgkz>6Z2+W>`5JUCljc!5}=hu}DB>F`noTgWrw zGg3hTnJ{op?N^=V;_%UG%{Rdy>OJ>olM zk~bim)4k2pgl}}ceKW34LQN>oP3(693r2By`S0CsMkH~6f)+_%>X1oN zGuF*bz0b!UUmhoSRQkSSf1Te^-CBU>)%@8)*fdCllX2)*ywYRhcslT_e?N_3fq+!#LL-T#J^e|xpMt{FQrLW*>%bTpkCs*vmA$z$ zCq(ysdvTZ)4Kk>{1DX83zV1CqgJJkj$;6r$N~5J8{~TwMUX?ym{cWJUuT-VsE-%Mm z77oMD9_p9Lk5ce)A_rk>Ygb)e({*5nov#hCN{T7DzPI6Ppf;&uw%q6kx8$lIC1>jo zLDY?J2PBLvlrcWKz5H%&XSLJ*rLSzNWNC2`ZE5~ptY{=mBtM2RILsfUrlEm*8x@O@ z{Z0ihah~k{!S}2t<88oZ4k3uxN_tT^l(?%04+UtcKA(76>{}X;DB%)7BEKHTYTvq|I{?J zO+-lWQkSLjHlXuQBL~gik_VISbyz&AG(74R34}^9RS~xE{vHJ|AlJ%zx5N8u+Am!; z%g+ffDNRg(?X_yR>OanpVKOqHUv9Uj$1ztKc#KkkOGKCtglAh1_xtZYMzvslksDO; zFyKW3y?bZGAQY4EK+iQXO$QUju03b$I!iEBBwK=_(eUyOYO(N9QGv)0?{%&d3M87t z(Yg3SPCq?9uN6$R3uKLYPESiIl4cr2K*6!tbXHa>ns}$lZ+Q?uHV>IqcBj9`*$y6E z2!D*t)tN;xQJVY22nu3KGco=&TPS~XdArBFhDZ9a(G%{Bl@6v}lJr*ihZ0Clr3pz9 zTGQP1Q{8zGc`Me1@)uLGPNi41%WF*qwWVFi#?tC@dPXK`E;zn6WAAh~M(ow{$`6;d zPWpI+zuDDMRZ=})P`hPu4q{}S9_!OR@^5nsyoT9GsZJ3u%+ul5JcvgW0()rc0Q!@A zEzhl6g?^~=hYJ0rP=WR0v{CDNU3fdh{_v0;S7fA6jy<|c`AXr8to6$46$6u0iDO-8ydv7(e?>9`EwP_mUy|gzM37cL}r~ z280LgR^yU>+zU2GY={NWsosCKu?ga^7ysSX=pav*B+{Oen;v-Djq-tv%*PmC0Fzc` zSx&(?@N!py(Fdm7J4IqgPv0pvd{>cjCkhXsunG73#XGrK76nui4z$e|*JnpfMdqUL zdA3=8dr*Bo@<6Q=Ml9uAjiO3NR#sNX)@MGx>mAlP)3~C6f;c(`I%M~Aok3!HA|0Eq z(dVriIaPU~0N&zOl;=7;=La>WkJMd9YL+|i=#4nCh1^ait@e{1p_&?XwPYRFUNN^~ z=v&}p(Kq+MnI88rx*DJSN!sNjlH()%*!!PjUEp147K^zu7MKS$6*6l4`Pe3%RS->W z&OYVGTgug?Mg41@(*Wfx@qME;=4a|tvbDRKAnps=7BWo+ELe@Q1`0?6(P=i-Ez zb4>=};wRZ=8Ux=Nxjt5U%;$v1)U}NSf4RRo1P%-v`NM?}qR2I@aUj!AHc3r$(?|N9 zDS;ZaxXb@q=P^}-Q=MrMSGYqy?r3e z7)mCnoX>WLB;J){{$Wrd@mos^+AXEuG5-EE(2TGGQVCcBgeINI6Lz>Q>Q}-~W zHDWYx>RHSebGr+FQqvCj`_evrg7Y~N9sy*Ep|#zb2?^cHJt4|cQ;ci>YPdwz%Om0m zM#l{G9oK8YzFajU4!oOT`=r=jTa2o2F8)dTqzWAbV7=AcAZL`5mc4|=d%a|(-xqP% z5;it_At}=a0xl^!pUC#@gy-K*%UVoM!x8-_+mBcjhOHi1B`l)!R>OoWpcwK~j#(l4 zJ*$t-5*m|Z*&A@O1lDwR)|yIE0sF$Ru)=@^^Yf)wlOMc6tl3AI=|dPjsBS4mCub-4`S}hynJMkd z%YU?yz7QRy4KJ~Gh-P1Jj+hbpBEnA15oe>T*yf7`YGPj#oUhCJ%dSyUors5x^TYj? zVjQdnP);AKRM-$f9k!adxfaer`}V zF$N59h9=aH;aBlCWNmjkYR0TpVnSH;2R8QhroA7y($Z5!hXWp(@Jn7Q==HbAHJAK@QUTw0XdfBb?Yg%jdZUUlB&c{iTwZ2;K zA0MzL2TpK^ac(7AJ|xtK%W7)UMoMkRZFud#eUca!dZ(o}`p2a~6xzS}_iEb^g4RMeo|&oIEc8w|*CD zYYPnxbq#;Ie~yhYzk4mQ9-LcS+@H^#yE#)dk>O?1h1WS2NME6LV#NvKf=T`WIEMb( z)0>37X*^G9%6G>ilY*>9B$#Ei>9%9OO>o01&qCDW{jf-e7Wx zG1T*E594TOc7qeCweReRx-`F`^FZSbw4n_LWaxzcRPuq%^_{5%Zs`|hJKI4E*9+Zu zZ(p47l4?rDi${NbAu1Cu6Nvt}k@)9a9OKpBpA)P2M9;NeCK84}wi&;TbHnTE>i^;5 zStR$Pj>Q@XNP{Euhy!rKqtRoTcuI!-nD2)ZWuE^~V{bCyixhU*N>NOZ`>X z^Y1T{qpzd9R4*zREQ>bK`zW6=`fcEB7&2tJ&HXOhiw2|8u{#-N7O_VsJJut%9=nl# zw@&SsbK`PTf*>Jj_0?%vet^^G{Ur`?{qCZ_=0z3f|B4+3enokPLq~aIK>sQ1P>snJ zz00*gCJb?o)_LjvA&2X`lC_?m_Gn4yU|Qg<;T3Vf*Q}}+0tPR{A)fWONg?~ex%d`$ zUwOm)6Lf%CYkuCH?;pJp-mcS)>>U-|(h~r$%F^C>#vSQ`APjX>iPmP+w+VY-JJCds zSW+NyPnm~$qVd82X=a*dGs?66GfDB1Co?m%i=*@O@NVG3 zm!LW;*?0Bys;Q~+zloymi`$UwCJt!cB+-c9xUHjVu1+kFPVo|dY?B!q4qo2q;mUvF zVP)wA<}+atg(yBjPfcgliLG_9r%BA~M&4Kxwm7L=jp-ukKcj;O^41Ke!~vEVGcm`T zu~|vQkSKvEV0w*DjNV&I_=!uMZ4SEi372gD=zQ`Ls(}2VS`{(r$G%t#_8!CjGl!qY;9s>qz*EMB_;;fMh@|jXn3s0GTJ{Q1qy|z#4iWtg^i{K zrlT&*-&H@JS43`jA^f_l1pAiT^?3H!^m|P>K5yUVo+c!Ycih$KhGjnlPyAHl<>MP2 zRdRh-QB?H;L@JeE#aF#-rT}F_#q6!5PLwO|7bHN#l^{$IQIwUH)zN9LsV*FD5d24F z|EdnYB^#%e!b*o6JGqQvobjNC8)>vnh>j3%7r1B^hPN_{9W2>j_R# zQnZzPRafqS3uENt=f`T|=HkIwLgJzRju6}#%$72)`3fvGr$1dJY7dyTzX)!B;9k^G z-C9eCfX$zpZYGf6jbFG2;o_Y3_gOnNy@V#?5ypHDd@VlDrPV^CD=14X!J8Zh`uQ2 z%PAJt!NLNr0wkMU>GqT)eCzDoVgw#?dZq}epX9+{^6!BM6V$B}0Wfz>7gD15+SRr3 z$HUn#u28l+C*6Af`w3da1><)T%p1wT*BdL@i7!rRWsc`-x9m`1l!u6uC7I0akf~y3 z%Reqpqgm-=+0Vf-ff*~}K<8=ZcSJe(rq%o=^30 zp0Bt26Cw?(w2*T6h02G? zoWfPf(>}MI{%3Jq+XH(le#VNIVY2bnp8~#oYuDG?02=hqNETO#wqpY-V*_IoviUfF zu*3?j;cewCop>inCaKkt{!HdSV0y;J{*O019Mq-d5S45OGu25=4Z^YP=Fg`cTnF+H zYZ#QAQLt|7x+_Ic)Uxf>qY+V|X%>w~Q21vZ=(Hb1yu47@Yv1A&oAOn#xTqJe_f?5;PYATAi_ke9d(6 zQ(Un>_7hCd4LY~uLF0^fGc$KfEK2$`1iPf=a3qW-#SVJ)2mq`}dU;{`|KuUnlp7;m zz3(sOAvmO@9~>NXn?B_(zbPy|^i@w$Wk{S;9l)fg65cF-0IpT5n+=jF&~O*1Cq@+B znriVD7ZS}}mK516Vv=qjAGeq<)H(OT^GhGtDxFk*h~y+?%B$D#|~l1pAB~lek%r-|@VE0}3!~Ck1()EIQe^P~u(~ zsQs(a_IsR#->R@_Q%3zya1y73#-5xI;=gGwY_5g+1K@oDfs-^8@?SVOU^CHaOFCh^ z^;dpAz-%k`rqPujn~FhqmanN?X!Z5dnodNy+6xiPJ-`qK4pJN}@Ei>c|5~Wh8-1k8v@6yJeN#r73=V!F5e- z$g}())0TRkuT=4Sm?gDH-_M>&@hxFo%R>_D7ZmSLx_o@nqC z{P{jxW^`zN^0!@_ARAuu4KgHWJVsh|j1D9&F)OKO2%dYy&a^KZfxo@51!ik|Pxox)E#5h{+a*#g28PS3DT?OH7Qg$<@2x(^ z3vSlCyMJTEqo}43Tgf7i*PlMpL-~stA_|FWJ@d=Uw?Hv52j8|V8Vl#68a1~3_QOM<&sH?Y zfiyKvDkw+}PB)*FUpP1Uccgo0(GWI;S>IMJ9G4u_K?enDi>tebWG~oj6NZBJMB}$R z&UpZIH2(=(%hSu1Qzb5r$99&;oG-@*H8Ck;{`FtbLJ+)57TZjhNKc|NaSA@Yk6ge3 zCb5M?ln3;DDru;JY*(P5M@|K)iRXc}HhG$7hpqCe#_O5-65N~Kz8*=!ELD-DRo~kr zfMQ^yL&WH*>~WZk?j=yxXFH*$^DN5i{@m$LQOk&A8Umg%54w-fDJvhcLQ_rNt9Ph-@j-7M2Gso>2x2xoi?oaNzb}i zy(^&p4%xAPt!=PuV77soFhIWMOgiADIvuo5^CG`8w^R251^jM3;p)r437|kJa=@h1 zj#?DM&`DxcUSey!pXBcPBCwUij85`CV-LHG%0|A1_x8T8dv>h4rZZqD1Wg1?N-8Bw zL?nIvrbvf#XL1wQZ(z1=o}4_pdDFE#Dc4$yhxL$)1;Rv~Wb9Xx`ci5#TJO5JMOS@t z5yN5Dyc&l6ie)JzczcZ!%57e5X7a9UC3ev(<1lHm2g& z`6RrUNwUyaF}M6@Ybh|dx~gmt7IUz_>CS^t5tsTU;rBSKH&b!HyNXw=6e;|Uz@K*) zBZT_d-AIUcu|>V$uA-Cd!|CqH5>ghT1bZz&8~N+=YV3CrlEf>P-sY~#oUL{<)WB46 zd6*eYJ*G|=XT?Km&mr*&`FEmZc+_ggAJ@FJrm-l#3fNlvz~8_He}{k+NR|FAUa>LI z33ITD;_mw`&tgI`#9w`2`1}`v8^81_bvTXFbUvB49la{yEQ%Yo=GkvBBqbL=?wOgMetU80 z@oSb%==vRLY*>LJZhm2*r4aUqD)Em20qW}N^}UHYu4+cweb%*i?6ITf+{9M`^lu_i zimC5bQwfOnc6UPHv0buQqIiy}Ljqd^45m}hfY22t6DCM=-%&gjXn3+TKYe&?8pWA} z#TNv*Kjg~^JW|{nhb?ri1x@N868(~RD1kR7W#PE0sC2!#xFCFjK}I^;B}7l6h;|nO zH{x!xbl!|UhaxYr4a&}0~C;js3Y1-N&irJh`*mdcWf8*1S-Ks35s!y zb$>9&vWrT(LJlfPEh^Q6oSRjaZ3tK~6;xt^GAhH8PB#Ndh)ELVde7EPoy}Bb><_(? zMY6I`c99Xd;!uAOm7}k%{P$mUcp`d`JwMftitBFgDH|-8dvOvUmy1@-7{Wzw2Lg+a zB9dW&KggqRkb|Dp!B*NkI_EJ{k1F}v?XXip_|%}VC%cC4T}kqmb^L)vc{a|i$bmk5 z=(lzntrda%iQ?d7G*+!A+Hn|$i>>W8e?TGEHBL>EI$NBqtYS{Bte|?JOy^%55Bl5> zim-3B1|X{c*<{Z?jN4wCw3jI5#(5(YvWMV{W~Z1{%2gvGX@gv%A5@1|az0?w z%3`!vhe`Y1&7NLfo%{)9Tx`>SLK~tzid?R{Qdqbv(I*2WDkxJ89=Br6v{}9Gz82TP zMgbj)l`B5of%LmO?!@du9sLN_1C*e*L^ZuDEnf@QvcO)>BenAv7w<8{lb<$G%5U2J z9|=Ld@1m6zl*D7e+y{uE8HXA}gh7h&hTODX<}feU)Qg}F^sqs$;~1|kYwzM!pJ3AG zkx@LPpqmtiq5EgIq}9dUrj`+bMbdABJ&%w60#f=V1nDIt7ZFc&4+D+r>aqZtrdId= z*28&#fgbTu3knct@tY*$Z>?ZRD|;q3V$O^tn-Wi0)MYz^ySTU?*k}i0m^nG4+g&Xp zZ#9&wb$G*6uQiL0m%6%6FSim*Oz3GO z`tf=PbMx}+ATul<2Yp(#{g?G1INrri0KNTL>u{oaOM^*tU&Y;XEaa@6ORb0IGtOBG zY37c3@GiZK`072|)2n?Q`D$|}5OC2RW;(}IFk_XKL0sMmv8kyZU%pJzjl;fCazvHK zXC>LBdhk_}vx192Qr3tJcBgDKF~#n!V)~!us)@iF`B}ZJS&;tzr_T7FCwF@?DSuLe z)v*<35D5SGe_4R-X9t`MWuy?B*C4bm#KVz^&G^hOT|n3BwYksFn=}ZOqiL2KZw-~5 zWPO{tq`y!}D$0I;5wNJuJ zKD8+l{kVC5aCH!EeV0bqSEIQ101+CcKnHzKcl$O1XP3>SGwL)Ms7~FSaXllDgCx!% z(wgFs89oRfG%|%Bzs7iSPAXaX6}W0q!OBwo1I%frY6(opk3Am7ZJ*Bp3p$SHC2H?W zRFGc|{$Ksc$NX=#{GZ7|`)Us_offTsvV!Ap0S~sm{(XNu1qw(pttJEPl2)Jmg5`i5 zCKRbqMAHaZU}2|=1!eLo3QB^+1hMFM#>Q`_CVAS);e9lG)K(k)s%7EsqYre(P#e?Y z@XDJm3@dS_xHla%gT5~93X`u^YyHMpYr0G1?2aBPl5cdXuXJ9+)WF+N zp7@JE&tTk93Y>*LCE0m;15f9!5&DIHALRHF6tp9ktgdL;>CHwf?;`o=JMtye=gvx` zz)A$q?nH~43ZMl)9~Gk&g+G8P;s)OdchodB&2#su^i8hfTS)UB{!D@M&RCRQnkxQy zY-<@b+ol66XLF^^nI%dw&?XsEnmB%qRl=b=ptr*vM>WwU7BNY98jhy#tv$5zk z?xux6y>Z8icW(#diBTc`cRH-Q9QvPO0T-}=n>ca~e_5&x;54)~G&0*G1k_8S!^2z4 zz9Z8^r%IG|Ycd6VRYq#@Y(h9QFw-22dv4NhTkUv7p`Gws=bOzwFBo{9loE`?8k?)s zs#p?|Gzq6y1c-?|1SJWZ=$4n2Y~X7f>VsHk=K(YG?fu0`WPqSG0ifx0`Bdly&x!bz zI@o%vWJP@s#03FmTF13-JB6%=octyG{w88e@$Z}mtTPrq4-g5>+LAZXXMZI;8X99` zV_l58e&od36v#y@(c#SOdYf%)iTP~rCy&H5dEWOi$WoP-YNhEM&qtqdlqCptVFMo1 zc>#Bg?Hlj1X_{ntsm*tt>+Yb>_g4q)x0DUgfq){*^dBwE z)NwG)OdJ#)kP1xas(*#`&uWWRD|R^b3U@R$`Ojn;gwqrphY4MXRK?4Z%Ll;!y5<@E zD`z*)*8Cz7cdQfu-}j!zp~NBlK!8azaAVXeF{Vn-AHCB)vKJ411aN8#>NPPj2V-c- z%s9@V8qI#IKA-uSlbK%IWv==ffe_Jt!-H_OFmyz-ctqaW?NDgreAM9j(AWEn?r?fCP0N#% z5KfkqJEf+|`Oxb+VjI=+aq_yjNQ}gm*8Y_dg?!C;`IKf!Tdt7U*LIBf=B{b8FZ1pB zH%hs}^|gAd6{99pK$rIVYV9Q`uG$&UdI6|=yrG135qer=)dwjrrgYQMdfhs>n*UeY@kG<~D zU^-}eO}Zx2BA`^aLlfNTp&wK!bVa;ggE~eAy1b&951tO0I5;q_*1maT`}(lMdEQU{ z`EyWHZ&SS*C8-gD{5v-sa{-go{qd%16fq6PBG1<-10?`jdVNQ??;os{Aq)fn_zO4I zR+asGsk#dc+j35^g$sy6%=p6Q+*%HhQkH+;5QE?1F{>Pz)OPz{#;=3DpE4A>BYRm< z+a3T{Md4}0gqGy{0#L+hAYWa#7`HnE0E_v)r(3#o=Y_aAoSc>QXiZY0#<(M>mmDeR zyg=56^UiGWf?$$>^!yUI4uC14SV0y@VPhjjts(cVZMSJ!BdtM&LzshtA`Ai<8B%?J z{PcLU>3f;}IRcOFV05^~Wu5oM@6q9U4kMes9LURD=o5}SY=;b1$Z_DGaPuoD$7^d= zd%MERN6+ye(!0Hl^s9jHa#j#r3fvCM`#1J#pS|U{G=jtET;1JqtN9+rSL(EAb*?}W=ul!wNmGWjp^E?1FTHmd#n0(`L~U={URdK7N680WjVDaNt+ zi;7!PGGU<~40>`9b}l4yal zIsyd&PfaK&&Nn4-w?7K!F#}PORz+q@yyE}tG>)_tJ2yXo)~6>5D&VtjHD^%Bjk~IC zX=a<$@QN^g)Y>z@{1?=Xv%;fTAypwI3emj+!bn#oEfd!s)d>?%3fI2K8XTl&@lgUY@aR4mQ@R&{-; zZdT_`-`{;NOb2BmYL8xu{tT!{qM}9!BPbT7SO+AObvU)*etu!0z(y7E1>p{bzXGJe zK_7JhK&zZ6d-qHKj=1ICQTkjCjBJP0nYRkyvSC}@R)0P{{vCq0K$VE1E~QGi%s@qE zQ~xRs`b;42V&*DsR*O7MMU!7$J(fqLtFLFe*!5WJD%|Sar=nuVS7jZFSAj7Yb*L^D6PHys+_`UjpR8g9gMCbqHu0>BC+c* z%LU9ooX#FAcx6MuUeg@qJ-f6pu`s)6Z)4HpZO~$QK;VSb2?@4!dQ>ykgKcu91U8yC zu00Htl~E7elI-tCd8iCB+vt?xzN&b-6P4&DqmyufHbS3peb611!?dV;t zysG?bIQ=V=(>!BnN{RCKq#Q(H2X6cn(${}Wz7yS672Z4*NWY`{R%pRpM!~C;UNC^-3yIwIt~U|sn_D#eCIUxe#gg_|K8%B_IbGn#LV+KwYZto zV#?A%WzAJu;wgp;(4*&{29DqzMVh-~q+rQPH-)j(UI)f4gqZJU;o9!AYdX*V)qV!C zUW>YyB1GX0AC;-j-56`LckJ`}*NE90{$&n|mXCDM32ttR0@-r~2o|M^FQR*HMjHWx z@0sk$loVx0N9w8W80mbrpE@0A&ae z^T+~L|C;=#@cwDzk- zBl=N`ezm+BT}1=c?jgQ-x%G>*VqbnraZ3lB5(w;crP;FZus(m^(!3R_IC{ry&jMtX z3|76AJh__x&BZAE`G@6JP+r{Ryfw}pDAN{4{FTtUq@-r)u6{dd6%ch$F=YuIwCJsh zZ1+zIhSMf&SKygc68S14xXi)U$3PF5{7wd2+ndqwJu=@#FB+Dza%|!;InXA5F`kMb zB?YY?Zv*Ky_l?ce`Q(At=2=Ov6Clz#CoQ4xUU4W%rn@*VBO?QZO-)+7tzf6wfL@FJ zAd>XPB)7b#@F$A$u#*{;Ms7ochoNlD`uSd};bgnn=;GC}Bk12GU9M1|u?&v}9<2qd z!x7KkSY$xNnN;?dykH{>Gb?t1VWQpdHx*ItAB)F;Ts*B7`Mi;;tn zp9BW4e7;3iYWq+6?KKxCppn@NVeyJws+l3(lei0;--Fb2#uM8LiFVpy{YFqfZ8bJ! zXL%Q(`fq&rJ-L|lL7g$F7LEKY5mz?d?1N-KO{;Jb>jCsqgx+!-2Kr@&5inr$P7lIO z^1;*a^FK}=n=$-JhJS&YdCv^)mV}cUh9p?= zO!*ohi&>&w1;kD@esoZCjK>v*!(Odx`E>akisUgmsz~8$kB4=|X=Zjgy2+G-Dd6I; zlp?)3aiGZ)cZGig(!wu5TcV8YOQd;a@_^w*>q0~Ihyr%*#MGwz*czT3`qAO>QKxU_ zfPQ5hYG9y`d9pH4KV#z{a0kG4k{2qELvRBVGwaF-5wa2X^SM+ueB)Hj=baZp+wcA< z85v;OFA?ImursOMQP+tBXZcZX^bQC#MiTU}vWl+sjk4crW2&}x{xtj4794Uz+tAdo z`S}7VC_X#e$jImiD!f$>@Vc^=&ITNl5_X$Dy~mNf(AH)-?W(RD@<_ZYhv>1 z=QDQRT%P%!x1IGlpvZAbNy!?t2dSHy*?k>{Hiv8M{oL2GpryfN1WQdi2%CabTT#Ee z^H6toeFTTL+=@YRPWjRWtji99kN{o^NZY4iwxysZIMpdsAKq;PdSPT_FD~w_n*l#D z$t%<`Fi=lgy{vH?7xm>IdqLZJB(N6X7#tk1a147c&I_7e3C6=>cd7y&nj)gPg(VMY zePoBxdtnocH$OY-JRk5?5i?QJVv0OG?^XMsp7v2FYV+titkN>cvN$gEhBTe+tQyb4 z?`@vjxO^9RkvU+J%UrB1;;#cR5UE{5xox4O*y2T~#D_=2^@>hTRU<>?uo9&|30Dty zmW4J_B}FTNkDqdkkb{EDS$f3$`B70Xbo_@WS+Rjo0^yuSQ*?V)as_KvUmN7yTrqkc z&tv=Z$3}qm)#+Wt#grEpw;?NH$0dAEOUrqX%6a;z+cIsCfJSr*?)>IwbbGq=H-5C} zau4gB$h@YWp7IFNiR3`I8xcng86OKDEk;}0=NR}Nhv8>e;`iMTG?O5?t@dRJ4_*vjn zTLcBqx?a}mv8~asj%1R)HS$3-_tDYuecY`oC{guK25h(u+6mEA-wn*I>nmvn@wsYj zRX-T~V1e^4y>jdwSWn%kN?hJWtM||Ewdb9dw49`*ob1?p7>5J)({ZiEz&YcRm zGP`u>xx)C^ZkN|y9Hq_dG?UciV@KT=2S0dDLIOFO_f{k8$enD)p41?N(70CGVVn#q&RF&>?#f`EX0b4nuMh1fyu)WuhRhoT7inJj>z{vzC>w1hDb zK;s8JrQ4H>^757s%>=k!wm_C`VXF9Yt-XIyUs@h=a(xxx?$#C8I7>BhCx5sZT0>@2 zZS%86Z)u(|{ju6Wz6M&bwGoAn0X_CBiMa0n#()Uh zz1v=$LwV@&{abDq6q$E;S7_>ah3SRC{&rsv+JwQL2XU{A zr>48XkD}CoXv$7&Ej1aqEr67?{Xc~oC0nLws50{Aj`f#oC%HA z0VG%qF22hP{fN>S;I4>>6soaM+Ymt4CL~G+w*1BlowtT}DTM!zQ4f1YYB47BR%)P} R18#Ey$xEw9RZEx#{~zwpET#Ye From 1be7c8a8cb7765bdf25115bf37040bf6b7c711e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sun, 24 Nov 2019 10:39:46 +0100 Subject: [PATCH 036/101] Delete arduino-mega-pinout-detail.png --- doc/images/arduino-mega-pinout-detail.png | Bin 22161 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/images/arduino-mega-pinout-detail.png diff --git a/doc/images/arduino-mega-pinout-detail.png b/doc/images/arduino-mega-pinout-detail.png deleted file mode 100644 index 47306d47fb0748514fee34d99da120dd555a014e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22161 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%0D|;9l$F%Q+ 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 Date: Sun, 24 Nov 2019 10:40:03 +0100 Subject: [PATCH 037/101] Add files via upload --- doc/images/arduino-mega-pinout-detail.png | Bin 0 -> 22161 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/arduino-mega-pinout-detail.png diff --git a/doc/images/arduino-mega-pinout-detail.png b/doc/images/arduino-mega-pinout-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..47306d47fb0748514fee34d99da120dd555a014e GIT binary patch literal 22161 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%0D|;9l$F%Q+ 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 Date: Sun, 15 Dec 2019 17:56:16 +0100 Subject: [PATCH 038/101] First implementation of hardware limits for RAMP --- .gitignore | 1 + grbl/config.h | 12 ++++++------ grbl/cpu_map.h | 11 +++++++++-- grbl/defaults.h | 50 +++++++++++++++++++++++++++++++------------------ grbl/grbl.h | 4 ++-- grbl/limits.c | 44 ++++++++++++++++++++++++++++++++++--------- grbl/limits.h | 4 ++++ grbl/serial.c | 2 +- grbl/stepper.c | 18 ++++++++++++++---- grbl/system.h | 18 +++++++++--------- 10 files changed, 113 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index 474d49cb3..9755d07aa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ *.elf *.DS_Store *.d +.grbl* README.md diff --git a/grbl/config.h b/grbl/config.h index c7ba0aa7b..49049b24d 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -176,10 +176,10 @@ #define HOMING_CYCLE_3 (1< 3 #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS4_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS4_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° #endif #if N_AXIS > 4 #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS5_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS5_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° #endif #if N_AXIS > 5 #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS6_ACCELERATION (100.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS6_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° #endif - #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm - #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm + #define DEFAULT_SPINDLE_RPM_MAX 12000 // rpm + #define DEFAULT_SPINDLE_RPM_MIN 550.0 // rpm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0 - #define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled) + #define DEFAULT_STEPPER_IDLE_LOCK_TIME 254 // msec (0-254, 255 keeps steppers enabled) #define DEFAULT_STATUS_REPORT_MASK 1 // MPos enabled #define DEFAULT_JUNCTION_DEVIATION 0.02 // mm #define DEFAULT_ARC_TOLERANCE 0.002 // mm @@ -502,8 +516,8 @@ #define DEFAULT_LASER_MODE 0 // false #define DEFAULT_HOMING_ENABLE 1 // true #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir - #define DEFAULT_HOMING_FEED_RATE 500.0 // mm/min - #define DEFAULT_HOMING_SEEK_RATE 2000.0 // mm/min + #define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min + #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) #define DEFAULT_HOMING_PULLOFF 5.0 // mm #endif diff --git a/grbl/grbl.h b/grbl/grbl.h index e14e5fdf6..8b6754859 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1l" -#define GRBL_VERSION_BUILD "20190605" +#define GRBL_VERSION "1.1m" +#define GRBL_VERSION_BUILD "20191212" // Define standard libraries used by Grbl. #include diff --git a/grbl/limits.c b/grbl/limits.c index 74ba34aeb..0c290f1a0 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -110,7 +110,7 @@ void limits_init() MAX_LIMIT_PORT(5) |= (1< Date: Sun, 15 Dec 2019 18:28:54 +0100 Subject: [PATCH 039/101] RAMPS_HW_LIMIT not enabled by default --- grbl/cpu_map.h | 5 ++++- grbl/grbl.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 1c0171d20..5cb3ffa20 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -315,7 +315,10 @@ // Enable Hardware limit support for RAMPS without using interrupt... // Warning! bouncing switches can cause a state check like this to misread the pin. // When hard limits are triggered, they should be 100% reliable. - #define ENABLE_RAMPS_HW_LIMITS + // The RAMPS_HW_LIMIT is implemented inside the stepper driver interrupt. Depending of your + // hardware, this can affect the max speed possibility of movments + // Disabled by default for performance optimization, uncomment to enable. + //#define ENABLE_RAMPS_HW_LIMITS // Define spindle enable and spindle direction output pins. #define SPINDLE_ENABLE_DDR DDRG diff --git a/grbl/grbl.h b/grbl/grbl.h index 8b6754859..58c2a976f 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1m" -#define GRBL_VERSION_BUILD "20191212" +#define GRBL_VERSION_BUILD "20191215" // Define standard libraries used by Grbl. #include From e9a2a6d76992fee285122f2d77479063dba605d4 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 20 Apr 2020 19:38:32 +0200 Subject: [PATCH 040/101] Issue #116 corrected --- grbl/gcode.c | 2 +- grbl/grbl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/gcode.c b/grbl/gcode.c index 61f146ec1..c9fe96ede 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -1190,7 +1190,7 @@ uint8_t gc_execute_line(char *line) if (gc_parser_flags & GC_PARSER_JOG_MOTION) { // Only distance and unit modal commands and G53 absolute override command are allowed. // NOTE: Feed rate word and axis word checks have already been performed in STEP 3. - if (command_dwords & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6 | bit(MODAL_GROUP_G0))) ) { FAIL(STATUS_INVALID_JOG_COMMAND) }; + if (command_dwords & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6) | bit(MODAL_GROUP_G0))) { FAIL(STATUS_INVALID_JOG_COMMAND) }; if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE || gc_block.non_modal_command == NON_MODAL_NO_ACTION)) { FAIL(STATUS_INVALID_JOG_COMMAND); } // Initialize planner data to current spindle and coolant modal state. diff --git a/grbl/grbl.h b/grbl/grbl.h index 58c2a976f..da4c1812a 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1m" -#define GRBL_VERSION_BUILD "20191215" +#define GRBL_VERSION_BUILD "20200420" // Define standard libraries used by Grbl. #include From 4feee99947a62a51d99323a3c6ccf063a38f8f1e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 24 Apr 2020 12:32:36 +0200 Subject: [PATCH 041/101] limits_get_state() error with Normaly Open limit switch (using $5=1 ro INVERT_*_MASK) --- grbl/config.h | 6 ++++-- grbl/grbl.h | 4 ++-- grbl/limits.c | 21 +++++++++++++-------- grbl/report.c | 9 ++++++--- grbl/report.h | 2 +- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 49049b24d..a8ab317d0 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -286,9 +286,11 @@ // NOTE: PLEASE DO NOT USE THIS, unless you have a situation that needs it. // #define INVERT_LIMIT_PIN_MASK ((1< diff --git a/grbl/limits.c b/grbl/limits.c index 0c290f1a0..fa85c5bc9 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -190,12 +190,13 @@ void limits_disable() // number in bit position, i.e. AXIS_3 is (1<<2) or bit 2, and AXIS_2 is (1<<1) or bit 1. uint8_t limits_get_state() { - uint8_t limit_state = 0; #ifdef DEFAULTS_RAMPS_BOARD + uint8_t limit_state_max = 0; + uint8_t limit_state_min = 0; uint8_t pin; uint8_t idx; #ifdef INVERT_LIMIT_PIN_MASK - #error "INVERT_LIMIT_PIN_MASK is not implemented" + #error "INVERT_LIMIT_PIN_MASK is not implemented, use INVERT__LIMIT_PIN_MASK" #endif for (idx=0; idx 0) { sys.report_wco_counter--; } @@ -759,12 +763,11 @@ void report_realtime_status() #ifdef DEBUG - void report_realtime_debug(float val) + void report_realtime_debug() { printPgmString(PSTR("{report_realtime_debug(")); - print_uint8_base10(val); + // print realtime debug values here printPgmString(PSTR(")}")); report_util_line_feed(); - } #endif diff --git a/grbl/report.h b/grbl/report.h index e44520259..f1480026a 100644 --- a/grbl/report.h +++ b/grbl/report.h @@ -125,7 +125,7 @@ void report_execute_startup_message(char *line, uint8_t status_code); void report_build_info(char *line); #ifdef DEBUG - void report_realtime_debug(float val); + void report_realtime_debug(); #endif #endif From a642d62613211956f091934d010445a7d44ff5d9 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 13 Aug 2018 15:01:34 -0600 Subject: [PATCH 042/101] Upstream updates. Spindle/coolant rare bug fixes. [new] Altered the way default settings are stored and restored. Saved about 300 bytes(!) of flashed size. Should free up enough for certain configurations of CoreXY machines. [fix] When the optional M7 mist coolant IO was enabled, coolant overrides was not disabling correctly. [fix] Coolant override states was not restored correctly after a parking motion in certain situations. It would restore programmed state, rather than current overridden state. [fix] Now allow coolant overrides to operate during jogging motion. [fix] Invert control pin mask typo. [new] Added a new build info feedback mechanism for enabling the safety door input pin. --- doc/csv/build_option_codes_en_US.csv | 1 + doc/markdown/interface.md | 1 + grbl/config.h | 2 +- grbl/coolant_control.c | 49 ++++++------ grbl/coolant_control.h | 4 +- grbl/defaults.h | 12 ++- grbl/gcode.c | 11 ++- grbl/planner.h | 1 + grbl/protocol.c | 11 ++- grbl/report.c | 18 ++--- grbl/settings.c | 113 +++++++++++++-------------- grbl/settings.h | 25 ++++-- 12 files changed, 134 insertions(+), 114 deletions(-) diff --git a/doc/csv/build_option_codes_en_US.csv b/doc/csv/build_option_codes_en_US.csv index f7abb8fe5..af8d2a6c4 100644 --- a/doc/csv/build_option_codes_en_US.csv +++ b/doc/csv/build_option_codes_en_US.csv @@ -12,6 +12,7 @@ "0","Spindle enable off when speed is zero","Enabled" "S","Software limit pin debouncing","Enabled" "R","Parking override control","Enabled" +"+","Safety door input pin","Enabled" "*","Restore all EEPROM command","Disabled" "$","Restore EEPROM `$` settings command","Disabled" "#","Restore EEPROM parameter data command","Disabled" diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md index 215c90027..24400f587 100644 --- a/doc/markdown/interface.md +++ b/doc/markdown/interface.md @@ -448,6 +448,7 @@ Feedback messages provide non-critical information on what Grbl is doing, what i | **`S`** | Software limit pin debouncing enabled | | **`R`** | Parking override control enabled | | **`A`** | Allow feed rate overrides in probe cycles | +| **`+`** | Safety door input pin enabled | | **`*`** | Restore all EEPROM disabled | | **`$`** | Restore EEPROM `$` settings disabled | | **`#`** | Restore EEPROM parameter data disabled | diff --git a/grbl/config.h b/grbl/config.h index a8ab317d0..7f55a99b3 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -276,7 +276,7 @@ // NOTE: The top option will mask and invert all control pins. The bottom option is an example of // inverting only two control pins, the safety door and reset. See cpu_map.h for other bit definitions. // #define INVERT_CONTROL_PIN_MASK CONTROL_MASK // Default disabled. Uncomment to disable. -// #define INVERT_CONTROL_PIN_MASK ((1<condition |= gc_state.modal.coolant; // Set condition flag for planner use. diff --git a/grbl/planner.h b/grbl/planner.h index 3fede15f3..d9d8e227c 100644 --- a/grbl/planner.h +++ b/grbl/planner.h @@ -42,6 +42,7 @@ #define PL_COND_FLAG_COOLANT_FLOOD bit(6) #define PL_COND_FLAG_COOLANT_MIST bit(7) #define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION|PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE) +#define PL_COND_SPINDLE_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW) #define PL_COND_ACCESSORY_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW|PL_COND_FLAG_COOLANT_FLOOD|PL_COND_FLAG_COOLANT_MIST) diff --git a/grbl/protocol.c b/grbl/protocol.c index 01eef2d06..4422756df 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -450,8 +450,10 @@ void protocol_exec_rt_system() last_s_override = max(last_s_override,MIN_SPINDLE_SPEED_OVERRIDE); if (last_s_override != sys.spindle_speed_ovr) { - bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); sys.spindle_speed_ovr = last_s_override; + // NOTE: Spindle speed overrides during HOLD state are taken care of by suspend function. + if (sys.state == STATE_IDLE) { spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); } + else { bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); } sys.report_ovr_counter = 0; // Set to report change immediately } @@ -466,8 +468,9 @@ void protocol_exec_rt_system() // NOTE: Since coolant state always performs a planner sync whenever it changes, the current // run state can be determined by checking the parser state. + // NOTE: Coolant overrides only operate during IDLE, CYCLE, HOLD, and JOG states. Ignored otherwise. if (rt_exec & (EXEC_COOLANT_FLOOD_OVR_TOGGLE | EXEC_COOLANT_MIST_OVR_TOGGLE)) { - if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOLD))) { + if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_JOG))) { uint8_t coolant_state = gc_state.modal.coolant; if (rt_exec & EXEC_COOLANT_MIST_OVR_TOGGLE) { if (coolant_state & COOLANT_MIST_ENABLE) { bit_false(coolant_state,COOLANT_MIST_ENABLE); } @@ -524,7 +527,7 @@ static void protocol_exec_rt_suspend() restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant); restore_spindle_speed = gc_state.spindle_speed; } else { - restore_condition = block->condition; + restore_condition = (block->condition & PL_COND_SPINDLE_MASK) | coolant_get_state(); restore_spindle_speed = block->spindle_speed; } #ifdef DISABLE_LASER_DURING_HOLD @@ -673,7 +676,7 @@ static void protocol_exec_rt_suspend() // Block if safety door re-opened during prior restore actions. if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) { // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin. - coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_FLOOD))); + coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST))); delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND); } } diff --git a/grbl/report.c b/grbl/report.c index f569b481d..7a3b0f46a 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -507,9 +507,10 @@ void report_build_info(char *line) #endif report_util_feedback_line_feed(); printPgmString(PSTR("[OPT:")); // Generate compile-time build option list - serial_write('V'); - serial_write('N'); - serial_write('M'); + serial_write('V'); // Variable spindle standard. + serial_write('N'); // Line number reporting standard. + serial_write('M'); // M7 mist coolant standard. + serial_write('+'); // Safety door support standard. #ifdef COREXY serial_write('C'); #endif @@ -540,6 +541,9 @@ void report_build_info(char *line) #ifdef ENABLE_PARKING_OVERRIDE_CONTROL serial_write('R'); #endif + #ifndef HOMING_INIT_LOCK + serial_write('L'); + #endif #ifndef ENABLE_RESTORE_EEPROM_WIPE_ALL // NOTE: Shown when disabled. serial_write('*'); #endif @@ -558,10 +562,6 @@ void report_build_info(char *line) #ifndef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // NOTE: Shown when disabled. serial_write('W'); #endif - #ifndef HOMING_INIT_LOCK - serial_write('L'); - #endif - // NOTE: Compiled values, like override increments/max/min values, may be added at some point later. serial_write(','); print_uint8_base10(BLOCK_BUFFER_SIZE-1); @@ -704,9 +704,7 @@ void report_realtime_status() #endif } if (ctrl_pin_state) { - #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN - if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_SAFETY_DOOR)) { serial_write('D'); } - #endif + if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_SAFETY_DOOR)) { serial_write('D'); } if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_RESET)) { serial_write('R'); } if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_FEED_HOLD)) { serial_write('H'); } if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_CYCLE_START)) { serial_write('S'); } diff --git a/grbl/settings.c b/grbl/settings.c index 8247bbf5b..71dfbc062 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -24,6 +24,60 @@ settings_t settings; +const __flash settings_t defaults = { + .pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS, + .stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME, + .step_invert_mask = DEFAULT_STEPPING_INVERT_MASK, + .dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK, + .status_report_mask = DEFAULT_STATUS_REPORT_MASK, + .junction_deviation = DEFAULT_JUNCTION_DEVIATION, + .arc_tolerance = DEFAULT_ARC_TOLERANCE, + .rpm_max = DEFAULT_SPINDLE_RPM_MAX, + .rpm_min = DEFAULT_SPINDLE_RPM_MIN, + .homing_dir_mask = DEFAULT_HOMING_DIR_MASK, + .homing_feed_rate = DEFAULT_HOMING_FEED_RATE, + .homing_seek_rate = DEFAULT_HOMING_SEEK_RATE, + .homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY, + .homing_pulloff = DEFAULT_HOMING_PULLOFF, + .flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | + (DEFAULT_LASER_MODE << BIT_LASER_MODE) | + (DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | + (DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) | + (DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | + (DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) | + (DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | + (DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN), + .steps_per_mm[AXIS_1] = DEFAULT_AXIS1_STEPS_PER_UNIT, + .steps_per_mm[AXIS_2] = DEFAULT_AXIS2_STEPS_PER_UNIT, + .steps_per_mm[AXIS_3] = DEFAULT_AXIS3_STEPS_PER_UNIT, + .max_rate[AXIS_1] = DEFAULT_AXIS1_MAX_RATE, + .max_rate[AXIS_2] = DEFAULT_AXIS2_MAX_RATE, + .max_rate[AXIS_3] = DEFAULT_AXIS3_MAX_RATE, + .acceleration[AXIS_1] = DEFAULT_AXIS1_ACCELERATION, + .acceleration[AXIS_2] = DEFAULT_AXIS2_ACCELERATION, + .acceleration[AXIS_3] = DEFAULT_AXIS3_ACCELERATION, + .max_travel[AXIS_1] = (-DEFAULT_AXIS1_MAX_TRAVEL), + .max_travel[AXIS_2] = (-DEFAULT_AXIS2_MAX_TRAVEL), + .max_travel[AXIS_3] = (-DEFAULT_AXIS3_MAX_TRAVEL), +#if N_AXIS > 3 + .steps_per_mm[AXIS_4] = DEFAULT_AXIS4_STEPS_PER_UNIT, + .max_rate[AXIS_4] = DEFAULT_AXIS4_MAX_RATE, + .acceleration[AXIS_4] = DEFAULT_AXIS4_ACCELERATION, + .max_travel[AXIS_4] = (-DEFAULT_AXIS4_MAX_TRAVEL), +#endif +#if N_AXIS > 4 + .steps_per_mm[AXIS_5] = DEFAULT_AXIS5_STEPS_PER_UNIT, + .max_rate[AXIS_5] = DEFAULT_AXIS5_MAX_RATE, + .acceleration[AXIS_5] = DEFAULT_AXIS5_ACCELERATION, + .max_travel[AXIS_5] = (-DEFAULT_AXIS5_MAX_TRAVEL), +#endif +#if N_AXIS > 5 + .steps_per_mm[AXIS_6] = DEFAULT_AXIS6_STEPS_PER_UNIT, + .max_rate[AXIS_6] = DEFAULT_AXIS6_MAX_RATE, + .acceleration[AXIS_6] = DEFAULT_AXIS6_ACCELERATION, + .max_travel[AXIS_6] = (-DEFAULT_AXIS6_MAX_TRAVEL), +#endif +}; // Method to store startup lines into EEPROM void settings_store_startup_line(uint8_t n, char *line) @@ -68,64 +122,7 @@ void write_global_settings() // Method to restore EEPROM-saved Grbl global settings back to defaults. void settings_restore(uint8_t restore_flag) { if (restore_flag & SETTINGS_RESTORE_DEFAULTS) { - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; - settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; - - settings.rpm_max = DEFAULT_SPINDLE_RPM_MAX; - settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN; - - settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; - settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE; - settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; - - settings.flags = 0; - if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } - if (DEFAULT_LASER_MODE) { settings.flags |= BITFLAG_LASER_MODE; } - if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } - if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } - if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } - if (DEFAULT_INVERT_PROBE_PIN) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } - - settings.steps_per_mm[AXIS_1] = DEFAULT_AXIS1_STEPS_PER_UNIT; - settings.steps_per_mm[AXIS_2] = DEFAULT_AXIS2_STEPS_PER_UNIT; - settings.steps_per_mm[AXIS_3] = DEFAULT_AXIS3_STEPS_PER_UNIT; - settings.max_rate[AXIS_1] = DEFAULT_AXIS1_MAX_RATE; - settings.max_rate[AXIS_2] = DEFAULT_AXIS2_MAX_RATE; - settings.max_rate[AXIS_3] = DEFAULT_AXIS3_MAX_RATE; - settings.acceleration[AXIS_1] = DEFAULT_AXIS1_ACCELERATION; - settings.acceleration[AXIS_2] = DEFAULT_AXIS2_ACCELERATION; - settings.acceleration[AXIS_3] = DEFAULT_AXIS3_ACCELERATION; - settings.max_travel[AXIS_1] = (-DEFAULT_AXIS1_MAX_TRAVEL); - settings.max_travel[AXIS_2] = (-DEFAULT_AXIS2_MAX_TRAVEL); - settings.max_travel[AXIS_3] = (-DEFAULT_AXIS3_MAX_TRAVEL); - #if N_AXIS > 3 - settings.steps_per_mm[AXIS_4] = DEFAULT_AXIS4_STEPS_PER_UNIT; - settings.max_rate[AXIS_4] = DEFAULT_AXIS4_MAX_RATE; - settings.acceleration[AXIS_4] = DEFAULT_AXIS4_ACCELERATION; - settings.max_travel[AXIS_4] = (-DEFAULT_AXIS4_MAX_TRAVEL); - #endif - #if N_AXIS > 4 - settings.steps_per_mm[AXIS_5] = DEFAULT_AXIS5_STEPS_PER_UNIT; - settings.max_rate[AXIS_5] = DEFAULT_AXIS5_MAX_RATE; - settings.acceleration[AXIS_5] = DEFAULT_AXIS5_ACCELERATION; - settings.max_travel[AXIS_5] = (-DEFAULT_AXIS5_MAX_TRAVEL); - #endif - #if N_AXIS > 5 - settings.steps_per_mm[AXIS_6] = DEFAULT_AXIS6_STEPS_PER_UNIT; - settings.max_rate[AXIS_6] = DEFAULT_AXIS6_MAX_RATE; - settings.acceleration[AXIS_6] = DEFAULT_AXIS6_ACCELERATION; - settings.max_travel[AXIS_6] = (-DEFAULT_AXIS6_MAX_TRAVEL); - #endif - + settings = defaults; write_global_settings(); } diff --git a/grbl/settings.h b/grbl/settings.h index a7fcfd3db..b556634fc 100644 --- a/grbl/settings.h +++ b/grbl/settings.h @@ -34,14 +34,23 @@ #define SETTINGS_VERSION 10 // NOTE: Check settings_reset() when moving to next version. // Define bit flag masks for the boolean settings in settings.flag. -#define BITFLAG_REPORT_INCHES bit(0) -#define BITFLAG_LASER_MODE bit(1) -#define BITFLAG_INVERT_ST_ENABLE bit(2) -#define BITFLAG_HARD_LIMIT_ENABLE bit(3) -#define BITFLAG_HOMING_ENABLE bit(4) -#define BITFLAG_SOFT_LIMIT_ENABLE bit(5) -#define BITFLAG_INVERT_LIMIT_PINS bit(6) -#define BITFLAG_INVERT_PROBE_PIN bit(7) +#define BIT_REPORT_INCHES 0 +#define BIT_LASER_MODE 1 +#define BIT_INVERT_ST_ENABLE 2 +#define BIT_HARD_LIMIT_ENABLE 3 +#define BIT_HOMING_ENABLE 4 +#define BIT_SOFT_LIMIT_ENABLE 5 +#define BIT_INVERT_LIMIT_PINS 6 +#define BIT_INVERT_PROBE_PIN 7 + +#define BITFLAG_REPORT_INCHES bit(BIT_REPORT_INCHES) +#define BITFLAG_LASER_MODE bit(BIT_LASER_MODE) +#define BITFLAG_INVERT_ST_ENABLE bit(BIT_INVERT_ST_ENABLE) +#define BITFLAG_HARD_LIMIT_ENABLE bit(BIT_HARD_LIMIT_ENABLE) +#define BITFLAG_HOMING_ENABLE bit(BIT_HOMING_ENABLE) +#define BITFLAG_SOFT_LIMIT_ENABLE bit(BIT_SOFT_LIMIT_ENABLE) +#define BITFLAG_INVERT_LIMIT_PINS bit(BIT_INVERT_LIMIT_PINS) +#define BITFLAG_INVERT_PROBE_PIN bit(BIT_INVERT_PROBE_PIN) // Define status reporting boolean enable bit flags in settings.status_report_mask #define BITFLAG_RT_STATUS_POSITION_TYPE bit(0) From cd5221c2577371f2add61516e43b01c4152b1bf5 Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 10 Aug 2018 09:39:10 -0400 Subject: [PATCH 043/101] fixes gremlin where changing stepper direction mask didn't work correctly because step_port_invert_mask[] (new with the RAMPS modifications) wasn't reset to 0 when it should have been. this made a mess. --- grbl/stepper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grbl/stepper.c b/grbl/stepper.c index feb26fda3..5a1dbdb4b 100644 --- a/grbl/stepper.c +++ b/grbl/stepper.c @@ -764,7 +764,10 @@ void st_generate_step_dir_invert_masks() #ifdef DEFAULTS_RAMPS_BOARD for (idx=0; idx Date: Mon, 13 Aug 2018 15:03:14 -0600 Subject: [PATCH 044/101] Updated version. --- doc/log/commit_log_v1.0d.txt | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/doc/log/commit_log_v1.0d.txt b/doc/log/commit_log_v1.0d.txt index 7c9462c60..422cf125d 100644 --- a/doc/log/commit_log_v1.0d.txt +++ b/doc/log/commit_log_v1.0d.txt @@ -1,3 +1,50 @@ +---------------- +Date: 2018-08-13 +Author: Sonny Jeon +Subject: Merge branch 'edge' of https://github.com/gnea/grbl-Mega into edge + + +---------------- +Date: 2018-08-13 +Author: Sonny Jeon +Subject: Upstream updates. Spindle/coolant rare bug fixes. + +[new] Altered the way default settings are stored and restored. Saved about 300 bytes(!) of flashed size. Should free up enough for certain configurations of CoreXY machines. + +[fix] When the optional M7 mist coolant IO was enabled, coolant overrides was not disabling correctly. + +[fix] Coolant override states was not restored correctly after a parking motion in certain situations. It would restore programmed state, rather than current overridden state. + +[fix] Now allow coolant overrides to operate during jogging motion. + +[fix] Invert control pin mask typo. + +[new] Added a new build info feedback mechanism for enabling the safety door input pin. + + +---------------- +Date: 2018-08-10 +Author: Sonny Jeon +Subject: Merge pull request #68 from bgort/gremlinfix + +Fixes gremlin where changing step and direction inversion masks made a mess + +---------------- +Date: 2018-08-10 +Author: Brian +Subject: fixes gremlin where changing stepper direction mask didn't work correctly +because step_port_invert_mask[] (new with the RAMPS modifications) +wasn't reset to 0 when it should have been. this made a mess. + + +---------------- +Date: 2017-08-02 +Author: Sonny Jeon +Subject: Fixed a very rare but critical bug when reducing override rates. + +- See main branch commit log for details. + + ---------------- Date: 2017-08-01 Author: Sonny Jeon From 536ce711580fe8cc1e7291b39c257fd5e0640733 Mon Sep 17 00:00:00 2001 From: Julien Lirochon Date: Fri, 6 Nov 2020 10:56:08 +0100 Subject: [PATCH 045/101] Back to more conservative defaults --- grbl/defaults.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grbl/defaults.h b/grbl/defaults.h index 014b53cce..21b1ec7fd 100644 --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -516,16 +516,16 @@ #define DEFAULT_REPORT_INCHES 0 // false #define DEFAULT_INVERT_ST_ENABLE 0 // false #define DEFAULT_INVERT_LIMIT_PINS 0 // false - #define DEFAULT_SOFT_LIMIT_ENABLE 0 // true - #define DEFAULT_HARD_LIMIT_ENABLE 0 // false + #define DEFAULT_SOFT_LIMIT_ENABLE 1 // true + #define DEFAULT_HARD_LIMIT_ENABLE 0 // false #define DEFAULT_INVERT_PROBE_PIN 0 // false #define DEFAULT_LASER_MODE 0 // false - #define DEFAULT_HOMING_ENABLE 0 // true + #define DEFAULT_HOMING_ENABLE 1 // true #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir #define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) - #define DEFAULT_HOMING_PULLOFF 1.0 // mm + #define DEFAULT_HOMING_PULLOFF 5.0 // mm #endif #endif From 9d9a1e54ea20e24f0b8fa4858fd19b0096bffa3e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 18 Nov 2020 12:14:24 +0100 Subject: [PATCH 046/101] Spindle output choice, 0-12v on D8 or 0-5v on D6 --- grbl/config.h | 7 +++++ grbl/cpu_map.h | 78 +++++++++++++++++++++++++++++++++++--------------- grbl/grbl.h | 4 +-- 3 files changed, 64 insertions(+), 25 deletions(-) mode change 100644 => 100755 grbl/config.h mode change 100644 => 100755 grbl/cpu_map.h mode change 100644 => 100755 grbl/grbl.h diff --git a/grbl/config.h b/grbl/config.h old mode 100644 new mode 100755 index a8ab317d0..e9a1294e9 --- a/grbl/config.h +++ b/grbl/config.h @@ -82,6 +82,13 @@ #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif +// Chose the spindle pin output : +// SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 +// SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) +// Uncomment the line which correspond to your hardware +#define SPINDLE_PWM_ON_D8 +// #define SPINDLE_PWM_ON_D6 + // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported // by Grbl to the correct axis name. diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h old mode 100644 new mode 100755 index 5cb3ffa20..c1973e5a8 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -358,30 +358,62 @@ #define PROBE_MASK (1< From 6c27ac6449090d0dfef56a4519e9ba082dd0a250 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 27 Nov 2020 15:53:47 +0100 Subject: [PATCH 047/101] Deletion of unnecessary lines in limits.c --- grbl/limits.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/grbl/limits.c b/grbl/limits.c index fa85c5bc9..65c05304e 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -200,7 +200,6 @@ uint8_t limits_get_state() #endif for (idx=0; idx Date: Thu, 3 Dec 2020 10:09:30 +0100 Subject: [PATCH 048/101] Safety door support option is reported as 'G' (Gate) rather than '+' for homogeneity with other reports --- doc/csv/build_option_codes_en_US.csv | 4 ++-- doc/markdown/interface.md | 2 +- grbl/grbl.h | 4 ++-- grbl/report.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/csv/build_option_codes_en_US.csv b/doc/csv/build_option_codes_en_US.csv index af8d2a6c4..d4f868223 100644 --- a/doc/csv/build_option_codes_en_US.csv +++ b/doc/csv/build_option_codes_en_US.csv @@ -12,11 +12,11 @@ "0","Spindle enable off when speed is zero","Enabled" "S","Software limit pin debouncing","Enabled" "R","Parking override control","Enabled" -"+","Safety door input pin","Enabled" +"G","Safety door input pin","Enabled" "*","Restore all EEPROM command","Disabled" "$","Restore EEPROM `$` settings command","Disabled" "#","Restore EEPROM parameter data command","Disabled" "I","Build info write user string command","Disabled" "E","Force sync upon EEPROM write","Disabled" "W","Force sync upon work coordinate offset change","Disabled" -"L","Homing initialization auto-lock","Disabled" \ No newline at end of file +"L","Homing initialization auto-lock","Disabled" diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md index 24400f587..aaa741d9b 100644 --- a/doc/markdown/interface.md +++ b/doc/markdown/interface.md @@ -448,7 +448,7 @@ Feedback messages provide non-critical information on what Grbl is doing, what i | **`S`** | Software limit pin debouncing enabled | | **`R`** | Parking override control enabled | | **`A`** | Allow feed rate overrides in probe cycles | -| **`+`** | Safety door input pin enabled | +| **`G`** | Safety door input pin enabled | | **`*`** | Restore all EEPROM disabled | | **`$`** | Restore EEPROM `$` settings disabled | | **`#`** | Restore EEPROM parameter data disabled | diff --git a/grbl/grbl.h b/grbl/grbl.h index 3739154d0..7d6bfead9 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1m" -#define GRBL_VERSION_BUILD "20201118" +#define GRBL_VERSION "1.1p" +#define GRBL_VERSION_BUILD "20201203" // Define standard libraries used by Grbl. #include diff --git a/grbl/report.c b/grbl/report.c index 7a3b0f46a..e1a4dbfb6 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -510,7 +510,7 @@ void report_build_info(char *line) serial_write('V'); // Variable spindle standard. serial_write('N'); // Line number reporting standard. serial_write('M'); // M7 mist coolant standard. - serial_write('+'); // Safety door support standard. + serial_write('G'); // Safety door support standard. #ifdef COREXY serial_write('C'); #endif From 7b72bd56a9a9d0948333791571c326d05bb8ce6b Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 4 Dec 2020 18:07:54 +0100 Subject: [PATCH 049/101] Ability to add CFLAGS on the make command line --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a95f53c6c..e4d58c0f0 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F # COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections # Compile flags for avr-gcc v4.9.2 compatible with the IDE. Or if you don't care about the warnings. -COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections -flto +COMPILE = avr-gcc $(CFLAGS) -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections -flto OBJECTS = $(addprefix $(BUILDDIR)/,$(notdir $(SOURCE:.c=.o))) From d970716c37a025c39acfeab1571caa724c459bfd Mon Sep 17 00:00:00 2001 From: fra589 Date: Mon, 4 Jan 2021 00:00:04 +0100 Subject: [PATCH 050/101] Allow status reports in critical Alarm state as mentioned in comment --- grbl/config.h | 5 +++-- grbl/cpu_map.h | 0 grbl/grbl.h | 4 ++-- grbl/protocol.c | 4 ++++ 4 files changed, 9 insertions(+), 4 deletions(-) mode change 100755 => 100644 grbl/config.h mode change 100755 => 100644 grbl/cpu_map.h mode change 100755 => 100644 grbl/grbl.h diff --git a/grbl/config.h b/grbl/config.h old mode 100755 new mode 100644 index c9d2d7b50..e1d5c8f8c --- a/grbl/config.h +++ b/grbl/config.h @@ -183,8 +183,9 @@ #define HOMING_CYCLE_3 (1< diff --git a/grbl/protocol.c b/grbl/protocol.c index 4422756df..9a8349596 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -237,6 +237,10 @@ void protocol_exec_rt_system() // the user and a GUI time to do what is needed before resetting, like killing the // incoming stream. The same could be said about soft limits. While the position is not // lost, continued streaming could cause a serious crash if by chance it gets executed. + if (sys_rt_exec_state & EXEC_STATUS_REPORT) { + report_realtime_status(); + system_clear_exec_state_flag(EXEC_STATUS_REPORT); + } } while (bit_isfalse(sys_rt_exec_state,EXEC_RESET)); } system_clear_exec_alarm(); // Clear alarm From f6d19326c8fe435c7a26ed8771dac6b8995060ef Mon Sep 17 00:00:00 2001 From: Rene Hogendoorn Date: Wed, 13 Jan 2021 17:30:15 +0100 Subject: [PATCH 051/101] Automatically select the proper configuration for a cloned axis --- grbl/defaults.h | 92 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/grbl/defaults.h b/grbl/defaults.h index 21b1ec7fd..c7a01c570 100644 --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -487,22 +487,90 @@ #define DEFAULT_AXIS2_MAX_TRAVEL 200.0 // mm #define DEFAULT_AXIS3_MAX_TRAVEL 200.0 // mm #if N_AXIS > 3 - #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS4_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° + #if AXIS_4_NAME != AXIS_1_NAME && AXIS_4_NAME != AXIS_2_NAME && AXIS_4_NAME != AXIS_3_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS4_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° + #elif AXIS_4_NAME == AXIS_1_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_4_NAME == AXIS_2_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #else + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #endif #endif #if N_AXIS > 4 - #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS5_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° + #if AXIS_5_NAME != AXIS_1_NAME && AXIS_5_NAME != AXIS_2_NAME && AXIS_5_NAME != AXIS_3_NAME && \ + AXIS_5_NAME != AXIS_4_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS5_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° + #elif AXIS_5_NAME == AXIS_1_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_5_NAME == AXIS_2_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #elif AXIS_5_NAME == AXIS_3_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #else + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS4_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS4_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL + #endif #endif #if N_AXIS > 5 - #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS6_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° + #if AXIS_6_NAME != AXIS_1_NAME && AXIS_6_NAME != AXIS_2_NAME && \ + AXIS_6_NAME != AXIS_3_NAME && AXIS_6_NAME != AXIS_4_NAME && AXIS_6_NAME != AXIS_5_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS6_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° + #elif AXIS_6_NAME == AXIS_1_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_2_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_3_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_4_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS4_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS4_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL + #else + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS5_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS5_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS5_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS5_MAX_TRAVEL + #endif #endif #define DEFAULT_SPINDLE_RPM_MAX 12000 // rpm #define DEFAULT_SPINDLE_RPM_MIN 550.0 // rpm From 8fd5f7c1731ea07bbf2640f230c550feb966fdd7 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 20 Mar 2021 16:47:04 +0100 Subject: [PATCH 052/101] Hardware limit bug #185 corrected --- grbl/grbl.h | 4 ++-- grbl/limits.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index 40bd222c1..d9af9fa05 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1q" -#define GRBL_VERSION_BUILD "20210103" +#define GRBL_VERSION "1.1r" +#define GRBL_VERSION_BUILD "20210320" // Define standard libraries used by Grbl. #include diff --git a/grbl/limits.c b/grbl/limits.c index 65c05304e..65e263b59 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -195,6 +195,7 @@ uint8_t limits_get_state() uint8_t limit_state_min = 0; uint8_t pin; uint8_t idx; + uint8_t unused_bits = 0xff - (1<_LIMIT_PIN_MASK" #endif @@ -217,7 +218,7 @@ uint8_t limits_get_state() } } if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { - return(~(limit_state_max & limit_state_min)); + return(~((limit_state_max & limit_state_min) | unused_bits)); } else { return(limit_state_max | limit_state_min); } From b93a5eac9606bf30f6c3d43a41f8aff21e07ff7a Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 29 Apr 2021 18:38:01 +0200 Subject: [PATCH 053/101] Old code incompatible with more than 3 axis suppressed --- grbl/config.h | 87 +++--- grbl/cpu_map.h | 110 +------- grbl/defaults.h | 703 ++++++++++-------------------------------------- grbl/limits.c | 700 ++++++++++++++++------------------------------- grbl/planner.c | 6 +- grbl/planner.h | 6 +- grbl/settings.c | 129 ++++----- grbl/stepper.c | 635 +++++++++++++++++-------------------------- grbl/system.h | 6 +- 9 files changed, 719 insertions(+), 1663 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index e1d5c8f8c..0d9dbe151 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -36,11 +36,7 @@ // NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only // one configuration file by placing their specific defaults and pin map at the bottom of this file. // If doing so, simply comment out these two defines and see instructions below. -//#define DEFAULTS_GENERIC -//#define CPU_MAP_2560_INITIAL - -// To use with RAMPS 1.4 Board, comment out the above defines and uncomment the next two defines -#define DEFAULTS_RAMPS_BOARD +#define DEFAULTS_GENERIC #define CPU_MAP_2560_RAMPS_BOARD // Serial baud rate @@ -48,13 +44,8 @@ #define BAUD_RATE 115200 // Axis array index values. Must start with 0 and be continuous. -#ifdef DEFAULTS_RAMPS_BOARD - // 4, 5 & 6 axis support only for RAMPS 1.4 (for the moment :-)...) - #define N_AXIS 5 // Number of axes - #define N_AXIS_LINEAR 3 // Number of linears axis -#else - #define N_AXIS 3 // Number of axes = 3 if not DEFAULTS_RAMPS_BOARD -#endif +#define N_AXIS 5 // Number of axes (3 to 6) +#define N_AXIS_LINEAR 3 // Number of linears axis #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. #define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V & W. @@ -175,36 +166,33 @@ // on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits // will not be affected by pin sharing. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. -#ifdef DEFAULTS_RAMPS_BOARD - #if N_AXIS == 4 // 4 axis : homing - #define HOMING_CYCLE_0 (1< 3 - #if AXIS_4_NAME != AXIS_1_NAME && AXIS_4_NAME != AXIS_2_NAME && AXIS_4_NAME != AXIS_3_NAME - #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS4_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° - #elif AXIS_4_NAME == AXIS_1_NAME - #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT - #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS1_MAX_RATE - #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS1_ACCELERATION - #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL - #elif AXIS_4_NAME == AXIS_2_NAME - #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT - #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS2_MAX_RATE - #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS2_ACCELERATION - #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL - #else - #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT - #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS3_MAX_RATE - #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS3_ACCELERATION - #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #ifdef DEFAULTS_GENERIC + // Generic conservative settings for a RAMP CNC machine. You must update these yourself. + // Keep in mind that Grbl is highly efficient and settings can be significantly different. + // Especially when optimizing for a different CNC task like going from 3d printing to + // CNC milling or laser cutting. Unlike Marlin, these defaults are only applied when the + // EEPROM is explicitly wiped, either by a `$RST=*` command or Grbl detecting a settings + // version type change (not frequent). + #define MICROSTEPS_AXIS1 4 // Microstepping = 1/4 pas + #define STEP_REVS_AXIS1 200 // Moteurs à 200 pas par tour + #define UNIT_PER_REV_AXIS1 2.0 // 2mm + #define DEFAULT_AXIS1_STEPS_PER_UNIT (MICROSTEPS_AXIS1*STEP_REVS_AXIS1/UNIT_PER_REV_AXIS1) // 400 + #define MICROSTEPS_AXIS2 4 // Microstepping = 1/4 pas + #define STEP_REVS_AXIS2 200 // Moteurs à 200 pas par tour + #define UNIT_PER_REV_AXIS2 2.0 // 2mm + #define DEFAULT_AXIS2_STEPS_PER_UNIT (MICROSTEPS_AXIS2*STEP_REVS_AXIS2/UNIT_PER_REV_AXIS2) + #define MICROSTEPS_AXIS3 4 // Microstepping = 1/4 pas + #define STEP_REVS_AXIS3 200 // Moteurs à 200 pas par tour + #define UNIT_PER_REV_AXIS3 2.0 // 2mm + #define DEFAULT_AXIS3_STEPS_PER_UNIT (MICROSTEPS_AXIS3*STEP_REVS_AXIS3/UNIT_PER_REV_AXIS3) + #define STEP_MAX_FREQUENCY 5000 + #define SECONDS_PER_MINUTE 60 + //#define DEFAULT_AXIS1_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec + #define DEFAULT_AXIS1_MAX_RATE (STEP_MAX_FREQUENCY/DEFAULT_AXIS1_STEPS_PER_UNIT*SECONDS_PER_MINUTE) // 3000 + //#define DEFAULT_AXIS2_MAX_RATE 9000.0 // 9000 mm/min = 9000/60 = 150 mm/sec + #define DEFAULT_AXIS2_MAX_RATE (STEP_MAX_FREQUENCY/DEFAULT_AXIS2_STEPS_PER_UNIT*SECONDS_PER_MINUTE) + //#define DEFAULT_AXIS3_MAX_RATE 300.0 // 300 mm/min = 300/60 = 5 mm/sec + #define DEFAULT_AXIS3_MAX_RATE (STEP_MAX_FREQUENCY/DEFAULT_AXIS3_STEPS_PER_UNIT*SECONDS_PER_MINUTE) + #define DEFAULT_AXIS1_ACCELERATION (50.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_AXIS2_ACCELERATION (50.0*60*60) // 300*60*60 mm/min^2 = 300 mm/sec^2 + #define DEFAULT_AXIS3_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS1_MAX_TRAVEL 400.0 // mm + #define DEFAULT_AXIS2_MAX_TRAVEL 200.0 // mm + #define DEFAULT_AXIS3_MAX_TRAVEL 200.0 // mm + #if N_AXIS > 3 + #if AXIS_4_NAME != AXIS_1_NAME && AXIS_4_NAME != AXIS_2_NAME && AXIS_4_NAME != AXIS_3_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS4_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS4_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS4_MAX_TRAVEL 360.0 // ° + #elif AXIS_4_NAME == AXIS_1_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_4_NAME == AXIS_2_NAME + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #else + #define DEFAULT_AXIS4_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS4_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS4_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS4_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #endif #endif - #endif - #if N_AXIS > 4 - #if AXIS_5_NAME != AXIS_1_NAME && AXIS_5_NAME != AXIS_2_NAME && AXIS_5_NAME != AXIS_3_NAME && \ - AXIS_5_NAME != AXIS_4_NAME - #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS5_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° - #elif AXIS_5_NAME == AXIS_1_NAME - #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT - #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS1_MAX_RATE - #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS1_ACCELERATION - #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL - #elif AXIS_5_NAME == AXIS_2_NAME - #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT - #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS2_MAX_RATE - #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS2_ACCELERATION - #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL - #elif AXIS_5_NAME == AXIS_3_NAME - #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT - #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS3_MAX_RATE - #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS3_ACCELERATION - #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL - #else - #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT - #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS4_MAX_RATE - #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS4_ACCELERATION - #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL + #if N_AXIS > 4 + #if AXIS_5_NAME != AXIS_1_NAME && AXIS_5_NAME != AXIS_2_NAME && AXIS_5_NAME != AXIS_3_NAME && \ + AXIS_5_NAME != AXIS_4_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS5_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS5_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS5_MAX_TRAVEL 180.0 // ° + #elif AXIS_5_NAME == AXIS_1_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_5_NAME == AXIS_2_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #elif AXIS_5_NAME == AXIS_3_NAME + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #else + #define DEFAULT_AXIS5_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT + #define DEFAULT_AXIS5_MAX_RATE DEFAULT_AXIS4_MAX_RATE + #define DEFAULT_AXIS5_ACCELERATION DEFAULT_AXIS4_ACCELERATION + #define DEFAULT_AXIS5_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL + #endif #endif - #endif - #if N_AXIS > 5 - #if AXIS_6_NAME != AXIS_1_NAME && AXIS_6_NAME != AXIS_2_NAME && \ - AXIS_6_NAME != AXIS_3_NAME && AXIS_6_NAME != AXIS_4_NAME && AXIS_6_NAME != AXIS_5_NAME - #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° - #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn - #define DEFAULT_AXIS6_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 - #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° - #elif AXIS_6_NAME == AXIS_1_NAME - #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT - #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS1_MAX_RATE - #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS1_ACCELERATION - #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL - #elif AXIS_6_NAME == AXIS_2_NAME - #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT - #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS2_MAX_RATE - #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS2_ACCELERATION - #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL - #elif AXIS_6_NAME == AXIS_3_NAME - #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT - #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS3_MAX_RATE - #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS3_ACCELERATION - #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL - #elif AXIS_6_NAME == AXIS_4_NAME - #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT - #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS4_MAX_RATE - #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS4_ACCELERATION - #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL - #else - #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS5_STEPS_PER_UNIT - #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS5_MAX_RATE - #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS5_ACCELERATION - #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS5_MAX_TRAVEL + #if N_AXIS > 5 + #if AXIS_6_NAME != AXIS_1_NAME && AXIS_6_NAME != AXIS_2_NAME && \ + AXIS_6_NAME != AXIS_3_NAME && AXIS_6_NAME != AXIS_4_NAME && AXIS_6_NAME != AXIS_5_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT 8.888889 // Direct drive : (200 pas par tours * 1/16 microsteps)/360° + #define DEFAULT_AXIS6_MAX_RATE 1440 // °/mn + #define DEFAULT_AXIS6_ACCELERATION (50.0*60*60) // 100*60*60 mm/min^2 = 100 mm/sec^2 + #define DEFAULT_AXIS6_MAX_TRAVEL 180.0 // ° + #elif AXIS_6_NAME == AXIS_1_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS1_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS1_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS1_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS1_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_2_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS2_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS2_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS2_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS2_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_3_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS3_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS3_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS3_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS3_MAX_TRAVEL + #elif AXIS_6_NAME == AXIS_4_NAME + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS4_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS4_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS4_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS4_MAX_TRAVEL + #else + #define DEFAULT_AXIS6_STEPS_PER_UNIT DEFAULT_AXIS5_STEPS_PER_UNIT + #define DEFAULT_AXIS6_MAX_RATE DEFAULT_AXIS5_MAX_RATE + #define DEFAULT_AXIS6_ACCELERATION DEFAULT_AXIS5_ACCELERATION + #define DEFAULT_AXIS6_MAX_TRAVEL DEFAULT_AXIS5_MAX_TRAVEL + #endif #endif - #endif - #define DEFAULT_SPINDLE_RPM_MAX 12000 // rpm - #define DEFAULT_SPINDLE_RPM_MIN 550.0 // rpm - #define DEFAULT_STEP_PULSE_MICROSECONDS 10 - #define DEFAULT_STEPPING_INVERT_MASK 0 - #define DEFAULT_DIRECTION_INVERT_MASK 0 - #define DEFAULT_STEPPER_IDLE_LOCK_TIME 254 // msec (0-254, 255 keeps steppers enabled) - #define DEFAULT_STATUS_REPORT_MASK 1 // MPos enabled - #define DEFAULT_JUNCTION_DEVIATION 0.02 // mm - #define DEFAULT_ARC_TOLERANCE 0.002 // mm - #define DEFAULT_REPORT_INCHES 0 // false - #define DEFAULT_INVERT_ST_ENABLE 0 // false - #define DEFAULT_INVERT_LIMIT_PINS 0 // false - #define DEFAULT_SOFT_LIMIT_ENABLE 1 // true - #define DEFAULT_HARD_LIMIT_ENABLE 0 // false - #define DEFAULT_INVERT_PROBE_PIN 0 // false - #define DEFAULT_LASER_MODE 0 // false - #define DEFAULT_HOMING_ENABLE 1 // true - #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir - #define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min - #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min - #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) - #define DEFAULT_HOMING_PULLOFF 5.0 // mm -#endif + #define DEFAULT_SPINDLE_RPM_MAX 12000 // rpm + #define DEFAULT_SPINDLE_RPM_MIN 550.0 // rpm + #define DEFAULT_STEP_PULSE_MICROSECONDS 10 + #define DEFAULT_STEPPING_INVERT_MASK 0 + #define DEFAULT_DIRECTION_INVERT_MASK 0 + #define DEFAULT_STEPPER_IDLE_LOCK_TIME 254 // msec (0-254, 255 keeps steppers enabled) + #define DEFAULT_STATUS_REPORT_MASK 1 // MPos enabled + #define DEFAULT_JUNCTION_DEVIATION 0.02 // mm + #define DEFAULT_ARC_TOLERANCE 0.002 // mm + #define DEFAULT_REPORT_INCHES 0 // false + #define DEFAULT_INVERT_ST_ENABLE 0 // false + #define DEFAULT_INVERT_LIMIT_PINS 0 // false + #define DEFAULT_SOFT_LIMIT_ENABLE 1 // true + #define DEFAULT_HARD_LIMIT_ENABLE 0 // false + #define DEFAULT_INVERT_PROBE_PIN 0 // false + #define DEFAULT_LASER_MODE 0 // false + #define DEFAULT_HOMING_ENABLE 1 // true + #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir + #define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min + #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min + #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) + #define DEFAULT_HOMING_PULLOFF 5.0 // mm + #endif #endif diff --git a/grbl/limits.c b/grbl/limits.c index 65e263b59..2e87cd4e4 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -32,264 +32,181 @@ void limits_init() { - #ifdef DEFAULTS_RAMPS_BOARD - // Set as input pins - MIN_LIMIT_DDR(0) &= ~(1< 3 + MIN_LIMIT_DDR(3) &= ~(1< 4 + MIN_LIMIT_DDR(4) &= ~(1< 5 + MIN_LIMIT_DDR(5) &= ~(1< 3 + MAX_LIMIT_DDR(3) &= ~(1< 4 + MAX_LIMIT_DDR(4) &= ~(1< 5 + MAX_LIMIT_DDR(5) &= ~(1< 3 - MIN_LIMIT_DDR(3) &= ~(1< 4 - MIN_LIMIT_DDR(4) &= ~(1< 5 - MIN_LIMIT_DDR(5) &= ~(1< 3 - MAX_LIMIT_DDR(3) &= ~(1< 4 - MAX_LIMIT_DDR(4) &= ~(1< 5 - MAX_LIMIT_DDR(5) &= ~(1< 3 - MIN_LIMIT_PORT(3) &= ~(1< 4 - MIN_LIMIT_PORT(4) &= ~(1< 5 - MIN_LIMIT_PORT(5) &= ~(1< 3 - MAX_LIMIT_PORT(3) &= ~(1< 4 - MAX_LIMIT_PORT(4) &= ~(1< 5 - MAX_LIMIT_PORT(5) &= ~(1< 3 - MIN_LIMIT_PORT(3) |= (1< 4 - MIN_LIMIT_PORT(4) |= (1< 5 - MIN_LIMIT_PORT(5) |= (1< 3 - MAX_LIMIT_PORT(3) |= (1< 4 - MAX_LIMIT_PORT(4) |= (1< 5 - MAX_LIMIT_PORT(5) |= (1< 3 + MIN_LIMIT_PORT(3) |= (1< 4 + MIN_LIMIT_PORT(4) |= (1< 5 + MIN_LIMIT_PORT(5) |= (1< 3 + MAX_LIMIT_PORT(3) |= (1< 4 + MAX_LIMIT_PORT(4) |= (1< 5 + MAX_LIMIT_PORT(5) |= (1<_LIMIT_PIN_MASK" + uint8_t limit_state_max = 0; + uint8_t limit_state_min = 0; + uint8_t pin; + uint8_t idx; + uint8_t unused_bits = 0xff - (1<_LIMIT_PIN_MASK" + #endif + for (idx=0; idxfeed_rate = homing_rate; // Set current homing rate. - plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion. - - sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags. - st_prep_buffer(); // Prep and fill segment buffer from newly planned block. - st_wake_up(); // Initiate motion - do { - if (approach) { - // Check limit state. Lock out cycle axes when they change. - limit_state = limits_get_state(); - for (idx=0; idx 3 - else if (idx==AXIS_4) { axislock[idx] &= ~(step_pin[AXIS_4]); } - #endif - #if N_AXIS > 4 - else if (idx==AXIS_5) { axislock[idx] &= ~(step_pin[AXIS_5]); } - #endif - #if N_AXIS > 5 - else if (idx==AXIS_6) { axislock[idx] &= ~(step_pin[AXIS_6]); } - #endif - else { axislock[idx] &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } - #else - axislock[idx] &= ~(step_pin[idx]); - #endif - } - } - sys.homing_axis_lock[idx] = axislock[idx]; - } - } + system_convert_array_steps_to_mpos(target,sys_position); - st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us. - - // Exit routines: No time to run protocol_execute_realtime() in this loop. - if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) { - uint8_t rt_exec = sys_rt_exec_state; - // Homing failure condition: Reset issued during cycle. - if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); } - // Homing failure condition: Safety door was opened. - if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); } - // Homing failure condition: Limit switch still engaged after pull-off motion - if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); } - // Homing failure condition: Limit switch not found during approach. - if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); } - if (sys_rt_exec_alarm) { - mc_reset(); // Stop motors, if they are running. - protocol_execute_realtime(); - return; + // Initialize and declare variables needed for homing routine. + n_active_axis = 0; + for (idx=0; idx 0); - #else - uint8_t limit_state, axislock, n_active_axis; - do { - system_convert_array_steps_to_mpos(target,sys_position); - - // Initialize and declare variables needed for homing routine. - axislock = 0; - n_active_axis = 0; - for (idx=0; idx 3 - sys_position[idx] = 0; - #else - sys_position[AXIS_3] = 0; - #endif - } - #else - sys_position[idx] = 0; - #endif - // Set target direction based on cycle mask and homing cycle approach state. - // NOTE: This happens to compile smaller than any other implementation tried. - if (bit_istrue(settings.homing_dir_mask,bit(idx))) { - if (approach) { target[idx] = -max_travel; } - else { target[idx] = max_travel; } - } else { - if (approach) { target[idx] = max_travel; } - else { target[idx] = -max_travel; } - } - // Apply axislock to the step port pins active in this cycle. - axislock |= step_pin[idx]; - } + } + homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate. - } - homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate. - sys.homing_axis_lock = axislock; - // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. - pl_data->feed_rate = homing_rate; // Set current homing rate. - plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion. + // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. + pl_data->feed_rate = homing_rate; // Set current homing rate. + plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion. - sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags. - st_prep_buffer(); // Prep and fill segment buffer from newly planned block. - st_wake_up(); // Initiate motion - do { - if (approach) { - // Check limit state. Lock out cycle axes when they change. - limit_state = limits_get_state(); - for (idx=0; idx 3 - else if (idx==AXIS_4) { axislock &= ~(step_pin[AXIS_4]); } - #endif - #if N_AXIS > 4 - else if (idx==AXIS_5) { axislock &= ~(step_pin[AXIS_5]); } - #endif - #if N_AXIS > 5 - else if (idx==AXIS_6) { axislock &= ~(step_pin[AXIS_6]); } - #endif - else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } - #else - axislock &= ~(step_pin[idx]); + sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags. + st_prep_buffer(); // Prep and fill segment buffer from newly planned block. + st_wake_up(); // Initiate motion + do { + if (approach) { + // Check limit state. Lock out cycle axes when they change. + limit_state = limits_get_state(); + for (idx=0; idx 3 + else if (idx==AXIS_4) { axislock[idx] &= ~(step_pin[AXIS_4]); } #endif - } + #if N_AXIS > 4 + else if (idx==AXIS_5) { axislock[idx] &= ~(step_pin[AXIS_5]); } + #endif + #if N_AXIS > 5 + else if (idx==AXIS_6) { axislock[idx] &= ~(step_pin[AXIS_6]); } + #endif + else { axislock[idx] &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } + #else + axislock[idx] &= ~(step_pin[idx]); + #endif } } - sys.homing_axis_lock = axislock; + sys.homing_axis_lock[idx] = axislock[idx]; } + } - st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us. - - // Exit routines: No time to run protocol_execute_realtime() in this loop. - if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) { - uint8_t rt_exec = sys_rt_exec_state; - // Homing failure condition: Reset issued during cycle. - if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); } - // Homing failure condition: Safety door was opened. - if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); } - // Homing failure condition: Limit switch still engaged after pull-off motion - if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); } - // Homing failure condition: Limit switch not found during approach. - if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); } - if (sys_rt_exec_alarm) { - mc_reset(); // Stop motors, if they are running. - protocol_execute_realtime(); - return; - } else { - // Pull-off motion complete. Disable CYCLE_STOP from executing. - system_clear_exec_state_flag(EXEC_CYCLE_STOP); - break; - } + st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us. + + // Exit routines: No time to run protocol_execute_realtime() in this loop. + if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) { + uint8_t rt_exec = sys_rt_exec_state; + // Homing failure condition: Reset issued during cycle. + if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); } + // Homing failure condition: Safety door was opened. + if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); } + // Homing failure condition: Limit switch still engaged after pull-off motion + if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); } + // Homing failure condition: Limit switch not found during approach. + if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); } + if (sys_rt_exec_alarm) { + mc_reset(); // Stop motors, if they are running. + protocol_execute_realtime(); + return; + } else { + // Pull-off motion complete. Disable CYCLE_STOP from executing. + system_clear_exec_state_flag(EXEC_CYCLE_STOP); + break; } + } - } while (STEP_MASK & axislock); - st_reset(); // Immediately force kill steppers and reset step segment buffer. - delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate. + } while (axislock_active(axislock)); + st_reset(); // Immediately force kill steppers and reset step segment buffer. + delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate. - // Reverse direction and reset homing rate for locate cycle(s). - approach = !approach; + // Reverse direction and reset homing rate for locate cycle(s). + approach = !approach; - // After first cycle, homing enters locating phase. Shorten search to pull-off distance. - if (approach) { - max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR; - homing_rate = settings.homing_feed_rate; - } else { - max_travel = settings.homing_pulloff; - homing_rate = settings.homing_seek_rate; - } - } while (n_cycle-- > 0); - #endif // DEFAULTS_RAMPS_BOARD + // After first cycle, homing enters locating phase. Shorten search to pull-off distance. + if (approach) { + max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR; + homing_rate = settings.homing_feed_rate; + } else { + max_travel = settings.homing_pulloff; + homing_rate = settings.homing_seek_rate; + } + } while (n_cycle-- > 0); // The active cycle axes should now be homed and machine limits have been located. By // default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches diff --git a/grbl/planner.c b/grbl/planner.c index f5c749c38..1900e44fd 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -380,11 +380,7 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) unit_vec[idx] = delta_mm; // Store unit vector numerator // Set direction bits. Bit enabled always means direction is negative. - #ifdef DEFAULTS_RAMPS_BOARD - if (delta_mm < 0.0 ) { block->direction_bits[idx] |= get_direction_pin_mask(idx); } - #else - if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); } - #endif // DEFAULTS_RAMPS_BOARD + if (delta_mm < 0.0 ) { block->direction_bits[idx] |= get_direction_pin_mask(idx); } } // Bail if this is a zero-length block. Highly unlikely to occur. diff --git a/grbl/planner.h b/grbl/planner.h index d9d8e227c..128eb509a 100644 --- a/grbl/planner.h +++ b/grbl/planner.h @@ -53,11 +53,7 @@ typedef struct { // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. uint32_t steps[N_AXIS]; // Step count along each axis uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block. - #ifdef DEFAULTS_RAMPS_BOARD - uint8_t direction_bits[N_AXIS]; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) - #else - uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) - #endif // DEFAULTS_RAMPS_BOARD + uint8_t direction_bits[N_AXIS]; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) // Block condition data to ensure correct execution depending on states and overrides. uint8_t condition; // Block bitflag variable defining block run conditions. Copied from pl_line_data. int32_t line_number; // Block line number for real-time reporting. Copied from pl_line_data. diff --git a/grbl/settings.c b/grbl/settings.c index 71dfbc062..570f7732a 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -331,91 +331,70 @@ void settings_init() { // Returns step pin mask according to Grbl internal axis indexing. uint8_t get_step_pin_mask(uint8_t axis_idx) { - #ifdef DEFAULTS_RAMPS_BOARD - if ( axis_idx == AXIS_1 ) { return((1< 3 - if ( axis_idx == AXIS_4 ) { return((1< 4 - if ( axis_idx == AXIS_5 ) { return((1< 5 - if ( axis_idx == AXIS_6 ) { return((1< 3 + if ( axis_idx == AXIS_4 ) { return((1< 4 + if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 3 - if ( axis_idx == AXIS_4 ) { return((1< 4 - if ( axis_idx == AXIS_5 ) { return((1< 5 - if ( axis_idx == AXIS_6 ) { return((1< 3 + if ( axis_idx == AXIS_4 ) { return((1< 4 + if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 3 - if ( axis_idx == AXIS_4 ) { return((1< 4 - if ( axis_idx == AXIS_5 ) { return((1< 5 - if ( axis_idx == AXIS_6 ) { return((1< 3 + if ( axis_idx == AXIS_4 ) { return((1< 4 + if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 3 - if ( axis_idx == AXIS_4 ) { return((1< 4 - if ( axis_idx == AXIS_5 ) { return((1< 5 - if ( axis_idx == AXIS_6 ) { return((1< 3 + if ( axis_idx == AXIS_4 ) { return((1< 4 + if ( axis_idx == AXIS_5 ) { return((1< 5 + if ( axis_idx == AXIS_6 ) { return((1< 3 - STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); - #endif - #if N_AXIS > 4 - STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); - #endif - #if N_AXIS > 5 - STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); - #endif - } else { - STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); - STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); - STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); - #if N_AXIS > 3 - STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); - #endif - #if N_AXIS > 4 - STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); - #endif - #if N_AXIS > 5 - STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); - #endif - } - // Initialize stepper output bits to ensure first ISR call does not step. - for (idx = 0; idx < N_AXIS; idx++) { - st.step_outbits[idx] = step_port_invert_mask[idx]; - } - #else - if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1< 3 + STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); + #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); + #endif + } else { + STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); + STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); + STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); + #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); + #endif + } + // Initialize stepper output bits to ensure first ISR call does not step. + for (idx = 0; idx < N_AXIS; idx++) { + st.step_outbits[idx] = step_port_invert_mask[idx]; + } // Initialize step pulse timing from settings. Here to ensure updating after re-writing. #ifdef STEP_PULSE_DELAY @@ -321,38 +287,33 @@ void st_go_idle() pin_state = true; // Override. Disable steppers. } if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { pin_state = !pin_state; } // Apply pin invert. - #ifdef DEFAULTS_RAMPS_BOARD - if (pin_state) { - STEPPER_DISABLE_PORT(0) |= (1 << STEPPER_DISABLE_BIT(0)); - STEPPER_DISABLE_PORT(1) |= (1 << STEPPER_DISABLE_BIT(1)); - STEPPER_DISABLE_PORT(2) |= (1 << STEPPER_DISABLE_BIT(2)); - #if N_AXIS > 3 - STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); - #endif - #if N_AXIS > 4 - STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); - #endif - #if N_AXIS > 5 - STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); - #endif - } else { - STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); - STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); - STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); - #if N_AXIS > 3 - STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); - #endif - #if N_AXIS > 4 - STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); - #endif - #if N_AXIS > 5 - STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); - #endif - } - #else - if (pin_state) { STEPPERS_DISABLE_PORT |= (1< 3 + STEPPER_DISABLE_PORT(3) |= (1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) |= (1 << STEPPER_DISABLE_BIT(4)); + #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) |= (1 << STEPPER_DISABLE_BIT(5)); + #endif + } else { + STEPPER_DISABLE_PORT(0) &= ~(1 << STEPPER_DISABLE_BIT(0)); + STEPPER_DISABLE_PORT(1) &= ~(1 << STEPPER_DISABLE_BIT(1)); + STEPPER_DISABLE_PORT(2) &= ~(1 << STEPPER_DISABLE_BIT(2)); + #if N_AXIS > 3 + STEPPER_DISABLE_PORT(3) &= ~(1 << STEPPER_DISABLE_BIT(3)); + #endif + #if N_AXIS > 4 + STEPPER_DISABLE_PORT(4) &= ~(1 << STEPPER_DISABLE_BIT(4)); + #endif + #if N_AXIS > 5 + STEPPER_DISABLE_PORT(5) &= ~(1 << STEPPER_DISABLE_BIT(5)); + #endif + } } @@ -406,76 +367,60 @@ void st_go_idle() // with probing and homing cycles that require true real-time positions. ISR(TIMER1_COMPA_vect) { - #ifdef DEFAULTS_RAMPS_BOARD - int i; - #endif // Ramps Board + int i; if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt - #ifdef DEFAULTS_RAMPS_BOARD - // Adding limit check status to implement hardware limits for RAMPS - // outside the changepin interrupt wich cannot be used - #ifdef ENABLE_RAMPS_HW_LIMITS - if (limits_get_state()) { - ramps_hard_limit(); - } - #endif - #endif // Ramps Board + // Adding limit check status to implement hardware limits for RAMPS + // outside the changepin interrupt wich cannot be used + #ifdef ENABLE_RAMPS_HW_LIMITS + if (limits_get_state()) { + ramps_hard_limit(); + } + #endif // Set the direction pins a couple of nanoseconds before we step the steppers - #ifdef DEFAULTS_RAMPS_BOARD - DIRECTION_PORT(0) = (DIRECTION_PORT(0) & ~(1 << DIRECTION_BIT(0))) | st.dir_outbits[0]; - DIRECTION_PORT(1) = (DIRECTION_PORT(1) & ~(1 << DIRECTION_BIT(1))) | st.dir_outbits[1]; - DIRECTION_PORT(2) = (DIRECTION_PORT(2) & ~(1 << DIRECTION_BIT(2))) | st.dir_outbits[2]; + DIRECTION_PORT(0) = (DIRECTION_PORT(0) & ~(1 << DIRECTION_BIT(0))) | st.dir_outbits[0]; + DIRECTION_PORT(1) = (DIRECTION_PORT(1) & ~(1 << DIRECTION_BIT(1))) | st.dir_outbits[1]; + DIRECTION_PORT(2) = (DIRECTION_PORT(2) & ~(1 << DIRECTION_BIT(2))) | st.dir_outbits[2]; + #if N_AXIS > 3 + DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | st.dir_outbits[3]; + #endif + #if N_AXIS > 4 + DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; + #endif + #if N_AXIS > 5 + DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | st.dir_outbits[5]; + #endif + + // Then pulse the stepping pins + #ifdef STEP_PULSE_DELAY + st.step_bits[0] = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; // Store out_bits to prevent overwriting. + st.step_bits[1] = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; // Store out_bits to prevent overwriting. + st.step_bits[2] = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; // Store out_bits to prevent overwriting. #if N_AXIS > 3 - DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | st.dir_outbits[3]; + st.step_bits[3] = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; // Store out_bits to prevent overwriting. #endif #if N_AXIS > 4 - DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | st.dir_outbits[4]; + st.step_bits[4] = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; // Store out_bits to prevent overwriting. #endif #if N_AXIS > 5 - DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | st.dir_outbits[5]; + st.step_bits[5] = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; // Store out_bits to prevent overwriting. #endif #else - DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK); - #endif // Ramps Boafd - - // Then pulse the stepping pins - #ifdef DEFAULTS_RAMPS_BOARD - #ifdef STEP_PULSE_DELAY - st.step_bits[0] = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; // Store out_bits to prevent overwriting. - st.step_bits[1] = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; // Store out_bits to prevent overwriting. - st.step_bits[2] = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; // Store out_bits to prevent overwriting. - #if N_AXIS > 3 - st.step_bits[3] = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; // Store out_bits to prevent overwriting. - #endif - #if N_AXIS > 4 - st.step_bits[4] = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; // Store out_bits to prevent overwriting. - #endif - #if N_AXIS > 5 - st.step_bits[5] = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; // Store out_bits to prevent overwriting. - #endif - #else - STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; - STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; - STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; - #if N_AXIS > 3 - STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; - #endif - #if N_AXIS > 4 - STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; - #endif - #if N_AXIS > 5 - STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; - #endif + STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | st.step_outbits[0]; + STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | st.step_outbits[1]; + STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | st.step_outbits[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | st.step_outbits[3]; #endif - #else - #ifdef STEP_PULSE_DELAY - st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting. - #else // Normal operation - STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits; + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | st.step_outbits[4]; + #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | st.step_outbits[5]; #endif - #endif // Ramps Board + #endif // Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after // exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler. @@ -518,12 +463,8 @@ ISR(TIMER1_COMPA_vect) st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); #endif } - #ifdef DEFAULTS_RAMPS_BOARD - for (i = 0; i < N_AXIS; i++) - st.dir_outbits[i] = st.exec_block->direction_bits[i] ^ dir_port_invert_mask[i]; - #else - st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask; - #endif // Ramps Board + for (i = 0; i < N_AXIS; i++) + st.dir_outbits[i] = st.exec_block->direction_bits[i] ^ dir_port_invert_mask[i]; #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level. @@ -559,12 +500,8 @@ ISR(TIMER1_COMPA_vect) if (sys_probe_state == PROBE_ACTIVE) { probe_state_monitor(); } // Reset step out bits. - #ifdef DEFAULTS_RAMPS_BOARD - for (i = 0; i < N_AXIS; i++) - st.step_outbits[i] = 0; - #else - st.step_outbits = 0; - #endif // Ramps Board + for (i = 0; i < N_AXIS; i++) + st.step_outbits[i] = 0; // Execute step displacement profile by Bresenham line algorithm #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING @@ -572,76 +509,47 @@ ISR(TIMER1_COMPA_vect) #else st.counter_x += st.exec_block->steps[AXIS_1]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_x > st.exec_block->step_event_count) { - st.step_outbits[AXIS_1] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_1] & (1< st.exec_block->step_event_count) { - st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_1] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_1] & (1<steps[AXIS_2]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_y > st.exec_block->step_event_count) { - st.step_outbits[AXIS_2] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_2] & (1< st.exec_block->step_event_count) { - st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_2] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_2] & (1<steps[AXIS_3]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_z > st.exec_block->step_event_count) { - st.step_outbits[AXIS_3] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_3] & (1< st.exec_block->step_event_count) { - st.step_outbits |= (1<step_event_count; - if (st.exec_block->direction_bits & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_3] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_3] & (1< 3 #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING st.counter_4 += st.steps[AXIS_4]; #else st.counter_4 += st.exec_block->steps[AXIS_4]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_4 > st.exec_block->step_event_count) { - st.step_outbits[AXIS_4] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_4] & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_4] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_4] & (1< 3 #if N_AXIS > 4 #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING @@ -649,14 +557,12 @@ ISR(TIMER1_COMPA_vect) #else st.counter_5 += st.exec_block->steps[AXIS_5]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_5 > st.exec_block->step_event_count) { - st.step_outbits[AXIS_5] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_5] & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_5] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_5] & (1< 4 #if N_AXIS > 5 #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING @@ -664,35 +570,25 @@ ISR(TIMER1_COMPA_vect) #else st.counter_6 += st.exec_block->steps[AXIS_6]; #endif - #ifdef DEFAULTS_RAMPS_BOARD - if (st.counter_6 > st.exec_block->step_event_count) { - st.step_outbits[AXIS_6] |= (1<step_event_count; - if (st.exec_block->direction_bits[AXIS_6] & (1< st.exec_block->step_event_count) { + st.step_outbits[AXIS_6] |= (1<step_event_count; + if (st.exec_block->direction_bits[AXIS_6] & (1< 5 // During a homing cycle, lock out and prevent desired axes from moving. - #ifdef DEFAULTS_RAMPS_BOARD - for (i = 0; i < N_AXIS; i++) + for (i = 0; i < N_AXIS; i++) if (sys.state == STATE_HOMING) { st.step_outbits[i] &= sys.homing_axis_lock[i]; } - #else - if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; } - #endif // Ramps Board st.step_count--; // Decrement step events count if (st.step_count == 0) { // Segment is complete. Discard current segment and advance segment indexing. st.exec_segment = NULL; if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; } } - #ifdef DEFAULTS_RAMPS_BOARD - for (i = 0; i < N_AXIS; i++) - st.step_outbits[i] ^= step_port_invert_mask[i]; // Apply step port invert mask - #else - st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask - #endif // Ramps Board + for (i = 0; i < N_AXIS; i++) + st.step_outbits[i] ^= step_port_invert_mask[i]; // Apply step port invert mask busy = false; } @@ -711,22 +607,18 @@ ISR(TIMER1_COMPA_vect) ISR(TIMER0_OVF_vect) { // Reset stepping pins (leave the direction pins) - #ifdef DEFAULTS_RAMPS_BOARD - STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | step_port_invert_mask[0]; - STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | step_port_invert_mask[1]; - STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | step_port_invert_mask[2]; - #if N_AXIS > 3 - STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; - #endif - #if N_AXIS > 4 - STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; - #endif - #if N_AXIS > 5 - STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; - #endif - #else - STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK); - #endif // Ramps Board + STEP_PORT(0) = (STEP_PORT(0) & ~(1 << STEP_BIT(0))) | step_port_invert_mask[0]; + STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | step_port_invert_mask[1]; + STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | step_port_invert_mask[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; + #endif + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; + #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; + #endif TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed. } #ifdef STEP_PULSE_DELAY @@ -737,22 +629,18 @@ ISR(TIMER0_OVF_vect) // st_wake_up() routine. ISR(TIMER0_COMPA_vect) { - #ifdef DEFAULTS_RAMPS_BOARD - STEP_PORT(0) = st.step_bits[0]; // Begin step pulse. - STEP_PORT(1) = st.step_bits[1]; // Begin step pulse. - STEP_PORT(2) = st.step_bits[2]; // Begin step pulse. - #if N_AXIS > 3 - STEP_PORT(3) = st.step_bits[3]; // Begin step pulse. - #endif - #if N_AXIS > 4 - STEP_PORT(4) = st.step_bits[4]; // Begin step pulse. - #endif - #if N_AXIS > 5 - STEP_PORT(5) = st.step_bits[5]; // Begin step pulse. - #endif - #else - STEP_PORT = st.step_bits; // Begin step pulse. - #endif // Ramps Board + STEP_PORT(0) = st.step_bits[0]; // Begin step pulse. + STEP_PORT(1) = st.step_bits[1]; // Begin step pulse. + STEP_PORT(2) = st.step_bits[2]; // Begin step pulse. + #if N_AXIS > 3 + STEP_PORT(3) = st.step_bits[3]; // Begin step pulse. + #endif + #if N_AXIS > 4 + STEP_PORT(4) = st.step_bits[4]; // Begin step pulse. + #endif + #if N_AXIS > 5 + STEP_PORT(5) = st.step_bits[5]; // Begin step pulse. + #endif } #endif @@ -761,31 +649,20 @@ ISR(TIMER0_OVF_vect) void st_generate_step_dir_invert_masks() { uint8_t idx; - #ifdef DEFAULTS_RAMPS_BOARD - for (idx=0; idx 3 - STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; - DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | dir_port_invert_mask[3]; - #endif - #if N_AXIS > 4 - STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; - DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | dir_port_invert_mask[4]; - #endif - #if N_AXIS > 5 - STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; - DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | dir_port_invert_mask[5]; - #endif - #else - st.dir_outbits = dir_port_invert_mask; // Initialize direction bits to default. + STEP_PORT(1) = (STEP_PORT(1) & ~(1 << STEP_BIT(1))) | step_port_invert_mask[1]; + DIRECTION_PORT(1) = (DIRECTION_PORT(1) & ~(1 << DIRECTION_BIT(1))) | dir_port_invert_mask[1]; - // Initialize step and direction port pins. - STEP_PORT = (STEP_PORT & ~STEP_MASK) | step_port_invert_mask; - DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | dir_port_invert_mask; - #endif // Ramps Board + STEP_PORT(2) = (STEP_PORT(2) & ~(1 << STEP_BIT(2))) | step_port_invert_mask[2]; + DIRECTION_PORT(2) = (DIRECTION_PORT(2) & ~(1 << DIRECTION_BIT(2))) | dir_port_invert_mask[2]; + #if N_AXIS > 3 + STEP_PORT(3) = (STEP_PORT(3) & ~(1 << STEP_BIT(3))) | step_port_invert_mask[3]; + DIRECTION_PORT(3) = (DIRECTION_PORT(3) & ~(1 << DIRECTION_BIT(3))) | dir_port_invert_mask[3]; + #endif + #if N_AXIS > 4 + STEP_PORT(4) = (STEP_PORT(4) & ~(1 << STEP_BIT(4))) | step_port_invert_mask[4]; + DIRECTION_PORT(4) = (DIRECTION_PORT(4) & ~(1 << DIRECTION_BIT(4))) | dir_port_invert_mask[4]; + #endif + #if N_AXIS > 5 + STEP_PORT(5) = (STEP_PORT(5) & ~(1 << STEP_BIT(5))) | step_port_invert_mask[5]; + DIRECTION_PORT(5) = (DIRECTION_PORT(5) & ~(1 << DIRECTION_BIT(5))) | dir_port_invert_mask[5]; + #endif } @@ -840,50 +709,44 @@ void st_reset() void stepper_init() { // Configure step and direction interface pins - #ifdef DEFAULTS_RAMPS_BOARD - STEP_DDR(0) |= 1< 3 - STEP_DDR(3) |= 1< 4 - STEP_DDR(4) |= 1< 5 - STEP_DDR(5) |= 1< 3 + STEP_DDR(3) |= 1< 4 + STEP_DDR(4) |= 1< 5 + STEP_DDR(5) |= 1< 3 - STEPPER_DISABLE_DDR(3) |= 1< 4 - STEPPER_DISABLE_DDR(4) |= 1< 5 - STEPPER_DISABLE_DDR(5) |= 1< 3 + STEPPER_DISABLE_DDR(3) |= 1< 4 + STEPPER_DISABLE_DDR(4) |= 1< 5 + STEPPER_DISABLE_DDR(5) |= 1< 3 - DIRECTION_DDR(3) |= 1< 4 - DIRECTION_DDR(4) |= 1< 5 - DIRECTION_DDR(5) |= 1< 3 + DIRECTION_DDR(3) |= 1< 4 + DIRECTION_DDR(4) |= 1< 5 + DIRECTION_DDR(5) |= 1<direction_bits[idx] = pl_block->direction_bits[idx]; - } - #else - st_prep_block->direction_bits = pl_block->direction_bits; - #endif // Ramps Board + for (idx=0; idxdirection_bits[idx] = pl_block->direction_bits[idx]; + } #ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING for (idx=0; idxsteps[idx] = (pl_block->steps[idx] << 1); } diff --git a/grbl/system.h b/grbl/system.h index 7ab51fda3..d68a0585a 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -123,11 +123,7 @@ typedef struct { uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean) uint8_t step_control; // Governs the step segment generator depending on system state. uint8_t probe_succeeded; // Tracks if last probing cycle was successful. - #ifdef DEFAULTS_RAMPS_BOARD - uint8_t homing_axis_lock[N_AXIS]; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. - #else - uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. - #endif + uint8_t homing_axis_lock[N_AXIS]; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. uint8_t f_override; // Feed rate override value in percent uint8_t r_override; // Rapids override value in percent uint8_t spindle_speed_ovr; // Spindle speed value in percent From 8d37598c0ed8e2f7ab53247597b5047f74ea0cf2 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 5 May 2021 11:22:22 +0200 Subject: [PATCH 054/101] Update comment in grblUpload.ino --- grbl/examples/grblUpload/grblUpload.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grbl/examples/grblUpload/grblUpload.ino b/grbl/examples/grblUpload/grblUpload.ino index 581b6b3d7..f1ebc2ab9 100644 --- a/grbl/examples/grblUpload/grblUpload.ino +++ b/grbl/examples/grblUpload/grblUpload.ino @@ -1,12 +1,13 @@ /*********************************************************************** -This sketch compiles and uploads Grbl to your 328p-based Arduino! +This sketch compiles and uploads Grbl to your Mega2560 Arduino board! To use: - First make sure you have imported Grbl source code into your Arduino - IDE. There are details on our Github website on how to do this. + IDE. See more details on the Github wiki page of grbl-Mega-5X: + https://github.com/fra589/grbl-Mega-5X/wiki/Compiling-grbl-Mega-5X - Select your Arduino Board and Serial Port in the Tools drop-down menu. - NOTE: Grbl only officially supports 328p-based Arduinos, like the Uno. + NOTE: grbl-Mega-5X only officially supports Mega2560 Arduino boards. Using other boards will likely not work! - Then just click 'Upload'. That's it! From 173d1200faa343eb4a5878492d6b8cceb43a5230 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 10 May 2021 11:27:24 +0200 Subject: [PATCH 055/101] Suppress old hardware limit interrupt code, not compatible with RAMPS --- grbl/cpu_map.h | 8 -------- grbl/grbl.h | 4 ++-- grbl/limits.c | 26 -------------------------- grbl/limits.h | 3 --- grbl/motion_control.c | 2 -- 5 files changed, 2 insertions(+), 41 deletions(-) diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 731605895..6638f30e2 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -197,14 +197,6 @@ #define MAX_LIMIT_PORT(i) _PORT(MAX_LIMIT_PORT_##i) #define MAX_LIMIT_PIN(i) _PIN(MAX_LIMIT_PORT_##i) - // #define LIMIT_INT PCIE0 // Pin change interrupt enable pin - // #define LIMIT_INT_vect PCINT0_vect - // #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register - // #define LIMIT_MASK ((1< diff --git a/grbl/limits.c b/grbl/limits.c index 2e87cd4e4..d6b276905 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -109,29 +109,6 @@ void limits_init() MAX_LIMIT_PORT(5) |= (1< Date: Mon, 10 May 2021 19:58:26 +0200 Subject: [PATCH 056/101] Adding ability to use X, Y, Z, U, V, W, A, B, C, D, E & H as axis names (issue #195) --- doc/csv/alarm_codes_en_US.csv | 1 + grbl/config.h | 7 ++- grbl/gcode.c | 34 +++++++++++- grbl/grbl.h | 2 +- grbl/limits.c | 4 +- grbl/main.c | 102 +++++++++++++++++++++++++++++++++- grbl/motion_control.c | 50 +++++++++++++++++ grbl/motion_control.h | 1 + grbl/protocol.c | 7 --- grbl/report.c | 9 ++- grbl/report.h | 2 +- grbl/system.c | 23 +++++++- grbl/system.h | 4 ++ 13 files changed, 226 insertions(+), 20 deletions(-) diff --git a/doc/csv/alarm_codes_en_US.csv b/doc/csv/alarm_codes_en_US.csv index 1ab2b8ae5..b9014d502 100644 --- a/doc/csv/alarm_codes_en_US.csv +++ b/doc/csv/alarm_codes_en_US.csv @@ -8,3 +8,4 @@ "7","Homing fail","Homing fail. Safety door was opened during homing cycle." "8","Homing fail","Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring." "9","Homing fail","Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring." +"10","Homing fail","Homing fail. Max travel is shorter than the pull off travel." diff --git a/grbl/config.h b/grbl/config.h index 0d9dbe151..691475af1 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -48,7 +48,7 @@ #define N_AXIS_LINEAR 3 // Number of linears axis #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. -#define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V & W. +#define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V, W, D, E & H. #define AXIS_2 1 #define AXIS_2_NAME 'Y' #define AXIS_3 2 @@ -84,9 +84,10 @@ // the order of their number. Some graphical interface are not able to affect axis values reported // by Grbl to the correct axis name. // Uncomment to enable sorting of axis values by axis_names rather than by axis number. Default disabled. -// If this option is enabled, the sorting order will be X, Y, Z, U, V, W, A, B and C as defined below. +// If this option is enabled, the sorting order will be X, Y, Z, U, V, W, A, B, C, D, E & H +// as defined below. //#define SORT_REPORT_BY_AXIS_NAME -//#define AXIS_NAME_SORT_ORDER {'X', 'Y', 'Z', 'U', 'V', 'W', 'A', 'B', 'C'} +//#define AXIS_NAME_SORT_ORDER {'X', 'Y', 'Z', 'U', 'V', 'W', 'A', 'B', 'C', 'D', 'E', 'H'} #ifdef SORT_REPORT_BY_AXIS_NAME #ifndef AXIS_NAME_SORT_ORDER diff --git a/grbl/gcode.c b/grbl/gcode.c index 390baed9a..cc5e9e39d 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -80,6 +80,7 @@ uint8_t gc_execute_line(char *line) uint8_t axis_0, axis_1, axis_linear; uint8_t axis_a, axis_b, axis_c; uint8_t axis_u, axis_v, axis_w; + uint8_t axis_d, axis_e, axis_h; uint8_t axis_0_mask = 0; uint8_t axis_1_mask = 0; uint8_t axis_linear_mask = 0; @@ -89,6 +90,9 @@ uint8_t gc_execute_line(char *line) uint8_t axis_u_mask = 0; uint8_t axis_v_mask = 0; uint8_t axis_w_mask = 0; + uint8_t axis_d_mask = 0; + uint8_t axis_e_mask = 0; + uint8_t axis_h_mask = 0; uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution // Initialize bitflag tracking variables for axis indices compatible operations. @@ -359,7 +363,7 @@ uint8_t gc_execute_line(char *line) if (value > MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } gc_block.values.t = int_value; break; - // case 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V' or 'W' depending of AXIS_*_NAME. + // case 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'D', 'E' or 'H' depending of AXIS_*_NAME. // case imposible because same name can be used more than one for axis cloning // case AXIS_1_NAME: case AXIS_2_NAME: case AXIS_3_NAME: case AXIS_4_NAME: case AXIS_5_NAME: case AXIS_6_NAME: default: @@ -572,6 +576,9 @@ uint8_t gc_execute_line(char *line) else if (AXIS_4_NAME == 'U') { axis_u_mask |= (1<feed_rate = homing_rate; // Set current homing rate. plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion. diff --git a/grbl/main.c b/grbl/main.c index 1b879cdd3..8ae294ea0 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -37,9 +37,12 @@ uint8_t axis_Z_mask = 0; // Global mask for axis Z bits uint8_t axis_A_mask = 0; // Global mask for axis A bits uint8_t axis_B_mask = 0; // Global mask for axis B bits uint8_t axis_C_mask = 0; // Global mask for axis C bits -uint8_t axis_U_mask = 0; // Global mask for axis C bits -uint8_t axis_V_mask = 0; // Global mask for axis C bits -uint8_t axis_W_mask = 0; // Global mask for axis C bits +uint8_t axis_U_mask = 0; // Global mask for axis U bits +uint8_t axis_V_mask = 0; // Global mask for axis V bits +uint8_t axis_W_mask = 0; // Global mask for axis W bits +uint8_t axis_D_mask = 0; // Global mask for axis U bits +uint8_t axis_E_mask = 0; // Global mask for axis V bits +uint8_t axis_H_mask = 0; // Global mask for axis W bits unsigned char axis_name[N_AXIS]; // Global table of axis names #ifdef DEBUG volatile uint8_t sys_rt_exec_debug; @@ -336,7 +339,100 @@ int main(void) axis_name[5] = 'W'; } #endif + + if (AXIS_1_NAME == 'D') { + axis_D_mask |= (1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< 3 + if (bit_istrue((1< 4 + if (bit_istrue((1< 5 + if (bit_istrue((1< Date: Thu, 16 Sep 2021 12:27:38 +0200 Subject: [PATCH 057/101] Adding GCode M62 to M65 digital output commands --- Makefile | 6 +- TODO | 10 ++ doc/csv/error_codes_en_US.csv | 2 +- grbl/config.h | 8 ++ grbl/coolant_control.c | 42 ++++---- grbl/cpu_map.h | 15 +++ grbl/digital_control.c | 175 ++++++++++++++++++++++++++++++++++ grbl/digital_control.h | 45 +++++++++ grbl/gcode.c | 43 ++++++++- grbl/gcode.h | 7 ++ grbl/grbl.h | 5 +- grbl/main.c | 1 + 12 files changed, 329 insertions(+), 30 deletions(-) create mode 100644 TODO create mode 100644 grbl/digital_control.c create mode 100644 grbl/digital_control.h diff --git a/Makefile b/Makefile index e4d58c0f0..61eaf9782 100644 --- a/Makefile +++ b/Makefile @@ -32,9 +32,9 @@ DEVICE ?= atmega2560 CLOCK = 16000000L ###PROGRAMMER ?= -c avrisp2 -P usb PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB0 -SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c serial.c \ - protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ - print.c probe.c report.c system.c sleep.c jog.c +SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ + serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ + print.c probe.c report.c system.c sleep.c jog.c BUILDDIR = build SOURCEDIR = grbl # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m diff --git a/TODO b/TODO new file mode 100644 index 000000000..7be8023d9 --- /dev/null +++ b/TODO @@ -0,0 +1,10 @@ +Square offset params +backlash compensation +#112 => 2 PWM outputs: One on D8 in 0-12V and the other on another port in 0-5V, new commands such as M3.1, M4.1 and M5.1 to control the second output. +#71 => G0 should find the shortest path on Rotary Axis (wrap-around coordinate system ?) +CoreXY homing bug correction grbl-Mega-5X #124 / grbl-Mega #49 +Second serial port communications +Spindle enable (laser enable) did not restore after feed hold, bug #79 + + + diff --git a/doc/csv/error_codes_en_US.csv b/doc/csv/error_codes_en_US.csv index 1375945aa..c7448c679 100644 --- a/doc/csv/error_codes_en_US.csv +++ b/doc/csv/error_codes_en_US.csv @@ -34,4 +34,4 @@ "35","Invalid gcode ID:35","G2 and G3 arcs require at least one in-plane offset word." "36","Invalid gcode ID:36","Unused value words found in block." "37","Invalid gcode ID:37","G43.1 dynamic tool length offset is not assigned to configured tool length axis." -"38","Invalid gcode ID:38","Tool number greater than max supported value." \ No newline at end of file +"38","Invalid gcode ID:38","Tool number or digital output number greater than max supported value." diff --git a/grbl/config.h b/grbl/config.h index 691475af1..d93e7784e 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -299,6 +299,14 @@ // #define INVERT_COOLANT_FLOOD_PIN // Default disabled. Uncomment to enable. // #define INVERT_COOLANT_MIST_PIN // Default disabled. Note: Enable M7 mist coolant in config.h +// Inverts the selected digital output pin from low-disabled/high-enabled to low-enabled/high-disabled. +// Useful for some pre-built electronic boards. +// #define INVERT_DIGITAL_OUTPUT_PIN_0 // Default disabled. Uncomment to enable. +// #define INVERT_DIGITAL_OUTPUT_PIN_1 // Default disabled. Uncomment to enable. +// #define INVERT_DIGITAL_OUTPUT_PIN_2 // Default disabled. Uncomment to enable. +// #define INVERT_DIGITAL_OUTPUT_PIN_3 // Default disabled. Uncomment to enable. + + // When Grbl powers-cycles or is hard reset with the Arduino reset button, Grbl boots up with no ALARM // by default. This is to make it as simple as possible for new users to start using Grbl. When homing // is enabled and a user has installed limit switches, Grbl will boot up in an ALARM state to indicate diff --git a/grbl/coolant_control.c b/grbl/coolant_control.c index 5100fd71e..9b780d87f 100644 --- a/grbl/coolant_control.c +++ b/grbl/coolant_control.c @@ -82,28 +82,28 @@ void coolant_set_state(uint8_t mode) #else COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); #endif - } else { - #ifdef INVERT_COOLANT_FLOOD_PIN - COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); - #else - COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); - #endif - } + } else { + #ifdef INVERT_COOLANT_FLOOD_PIN + COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); + #else + COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); + #endif + } + + if (mode & COOLANT_MIST_ENABLE) { + #ifdef INVERT_COOLANT_MIST_PIN + COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); + #else + COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); + #endif + } else { + #ifdef INVERT_COOLANT_MIST_PIN + COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); + #else + COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); + #endif + } - if (mode & COOLANT_MIST_ENABLE) { - #ifdef INVERT_COOLANT_MIST_PIN - COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); - #else - COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); - #endif - } else { - #ifdef INVERT_COOLANT_MIST_PIN - COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); - #else - COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); - #endif - } - sys.report_ovr_counter = 0; // Set to report change immediately } diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 6638f30e2..c268b227c 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -221,6 +221,21 @@ #define COOLANT_MIST_PORT PORTH #define COOLANT_MIST_BIT 6 // MEGA2560 Digital Pin 9 - Ramps 1.4 12v output + // Define M62 - M65 Digital Output Control ports + // D16 D17 D23 D25 + #define DIGITAL_OUTPUT_DDR_0 DDRH + #define DIGITAL_OUTPUT_PORT_0 PORTH + #define DIGITAL_OUTPUT_BIT_0 1 // MEGA2560 Digital Pin 16 - Ramps 1.4 AUX-4 D16 + #define DIGITAL_OUTPUT_DDR_1 DDRH + #define DIGITAL_OUTPUT_PORT_1 PORTH + #define DIGITAL_OUTPUT_BIT_1 0 // MEGA2560 Digital Pin 17 - Ramps 1.4 AUX-4 D17 + #define DIGITAL_OUTPUT_DDR_2 DDRA + #define DIGITAL_OUTPUT_PORT_2 PORTA + #define DIGITAL_OUTPUT_BIT_2 1 // MEGA2560 Digital Pin 23 - Ramps 1.4 AUX-4 D23 + #define DIGITAL_OUTPUT_DDR_3 DDRA + #define DIGITAL_OUTPUT_PORT_3 PORTA + #define DIGITAL_OUTPUT_BIT_3 3 // MEGA2560 Digital Pin 23 - Ramps 1.4 AUX-4 D25 + // Define user-control CONTROLs (cycle start, reset, feed hold) input pins. // NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits). #define CONTROL_DDR DDRK diff --git a/grbl/digital_control.c b/grbl/digital_control.c new file mode 100644 index 000000000..d607b9e18 --- /dev/null +++ b/grbl/digital_control.c @@ -0,0 +1,175 @@ +/* + digital_control.c - digital output M62-M65 control methods + Part of Grbl + + Copyright (c) 2017-2021 Gauthier Briere + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include "grbl.h" + + +void digital_init() +{ + DIGITAL_OUTPUT_DDR_0 |= (1 << DIGITAL_OUTPUT_BIT_0); // Configure as output pin. + DIGITAL_OUTPUT_DDR_1 |= (1 << DIGITAL_OUTPUT_BIT_1); // Configure as output pin. + DIGITAL_OUTPUT_DDR_2 |= (1 << DIGITAL_OUTPUT_BIT_2); // Configure as output pin. + DIGITAL_OUTPUT_DDR_3 |= (1 << DIGITAL_OUTPUT_BIT_3); // Configure as output pin. + digital_stop(0x0F); +} + + +// Returns current digital output state. Overrides may alter it from programmed state. +uint8_t digital_get_state() +{ + uint8_t digital_state = DIGITAL_OUTPUT_STATE_OFF; + #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + if (bit_isfalse(DIGITAL_OUTPUT_PORT_0,(1 << DIGITAL_OUTPUT_BIT_0))) { + #else + if (bit_istrue(DIGITAL_OUTPUT_PORT_0,(1 << DIGITAL_OUTPUT_BIT_0))) { + #endif + digital_state |= DIGITAL_OUTPUT_STATE_P0; + } + #ifdef INVERT_DIGITAL_OUTPUT_PIN_1 + if (bit_isfalse(DIGITAL_OUTPUT_PORT_1,(1 << DIGITAL_OUTPUT_BIT_1))) { + #else + if (bit_istrue(DIGITAL_OUTPUT_PORT_1,(1 << DIGITAL_OUTPUT_BIT_1))) { + #endif + digital_state |= DIGITAL_OUTPUT_STATE_P1; + } + #ifdef INVERT_DIGITAL_OUTPUT_PIN_2 + if (bit_isfalse(DIGITAL_OUTPUT_PORT_2,(1 << DIGITAL_OUTPUT_BIT_2))) { + #else + if (bit_istrue(DIGITAL_OUTPUT_PORT_2,(1 << DIGITAL_OUTPUT_BIT_2))) { + #endif + digital_state |= DIGITAL_OUTPUT_STATE_P2; + } + #ifdef INVERT_DIGITAL_OUTPUT_PIN_3 + if (bit_isfalse(DIGITAL_OUTPUT_PORT_3,(1 << DIGITAL_OUTPUT_BIT_3))) { + #else + if (bit_istrue(DIGITAL_OUTPUT_PORT_3,(1 << DIGITAL_OUTPUT_BIT_3))) { + #endif + digital_state |= DIGITAL_OUTPUT_STATE_P3; + } + return(digital_state); +} + + +// Directly called by digital_init(), digital_set_state(), and mc_reset(), which can be at +// an interrupt-level. No report flag set, but only called by routines that don't need it. +// The mode argument is a bit flag wich define which output must be stopped. +void digital_stop(const uint8_t mode) +{ + if (mode & DIGITAL_OUTPUT_STATE_P0) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + DIGITAL_OUTPUT_PORT_0 |= (1 << DIGITAL_OUTPUT_BIT_0); + #else + DIGITAL_OUTPUT_PORT_0 &= ~(1 << DIGITAL_OUTPUT_BIT_0); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P1) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_1 + DIGITAL_OUTPUT_PORT_1 |= (1 << DIGITAL_OUTPUT_BIT_1); + #else + DIGITAL_OUTPUT_PORT_1 &= ~(1 << DIGITAL_OUTPUT_BIT_1); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P2) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_2 + DIGITAL_OUTPUT_PORT_2 |= (1 << DIGITAL_OUTPUT_BIT_2); + #else + DIGITAL_OUTPUT_PORT_2 &= ~(1 << DIGITAL_OUTPUT_BIT_2); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P3) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_3 + DIGITAL_OUTPUT_PORT_3 |= (1 << DIGITAL_OUTPUT_BIT_3); + #else + DIGITAL_OUTPUT_PORT_3 &= ~(1 << DIGITAL_OUTPUT_BIT_3); + #endif + } +} + + +// Immediately sets digital outputs +// Also sets a flag to report an update to a digital state. +void digital_set_state(uint8_t mode) +{ + if (sys.abort) { return; } // Block during abort. + + if (mode & DIGITAL_OUTPUT_STATE_P0) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + DIGITAL_OUTPUT_PORT_0 &= ~(1 << DIGITAL_OUTPUT_BIT_0); + #else + DIGITAL_OUTPUT_PORT_0 |= (1 << DIGITAL_OUTPUT_BIT_0); + #endif + } else { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + DIGITAL_OUTPUT_PORT_0 |= (1 << DIGITAL_OUTPUT_BIT_0); + #else + DIGITAL_OUTPUT_PORT_0 &= ~(1 << DIGITAL_OUTPUT_BIT_0); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P1) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_1 + DIGITAL_OUTPUT_PORT_1 &= ~(1 << DIGITAL_OUTPUT_BIT_1); + #else + DIGITAL_OUTPUT_PORT_1 |= (1 << DIGITAL_OUTPUT_BIT_1); + #endif + } else { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + DIGITAL_OUTPUT_PORT_1 |= (1 << DIGITAL_OUTPUT_BIT_1); + #else + DIGITAL_OUTPUT_PORT_1 &= ~(1 << DIGITAL_OUTPUT_BIT_1); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P2) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_2 + DIGITAL_OUTPUT_PORT_2 &= ~(1 << DIGITAL_OUTPUT_BIT_2); + #else + DIGITAL_OUTPUT_PORT_2 |= (1 << DIGITAL_OUTPUT_BIT_2); + #endif + } else { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_2 + DIGITAL_OUTPUT_PORT_2 |= (1 << DIGITAL_OUTPUT_BIT_2); + #else + DIGITAL_OUTPUT_PORT_2 &= ~(1 << DIGITAL_OUTPUT_BIT_2); + #endif + } + if (mode & DIGITAL_OUTPUT_STATE_P3) { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_3 + DIGITAL_OUTPUT_PORT_3 &= ~(1 << DIGITAL_OUTPUT_BIT_3); + #else + DIGITAL_OUTPUT_PORT_3 |= (1 << DIGITAL_OUTPUT_BIT_3); + #endif + } else { + #ifdef INVERT_DIGITAL_OUTPUT_PIN_3 + DIGITAL_OUTPUT_PORT_3 |= (1 << DIGITAL_OUTPUT_BIT_3); + #else + DIGITAL_OUTPUT_PORT_3 &= ~(1 << DIGITAL_OUTPUT_BIT_3); + #endif + } + sys.report_ovr_counter = 0; // Set to report change immediately +} + + +// G-code parser entry-point for setting digital state. Forces a planner buffer sync and bails +// if an abort or check-mode is active. +void digital_sync(uint8_t mode) +{ + if (sys.state == STATE_CHECK_MODE) { return; } + protocol_buffer_synchronize(); // Ensure digital turns on when specified in program. + digital_set_state(mode); +} diff --git a/grbl/digital_control.h b/grbl/digital_control.h new file mode 100644 index 000000000..629aef260 --- /dev/null +++ b/grbl/digital_control.h @@ -0,0 +1,45 @@ +/* + digital_control.h - digital output M62-M65 control methods + Part of Grbl + + Copyright (c) 2017-2021 Gauthier Briere + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef digital_control_h +#define digital_control_h + +#define DIGITAL_OUTPUT_STATE_OFF 0 // Must be zero +#define DIGITAL_OUTPUT_STATE_P0 bit(0) +#define DIGITAL_OUTPUT_STATE_P1 bit(1) +#define DIGITAL_OUTPUT_STATE_P2 bit(2) +#define DIGITAL_OUTPUT_STATE_P3 bit(3) + +// Initializes digital output control pins. +void digital_init(); + +// Returns current digital output state. Overrides may alter it from programmed state. +uint8_t digital_get_state(); + +// Immediately disables digital output pins. +void digital_stop(const uint8_t n); + +// Sets the digital output pins according to state specified. +void digital_set_state(uint8_t mode); + +// G-code parser entry-point for setting digital output states. Checks for and executes additional conditions. +void digital_sync(uint8_t mode); + +#endif diff --git a/grbl/gcode.c b/grbl/gcode.c index cc5e9e39d..ff0c75954 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -288,6 +288,10 @@ uint8_t gc_execute_line(char *line) gc_block.modal.override = OVERRIDE_PARKING_MOTION; break; #endif + case 62: case 63: case 64: case 65: + dword_bit = MODAL_GROUP_M10; + gc_block.non_modal_command = int_value; + break; default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command] } @@ -353,9 +357,15 @@ uint8_t gc_execute_line(char *line) #endif break; case 'L': dword_bit = DWORD_L; gc_block.values.l = int_value; break; - case 'N': dword_bit = DWORD_N; gc_block.values.n = trunc(value); break; - case 'P': dword_bit = DWORD_P; gc_block.values.p = value; break; - // NOTE: For certain commands, P value must be an integer, but none of these commands are supported. + case 'N': dword_bit = DWORD_N; gc_block.values.n = trunc(value); + case 'P': dword_bit = DWORD_P; + // NOTE: For certain commands, P value must be an integer, This is the case of Digital output M26-M65 + if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { + gc_block.values.p = int_value; + } else { + gc_block.values.p = value; + } + break; // case 'Q': // Not supported case 'R': dword_bit = DWORD_R; gc_block.values.r = value; break; case 'S': dword_bit = DWORD_S; gc_block.values.s = value; break; @@ -1188,6 +1198,13 @@ uint8_t gc_execute_line(char *line) // [21. Program flow ]: No error checks required. + // [22. Digital output ]: P value missing, P must be: 0<=P<=3 (value < 0 is aleready checked) + if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { + if (bit_isfalse(value_dwords,dwbit(DWORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] + if (gc_block.values.p > 3) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } + bit_false(value_dwords,dwbit(DWORD_P)); + } + // [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc // radius mode, or axis words that aren't used in the block. if (gc_parser_flags & GC_PARSER_JOG_MOTION) { @@ -1488,12 +1505,32 @@ uint8_t gc_execute_line(char *line) system_flag_wco_change(); // Set to refresh immediately just in case something altered. spindle_set_state(SPINDLE_DISABLE,0.0); coolant_set_state(COOLANT_DISABLE); + digital_set_state(DIGITAL_OUTPUT_STATE_OFF); } report_feedback_message(MESSAGE_PROGRAM_END); } gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; // Reset program flow. } + // [22. Digital output ]: P value missing. P is negative (done.) NOTE: See below. + if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { + uint8_t digital_mode = digital_get_state(); + switch(gc_block.non_modal_command) { + case NON_MODAL_DIGITAL_SYNC_ON: + digital_sync(digital_mode | bit((uint8_t) gc_block.values.p)); + break; + case NON_MODAL_DIGITAL_SYNC_OFF: + digital_sync(digital_mode & ~(bit((uint8_t) gc_block.values.p))); + break; + case NON_MODAL_DIGITAL_IMMEDIATE_ON: + digital_set_state(digital_mode | bit((uint8_t) gc_block.values.p)); + break; + case NON_MODAL_DIGITAL_IMMEDIATE_OFF: + digital_set_state(digital_mode & ~(bit((uint8_t) gc_block.values.p))); + break; + } + } + // TODO: % to denote start of program. return(STATUS_OK); diff --git a/grbl/gcode.h b/grbl/gcode.h index 2fb054260..1ce529f16 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -46,6 +46,7 @@ #define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning #define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control #define MODAL_GROUP_M9 14 // [M56] Override control +#define MODAL_GROUP_M10 15 // [M62-M65] Digital output -Non-modal // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. @@ -133,6 +134,12 @@ #define OVERRIDE_DISABLED 1 // Parking disabled. #endif +// Modal Group M10: Digital output (non modal) +#define NON_MODAL_DIGITAL_SYNC_ON 62 // M62 +#define NON_MODAL_DIGITAL_SYNC_OFF 63 // M63 +#define NON_MODAL_DIGITAL_IMMEDIATE_ON 64 // M64 +#define NON_MODAL_DIGITAL_IMMEDIATE_OFF 65 // M65 + // Modal Group G12: Active work coordinate system // N/A: Stores coordinate system value (54-59) to change to. diff --git a/grbl/grbl.h b/grbl/grbl.h index 9a4d99c5f..731800b35 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.1t" -#define GRBL_VERSION_BUILD "20210510" +#define GRBL_VERSION "1.2a" +#define GRBL_VERSION_BUILD "20210912" // Define standard libraries used by Grbl. #include @@ -48,6 +48,7 @@ #include "cpu_map.h" #include "planner.h" #include "coolant_control.h" +#include "digital_control.h" #include "eeprom.h" #include "gcode.h" #include "limits.h" diff --git a/grbl/main.c b/grbl/main.c index 8ae294ea0..eaa6de12c 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -508,6 +508,7 @@ int main(void) gc_init(); // Set g-code parser to default state spindle_init(); coolant_init(); + digital_init(); limits_init(); probe_init(); sleep_init(); From 6656e7d7c2c19f01997163baa6bd88c8eff08f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Thu, 16 Sep 2021 20:12:17 +0200 Subject: [PATCH 058/101] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..fcc7f0ce5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: fra589 +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 3a53e7ff3e801bc10707a224188bd3d1059f6095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Thu, 16 Sep 2021 20:18:41 +0200 Subject: [PATCH 059/101] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index fcc7f0ce5..556d0d5b4 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: fra589 +patreon: user?u=61775893 open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel From 6ae3eceb309adaca4b00707434ea5c34936be559 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 16 Sep 2021 20:31:04 +0200 Subject: [PATCH 060/101] code cleaning and digital output report --- Makefile | 2 +- grbl/config.h | 26 -------------------------- grbl/gcode.c | 8 +++++--- grbl/report.c | 16 +++++++++------- 4 files changed, 15 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 61eaf9782..0f4c1d6ae 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ DEVICE ?= atmega2560 CLOCK = 16000000L ###PROGRAMMER ?= -c avrisp2 -P usb -PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB0 +PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB1 SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ print.c probe.c report.c system.c sleep.c jog.c diff --git a/grbl/config.h b/grbl/config.h index d93e7784e..3d701450f 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -248,12 +248,6 @@ // coordinates through Grbl '$#' print parameters. #define MESSAGE_PROBE_COORDINATES // Enabled by default. Comment to disable. -// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered, -// immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until -// the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the -// previous tool path, as if nothing happened. -#define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable. - // After the safety door switch has been toggled and restored, this setting sets the power-up delay // between restoring the spindle and coolant and resuming the cycle. #define SAFETY_DOOR_SPINDLE_DELAY 4.0 // Float (seconds) @@ -339,7 +333,6 @@ #define DEFAULT_RAPID_OVERRIDE 100 // 100%. Don't change this value. #define RAPID_OVERRIDE_MEDIUM 50 // Percent of rapid (1-99). Usually 50%. #define RAPID_OVERRIDE_LOW 25 // Percent of rapid (1-99). Usually 25%. -// #define RAPID_OVERRIDE_EXTRA_LOW 5 // *NOT SUPPORTED* Percent of rapid (1-99). Usually 5%. #define DEFAULT_SPINDLE_SPEED_OVERRIDE 100 // 100%. Don't change this value. #define MAX_SPINDLE_SPEED_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%. @@ -540,25 +533,6 @@ // these string storage locations won't corrupt one another. // #define EEPROM_LINE_SIZE 80 // Uncomment to override defaults in settings.h -// Toggles XON/XOFF software flow control for serial communications. Not officially supported -// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware -// on these chips do not support XON/XOFF flow control characters and the intermediate buffer -// in the chips cause latency and overflow problems with standard terminal programs. However, -// using specifically-programmed UI's to manage this latency problem has been confirmed to work. -// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard -// terminal programs since their firmware correctly manage these XON/XOFF characters. In any -// case, please report any successes to grbl administrators! -// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable. - -// A simple software debouncing feature for hard limit switches. When enabled, the interrupt -// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check -// the limit pin state after a delay of about 32msec. This can help with CNC machines with -// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with -// electrical interference on the signal cables from external sources. It's recommended to first -// use shielded signal cables with their shielding connected to ground (old USB/computer cables -// work well and are cheap to find) and wire in a low-pass circuit into each limit pin. -// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable. - // Configures the position after a probing cycle during Grbl's check mode. Disabled sets // the position to the probe target, when enabled sets the position to the start position. // #define SET_CHECK_MODE_PROBE_TO_START // Default disabled. Uncomment to enable. diff --git a/grbl/gcode.c b/grbl/gcode.c index ff0c75954..c5a323839 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -26,7 +26,8 @@ // arbitrary value, and some GUIs may require more. So we increased it based on a max safe // value when converting a float (7.2 digit precision)s to an integer. #define MAX_LINE_NUMBER 10000000 -#define MAX_TOOL_NUMBER 255 // Limited by max unsigned 8-bit value +#define MAX_TOOL_NUMBER 255 // Limited by max unsigned 8-bit value +#define MAX_DIGITAL_OUTPUT 3 // 4 defined digital IO output (0 to 3) #define AXIS_COMMAND_NONE 0 #define AXIS_COMMAND_NON_MODAL 1 @@ -289,6 +290,7 @@ uint8_t gc_execute_line(char *line) break; #endif case 62: case 63: case 64: case 65: + // M62, M63, M64, M65 dword_bit = MODAL_GROUP_M10; gc_block.non_modal_command = int_value; break; @@ -1198,10 +1200,10 @@ uint8_t gc_execute_line(char *line) // [21. Program flow ]: No error checks required. - // [22. Digital output ]: P value missing, P must be: 0<=P<=3 (value < 0 is aleready checked) + // [22. Digital output ]: P value missing, P must be: 0<=P<=MAX_DIGITAL_OUTPUT (value < 0 is aleready checked) if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { if (bit_isfalse(value_dwords,dwbit(DWORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] - if (gc_block.values.p > 3) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } + if (gc_block.values.p > MAX_DIGITAL_OUTPUT) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); } bit_false(value_dwords,dwbit(DWORD_P)); } diff --git a/grbl/report.c b/grbl/report.c index 721fad978..5fbc77565 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -529,15 +529,9 @@ void report_build_info(char *line) #ifdef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES serial_write('A'); #endif - #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN - serial_write('D'); - #endif #ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED serial_write('0'); #endif - #ifdef ENABLE_SOFTWARE_DEBOUNCE - serial_write('S'); - #endif #ifdef ENABLE_PARKING_OVERRIDE_CONTROL serial_write('R'); #endif @@ -743,7 +737,8 @@ void report_realtime_status() uint8_t sp_state = spindle_get_state(); uint8_t cl_state = coolant_get_state(); - if (sp_state || cl_state) { + uint8_t dg_state = digital_get_state(); + if (sp_state || cl_state || dg_state) { printPgmString(PSTR("|A:")); if (sp_state) { // != SPINDLE_STATE_DISABLE if (sp_state == SPINDLE_STATE_CW) { serial_write('S'); } // CW @@ -751,6 +746,13 @@ void report_realtime_status() } if (cl_state & COOLANT_STATE_FLOOD) { serial_write('F'); } if (cl_state & COOLANT_STATE_MIST) { serial_write('M'); } + if (dg_state) { // One or more digital output is active + serial_write('D'); + if (dg_state & DIGITAL_OUTPUT_STATE_P3) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P2) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P1) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P0) { serial_write('1'); } else {serial_write('0');} + } } } #endif From 5369b7b3764dc67120863d78a39dffb899119498 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 19 Sep 2021 16:02:36 +0200 Subject: [PATCH 061/101] Adding ability to run spindle PWM on D11 --- grbl/config.h | 8 ++++--- grbl/cpu_map.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 4 deletions(-) mode change 100644 => 100755 grbl/config.h mode change 100644 => 100755 grbl/cpu_map.h diff --git a/grbl/config.h b/grbl/config.h old mode 100644 new mode 100755 index 3d701450f..1c73d4bc7 --- a/grbl/config.h +++ b/grbl/config.h @@ -74,11 +74,13 @@ #endif // Chose the spindle pin output : -// SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 -// SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) +// SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 +// SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) +// SPINDLE_PWM_ON_D11 => 0-5v 8bits PWM on RAMPS Servo 1 signal (Mega 2560 D11) // Uncomment the line which correspond to your hardware #define SPINDLE_PWM_ON_D8 -// #define SPINDLE_PWM_ON_D6 +//#define SPINDLE_PWM_ON_D6 +//#define SPINDLE_PWM_ON_D11 // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h old mode 100644 new mode 100755 index c268b227c..946e79f06 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -257,7 +257,36 @@ #define PROBE_BIT 7 // MEGA2560 Analog Pin 15 #define PROBE_MASK (1< Date: Sun, 19 Sep 2021 16:14:32 +0200 Subject: [PATCH 062/101] Update grbl-Mega-5X_Wiring.svg for the M62-M65 capability --- doc/images/grbl-Mega-5X_Wiring.svg | 304 +++++++++++++++++++---------- 1 file changed, 198 insertions(+), 106 deletions(-) diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg index 3c3b637ed..74358b294 100644 --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -1,6 +1,4 @@ - - grbl-Mega-5X pinout @@ -27,7 +25,7 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.813" + inkscape:zoom="0.802" inkscape:cx="500" inkscape:cy="500" inkscape:document-units="px" @@ -35,11 +33,12 @@ showgrid="true" units="px" showguides="false" - inkscape:window-width="1366" - inkscape:window-height="708" + inkscape:window-width="1680" + inkscape:window-height="993" inkscape:window-x="0" inkscape:window-y="0" - inkscape:window-maximized="1"> + inkscape:window-maximized="1" + inkscape:document-rotation="0"> @@ -93,6 +92,15 @@ height="1000" x="0" y="91.250053" /> + axe 1 motor axe 2 motor D10 D09 axe 4 motor axe 5 motor X min end stop (D3) + x="834.89307" + y="132.18745" + style="font-size:14.6667px">X min end stop (D3) X max end stop (D2) + x="834.89307" + y="152.18745" + style="font-size:14.6667px">X max end stop (D2) Y min end stop (D14) + x="835.35852" + y="172.18745" + style="font-size:14.6667px">Y min end stop (D14) Y max end stop (D15) + x="835.35852" + y="192.18742" + style="font-size:14.6667px">Y max end stop (D15) Z min end stop (D18) + x="834.67102" + y="212.18742" + style="font-size:14.6667px">Z min end stop (D18) Z max end stop (D19) + x="914.43176" + y="232.18742" + style="font-size:14.6667px;text-align:center;text-anchor:middle">Z max end stop (D19) Aux-2 Aux-1 Aux-3 Axe 5 max end stop (D59) + style="font-size:14.6667px">Axe 5 max end stop (D59) Axe 4 max end stop (D40) + style="font-size:14.6667px">Axe 4 max end stop (D40) Axe 4 min end stop (D42) + style="font-size:14.6667px">Axe 4 min end stop (D42) Axe 5 min end stop (D44) + style="font-size:14.6667px">Axe 5 min end stop (D44) Spindle enable (D4) + style="font-size:14.6667px">Spindle enable (D4) Spindle direction (D5) + style="font-size:14.6667px">Spindle direction (D5) Coolant flood + style="font-size:14.6667px">Coolant flood Coolant mist + style="font-size:14.6667px">Coolant mist @@ -2502,7 +2510,7 @@ sodipodi:nodetypes="ccc" /> Feed hold switch (A10) + style="font-size:14.6667px">Feed hold switch (A10) Reset switch (A9) + style="font-size:14.6667px">Reset switch (A9) Cycle start switch (A11) + style="font-size:14.6667px">Cycle start switch (A11) Safety door switch (A12) + style="font-size:14.6667px">Safety door switch (A12) @@ -2571,24 +2579,24 @@ id="text3074" y="549.21771" x="5.1544418" - style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:25px;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none" + style="font-style:normal;font-weight:normal;font-size:18.6667px;line-height:25px;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none" xml:space="preserve">Spindle PWM Probe switch (A15) + x="901.92065" + y="253.30461" + style="font-size:14.6667px;text-align:center;text-anchor:middle">Probe switch (A15) grbl-Mega-5X pinout 5V GND + style="font-size:9.33333px">GND + digital output 0 + digital output 1 + digital output 2 + digital output 3 + + + + + M62-M65digital outputs From ec0471874f7909a643b2fc30bb2e414011d926f4 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 19 Sep 2021 16:30:52 +0200 Subject: [PATCH 063/101] Update grbl-Mega-5X_Wiring.svg for the M62-M65 capability --- doc/images/grbl-Mega-5X_Wiring.svg | 124 +++++++++++++++-------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg index 74358b294..fc0f78d0d 100644 --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -7,9 +7,9 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="1000" + width="1040" height="1000" - viewBox="0 0 1000 1000" + viewBox="0 0 1040 1000" version="1.1" id="svg8" inkscape:version="1.0.2 (e86c870879, 2021-01-15)" @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.802" - inkscape:cx="500" - inkscape:cy="500" + inkscape:zoom="0.56709964" + inkscape:cx="295.34878" + inkscape:cy="391.65248" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -38,10 +38,16 @@ inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:document-rotation="0"> + inkscape:document-rotation="0" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0"> + id="grid856" + originx="0" + originy="-7.0405762e-05" /> @@ -84,25 +90,25 @@ inkscape:label="Calque 1" inkscape:groupmode="layer" id="layer1" - transform="translate(0,-91.249983)"> + transform="translate(0,-91.250053)"> + style="fill:#007f00;fill-opacity:0.960854;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -2854,91 +2860,91 @@ @@ -2991,22 +2997,22 @@ id="rect1538" transform="translate(0,91.249983)" d="m 1470,415 v 45 h 80 v -45 h -10 v 25 h -10 v -25 h -40 v 25 h -10 v -25 z" - style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.89999998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" inkscape:connector-curvature="0" /> + style="fill:#e54241;fill-opacity:1;stroke:none;stroke-width:0.9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -3206,20 +3212,20 @@ d="M 738.72686,710.74996 583,1045.25 h -83" style="fill:none;stroke:#de7f00;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -3233,7 +3239,7 @@ id="tspan2437-75" x="864.16895" y="354.56454" - style="font-size:14.6667px">digital output 0 + style="font-size:14.6667px">digital output 0 (D16) digital output 1 + style="font-size:14.6667px">digital output 1 (D17) digital output 2 + style="font-size:14.6667px">digital output 2 (D23) digital output 3 + style="font-size:14.6667px">digital output 3 (D25) From 15ef4ee967651665490b3f0e8d85e1bd3ec97980 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 19 Sep 2021 17:29:43 +0200 Subject: [PATCH 064/101] Add .vscode folder and platformio.ini to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9755d07aa..c184355f6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ *.DS_Store *.d .grbl* +.vscode +platformio.ini README.md From 00bcbc33b9c7e2d01ea33d79542b6f74a8be7ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Sun, 19 Sep 2021 18:25:34 +0200 Subject: [PATCH 065/101] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 620d2604d..036fff71f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,3 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! - -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/pools/c/842hNSm2It) - From d6c0383d61646ec4b7430b532ed1993527f8b1de Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 26 Sep 2021 10:45:30 +0200 Subject: [PATCH 066/101] fix grbl-Mega-5X's issue #214: Wrong state RUN when jogging right after probing --- Makefile | 2 +- grbl/grbl.h | 4 ++-- grbl/motion_control.c | 2 ++ grbl/protocol.c | 2 +- grbl/report.c | 45 ++++++++++++++++++++++++++++++------------- grbl/report.h | 3 ++- grbl/system.h | 24 +++++++++++------------ 7 files changed, 52 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 0f4c1d6ae..61eaf9782 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ DEVICE ?= atmega2560 CLOCK = 16000000L ###PROGRAMMER ?= -c avrisp2 -P usb -PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB1 +PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB0 SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ print.c probe.c report.c system.c sleep.c jog.c diff --git a/grbl/grbl.h b/grbl/grbl.h index 731800b35..739350555 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2a" -#define GRBL_VERSION_BUILD "20210912" +#define GRBL_VERSION "1.2b" +#define GRBL_VERSION_BUILD "20210926" // Define standard libraries used by Grbl. #include diff --git a/grbl/motion_control.c b/grbl/motion_control.c index 1037f56dd..eccd80ec9 100644 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -505,6 +505,8 @@ uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_ protocol_execute_realtime(); // Check and execute run-time commands // Reset the stepper and planner buffers to remove the remainder of the probe motion. + sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation + // fix grbl-Mega-5X's issue #214 st_reset(); // Reset step segment buffer. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. plan_sync_position(); // Sync planner position to current machine position. diff --git a/grbl/protocol.c b/grbl/protocol.c index 76d9f45b3..b19e2d6d1 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -53,8 +53,8 @@ void protocol_main_loop() report_feedback_message(MESSAGE_ALARM_LOCK); sys.state = STATE_ALARM; // Ensure alarm state is set. } else { - // Check if the safety door is open. sys.state = STATE_IDLE; + // Check if the safety door is open. if (system_check_safety_door_ajar()) { bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state. diff --git a/grbl/report.c b/grbl/report.c index 5fbc77565..f25ed3366 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -28,7 +28,7 @@ */ #include "grbl.h" - +#include // Internal report utilities to reduce flash with repetitive tasks turned into functions. void report_util_setting_prefix(uint8_t n) { serial_write('$'); print_uint8_base10(n); serial_write('='); } @@ -705,6 +705,7 @@ void report_realtime_status() } } #endif + #ifdef DEBUG printPgmString(PSTR("|Dbg:")); // Other debugs here... @@ -763,16 +764,34 @@ void report_realtime_status() #ifdef DEBUG - void report_realtime_debug(char *line, uint8_t val) - { - printPgmString(PSTR("{debug(")); - printString(line); - printPgmString(PSTR("|val:")); - print_uint8_base10(val); - printPgmString(PSTR("|Amask:")); - print_uint8_base10(axis_A_mask); - // print realtime debug values here - printPgmString(PSTR(")}")); - report_util_line_feed(); + +// Report debug string on serial +void report_debug_string(char *line) +{ + printPgmString(PSTR("{debug(")); + printString(line); + printPgmString(PSTR(")}")); + report_util_line_feed(); +} + +// Report debug int value +// This function accept the variable's name string as optional argument +void report_debug_int(uint8_t val, ...) +{ + va_list args; + char *valname; + + va_start(args, val); + valname = va_arg(args, char *); + va_end(args); + + printPgmString(PSTR("{debug(")); + if ( valname != NULL ) { + printString(valname); + printString(" = "); } -#endif + print_uint8_base10(val); + printPgmString(PSTR(")}")); + report_util_line_feed(); +} +#endif // DEBUG diff --git a/grbl/report.h b/grbl/report.h index 1e5f1db9b..f9833e1cf 100644 --- a/grbl/report.h +++ b/grbl/report.h @@ -125,7 +125,8 @@ void report_execute_startup_message(char *line, uint8_t status_code); void report_build_info(char *line); #ifdef DEBUG - void report_realtime_debug(char *line, uint8_t val); + void report_debug_string(char *line); + void report_debug_int(uint8_t val, ...); #endif #endif diff --git a/grbl/system.h b/grbl/system.h index a11615173..5296d1108 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -28,14 +28,14 @@ // NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default // flags are always false, so the realtime protocol only needs to check for a non-zero value to // know when there is a realtime command to execute. -#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 -#define EXEC_CYCLE_START bit(1) // bitmask 00000010 -#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 -#define EXEC_FEED_HOLD bit(3) // bitmask 00001000 -#define EXEC_RESET bit(4) // bitmask 00010000 -#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000 -#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000 -#define EXEC_SLEEP bit(7) // bitmask 10000000 +#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 1 +#define EXEC_CYCLE_START bit(1) // bitmask 00000010 2 +#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 4 +#define EXEC_FEED_HOLD bit(3) // bitmask 00001000 8 +#define EXEC_RESET bit(4) // bitmask 00010000 16 +#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000 32 +#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000 64 +#define EXEC_SLEEP bit(7) // bitmask 10000000 128 // Alarm executor codes. Valid values (1-255). Zero is reserved. #define EXEC_ALARM_HARD_LIMIT 1 @@ -96,10 +96,10 @@ // Define step segment generator state flags. #define STEP_CONTROL_NORMAL_OP 0 // Must be zero. -#define STEP_CONTROL_END_MOTION bit(0) -#define STEP_CONTROL_EXECUTE_HOLD bit(1) -#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2) -#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3) +#define STEP_CONTROL_END_MOTION bit(0) // 00000001 1 +#define STEP_CONTROL_EXECUTE_HOLD bit(1) // 00000010 2 +#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2) // 00000100 4 +#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3) // 00001000 8 // Define control pin index for Grbl internal use. Pin maps may change, but these values don't. #define N_CONTROL_PIN 4 From d403c5237b305699fbc229b137b0a1ea15244365 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 27 Sep 2021 09:59:11 +0200 Subject: [PATCH 067/101] =?UTF-8?q?Mise=20=C3=A0=20jour=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 620d2604d..036fff71f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,3 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! - -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/pools/c/842hNSm2It) - From b9399b9e0b622cdc5c21ec1b66765ecbdfbb375b Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 27 Sep 2021 10:00:44 +0200 Subject: [PATCH 068/101] Suppress spindle on D11 due to stepper timer conflict --- grbl/config.h | 2 -- grbl/cpu_map.h | 27 --------------------------- grbl/grbl.h | 4 ++-- 3 files changed, 2 insertions(+), 31 deletions(-) mode change 100644 => 100755 grbl/grbl.h diff --git a/grbl/config.h b/grbl/config.h index 1c73d4bc7..28764b1eb 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -76,11 +76,9 @@ // Chose the spindle pin output : // SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 // SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) -// SPINDLE_PWM_ON_D11 => 0-5v 8bits PWM on RAMPS Servo 1 signal (Mega 2560 D11) // Uncomment the line which correspond to your hardware #define SPINDLE_PWM_ON_D8 //#define SPINDLE_PWM_ON_D6 -//#define SPINDLE_PWM_ON_D11 // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 946e79f06..60397d794 100755 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -341,33 +341,6 @@ #define SPINDLE_PWM_PORT PORTH #define SPINDLE_PWM_BIT 3 // MEGA2560 Digital Pin 6 - #elif defined (SPINDLE_PWM_ON_D11) - - // Set Timer up to use TIMER1 which is attached to Digital Pin 11 - Ramps Servo 1 - #define SPINDLE_PWM_MAX_VALUE 255.0 // Translates to about 1.9 kHz PWM frequency at 1/8 prescaler - #ifndef SPINDLE_PWM_MIN_VALUE - #define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero. - #endif - #define SPINDLE_PWM_OFF_VALUE 0 - #define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE) - - //Control Digital Pin 6 which is Servo 2 signal pin on Ramps 1.4 board - #define SPINDLE_TCCRA_REGISTER TCCR1A - #define SPINDLE_TCCRB_REGISTER TCCR1B - #define SPINDLE_OCR_REGISTER OCR1A - #define SPINDLE_COMB_BIT COM1A1 - - // 1/8 Prescaler, 16-bit Fast PWM mode - #define SPINDLE_TCCRA_INIT_MASK (1< From 2fc86a656311f71cf9be0faa4eb9e476c85cc539 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 27 Sep 2021 10:02:06 +0200 Subject: [PATCH 069/101] Force PWM output to 0 before pin assign to avoid flash on laser --- grbl/spindle_control.c | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 grbl/spindle_control.c diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c old mode 100644 new mode 100755 index 8982dfa2e..17af735d6 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -27,6 +27,8 @@ static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversi void spindle_init() { + // Force PWM output zero to avoid PWM output flash on laser output + SPINDLE_TCCRA_REGISTER &= ~(1< Date: Mon, 27 Sep 2021 10:16:38 +0200 Subject: [PATCH 070/101] Update TODO --- TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO b/TODO index 7be8023d9..7667e7f3b 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,6 @@ backlash compensation CoreXY homing bug correction grbl-Mega-5X #124 / grbl-Mega #49 Second serial port communications Spindle enable (laser enable) did not restore after feed hold, bug #79 - +Implementy analog output (PWM output) M67/M68 From 8e0609a873f95f0aceaaf59db3c7c0e5c47a6f63 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 30 Sep 2021 08:31:39 +0200 Subject: [PATCH 071/101] Bug correction: Line numbering N GCode command give error 36 --- grbl/gcode.c | 2 +- grbl/grbl.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grbl/gcode.c b/grbl/gcode.c index c5a323839..5ac70b126 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -359,7 +359,7 @@ uint8_t gc_execute_line(char *line) #endif break; case 'L': dword_bit = DWORD_L; gc_block.values.l = int_value; break; - case 'N': dword_bit = DWORD_N; gc_block.values.n = trunc(value); + case 'N': dword_bit = DWORD_N; gc_block.values.n = trunc(value);break; case 'P': dword_bit = DWORD_P; // NOTE: For certain commands, P value must be an integer, This is the case of Digital output M26-M65 if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { diff --git a/grbl/grbl.h b/grbl/grbl.h index 36b0ee35d..08e0f49a1 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2c" -#define GRBL_VERSION_BUILD "20210927" +#define GRBL_VERSION "1.2d" +#define GRBL_VERSION_BUILD "20210930" // Define standard libraries used by Grbl. #include From a3b048c3865ef4c1fbb4aa6e16d3c48d73ecef48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Mon, 15 Nov 2021 19:21:56 +0100 Subject: [PATCH 072/101] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 556d0d5b4..3cbc8dec8 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: user?u=61775893 +patreon: # open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel From 21b603d40fd7bb7885b355417a01c109e8917e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Mon, 15 Nov 2021 20:03:05 +0100 Subject: [PATCH 073/101] Update FUNDING.yml --- .github/FUNDING.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3cbc8dec8..53c9a0669 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,12 +1,12 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # +patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username +liberapay: fra589 issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 9b75dfa2f19d9b8cd7460337dd0a1fbc2f856924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Mon, 15 Nov 2021 20:05:16 +0100 Subject: [PATCH 074/101] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 53c9a0669..8cc6ea15f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -6,7 +6,7 @@ open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: fra589 +liberapay: Gauthier issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 8d17a517e63582aa3f61dcdbf1641adf07d2e8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 16 Nov 2021 19:42:17 +0100 Subject: [PATCH 075/101] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 036fff71f..fc7106b1f 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,4 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! +https://www.paypal.com/donate/?business=CZZN52UPPVHCW&no_recurring=0&item_name=Grbl-Mega-5X+%26+cn5X%2B%2B+donations¤cy_code=EUR From 074d8263d21a6be669ce051aa283b184317baace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 16 Nov 2021 19:44:53 +0100 Subject: [PATCH 076/101] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc7106b1f..bd89ce508 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,4 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! -https://www.paypal.com/donate/?business=CZZN52UPPVHCW&no_recurring=0&item_name=Grbl-Mega-5X+%26+cn5X%2B%2B+donations¤cy_code=EUR + From 2884b59f371b0aee4a6cfe4f47d5549b06519e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 16 Nov 2021 19:51:15 +0100 Subject: [PATCH 077/101] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd89ce508..f4d9ab17d 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,4 @@ List of Supported G-Codes in Grbl v1.1: ------------- Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! - +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?business=CZZN52UPPVHCW&no_recurring=0&item_name=Grbl-Mega-5X+%26+cn5X%2B%2B+donations¤cy_code=EUR) From 9f43d8e6f954f3e99b54ec3455e603301b7c7686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 16 Nov 2021 19:52:37 +0100 Subject: [PATCH 078/101] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f4d9ab17d..09c8282e8 100644 --- a/README.md +++ b/README.md @@ -71,5 +71,4 @@ List of Supported G-Codes in Grbl v1.1: ``` ------------- -Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?business=CZZN52UPPVHCW&no_recurring=0&item_name=Grbl-Mega-5X+%26+cn5X%2B%2B+donations¤cy_code=EUR) +Grbl-Mega-5X is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you! [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?business=CZZN52UPPVHCW&no_recurring=0&item_name=Grbl-Mega-5X+%26+cn5X%2B%2B+donations¤cy_code=EUR) From 4ae22bca808ad1270383cce6239f2ddad5708701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 25 Jan 2022 18:25:16 +0100 Subject: [PATCH 079/101] Add comment on axis definition Adding comment about the need to issue the $RST=* Grbl's command after changing the axis definition. --- grbl/config.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index 28764b1eb..d6dd1aff5 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -43,17 +43,24 @@ // #define BAUD_RATE 230400 #define BAUD_RATE 115200 -// Axis array index values. Must start with 0 and be continuous. -#define N_AXIS 5 // Number of axes (3 to 6) -#define N_AXIS_LINEAR 3 // Number of linears axis - +//---------------------------------------------------------------------- +// Axis definitions : +//---------------------------------------------------------------------- +// IMPORTANT: When changing the axis definitions (axis numbers N_AXIS, +// linears axis number N_AXIS_LINEAR or axes names AXIS_*_NAME, +// don't forget to issue the reset factory defaults Grbl command: $RST=* +// if you forget the $RST=* command after change, Grbl may have +// unpredictable behavior! +//---------------------------------------------------------------------- +#define N_AXIS 5 // Number of axes (3 to 6) +#define N_AXIS_LINEAR 3 // Number of linears axis, must be <= N_AXIS +// Axis indexing and names #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. #define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V, W, D, E & H. #define AXIS_2 1 #define AXIS_2_NAME 'Y' #define AXIS_3 2 #define AXIS_3_NAME 'Z' - #if N_AXIS <3 #error "N_AXIS must be >= 3. N_AXIS < 3 is not implemented." #endif @@ -72,6 +79,7 @@ #if N_AXIS > 6 #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif +//---------------------------------------------------------------------- // Chose the spindle pin output : // SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 From 84951786a3c36a9261aecf98009ede632e9b0e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Thu, 27 Jan 2022 11:33:31 +0100 Subject: [PATCH 080/101] Update Wiki link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09c8282e8..8d89f58ff 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Grbl includes full acceleration management with look ahead. That means the contr * [Licensing](https://github.com/fra589/grbl-Mega-5X/blob/edge/COPYING): Grbl is free software, released under the GPLv3 license. -* For more information and help, check out our **[Wiki pages!](https://github.com/gnea/grbl/wiki)** If you find that the information is out-dated, please to help us keep it updated by editing it or notifying our community! Thanks! +* For more information and help, check out our **[Wiki pages!](https://github.com/fra589/grbl-Mega-5X/wiki)** If you find that the information is out-dated, please to help us keep it updated by editing it or notifying our community! Thanks! * Lead Developer: Gauthier Brière (France) aka @fra589 From d5624a74cacc6d483fde92f88ca019bb99f92002 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 18 Feb 2022 15:23:27 +0100 Subject: [PATCH 081/101] Update Makefile --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 61eaf9782..fd72f77e7 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,8 @@ DEVICE ?= atmega2560 CLOCK = 16000000L ###PROGRAMMER ?= -c avrisp2 -P usb -PROGRAMMER ?= -D -v -c avrisp2 -P /dev/ttyUSB0 +DEVICE_PORT ?= /dev/ttyUSB0 +PROGRAMMER ?= -D -v -c avrisp2 -P $(DEVICE_PORT) SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ print.c probe.c report.c system.c sleep.c jog.c From 8a469363e0119667e0a921684cbfdaba7961313f Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 18 Feb 2022 15:38:50 +0100 Subject: [PATCH 082/101] Adding ability to invert PWM signal (INVERT_PWM_VALUES) --- grbl/config.h | 7 +++++++ grbl/grbl.h | 4 ++-- grbl/spindle_control.c | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index d6dd1aff5..790e91e71 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -88,6 +88,13 @@ #define SPINDLE_PWM_ON_D8 //#define SPINDLE_PWM_ON_D6 +// PWM signal inversion: +// In case of particular electronics, it may be necessary to invert the values +// of the PWM signal of the spindle. For example, if the minimum spindle +// rpm is 1 and maximum is 1000, M3S250 will output 75% instead of 25% and +// M3S750 will output 25% instead of 75%. Disabled by default +#define INVERT_PWM_VALUES + // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported // by Grbl to the correct axis name. diff --git a/grbl/grbl.h b/grbl/grbl.h index 08e0f49a1..747d72470 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2d" -#define GRBL_VERSION_BUILD "20210930" +#define GRBL_VERSION "1.2e" +#define GRBL_VERSION_BUILD "20220218" // Define standard libraries used by Grbl. #include diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index 17af735d6..091294ec6 100755 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -165,7 +165,11 @@ void spindle_set_speed(uint16_t pwm_value) sys.spindle_speed = rpm; pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE; } - return(pwm_value); + #ifdef INVERT_PWM_VALUES + return(pwm_value); + #else + return(SPINDLE_PWM_MAX_VALUE - pwm_value); + #endif } #endif From 0f010fdb0a3335b60eca75c152ec2a92e708a667 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 18 Feb 2022 15:38:50 +0100 Subject: [PATCH 083/101] Adding ability to invert PWM signal values (INVERT_PWM_VALUES) --- grbl/config.h | 7 +++++++ grbl/grbl.h | 4 ++-- grbl/spindle_control.c | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/grbl/config.h b/grbl/config.h index d6dd1aff5..790e91e71 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -88,6 +88,13 @@ #define SPINDLE_PWM_ON_D8 //#define SPINDLE_PWM_ON_D6 +// PWM signal inversion: +// In case of particular electronics, it may be necessary to invert the values +// of the PWM signal of the spindle. For example, if the minimum spindle +// rpm is 1 and maximum is 1000, M3S250 will output 75% instead of 25% and +// M3S750 will output 25% instead of 75%. Disabled by default +#define INVERT_PWM_VALUES + // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported // by Grbl to the correct axis name. diff --git a/grbl/grbl.h b/grbl/grbl.h index 08e0f49a1..747d72470 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2d" -#define GRBL_VERSION_BUILD "20210930" +#define GRBL_VERSION "1.2e" +#define GRBL_VERSION_BUILD "20220218" // Define standard libraries used by Grbl. #include diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index 17af735d6..091294ec6 100755 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -165,7 +165,11 @@ void spindle_set_speed(uint16_t pwm_value) sys.spindle_speed = rpm; pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE; } - return(pwm_value); + #ifdef INVERT_PWM_VALUES + return(pwm_value); + #else + return(SPINDLE_PWM_MAX_VALUE - pwm_value); + #endif } #endif From 26c7c6ecc3e2ecb83b06a563968035d756ec9fde Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 18 Feb 2022 15:47:37 +0100 Subject: [PATCH 084/101] INVERT_PWM_VALUES disabled by default --- grbl/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grbl/config.h b/grbl/config.h index 790e91e71..6563bdd40 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -93,7 +93,7 @@ // of the PWM signal of the spindle. For example, if the minimum spindle // rpm is 1 and maximum is 1000, M3S250 will output 75% instead of 25% and // M3S750 will output 25% instead of 75%. Disabled by default -#define INVERT_PWM_VALUES +//#define INVERT_PWM_VALUES // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported From 23438b3c8d314c5ca9e223c06e93db7149413a0e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 24 Feb 2022 10:42:56 +0100 Subject: [PATCH 085/101] Issue #253 patch (serial.c need volatiles variables) --- grbl/grbl.h | 2 +- grbl/serial.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index 747d72470..7543e6dfb 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.2e" -#define GRBL_VERSION_BUILD "20220218" +#define GRBL_VERSION_BUILD "20220224" // Define standard libraries used by Grbl. #include diff --git a/grbl/serial.c b/grbl/serial.c index 50748dc26..222bb2d53 100644 --- a/grbl/serial.c +++ b/grbl/serial.c @@ -25,8 +25,8 @@ #define RX_RING_BUFFER (RX_BUFFER_SIZE+1) #define TX_RING_BUFFER (TX_BUFFER_SIZE+1) -uint8_t serial_rx_buffer[RX_RING_BUFFER]; -uint8_t serial_rx_buffer_head = 0; +volatile uint8_t serial_rx_buffer[RX_RING_BUFFER]; +volatile uint8_t serial_rx_buffer_head = 0; volatile uint8_t serial_rx_buffer_tail = 0; uint8_t serial_tx_buffer[TX_RING_BUFFER]; From 5286284f2145abc7c1005d11ad5dea8f1388938b Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 24 Feb 2022 10:49:51 +0100 Subject: [PATCH 086/101] before merge --- doc/images/grbl-Mega-5X_Wiring.svg | 186 +++++++++++++++++++++++------ grbl/config.h | 17 ++- grbl/cpu_map.h | 34 ++++++ grbl/digital_control.c | 49 ++++++++ grbl/digital_control.h | 4 + grbl/grbl.h | 2 +- grbl/report.c | 8 +- grbl/serial.c | 2 +- 8 files changed, 258 insertions(+), 44 deletions(-) mode change 100644 => 100755 grbl/digital_control.c diff --git a/doc/images/grbl-Mega-5X_Wiring.svg b/doc/images/grbl-Mega-5X_Wiring.svg index fc0f78d0d..e3d5f46b0 100644 --- a/doc/images/grbl-Mega-5X_Wiring.svg +++ b/doc/images/grbl-Mega-5X_Wiring.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.56709964" - inkscape:cx="295.34878" - inkscape:cy="391.65248" + inkscape:zoom="0.802" + inkscape:cx="520" + inkscape:cy="500" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -98,13 +98,22 @@ height="1000" x="0" y="91.250053" /> + axe 1 motor + x="218.27432" + y="752.02478">axe 1 motor @@ -2583,15 +2592,15 @@ style="fill:none;stroke:#bf003f;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> Spindle PWM + y="542.64343" + x="6.1323195" + sodipodi:role="line" + id="tspan1406">Spindle PWM digital output 0 (D16) digital output 1 (D17) digital output 2 (D23) digital output 3 (D25) @@ -3313,5 +3322,112 @@ y="328.93997" style="font-size:10.6667px;text-align:center;text-anchor:middle" id="tspan1412">digital outputs + + + + + digital input 0 (D27) + digital input 1 (D29) + digital input 2 (D31) + digital input 3 (D33) + (SPINDLE_PWM_ON_D8) + Spindle PWM (D6) + (SPINDLE_PWM_ON_D86 + diff --git a/grbl/config.h b/grbl/config.h index 6563bdd40..0c4c4e5aa 100755 --- a/grbl/config.h +++ b/grbl/config.h @@ -310,11 +310,17 @@ // Inverts the selected digital output pin from low-disabled/high-enabled to low-enabled/high-disabled. // Useful for some pre-built electronic boards. -// #define INVERT_DIGITAL_OUTPUT_PIN_0 // Default disabled. Uncomment to enable. -// #define INVERT_DIGITAL_OUTPUT_PIN_1 // Default disabled. Uncomment to enable. -// #define INVERT_DIGITAL_OUTPUT_PIN_2 // Default disabled. Uncomment to enable. -// #define INVERT_DIGITAL_OUTPUT_PIN_3 // Default disabled. Uncomment to enable. - +//#define INVERT_DIGITAL_OUTPUT_PIN_0 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_OUTPUT_PIN_1 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_OUTPUT_PIN_2 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_OUTPUT_PIN_3 // Default disabled. Uncomment to enable. + +// Invert the digital input status. Default is normaly open switch between pin +// to GND, uncomment to use normaly closed switch. +//#define INVERT_DIGITAL_INPUT_PIN_0 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_INPUT_PIN_1 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_INPUT_PIN_2 // Default disabled. Uncomment to enable. +//#define INVERT_DIGITAL_INPUT_PIN_3 // Default disabled. Uncomment to enable. // When Grbl powers-cycles or is hard reset with the Arduino reset button, Grbl boots up with no ALARM // by default. This is to make it as simple as possible for new users to start using Grbl. When homing @@ -426,6 +432,7 @@ //#define DISABLE_LIMIT_PIN_PULL_UP //#define DISABLE_PROBE_PIN_PULL_UP //#define DISABLE_CONTROL_PIN_PULL_UP +//#define DISABLE_DIGITAL_INPUT_PIN_PULL_UP // Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with // the selected axis with the tool oriented toward the negative direction. In other words, a positive diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 60397d794..52194d40b 100755 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -257,6 +257,40 @@ #define PROBE_BIT 7 // MEGA2560 Analog Pin 15 #define PROBE_MASK (1< Date: Thu, 3 Mar 2022 07:57:46 +0100 Subject: [PATCH 087/101] Correct issue #257 - inversion error on INVERT_PWM_VALUES --- grbl/grbl.h | 2 +- grbl/spindle_control.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index 7543e6dfb..56b7e58c5 100755 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.2e" -#define GRBL_VERSION_BUILD "20220224" +#define GRBL_VERSION_BUILD "20220303" // Define standard libraries used by Grbl. #include diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index 091294ec6..db4250b40 100755 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -165,7 +165,7 @@ void spindle_set_speed(uint16_t pwm_value) sys.spindle_speed = rpm; pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE; } - #ifdef INVERT_PWM_VALUES + #ifndef INVERT_PWM_VALUES return(pwm_value); #else return(SPINDLE_PWM_MAX_VALUE - pwm_value); From c23c0804dd03aab45fd24693991b0a4a6ba28353 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 9 May 2022 11:40:38 +0200 Subject: [PATCH 088/101] Adding up to 4 digital input capability --- Makefile | 8 +++- grbl/config.h | 7 +++- grbl/coolant_control.c | 1 + grbl/coolant_control.h | 1 + grbl/cpu_map.h | 72 +++++++++++++++++---------------- grbl/defaults.h | 2 +- grbl/digital_control.c | 92 +++++++++++++++++++++--------------------- grbl/digital_control.h | 12 +++--- grbl/gcode.c | 2 +- grbl/gcode.h | 2 +- grbl/grbl.h | 6 +-- grbl/jog.c | 1 + grbl/jog.h | 1 + grbl/limits.c | 2 +- grbl/limits.h | 1 + grbl/main.c | 1 + grbl/motion_control.c | 2 +- grbl/motion_control.h | 2 +- grbl/nuts_bolts.c | 2 +- grbl/nuts_bolts.h | 2 +- grbl/planner.c | 2 +- grbl/planner.h | 1 + grbl/print.c | 1 + grbl/print.h | 1 + grbl/probe.c | 1 + grbl/probe.h | 1 + grbl/protocol.c | 1 + grbl/protocol.h | 1 + grbl/report.c | 44 ++++++++++++++------ grbl/report.h | 5 +++ grbl/serial.h | 1 + grbl/settings.c | 2 +- grbl/settings.h | 2 +- grbl/spindle_control.c | 1 + grbl/spindle_control.h | 1 + grbl/stepper.c | 2 +- grbl/stepper.h | 1 + grbl/system.c | 13 +++++- grbl/system.h | 1 + 39 files changed, 185 insertions(+), 116 deletions(-) mode change 100755 => 100644 grbl/config.h mode change 100755 => 100644 grbl/cpu_map.h mode change 100755 => 100644 grbl/digital_control.c mode change 100755 => 100644 grbl/grbl.h mode change 100755 => 100644 grbl/spindle_control.c diff --git a/Makefile b/Makefile index fd72f77e7..8de72a069 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # Part of Grbl # +# Copyright (c) 2017-2022 Gauthier Briere # Copyright (c) 2009-2011 Simen Svale Skogsrud # Copyright (c) 2012-2015 Sungeun K. Jeon # @@ -30,14 +31,17 @@ DEVICE ?= atmega2560 CLOCK = 16000000L -###PROGRAMMER ?= -c avrisp2 -P usb + DEVICE_PORT ?= /dev/ttyUSB0 PROGRAMMER ?= -D -v -c avrisp2 -P $(DEVICE_PORT) + SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ - print.c probe.c report.c system.c sleep.c jog.c + analog_control.c print.c probe.c report.c system.c sleep.c jog.c + BUILDDIR = build SOURCEDIR = grbl + # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m diff --git a/grbl/config.h b/grbl/config.h old mode 100755 new mode 100644 index 0c4c4e5aa..a163bd431 --- a/grbl/config.h +++ b/grbl/config.h @@ -2,7 +2,7 @@ config.h - compile time configuration Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -315,6 +315,11 @@ //#define INVERT_DIGITAL_OUTPUT_PIN_2 // Default disabled. Uncomment to enable. //#define INVERT_DIGITAL_OUTPUT_PIN_3 // Default disabled. Uncomment to enable. +// Digital inputs: Uncomment the folloing line to enable the use of up to +// 4 digital input pins. Digital inputs work in the same way as the other input +#define USE_DIGITAL_INPUT // Default disabled. Uncomment to enable. + +// pins (probe, safety door, cycle start, reset, feed hold). // Invert the digital input status. Default is normaly open switch between pin // to GND, uncomment to use normaly closed switch. //#define INVERT_DIGITAL_INPUT_PIN_0 // Default disabled. Uncomment to enable. diff --git a/grbl/coolant_control.c b/grbl/coolant_control.c index 9b780d87f..41eec77b3 100644 --- a/grbl/coolant_control.c +++ b/grbl/coolant_control.c @@ -2,6 +2,7 @@ coolant_control.c - coolant control methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/coolant_control.h b/grbl/coolant_control.h index 49b85f000..26cfe17e2 100644 --- a/grbl/coolant_control.h +++ b/grbl/coolant_control.h @@ -2,6 +2,7 @@ coolant_control.h - spindle control methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h old mode 100755 new mode 100644 index 52194d40b..810d25fa6 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -2,7 +2,7 @@ cpu_map.h - CPU and pin mapping configuration file Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -257,39 +257,43 @@ #define PROBE_BIT 7 // MEGA2560 Analog Pin 15 #define PROBE_MASK (1< diff --git a/grbl/jog.c b/grbl/jog.c index ee65b3dda..da4394a7f 100644 --- a/grbl/jog.c +++ b/grbl/jog.c @@ -2,6 +2,7 @@ jog.h - Jogging methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/jog.h b/grbl/jog.h index c72624634..c483099d5 100644 --- a/grbl/jog.h +++ b/grbl/jog.h @@ -2,6 +2,7 @@ jog.h - Jogging methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/limits.c b/grbl/limits.c index c0e073bca..5e170a57c 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -2,7 +2,7 @@ limits.c - code pertaining to limit-switches and performing the homing cycle Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/limits.h b/grbl/limits.h index b3ef88b8d..e1873b250 100644 --- a/grbl/limits.h +++ b/grbl/limits.h @@ -2,6 +2,7 @@ limits.h - code pertaining to limit-switches and performing the homing cycle Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/main.c b/grbl/main.c index eaa6de12c..a3fb06a9f 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -2,6 +2,7 @@ main.c - An embedded CNC Controller with rs274/ngc (g-code) support Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/motion_control.c b/grbl/motion_control.c index eccd80ec9..db9257eb9 100644 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -2,7 +2,7 @@ motion_control.c - high level interface for issuing motion commands Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/motion_control.h b/grbl/motion_control.h index b2dd19e79..7be04690f 100644 --- a/grbl/motion_control.h +++ b/grbl/motion_control.h @@ -2,7 +2,7 @@ motion_control.h - high level interface for issuing motion commands Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/nuts_bolts.c b/grbl/nuts_bolts.c index ca32b9bec..1b3e45848 100644 --- a/grbl/nuts_bolts.c +++ b/grbl/nuts_bolts.c @@ -2,7 +2,7 @@ nuts_bolts.c - Shared functions Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/nuts_bolts.h b/grbl/nuts_bolts.h index b99b28657..af1490298 100644 --- a/grbl/nuts_bolts.h +++ b/grbl/nuts_bolts.h @@ -2,7 +2,7 @@ nuts_bolts.h - Header file for shared definitions, variables, and functions Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/planner.c b/grbl/planner.c index 1900e44fd..b5706445d 100644 --- a/grbl/planner.c +++ b/grbl/planner.c @@ -2,7 +2,7 @@ planner.c - buffers movement commands and manages the acceleration profile plan Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Jens Geisler diff --git a/grbl/planner.h b/grbl/planner.h index 128eb509a..fd957e34a 100644 --- a/grbl/planner.h +++ b/grbl/planner.h @@ -2,6 +2,7 @@ planner.h - buffers movement commands and manages the acceleration profile plan Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/print.c b/grbl/print.c index a205aca74..230e569ce 100644 --- a/grbl/print.c +++ b/grbl/print.c @@ -2,6 +2,7 @@ print.c - Functions for formatting output strings Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/print.h b/grbl/print.h index 31e0a576c..017ef5277 100644 --- a/grbl/print.h +++ b/grbl/print.h @@ -2,6 +2,7 @@ print.h - Functions for formatting output strings Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/probe.c b/grbl/probe.c index 60c9073ad..b2b437630 100644 --- a/grbl/probe.c +++ b/grbl/probe.c @@ -2,6 +2,7 @@ probe.c - code pertaining to probing methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/probe.h b/grbl/probe.h index 03d5fd329..11b07bb3d 100644 --- a/grbl/probe.h +++ b/grbl/probe.h @@ -2,6 +2,7 @@ probe.h - code pertaining to probing methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify diff --git a/grbl/protocol.c b/grbl/protocol.c index b19e2d6d1..46bfedd78 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -2,6 +2,7 @@ protocol.c - controls Grbl execution protocol and procedures Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/protocol.h b/grbl/protocol.h index 56acba30f..6a434aed6 100644 --- a/grbl/protocol.h +++ b/grbl/protocol.h @@ -2,6 +2,7 @@ protocol.h - controls Grbl execution protocol and procedures Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/report.c b/grbl/report.c index 608f33b92..865d84ab3 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -2,7 +2,7 @@ report.c - reporting and messaging methods Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -247,7 +247,7 @@ void report_init_message() // Grbl help message void report_grbl_help() { - printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n")); + printPgmString(PSTR("[HLP:$$ $# $D $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n")); } @@ -556,6 +556,9 @@ void report_build_info(char *line) #ifndef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // NOTE: Shown when disabled. serial_write('W'); #endif + #ifdef USE_DIGITAL_INPUT + serial_write('D'); + #endif // NOTE: Compiled values, like override increments/max/min values, may be added at some point later. serial_write(','); print_uint8_base10(BLOCK_BUFFER_SIZE-1); @@ -739,7 +742,7 @@ void report_realtime_status() uint8_t sp_state = spindle_get_state(); uint8_t cl_state = coolant_get_state(); uint8_t dg_state = digital_get_state(); - if (sp_state || cl_state || dg_state || 1) { + if (sp_state || cl_state || dg_state) { printPgmString(PSTR("|A:")); if (sp_state) { // != SPINDLE_STATE_DISABLE if (sp_state == SPINDLE_STATE_CW) { serial_write('S'); } // CW @@ -747,16 +750,9 @@ void report_realtime_status() } if (cl_state & COOLANT_STATE_FLOOD) { serial_write('F'); } if (cl_state & COOLANT_STATE_MIST) { serial_write('M'); } - if (dg_state || 1) { // One or more digital output is active + if (dg_state) { // One or more digital output is active serial_write('D'); - if (dg_state & DIGITAL_INPUT_STATE_P3) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_INPUT_STATE_P2) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_INPUT_STATE_P1) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_INPUT_STATE_P0) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_OUTPUT_STATE_P3) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_OUTPUT_STATE_P2) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_OUTPUT_STATE_P1) { serial_write('1'); } else {serial_write('0');} - if (dg_state & DIGITAL_OUTPUT_STATE_P0) { serial_write('1'); } else {serial_write('0');} + printDgState(dg_state); } } } @@ -767,6 +763,30 @@ void report_realtime_status() } +// Print digital input / output status +void report_digital_status(uint8_t dg_state) +{ + printPgmString(PSTR("[D:")); + printDgState(dg_state); + serial_write(']'); + report_util_line_feed(); +} + +void printDgState(uint8_t dg_state) +{ + #ifdef USE_DIGITAL_INPUT + if (dg_state & DIGITAL_INPUT_STATE_P3) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_INPUT_STATE_P2) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_INPUT_STATE_P1) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_INPUT_STATE_P0) { serial_write('1'); } else {serial_write('0');} + #endif + if (dg_state & DIGITAL_OUTPUT_STATE_P3) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P2) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P1) { serial_write('1'); } else {serial_write('0');} + if (dg_state & DIGITAL_OUTPUT_STATE_P0) { serial_write('1'); } else {serial_write('0');} +} + + #ifdef DEBUG // Report debug string on serial diff --git a/grbl/report.h b/grbl/report.h index f9833e1cf..b3bb30643 100644 --- a/grbl/report.h +++ b/grbl/report.h @@ -2,6 +2,7 @@ report.h - reporting and messaging methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -124,6 +125,10 @@ void report_execute_startup_message(char *line, uint8_t status_code); // Prints build info and user info void report_build_info(char *line); +// Print digital input / output status +void report_digital_status(uint8_t dg_state); +void printDgState(uint8_t dg_state); + #ifdef DEBUG void report_debug_string(char *line); void report_debug_int(uint8_t val, ...); diff --git a/grbl/serial.h b/grbl/serial.h index 2c7a929b7..8dcafeedf 100644 --- a/grbl/serial.h +++ b/grbl/serial.h @@ -2,6 +2,7 @@ serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/settings.c b/grbl/settings.c index 570f7732a..e92009646 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -2,7 +2,7 @@ settings.c - eeprom configuration handling Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/settings.h b/grbl/settings.h index b556634fc..4f6526e03 100644 --- a/grbl/settings.h +++ b/grbl/settings.h @@ -2,7 +2,7 @@ settings.h - eeprom configuration handling Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c old mode 100755 new mode 100644 index db4250b40..0bace0ac0 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -2,6 +2,7 @@ spindle_control.c - spindle control methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2017 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/spindle_control.h b/grbl/spindle_control.h index 7e4145e8a..9bb4f4ee6 100644 --- a/grbl/spindle_control.h +++ b/grbl/spindle_control.h @@ -2,6 +2,7 @@ spindle_control.h - spindle control methods Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/stepper.c b/grbl/stepper.c index 7bc4aab6a..0abf35c7a 100644 --- a/grbl/stepper.c +++ b/grbl/stepper.c @@ -2,7 +2,7 @@ stepper.c - stepper motor driver: executes motion plans using stepper motors Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/stepper.h b/grbl/stepper.h index b552d542a..90591ec26 100644 --- a/grbl/stepper.h +++ b/grbl/stepper.h @@ -2,6 +2,7 @@ stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/grbl/system.c b/grbl/system.c index 31e3a3cd0..7f39977f3 100644 --- a/grbl/system.c +++ b/grbl/system.c @@ -2,7 +2,7 @@ system.c - Handles system level commands and real-time processes Part of Grbl - Copyright (c) 2017-2018 Gauthier Briere + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify @@ -122,7 +122,7 @@ uint8_t system_execute_line(char *line) if(line[2] != '=') { return(STATUS_INVALID_STATEMENT); } return(gc_execute_line(line)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions. break; - case '$': case 'G': case 'C': case 'X': + case '$': case 'G': case 'C': case 'X': case 'D': if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); } switch( line[1] ) { case '$' : // Prints Grbl settings @@ -155,6 +155,15 @@ uint8_t system_execute_line(char *line) // Don't run startup script. Prevents stored moves in startup from causing accidents. } // Otherwise, no effect. break; + case 'D' : // Show digital input / output status + if ( line[2] != 0 ) { + return(STATUS_INVALID_STATEMENT); + } + else { + uint8_t dg_state = digital_get_state(); + report_digital_status(dg_state); + } + break; } break; default : diff --git a/grbl/system.h b/grbl/system.h index 5296d1108..4166be40f 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -2,6 +2,7 @@ system.h - Header for system level commands and real-time processes Part of Grbl + Copyright (c) 2017-2022 Gauthier Briere Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC Grbl is free software: you can redistribute it and/or modify From cca2c2b899b79d968e65c709b5328799af866051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gauthier=20Bri=C3=A8re?= Date: Tue, 10 May 2022 09:32:54 +0200 Subject: [PATCH 089/101] Disable digital input by default --- grbl/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grbl/config.h b/grbl/config.h index a163bd431..7e3d2ec6d 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -317,7 +317,7 @@ // Digital inputs: Uncomment the folloing line to enable the use of up to // 4 digital input pins. Digital inputs work in the same way as the other input -#define USE_DIGITAL_INPUT // Default disabled. Uncomment to enable. +//#define USE_DIGITAL_INPUT // Default disabled. Uncomment to enable. // pins (probe, safety door, cycle start, reset, feed hold). // Invert the digital input status. Default is normaly open switch between pin From 8f06c7b93ebdc04d40b4b328aba558bf3a661564 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 14 May 2022 18:36:48 +0200 Subject: [PATCH 090/101] =?UTF-8?q?debut=20travail=20sur=20entr=C3=A9es/so?= =?UTF-8?q?rties=20analogiques?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grbl/analog_control.c | 50 +++++++++++++++++++++++++++++++++++++++++++ grbl/analog_control.h | 33 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 grbl/analog_control.c create mode 100644 grbl/analog_control.h diff --git a/grbl/analog_control.c b/grbl/analog_control.c new file mode 100644 index 000000000..53e07c4cf --- /dev/null +++ b/grbl/analog_control.c @@ -0,0 +1,50 @@ +/* + analog_control.c - Read analog input + Part of Grbl + + Grbl $ command: + $A : Read all analog inputs + + User defined M codes : + M167 P<0/1>: Read analog input, Synchronized + M168 P<0/1>: Read analog input, Immediate + + Copyright (c) 2017-2022 Gauthier Briere + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include "grbl.h" + +float readAnalog() +{ + float value = 0.0; + // Voltage reference for ADC. The default Arduino's analog reference + // of 5 volts on 5V Arduino boards. + uint8_t adcVRef = 1 << 6; + // A13 : pin = 13 --- A14 pin = 14 + uint8_t pin = 13; + + // ADC Prescaler = 32 + sbi(ADCSRA, ADPS2); + cbi(ADCSRA, ADPS1); + sbi(ADCSRA, ADPS0); + + // Selection de la pin + ADMUX = + // On lance la lecture analogique + sbi(ADCSRA, ADSC); + + return value; +} diff --git a/grbl/analog_control.h b/grbl/analog_control.h new file mode 100644 index 000000000..df46f8324 --- /dev/null +++ b/grbl/analog_control.h @@ -0,0 +1,33 @@ +/* + analog_control.h - Read analog input + Part of Grbl + + Copyright (c) 2017-2022 Gauthier Briere + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef analog_control_h +#define analog_control_h + +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +float readAnalog(); + +#endif From 0618607c02bf7ebb67731c81042b3188153d88f5 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 14 May 2022 18:40:14 +0200 Subject: [PATCH 091/101] reduce homing feed and seek for more accuracy --- grbl/defaults.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/defaults.h b/grbl/defaults.h index 04a179c48..661c35123 100644 --- a/grbl/defaults.h +++ b/grbl/defaults.h @@ -165,8 +165,8 @@ #define DEFAULT_LASER_MODE 0 // false #define DEFAULT_HOMING_ENABLE 1 // true #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir - #define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min - #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min + #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min + #define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) #define DEFAULT_HOMING_PULLOFF 5.0 // mm #endif From 060ca9040a1fa9bdec48f9da6b5e38ac17102a6f Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 19 May 2022 16:38:38 +0200 Subject: [PATCH 092/101] Keep track of current tool number between gc_execute_line() calls --- Makefile | 3 ++- grbl/gcode.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8de72a069..bee1eb014 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,8 @@ PROGRAMMER ?= -D -v -c avrisp2 -P $(DEVICE_PORT) SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ - analog_control.c print.c probe.c report.c system.c sleep.c jog.c + print.c probe.c report.c system.c sleep.c jog.c +# analog_control.c is in the analog_control branch... BUILDDIR = build SOURCEDIR = grbl diff --git a/grbl/gcode.c b/grbl/gcode.c index 02431c944..6488ec9a8 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -77,6 +77,9 @@ uint8_t gc_execute_line(char *line) memset(&gc_block, 0, sizeof(parser_block_t)); // Initialize the parser block struct. memcpy(&gc_block.modal,&gc_state.modal,sizeof(gc_modal_t)); // Copy current modes + // Keep track of current tool number (set tool number modal) + gc_block.values.t = gc_state.tool; + uint8_t axis_command = AXIS_COMMAND_NONE; uint8_t axis_0, axis_1, axis_linear; uint8_t axis_a, axis_b, axis_c; From b2886dfdaac798fd3756a5cea43a402109b3937c Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 21 Jun 2022 18:56:46 +0200 Subject: [PATCH 093/101] First optional analog output PWM beta version --- doc/csv/setting_codes_en_US.csv | 2 + grbl/analog_control.c | 109 +++++++++++++++++++++++- grbl/analog_control.h | 24 +++++- grbl/config.h | 76 ++++++++++++----- grbl/cpu_map.h | 142 ++++++++++++++++++++++++++++---- grbl/defaults.h | 9 ++ grbl/gcode.c | 80 ++++++++++++++++-- grbl/gcode.h | 40 ++++++--- grbl/grbl.h | 5 +- grbl/main.c | 3 + grbl/planner.h | 12 ++- grbl/protocol.c | 4 +- grbl/report.c | 36 +++++++- grbl/settings.c | 8 ++ grbl/settings.h | 5 ++ grbl/spindle_control.c | 56 ++++++------- grbl/system.h | 3 + 17 files changed, 522 insertions(+), 92 deletions(-) diff --git a/doc/csv/setting_codes_en_US.csv b/doc/csv/setting_codes_en_US.csv index c0e3de22b..184651c17 100644 --- a/doc/csv/setting_codes_en_US.csv +++ b/doc/csv/setting_codes_en_US.csv @@ -21,6 +21,8 @@ "30","Maximum spindle speed","RPM","Maximum spindle speed. Sets PWM to 100% duty cycle." "31","Minimum spindle speed","RPM","Minimum spindle speed. Sets PWM to 0.4% or lowest duty cycle." "32","Laser-mode enable","boolean","Enables laser mode. Consecutive G1/2/3 commands will not halt when spindle speed is changed." +"35","Maximum output PWM value","Volts or other units","Maximum output value. Sets PWM to 100% duty cycle." +"36","Minimum output PWM value","Volts or other units","Minimum output value. Sets PWM to 1% or lowest duty cycle." "100","X-axis travel resolution","step/mm","X-axis travel resolution in steps per millimeter." "101","Y-axis travel resolution","step/mm","Y-axis travel resolution in steps per millimeter." "102","Z-axis travel resolution","step/mm","Z-axis travel resolution in steps per millimeter." diff --git a/grbl/analog_control.c b/grbl/analog_control.c index 53e07c4cf..609fcd0f8 100644 --- a/grbl/analog_control.c +++ b/grbl/analog_control.c @@ -27,6 +27,111 @@ #include "grbl.h" + +static float output_pwm_gradient; // Precalulated value to speed up volts to PWM conversions. + + +#ifdef USE_OUTPUT_PWM +float o_pwm_gradient() +{ + return output_pwm_gradient; +} + +void output_pwm_init() +{ + // Force PWM output zero to avoid PWM output flash on laser output + OUTPUT_TCCRA_REGISTER &= ~(1<= settings.volts_max) || (volts >= settings.volts_max)) { + // No PWM range possible. Set simple on/off output control pin state. + sys.output_volts = settings.volts_max; + pwm_value = OUTPUT_PWM_MAX_VALUE; + } else if (volts <= settings.volts_min) { + if (volts == 0.0) { // S0 disables output + sys.output_volts = 0.0; + pwm_value = OUTPUT_PWM_OFF_VALUE; + } else { // Set minimum PWM output + sys.output_volts = settings.volts_min; + pwm_value = OUTPUT_PWM_MIN_VALUE; + } + } else { + // Compute intermediate PWM value with linear output volts model. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. + sys.output_volts = volts; + pwm_value = floor((volts - settings.volts_min)*output_pwm_gradient) + OUTPUT_PWM_MIN_VALUE; + } + return(pwm_value); +} + +void output_pwm_set_state(uint8_t state, float volts) +{ + if (sys.abort) { return; } // Block during abort. + if (state == OUTPUT_PWM_STATE_OFF) { // Stop output PWM. + sys.output_volts = 0.0; + output_pwm_stop(); + } else { + // NOTE: In laser mode, output will be active only when moving (STATE_CYCLE) or sleeping (STATE_SLEEP) + if (settings.flags & BITFLAG_LASER_MODE) { + if ((sys.state == STATE_IDLE) || (sys.state & (STATE_ALARM | STATE_CHECK_MODE | STATE_HOMING | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR ))) { + volts = 0.0; + } + } + output_pwm_set_value(output_compute_pwm_value(volts)); + } + // Set to report change immediately + sys.report_ovr_counter = 0; +} + +// G-code parser entry-point for setting output PWM state. Forces a planner buffer sync and bails +// if an abort or check-mode is active. +void output_pwm_sync(uint8_t state, float volts) +{ + if (sys.state == STATE_CHECK_MODE) { return; } + protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed. + output_pwm_set_state(state, volts); +} + +#endif // USE_OUTPUT_PWM + + +#ifdef USE_ANALOG_INPUTS + float readAnalog() { float value = 0.0; @@ -42,9 +147,11 @@ float readAnalog() sbi(ADCSRA, ADPS0); // Selection de la pin - ADMUX = +///// ADMUX = // On lance la lecture analogique sbi(ADCSRA, ADSC); return value; } + +#endif // USE_ANALOG_INPUTS diff --git a/grbl/analog_control.h b/grbl/analog_control.h index df46f8324..7bca02ea2 100644 --- a/grbl/analog_control.h +++ b/grbl/analog_control.h @@ -21,13 +21,31 @@ #ifndef analog_control_h #define analog_control_h +#define OUTPUT_PWM_STATE_OFF 0 // Must be 0 +#define OUTPUT_PWM_STATE_ON bit(0) + #ifndef cbi -#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) + #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi -#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) + #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif -float readAnalog(); +#ifdef USE_OUTPUT_PWM + +float o_pwm_gradient(); + + void output_pwm_init(); + uint8_t output_pwm_get_state(); + void output_pwm_stop(); + void output_pwm_set_value(uint16_t pwm_value); + uint16_t output_compute_pwm_value(float volts); + void output_pwm_set_state(uint8_t state, float volts); + void output_pwm_sync(uint8_t state, float volts); +#endif // USE_OUTPUT_PWM + +#ifdef USE_ANALOG_INPUTS + float readAnalog(); +#endif // USE_ANALOG_INPUTS #endif diff --git a/grbl/config.h b/grbl/config.h index 7e3d2ec6d..d20fc2926 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -31,6 +31,9 @@ #define config_h #include "grbl.h" // For Arduino IDE compatibility. +// Serial baud rate +// #define BAUD_RATE 230400 +#define BAUD_RATE 115200 // Define CPU pin map and default settings. // NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only @@ -39,10 +42,6 @@ #define DEFAULTS_GENERIC #define CPU_MAP_2560_RAMPS_BOARD -// Serial baud rate -// #define BAUD_RATE 230400 -#define BAUD_RATE 115200 - //---------------------------------------------------------------------- // Axis definitions : //---------------------------------------------------------------------- @@ -52,8 +51,10 @@ // if you forget the $RST=* command after change, Grbl may have // unpredictable behavior! //---------------------------------------------------------------------- + #define N_AXIS 5 // Number of axes (3 to 6) #define N_AXIS_LINEAR 3 // Number of linears axis, must be <= N_AXIS + // Axis indexing and names #define AXIS_1 0 // Axis indexing value. Must start with 0 and be continuous. #define AXIS_1_NAME 'X' // Axis names must be in X, Y, Z, A, B, C, U, V, W, D, E & H. @@ -79,21 +80,6 @@ #if N_AXIS > 6 #error "N_AXIS must be <= 6. N_AXIS > 6 is not implemented." #endif -//---------------------------------------------------------------------- - -// Chose the spindle pin output : -// SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 -// SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) -// Uncomment the line which correspond to your hardware -#define SPINDLE_PWM_ON_D8 -//#define SPINDLE_PWM_ON_D6 - -// PWM signal inversion: -// In case of particular electronics, it may be necessary to invert the values -// of the PWM signal of the spindle. For example, if the minimum spindle -// rpm is 1 and maximum is 1000, M3S250 will output 75% instead of 25% and -// M3S750 will output 25% instead of 75%. Disabled by default -//#define INVERT_PWM_VALUES // Renaming axis doesn't change their number. By default, the status report give axis values in // the order of their number. Some graphical interface are not able to affect axis values reported @@ -121,6 +107,58 @@ #endif #endif +//---------------------------------------------------------------------- +// End of axis definitions : +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// Spindle and other PWM output +//---------------------------------------------------------------------- +// Chose the spindle pin output : +// SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 (default) +// SPINDLE_PWM_ON_D9 => 0-12v 8 bits PWM on RAMPS D9 +// SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) +// Uncomment the line which correspond to your hardware +//#define SPINDLE_PWM_ON_D8 +#define SPINDLE_PWM_ON_D9 +//#define SPINDLE_PWM_ON_D6 + +// Spindle PWM signal inversion: +// In case of particular electronics, it may be necessary to invert the values +// of the PWM signal of the spindle. For example, if the minimum spindle +// rpm is 1 and maximum is 1000, M3S250 will output 75% instead of 25% and +// M3S750 will output 25% instead of 75%. Disabled by default +//#define INVERT_SPINDLE_PWM_VALUES + + +// Use PWM output drived by GCode command M67(Analog Output,Synchronized) +// or GCode command M68(Analog Output, Immediate). +//---------------------------------------------------------------------- +// ! IMPORTANT: When changing the USE_OUTPUT_PWM compil option, +// don't forget to issue the reset factory defaults Grbl command: $RST=* +// if you forget the $RST=* command after change, Grbl may have +// unpredictable behavior! +//---------------------------------------------------------------------- +// Uncomment the next line to enable the use of M67/M68 PWM output (Disabled by default). +#define USE_OUTPUT_PWM + +// second PWM can be on D9 (default) or on D8 or D6. +//#define OUTPUT_PWM_ON_D9 +#define OUTPUT_PWM_ON_D8 +//#define OUTPUT_PWM_ON_D6 + +//---------------------------------------------------------------------- +// End of spindle and other PWM output +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// Analog input +//---------------------------------------------------------------------- +//#define USE_ANALOG_INPUTS + + // Define realtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 810d25fa6..cfc75d381 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -301,12 +301,11 @@ // Spindle PWM configuration : // list of timers in Arduino Mega 2560 - // timer 0 (controls pin 13, 4); - // timer 1 (controls pin 12, 11); - // timer 2 (controls pin 10, 9); - // timer 3 (controls pin 5, 3, 2); - // timer 4 (controls pin 8, 7, 6); - // + // TIMER0 (controls pin 13, 4); => Timer0 is used by stepper.c + // TIMER1 (controls pin 12, 11); => Timer1 is used by stepper.c + // TIMER2 (controls pin 10, 9); => Timer2 is used by analog output or spindle PWM on D9 + // TIMER3 (controls pin 5, 3, 2); => Timer3 is used by sleep.c + // TIMER4 (controls pin 8, 7, 6); => Timer4 is used by analog output or spindle PWM on D8 or D6 // Arduino pin number and the corresponding register for controlling the duty cycle : // Pin Register // 2 OCR3B @@ -368,7 +367,7 @@ #define SPINDLE_OCR_REGISTER OCR4A #define SPINDLE_COMB_BIT COM4A1 - // 1/8 Prescaler, 16-bit Fast PWM mode + // 1/8 Prescaler, 8-bit Fast PWM mode #define SPINDLE_TCCRA_INIT_MASK (1<= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { @@ -421,9 +435,13 @@ uint8_t gc_execute_line(char *line) // NOTE: Variable 'dword_bit' is always assigned, if the non-command letter is valid. if (bit_istrue(value_dwords,dwbit(dword_bit))) { FAIL(STATUS_GCODE_WORD_REPEATED); } // [Word repeated] - // Check for invalid negative values for words F, N, P, T, and S. + // Check for invalid negative values for words F, N, O, P, T, and S. // NOTE: Negative value check is done here simply for code-efficiency. + #ifdef USE_OUTPUT_PWM + if ( dwbit(dword_bit) & (dwbit(DWORD_F)|dwbit(DWORD_N)|dwbit(DWORD_O)|dwbit(DWORD_P)|dwbit(DWORD_T)|dwbit(DWORD_S)) ) { + #else if ( dwbit(dword_bit) & (dwbit(DWORD_F)|dwbit(DWORD_N)|dwbit(DWORD_P)|dwbit(DWORD_T)|dwbit(DWORD_S)) ) { + #endif if (value < 0.0) { FAIL(STATUS_NEGATIVE_VALUE); } // [Word value cannot be negative] } value_dwords |= dwbit(dword_bit); // Flag to indicate parameter assigned. @@ -526,6 +544,10 @@ uint8_t gc_execute_line(char *line) if (bit_isfalse(value_dwords,dwbit(DWORD_S))) { gc_block.values.s = gc_state.spindle_speed; } // bit_false(value_dwords,dwbit(DWORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. + // [4.bis Set output PWM value]: O is negative (done.) + if (bit_isfalse(value_dwords,dwbit(DWORD_O))) { gc_block.values.o = gc_state.output_volts; } + // bit_false(value_dwords,dwbit(DWORD_O)); // NOTE: Single-meaning value word. Set at end of error-checking. + // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. // bit_false(value_dwords,dwbit(DWORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. @@ -1213,7 +1235,11 @@ uint8_t gc_execute_line(char *line) // Jogging only uses the F feed rate and XYZ value words. N is valid, but S and T are invalid. bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F))); } else { + #ifdef USE_OUTPUT_PWM + bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F)|dwbit(DWORD_O)|dwbit(DWORD_S)|dwbit(DWORD_T))); // Remove single-meaning value words. + #else bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F)|dwbit(DWORD_S)|dwbit(DWORD_T))); // Remove single-meaning value words. + #endif } #if N_AXIS > 3 if (axis_command) { bit_false(value_dwords,(dwbit(DWORD_X)|dwbit(DWORD_Y)|dwbit(DWORD_Z)|dwbit(DWORD_A)|dwbit(DWORD_B)|dwbit(DWORD_C)|dwbit(DWORD_U)|dwbit(DWORD_V)|dwbit(DWORD_W))); } // Remove axis words. @@ -1246,6 +1272,10 @@ uint8_t gc_execute_line(char *line) // Initialize planner data to current spindle and coolant modal state. pl_data->spindle_speed = gc_state.spindle_speed; plan_data.condition = (gc_state.modal.spindle | gc_state.modal.coolant); + #ifdef USE_OUTPUT_PWM + // Add output PWM value to planner data + pl_data->output_volts = gc_state.output_volts; + #endif uint8_t status = jog_execute(&plan_data, &gc_block); if (status == STATUS_OK) { memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); } @@ -1304,7 +1334,9 @@ uint8_t gc_execute_line(char *line) if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_ISMOTION)) { if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { spindle_sync(gc_state.modal.spindle, 0.0); - } else { spindle_sync(gc_state.modal.spindle, gc_block.values.s); } + } else { + spindle_sync(gc_state.modal.spindle, gc_block.values.s); + } } } gc_state.spindle_speed = gc_block.values.s; // Update spindle speed state. @@ -1514,7 +1546,7 @@ uint8_t gc_execute_line(char *line) gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; // Reset program flow. } - // [22. Digital output ]: P value missing. P is negative (done.) NOTE: See below. + // [22. Digital output ]: if ((gc_block.non_modal_command >= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { uint8_t digital_mode = digital_get_state(); switch(gc_block.non_modal_command) { @@ -1533,6 +1565,42 @@ uint8_t gc_execute_line(char *line) } } + #ifdef USE_OUTPUT_PWM + // [23. Output PWM ]: + if ((gc_state.output_volts != gc_block.values.o) || ((gc_block.non_modal_command >= NON_MODAL_ANALOG_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_ANALOG_IMMEDIATE_OFF))) { + switch(gc_block.non_modal_command) { + case NON_MODAL_ANALOG_SYNC_ON: // M162 + output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.o); + gc_state.output_last_command = gc_block.non_modal_command; + break; + case NON_MODAL_ANALOG_SYNC_OFF: // M163 + output_pwm_sync(OUTPUT_PWM_STATE_OFF, 0.0); + gc_state.output_last_command = gc_block.non_modal_command; + break; + case NON_MODAL_ANALOG_IMMEDIATE_ON: // M164 + output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.o); + gc_state.output_last_command = gc_block.non_modal_command; + break; + case NON_MODAL_ANALOG_IMMEDIATE_OFF: // M165 + output_pwm_set_state(OUTPUT_PWM_STATE_OFF, 0.0); + gc_state.output_last_command = gc_block.non_modal_command; + break; + default: + // Changement de la valeur de sortie en dehors d'une commande M162-M165 + // En fonction de la dernière commande stockée dans gc_state.output_last, + // on change immediatement ou synchronisé si NON_MODAL_ANALOG_SYNC_ON ou NON_MODAL_ANALOG_IMMEDIATE_ON + // ou on laissera off si NON_MODAL_ANALOG_SYNC_OFF ou NON_MODAL_ANALOG_IMMEDIATE_OFF. + if (gc_state.output_last_command == NON_MODAL_ANALOG_SYNC_ON) { + output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.o); + } else if (gc_state.output_last_command == NON_MODAL_ANALOG_IMMEDIATE_ON) { + output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.o); + } + break; + } + gc_state.output_volts = gc_block.values.o; // Update output volt state gc_state.spindle . + } + #endif + // TODO: % to denote start of program. return(STATUS_OK); diff --git a/grbl/gcode.h b/grbl/gcode.h index bf3e115c1..278c34a21 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -47,6 +47,9 @@ #define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control #define MODAL_GROUP_M9 14 // [M56] Override control #define MODAL_GROUP_M10 15 // [M62-M65] Digital output -Non-modal +#ifdef USE_OUTPUT_PWM + #define MODAL_GROUP_M11 16 // [M162-M165] Analog output -Non-modal +#endif // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. @@ -135,16 +138,23 @@ #endif // Modal Group M10: Digital output (non modal) -#define NON_MODAL_DIGITAL_SYNC_ON 62 // M62 -#define NON_MODAL_DIGITAL_SYNC_OFF 63 // M63 -#define NON_MODAL_DIGITAL_IMMEDIATE_ON 64 // M64 +#define NON_MODAL_DIGITAL_SYNC_ON 62 // M62 +#define NON_MODAL_DIGITAL_SYNC_OFF 63 // M63 +#define NON_MODAL_DIGITAL_IMMEDIATE_ON 64 // M64 #define NON_MODAL_DIGITAL_IMMEDIATE_OFF 65 // M65 +#ifdef USE_OUTPUT_PWM + // Modal Group M11: Analog output (PWM) (non modal) + #define NON_MODAL_ANALOG_SYNC_ON 162 // M162 + #define NON_MODAL_ANALOG_SYNC_OFF 163 // M163 + #define NON_MODAL_ANALOG_IMMEDIATE_ON 164 // M164 + #define NON_MODAL_ANALOG_IMMEDIATE_OFF 165 // M165 +#endif // Modal Group G12: Active work coordinate system // N/A: Stores coordinate system value (54-59) to change to. // Define parameter word mapping. -// Updated to 32 bits tou support more than 16 values... Needed for new axis U, V & W +// Updated to 32 bits to support more than 16 values... Needed for new axis U, V & W #define DWORD_F 0 #define DWORD_I 1 #define DWORD_J 2 @@ -164,7 +174,9 @@ #define DWORD_U 16 #define DWORD_V 17 #define DWORD_W 18 - +#ifdef USE_OUTPUT_PWM + #define DWORD_O 19 +#endif // Define g-code parser position updating flags #define GC_UPDATE_POS_TARGET 0 // Must be zero #define GC_UPDATE_POS_SYSTEM 1 @@ -220,8 +232,10 @@ typedef struct { #endif uint8_t l; // G10 or canned cycles parameters int32_t n; // Line number + #ifdef USE_OUTPUT_PWM + float o; // Output PWM valus + #endif float p; // G10 or dwell parameters - // float q; // G82 peck drilling float r; // Arc radius float s; // Spindle speed uint8_t t; // Tool selection @@ -236,12 +250,16 @@ typedef struct { typedef struct { gc_modal_t modal; - float spindle_speed; // RPM - float feed_rate; // Millimeters/min - uint8_t tool; // Tracks tool number. NOT USED. - int32_t line_number; // Last line number sent + float spindle_speed; // RPM + #ifdef USE_OUTPUT_PWM + float output_volts; // Output PWM value + uint8_t output_last_command; // Last command used to modify output PWM + #endif + float feed_rate; // Millimeters/min + uint8_t tool; // Tracks tool number. NOT USED. + int32_t line_number; // Last line number sent - float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code + float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine // position in mm. Loaded from EEPROM when called. diff --git a/grbl/grbl.h b/grbl/grbl.h index af7939236..606739e17 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2f" -#define GRBL_VERSION_BUILD "20220509" +#define GRBL_VERSION "1.2g" +#define GRBL_VERSION_BUILD "20220620" // Define standard libraries used by Grbl. #include @@ -49,6 +49,7 @@ #include "planner.h" #include "coolant_control.h" #include "digital_control.h" +#include "analog_control.h" #include "eeprom.h" #include "gcode.h" #include "limits.h" diff --git a/grbl/main.c b/grbl/main.c index a3fb06a9f..1b8125a75 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -508,6 +508,9 @@ int main(void) serial_reset_read_buffer(); // Clear serial read buffer gc_init(); // Set g-code parser to default state spindle_init(); + #ifdef USE_OUTPUT_PWM + output_pwm_init(); + #endif coolant_init(); digital_init(); limits_init(); diff --git a/grbl/planner.h b/grbl/planner.h index fd957e34a..679c9cb50 100644 --- a/grbl/planner.h +++ b/grbl/planner.h @@ -75,15 +75,21 @@ typedef struct { // Stored spindle speed data used by spindle overrides and resuming methods. float spindle_speed; // Block spindle speed. Copied from pl_line_data. + #ifdef USE_OUTPUT_PWM + float output_volts; // Block output PWM value. Copied from pl_line_data. + #endif } plan_block_t; // Planner data prototype. Must be used when passing new motions to the planner. typedef struct { - float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion. - float spindle_speed; // Desired spindle speed through line motion. + float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion. + float spindle_speed; // Desired spindle speed through line motion. + #ifdef USE_OUTPUT_PWM + float output_volts; // Desired output PWM value for line motion. Value is ignored, if rapid motion. + #endif int32_t line_number; // Desired line number to report when executing. - uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above. + uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above. } plan_line_data_t; diff --git a/grbl/protocol.c b/grbl/protocol.c index 46bfedd78..0863cfc0c 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -536,7 +536,9 @@ static void protocol_exec_rt_suspend() while (sys.suspend) { - if (sys.abort) { return; } + if (sys.abort) { + return; + } // Block until initial hold is complete and the machine has stopped motion. if (sys.suspend & SUSPEND_HOLD_COMPLETE) { diff --git a/grbl/report.c b/grbl/report.c index 865d84ab3..ccca43753 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -277,6 +277,10 @@ void report_grbl_settings() { report_util_float_setting(30,settings.rpm_max,N_DECIMAL_RPMVALUE); report_util_float_setting(31,settings.rpm_min,N_DECIMAL_RPMVALUE); report_util_uint8_setting(32,bit_istrue(settings.flags,BITFLAG_LASER_MODE)); + #ifdef USE_OUTPUT_PWM + report_util_float_setting(35,settings.volts_max, N_DECIMAL_SETTINGVALUE); + report_util_float_setting(36,settings.volts_min, N_DECIMAL_SETTINGVALUE); + #endif // Print axis settings uint8_t idx, set_idx; uint8_t val = AXIS_SETTINGS_START_VAL; @@ -507,6 +511,10 @@ void report_build_info(char *line) #endif report_util_feedback_line_feed(); printPgmString(PSTR("[OPT:")); // Generate compile-time build option list + //-------------------------------------------------------------------- + // ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789*$# => Lettre flag + // ! !!! !!! !!!!! ! ! !! !! !!! => ! = Utilisée + //-------------------------------------------------------------------- serial_write('V'); // Variable spindle standard. serial_write('N'); // Line number reporting standard. serial_write('M'); // M7 mist coolant standard. @@ -526,6 +534,9 @@ void report_build_info(char *line) #ifdef LIMITS_TWO_SWITCHES_ON_AXES serial_write('T'); #endif + #ifdef USE_OUTPUT_PWM + serial_write('O'); + #endif #ifdef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES serial_write('A'); #endif @@ -727,11 +738,14 @@ void report_realtime_status() #endif #ifdef REPORT_FIELD_OVERRIDES - if (sys.report_ovr_counter > 0) { sys.report_ovr_counter--; } - else { + if (sys.report_ovr_counter > 0) { + sys.report_ovr_counter--; + } else { if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) { sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-1); // Reset counter for slow refresh - } else { sys.report_ovr_counter = (REPORT_OVR_REFRESH_IDLE_COUNT-1); } + } else { + sys.report_ovr_counter = (REPORT_OVR_REFRESH_IDLE_COUNT-1); + } printPgmString(PSTR("|Ov:")); print_uint8_base10(sys.f_override); serial_write(','); @@ -754,10 +768,26 @@ void report_realtime_status() serial_write('D'); printDgState(dg_state); } + #ifdef USE_OUTPUT_PWM + #endif } } #endif + #ifdef USE_OUTPUT_PWM + uint8_t ot_state = output_pwm_get_state(); + if (ot_state) { // Analog output (PWM) in active + printPgmString(PSTR("|O:")); + printFloat(sys.output_volts, N_DECIMAL_SETTINGVALUE); + /* pour debug calculs PWM + * serial_write(','); + * print_uint8_base10(output_compute_pwm_value(sys.output_volts)); + * serial_write(','); + * printFloat(o_pwm_gradient(), N_DECIMAL_SETTINGVALUE); + */ + } + #endif + serial_write('>'); report_util_line_feed(); } diff --git a/grbl/settings.c b/grbl/settings.c index e92009646..906daf995 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -34,6 +34,10 @@ const __flash settings_t defaults = { .arc_tolerance = DEFAULT_ARC_TOLERANCE, .rpm_max = DEFAULT_SPINDLE_RPM_MAX, .rpm_min = DEFAULT_SPINDLE_RPM_MIN, + #ifdef USE_OUTPUT_PWM + .volts_max = DEFAULT_OUTPUT_PWM_MAX, + .volts_min = DEFAULT_OUTPUT_PWM_MIN, + #endif .homing_dir_mask = DEFAULT_HOMING_DIR_MASK, .homing_feed_rate = DEFAULT_HOMING_FEED_RATE, .homing_seek_rate = DEFAULT_HOMING_SEEK_RATE, @@ -309,6 +313,10 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) { if (int_value) { settings.flags |= BITFLAG_LASER_MODE; } else { settings.flags &= ~BITFLAG_LASER_MODE; } break; + #ifdef USE_OUTPUT_PWM + case 35: settings.volts_max = value; output_pwm_init(); break; + case 36: settings.volts_min = value; output_pwm_init(); break; + #endif default: return(STATUS_INVALID_STATEMENT); } diff --git a/grbl/settings.h b/grbl/settings.h index 4f6526e03..dab29ee31 100644 --- a/grbl/settings.h +++ b/grbl/settings.h @@ -104,6 +104,11 @@ typedef struct { float rpm_max; float rpm_min; + #ifdef USE_OUTPUT_PWM + float volts_max; + float volts_min; + #endif + uint8_t flags; // Contains default boolean settings uint8_t homing_dir_mask; diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index 0bace0ac0..6e8439f0e 100644 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -23,7 +23,7 @@ #include "grbl.h" -static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions. +static float spindle_pwm_gradient; // Precalulated value to speed up rpm to PWM conversions. void spindle_init() @@ -38,7 +38,7 @@ void spindle_init() SPINDLE_ENABLE_DDR |= (1<= settings.rpm_max) || (rpm >= settings.rpm_max)) { - // No PWM range possible. Set simple on/off spindle control pin state. - sys.spindle_speed = settings.rpm_max; - pwm_value = SPINDLE_PWM_MAX_VALUE; - } else if (rpm <= settings.rpm_min) { - if (rpm == 0.0) { // S0 disables spindle - sys.spindle_speed = 0.0; - pwm_value = SPINDLE_PWM_OFF_VALUE; - } else { // Set minimum PWM output - sys.spindle_speed = settings.rpm_min; - pwm_value = SPINDLE_PWM_MIN_VALUE; - } - } else { - // Compute intermediate PWM value with linear spindle speed model. - // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. - sys.spindle_speed = rpm; - pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE; - } - #ifndef INVERT_PWM_VALUES - return(pwm_value); - #else - return(SPINDLE_PWM_MAX_VALUE - pwm_value); - #endif + uint16_t pwm_value; + rpm *= (0.010*sys.spindle_speed_ovr); // Scale by spindle speed override value. + // Calculate PWM register value based on rpm max/min settings and programmed rpm. + if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) { + // No PWM range possible. Set simple on/off spindle control pin state. + sys.spindle_speed = settings.rpm_max; + pwm_value = SPINDLE_PWM_MAX_VALUE; + } else if (rpm <= settings.rpm_min) { + if (rpm == 0.0) { // S0 disables spindle + sys.spindle_speed = 0.0; + pwm_value = SPINDLE_PWM_OFF_VALUE; + } else { // Set minimum PWM output + sys.spindle_speed = settings.rpm_min; + pwm_value = SPINDLE_PWM_MIN_VALUE; + } + } else { + // Compute intermediate PWM value with linear spindle speed model. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. + sys.spindle_speed = rpm; + pwm_value = floor((rpm - settings.rpm_min) * spindle_pwm_gradient) + SPINDLE_PWM_MIN_VALUE; + } + #ifndef INVERT_SPINDLE_PWM_VALUES + return(pwm_value); + #else + return(SPINDLE_PWM_MAX_VALUE - pwm_value); + #endif } #endif diff --git a/grbl/system.h b/grbl/system.h index 4166be40f..11da2e7c2 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -136,6 +136,9 @@ typedef struct { uint8_t override_ctrl; // Tracks override control states. #endif float spindle_speed; + #ifdef USE_OUTPUT_PWM + float output_volts; // GBGB TODO: Implémenter de la même manière que spindle_speed... + #endif } system_t; extern system_t sys; From 0865fedaaf8262f675d4bc0391e019ca9459110a Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 21 Jun 2022 19:05:02 +0200 Subject: [PATCH 094/101] Add compil error for output PWM and spindle timer conflict --- grbl/cpu_map.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index cfc75d381..349a7c487 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -445,6 +445,13 @@ #elif defined (OUTPUT_PWM_ON_D8) + // Error if both spindle and analog output are defined on the same output + #ifdef SPINDLE_PWM_ON_D8 + #error "Spindle is already defined on D8, you cant use the same D8 pin for analog output!" + #endif + #ifdef SPINDLE_PWM_ON_D6 + #error "Spindle is defined on D6 which use the same timer than D8, you cant use D8 pin for analog output!" + #endif // Set Timer up to use TIMER4B which is attached to Digital Pin 8 - Ramps 1.4 12v output with heat sink #define OUTPUT_PWM_MAX_VALUE 1024.0 // Translates to about 1.9 kHz PWM frequency at 1/8 prescaler #ifndef OUTPUT_PWM_MIN_VALUE @@ -472,6 +479,13 @@ #elif defined (OUTPUT_PWM_ON_D6) + // Error if both spindle and analog output are defined on the same output + #ifdef SPINDLE_PWM_ON_D6 + #error "Spindle is already defined on D6, you cant use the same D6 pin for analog output!" + #endif + #ifdef SPINDLE_PWM_ON_D8 + #error "Spindle is defined on D8 which use the same timer than D6, you cant use D6 pin for analog output!" + #endif // Set Timer up to use TIMER4C which is attached to Digital Pin 6 - Ramps Servo 2 #define OUTPUT_PWM_MAX_VALUE 255.0 // Translates to about 1.9 kHz PWM frequency at 1/8 prescaler #ifndef OUTPUT_PWM_MIN_VALUE From 02bd27e226678eab90e846ab7717aaf45fe650c7 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 24 Jun 2022 18:43:10 +0200 Subject: [PATCH 095/101] Optional M67/68 analog output and / or separate spindle/laser output pin --- doc/csv/build_option_codes_en_US.csv | 11 +-- doc/csv/setting_codes_en_US.csv | 6 +- grbl/analog_control.c | 37 +-------- grbl/analog_control.h | 4 - grbl/config.h | 59 ++++++++++---- grbl/cpu_map.h | 113 ++++++++++++++++++++++++--- grbl/defaults.h | 14 ++-- grbl/gcode.c | 69 ++++++++-------- grbl/gcode.h | 10 +-- grbl/print.c | 20 +++++ grbl/print.h | 1 + grbl/report.c | 96 +++++++++++++++++++---- grbl/report.h | 4 +- grbl/settings.c | 22 +++++- grbl/settings.h | 5 +- grbl/spindle_control.c | 109 +++++++++++++++++++++++--- 16 files changed, 435 insertions(+), 145 deletions(-) diff --git a/doc/csv/build_option_codes_en_US.csv b/doc/csv/build_option_codes_en_US.csv index d4f868223..90540d394 100644 --- a/doc/csv/build_option_codes_en_US.csv +++ b/doc/csv/build_option_codes_en_US.csv @@ -1,22 +1,23 @@ -"OPT: Code"," Build-Option Description","State" +"OPT: Code","Build-Option Description","State" "V","Variable spindle","Enabled" "N","Line numbers","Enabled" "M","Mist coolant M7","Enabled" +"G","Safety door input pin","Enabled" "C","CoreXY","Enabled" "P","Parking motion","Enabled" "Z","Homing force origin","Enabled" "H","Homing single axis commands","Enabled" "T","Two limit switches on axis","Enabled" +"S","Use different spindle output pin in laser mode","Enabled" +"D","Digital input","Enabled" +"Q","Analog output (PWM)","Enabled" "A","Allow feed rate overrides in probe cycles","Enabled" -"D","Use spindle direction as enable pin","Enabled" "0","Spindle enable off when speed is zero","Enabled" -"S","Software limit pin debouncing","Enabled" "R","Parking override control","Enabled" -"G","Safety door input pin","Enabled" +"L","Homing initialization auto-lock","Disabled" "*","Restore all EEPROM command","Disabled" "$","Restore EEPROM `$` settings command","Disabled" "#","Restore EEPROM parameter data command","Disabled" "I","Build info write user string command","Disabled" "E","Force sync upon EEPROM write","Disabled" "W","Force sync upon work coordinate offset change","Disabled" -"L","Homing initialization auto-lock","Disabled" diff --git a/doc/csv/setting_codes_en_US.csv b/doc/csv/setting_codes_en_US.csv index 184651c17..11696b5ad 100644 --- a/doc/csv/setting_codes_en_US.csv +++ b/doc/csv/setting_codes_en_US.csv @@ -21,8 +21,10 @@ "30","Maximum spindle speed","RPM","Maximum spindle speed. Sets PWM to 100% duty cycle." "31","Minimum spindle speed","RPM","Minimum spindle speed. Sets PWM to 0.4% or lowest duty cycle." "32","Laser-mode enable","boolean","Enables laser mode. Consecutive G1/2/3 commands will not halt when spindle speed is changed." -"35","Maximum output PWM value","Volts or other units","Maximum output value. Sets PWM to 100% duty cycle." -"36","Minimum output PWM value","Volts or other units","Minimum output value. Sets PWM to 1% or lowest duty cycle." +"33","Maximum laser value","Laser unit","Maximum laser value. Sets PWM to 100% duty cycle." +"34","Minimum laser value","Laser unit","Minimum laser value. Sets PWM to 0.4% or lowest duty cycle." +"35","Maximum output value","Volts or other units","Maximum output value. Sets PWM to 100% duty cycle." +"36","Minimum output value","Volts or other units","Minimum output value. Sets PWM to 0.4% or lowest duty cycle." "100","X-axis travel resolution","step/mm","X-axis travel resolution in steps per millimeter." "101","Y-axis travel resolution","step/mm","Y-axis travel resolution in steps per millimeter." "102","Z-axis travel resolution","step/mm","Z-axis travel resolution in steps per millimeter." diff --git a/grbl/analog_control.c b/grbl/analog_control.c index 609fcd0f8..4748902e5 100644 --- a/grbl/analog_control.c +++ b/grbl/analog_control.c @@ -2,13 +2,6 @@ analog_control.c - Read analog input Part of Grbl - Grbl $ command: - $A : Read all analog inputs - - User defined M codes : - M167 P<0/1>: Read analog input, Synchronized - M168 P<0/1>: Read analog input, Immediate - Copyright (c) 2017-2022 Gauthier Briere Grbl is free software: you can redistribute it and/or modify @@ -28,10 +21,10 @@ #include "grbl.h" -static float output_pwm_gradient; // Precalulated value to speed up volts to PWM conversions. +#ifdef USE_OUTPUT_PWM +static float output_pwm_gradient; // Precalulated value to speed up volts to PWM conversions. -#ifdef USE_OUTPUT_PWM float o_pwm_gradient() { return output_pwm_gradient; @@ -129,29 +122,3 @@ void output_pwm_sync(uint8_t state, float volts) #endif // USE_OUTPUT_PWM - -#ifdef USE_ANALOG_INPUTS - -float readAnalog() -{ - float value = 0.0; - // Voltage reference for ADC. The default Arduino's analog reference - // of 5 volts on 5V Arduino boards. - uint8_t adcVRef = 1 << 6; - // A13 : pin = 13 --- A14 pin = 14 - uint8_t pin = 13; - - // ADC Prescaler = 32 - sbi(ADCSRA, ADPS2); - cbi(ADCSRA, ADPS1); - sbi(ADCSRA, ADPS0); - - // Selection de la pin -///// ADMUX = - // On lance la lecture analogique - sbi(ADCSRA, ADSC); - - return value; -} - -#endif // USE_ANALOG_INPUTS diff --git a/grbl/analog_control.h b/grbl/analog_control.h index 7bca02ea2..fe7afe7d8 100644 --- a/grbl/analog_control.h +++ b/grbl/analog_control.h @@ -44,8 +44,4 @@ float o_pwm_gradient(); void output_pwm_sync(uint8_t state, float volts); #endif // USE_OUTPUT_PWM -#ifdef USE_ANALOG_INPUTS - float readAnalog(); -#endif // USE_ANALOG_INPUTS - #endif diff --git a/grbl/config.h b/grbl/config.h index d20fc2926..b8dd9e10b 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -29,10 +29,10 @@ #ifndef config_h #define config_h + #include "grbl.h" // For Arduino IDE compatibility. // Serial baud rate -// #define BAUD_RATE 230400 #define BAUD_RATE 115200 // Define CPU pin map and default settings. @@ -113,16 +113,16 @@ //---------------------------------------------------------------------- -// Spindle and other PWM output +// Spindle, laser and other PWM output //---------------------------------------------------------------------- // Chose the spindle pin output : // SPINDLE_PWM_ON_D8 => 0-12v 16 bits PWM on RAMPS D8 (default) // SPINDLE_PWM_ON_D9 => 0-12v 8 bits PWM on RAMPS D9 // SPINDLE_PWM_ON_D6 => 0-5v 8bits PWM on RAMPS Servo 2 signal (Mega 2560 D6) // Uncomment the line which correspond to your hardware -//#define SPINDLE_PWM_ON_D8 -#define SPINDLE_PWM_ON_D9 +#define SPINDLE_PWM_ON_D8 //#define SPINDLE_PWM_ON_D6 +//#define SPINDLE_PWM_ON_D9 // Spindle PWM signal inversion: // In case of particular electronics, it may be necessary to invert the values @@ -131,8 +131,28 @@ // M3S750 will output 25% instead of 75%. Disabled by default //#define INVERT_SPINDLE_PWM_VALUES +// Use different spindle output pin in laser mode: +// Spindle or laser tools do not have the same hardware specifications. +// When using both spindle and laser on the same machine it will be useful +// to have spindle and laser on diffrents pins which can deliver the +// differents outputs nedded. +//---------------------------------------------------------------------- +// ! IMPORTANT: When changing the SEPARATE_SPINDLE_LASER_PIN compil option, +// don't forget to issue the reset factory defaults Grbl command: $RST=* +// if you forget the $RST=* command after change, Grbl may have +// unpredictable behavior! +//---------------------------------------------------------------------- +// Uncomment the next line to enable this functionality (default disabled): +//#define SEPARATE_SPINDLE_LASER_PIN + +#ifdef SEPARATE_SPINDLE_LASER_PIN + // Laser PWM can be on D6 (default) or on D8 or D9. + #define LASER_PWM_ON_D6 + //#define LASER_PWM_ON_D8 + //#define LASER_PWM_ON_D9 +#endif -// Use PWM output drived by GCode command M67(Analog Output,Synchronized) +// Use output PWM drived by GCode command M67(Analog Output,Synchronized) // or GCode command M68(Analog Output, Immediate). //---------------------------------------------------------------------- // ! IMPORTANT: When changing the USE_OUTPUT_PWM compil option, @@ -141,24 +161,22 @@ // unpredictable behavior! //---------------------------------------------------------------------- // Uncomment the next line to enable the use of M67/M68 PWM output (Disabled by default). -#define USE_OUTPUT_PWM - -// second PWM can be on D9 (default) or on D8 or D6. -//#define OUTPUT_PWM_ON_D9 -#define OUTPUT_PWM_ON_D8 -//#define OUTPUT_PWM_ON_D6 +//#define USE_OUTPUT_PWM + +#ifdef USE_OUTPUT_PWM + // Optional PWM can be on D9 (default) or on D8 or D6. + // Warning ! Optional can't use the same timer than the spindle or the laser pin + // For more information about this, see the comment of the relevant section in cpu_map.h + #define OUTPUT_PWM_ON_D9 + //#define OUTPUT_PWM_ON_D8 + //#define OUTPUT_PWM_ON_D6 +#endif //---------------------------------------------------------------------- // End of spindle and other PWM output //---------------------------------------------------------------------- -//---------------------------------------------------------------------- -// Analog input -//---------------------------------------------------------------------- -//#define USE_ANALOG_INPUTS - - // Define realtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be @@ -729,6 +747,13 @@ // 'fit_nonlinear_spindle.py' script in the /doc/script folder of the repo. See file comments // on how to gather spindle data and run the script to generate a solution. // #define ENABLE_PIECEWISE_LINEAR_SPINDLE // Default disabled. Uncomment to enable. +// ENABLE_PIECEWISE_LINEAR_SPINDLE is not compatible with the SEPARATE_SPINDLE_LASER_PIN option +#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE + #ifdef SEPARATE_SPINDLE_LASER_PIN + #error ENABLE_PIECEWISE_LINEAR_SPINDLE compile option is not compatible with the SEPARATE_SPINDLE_LASER_PIN option + #endif +#endif + // N_PIECES, RPM_MAX, RPM_MIN, RPM_POINTxx, and RPM_LINE_XX constants are all set and given by // the 'fit_nonlinear_spindle.py' script solution. Used only when ENABLE_PIECEWISE_LINEAR_SPINDLE diff --git a/grbl/cpu_map.h b/grbl/cpu_map.h index 349a7c487..21e48d543 100644 --- a/grbl/cpu_map.h +++ b/grbl/cpu_map.h @@ -301,11 +301,14 @@ // Spindle PWM configuration : // list of timers in Arduino Mega 2560 - // TIMER0 (controls pin 13, 4); => Timer0 is used by stepper.c - // TIMER1 (controls pin 12, 11); => Timer1 is used by stepper.c - // TIMER2 (controls pin 10, 9); => Timer2 is used by analog output or spindle PWM on D9 - // TIMER3 (controls pin 5, 3, 2); => Timer3 is used by sleep.c - // TIMER4 (controls pin 8, 7, 6); => Timer4 is used by analog output or spindle PWM on D8 or D6 + // TIMER0 (controls pin D13, D4); => Timer0 is used by stepper.c + // TIMER1 (controls pin D12, D11); => Timer1 is used by stepper.c + // TIMER2 (controls pin D10, D9); => Timer2 is used by analog output or spindle PWM on D9 + // TIMER3 (controls pin D5, D3, D2); => Timer3 is used by sleep.c + // TIMER4 (controls pin D8, D7, D6); => Timer4 is used by analog output or spindle PWM on D8 or D6 + // TIMER5 (controls pin D46, D45, D44); => Timer5 is unused by grbl-Mega-5X. It's possible to add + // PWM capability to ports D44 (RAMPS AUX-2), D45 (RAMPS AUX-4). + // D46 is not available for PWM because it's used by Z step. // Arduino pin number and the corresponding register for controlling the duty cycle : // Pin Register // 2 OCR3B @@ -410,11 +413,100 @@ #endif + #ifdef SEPARATE_SPINDLE_LASER_PIN + + #if defined (LASER_PWM_ON_D6) + + // Set Timer up to use TIMER4C which is attached to Digital Pin 6 - Ramps Servo 2 + #define LASER_PWM_MAX_VALUE 255.0 // Translates to about 1.9 kHz PWM frequency at 1/8 prescaler + #ifndef LASER_PWM_MIN_VALUE + #define LASER_PWM_MIN_VALUE 1 // Must be greater than zero. + #endif + #define LASER_PWM_OFF_VALUE 0 + #define LASER_PWM_RANGE (LASER_PWM_MAX_VALUE-LASER_PWM_MIN_VALUE) + + //Control Digital Pin 6 which is Servo 2 signal pin on Ramps 1.4 board + #define LASER_TCCRA_REGISTER TCCR4A + #define LASER_TCCRB_REGISTER TCCR4B + #define LASER_OCR_REGISTER OCR4A + #define LASER_COMB_BIT COM4A1 + + // 1/8 Prescaler, 8-bit Fast PWM mode + #define LASER_TCCRA_INIT_MASK (1<= NON_MODAL_DIGITAL_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_DIGITAL_IMMEDIATE_OFF)) { @@ -382,7 +380,9 @@ uint8_t gc_execute_line(char *line) gc_block.values.p = value; } break; - // case 'Q': // Not supported + #ifdef USE_OUTPUT_PWM + case 'Q': dword_bit = DWORD_Q; gc_block.values.q = value; break; + #endif case 'R': dword_bit = DWORD_R; gc_block.values.r = value; break; case 'S': dword_bit = DWORD_S; gc_block.values.s = value; break; case 'T': dword_bit = DWORD_T; @@ -438,7 +438,7 @@ uint8_t gc_execute_line(char *line) // Check for invalid negative values for words F, N, O, P, T, and S. // NOTE: Negative value check is done here simply for code-efficiency. #ifdef USE_OUTPUT_PWM - if ( dwbit(dword_bit) & (dwbit(DWORD_F)|dwbit(DWORD_N)|dwbit(DWORD_O)|dwbit(DWORD_P)|dwbit(DWORD_T)|dwbit(DWORD_S)) ) { + if ( dwbit(dword_bit) & (dwbit(DWORD_F)|dwbit(DWORD_N)|dwbit(DWORD_Q)|dwbit(DWORD_P)|dwbit(DWORD_T)|dwbit(DWORD_S)) ) { #else if ( dwbit(dword_bit) & (dwbit(DWORD_F)|dwbit(DWORD_N)|dwbit(DWORD_P)|dwbit(DWORD_T)|dwbit(DWORD_S)) ) { #endif @@ -544,9 +544,11 @@ uint8_t gc_execute_line(char *line) if (bit_isfalse(value_dwords,dwbit(DWORD_S))) { gc_block.values.s = gc_state.spindle_speed; } // bit_false(value_dwords,dwbit(DWORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. - // [4.bis Set output PWM value]: O is negative (done.) - if (bit_isfalse(value_dwords,dwbit(DWORD_O))) { gc_block.values.o = gc_state.output_volts; } - // bit_false(value_dwords,dwbit(DWORD_O)); // NOTE: Single-meaning value word. Set at end of error-checking. + #ifdef USE_OUTPUT_PWM + // [4.bis Set output PWM value]: Q value missing, Q is negative (done.) + if (bit_isfalse(value_dwords,dwbit(DWORD_Q))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [Q word missing] + // bit_false(value_dwords,dwbit(DWORD_Q)); // NOTE: Single-meaning value word. Set at end of error-checking. + #endif // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. // bit_false(value_dwords,dwbit(DWORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. @@ -1236,7 +1238,7 @@ uint8_t gc_execute_line(char *line) bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F))); } else { #ifdef USE_OUTPUT_PWM - bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F)|dwbit(DWORD_O)|dwbit(DWORD_S)|dwbit(DWORD_T))); // Remove single-meaning value words. + bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F)|dwbit(DWORD_Q)|dwbit(DWORD_S)|dwbit(DWORD_T))); // Remove single-meaning value words. #else bit_false(value_dwords,(dwbit(DWORD_N)|dwbit(DWORD_F)|dwbit(DWORD_S)|dwbit(DWORD_T))); // Remove single-meaning value words. #endif @@ -1567,37 +1569,40 @@ uint8_t gc_execute_line(char *line) #ifdef USE_OUTPUT_PWM // [23. Output PWM ]: - if ((gc_state.output_volts != gc_block.values.o) || ((gc_block.non_modal_command >= NON_MODAL_ANALOG_SYNC_ON) && (gc_block.non_modal_command <= NON_MODAL_ANALOG_IMMEDIATE_OFF))) { + if ((gc_state.output_volts != gc_block.values.q) || ((gc_block.non_modal_command >= NON_MODAL_ANALOG_OUTPUT_SYNC) && (gc_block.non_modal_command <= NON_MODAL_ANALOG_OUTPUT_IMMEDIATE))) { switch(gc_block.non_modal_command) { - case NON_MODAL_ANALOG_SYNC_ON: // M162 - output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.o); - gc_state.output_last_command = gc_block.non_modal_command; - break; - case NON_MODAL_ANALOG_SYNC_OFF: // M163 - output_pwm_sync(OUTPUT_PWM_STATE_OFF, 0.0); - gc_state.output_last_command = gc_block.non_modal_command; - break; - case NON_MODAL_ANALOG_IMMEDIATE_ON: // M164 - output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.o); + case NON_MODAL_ANALOG_OUTPUT_SYNC: // M67 + if (gc_block.values.q != 0.0) { + output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.q); + } else { + output_pwm_sync(OUTPUT_PWM_STATE_OFF, 0.0); + } gc_state.output_last_command = gc_block.non_modal_command; break; - case NON_MODAL_ANALOG_IMMEDIATE_OFF: // M165 - output_pwm_set_state(OUTPUT_PWM_STATE_OFF, 0.0); + case NON_MODAL_ANALOG_OUTPUT_IMMEDIATE: // M68 + if (gc_block.values.q != 0.0) { + output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.q); + } else { + output_pwm_set_state(OUTPUT_PWM_STATE_OFF, 0.0); + } gc_state.output_last_command = gc_block.non_modal_command; break; default: - // Changement de la valeur de sortie en dehors d'une commande M162-M165 + // Changement de la valeur de sortie en dehors d'une commande M67/M68 // En fonction de la dernière commande stockée dans gc_state.output_last, - // on change immediatement ou synchronisé si NON_MODAL_ANALOG_SYNC_ON ou NON_MODAL_ANALOG_IMMEDIATE_ON - // ou on laissera off si NON_MODAL_ANALOG_SYNC_OFF ou NON_MODAL_ANALOG_IMMEDIATE_OFF. - if (gc_state.output_last_command == NON_MODAL_ANALOG_SYNC_ON) { - output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.o); - } else if (gc_state.output_last_command == NON_MODAL_ANALOG_IMMEDIATE_ON) { - output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.o); + // on change immediatement ou synchronisé si respectivement NON_MODAL_ANALOG_OUTPUT_SYNC + // ou NON_MODAL_ANALOG_OUTPUT_IMMEDIATE. + // On laissera off si OUTPUT_PWM_STATE_OFF en cours (dernière action avec valeur 0.0). + if (gc_state.output_volts != 0.0) { + if (gc_state.output_last_command == NON_MODAL_ANALOG_OUTPUT_SYNC) { + output_pwm_sync(OUTPUT_PWM_STATE_ON, gc_block.values.q); + } else if (gc_state.output_last_command == NON_MODAL_ANALOG_OUTPUT_IMMEDIATE) { + output_pwm_set_state(OUTPUT_PWM_STATE_ON, gc_block.values.q); + } } break; } - gc_state.output_volts = gc_block.values.o; // Update output volt state gc_state.spindle . + gc_state.output_volts = gc_block.values.q; // Update output volt state gc_state.output_volts . } #endif diff --git a/grbl/gcode.h b/grbl/gcode.h index 278c34a21..75f4f6b57 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -145,10 +145,8 @@ #ifdef USE_OUTPUT_PWM // Modal Group M11: Analog output (PWM) (non modal) - #define NON_MODAL_ANALOG_SYNC_ON 162 // M162 - #define NON_MODAL_ANALOG_SYNC_OFF 163 // M163 - #define NON_MODAL_ANALOG_IMMEDIATE_ON 164 // M164 - #define NON_MODAL_ANALOG_IMMEDIATE_OFF 165 // M165 + #define NON_MODAL_ANALOG_OUTPUT_SYNC 67 // M162 + #define NON_MODAL_ANALOG_OUTPUT_IMMEDIATE 68 // M164 #endif // Modal Group G12: Active work coordinate system // N/A: Stores coordinate system value (54-59) to change to. @@ -175,7 +173,7 @@ #define DWORD_V 17 #define DWORD_W 18 #ifdef USE_OUTPUT_PWM - #define DWORD_O 19 + #define DWORD_Q 19 #endif // Define g-code parser position updating flags #define GC_UPDATE_POS_TARGET 0 // Must be zero @@ -233,7 +231,7 @@ typedef struct { uint8_t l; // G10 or canned cycles parameters int32_t n; // Line number #ifdef USE_OUTPUT_PWM - float o; // Output PWM valus + float q; // Output PWM valus #endif float p; // G10 or dwell parameters float r; // Arc radius diff --git a/grbl/print.c b/grbl/print.c index 230e569ce..4f4b4c5e6 100644 --- a/grbl/print.c +++ b/grbl/print.c @@ -95,6 +95,26 @@ void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) { } +void print_uint16_base10(uint16_t n) +{ + if (n == 0) { + serial_write('0'); + return; + } + + unsigned char buf[10]; + uint8_t i = 0; + + while (n > 0) { + buf[i++] = n % 10; + n /= 10; + } + + for (; i > 0; i--) + serial_write('0' + buf[i-1]); +} + + void print_uint32_base10(uint32_t n) { if (n == 0) { diff --git a/grbl/print.h b/grbl/print.h index 017ef5277..91a9c2c51 100644 --- a/grbl/print.h +++ b/grbl/print.h @@ -30,6 +30,7 @@ void printPgmString(const char *s); void printInteger(long n); +void print_uint16_base10(uint16_t n); void print_uint32_base10(uint32_t n); // Prints an uint8 variable in base 10. diff --git a/grbl/report.c b/grbl/report.c index ccca43753..3c3e518ac 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -30,6 +30,8 @@ #include "grbl.h" #include +static uint8_t report_grbl_settings_running; + // Internal report utilities to reduce flash with repetitive tasks turned into functions. void report_util_setting_prefix(uint8_t n) { serial_write('$'); print_uint8_base10(n); serial_write('='); } static void report_util_line_feed() { printPgmString(PSTR("\r\n")); } @@ -255,6 +257,7 @@ void report_grbl_help() { // NOTE: The numbering scheme here must correlate to storing in settings.c void report_grbl_settings() { // Print Grbl settings. + report_grbl_settings_running = 1; // Flag for long report report_util_uint8_setting(0,settings.pulse_microseconds); report_util_uint8_setting(1,settings.stepper_idle_lock_time); report_util_uint8_setting(2,settings.step_invert_mask); @@ -277,6 +280,10 @@ void report_grbl_settings() { report_util_float_setting(30,settings.rpm_max,N_DECIMAL_RPMVALUE); report_util_float_setting(31,settings.rpm_min,N_DECIMAL_RPMVALUE); report_util_uint8_setting(32,bit_istrue(settings.flags,BITFLAG_LASER_MODE)); + #ifdef SEPARATE_SPINDLE_LASER_PIN + report_util_float_setting(33,settings.laser_max, N_DECIMAL_SETTINGVALUE); + report_util_float_setting(34,settings.laser_min, N_DECIMAL_SETTINGVALUE); + #endif #ifdef USE_OUTPUT_PWM report_util_float_setting(35,settings.volts_max, N_DECIMAL_SETTINGVALUE); report_util_float_setting(36,settings.volts_min, N_DECIMAL_SETTINGVALUE); @@ -295,6 +302,11 @@ void report_grbl_settings() { } val += AXIS_SETTINGS_INCREMENT; } + report_grbl_settings_running = 0; +} + +uint8_t is_report_grbl_settings_running() { + return report_grbl_settings_running; } @@ -512,13 +524,14 @@ void report_build_info(char *line) report_util_feedback_line_feed(); printPgmString(PSTR("[OPT:")); // Generate compile-time build option list //-------------------------------------------------------------------- - // ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789*$# => Lettre flag - // ! !!! !!! !!!!! ! ! !! !! !!! => ! = Utilisée + // ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789*$# => Option letter + // ! !!! !!! !!! !!! ! !! !! !!! => ! = Used //-------------------------------------------------------------------- - serial_write('V'); // Variable spindle standard. - serial_write('N'); // Line number reporting standard. - serial_write('M'); // M7 mist coolant standard. - serial_write('G'); // Safety door support standard. + + serial_write('V'); // Variable spindle, standard. + serial_write('N'); // Line number reporting, standard. + serial_write('M'); // M7 mist coolant, standard. + serial_write('G'); // Safety door support, standard. #ifdef COREXY serial_write('C'); #endif @@ -534,8 +547,14 @@ void report_build_info(char *line) #ifdef LIMITS_TWO_SWITCHES_ON_AXES serial_write('T'); #endif + #ifdef SEPARATE_SPINDLE_LASER_PIN + serial_write('S'); + #endif + #ifdef USE_DIGITAL_INPUT + serial_write('D'); + #endif #ifdef USE_OUTPUT_PWM - serial_write('O'); + serial_write('Q'); #endif #ifdef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES serial_write('A'); @@ -567,9 +586,6 @@ void report_build_info(char *line) #ifndef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // NOTE: Shown when disabled. serial_write('W'); #endif - #ifdef USE_DIGITAL_INPUT - serial_write('D'); - #endif // NOTE: Compiled values, like override increments/max/min values, may be added at some point later. serial_write(','); print_uint8_base10(BLOCK_BUFFER_SIZE-1); @@ -777,11 +793,18 @@ void report_realtime_status() #ifdef USE_OUTPUT_PWM uint8_t ot_state = output_pwm_get_state(); if (ot_state) { // Analog output (PWM) in active - printPgmString(PSTR("|O:")); - printFloat(sys.output_volts, N_DECIMAL_SETTINGVALUE); + if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) { + // If reporting in machine position, print the PWM value of output + printPgmString(PSTR("|Qm:")); + print_uint16_base10(output_compute_pwm_value(sys.output_volts)); + } else { + // If reporting in working position, print the output_volts value + printPgmString(PSTR("|Qw:")); + printFloat(sys.output_volts, N_DECIMAL_SETTINGVALUE); + } /* pour debug calculs PWM * serial_write(','); - * print_uint8_base10(output_compute_pwm_value(sys.output_volts)); + * print_uint16_base10(output_compute_pwm_value(sys.output_volts)); * serial_write(','); * printFloat(o_pwm_gradient(), N_DECIMAL_SETTINGVALUE); */ @@ -828,9 +851,9 @@ void report_debug_string(char *line) report_util_line_feed(); } -// Report debug int value -// This function accept the variable's name string as optional argument -void report_debug_int(uint8_t val, ...) +// Report debug int_8 and int_16 value +// Those functions accept the variable's name string as optional argument +void report_debug_int_8(uint8_t val, ...) { va_list args; char *valname; @@ -848,4 +871,45 @@ void report_debug_int(uint8_t val, ...) printPgmString(PSTR(")}")); report_util_line_feed(); } + +void report_debug_int_16(uint16_t val, ...) +{ + va_list args; + char *valname; + + va_start(args, val); + valname = va_arg(args, char *); + va_end(args); + + printPgmString(PSTR("{debug(")); + if ( valname != NULL ) { + printString(valname); + printString(" = "); + } + print_uint16_base10(val); + printPgmString(PSTR(")}")); + report_util_line_feed(); +} + +// Report debug float value +// This function accept the variable's name string as optional argument +void report_debug_float(float val, ...) +{ + va_list args; + char *valname; + + va_start(args, val); + valname = va_arg(args, char *); + va_end(args); + + printPgmString(PSTR("{debug(")); + if ( valname != NULL ) { + printString(valname); + printString(" = "); + } + printFloat(val, N_DECIMAL_SETTINGVALUE); + printPgmString(PSTR(")}")); + report_util_line_feed(); +} + #endif // DEBUG diff --git a/grbl/report.h b/grbl/report.h index b3bb30643..33cd9184b 100644 --- a/grbl/report.h +++ b/grbl/report.h @@ -102,6 +102,7 @@ void report_grbl_help(); // Prints Grbl global settings void report_grbl_settings(); +uint8_t is_report_grbl_settings_running(); // Prints an echo of the pre-parsed line received right before execution. void report_echo_line_received(char *line); @@ -131,7 +132,8 @@ void printDgState(uint8_t dg_state); #ifdef DEBUG void report_debug_string(char *line); - void report_debug_int(uint8_t val, ...); + void report_debug_int_8(uint8_t val, ...); + void report_debug_int_16(uint16_t val, ...); #endif #endif diff --git a/grbl/settings.c b/grbl/settings.c index 906daf995..60722e645 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -34,9 +34,13 @@ const __flash settings_t defaults = { .arc_tolerance = DEFAULT_ARC_TOLERANCE, .rpm_max = DEFAULT_SPINDLE_RPM_MAX, .rpm_min = DEFAULT_SPINDLE_RPM_MIN, + #ifdef SEPARATE_SPINDLE_LASER_PIN + .laser_max = DEFAULT_LASER_MAX, + .laser_min = DEFAULT_LASER_MIN, + #endif #ifdef USE_OUTPUT_PWM - .volts_max = DEFAULT_OUTPUT_PWM_MAX, - .volts_min = DEFAULT_OUTPUT_PWM_MIN, + .volts_max = DEFAULT_OUTPUT_MAX, + .volts_min = DEFAULT_OUTPUT_MIN, #endif .homing_dir_mask = DEFAULT_HOMING_DIR_MASK, .homing_feed_rate = DEFAULT_HOMING_FEED_RATE, @@ -312,7 +316,14 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) { case 32: if (int_value) { settings.flags |= BITFLAG_LASER_MODE; } else { settings.flags &= ~BITFLAG_LASER_MODE; } + #ifdef SEPARATE_SPINDLE_LASER_PIN + spindle_init(); // Re-initialize spindle / laser calibration + #endif break; + #ifdef SEPARATE_SPINDLE_LASER_PIN + case 33: settings.laser_max = value; spindle_init(); break; // Re-initialize laser calibration + case 34: settings.laser_min = value; spindle_init(); break; // Re-initialize laser calibration + #endif #ifdef USE_OUTPUT_PWM case 35: settings.volts_max = value; output_pwm_init(); break; case 36: settings.volts_min = value; output_pwm_init(); break; @@ -332,7 +343,14 @@ void settings_init() { report_status_message(STATUS_SETTING_READ_FAIL); settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data. report_grbl_settings(); + do {} while (is_report_grbl_settings_running()); // Wait for report settings complete } + #ifdef DEBUG + else + { + report_debug_string("settings_init() Ok."); + } + #endif } diff --git a/grbl/settings.h b/grbl/settings.h index dab29ee31..10d9dd7bf 100644 --- a/grbl/settings.h +++ b/grbl/settings.h @@ -103,7 +103,10 @@ typedef struct { float rpm_max; float rpm_min; - + #ifdef SEPARATE_SPINDLE_LASER_PIN + float laser_max; + float laser_min; + #endif #ifdef USE_OUTPUT_PWM float volts_max; float volts_min; diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index 6e8439f0e..3d9de3f74 100644 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -27,7 +27,21 @@ static float spindle_pwm_gradient; // Precalulated value to speed up rpm to PWM void spindle_init() -{ +{ + #ifdef SEPARATE_SPINDLE_LASER_PIN + if (settings.flags & BITFLAG_LASER_MODE) { + // In laser mode, spindle routines use laser pin output + // Force PWM output zero to avoid PWM output flash on laser output + LASER_TCCRA_REGISTER &= ~(1<= settings.laser_max) || (rpm >= settings.laser_max)) { + // No PWM range possible. Set simple on/off pin state. + sys.spindle_speed = settings.laser_max; + pwm_value = LASER_PWM_MAX_VALUE; + } else if (rpm <= settings.laser_min) { + if (rpm == 0.0) { // S0 disables laser + sys.spindle_speed = 0.0; + pwm_value = LASER_PWM_OFF_VALUE; + } else { // Set minimum PWM output + sys.spindle_speed = settings.laser_min; + pwm_value = LASER_PWM_MIN_VALUE; + } + } else { + // Compute intermediate PWM value with linear laser power model. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. + sys.spindle_speed = rpm; + pwm_value = floor((rpm - settings.laser_min) * spindle_pwm_gradient) + LASER_PWM_MIN_VALUE; + } + } else { + #endif // Calculate PWM register value based on rpm max/min settings and programmed rpm. if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) { // No PWM range possible. Set simple on/off spindle control pin state. @@ -166,6 +252,9 @@ void spindle_set_speed(uint16_t pwm_value) sys.spindle_speed = rpm; pwm_value = floor((rpm - settings.rpm_min) * spindle_pwm_gradient) + SPINDLE_PWM_MIN_VALUE; } + #ifdef SEPARATE_SPINDLE_LASER_PIN + } + #endif #ifndef INVERT_SPINDLE_PWM_VALUES return(pwm_value); #else From 4416bd4d51462c7380109c0d0246d3333d30c6be Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 26 Aug 2022 17:53:20 +0200 Subject: [PATCH 096/101] typo error correct in digital_control.c issue #283 --- grbl/digital_control.c | 2 +- grbl/grbl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/digital_control.c b/grbl/digital_control.c index 70d4643b0..e6bf9a264 100644 --- a/grbl/digital_control.c +++ b/grbl/digital_control.c @@ -180,7 +180,7 @@ void digital_set_state(uint8_t mode) DIGITAL_OUTPUT_PORT_1 |= (1 << DIGITAL_OUTPUT_BIT_1); #endif } else { - #ifdef INVERT_DIGITAL_OUTPUT_PIN_0 + #ifdef INVERT_DIGITAL_OUTPUT_PIN_1 DIGITAL_OUTPUT_PORT_1 |= (1 << DIGITAL_OUTPUT_BIT_1); #else DIGITAL_OUTPUT_PORT_1 &= ~(1 << DIGITAL_OUTPUT_BIT_1); diff --git a/grbl/grbl.h b/grbl/grbl.h index 606739e17..e66c26c0c 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.2g" -#define GRBL_VERSION_BUILD "20220620" +#define GRBL_VERSION_BUILD "20220826" // Define standard libraries used by Grbl. #include From bf7ec521818c891e1fbf7efa8dc6cbb11278238b Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 17 Sep 2022 11:28:42 +0200 Subject: [PATCH 097/101] Issue #112, bug error:28 correction new version 1.2g build 20220917 --- Makefile | 5 ++--- grbl/gcode.c | 8 +++++--- grbl/gcode.h | 2 +- grbl/grbl.h | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index bee1eb014..573f604c6 100644 --- a/Makefile +++ b/Makefile @@ -36,9 +36,8 @@ DEVICE_PORT ?= /dev/ttyUSB0 PROGRAMMER ?= -D -v -c avrisp2 -P $(DEVICE_PORT) SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c digital_control.c\ - serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \ - print.c probe.c report.c system.c sleep.c jog.c -# analog_control.c is in the analog_control branch... + analog_control.c serial.c protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c\ + limits.c print.c probe.c report.c system.c sleep.c jog.c BUILDDIR = build SOURCEDIR = grbl diff --git a/grbl/gcode.c b/grbl/gcode.c index 7db6f410a..86f0b65e3 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -548,9 +548,11 @@ uint8_t gc_execute_line(char *line) // bit_false(value_dwords,dwbit(DWORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. #ifdef USE_OUTPUT_PWM - // [4.bis Set output PWM value]: Q value missing, Q is negative (done.) - if (bit_isfalse(value_dwords,dwbit(DWORD_Q))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [Q word missing] - // bit_false(value_dwords,dwbit(DWORD_Q)); // NOTE: Single-meaning value word. Set at end of error-checking. + if (dword_bit == MODAL_GROUP_M11) { + // [4.bis Set output PWM value]: Q value missing, Q is negative (done.) + if (bit_isfalse(value_dwords,dwbit(DWORD_Q))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [Q word missing] + // bit_false(value_dwords,dwbit(DWORD_Q)); // NOTE: Single-meaning value word. Set at end of error-checking. + } #endif // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. diff --git a/grbl/gcode.h b/grbl/gcode.h index 75f4f6b57..d35d80f05 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -48,7 +48,7 @@ #define MODAL_GROUP_M9 14 // [M56] Override control #define MODAL_GROUP_M10 15 // [M62-M65] Digital output -Non-modal #ifdef USE_OUTPUT_PWM - #define MODAL_GROUP_M11 16 // [M162-M165] Analog output -Non-modal + #define MODAL_GROUP_M11 16 // [M67-M68] Analog output -Non-modal #endif // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used diff --git a/grbl/grbl.h b/grbl/grbl.h index e66c26c0c..e8d97dbb8 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -24,7 +24,7 @@ // Grbl versioning system #define GRBL_VERSION "1.2g" -#define GRBL_VERSION_BUILD "20220826" +#define GRBL_VERSION_BUILD "20220917" // Define standard libraries used by Grbl. #include From 8864f2d249c0abc518b143b9b08cf8b30eb9af40 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 9 Jan 2023 11:11:03 +0100 Subject: [PATCH 098/101] Adding serial RX buffer overflow alarm number 11 --- doc/csv/alarm_codes_en_US.csv | 1 + grbl/serial.c | 3 +++ grbl/system.h | 1 + 3 files changed, 5 insertions(+) diff --git a/doc/csv/alarm_codes_en_US.csv b/doc/csv/alarm_codes_en_US.csv index b9014d502..68d168c0d 100644 --- a/doc/csv/alarm_codes_en_US.csv +++ b/doc/csv/alarm_codes_en_US.csv @@ -9,3 +9,4 @@ "8","Homing fail","Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring." "9","Homing fail","Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring." "10","Homing fail","Homing fail. Max travel is shorter than the pull off travel." +"11","Serial overflow","Serial RX buffer overflow." diff --git a/grbl/serial.c b/grbl/serial.c index ef0c03cb8..3f33d14a3 100644 --- a/grbl/serial.c +++ b/grbl/serial.c @@ -192,6 +192,9 @@ ISR(SERIAL_RX) if (next_head != serial_rx_buffer_tail) { serial_rx_buffer[serial_rx_buffer_head] = data; serial_rx_buffer_head = next_head; + } else { + // Indicate serial buffer overflow critical event. + system_set_exec_alarm(EXEC_ALARM_SERIAL_RX_OVERFLOW); } } } diff --git a/grbl/system.h b/grbl/system.h index 11da2e7c2..b17e751ad 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -49,6 +49,7 @@ #define EXEC_ALARM_HOMING_FAIL_PULLOFF 8 #define EXEC_ALARM_HOMING_FAIL_APPROACH 9 #define EXEC_ALARM_HOMING_FAIL_TRAVEL 10 +#define EXEC_ALARM_SERIAL_RX_OVERFLOW 11 // Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides. // Spindle/coolant and feed/rapids are separated into two controlling flag variables. From 22e19aadbf44a8c341313ca4cc89b2decac746b8 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 9 Jan 2023 11:34:09 +0100 Subject: [PATCH 099/101] Dont send 2 OK when character is LF just after a CR --- grbl/protocol.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/grbl/protocol.c b/grbl/protocol.c index 0863cfc0c..acffc1c12 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -72,6 +72,8 @@ void protocol_main_loop() uint8_t line_flags = 0; uint8_t char_counter = 0; uint8_t c; + uint8_t crlf_flag = 0; + for (;;) { // Process one line of incoming serial data, as the data becomes available. Performs an @@ -79,6 +81,14 @@ void protocol_main_loop() while((c = serial_read()) != SERIAL_NO_DATA) { if ((c == '\n') || (c == '\r')) { // End of line reached + if (c == '\r') { + // Track CR to detect Windows CRLF + crlf_flag = 1; + } else if (c != '\n') { + // This is not a CRLF sequence + crlf_flag = 0; + } + protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to calling function upon system abort @@ -92,8 +102,12 @@ void protocol_main_loop() // Report line overflow error. report_status_message(STATUS_OVERFLOW); } else if (line[0] == 0) { - // Empty or comment line. For syncing purposes. - report_status_message(STATUS_OK); + // Dont send 2 OK when character is LF just after a CR + // Send the OK reply when CR is received or if LF is received without CR just before. + if ((c == '\r') || ((c == '\n') && (crlf_flag != 1))) { + // Empty or comment line. For syncing purposes. + report_status_message(STATUS_OK); + } } else if (line[0] == '$') { // Grbl '$' system command report_status_message(system_execute_line(line)); From 181cface4f8e5dafe436fb0a3c933f045c5fffc0 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 9 Jan 2023 11:42:58 +0100 Subject: [PATCH 100/101] Update version to 1.2h - Patch to issue #312 --- grbl/grbl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grbl/grbl.h b/grbl/grbl.h index e8d97dbb8..3e6656f97 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,8 +23,8 @@ #define grbl_h // Grbl versioning system -#define GRBL_VERSION "1.2g" -#define GRBL_VERSION_BUILD "20220917" +#define GRBL_VERSION "1.2h" +#define GRBL_VERSION_BUILD "20220109" // Define standard libraries used by Grbl. #include From a5596efc14a0fcda585bbb27a472f5b75e322759 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 20 Oct 2024 11:36:11 +0200 Subject: [PATCH 101/101] Add homing of rotary axis with max_travel = 0 --- grbl/limits.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grbl/limits.c b/grbl/limits.c index 5e170a57c..cff3a8ebf 100644 --- a/grbl/limits.c +++ b/grbl/limits.c @@ -249,6 +249,8 @@ void limits_go_home(uint8_t cycle_mask) // Set target based on max_travel setting. Ensure homing switches engaged with search scalar. // NOTE: settings.max_travel[] is stored as a negative value. max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]); + // Special case of rotary axis with max_travel = 0 + if ((idx >= N_AXIS_LINEAR) && (max_travel == 0)) { max_travel = HOMING_AXIS_SEARCH_SCALAR * 360.0; } if (max_travel < HOMING_AXIS_LOCATE_SCALAR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_TRAVEL); } } }