blob: 1348c147ef77660e784eac8d8c70bdde87bd9c93 [file] [log] [blame]
Luigi Santivetti69972f92019-11-12 22:55:40 +00001/*
2 limits.c - code pertaining to limit-switches and performing the homing cycle
3 Part of Grbl
4
5 Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
6 Copyright (c) 2009-2011 Simen Svale Skogsrud
7
8 Grbl is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Grbl is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Grbl. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "grbl.h"
23
24
25// Homing axis search distance multiplier. Computed by this value times the cycle travel.
26#ifndef HOMING_AXIS_SEARCH_SCALAR
27 #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
28#endif
29#ifndef HOMING_AXIS_LOCATE_SCALAR
30 #define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared.
31#endif
32
33#ifdef ENABLE_DUAL_AXIS
34 // Flags for dual axis async limit trigger check.
35 #define DUAL_AXIS_CHECK_DISABLE 0 // Must be zero
36 #define DUAL_AXIS_CHECK_ENABLE bit(0)
37 #define DUAL_AXIS_CHECK_TRIGGER_1 bit(1)
38 #define DUAL_AXIS_CHECK_TRIGGER_2 bit(2)
39#endif
40
41void limits_init()
42{
43 LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
44
45 #ifdef DISABLE_LIMIT_PIN_PULL_UP
46 LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
47 #else
48 LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
49 #endif
50
51 if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
52 LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
53 PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
54 } else {
55 limits_disable();
56 }
57
58 #ifdef ENABLE_SOFTWARE_DEBOUNCE
59 MCUSR &= ~(1<<WDRF);
60 WDTCSR |= (1<<WDCE) | (1<<WDE);
61 WDTCSR = (1<<WDP0); // Set time-out at ~32msec.
62 #endif
63}
64
65
66// Disables hard limits.
67void limits_disable()
68{
69 LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt
70 PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
71}
72
73
74// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
75// triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their
76// number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1.
77uint8_t limits_get_state()
78{
79 uint8_t limit_state = 0;
80 uint8_t pin = (LIMIT_PIN & LIMIT_MASK);
81 #ifdef INVERT_LIMIT_PIN_MASK
82 pin ^= INVERT_LIMIT_PIN_MASK;
83 #endif
84 if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { pin ^= LIMIT_MASK; }
85 if (pin) {
86 uint8_t idx;
87 for (idx=0; idx<N_AXIS; idx++) {
88 if (pin & get_limit_pin_mask(idx)) { limit_state |= (1 << idx); }
89 }
90 #ifdef ENABLE_DUAL_AXIS
91 if (pin & (1<<DUAL_LIMIT_BIT)) { limit_state |= (1 << N_AXIS); }
92 #endif
93 }
94 return(limit_state);
95}
96
97
98// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
99// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
100// If a switch is triggered at all, something bad has happened and treat it as such, regardless
101// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
102// bouncing pin because the Arduino microcontroller does not retain any state information when
103// detecting a pin change. If we poll the pins in the ISR, you can miss the correct reading if the
104// switch is bouncing.
105// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
106// homing cycles and will not respond correctly. Upon user request or need, there may be a
107// special pinout for an e-stop, but it is generally recommended to just directly connect
108// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
109#ifndef ENABLE_SOFTWARE_DEBOUNCE
110 ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
111 {
112 // Ignore limit switches if already in an alarm state or in-process of executing an alarm.
113 // When in the alarm state, Grbl should have been reset or will force a reset, so any pending
114 // moves in the planner and serial buffers are all cleared and newly sent blocks will be
115 // locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
116 // limit setting if their limits are constantly triggering after a reset and move their axes.
117 if (sys.state != STATE_ALARM) {
118 if (!(sys_rt_exec_alarm)) {
119 #ifdef HARD_LIMIT_FORCE_STATE_CHECK
120 // Check limit pin state.
121 if (limits_get_state()) {
122 mc_reset(); // Initiate system kill.
123 system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
124 }
125 #else
126 mc_reset(); // Initiate system kill.
127 system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
128 #endif
129 }
130 }
131 }
132#else // OPTIONAL: Software debounce limit pin routine.
133 // Upon limit pin change, enable watchdog timer to create a short delay.
134 ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
135 ISR(WDT_vect) // Watchdog timer ISR
136 {
137 WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
138 if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
139 if (!(sys_rt_exec_alarm)) {
140 // Check limit pin state.
141 if (limits_get_state()) {
142 mc_reset(); // Initiate system kill.
143 system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
144 }
145 }
146 }
147 }
148#endif
149
150// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
151// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate
152// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
153// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
154// circumvent the processes for executing motions in normal operation.
155// NOTE: Only the abort realtime command can interrupt this process.
156// TODO: Move limit pin-specific calls to a general function for portability.
157void limits_go_home(uint8_t cycle_mask)
158{
159 if (sys.abort) { return; } // Block if system reset has been issued.
160
161 // Initialize plan data struct for homing motion. Spindle and coolant are disabled.
162 plan_line_data_t plan_data;
163 plan_line_data_t *pl_data = &plan_data;
164 memset(pl_data,0,sizeof(plan_line_data_t));
165 pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
166 #ifdef USE_LINE_NUMBERS
167 pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
168 #endif
169
170 // Initialize variables used for homing computations.
171 uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1);
172 uint8_t step_pin[N_AXIS];
173 #ifdef ENABLE_DUAL_AXIS
174 uint8_t step_pin_dual;
175 uint8_t dual_axis_async_check;
176 int32_t dual_trigger_position;
177 #if (DUAL_AXIS_SELECT == X_AXIS)
178 float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[Y_AXIS];
179 #else
180 float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[X_AXIS];
181 #endif
182 fail_distance = min(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX);
183 fail_distance = max(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN);
184 int32_t dual_fail_distance = trunc(fail_distance*settings.steps_per_mm[DUAL_AXIS_SELECT]);
185 // int32_t dual_fail_distance = trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]);
186 #endif
187 float target[N_AXIS];
188 float max_travel = 0.0;
189 uint8_t idx;
190 for (idx=0; idx<N_AXIS; idx++) {
191 // Initialize step pin masks
192 step_pin[idx] = get_step_pin_mask(idx);
193 #ifdef COREXY
194 if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
195 #endif
196
197 if (bit_istrue(cycle_mask,bit(idx))) {
198 // Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
199 // NOTE: settings.max_travel[] is stored as a negative value.
200 max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]);
201 }
202 }
203 #ifdef ENABLE_DUAL_AXIS
204 step_pin_dual = (1<<DUAL_STEP_BIT);
205 #endif
206
207 // Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches.
208 bool approach = true;
209 float homing_rate = settings.homing_seek_rate;
210
211 uint8_t limit_state, axislock, n_active_axis;
212 do {
213
214 system_convert_array_steps_to_mpos(target,sys_position);
215
216 // Initialize and declare variables needed for homing routine.
217 axislock = 0;
218 #ifdef ENABLE_DUAL_AXIS
219 sys.homing_axis_lock_dual = 0;
220 dual_trigger_position = 0;
221 dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
222 #endif
223 n_active_axis = 0;
224 for (idx=0; idx<N_AXIS; idx++) {
225 // Set target location for active axes and setup computation for homing rate.
226 if (bit_istrue(cycle_mask,bit(idx))) {
227 n_active_axis++;
228 #ifdef COREXY
229 if (idx == X_AXIS) {
230 int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
231 sys_position[A_MOTOR] = axis_position;
232 sys_position[B_MOTOR] = -axis_position;
233 } else if (idx == Y_AXIS) {
234 int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
235 sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position;
236 } else {
237 sys_position[Z_AXIS] = 0;
238 }
239 #else
240 sys_position[idx] = 0;
241 #endif
242 // Set target direction based on cycle mask and homing cycle approach state.
243 // NOTE: This happens to compile smaller than any other implementation tried.
244 if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
245 if (approach) { target[idx] = -max_travel; }
246 else { target[idx] = max_travel; }
247 } else {
248 if (approach) { target[idx] = max_travel; }
249 else { target[idx] = -max_travel; }
250 }
251 // Apply axislock to the step port pins active in this cycle.
252 axislock |= step_pin[idx];
253 #ifdef ENABLE_DUAL_AXIS
254 if (idx == DUAL_AXIS_SELECT) { sys.homing_axis_lock_dual = step_pin_dual; }
255 #endif
256 }
257
258 }
259 homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
260 sys.homing_axis_lock = axislock;
261
262 // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
263 pl_data->feed_rate = homing_rate; // Set current homing rate.
264 plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
265
266 sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
267 st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
268 st_wake_up(); // Initiate motion
269 do {
270 if (approach) {
271 // Check limit state. Lock out cycle axes when they change.
272 limit_state = limits_get_state();
273 for (idx=0; idx<N_AXIS; idx++) {
274 if (axislock & step_pin[idx]) {
275 if (limit_state & (1 << idx)) {
276 #ifdef COREXY
277 if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); }
278 else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); }
279 #else
280 axislock &= ~(step_pin[idx]);
281 #ifdef ENABLE_DUAL_AXIS
282 if (idx == DUAL_AXIS_SELECT) { dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_1; }
283 #endif
284 #endif
285 }
286 }
287 }
288 sys.homing_axis_lock = axislock;
289 #ifdef ENABLE_DUAL_AXIS
290 if (sys.homing_axis_lock_dual) { // NOTE: Only true when homing dual axis.
291 if (limit_state & (1 << N_AXIS)) {
292 sys.homing_axis_lock_dual = 0;
293 dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_2;
294 }
295 }
296
297 // When first dual axis limit triggers, record position and begin checking distance until other limit triggers. Bail upon failure.
298 if (dual_axis_async_check) {
299 if (dual_axis_async_check & DUAL_AXIS_CHECK_ENABLE) {
300 if (( dual_axis_async_check & (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) == (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) {
301 dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
302 } else {
303 if (abs(dual_trigger_position - sys_position[DUAL_AXIS_SELECT]) > dual_fail_distance) {
304 system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH);
305 mc_reset();
306 protocol_execute_realtime();
307 return;
308 }
309 }
310 } else {
311 dual_axis_async_check |= DUAL_AXIS_CHECK_ENABLE;
312 dual_trigger_position = sys_position[DUAL_AXIS_SELECT];
313 }
314 }
315 #endif
316 }
317
318 st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
319
320 // Exit routines: No time to run protocol_execute_realtime() in this loop.
321 if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
322 uint8_t rt_exec = sys_rt_exec_state;
323 // Homing failure condition: Reset issued during cycle.
324 if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
325 // Homing failure condition: Safety door was opened.
326 if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
327 // Homing failure condition: Limit switch still engaged after pull-off motion
328 if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); }
329 // Homing failure condition: Limit switch not found during approach.
330 if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); }
331 if (sys_rt_exec_alarm) {
332 mc_reset(); // Stop motors, if they are running.
333 protocol_execute_realtime();
334 return;
335 } else {
336 // Pull-off motion complete. Disable CYCLE_STOP from executing.
337 system_clear_exec_state_flag(EXEC_CYCLE_STOP);
338 break;
339 }
340 }
341
342 #ifdef ENABLE_DUAL_AXIS
343 } while ((STEP_MASK & axislock) || (sys.homing_axis_lock_dual));
344 #else
345 } while (STEP_MASK & axislock);
346 #endif
347
348 st_reset(); // Immediately force kill steppers and reset step segment buffer.
349 delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
350
351 // Reverse direction and reset homing rate for locate cycle(s).
352 approach = !approach;
353
354 // After first cycle, homing enters locating phase. Shorten search to pull-off distance.
355 if (approach) {
356 max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR;
357 homing_rate = settings.homing_feed_rate;
358 } else {
359 max_travel = settings.homing_pulloff;
360 homing_rate = settings.homing_seek_rate;
361 }
362
363 } while (n_cycle-- > 0);
364
365 // The active cycle axes should now be homed and machine limits have been located. By
366 // default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches
367 // can be on either side of an axes, check and set axes machine zero appropriately. Also,
368 // set up pull-off maneuver from axes limit switches that have been homed. This provides
369 // some initial clearance off the switches and should also help prevent them from falsely
370 // triggering when hard limits are enabled or when more than one axes shares a limit pin.
371 int32_t set_axis_position;
372 // Set machine positions for homed limit switches. Don't update non-homed axes.
373 for (idx=0; idx<N_AXIS; idx++) {
374 // NOTE: settings.max_travel[] is stored as a negative value.
375 if (cycle_mask & bit(idx)) {
376 #ifdef HOMING_FORCE_SET_ORIGIN
377 set_axis_position = 0;
378 #else
379 if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
380 set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
381 } else {
382 set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
383 }
384 #endif
385
386 #ifdef COREXY
387 if (idx==X_AXIS) {
388 int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
389 sys_position[A_MOTOR] = set_axis_position + off_axis_position;
390 sys_position[B_MOTOR] = set_axis_position - off_axis_position;
391 } else if (idx==Y_AXIS) {
392 int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
393 sys_position[A_MOTOR] = off_axis_position + set_axis_position;
394 sys_position[B_MOTOR] = off_axis_position - set_axis_position;
395 } else {
396 sys_position[idx] = set_axis_position;
397 }
398 #else
399 sys_position[idx] = set_axis_position;
400 #endif
401
402 }
403 }
404 sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
405}
406
407
408// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
409// the workspace volume is in all negative space, and the system is in normal operation.
410// NOTE: Used by jogging to limit travel within soft-limit volume.
411void limits_soft_check(float *target)
412{
413 if (system_check_travel_limits(target)) {
414 sys.soft_limit = true;
415 // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
416 // workspace volume so just come to a controlled stop so position is not lost. When complete
417 // enter alarm mode.
418 if (sys.state == STATE_CYCLE) {
419 system_set_exec_state_flag(EXEC_FEED_HOLD);
420 do {
421 protocol_execute_realtime();
422 if (sys.abort) { return; }
423 } while ( sys.state != STATE_IDLE );
424 }
425 mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
426 system_set_exec_alarm(EXEC_ALARM_SOFT_LIMIT); // Indicate soft limit critical event
427 protocol_execute_realtime(); // Execute to enter critical event loop and system abort
428 return;
429 }
430}