Merge v0.8 edge to master
This commit is contained in:
commit
c712af23ab
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
*.o
|
||||
*.elf
|
||||
*.DS_Store
|
||||
*.d
|
||||
|
20
Makefile
Normal file → Executable file
20
Makefile
Normal file → Executable file
@ -1,6 +1,7 @@
|
||||
# Part of Grbl
|
||||
#
|
||||
# Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
# Copyright (c) 2012 Sungeun K. Jeon
|
||||
#
|
||||
# Grbl is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -27,11 +28,12 @@
|
||||
# is connected.
|
||||
# FUSES ........ Parameters for avrdude to flash the fuses appropriately.
|
||||
|
||||
DEVICE = atmega328p
|
||||
DEVICE ?= atmega328p
|
||||
CLOCK = 16000000
|
||||
PROGRAMMER = -c avrisp2 -P usb
|
||||
OBJECTS = main.o motion_control.o gcode.o spindle_control.o serial.o protocol.o stepper.o \
|
||||
eeprom.o settings.o planner.o nuts_bolts.o limits.o print.o
|
||||
PROGRAMMER ?= -c avrisp2 -P usb
|
||||
OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \
|
||||
protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \
|
||||
print.o report.o
|
||||
# FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
|
||||
FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m
|
||||
# update that line with this when programmer is back up:
|
||||
@ -47,6 +49,7 @@ all: grbl.hex
|
||||
|
||||
.c.o:
|
||||
$(COMPILE) -c $< -o $@
|
||||
@$(COMPILE) -MM $< > $*.d
|
||||
|
||||
.S.o:
|
||||
$(COMPILE) -x assembler-with-cpp -c $< -o $@
|
||||
@ -72,7 +75,7 @@ load: all
|
||||
bootloadHID grbl.hex
|
||||
|
||||
clean:
|
||||
rm -f grbl.hex main.elf $(OBJECTS)
|
||||
rm -f grbl.hex main.elf $(OBJECTS) $(OBJECTS:.o=.d)
|
||||
|
||||
# file targets:
|
||||
main.elf: $(OBJECTS)
|
||||
@ -81,8 +84,7 @@ main.elf: $(OBJECTS)
|
||||
grbl.hex: main.elf
|
||||
rm -f grbl.hex
|
||||
avr-objcopy -j .text -j .data -O ihex main.elf grbl.hex
|
||||
avr-objdump -h main.elf | grep .bss | ruby -e 'puts "\n\n--- Requires %s bytes of SRAM" % STDIN.read.match(/0[0-9a-f]+\s/)[0].to_i(16)'
|
||||
avr-size *.hex *.elf *.o
|
||||
avr-size -C --mcu=$(DEVICE) main.elf
|
||||
# If you have an EEPROM section, you must also create a hex file for the
|
||||
# EEPROM and add it to the "flash" target.
|
||||
|
||||
@ -92,3 +94,7 @@ disasm: main.elf
|
||||
|
||||
cpp:
|
||||
$(COMPILE) -E main.c
|
||||
|
||||
# include generated header dependencies
|
||||
-include $(OBJECTS:.o=.d)
|
||||
|
||||
|
244
config.h
Normal file → Executable file
244
config.h
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -24,46 +24,86 @@
|
||||
|
||||
// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them.
|
||||
|
||||
// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
|
||||
#define DEFAULTS_GENERIC
|
||||
|
||||
// Serial baud rate
|
||||
#define BAUD_RATE 9600
|
||||
|
||||
// Updated default pin-assignments from 0.6 onwards
|
||||
// (see bottom of file for a copy of the old config)
|
||||
|
||||
#define STEPPERS_DISABLE_DDR DDRB
|
||||
#define STEPPERS_DISABLE_PORT PORTB
|
||||
#define STEPPERS_DISABLE_BIT 0
|
||||
|
||||
// Define pin-assignments
|
||||
// NOTE: All step bit and direction pins must be on the same port.
|
||||
#define STEPPING_DDR DDRD
|
||||
#define STEPPING_PORT PORTD
|
||||
#define X_STEP_BIT 2
|
||||
#define Y_STEP_BIT 3
|
||||
#define Z_STEP_BIT 4
|
||||
#define X_DIRECTION_BIT 5
|
||||
#define Y_DIRECTION_BIT 6
|
||||
#define Z_DIRECTION_BIT 7
|
||||
#define X_STEP_BIT 2 // Uno Digital Pin 2
|
||||
#define Y_STEP_BIT 3 // Uno Digital Pin 3
|
||||
#define Z_STEP_BIT 4 // Uno Digital Pin 4
|
||||
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
|
||||
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
|
||||
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
|
||||
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
|
||||
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
|
||||
|
||||
#define LIMIT_DDR DDRB
|
||||
#define LIMIT_PIN PINB
|
||||
#define X_LIMIT_BIT 1
|
||||
#define Y_LIMIT_BIT 2
|
||||
#define Z_LIMIT_BIT 3
|
||||
#define STEPPERS_DISABLE_DDR DDRB
|
||||
#define STEPPERS_DISABLE_PORT PORTB
|
||||
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
|
||||
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
|
||||
|
||||
#define SPINDLE_ENABLE_DDR DDRB
|
||||
#define SPINDLE_ENABLE_PORT PORTB
|
||||
#define SPINDLE_ENABLE_BIT 4
|
||||
// NOTE: All limit bit pins must be on the same port
|
||||
#define LIMIT_DDR DDRB
|
||||
#define LIMIT_PIN PINB
|
||||
#define LIMIT_PORT PORTB
|
||||
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
|
||||
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
|
||||
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
|
||||
#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<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
|
||||
|
||||
#define SPINDLE_DIRECTION_DDR DDRB
|
||||
#define SPINDLE_DIRECTION_PORT PORTB
|
||||
#define SPINDLE_DIRECTION_BIT 5
|
||||
#define SPINDLE_ENABLE_DDR DDRB
|
||||
#define SPINDLE_ENABLE_PORT PORTB
|
||||
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
|
||||
|
||||
// This parameter sets the delay time before disabling the steppers after the final block of movement.
|
||||
// A short delay ensures the steppers come to a complete stop and the residual inertial force in the
|
||||
// CNC axes don't cause the axes to drift off position. This is particularly important when manually
|
||||
// entering g-code into grbl, i.e. locating part zero or simple manual machining. If the axes drift,
|
||||
// grbl has no way to know this has happened, since stepper motors are open-loop control. Depending
|
||||
// on the machine, this parameter may need to be larger or smaller than the default time.
|
||||
// NOTE: If defined 0, the delay will not be compiled.
|
||||
#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer >= 0
|
||||
#define SPINDLE_DIRECTION_DDR DDRB
|
||||
#define SPINDLE_DIRECTION_PORT PORTB
|
||||
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
|
||||
|
||||
#define COOLANT_FLOOD_DDR DDRC
|
||||
#define COOLANT_FLOOD_PORT PORTC
|
||||
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
|
||||
|
||||
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
|
||||
// a later date if flash and memory space allows.
|
||||
// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable.
|
||||
#ifdef ENABLE_M7
|
||||
#define COOLANT_MIST_DDR DDRC
|
||||
#define COOLANT_MIST_PORT PORTC
|
||||
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
|
||||
#endif
|
||||
|
||||
// NOTE: All pinouts pins must be on the same port
|
||||
#define PINOUT_DDR DDRC
|
||||
#define PINOUT_PIN PINC
|
||||
#define PINOUT_PORT PORTC
|
||||
#define PIN_RESET 0 // Uno Analog Pin 0
|
||||
#define PIN_FEED_HOLD 1 // Uno Analog Pin 1
|
||||
#define PIN_CYCLE_START 2 // Uno Analog Pin 2
|
||||
#define PINOUT_INT PCIE1 // Pin change interrupt enable pin
|
||||
#define PINOUT_INT_vect PCINT1_vect
|
||||
#define PINOUT_PCMSK PCMSK1 // Pin change interrupt register
|
||||
#define PINOUT_MASK ((1<<PIN_RESET)|(1<<PIN_FEED_HOLD)|(1<<PIN_CYCLE_START))
|
||||
|
||||
// Define runtime 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
|
||||
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
|
||||
// g-code programs, maybe selected for interface programs.
|
||||
// NOTE: If changed, manually update help message in report.c.
|
||||
#define CMD_STATUS_REPORT '?'
|
||||
#define CMD_FEED_HOLD '!'
|
||||
#define CMD_CYCLE_START '~'
|
||||
#define CMD_RESET 0x18 // ctrl-x
|
||||
|
||||
// The temporal resolution of the acceleration management subsystem. Higher number give smoother
|
||||
// acceleration but may impact performance.
|
||||
@ -87,40 +127,108 @@
|
||||
// never reach its target. This parameter should always be greater than zero.
|
||||
#define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only
|
||||
|
||||
// Number of arc generation iterations by small angle approximation before exact arc trajectory
|
||||
// correction. This parameter maybe decreased if there are issues with the accuracy of the arc
|
||||
// generations. In general, the default value is more than enough for the intended CNC applications
|
||||
// of grbl, and should be on the order or greater than the size of the buffer to help with the
|
||||
// computational efficiency of generating arcs.
|
||||
#define N_ARC_CORRECTION 25 // Integer (1-255)
|
||||
// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
|
||||
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
|
||||
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
|
||||
// run-time command executions, like status reports, since these are performed between each dwell
|
||||
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
|
||||
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
|
||||
|
||||
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
|
||||
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
|
||||
// mainly a safety feature to remind the user to home, since position is unknown to Grbl.
|
||||
#define HOMING_INIT_LOCK // Comment to disable
|
||||
|
||||
// The homing cycle seek and feed rates will adjust so all axes independently move at the homing
|
||||
// seek and feed rates regardless of how many axes are in motion simultaneously. If disabled, rates
|
||||
// are point-to-point rates, as done in normal operation. For example in an XY diagonal motion, the
|
||||
// diagonal motion moves at the intended rate, but the individual axes move at 70% speed. This option
|
||||
// just moves them all at 100% speed.
|
||||
#define HOMING_RATE_ADJUST // Comment to disable
|
||||
|
||||
// Define the homing cycle search patterns with bitmasks. The homing cycle first performs a search
|
||||
// to engage the limit switches. HOMING_SEARCH_CYCLE_x are executed in order starting with suffix 0
|
||||
// and searches the enabled axes in the bitmask. This allows for users with non-standard cartesian
|
||||
// machines, such as a lathe (x then z), to configure the homing cycle behavior to their needs.
|
||||
// Search cycle 0 is required, but cycles 1 and 2 are both optional and may be commented to disable.
|
||||
// After the search cycle, homing then performs a series of locating about the limit switches to hone
|
||||
// in on machine zero, followed by a pull-off maneuver. HOMING_LOCATE_CYCLE governs these final moves,
|
||||
// and this mask must contain all axes in the search.
|
||||
// NOTE: Later versions may have this installed in settings.
|
||||
#define HOMING_SEARCH_CYCLE_0 (1<<Z_AXIS) // First move Z to clear workspace.
|
||||
#define HOMING_SEARCH_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // Then move X,Y at the same time.
|
||||
// #define HOMING_SEARCH_CYCLE_2 // Uncomment and add axes mask to enable
|
||||
#define HOMING_LOCATE_CYCLE ((1<<X_AXIS)|(1<<Y_AXIS)|(1<<Z_AXIS)) // Must contain ALL search axes
|
||||
|
||||
// Number of homing cycles performed after when the machine initially jogs to limit switches.
|
||||
// This help in preventing overshoot and should improve repeatability. This value should be one or
|
||||
// greater.
|
||||
#define N_HOMING_LOCATE_CYCLE 2 // Integer (1-128)
|
||||
|
||||
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
|
||||
// and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may
|
||||
// be stored and executed in order. These startup blocks would typically be used to set the g-code
|
||||
// parser state depending on user preferences.
|
||||
#define N_STARTUP_LINE 2 // Integer (1-5)
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// FOR ADVANCED USERS ONLY:
|
||||
|
||||
// The number of linear motions in the planner buffer to be planned at any give time. The vast
|
||||
// majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra
|
||||
// available RAM, like when re-compiling for a Teensy or Sanguino. Or decrease if the Arduino
|
||||
// begins to crash due to the lack of available RAM or if the CPU is having trouble keeping
|
||||
// up with planning new incoming motions as they are executed.
|
||||
// #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h.
|
||||
|
||||
// Line buffer size from the serial input stream to be executed. Also, governs the size of
|
||||
// each of the startup blocks, as they are each stored as a string of this size. Make sure
|
||||
// to account for the available EEPROM at the defined memory address in settings.h and for
|
||||
// the number of desired startup blocks.
|
||||
// NOTE: 50 characters is not a problem except for extreme cases, but the line buffer size
|
||||
// can be too small and g-code blocks can get truncated. Officially, the g-code standards
|
||||
// support up to 256 characters. In future versions, this default will be increased, when
|
||||
// we know how much extra memory space we can re-invest into this.
|
||||
// #define LINE_BUFFER_SIZE 50 // Uncomment to override default in protocol.h
|
||||
|
||||
// Serial send and receive buffer size. The receive buffer is often used as another streaming
|
||||
// buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming
|
||||
// interfaces will character count and track each block send to each block response. So,
|
||||
// increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable
|
||||
// memory allows. The send buffer primarily handles messages in Grbl. Only increase if large
|
||||
// messages are sent and Grbl begins to stall, waiting to send the rest of the message.
|
||||
// #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h
|
||||
// #define TX_BUFFER_SIZE 64
|
||||
|
||||
// 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.
|
||||
|
||||
// Creates a delay between the direction pin setting and corresponding step pulse by creating
|
||||
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
|
||||
// sets the direction pins, and does not immediately set the stepper pins, as it would in
|
||||
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
|
||||
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
|
||||
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
|
||||
// This is an experimental feature that should only be used if your setup requires a longer
|
||||
// delay between direction and step pin settings (some opto coupler based drivers), as it may
|
||||
// adversely effect Grbl's high-end performance (>10kHz). Please notify Grbl administrators
|
||||
// of your successes or difficulties, as we will monitor this and possibly integrate this as a
|
||||
// standard feature for future releases. However, we suggest to first try our direction delay
|
||||
// hack/solution posted in the Wiki involving inverting the stepper pin mask.
|
||||
// NOTE: Uncomment to enable. The recommended delay must be > 3us and the total step pulse
|
||||
// time, which includes the Grbl settings pulse microseconds, must not exceed 127us. Reported
|
||||
// successful values for certain setups have ranged from 10 to 20us.
|
||||
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
// TODO: Install compile-time option to send numeric status codes rather than strings.
|
||||
|
||||
#endif
|
||||
|
||||
// Pin-assignments from Grbl 0.5
|
||||
|
||||
// #define STEPPERS_DISABLE_DDR DDRD
|
||||
// #define STEPPERS_DISABLE_PORT PORTD
|
||||
// #define STEPPERS_DISABLE_BIT 2
|
||||
//
|
||||
// #define STEPPING_DDR DDRC
|
||||
// #define STEPPING_PORT PORTC
|
||||
// #define X_STEP_BIT 0
|
||||
// #define Y_STEP_BIT 1
|
||||
// #define Z_STEP_BIT 2
|
||||
// #define X_DIRECTION_BIT 3
|
||||
// #define Y_DIRECTION_BIT 4
|
||||
// #define Z_DIRECTION_BIT 5
|
||||
//
|
||||
// #define LIMIT_DDR DDRD
|
||||
// #define LIMIT_PORT PORTD
|
||||
// #define X_LIMIT_BIT 3
|
||||
// #define Y_LIMIT_BIT 4
|
||||
// #define Z_LIMIT_BIT 5
|
||||
//
|
||||
// #define SPINDLE_ENABLE_DDR DDRD
|
||||
// #define SPINDLE_ENABLE_PORT PORTD
|
||||
// #define SPINDLE_ENABLE_BIT 6
|
||||
//
|
||||
// #define SPINDLE_DIRECTION_DDR DDRD
|
||||
// #define SPINDLE_DIRECTION_PORT PORTD
|
||||
// #define SPINDLE_DIRECTION_BIT 7
|
||||
|
65
coolant_control.c
Normal file
65
coolant_control.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
coolant_control.c - coolant control methods
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "coolant_control.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include "planner.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
static uint8_t current_coolant_mode;
|
||||
|
||||
void coolant_init()
|
||||
{
|
||||
current_coolant_mode = COOLANT_DISABLE;
|
||||
#if ENABLE_M7
|
||||
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
|
||||
#endif
|
||||
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
|
||||
coolant_stop();
|
||||
}
|
||||
|
||||
void coolant_stop()
|
||||
{
|
||||
#ifdef ENABLE_M7
|
||||
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
|
||||
#endif
|
||||
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
|
||||
}
|
||||
|
||||
|
||||
void coolant_run(uint8_t mode)
|
||||
{
|
||||
if (mode != current_coolant_mode)
|
||||
{
|
||||
plan_synchronize(); // Ensure coolant turns on when specified in program.
|
||||
if (mode == COOLANT_FLOOD_ENABLE) {
|
||||
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
|
||||
#ifdef ENABLE_M7
|
||||
} else if (mode == COOLANT_MIST_ENABLE) {
|
||||
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
|
||||
#endif
|
||||
} else {
|
||||
coolant_stop();
|
||||
}
|
||||
current_coolant_mode = mode;
|
||||
}
|
||||
}
|
34
coolant_control.h
Normal file
34
coolant_control.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
coolant_control.h - spindle control methods
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef coolant_control_h
|
||||
#define coolant_control_h
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#define COOLANT_MIST_ENABLE 2
|
||||
#define COOLANT_FLOOD_ENABLE 1
|
||||
#define COOLANT_DISABLE 0 // Must be zero.
|
||||
|
||||
void coolant_init();
|
||||
void coolant_stop();
|
||||
void coolant_run(uint8_t mode);
|
||||
|
||||
#endif
|
122
defaults.h
Normal file
122
defaults.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
defaults.h - defaults settings configuration file
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The defaults.h file serves as a central default settings file for different machine
|
||||
types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
|
||||
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
|
||||
our nefarious needs. */
|
||||
|
||||
#ifndef defaults_h
|
||||
#define defaults_h
|
||||
|
||||
#ifdef DEFAULTS_GENERIC
|
||||
// Grbl generic default settings. Should work across different machines.
|
||||
#define DEFAULT_X_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_Y_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_Z_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_MM_PER_ARC_SEGMENT 0.1
|
||||
#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min
|
||||
#define DEFAULT_FEEDRATE 250.0
|
||||
#define DEFAULT_ACCELERATION (10.0*60*60) // 10 mm/min^2
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
|
||||
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
|
||||
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)
|
||||
#define DEFAULT_DECIMAL_PLACES 3
|
||||
#define DEFAULT_N_ARC_CORRECTION 25
|
||||
#endif
|
||||
|
||||
#ifdef DEFAULTS_SHERLINE_5400
|
||||
// Description: Sherline 5400 mill with three NEMA 23 185 oz-in stepper motors, driven by
|
||||
// three Pololu A4988 stepper drivers with a 30V, 6A power supply at 1.5A per winding.
|
||||
#define MICROSTEPS 4
|
||||
#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_MM (STEP_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Y_STEPS_PER_MM (STEP_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Z_STEPS_PER_MM (STEP_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_MM_PER_ARC_SEGMENT 0.1
|
||||
#define DEFAULT_RAPID_FEEDRATE 635.0 // mm/min (25ipm)
|
||||
#define DEFAULT_FEEDRATE 254.0 // mm/min (10ipm)
|
||||
#define DEFAULT_ACCELERATION 50.0*60*60 // 50 mm/min^2
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_REPORT_INCHES 1 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
|
||||
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
|
||||
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)
|
||||
#define DEFAULT_DECIMAL_PLACES 3
|
||||
#define DEFAULT_N_ARC_CORRECTION 25
|
||||
#endif
|
||||
|
||||
#ifdef DEFAULTS_SHAPEOKO
|
||||
// Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos
|
||||
// grblShield with a 24V, 4.2A power supply.
|
||||
#define MICROSTEPS_XY 8
|
||||
#define STEP_REVS_XY 400
|
||||
#define MM_PER_REV_XY (0.08*18*MM_PER_INCH) // 0.08 in belt pitch, 18 pulley teeth
|
||||
#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_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
|
||||
#define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
|
||||
#define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z)
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_MM_PER_ARC_SEGMENT 0.1
|
||||
#define DEFAULT_RAPID_FEEDRATE 1000.0 // mm/min
|
||||
#define DEFAULT_FEEDRATE 250.0
|
||||
#define DEFAULT_ACCELERATION (15.0*60*60) // 15 mm/min^2
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
|
||||
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
|
||||
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-255)
|
||||
#define DEFAULT_DECIMAL_PLACES 3
|
||||
#define DEFAULT_N_ARC_CORRECTION 25
|
||||
#endif
|
||||
|
||||
#endif
|
21
doc/commands.txt
Executable file
21
doc/commands.txt
Executable file
@ -0,0 +1,21 @@
|
||||
Runtime commands for Grbl
|
||||
=========================
|
||||
|
||||
In normal operation, grbl accepts g-code blocks followed by a carriage return. Each block is then parsed, processed, and placed into a ring buffer with computed acceleration profiles. Grbl will respond with an 'ok' or 'error:XXX' for each block received.
|
||||
|
||||
As of v0.8, grbl features multi-tasking events, which allow for immediate execution of run-time commands regardless of what grbl is doing. With this functionality, direct control of grbl may be possible, such as a controlled decelerating feed hold, resume, and system abort/reset. In addition, this provides the ability to report the real-time status of the CNC machine's current location and feed rates.
|
||||
|
||||
How it works: The run-time commands are defined as special characters, which are picked off the serial read buffer at an interrupt level. The serial interrupt then sets a run-time command system flag for the main program to execute when ready. The main program consists of run-time command check points placed strategically in various points in the program, where grbl maybe idle waiting for room in a buffer or the execution time from the last check point may exceed a fraction of a second.
|
||||
|
||||
How to interface: From a terminal connection, run-time commands may be sent at anytime via keystrokes. When streaming g-code, the user interface should be able to send these special characters independently of the stream. Grbl will not write these run-time command special characters to the buffer, so they may be placed anywhere in the stream at anytime, as long as there is room in the buffer. Also, ensure that the g-code program being streamed to grbl does not contain any of these special characters in the program. These characters may be defined per user requirements in the 'config.h' file.
|
||||
|
||||
Run-time commands:
|
||||
|
||||
- Feed Hold: This initiates an immediate controlled deceleration of the streaming g-code program to a stop. The deceleration, limited by the machine acceleration settings, ensures no steps are lost and positioning is maintained. Grbl may still receive and buffer g-code blocks as the feed hold is being executed. Once the feed hold completes, grbl will replan the buffer and resume upon a 'cycle start' command.
|
||||
|
||||
- Cycle Start: (a.k.a. Resume) For now, cycle start only resumes the g-code program after a feed hold. In later releases, this may also function as a way to initiate the steppers manually when a user would like to fill the planner buffer completely before starting the cycle.
|
||||
|
||||
- Reset: This issues an immediate shutdown of the stepper motors and a system abort. The main program will exit back to the main loop and re-initialize grbl.
|
||||
|
||||
- Status Report: (TODO) In future releases, this will provide real-time positioning, feed rate, and block processed data, as well as other important data to the user. This also may be considered a 'poor-man's' DRO (digital read-out), where grbl thinks it is, rather than a direct and absolute measurement.
|
||||
|
@ -1,30 +0,0 @@
|
||||
Reasoning behind the mathematics in 'planner' module (in the key of 'Mathematica')
|
||||
==================================================================================
|
||||
|
||||
|
||||
s == speed, a == acceleration, t == time, d == distance
|
||||
|
||||
Basic definitions:
|
||||
|
||||
Speed[s_, a_, t_] := s + (a*t)
|
||||
Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t]
|
||||
|
||||
Distance to reach a specific speed with a constant acceleration:
|
||||
|
||||
Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t]
|
||||
d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance()
|
||||
|
||||
Speed after a given distance of travel with constant acceleration:
|
||||
|
||||
Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t]
|
||||
m -> Sqrt[2 a d + s^2]
|
||||
|
||||
DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2]
|
||||
|
||||
When to start braking (di) to reach a specified destionation speed (s2) after accelerating
|
||||
from initial speed s1 without ever stopping at a plateau:
|
||||
|
||||
Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di]
|
||||
di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance()
|
||||
|
||||
IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a)
|
0
doc/resources.txt
Normal file → Executable file
0
doc/resources.txt
Normal file → Executable file
14
doc/structure.txt
Normal file → Executable file
14
doc/structure.txt
Normal file → Executable file
@ -4,7 +4,8 @@ The general structure of Grbl
|
||||
The main processing stack:
|
||||
|
||||
'protocol' : Accepts command lines from the serial port and passes them to 'gcode' for execution.
|
||||
Provides status responses for each command.
|
||||
Provides status responses for each command. Also manages run-time commands set by
|
||||
the serial interrupt.
|
||||
|
||||
'gcode' : Recieves gcode from 'protocol', parses it according to the current state
|
||||
of the parser and issues commands via '..._control' modules
|
||||
@ -14,8 +15,8 @@ The main processing stack:
|
||||
'motion_control' : Accepts motion commands from 'gcode' and passes them to the 'planner'. This module
|
||||
represents the public interface of the planner/stepper duo.
|
||||
|
||||
'planner' : Recieves linear motion commands from 'motion_control' and adds them to the plan of
|
||||
prepared motions. It takes care of continously optimizing the acceleration profile
|
||||
'planner' : Receives linear motion commands from 'motion_control' and adds them to the plan of
|
||||
prepared motions. It takes care of continuously optimizing the acceleration profile
|
||||
as motions are added.
|
||||
|
||||
'stepper' : Executes the motions by stepping the steppers according to the plan.
|
||||
@ -27,15 +28,16 @@ Supporting files:
|
||||
|
||||
'config.h' : Compile time user settings
|
||||
|
||||
'settings' : Maintains the run time settings record in eeprom and makes it availible
|
||||
'settings' : Maintains the run time settings record in eeprom and makes it available
|
||||
to all modules.
|
||||
|
||||
'eeprom' : A library from Atmel that provides methods for reading and writing the eeprom with
|
||||
a small addition from us that read and write binary streams with check sums used
|
||||
to verify validity of the settings record.
|
||||
|
||||
'nuts_bolts.h' : A tiny collection of useful constants and macros used everywhere
|
||||
'nuts_bolts.h' : A collection of global variable definitions, useful constants, and macros used everywhere
|
||||
|
||||
'serial' : Low level serial communications
|
||||
'serial' : Low level serial communications and picks off run-time commands real-time for asynchronous
|
||||
control.
|
||||
|
||||
'print' : Functions to print strings of different formats (using serial)
|
738
gcode.c
Normal file → Executable file
738
gcode.c
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -29,55 +29,17 @@
|
||||
#include "settings.h"
|
||||
#include "motion_control.h"
|
||||
#include "spindle_control.h"
|
||||
#include "coolant_control.h"
|
||||
#include "errno.h"
|
||||
#include "protocol.h"
|
||||
#include "report.h"
|
||||
|
||||
#define MM_PER_INCH (25.4)
|
||||
|
||||
#define NEXT_ACTION_DEFAULT 0
|
||||
#define NEXT_ACTION_DWELL 1
|
||||
#define NEXT_ACTION_GO_HOME 2
|
||||
#define NEXT_ACTION_SET_COORDINATE_OFFSET 3
|
||||
|
||||
#define MOTION_MODE_SEEK 0 // G0
|
||||
#define MOTION_MODE_LINEAR 1 // G1
|
||||
#define MOTION_MODE_CW_ARC 2 // G2
|
||||
#define MOTION_MODE_CCW_ARC 3 // G3
|
||||
#define MOTION_MODE_CANCEL 4 // G80
|
||||
|
||||
#define PATH_CONTROL_MODE_EXACT_PATH 0
|
||||
#define PATH_CONTROL_MODE_EXACT_STOP 1
|
||||
#define PATH_CONTROL_MODE_CONTINOUS 2
|
||||
|
||||
#define PROGRAM_FLOW_RUNNING 0
|
||||
#define PROGRAM_FLOW_PAUSED 1
|
||||
#define PROGRAM_FLOW_COMPLETED 2
|
||||
|
||||
#define SPINDLE_DIRECTION_CW 0
|
||||
#define SPINDLE_DIRECTION_CCW 1
|
||||
|
||||
typedef struct {
|
||||
uint8_t status_code;
|
||||
|
||||
uint8_t motion_mode; /* {G0, G1, G2, G3, G80} */
|
||||
uint8_t inverse_feed_rate_mode; /* G93, G94 */
|
||||
uint8_t inches_mode; /* 0 = millimeter mode, 1 = inches mode {G20, G21} */
|
||||
uint8_t absolute_mode; /* 0 = relative motion, 1 = absolute motion {G90, G91} */
|
||||
uint8_t program_flow;
|
||||
int8_t spindle_direction;
|
||||
double feed_rate, seek_rate; /* Millimeters/second */
|
||||
double position[3]; /* Where the interpreter considers the tool to be at this point in the code */
|
||||
uint8_t tool;
|
||||
int16_t spindle_speed; /* RPM/100 */
|
||||
uint8_t plane_axis_0,
|
||||
plane_axis_1,
|
||||
plane_axis_2; // The axes of the selected plane
|
||||
} parser_state_t;
|
||||
static parser_state_t gc;
|
||||
// Declare gc extern struct
|
||||
parser_state_t gc;
|
||||
|
||||
#define FAIL(status) gc.status_code = status;
|
||||
|
||||
static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter);
|
||||
static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter);
|
||||
|
||||
static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
|
||||
{
|
||||
@ -86,271 +48,516 @@ static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
|
||||
gc.plane_axis_2 = axis_2;
|
||||
}
|
||||
|
||||
void gc_init() {
|
||||
void gc_init()
|
||||
{
|
||||
memset(&gc, 0, sizeof(gc));
|
||||
gc.feed_rate = settings.default_feed_rate;
|
||||
gc.seek_rate = settings.default_seek_rate;
|
||||
gc.feed_rate = settings.default_feed_rate; // Should be zero at initialization.
|
||||
// gc.seek_rate = settings.default_seek_rate;
|
||||
select_plane(X_AXIS, Y_AXIS, Z_AXIS);
|
||||
gc.absolute_mode = true;
|
||||
|
||||
// Load default G54 coordinate system.
|
||||
if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
static float to_millimeters(double value) {
|
||||
// Sets g-code parser position in mm. Input in steps. Called by the system abort and hard
|
||||
// limit pull-off routines.
|
||||
void gc_set_current_position(int32_t x, int32_t y, int32_t z)
|
||||
{
|
||||
gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS];
|
||||
gc.position[Y_AXIS] = y/settings.steps_per_mm[Y_AXIS];
|
||||
gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS];
|
||||
}
|
||||
|
||||
static float to_millimeters(float value)
|
||||
{
|
||||
return(gc.inches_mode ? (value * MM_PER_INCH) : value);
|
||||
}
|
||||
|
||||
// Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase
|
||||
// characters and signed floating point values (no whitespace). Comments and block delete
|
||||
// characters have been removed.
|
||||
uint8_t gc_execute_line(char *line) {
|
||||
// characters have been removed. All units and positions are converted and exported to grbl's
|
||||
// internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively.
|
||||
uint8_t gc_execute_line(char *line)
|
||||
{
|
||||
|
||||
// If in alarm state, don't process. Immediately return with error.
|
||||
// NOTE: Might not be right place for this, but also prevents $N storing during alarm.
|
||||
if (sys.state == STATE_ALARM) { return(STATUS_ALARM_LOCK); }
|
||||
|
||||
uint8_t char_counter = 0;
|
||||
char letter;
|
||||
double value;
|
||||
double unit_converted_value;
|
||||
double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified
|
||||
uint8_t radius_mode = false;
|
||||
|
||||
uint8_t absolute_override = false; /* 1 = absolute motion for this block only {G53} */
|
||||
uint8_t next_action = NEXT_ACTION_DEFAULT; /* The action that will be taken by the parsed line */
|
||||
|
||||
double target[3], offset[3];
|
||||
|
||||
double p = 0, r = 0;
|
||||
float value;
|
||||
int int_value;
|
||||
|
||||
uint16_t modal_group_words = 0; // Bitflag variable to track and check modal group words in block
|
||||
uint8_t axis_words = 0; // Bitflag to track which XYZ(ABC) parameters exist in block
|
||||
|
||||
float inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified
|
||||
uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53}
|
||||
uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal)
|
||||
|
||||
float target[3], offset[3];
|
||||
clear_vector(target); // XYZ(ABC) axes parameters.
|
||||
clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change.
|
||||
|
||||
gc.status_code = STATUS_OK;
|
||||
|
||||
// Pass 1: Commands
|
||||
/* Pass 1: Commands and set all modes. Check for modal group violations.
|
||||
NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */
|
||||
uint8_t group_number = MODAL_GROUP_NONE;
|
||||
while(next_statement(&letter, &value, line, &char_counter)) {
|
||||
int_value = trunc(value);
|
||||
switch(letter) {
|
||||
case 'G':
|
||||
switch(int_value) {
|
||||
case 0: gc.motion_mode = MOTION_MODE_SEEK; break;
|
||||
case 1: gc.motion_mode = MOTION_MODE_LINEAR; break;
|
||||
#ifdef __AVR_ATmega328P__
|
||||
case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break;
|
||||
case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break;
|
||||
#endif
|
||||
case 4: next_action = NEXT_ACTION_DWELL; break;
|
||||
case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break;
|
||||
case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break;
|
||||
case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break;
|
||||
case 20: gc.inches_mode = true; break;
|
||||
case 21: gc.inches_mode = false; break;
|
||||
case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break;
|
||||
case 53: absolute_override = true; break;
|
||||
case 80: gc.motion_mode = MOTION_MODE_CANCEL; break;
|
||||
case 90: gc.absolute_mode = true; break;
|
||||
case 91: gc.absolute_mode = false; break;
|
||||
case 92: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break;
|
||||
case 93: gc.inverse_feed_rate_mode = true; break;
|
||||
case 94: gc.inverse_feed_rate_mode = false; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
|
||||
// Set modal group values
|
||||
switch(int_value) {
|
||||
case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break;
|
||||
case 0: case 1: case 2: case 3: case 80: group_number = MODAL_GROUP_1; break;
|
||||
case 17: case 18: case 19: group_number = MODAL_GROUP_2; break;
|
||||
case 90: case 91: group_number = MODAL_GROUP_3; break;
|
||||
case 93: case 94: group_number = MODAL_GROUP_5; break;
|
||||
case 20: case 21: group_number = MODAL_GROUP_6; break;
|
||||
case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break;
|
||||
}
|
||||
// Set 'G' commands
|
||||
switch(int_value) {
|
||||
case 0: gc.motion_mode = MOTION_MODE_SEEK; break;
|
||||
case 1: gc.motion_mode = MOTION_MODE_LINEAR; break;
|
||||
case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break;
|
||||
case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break;
|
||||
case 4: non_modal_action = NON_MODAL_DWELL; break;
|
||||
case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break;
|
||||
case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break;
|
||||
case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break;
|
||||
case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break;
|
||||
case 20: gc.inches_mode = true; break;
|
||||
case 21: gc.inches_mode = false; break;
|
||||
case 28: case 30:
|
||||
int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1
|
||||
switch(int_value) {
|
||||
case 280: non_modal_action = NON_MODAL_GO_HOME_0; break;
|
||||
case 281: non_modal_action = NON_MODAL_SET_HOME_0; break;
|
||||
case 300: non_modal_action = NON_MODAL_GO_HOME_1; break;
|
||||
case 301: non_modal_action = NON_MODAL_SET_HOME_1; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
case 53: absolute_override = true; break;
|
||||
case 54: case 55: case 56: case 57: case 58: case 59:
|
||||
gc.coord_select = int_value-54;
|
||||
break;
|
||||
case 80: gc.motion_mode = MOTION_MODE_CANCEL; break;
|
||||
case 90: gc.absolute_mode = true; break;
|
||||
case 91: gc.absolute_mode = false; break;
|
||||
case 92:
|
||||
int_value = trunc(10*value); // Multiply by 10 to pick up G92.1
|
||||
switch(int_value) {
|
||||
case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break;
|
||||
case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
case 93: gc.inverse_feed_rate_mode = true; break;
|
||||
case 94: gc.inverse_feed_rate_mode = false; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
switch(int_value) {
|
||||
case 0: case 1: gc.program_flow = PROGRAM_FLOW_PAUSED; break;
|
||||
case 2: case 30: case 60: gc.program_flow = PROGRAM_FLOW_COMPLETED; break;
|
||||
case 3: gc.spindle_direction = 1; break;
|
||||
case 4: gc.spindle_direction = -1; break;
|
||||
case 5: gc.spindle_direction = 0; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
case 'T': gc.tool = trunc(value); break;
|
||||
// Set modal group values
|
||||
switch(int_value) {
|
||||
case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break;
|
||||
case 3: case 4: case 5: group_number = MODAL_GROUP_7; break;
|
||||
}
|
||||
// Set 'M' commands
|
||||
switch(int_value) {
|
||||
case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
|
||||
case 1: break; // Optional stop not supported. Ignore.
|
||||
case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
|
||||
case 3: gc.spindle_direction = 1; break;
|
||||
case 4: gc.spindle_direction = -1; break;
|
||||
case 5: gc.spindle_direction = 0; break;
|
||||
#ifdef ENABLE_M7
|
||||
case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break;
|
||||
#endif
|
||||
case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break;
|
||||
case 9: gc.coolant_mode = COOLANT_DISABLE; break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Check for modal group multiple command violations in the current block
|
||||
if (group_number) {
|
||||
if ( bit_istrue(modal_group_words,bit(group_number)) ) {
|
||||
FAIL(STATUS_MODAL_GROUP_VIOLATION);
|
||||
} else {
|
||||
bit_true(modal_group_words,bit(group_number));
|
||||
}
|
||||
group_number = MODAL_GROUP_NONE; // Reset for next command.
|
||||
}
|
||||
if(gc.status_code) { break; }
|
||||
}
|
||||
|
||||
// If there were any errors parsing this line, we will return right away with the bad news
|
||||
if (gc.status_code) { return(gc.status_code); }
|
||||
|
||||
/* Pass 2: Parameters. All units converted according to current block commands. Position
|
||||
parameters are converted and flagged to indicate a change. These can have multiple connotations
|
||||
for different commands. Each will be converted to their proper value upon execution. */
|
||||
float p = 0, r = 0;
|
||||
uint8_t l = 0;
|
||||
char_counter = 0;
|
||||
clear_vector(target);
|
||||
clear_vector(offset);
|
||||
memcpy(target, gc.position, sizeof(target)); // i.e. target = gc.position
|
||||
|
||||
// Pass 2: Parameters
|
||||
while(next_statement(&letter, &value, line, &char_counter)) {
|
||||
int_value = trunc(value);
|
||||
unit_converted_value = to_millimeters(value);
|
||||
switch(letter) {
|
||||
case 'G': case 'M': case 'N': break; // Ignore command statements and line numbers
|
||||
case 'F':
|
||||
if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero
|
||||
if (gc.inverse_feed_rate_mode) {
|
||||
inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only
|
||||
} else {
|
||||
if (gc.motion_mode == MOTION_MODE_SEEK) {
|
||||
gc.seek_rate = unit_converted_value;
|
||||
if (value <= 0) { FAIL(STATUS_INVALID_STATEMENT); } // Must be greater than zero
|
||||
if (gc.inverse_feed_rate_mode) {
|
||||
inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only
|
||||
} else {
|
||||
gc.feed_rate = unit_converted_value; // millimeters per minute
|
||||
gc.feed_rate = to_millimeters(value); // millimeters per minute
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'I': case 'J': case 'K': offset[letter-'I'] = unit_converted_value; break;
|
||||
break;
|
||||
case 'I': case 'J': case 'K': offset[letter-'I'] = to_millimeters(value); break;
|
||||
case 'L': l = trunc(value); break;
|
||||
case 'P': p = value; break;
|
||||
case 'R': r = unit_converted_value; radius_mode = true; break;
|
||||
case 'S': gc.spindle_speed = value; break;
|
||||
case 'X': case 'Y': case 'Z':
|
||||
if (gc.absolute_mode || absolute_override) {
|
||||
target[letter - 'X'] = unit_converted_value;
|
||||
} else {
|
||||
target[letter - 'X'] += unit_converted_value;
|
||||
}
|
||||
break;
|
||||
case 'R': r = to_millimeters(value); break;
|
||||
case 'S':
|
||||
if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
|
||||
// TBD: Spindle speed not supported due to PWM issues, but may come back once resolved.
|
||||
// gc.spindle_speed = value;
|
||||
break;
|
||||
case 'T':
|
||||
if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
|
||||
gc.tool = trunc(value);
|
||||
break;
|
||||
case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break;
|
||||
case 'Y': target[Y_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Y_AXIS)); break;
|
||||
case 'Z': target[Z_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Z_AXIS)); break;
|
||||
default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any errors parsing this line, we will return right away with the bad news
|
||||
if (gc.status_code) { return(gc.status_code); }
|
||||
|
||||
// Update spindle state
|
||||
spindle_run(gc.spindle_direction, gc.spindle_speed);
|
||||
|
||||
// Perform any physical actions
|
||||
switch (next_action) {
|
||||
case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector(target); break;
|
||||
case NEXT_ACTION_DWELL: mc_dwell(p); break;
|
||||
case NEXT_ACTION_SET_COORDINATE_OFFSET:
|
||||
mc_set_current_position(target[X_AXIS], target[Y_AXIS], target[Z_AXIS]);
|
||||
break;
|
||||
case NEXT_ACTION_DEFAULT:
|
||||
switch (gc.motion_mode) {
|
||||
case MOTION_MODE_CANCEL: break;
|
||||
case MOTION_MODE_SEEK:
|
||||
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false);
|
||||
break;
|
||||
case MOTION_MODE_LINEAR:
|
||||
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS],
|
||||
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode);
|
||||
break;
|
||||
#ifdef __AVR_ATmega328P__
|
||||
case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
|
||||
if (radius_mode) {
|
||||
/*
|
||||
We need to calculate the center of the circle that has the designated radius and passes
|
||||
through both the current position and the target position. This method calculates the following
|
||||
set of equations where [x,y] is the vector from current to target position, d == magnitude of
|
||||
that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to
|
||||
the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the
|
||||
length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point
|
||||
[i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
|
||||
/* Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41.
|
||||
NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency
|
||||
and simplicity purposes, but this should not affect proper g-code execution. */
|
||||
|
||||
d^2 == x^2 + y^2
|
||||
h^2 == r^2 - (d/2)^2
|
||||
i == x/2 - y/d*h
|
||||
j == y/2 + x/d*h
|
||||
// ([F]: Set feed and seek rates.)
|
||||
// TODO: Seek rates can change depending on the direction and maximum speeds of each axes. When
|
||||
// max axis speed is installed, the calculation can be performed here, or maybe in the planner.
|
||||
|
||||
O <- [i,j]
|
||||
- |
|
||||
r - |
|
||||
- |
|
||||
- | h
|
||||
- |
|
||||
[0,0] -> C -----------------+--------------- T <- [x,y]
|
||||
| <------ d/2 ---->|
|
||||
if (sys.state != STATE_CHECK_MODE) {
|
||||
// ([M6]: Tool change should be executed here.)
|
||||
|
||||
C - Current position
|
||||
T - Target position
|
||||
O - center of circle that pass through both C and T
|
||||
d - distance from C to T
|
||||
r - designated radius
|
||||
h - distance from center of CT to O
|
||||
// [M3,M4,M5]: Update spindle state
|
||||
spindle_run(gc.spindle_direction);
|
||||
|
||||
Expanding the equations:
|
||||
|
||||
d -> sqrt(x^2 + y^2)
|
||||
h -> sqrt(4 * r^2 - x^2 - y^2)/2
|
||||
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
|
||||
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
|
||||
|
||||
Which can be written:
|
||||
|
||||
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
|
||||
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
|
||||
|
||||
Which we for size and speed reasons optimize to:
|
||||
|
||||
h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
|
||||
i = (x - (y * h_x2_div_d))/2
|
||||
j = (y + (x * h_x2_div_d))/2
|
||||
|
||||
*/
|
||||
|
||||
// Calculate the change in position along each selected axis
|
||||
double x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0];
|
||||
double y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1];
|
||||
|
||||
clear_vector(offset);
|
||||
double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d)
|
||||
// If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any
|
||||
// real CNC, and thus - for practical reasons - we will terminate promptly:
|
||||
if(isnan(h_x2_div_d)) { FAIL(STATUS_FLOATING_POINT_ERROR); return(gc.status_code); }
|
||||
// Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
|
||||
if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
|
||||
|
||||
/* The counter clockwise circle lies to the left of the target direction. When offset is positive,
|
||||
the left hand circle will be generated - when it is negative the right hand circle is generated.
|
||||
|
||||
|
||||
T <-- Target position
|
||||
|
||||
^
|
||||
Clockwise circles with this center | Clockwise circles with this center will have
|
||||
will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
|
||||
\ | /
|
||||
center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
|
||||
|
|
||||
|
|
||||
|
||||
C <-- Current position */
|
||||
|
||||
|
||||
// Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
|
||||
// even though it is advised against ever generating such circles in a single line of g-code. By
|
||||
// inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
|
||||
// travel and thus we get the unadvisably long arcs as prescribed.
|
||||
if (r < 0) {
|
||||
h_x2_div_d = -h_x2_div_d;
|
||||
r = -r; // Finished with r. Set to positive for mc_arc
|
||||
}
|
||||
// Complete the operation by calculating the actual center of the arc
|
||||
offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d));
|
||||
offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d));
|
||||
|
||||
} else { // Offset mode specific computations
|
||||
|
||||
r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc
|
||||
|
||||
}
|
||||
|
||||
// Set clockwise/counter-clockwise sign for mc_arc computations
|
||||
uint8_t isclockwise = false;
|
||||
if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; }
|
||||
|
||||
// Trace the arc
|
||||
mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2,
|
||||
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode,
|
||||
r, isclockwise);
|
||||
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
// [*M7,M8,M9]: Update coolant state
|
||||
coolant_run(gc.coolant_mode);
|
||||
}
|
||||
|
||||
// [G54,G55,...,G59]: Coordinate system selection
|
||||
if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block
|
||||
float coord_data[N_AXIS];
|
||||
if (!(settings_read_coord_data(gc.coord_select,coord_data))) { return(STATUS_SETTING_READ_FAIL); }
|
||||
memcpy(gc.coord_system,coord_data,sizeof(coord_data));
|
||||
}
|
||||
|
||||
// [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets.
|
||||
// NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this
|
||||
// modal group and do not effect these actions.
|
||||
switch (non_modal_action) {
|
||||
case NON_MODAL_DWELL:
|
||||
if (p < 0) { // Time cannot be negative.
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
} else {
|
||||
// Ignore dwell in check gcode modes
|
||||
if (sys.state != STATE_CHECK_MODE) { mc_dwell(p); }
|
||||
}
|
||||
break;
|
||||
case NON_MODAL_SET_COORDINATE_DATA:
|
||||
int_value = trunc(p); // Convert p value to int.
|
||||
if ((l != 2 && l != 20) || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 and L20. P1=G54, P2=G55, ...
|
||||
FAIL(STATUS_UNSUPPORTED_STATEMENT);
|
||||
} else if (!axis_words && l==2) { // No axis words.
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
} else {
|
||||
int_value--; // Adjust P index to EEPROM coordinate data indexing.
|
||||
if (l == 20) {
|
||||
settings_write_coord_data(int_value,gc.position);
|
||||
// Update system coordinate system if currently active.
|
||||
if (gc.coord_select == int_value) { memcpy(gc.coord_system,gc.position,sizeof(gc.position)); }
|
||||
} else {
|
||||
float coord_data[N_AXIS];
|
||||
if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); }
|
||||
// Update axes defined only in block. Always in machine coordinates. Can change non-active system.
|
||||
uint8_t i;
|
||||
for (i=0; i<N_AXIS; i++) { // Axes indices are consistent, so loop may be used.
|
||||
if ( bit_istrue(axis_words,bit(i)) ) { coord_data[i] = target[i]; }
|
||||
}
|
||||
settings_write_coord_data(int_value,coord_data);
|
||||
// Update system coordinate system if currently active.
|
||||
if (gc.coord_select == int_value) { memcpy(gc.coord_system,coord_data,sizeof(coord_data)); }
|
||||
}
|
||||
}
|
||||
axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.
|
||||
break;
|
||||
case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
|
||||
// Move to intermediate position before going home. Obeys current coordinate system and offsets
|
||||
// and absolute and incremental modes.
|
||||
if (axis_words) {
|
||||
// Apply absolute mode coordinate offsets or incremental mode offsets.
|
||||
uint8_t i;
|
||||
for (i=0; i<N_AXIS; i++) { // Axes indices are consistent, so loop may be used.
|
||||
if ( bit_istrue(axis_words,bit(i)) ) {
|
||||
if (gc.absolute_mode) {
|
||||
target[i] += gc.coord_system[i] + gc.coord_offset[i];
|
||||
} else {
|
||||
target[i] += gc.position[i];
|
||||
}
|
||||
} else {
|
||||
target[i] = gc.position[i];
|
||||
}
|
||||
}
|
||||
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false);
|
||||
}
|
||||
// Retreive G28/30 go-home position data (in machine coordinates) from EEPROM
|
||||
float coord_data[N_AXIS];
|
||||
uint8_t home_select = SETTING_INDEX_G28;
|
||||
if (non_modal_action == NON_MODAL_GO_HOME_1) { home_select = SETTING_INDEX_G30; }
|
||||
if (!settings_read_coord_data(home_select,coord_data)) { return(STATUS_SETTING_READ_FAIL); }
|
||||
mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], settings.default_seek_rate, false);
|
||||
axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.
|
||||
break;
|
||||
case NON_MODAL_SET_HOME_0: case NON_MODAL_SET_HOME_1:
|
||||
home_select = SETTING_INDEX_G28;
|
||||
if (non_modal_action == NON_MODAL_SET_HOME_1) { home_select = SETTING_INDEX_G30; }
|
||||
settings_write_coord_data(home_select,gc.position);
|
||||
break;
|
||||
case NON_MODAL_SET_COORDINATE_OFFSET:
|
||||
if (!axis_words) { // No axis words
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
} else {
|
||||
// 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.
|
||||
uint8_t i;
|
||||
for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used.
|
||||
if (bit_istrue(axis_words,bit(i)) ) {
|
||||
gc.coord_offset[i] = gc.position[i]-gc.coord_system[i]-target[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.
|
||||
break;
|
||||
case NON_MODAL_RESET_COORDINATE_OFFSET:
|
||||
clear_vector(gc.coord_offset); // Disable G92 offsets by zeroing offset vector.
|
||||
break;
|
||||
}
|
||||
|
||||
// [G0,G1,G2,G3,G80]: Perform motion modes.
|
||||
// NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes.
|
||||
// Enter motion modes only if there are axis words or a motion mode command word in the block.
|
||||
if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_1)) || axis_words ) {
|
||||
|
||||
// G1,G2,G3 require F word in inverse time mode.
|
||||
if ( gc.inverse_feed_rate_mode ) {
|
||||
if (inverse_feed_rate < 0 && gc.motion_mode != MOTION_MODE_CANCEL) {
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
}
|
||||
}
|
||||
// Absolute override G53 only valid with G0 and G1 active.
|
||||
if ( absolute_override && !(gc.motion_mode == MOTION_MODE_SEEK || gc.motion_mode == MOTION_MODE_LINEAR)) {
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
}
|
||||
// Report any errors.
|
||||
if (gc.status_code) { return(gc.status_code); }
|
||||
|
||||
// Convert all target position data to machine coordinates for executing motion. Apply
|
||||
// absolute mode coordinate offsets or incremental mode offsets.
|
||||
// NOTE: Tool offsets may be appended to these conversions when/if this feature is added.
|
||||
uint8_t i;
|
||||
for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used to save flash space.
|
||||
if ( bit_istrue(axis_words,bit(i)) ) {
|
||||
if (!absolute_override) { // Do not update target in absolute override mode
|
||||
if (gc.absolute_mode) {
|
||||
target[i] += gc.coord_system[i] + gc.coord_offset[i]; // Absolute mode
|
||||
} else {
|
||||
target[i] += gc.position[i]; // Incremental mode
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target[i] = gc.position[i]; // No axis word in block. Keep same axis position.
|
||||
}
|
||||
}
|
||||
|
||||
switch (gc.motion_mode) {
|
||||
case MOTION_MODE_CANCEL:
|
||||
if (axis_words) { FAIL(STATUS_INVALID_STATEMENT); } // No axis words allowed while active.
|
||||
break;
|
||||
case MOTION_MODE_SEEK:
|
||||
if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);}
|
||||
else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); }
|
||||
break;
|
||||
case MOTION_MODE_LINEAR:
|
||||
// TODO: Inverse time requires F-word with each statement. Need to do a check. Also need
|
||||
// to check for initial F-word upon startup. Maybe just set to zero upon initialization
|
||||
// and after an inverse time move and then check for non-zero feed rate each time. This
|
||||
// should be efficient and effective.
|
||||
if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);}
|
||||
else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS],
|
||||
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); }
|
||||
break;
|
||||
case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
|
||||
// Check if at least one of the axes of the selected plane has been specified. If in center
|
||||
// format arc mode, also check for at least one of the IJK axes of the selected plane was sent.
|
||||
if ( !( bit_false(axis_words,bit(gc.plane_axis_2)) ) ||
|
||||
( !r && !offset[gc.plane_axis_0] && !offset[gc.plane_axis_1] ) ) {
|
||||
FAIL(STATUS_INVALID_STATEMENT);
|
||||
} else {
|
||||
if (r != 0) { // Arc Radius Mode
|
||||
/*
|
||||
We need to calculate the center of the circle that has the designated radius and passes
|
||||
through both the current position and the target position. This method calculates the following
|
||||
set of equations where [x,y] is the vector from current to target position, d == magnitude of
|
||||
that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to
|
||||
the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the
|
||||
length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point
|
||||
[i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
|
||||
|
||||
d^2 == x^2 + y^2
|
||||
h^2 == r^2 - (d/2)^2
|
||||
i == x/2 - y/d*h
|
||||
j == y/2 + x/d*h
|
||||
|
||||
O <- [i,j]
|
||||
- |
|
||||
r - |
|
||||
- |
|
||||
- | h
|
||||
- |
|
||||
[0,0] -> C -----------------+--------------- T <- [x,y]
|
||||
| <------ d/2 ---->|
|
||||
|
||||
C - Current position
|
||||
T - Target position
|
||||
O - center of circle that pass through both C and T
|
||||
d - distance from C to T
|
||||
r - designated radius
|
||||
h - distance from center of CT to O
|
||||
|
||||
Expanding the equations:
|
||||
|
||||
d -> sqrt(x^2 + y^2)
|
||||
h -> sqrt(4 * r^2 - x^2 - y^2)/2
|
||||
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
|
||||
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
|
||||
|
||||
Which can be written:
|
||||
|
||||
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
|
||||
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
|
||||
|
||||
Which we for size and speed reasons optimize to:
|
||||
|
||||
h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
|
||||
i = (x - (y * h_x2_div_d))/2
|
||||
j = (y + (x * h_x2_div_d))/2
|
||||
|
||||
*/
|
||||
|
||||
// Calculate the change in position along each selected axis
|
||||
float x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0];
|
||||
float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1];
|
||||
|
||||
clear_vector(offset);
|
||||
// First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller
|
||||
// than d. If so, the sqrt of a negative number is complex and error out.
|
||||
float h_x2_div_d = 4 * r*r - x*x - y*y;
|
||||
if (h_x2_div_d < 0) { FAIL(STATUS_ARC_RADIUS_ERROR); return(gc.status_code); }
|
||||
// Finish computing h_x2_div_d.
|
||||
h_x2_div_d = -sqrt(h_x2_div_d)/hypot(x,y); // == -(h * 2 / d)
|
||||
// Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
|
||||
if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
|
||||
|
||||
/* The counter clockwise circle lies to the left of the target direction. When offset is positive,
|
||||
the left hand circle will be generated - when it is negative the right hand circle is generated.
|
||||
|
||||
|
||||
T <-- Target position
|
||||
|
||||
^
|
||||
Clockwise circles with this center | Clockwise circles with this center will have
|
||||
will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
|
||||
\ | /
|
||||
center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
|
||||
|
|
||||
|
|
||||
|
||||
C <-- Current position */
|
||||
|
||||
|
||||
// Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
|
||||
// even though it is advised against ever generating such circles in a single line of g-code. By
|
||||
// inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
|
||||
// travel and thus we get the unadvisably long arcs as prescribed.
|
||||
if (r < 0) {
|
||||
h_x2_div_d = -h_x2_div_d;
|
||||
r = -r; // Finished with r. Set to positive for mc_arc
|
||||
}
|
||||
// Complete the operation by calculating the actual center of the arc
|
||||
offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d));
|
||||
offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d));
|
||||
|
||||
} else { // Arc Center Format Offset Mode
|
||||
r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc
|
||||
}
|
||||
|
||||
// Set clockwise/counter-clockwise sign for mc_arc computations
|
||||
uint8_t isclockwise = false;
|
||||
if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; }
|
||||
|
||||
// Trace the arc
|
||||
mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2,
|
||||
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode,
|
||||
r, isclockwise);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Report any errors.
|
||||
if (gc.status_code) { return(gc.status_code); }
|
||||
|
||||
// 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.
|
||||
memcpy(gc.position, target, sizeof(target)); // gc.position[] = target[];
|
||||
}
|
||||
|
||||
// M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may
|
||||
// refill and can only be resumed by the cycle start run-time command.
|
||||
if (gc.program_flow) {
|
||||
plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete.
|
||||
sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued.
|
||||
|
||||
// If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise,
|
||||
// re-enable program flow after pause complete, where cycle start will resume the program.
|
||||
if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); }
|
||||
else { gc.program_flow = PROGRAM_FLOW_RUNNING; }
|
||||
}
|
||||
|
||||
// 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.
|
||||
memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[];
|
||||
return(gc.status_code);
|
||||
}
|
||||
|
||||
// Parses the next statement and leaves the counter on the first character following
|
||||
// the statement. Returns 1 if there was a statements, 0 if end of string was reached
|
||||
// or there was an error (check state.status_code).
|
||||
static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter) {
|
||||
static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter)
|
||||
{
|
||||
if (line[*char_counter] == 0) {
|
||||
return(0); // No more statements
|
||||
}
|
||||
@ -361,7 +568,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t
|
||||
return(0);
|
||||
}
|
||||
(*char_counter)++;
|
||||
if (!read_double(line, char_counter, double_ptr)) {
|
||||
if (!read_float(line, char_counter, float_ptr)) {
|
||||
FAIL(STATUS_BAD_NUMBER_FORMAT);
|
||||
return(0);
|
||||
};
|
||||
@ -369,21 +576,24 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t
|
||||
}
|
||||
|
||||
/*
|
||||
Intentionally not supported:
|
||||
Not supported:
|
||||
|
||||
- Canned cycles
|
||||
- Tool radius compensation
|
||||
- A,B,C-axes
|
||||
- Multiple coordinate systems
|
||||
- Evaluation of expressions
|
||||
- Variables
|
||||
- Multiple home locations
|
||||
- Probing
|
||||
- Override control
|
||||
- Override control (TBD)
|
||||
- Tool changes
|
||||
- Switches
|
||||
|
||||
group 0 = {G10, G28, G30, G92, G92.1, G92.2, G92.3} (Non modal G-codes)
|
||||
group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time)
|
||||
(*) Indicates optional parameter, enabled through config.h and re-compile
|
||||
group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets)
|
||||
group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles)
|
||||
group 4 = {M1} (Optional stop, ignored)
|
||||
group 6 = {M6} (Tool change)
|
||||
group 8 = {*M7} enable mist coolant
|
||||
group 9 = {M48, M49} enable/disable feed and speed override switches
|
||||
group 12 = {G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection
|
||||
group 13 = {G61, G61.1, G64} path control mode
|
||||
*/
|
||||
|
71
gcode.h
Normal file → Executable file
71
gcode.h
Normal file → Executable file
@ -1,8 +1,9 @@
|
||||
/*
|
||||
gcode.c - rs274/ngc parser.
|
||||
gcode.h - rs274/ngc parser.
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -18,10 +19,73 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef gcode_h
|
||||
#define gcode_h
|
||||
#include <avr/io.h>
|
||||
#include "nuts_bolts.h"
|
||||
|
||||
// Define modal group internal numbers for checking multiple command violations and tracking the
|
||||
// type of command that is called in the block. A modal group is a group of g-code commands that are
|
||||
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
|
||||
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
|
||||
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
|
||||
#define MODAL_GROUP_NONE 0
|
||||
#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal
|
||||
#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion
|
||||
#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection
|
||||
#define MODAL_GROUP_3 4 // [G90,G91] Distance mode
|
||||
#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping
|
||||
#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode
|
||||
#define MODAL_GROUP_6 7 // [G20,G21] Units
|
||||
#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning
|
||||
#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
|
||||
|
||||
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
|
||||
// internally by the parser to know which command to execute.
|
||||
#define MOTION_MODE_SEEK 0 // G0
|
||||
#define MOTION_MODE_LINEAR 1 // G1
|
||||
#define MOTION_MODE_CW_ARC 2 // G2
|
||||
#define MOTION_MODE_CCW_ARC 3 // G3
|
||||
#define MOTION_MODE_CANCEL 4 // G80
|
||||
|
||||
#define PROGRAM_FLOW_RUNNING 0
|
||||
#define PROGRAM_FLOW_PAUSED 1 // M0, M1
|
||||
#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
|
||||
|
||||
#define NON_MODAL_NONE 0
|
||||
#define NON_MODAL_DWELL 1 // G4
|
||||
#define NON_MODAL_SET_COORDINATE_DATA 2 // G10
|
||||
#define NON_MODAL_GO_HOME_0 3 // G28
|
||||
#define NON_MODAL_SET_HOME_0 4 // G28.1
|
||||
#define NON_MODAL_GO_HOME_1 5 // G30
|
||||
#define NON_MODAL_SET_HOME_1 6 // G30.1
|
||||
#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92
|
||||
#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1
|
||||
|
||||
typedef struct {
|
||||
uint8_t status_code; // Parser status for current block
|
||||
uint8_t motion_mode; // {G0, G1, G2, G3, G80}
|
||||
uint8_t inverse_feed_rate_mode; // {G93, G94}
|
||||
uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21}
|
||||
uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91}
|
||||
uint8_t program_flow; // {M0, M1, M2, M30}
|
||||
int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5}
|
||||
uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9}
|
||||
float feed_rate; // Millimeters/min
|
||||
// float seek_rate; // Millimeters/min. Will be used in v0.9 when axis independence is installed
|
||||
float position[3]; // Where the interpreter considers the tool to be at this point in the code
|
||||
uint8_t tool;
|
||||
// uint16_t spindle_speed; // RPM/100
|
||||
uint8_t plane_axis_0,
|
||||
plane_axis_1,
|
||||
plane_axis_2; // The axes of the selected plane
|
||||
uint8_t coord_select; // Active work coordinate system number. Default: 0=G54.
|
||||
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
|
||||
// position in mm. Loaded from EEPROM when called.
|
||||
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
|
||||
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
|
||||
} parser_state_t;
|
||||
extern parser_state_t gc;
|
||||
|
||||
// Initialize the parser
|
||||
void gc_init();
|
||||
@ -29,4 +93,7 @@ void gc_init();
|
||||
// Execute one block of rs275/ngc/g-code
|
||||
uint8_t gc_execute_line(char *line);
|
||||
|
||||
// Set g-code parser position. Input in steps.
|
||||
void gc_set_current_position(int32_t x, int32_t y, int32_t z);
|
||||
|
||||
#endif
|
||||
|
260
limits.c
Normal file → Executable file
260
limits.c
Normal file → Executable file
@ -1,8 +1,9 @@
|
||||
/*
|
||||
limits.h - code pertaining to limit-switches and performing the homing cycle
|
||||
limits.c - code pertaining to limit-switches and performing the homing cycle
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,84 +21,227 @@
|
||||
|
||||
#include <util/delay.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "stepper.h"
|
||||
#include "settings.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "config.h"
|
||||
#include "spindle_control.h"
|
||||
#include "motion_control.h"
|
||||
#include "planner.h"
|
||||
#include "protocol.h"
|
||||
#include "limits.h"
|
||||
#include "report.h"
|
||||
|
||||
void limits_init() {
|
||||
LIMIT_DDR &= ~(LIMIT_MASK);
|
||||
#define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND)
|
||||
|
||||
void limits_init()
|
||||
{
|
||||
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
|
||||
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
|
||||
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
|
||||
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
|
||||
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
|
||||
} else {
|
||||
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable
|
||||
PCICR &= ~(1 << LIMIT_INT);
|
||||
}
|
||||
}
|
||||
|
||||
static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, bool reverse_direction, uint32_t microseconds_per_pulse) {
|
||||
// First home the Z axis
|
||||
uint32_t step_delay = microseconds_per_pulse - settings.pulse_microseconds;
|
||||
uint8_t out_bits = DIRECTION_MASK;
|
||||
uint8_t limit_bits;
|
||||
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
|
||||
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
|
||||
// If a switch is triggered at all, something bad has happened and treat it as such, regardless
|
||||
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
|
||||
// bouncing pin without a debouncing method.
|
||||
// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
|
||||
// homing cycles and will not respond correctly. Upon user request or need, there may be a
|
||||
// special pinout for an e-stop, but it is generally recommended to just directly connect
|
||||
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
|
||||
ISR(LIMIT_INT_vect)
|
||||
{
|
||||
// TODO: This interrupt may be used to manage the homing cycle directly with the main stepper
|
||||
// interrupt without adding too much to it. All it would need is some way to stop one axis
|
||||
// when its limit is triggered and continue the others. This may reduce some of the code, but
|
||||
// would make Grbl a little harder to read and understand down road. Holding off on this until
|
||||
// we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it.
|
||||
|
||||
if (x_axis) { out_bits |= (1<<X_STEP_BIT); }
|
||||
if (y_axis) { out_bits |= (1<<Y_STEP_BIT); }
|
||||
if (z_axis) { out_bits |= (1<<Z_STEP_BIT); }
|
||||
|
||||
// Invert direction bits if this is a reverse homing_cycle
|
||||
if (reverse_direction) {
|
||||
out_bits ^= DIRECTION_MASK;
|
||||
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
|
||||
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
|
||||
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
|
||||
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
|
||||
// limit setting if their limits are constantly triggering after a reset and move their axes.
|
||||
if (sys.state != STATE_ALARM) {
|
||||
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
|
||||
mc_reset(); // Initiate system kill.
|
||||
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the global invert mask
|
||||
out_bits ^= settings.invert_mask;
|
||||
|
||||
// Set direction pins
|
||||
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
|
||||
// Moves all specified axes in same specified direction (positive=true, negative=false)
|
||||
// and at the homing rate. Homing is a special motion case, where there is only an
|
||||
// acceleration followed by abrupt asynchronous stops by each axes reaching their limit
|
||||
// switch independently. Instead of shoehorning homing cycles into the main stepper
|
||||
// algorithm and overcomplicate things, a stripped-down, lite version of the stepper
|
||||
// algorithm is written here. This also lets users hack and tune this code freely for
|
||||
// their own particular needs without affecting the rest of Grbl.
|
||||
// NOTE: Only the abort runtime command can interrupt this process.
|
||||
static void homing_cycle(uint8_t cycle_mask, int8_t pos_dir, bool invert_pin, float homing_rate)
|
||||
{
|
||||
// Determine governing axes with finest step resolution per distance for the Bresenham
|
||||
// algorithm. This solves the issue when homing multiple axes that have different
|
||||
// resolutions without exceeding system acceleration setting. It doesn't have to be
|
||||
// perfect since homing locates machine zero, but should create for a more consistent
|
||||
// and speedy homing routine.
|
||||
// NOTE: For each axes enabled, the following calculations assume they physically move
|
||||
// an equal distance over each time step until they hit a limit switch, aka dogleg.
|
||||
uint32_t steps[3];
|
||||
uint8_t dist = 0;
|
||||
clear_vector(steps);
|
||||
if (cycle_mask & (1<<X_AXIS)) {
|
||||
dist++;
|
||||
steps[X_AXIS] = lround(settings.steps_per_mm[X_AXIS]);
|
||||
}
|
||||
if (cycle_mask & (1<<Y_AXIS)) {
|
||||
dist++;
|
||||
steps[Y_AXIS] = lround(settings.steps_per_mm[Y_AXIS]);
|
||||
}
|
||||
if (cycle_mask & (1<<Z_AXIS)) {
|
||||
dist++;
|
||||
steps[Z_AXIS] = lround(settings.steps_per_mm[Z_AXIS]);
|
||||
}
|
||||
uint32_t step_event_count = max(steps[X_AXIS], max(steps[Y_AXIS], steps[Z_AXIS]));
|
||||
|
||||
// To ensure global acceleration is not exceeded, reduce the governing axes nominal rate
|
||||
// by adjusting the actual axes distance traveled per step. This is the same procedure
|
||||
// used in the main planner to account for distance traveled when moving multiple axes.
|
||||
// NOTE: When axis acceleration independence is installed, this will be updated to move
|
||||
// all axes at their maximum acceleration and rate.
|
||||
float ds = step_event_count/sqrt(dist);
|
||||
|
||||
// Compute the adjusted step rate change with each acceleration tick. (in step/min/acceleration_tick)
|
||||
uint32_t delta_rate = ceil( ds*settings.acceleration/(60*ACCELERATION_TICKS_PER_SECOND));
|
||||
|
||||
#ifdef HOMING_RATE_ADJUST
|
||||
// Adjust homing rate so a multiple axes moves all at the homing rate independently.
|
||||
homing_rate *= sqrt(dist); // Eq. only works if axes values are 1 or 0.
|
||||
#endif
|
||||
|
||||
// Nominal and initial time increment per step. Nominal should always be greater then 3
|
||||
// usec, since they are based on the same parameters as the main stepper routine. Initial
|
||||
// is based on the MINIMUM_STEPS_PER_MINUTE config. Since homing feed can be very slow,
|
||||
// disable acceleration when rates are below MINIMUM_STEPS_PER_MINUTE.
|
||||
uint32_t dt_min = lround(1000000*60/(ds*homing_rate)); // Cruising (usec/step)
|
||||
uint32_t dt = 1000000*60/MINIMUM_STEPS_PER_MINUTE; // Initial (usec/step)
|
||||
if (dt > dt_min) { dt = dt_min; } // Disable acceleration for very slow rates.
|
||||
|
||||
// Set default out_bits.
|
||||
uint8_t out_bits0 = settings.invert_mask;
|
||||
out_bits0 ^= (settings.homing_dir_mask & DIRECTION_MASK); // Apply homing direction settings
|
||||
if (!pos_dir) { out_bits0 ^= DIRECTION_MASK; } // Invert bits, if negative dir.
|
||||
|
||||
// Initialize stepping variables
|
||||
int32_t counter_x = -(step_event_count >> 1); // Bresenham counters
|
||||
int32_t counter_y = counter_x;
|
||||
int32_t counter_z = counter_x;
|
||||
uint32_t step_delay = dt-settings.pulse_microseconds; // Step delay after pulse
|
||||
uint32_t step_rate = 0; // Tracks step rate. Initialized from 0 rate. (in step/min)
|
||||
uint32_t trap_counter = MICROSECONDS_PER_ACCELERATION_TICK/2; // Acceleration trapezoid counter
|
||||
uint8_t out_bits;
|
||||
uint8_t limit_state;
|
||||
for(;;) {
|
||||
limit_bits = LIMIT_PIN;
|
||||
if (reverse_direction) {
|
||||
// Invert limit_bits if this is a reverse homing_cycle
|
||||
limit_bits ^= LIMIT_MASK;
|
||||
|
||||
// Reset out bits. Both direction and step pins appropriately inverted and set.
|
||||
out_bits = out_bits0;
|
||||
|
||||
// Get limit pin state.
|
||||
limit_state = LIMIT_PIN;
|
||||
if (invert_pin) { limit_state ^= LIMIT_MASK; } // If leaving switch, invert to move.
|
||||
|
||||
// Set step pins by Bresenham line algorithm. If limit switch reached, disable and
|
||||
// flag for completion.
|
||||
if (cycle_mask & (1<<X_AXIS)) {
|
||||
counter_x += steps[X_AXIS];
|
||||
if (counter_x > 0) {
|
||||
if (limit_state & (1<<X_LIMIT_BIT)) { out_bits ^= (1<<X_STEP_BIT); }
|
||||
else { cycle_mask &= ~(1<<X_AXIS); }
|
||||
counter_x -= step_event_count;
|
||||
}
|
||||
}
|
||||
if (x_axis && !(LIMIT_PIN & (1<<X_LIMIT_BIT))) {
|
||||
x_axis = false;
|
||||
out_bits ^= (1<<X_STEP_BIT);
|
||||
if (cycle_mask & (1<<Y_AXIS)) {
|
||||
counter_y += steps[Y_AXIS];
|
||||
if (counter_y > 0) {
|
||||
if (limit_state & (1<<Y_LIMIT_BIT)) { out_bits ^= (1<<Y_STEP_BIT); }
|
||||
else { cycle_mask &= ~(1<<Y_AXIS); }
|
||||
counter_y -= step_event_count;
|
||||
}
|
||||
}
|
||||
if (y_axis && !(LIMIT_PIN & (1<<Y_LIMIT_BIT))) {
|
||||
y_axis = false;
|
||||
out_bits ^= (1<<Y_STEP_BIT);
|
||||
if (cycle_mask & (1<<Z_AXIS)) {
|
||||
counter_z += steps[Z_AXIS];
|
||||
if (counter_z > 0) {
|
||||
if (limit_state & (1<<Z_LIMIT_BIT)) { out_bits ^= (1<<Z_STEP_BIT); }
|
||||
else { cycle_mask &= ~(1<<Z_AXIS); }
|
||||
counter_z -= step_event_count;
|
||||
}
|
||||
}
|
||||
if (z_axis && !(LIMIT_PIN & (1<<Z_LIMIT_BIT))) {
|
||||
z_axis = false;
|
||||
out_bits ^= (1<<Z_STEP_BIT);
|
||||
}
|
||||
// Check if we are done
|
||||
if(!(x_axis || y_axis || z_axis)) { return; }
|
||||
STEPPING_PORT |= out_bits & STEP_MASK;
|
||||
|
||||
// Check if we are done or for system abort
|
||||
protocol_execute_runtime();
|
||||
if (!(cycle_mask) || sys.abort) { return; }
|
||||
|
||||
// Perform step.
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (out_bits & STEP_MASK);
|
||||
delay_us(settings.pulse_microseconds);
|
||||
STEPPING_PORT ^= out_bits & STEP_MASK;
|
||||
STEPPING_PORT = out_bits0;
|
||||
delay_us(step_delay);
|
||||
|
||||
// Track and set the next step delay, if required. This routine uses another Bresenham
|
||||
// line algorithm to follow the constant acceleration line in the velocity and time
|
||||
// domain. This is a lite version of the same routine used in the main stepper program.
|
||||
if (dt > dt_min) { // Unless cruising, check for time update.
|
||||
trap_counter += dt; // Track time passed since last update.
|
||||
if (trap_counter > MICROSECONDS_PER_ACCELERATION_TICK) {
|
||||
trap_counter -= MICROSECONDS_PER_ACCELERATION_TICK;
|
||||
step_rate += delta_rate; // Increment velocity
|
||||
dt = (1000000*60)/step_rate; // Compute new time increment
|
||||
if (dt < dt_min) {dt = dt_min;} // If target rate reached, cruise.
|
||||
step_delay = dt-settings.pulse_microseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void approach_limit_switch(bool x, bool y, bool z) {
|
||||
homing_cycle(x, y, z, false, 100000);
|
||||
}
|
||||
|
||||
static void leave_limit_switch(bool x, bool y, bool z) {
|
||||
homing_cycle(x, y, z, true, 500000);
|
||||
}
|
||||
void limits_go_home()
|
||||
{
|
||||
// Enable only the steppers, not the cycle. Cycle should be inactive/complete.
|
||||
st_wake_up();
|
||||
|
||||
void limits_go_home() {
|
||||
st_synchronize();
|
||||
// Store the current limit switch state
|
||||
uint8_t original_limit_state = LIMIT_PIN;
|
||||
approach_limit_switch(false, false, true); // First home the z axis
|
||||
approach_limit_switch(true, true, false); // Then home the x and y axis
|
||||
// Xor previous and current limit switch state to determine which were high then but have become
|
||||
// low now. These are the actual installed limit switches.
|
||||
uint8_t limit_switches_present = (original_limit_state ^ LIMIT_PIN) & LIMIT_MASK;
|
||||
// Now carefully leave the limit switches
|
||||
leave_limit_switch(
|
||||
limit_switches_present & (1<<X_LIMIT_BIT),
|
||||
limit_switches_present & (1<<Y_LIMIT_BIT),
|
||||
limit_switches_present & (1<<Z_LIMIT_BIT));
|
||||
// Search to engage all axes limit switches at faster homing seek rate.
|
||||
homing_cycle(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0
|
||||
#ifdef HOMING_SEARCH_CYCLE_1
|
||||
homing_cycle(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1
|
||||
#endif
|
||||
#ifdef HOMING_SEARCH_CYCLE_2
|
||||
homing_cycle(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2
|
||||
#endif
|
||||
delay_ms(settings.homing_debounce_delay); // Delay to debounce signal
|
||||
|
||||
// Now in proximity of all limits. Carefully leave and approach switches in multiple cycles
|
||||
// to precisely hone in on the machine zero location. Moves at slower homing feed rate.
|
||||
int8_t n_cycle = N_HOMING_LOCATE_CYCLE;
|
||||
while (n_cycle--) {
|
||||
// Leave all switches to release them. After cycles complete, this is machine zero.
|
||||
homing_cycle(HOMING_LOCATE_CYCLE, false, true, settings.homing_feed_rate);
|
||||
delay_ms(settings.homing_debounce_delay);
|
||||
|
||||
if (n_cycle > 0) {
|
||||
// Re-approach all switches to re-engage them.
|
||||
homing_cycle(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate);
|
||||
delay_ms(settings.homing_debounce_delay);
|
||||
}
|
||||
}
|
||||
|
||||
st_go_idle(); // Call main stepper shutdown routine.
|
||||
}
|
||||
|
81
main.c
Normal file → Executable file
81
main.c
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -18,38 +19,90 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/sleep.h>
|
||||
/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has
|
||||
been integral throughout the development of the higher level details of Grbl, as well
|
||||
as being a consistent sounding board for the future of accessible and free CNC. */
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "config.h"
|
||||
#include "planner.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "stepper.h"
|
||||
#include "spindle_control.h"
|
||||
#include "coolant_control.h"
|
||||
#include "motion_control.h"
|
||||
#include "gcode.h"
|
||||
#include "protocol.h"
|
||||
#include "limits.h"
|
||||
|
||||
#include "report.h"
|
||||
#include "settings.h"
|
||||
#include "serial.h"
|
||||
|
||||
// Declare system global variable structure
|
||||
system_t sys;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Initialize system
|
||||
serial_init(); // Setup serial baud rate and interrupts
|
||||
settings_init(); // Load grbl settings from EEPROM
|
||||
st_init(); // Setup stepper pins and interrupt timers
|
||||
sei(); // Enable interrupts
|
||||
|
||||
serial_init(BAUD_RATE);
|
||||
protocol_init();
|
||||
settings_init();
|
||||
plan_init();
|
||||
st_init();
|
||||
spindle_init();
|
||||
gc_init();
|
||||
limits_init();
|
||||
memset(&sys, 0, sizeof(sys)); // Clear all system variables
|
||||
sys.abort = true; // Set abort to complete initialization
|
||||
sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position
|
||||
|
||||
while (1) {
|
||||
// sleep_mode(); // Wait for it ...
|
||||
for(;;) {
|
||||
|
||||
// Execute system reset upon a system abort, where the main program will return to this loop.
|
||||
// Once here, it is safe to re-initialize the system. At startup, the system will automatically
|
||||
// reset to finish the initialization process.
|
||||
if (sys.abort) {
|
||||
// Reset system.
|
||||
serial_reset_read_buffer(); // Clear serial read buffer
|
||||
plan_init(); // Clear block buffer and planner variables
|
||||
gc_init(); // Set g-code parser to default state
|
||||
protocol_init(); // Clear incoming line data and execute startup lines
|
||||
spindle_init();
|
||||
coolant_init();
|
||||
limits_init();
|
||||
st_reset(); // Clear stepper subsystem variables.
|
||||
|
||||
// Sync cleared gcode and planner positions to current system position, which is only
|
||||
// cleared upon startup, not a reset/abort.
|
||||
sys_sync_current_position();
|
||||
|
||||
// Reset system variables.
|
||||
sys.abort = false;
|
||||
sys.execute = 0;
|
||||
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
|
||||
|
||||
// Check for power-up and set system alarm if homing is enabled to force homing cycle
|
||||
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
|
||||
// startup scripts, but allows access to settings and internal commands. Only a homing
|
||||
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
|
||||
// NOTE: The startup script will run after successful completion of the homing cycle, but
|
||||
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
|
||||
// things uncontrollably. Very bad.
|
||||
#ifdef HOMING_INIT_LOCK
|
||||
if (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
|
||||
#endif
|
||||
|
||||
// Check for and report alarm state after a reset, error, or an initial power up.
|
||||
if (sys.state == STATE_ALARM) {
|
||||
report_feedback_message(MESSAGE_ALARM_LOCK);
|
||||
} else {
|
||||
// All systems go. Set system to ready and execute startup script.
|
||||
sys.state = STATE_IDLE;
|
||||
protocol_execute_startup();
|
||||
}
|
||||
}
|
||||
|
||||
protocol_execute_runtime();
|
||||
protocol_process(); // ... process the serial protocol
|
||||
|
||||
}
|
||||
return 0; /* never reached */
|
||||
}
|
||||
|
222
motion_control.c
Normal file → Executable file
222
motion_control.c
Normal file → Executable file
@ -3,7 +3,8 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
Copyright (c) 2011 Jens Geisler
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,48 +21,98 @@
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include "motion_control.h"
|
||||
#include <util/delay.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include "gcode.h"
|
||||
#include "motion_control.h"
|
||||
#include "spindle_control.h"
|
||||
#include "coolant_control.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "stepper.h"
|
||||
#include "planner.h"
|
||||
#include "limits.h"
|
||||
#include "protocol.h"
|
||||
|
||||
// Execute dwell in seconds. Maximum time delay is > 18 hours, more than enough for any application.
|
||||
void mc_dwell(double seconds)
|
||||
// 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
|
||||
// (1 minute)/feed_rate time.
|
||||
// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
|
||||
// segments, must pass through this routine before being passed to the planner. The seperation of
|
||||
// mc_line and plan_buffer_line is done primarily to make backlash compensation integration simple
|
||||
// and direct.
|
||||
// TODO: Check for a better way to avoid having to push the arguments twice for non-backlash cases.
|
||||
// However, this keeps the memory requirements lower since it doesn't have to call and hold two
|
||||
// plan_buffer_lines in memory. Grbl only has to retain the original line input variables during a
|
||||
// backlash segment(s).
|
||||
void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate)
|
||||
{
|
||||
uint16_t i = floor(seconds);
|
||||
st_synchronize();
|
||||
delay_ms(floor(1000*(seconds-i))); // Delay millisecond remainder
|
||||
while (i > 0) {
|
||||
_delay_ms(1000); // Delay one second
|
||||
i--;
|
||||
}
|
||||
// TODO: Perform soft limit check here. Just check if the target x,y,z values are outside the
|
||||
// work envelope. Should be straightforward and efficient. By placing it here, rather than in
|
||||
// the g-code parser, it directly picks up motions from everywhere in Grbl.
|
||||
|
||||
// If in check gcode mode, prevent motion by blocking planner.
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
|
||||
// TODO: Backlash compensation may be installed here. Only need direction info to track when
|
||||
// to insert a backlash line motion(s) before the intended line motion. Requires its own
|
||||
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
|
||||
// backlash steps will need to be also tracked. Not sure what the best strategy is for this,
|
||||
// i.e. keep the planner independent and do the computations in the status reporting, or let
|
||||
// the planner handle the position corrections. The latter may get complicated.
|
||||
|
||||
// If the buffer is full: good! That means we are well ahead of the robot.
|
||||
// Remain in this loop until there is room in the buffer.
|
||||
do {
|
||||
protocol_execute_runtime(); // Check for any run-time commands
|
||||
if (sys.abort) { return; } // Bail, if system abort.
|
||||
} while ( plan_check_full_buffer() );
|
||||
plan_buffer_line(x, y, z, feed_rate, invert_feed_rate);
|
||||
|
||||
// If idle, indicate to the system there is now a planned block in the buffer ready to cycle
|
||||
// start. Otherwise ignore and continue on.
|
||||
if (!sys.state) { sys.state = STATE_QUEUED; }
|
||||
|
||||
// Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During
|
||||
// a feed hold, auto-start is disabled momentarily until the cycle is resumed by the cycle-start
|
||||
// runtime command.
|
||||
// NOTE: This is allows the user to decide to exclusively use the cycle start runtime command to
|
||||
// begin motion or let grbl auto-start it for them. This is useful when: manually cycle-starting
|
||||
// when the buffer is completely full and primed; auto-starting, if there was only one g-code
|
||||
// command sent during manual operation; or if a system is prone to buffer starvation, auto-start
|
||||
// helps make sure it minimizes any dwelling/motion hiccups and keeps the cycle going.
|
||||
if (sys.auto_start) { st_cycle_start(); }
|
||||
}
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
|
||||
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
|
||||
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
|
||||
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
|
||||
// for vector transformation direction.
|
||||
// The arc is approximated by generating a huge number of tiny, linear segments. The length of each
|
||||
// segment is configured in settings.mm_per_arc_segment.
|
||||
void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, uint8_t axis_1,
|
||||
uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise)
|
||||
void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
|
||||
uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise)
|
||||
{
|
||||
double center_axis0 = position[axis_0] + offset[axis_0];
|
||||
double center_axis1 = position[axis_1] + offset[axis_1];
|
||||
double linear_travel = target[axis_linear] - position[axis_linear];
|
||||
double r_axis0 = -offset[axis_0]; // Radius vector from center to current location
|
||||
double r_axis1 = -offset[axis_1];
|
||||
double rt_axis0 = target[axis_0] - center_axis0;
|
||||
double rt_axis1 = target[axis_1] - center_axis1;
|
||||
float center_axis0 = position[axis_0] + offset[axis_0];
|
||||
float center_axis1 = position[axis_1] + offset[axis_1];
|
||||
float linear_travel = target[axis_linear] - position[axis_linear];
|
||||
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
|
||||
float r_axis1 = -offset[axis_1];
|
||||
float rt_axis0 = target[axis_0] - center_axis0;
|
||||
float rt_axis1 = target[axis_1] - center_axis1;
|
||||
|
||||
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
|
||||
double angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
|
||||
if (angular_travel < 0) { angular_travel += 2*M_PI; }
|
||||
if (isclockwise) { angular_travel -= 2*M_PI; }
|
||||
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
|
||||
if (isclockwise) { // Correct atan2 output per direction
|
||||
if (angular_travel >= 0) { angular_travel -= 2*M_PI; }
|
||||
} else {
|
||||
if (angular_travel <= 0) { angular_travel += 2*M_PI; }
|
||||
}
|
||||
|
||||
double millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
|
||||
float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
|
||||
if (millimeters_of_travel == 0.0) { return; }
|
||||
uint16_t segments = floor(millimeters_of_travel/settings.mm_per_arc_segment);
|
||||
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
|
||||
@ -69,11 +120,11 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
|
||||
// all segments.
|
||||
if (invert_feed_rate) { feed_rate *= segments; }
|
||||
|
||||
double theta_per_segment = angular_travel/segments;
|
||||
double linear_per_segment = linear_travel/segments;
|
||||
float theta_per_segment = angular_travel/segments;
|
||||
float linear_per_segment = linear_travel/segments;
|
||||
|
||||
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
|
||||
and phi is the angle of rotation. Based on the solution approach by Jens Geisler.
|
||||
and phi is the angle of rotation. Solution approach by Jens Geisler.
|
||||
r_T = [cos(phi) -sin(phi);
|
||||
sin(phi) cos(phi] * r ;
|
||||
|
||||
@ -98,13 +149,13 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
|
||||
This is important when there are successive arc motions.
|
||||
*/
|
||||
// Vector rotation matrix values
|
||||
double cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation
|
||||
double sin_T = theta_per_segment;
|
||||
float cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation
|
||||
float sin_T = theta_per_segment;
|
||||
|
||||
double arc_target[3];
|
||||
double sin_Ti;
|
||||
double cos_Ti;
|
||||
double r_axisi;
|
||||
float arc_target[3];
|
||||
float sin_Ti;
|
||||
float cos_Ti;
|
||||
float r_axisi;
|
||||
uint16_t i;
|
||||
int8_t count = 0;
|
||||
|
||||
@ -113,14 +164,14 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
|
||||
|
||||
for (i = 1; i<segments; i++) { // Increment (segments-1)
|
||||
|
||||
if (count < N_ARC_CORRECTION) {
|
||||
if (count < settings.n_arc_correction) {
|
||||
// Apply vector rotation matrix
|
||||
r_axisi = r_axis0*sin_T + r_axis1*cos_T;
|
||||
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
|
||||
r_axis1 = r_axisi;
|
||||
count++;
|
||||
} else {
|
||||
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
|
||||
// Arc correction to radius vector. Computed only every n_arc_correction increments.
|
||||
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
|
||||
cos_Ti = cos(i*theta_per_segment);
|
||||
sin_Ti = sin(i*theta_per_segment);
|
||||
@ -133,15 +184,102 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
|
||||
arc_target[axis_0] = center_axis0 + r_axis0;
|
||||
arc_target[axis_1] = center_axis1 + r_axis1;
|
||||
arc_target[axis_linear] += linear_per_segment;
|
||||
plan_buffer_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, invert_feed_rate);
|
||||
mc_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, invert_feed_rate);
|
||||
|
||||
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
|
||||
if (sys.abort) { return; }
|
||||
}
|
||||
// Ensure last segment arrives at target location.
|
||||
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate);
|
||||
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Execute dwell in seconds.
|
||||
void mc_dwell(float seconds)
|
||||
{
|
||||
uint16_t i = floor(1000/DWELL_TIME_STEP*seconds);
|
||||
plan_synchronize();
|
||||
delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder
|
||||
while (i-- > 0) {
|
||||
// NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds.
|
||||
protocol_execute_runtime();
|
||||
if (sys.abort) { return; }
|
||||
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
|
||||
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
|
||||
// executing the homing cycle. This prevents incorrect buffered plans after homing.
|
||||
void mc_go_home()
|
||||
{
|
||||
st_go_home();
|
||||
sys.state = STATE_HOMING; // Set system state variable
|
||||
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration
|
||||
|
||||
limits_go_home(); // Perform homing routine.
|
||||
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
|
||||
|
||||
// The machine should now be homed and machine zero has been located. Upon completion,
|
||||
// reset system position and sync internal position vectors.
|
||||
clear_vector_float(sys.position); // Set machine zero
|
||||
sys_sync_current_position();
|
||||
sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed.
|
||||
|
||||
// Pull-off axes (that have been homed) from limit switches before continuing motion.
|
||||
// This provides some initial clearance off the switches and should also help prevent them
|
||||
// from falsely tripping when hard limits are enabled.
|
||||
int8_t x_dir, y_dir, z_dir;
|
||||
x_dir = y_dir = z_dir = 0;
|
||||
if (HOMING_LOCATE_CYCLE & (1<<X_AXIS)) {
|
||||
if (settings.homing_dir_mask & (1<<X_DIRECTION_BIT)) { x_dir = 1; }
|
||||
else { x_dir = -1; }
|
||||
}
|
||||
if (HOMING_LOCATE_CYCLE & (1<<Y_AXIS)) {
|
||||
if (settings.homing_dir_mask & (1<<Y_DIRECTION_BIT)) { y_dir = 1; }
|
||||
else { y_dir = -1; }
|
||||
}
|
||||
if (HOMING_LOCATE_CYCLE & (1<<Z_AXIS)) {
|
||||
if (settings.homing_dir_mask & (1<<Z_DIRECTION_BIT)) { z_dir = 1; }
|
||||
else { z_dir = -1; }
|
||||
}
|
||||
mc_line(x_dir*settings.homing_pulloff, y_dir*settings.homing_pulloff,
|
||||
z_dir*settings.homing_pulloff, settings.homing_seek_rate, false);
|
||||
st_cycle_start(); // Move it. Nothing should be in the buffer except this motion.
|
||||
plan_synchronize(); // Make sure the motion completes.
|
||||
|
||||
// The gcode parser position circumvented by the pull-off maneuver, so sync position vectors.
|
||||
sys_sync_current_position();
|
||||
|
||||
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
|
||||
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; }
|
||||
// Finished!
|
||||
}
|
||||
|
||||
|
||||
// Method to ready the system to reset by setting the runtime reset command and killing any
|
||||
// active processes in the system. This also checks if a system reset is issued while Grbl
|
||||
// is in a motion state. If so, kills the steppers and sets the system alarm to flag position
|
||||
// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by
|
||||
// runtime abort command and hard limits. So, keep to a minimum.
|
||||
void mc_reset()
|
||||
{
|
||||
// Only this function can set the system reset. Helps prevent multiple kill calls.
|
||||
if (bit_isfalse(sys.execute, EXEC_RESET)) {
|
||||
sys.execute |= EXEC_RESET;
|
||||
|
||||
// Kill spindle and coolant.
|
||||
spindle_stop();
|
||||
coolant_stop();
|
||||
|
||||
// Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging
|
||||
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
|
||||
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
|
||||
// violated, by which, all bets are off.
|
||||
switch (sys.state) {
|
||||
case STATE_CYCLE: case STATE_HOLD: case STATE_HOMING: // case STATE_JOG:
|
||||
sys.execute |= EXEC_ALARM; // Execute alarm state.
|
||||
st_go_idle(); // Execute alarm force kills steppers. Position likely lost.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
motion_control.h
Normal file → Executable file
22
motion_control.h
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -25,29 +25,25 @@
|
||||
#include <avr/io.h>
|
||||
#include "planner.h"
|
||||
|
||||
// NOTE: Although the following functions structurally belongs in this module, there is nothing to do but
|
||||
// to forward the request to the planner.
|
||||
|
||||
// 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
|
||||
// (1 minute)/feed_rate time.
|
||||
#define mc_line(x, y, z, feed_rate, invert_feed_rate) plan_buffer_line(x, y, z, feed_rate, invert_feed_rate)
|
||||
void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate);
|
||||
|
||||
#define mc_set_current_position(x, y, z) plan_set_current_position(x, y, z)
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
|
||||
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
|
||||
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
|
||||
// for vector transformation direction.
|
||||
void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, uint8_t axis_1,
|
||||
uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise);
|
||||
#endif
|
||||
void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
|
||||
uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise);
|
||||
|
||||
// Dwell for a specific number of seconds
|
||||
void mc_dwell(double seconds);
|
||||
void mc_dwell(float seconds);
|
||||
|
||||
// Send the tool home (not implemented)
|
||||
// Perform homing cycle to locate machine zero. Requires limit switches.
|
||||
void mc_go_home();
|
||||
|
||||
// Performs system reset. If in motion state, kills all motion and sets system alarm.
|
||||
void mc_reset();
|
||||
|
||||
#endif
|
||||
|
123
nuts_bolts.c
Normal file → Executable file
123
nuts_bolts.c
Normal file → Executable file
@ -19,25 +19,97 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nuts_bolts.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <util/delay.h>
|
||||
#include "nuts_bolts.h"
|
||||
#include "gcode.h"
|
||||
#include "planner.h"
|
||||
|
||||
int read_double(char *line, uint8_t *char_counter, double *double_ptr)
|
||||
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
|
||||
extern float __floatunsisf (unsigned long);
|
||||
|
||||
// Extracts a floating point value from a string. The following code is based loosely on
|
||||
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
|
||||
// available conversion method examples, but has been highly optimized for Grbl. For known
|
||||
// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
|
||||
// Scientific notation is officially not supported by g-code, and the 'E' character may
|
||||
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
|
||||
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
|
||||
int read_float(char *line, uint8_t *char_counter, float *float_ptr)
|
||||
{
|
||||
char *start = line + *char_counter;
|
||||
char *end;
|
||||
char *ptr = line + *char_counter;
|
||||
unsigned char c;
|
||||
|
||||
*double_ptr = strtod(start, &end);
|
||||
if(end == start) {
|
||||
return(false);
|
||||
};
|
||||
// Grab first character and increment pointer. No spaces assumed in line.
|
||||
c = *ptr++;
|
||||
|
||||
// Capture initial positive/minus character
|
||||
bool isnegative = false;
|
||||
if (c == '-') {
|
||||
isnegative = true;
|
||||
c = *ptr++;
|
||||
} else if (c == '+') {
|
||||
c = *ptr++;
|
||||
}
|
||||
|
||||
// Extract number into fast integer. Track decimal in terms of exponent value.
|
||||
uint32_t intval = 0;
|
||||
int8_t exp = 0;
|
||||
uint8_t ndigit = 0;
|
||||
bool isdecimal = false;
|
||||
while(1) {
|
||||
c -= '0';
|
||||
if (c <= 9) {
|
||||
ndigit++;
|
||||
if (ndigit <= MAX_INT_DIGITS) {
|
||||
if (isdecimal) { exp--; }
|
||||
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
|
||||
} else {
|
||||
if (!(isdecimal)) { exp++; } // Drop overflow digits
|
||||
}
|
||||
} else if (c == (('.'-'0') & 0xff) && !(isdecimal)) {
|
||||
isdecimal = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
c = *ptr++;
|
||||
}
|
||||
|
||||
// Return if no digits have been read.
|
||||
if (!ndigit) { return(false); };
|
||||
|
||||
// Convert integer into floating point.
|
||||
float fval;
|
||||
fval = __floatunsisf(intval);
|
||||
|
||||
// Apply decimal. Should perform no more than two floating point multiplications for the
|
||||
// expected range of E0 to E-4.
|
||||
if (fval != 0) {
|
||||
while (exp <= -2) {
|
||||
fval *= 0.01;
|
||||
exp += 2;
|
||||
}
|
||||
if (exp < 0) {
|
||||
fval *= 0.1;
|
||||
} else if (exp > 0) {
|
||||
do {
|
||||
fval *= 10.0;
|
||||
} while (--exp > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign floating point value with correct sign.
|
||||
if (isnegative) {
|
||||
*float_ptr = -fval;
|
||||
} else {
|
||||
*float_ptr = fval;
|
||||
}
|
||||
|
||||
*char_counter = ptr - line - 1; // Set char_counter to next statement
|
||||
|
||||
*char_counter = end - line;
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(),
|
||||
// which only accepts constants in future compiler releases.
|
||||
void delay_ms(uint16_t ms)
|
||||
@ -45,9 +117,32 @@ void delay_ms(uint16_t ms)
|
||||
while ( ms-- ) { _delay_ms(1); }
|
||||
}
|
||||
|
||||
|
||||
// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(),
|
||||
// which only accepts constants in future compiler releases.
|
||||
void delay_us(uint16_t us)
|
||||
// which only accepts constants in future compiler releases. Written to perform more
|
||||
// efficiently with larger delays, as the counter adds parasitic time in each iteration.
|
||||
void delay_us(uint32_t us)
|
||||
{
|
||||
while ( us-- ) { _delay_us(1); }
|
||||
while (us) {
|
||||
if (us < 10) {
|
||||
_delay_us(1);
|
||||
us--;
|
||||
} else if (us < 100) {
|
||||
_delay_us(10);
|
||||
us -= 10;
|
||||
} else if (us < 1000) {
|
||||
_delay_us(100);
|
||||
us -= 100;
|
||||
} else {
|
||||
_delay_ms(1);
|
||||
us -= 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Syncs all internal position vectors to the current system position.
|
||||
void sys_sync_current_position()
|
||||
{
|
||||
plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
|
||||
gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
|
||||
}
|
||||
|
71
nuts_bolts.h
Normal file → Executable file
71
nuts_bolts.h
Normal file → Executable file
@ -21,26 +21,89 @@
|
||||
|
||||
#ifndef nuts_bolts_h
|
||||
#define nuts_bolts_h
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
#define X_AXIS 0
|
||||
#define N_AXIS 3 // Number of axes
|
||||
#define X_AXIS 0 // Axis indexing value
|
||||
#define Y_AXIS 1
|
||||
#define Z_AXIS 2
|
||||
|
||||
#define MM_PER_INCH (25.40)
|
||||
#define INCH_PER_MM (0.0393701)
|
||||
|
||||
// Useful macros
|
||||
#define clear_vector(a) memset(a, 0, sizeof(a))
|
||||
#define clear_vector_double(a) memset(a, 0.0, sizeof(a))
|
||||
#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
// Bit field and masking macros
|
||||
#define bit(n) (1 << n)
|
||||
#define bit_true(x,mask) (x |= mask)
|
||||
#define bit_false(x,mask) (x &= ~mask)
|
||||
#define bit_toggle(x,mask) (x ^= mask)
|
||||
#define bit_istrue(x,mask) ((x & mask) != 0)
|
||||
#define bit_isfalse(x,mask) ((x & mask) == 0)
|
||||
|
||||
// Define system executor bit map. Used internally by runtime protocol as runtime command flags,
|
||||
// which notifies the main program to execute the specified runtime command asynchronously.
|
||||
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
|
||||
// flags are always false, so the runtime protocol only needs to check for a non-zero value to
|
||||
// know when there is a runtime 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_ALARM bit(5) // bitmask 00100000
|
||||
#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000
|
||||
// #define bit(7) // bitmask 10000000
|
||||
|
||||
// Define system state bit map. The state variable primarily tracks the individual functions
|
||||
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
|
||||
// critical events.
|
||||
#define STATE_IDLE 0 // Must be zero.
|
||||
#define STATE_INIT 1 // Initial power up state.
|
||||
#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start.
|
||||
#define STATE_CYCLE 3 // Cycle is running
|
||||
#define STATE_HOLD 4 // Executing feed hold
|
||||
#define STATE_HOMING 5 // Performing homing cycle
|
||||
#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access.
|
||||
#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only.
|
||||
// #define STATE_JOG 8 // Jogging mode is unique like homing.
|
||||
|
||||
// Define global system variables
|
||||
typedef struct {
|
||||
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
|
||||
uint8_t state; // Tracks the current state of Grbl.
|
||||
volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks.
|
||||
int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
|
||||
// NOTE: This may need to be a volatile variable, if problems arise.
|
||||
uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
|
||||
} system_t;
|
||||
extern system_t sys;
|
||||
|
||||
// Read a floating point value from a string. Line points to the input buffer, char_counter
|
||||
// is the indexer pointing to the current character of the line, while double_ptr is
|
||||
// is the indexer pointing to the current character of the line, while float_ptr is
|
||||
// a pointer to the result variable. Returns true when it succeeds
|
||||
int read_double(char *line, uint8_t *char_counter, double *double_ptr);
|
||||
int read_float(char *line, uint8_t *char_counter, float *float_ptr);
|
||||
|
||||
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
|
||||
void delay_ms(uint16_t ms);
|
||||
|
||||
// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us().
|
||||
void delay_us(uint32_t us);
|
||||
|
||||
// Syncs Grbl's gcode and planner position variables with the system position.
|
||||
void sys_sync_current_position();
|
||||
|
||||
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
|
||||
void delay_ms(uint16_t ms);
|
||||
|
187
planner.c
Normal file → Executable file
187
planner.c
Normal file → Executable file
@ -3,7 +3,8 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
Copyright (c) 2011 Jens Geisler
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,30 +23,28 @@
|
||||
/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "planner.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "stepper.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
|
||||
// The number of linear motions that can be in the plan at any give time
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#define BLOCK_BUFFER_SIZE 18
|
||||
#else
|
||||
#define BLOCK_BUFFER_SIZE 5
|
||||
#endif
|
||||
#include "protocol.h"
|
||||
|
||||
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
||||
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
|
||||
static volatile uint8_t block_buffer_tail; // Index of the block to process now
|
||||
static uint8_t next_buffer_head; // Index of the next buffer head
|
||||
|
||||
static int32_t position[3]; // The current position of the tool in absolute steps
|
||||
static double previous_unit_vec[3]; // Unit vector of previous path line segment
|
||||
static double previous_nominal_speed; // Nominal speed of previous path line segment
|
||||
|
||||
// Define planner variables
|
||||
typedef struct {
|
||||
int32_t position[3]; // The planner position of the tool in absolute steps. Kept separate
|
||||
// from g-code position for movements requiring multiple line motions,
|
||||
// i.e. arcs, canned cycles, and backlash compensation.
|
||||
float previous_unit_vec[3]; // Unit vector of previous path line segment
|
||||
float previous_nominal_speed; // Nominal speed of previous path line segment
|
||||
} planner_t;
|
||||
static planner_t pl;
|
||||
|
||||
// Returns the index of the next block in the ring buffer
|
||||
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
|
||||
@ -68,7 +67,7 @@ static uint8_t prev_block_index(uint8_t block_index)
|
||||
|
||||
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the
|
||||
// given acceleration:
|
||||
static double estimate_acceleration_distance(double initial_rate, double target_rate, double acceleration)
|
||||
static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration)
|
||||
{
|
||||
return( (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration) );
|
||||
}
|
||||
@ -87,7 +86,7 @@ static double estimate_acceleration_distance(double initial_rate, double target_
|
||||
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
|
||||
// a total travel of distance. This can be used to compute the intersection point between acceleration and
|
||||
// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
|
||||
static double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance)
|
||||
static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance)
|
||||
{
|
||||
return( (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration) );
|
||||
}
|
||||
@ -98,7 +97,7 @@ static double intersection_distance(double initial_rate, double final_rate, doub
|
||||
// NOTE: sqrt() reimplimented here from prior version due to improved planner logic. Increases speed
|
||||
// in time critical computations, i.e. arcs or rapid short lines from curves. Guaranteed to not exceed
|
||||
// BLOCK_BUFFER_SIZE calls per planner cycle.
|
||||
static double max_allowable_speed(double acceleration, double target_velocity, double distance)
|
||||
static float max_allowable_speed(float acceleration, float target_velocity, float distance)
|
||||
{
|
||||
return( sqrt(target_velocity*target_velocity-2*acceleration*distance) );
|
||||
}
|
||||
@ -158,7 +157,7 @@ static void planner_forward_pass_kernel(block_t *previous, block_t *current, blo
|
||||
// If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
|
||||
if (!previous->nominal_length_flag) {
|
||||
if (previous->entry_speed < current->entry_speed) {
|
||||
double entry_speed = min( current->entry_speed,
|
||||
float entry_speed = min( current->entry_speed,
|
||||
max_allowable_speed(-settings.acceleration,previous->entry_speed,previous->millimeters) );
|
||||
|
||||
// Check for junction speed change
|
||||
@ -201,7 +200,7 @@ static void planner_forward_pass()
|
||||
// The factors represent a factor of braking and must be in the range 0.0-1.0.
|
||||
// This converts the planner parameters to the data required by the stepper controller.
|
||||
// NOTE: Final rates must be computed in terms of their respective blocks.
|
||||
static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor)
|
||||
static void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor)
|
||||
{
|
||||
block->initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min)
|
||||
block->final_rate = ceil(block->nominal_rate*exit_factor); // (step/min)
|
||||
@ -296,76 +295,92 @@ static void planner_recalculate()
|
||||
planner_recalculate_trapezoids();
|
||||
}
|
||||
|
||||
void plan_init()
|
||||
void plan_reset_buffer()
|
||||
{
|
||||
block_buffer_head = 0;
|
||||
block_buffer_tail = 0;
|
||||
clear_vector(position);
|
||||
clear_vector_double(previous_unit_vec);
|
||||
previous_nominal_speed = 0.0;
|
||||
block_buffer_tail = block_buffer_head;
|
||||
next_buffer_head = next_block_index(block_buffer_head);
|
||||
}
|
||||
|
||||
void plan_discard_current_block()
|
||||
void plan_init()
|
||||
{
|
||||
plan_reset_buffer();
|
||||
memset(&pl, 0, sizeof(pl)); // Clear planner struct
|
||||
}
|
||||
|
||||
inline void plan_discard_current_block()
|
||||
{
|
||||
if (block_buffer_head != block_buffer_tail) {
|
||||
block_buffer_tail = next_block_index( block_buffer_tail );
|
||||
}
|
||||
}
|
||||
|
||||
block_t *plan_get_current_block()
|
||||
inline block_t *plan_get_current_block()
|
||||
{
|
||||
if (block_buffer_head == block_buffer_tail) { return(NULL); }
|
||||
return(&block_buffer[block_buffer_tail]);
|
||||
}
|
||||
|
||||
// Returns the availability status of the block ring buffer. True, if full.
|
||||
uint8_t plan_check_full_buffer()
|
||||
{
|
||||
if (block_buffer_tail == next_buffer_head) { return(true); }
|
||||
return(false);
|
||||
}
|
||||
|
||||
// Block until all buffered steps are executed or in a cycle state. Works with feed hold
|
||||
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
||||
void plan_synchronize()
|
||||
{
|
||||
while (plan_get_current_block() || sys.state == STATE_CYCLE) {
|
||||
protocol_execute_runtime(); // Check and execute run-time commands
|
||||
if (sys.abort) { return; } // Check for system abort
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
|
||||
// millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
||||
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
|
||||
void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate)
|
||||
// All position data passed to the planner must be in terms of machine position to keep the planner
|
||||
// independent of any coordinate system changes and offsets, which are handled by the g-code parser.
|
||||
// NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control.
|
||||
void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate)
|
||||
{
|
||||
// Prepare to set up new block
|
||||
block_t *block = &block_buffer[block_buffer_head];
|
||||
|
||||
// Calculate target position in absolute steps
|
||||
int32_t target[3];
|
||||
target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]);
|
||||
target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]);
|
||||
target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]);
|
||||
|
||||
// Calculate the buffer head after we push this byte
|
||||
uint8_t next_buffer_head = next_block_index( block_buffer_head );
|
||||
// If the buffer is full: good! That means we are well ahead of the robot.
|
||||
// Rest here until there is room in the buffer.
|
||||
while(block_buffer_tail == next_buffer_head) { sleep_mode(); }
|
||||
|
||||
// Prepare to set up new block
|
||||
block_t *block = &block_buffer[block_buffer_head];
|
||||
|
||||
// Compute direction bits for this block
|
||||
block->direction_bits = 0;
|
||||
if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<<X_DIRECTION_BIT); }
|
||||
if (target[Y_AXIS] < position[Y_AXIS]) { block->direction_bits |= (1<<Y_DIRECTION_BIT); }
|
||||
if (target[Z_AXIS] < position[Z_AXIS]) { block->direction_bits |= (1<<Z_DIRECTION_BIT); }
|
||||
if (target[X_AXIS] < pl.position[X_AXIS]) { block->direction_bits |= (1<<X_DIRECTION_BIT); }
|
||||
if (target[Y_AXIS] < pl.position[Y_AXIS]) { block->direction_bits |= (1<<Y_DIRECTION_BIT); }
|
||||
if (target[Z_AXIS] < pl.position[Z_AXIS]) { block->direction_bits |= (1<<Z_DIRECTION_BIT); }
|
||||
|
||||
// Number of steps for each axis
|
||||
block->steps_x = labs(target[X_AXIS]-position[X_AXIS]);
|
||||
block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]);
|
||||
block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]);
|
||||
block->steps_x = labs(target[X_AXIS]-pl.position[X_AXIS]);
|
||||
block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]);
|
||||
block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]);
|
||||
block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z));
|
||||
|
||||
// Bail if this is a zero-length block
|
||||
if (block->step_event_count == 0) { return; };
|
||||
|
||||
// Compute path vector in terms of absolute step target and current positions
|
||||
double delta_mm[3];
|
||||
delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/settings.steps_per_mm[X_AXIS];
|
||||
delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/settings.steps_per_mm[Y_AXIS];
|
||||
delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/settings.steps_per_mm[Z_AXIS];
|
||||
block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) +
|
||||
square(delta_mm[Z_AXIS]));
|
||||
double inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides
|
||||
float delta_mm[3];
|
||||
delta_mm[X_AXIS] = (target[X_AXIS]-pl.position[X_AXIS])/settings.steps_per_mm[X_AXIS];
|
||||
delta_mm[Y_AXIS] = (target[Y_AXIS]-pl.position[Y_AXIS])/settings.steps_per_mm[Y_AXIS];
|
||||
delta_mm[Z_AXIS] = (target[Z_AXIS]-pl.position[Z_AXIS])/settings.steps_per_mm[Z_AXIS];
|
||||
block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] +
|
||||
delta_mm[Z_AXIS]*delta_mm[Z_AXIS]);
|
||||
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides
|
||||
|
||||
// Calculate speed in mm/minute for each axis. No divide by zero due to previous checks.
|
||||
// NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c
|
||||
double inverse_minute;
|
||||
float inverse_minute;
|
||||
if (!invert_feed_rate) {
|
||||
inverse_minute = feed_rate * inverse_millimeters;
|
||||
} else {
|
||||
@ -385,7 +400,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in
|
||||
settings.acceleration / (60 * ACCELERATION_TICKS_PER_SECOND )); // (step/min/acceleration_tick)
|
||||
|
||||
// Compute path unit vector
|
||||
double unit_vec[3];
|
||||
float unit_vec[3];
|
||||
|
||||
unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters;
|
||||
unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters;
|
||||
@ -400,23 +415,29 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in
|
||||
// path width or max_jerk in the previous grbl version. This approach does not actually deviate
|
||||
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
|
||||
// nonlinearities of both the junction angle and junction velocity.
|
||||
double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed
|
||||
// NOTE: This is basically an exact path mode (G61), but it doesn't come to a complete stop unless
|
||||
// the junction deviation value is high. In the future, if continuous mode (G64) is desired, the
|
||||
// math here is exactly the same. Instead of motioning all the way to junction point, the machine
|
||||
// will just need to follow the arc circle defined above and check if the arc radii are no longer
|
||||
// than half of either line segment to ensure no overlapping. Right now, the Arduino likely doesn't
|
||||
// have the horsepower to do these calculations at high feed rates.
|
||||
float vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed
|
||||
|
||||
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
|
||||
if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) {
|
||||
if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed > 0.0)) {
|
||||
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
|
||||
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
|
||||
double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
|
||||
- previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS]
|
||||
- previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ;
|
||||
float cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
|
||||
- pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS]
|
||||
- pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ;
|
||||
|
||||
// Skip and use default max junction speed for 0 degree acute junction.
|
||||
if (cos_theta < 0.95) {
|
||||
vmax_junction = min(previous_nominal_speed,block->nominal_speed);
|
||||
vmax_junction = min(pl.previous_nominal_speed,block->nominal_speed);
|
||||
// Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds.
|
||||
if (cos_theta > -0.95) {
|
||||
// Compute maximum junction velocity based on maximum acceleration and junction deviation
|
||||
double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
|
||||
float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
|
||||
vmax_junction = min(vmax_junction,
|
||||
sqrt(settings.acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) );
|
||||
}
|
||||
@ -425,7 +446,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in
|
||||
block->max_entry_speed = vmax_junction;
|
||||
|
||||
// Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
|
||||
double v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters);
|
||||
float v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters);
|
||||
block->entry_speed = min(vmax_junction, v_allowable);
|
||||
|
||||
// Initialize planner efficiency flags
|
||||
@ -441,23 +462,43 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in
|
||||
block->recalculate_flag = true; // Always calculate trapezoid for new block
|
||||
|
||||
// Update previous path unit_vector and nominal speed
|
||||
memcpy(previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[]
|
||||
previous_nominal_speed = block->nominal_speed;
|
||||
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
|
||||
pl.previous_nominal_speed = block->nominal_speed;
|
||||
|
||||
// Move buffer head
|
||||
// Update buffer head and next buffer head indices
|
||||
block_buffer_head = next_buffer_head;
|
||||
// Update position
|
||||
memcpy(position, target, sizeof(target)); // position[] = target[]
|
||||
next_buffer_head = next_block_index(block_buffer_head);
|
||||
|
||||
// Update planner position
|
||||
memcpy(pl.position, target, sizeof(target)); // pl.position[] = target[]
|
||||
|
||||
planner_recalculate();
|
||||
st_cycle_start();
|
||||
}
|
||||
|
||||
// Reset the planner position vector and planner speed
|
||||
void plan_set_current_position(double x, double y, double z) {
|
||||
position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]);
|
||||
position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]);
|
||||
position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]);
|
||||
previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest.
|
||||
clear_vector_double(previous_unit_vec);
|
||||
// Reset the planner position vector (in steps). Called by the system abort routine.
|
||||
void plan_set_current_position(int32_t x, int32_t y, int32_t z)
|
||||
{
|
||||
pl.position[X_AXIS] = x;
|
||||
pl.position[Y_AXIS] = y;
|
||||
pl.position[Z_AXIS] = z;
|
||||
}
|
||||
|
||||
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
|
||||
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
|
||||
void plan_cycle_reinitialize(int32_t step_events_remaining)
|
||||
{
|
||||
block_t *block = &block_buffer[block_buffer_tail]; // Point to partially completed block
|
||||
|
||||
// Only remaining millimeters and step_event_count need to be updated for planner recalculate.
|
||||
// Other variables (step_x, step_y, step_z, rate_delta, etc.) all need to remain the same to
|
||||
// ensure the original planned motion is resumed exactly.
|
||||
block->millimeters = (block->millimeters*step_events_remaining)/block->step_event_count;
|
||||
block->step_event_count = step_events_remaining;
|
||||
|
||||
// Re-plan from a complete stop. Reset planner entry speeds and flags.
|
||||
block->entry_speed = 0.0;
|
||||
block->max_entry_speed = 0.0;
|
||||
block->nominal_length_flag = false;
|
||||
block->recalculate_flag = true;
|
||||
planner_recalculate();
|
||||
}
|
||||
|
33
planner.h
Normal file → Executable file
33
planner.h
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,7 +22,10 @@
|
||||
#ifndef planner_h
|
||||
#define planner_h
|
||||
|
||||
#include <inttypes.h>
|
||||
// The number of linear motions that can be in the plan at any give time
|
||||
#ifndef BLOCK_BUFFER_SIZE
|
||||
#define BLOCK_BUFFER_SIZE 18
|
||||
#endif
|
||||
|
||||
// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in
|
||||
// the source g-code and may never actually be reached if acceleration management is active.
|
||||
@ -34,10 +37,10 @@ typedef struct {
|
||||
int32_t step_event_count; // The number of step events required to complete this block
|
||||
|
||||
// Fields used by the motion planner to manage acceleration
|
||||
double nominal_speed; // The nominal speed for this block in mm/min
|
||||
double entry_speed; // Entry speed at previous-current junction in mm/min
|
||||
double max_entry_speed; // Maximum allowable junction entry speed in mm/min
|
||||
double millimeters; // The total travel of this block in mm
|
||||
float nominal_speed; // The nominal speed for this block in mm/min
|
||||
float entry_speed; // Entry speed at previous-current block junction in mm/min
|
||||
float max_entry_speed; // Maximum allowable junction entry speed in mm/min
|
||||
float millimeters; // The total travel of this block in mm
|
||||
uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction
|
||||
uint8_t nominal_length_flag; // Planner flag for nominal speed always reached
|
||||
|
||||
@ -57,7 +60,7 @@ void plan_init();
|
||||
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
|
||||
// millimaters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
||||
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
|
||||
void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate);
|
||||
void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate);
|
||||
|
||||
// Called when the current block is no longer needed. Discards the block and makes the memory
|
||||
// availible for new blocks.
|
||||
@ -66,7 +69,19 @@ void plan_discard_current_block();
|
||||
// Gets the current block. Returns NULL if buffer empty
|
||||
block_t *plan_get_current_block();
|
||||
|
||||
// Reset the position vector
|
||||
void plan_set_current_position(double x, double y, double z);
|
||||
// Reset the planner position vector (in steps)
|
||||
void plan_set_current_position(int32_t x, int32_t y, int32_t z);
|
||||
|
||||
// Reinitialize plan with a partially completed block
|
||||
void plan_cycle_reinitialize(int32_t step_events_remaining);
|
||||
|
||||
// Reset buffer
|
||||
void plan_reset_buffer();
|
||||
|
||||
// Returns the status of the block ring buffer. True, if buffer is full.
|
||||
uint8_t plan_check_full_buffer();
|
||||
|
||||
// Block until all buffered steps are executed
|
||||
void plan_synchronize();
|
||||
|
||||
#endif
|
||||
|
143
print.c
Normal file → Executable file
143
print.c
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -23,14 +23,10 @@
|
||||
used to be a part of the Arduino project. */
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "config.h"
|
||||
#include "serial.h"
|
||||
|
||||
#ifndef DECIMAL_PLACES
|
||||
#define DECIMAL_PLACES 3
|
||||
#define DECIMAL_MULTIPLIER 1000
|
||||
#endif
|
||||
#include "settings.h"
|
||||
|
||||
void printString(const char *s)
|
||||
{
|
||||
@ -46,59 +42,108 @@ void printPgmString(const char *s)
|
||||
serial_write(c);
|
||||
}
|
||||
|
||||
void printIntegerInBase(unsigned long n, unsigned long base)
|
||||
// void printIntegerInBase(unsigned long n, unsigned long base)
|
||||
// {
|
||||
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
|
||||
// unsigned long i = 0;
|
||||
//
|
||||
// if (n == 0) {
|
||||
// serial_write('0');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// while (n > 0) {
|
||||
// buf[i++] = n % base;
|
||||
// n /= base;
|
||||
// }
|
||||
//
|
||||
// for (; i > 0; i--)
|
||||
// serial_write(buf[i - 1] < 10 ?
|
||||
// '0' + buf[i - 1] :
|
||||
// 'A' + buf[i - 1] - 10);
|
||||
// }
|
||||
|
||||
void print_uint8_base2(uint8_t n)
|
||||
{
|
||||
unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
|
||||
unsigned long i = 0;
|
||||
unsigned char buf[8];
|
||||
uint8_t i = 0;
|
||||
|
||||
if (n == 0) {
|
||||
serial_write('0');
|
||||
return;
|
||||
}
|
||||
|
||||
while (n > 0) {
|
||||
buf[i++] = n % base;
|
||||
n /= base;
|
||||
for (; i < 8; i++) {
|
||||
buf[i] = n & 1;
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
for (; i > 0; i--)
|
||||
serial_write(buf[i - 1] < 10 ?
|
||||
'0' + buf[i - 1] :
|
||||
'A' + buf[i - 1] - 10);
|
||||
serial_write('0' + buf[i - 1]);
|
||||
}
|
||||
|
||||
static void print_uint32_base10(unsigned long n)
|
||||
{
|
||||
unsigned char buf[10];
|
||||
uint8_t i = 0;
|
||||
|
||||
if (n == 0) {
|
||||
serial_write('0');
|
||||
return;
|
||||
}
|
||||
|
||||
while (n > 0) {
|
||||
buf[i++] = n % 10 + '0';
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
for (; i > 0; i--)
|
||||
serial_write(buf[i-1]);
|
||||
}
|
||||
|
||||
void printInteger(long n)
|
||||
{
|
||||
if (n < 0) {
|
||||
serial_write('-');
|
||||
n = -n;
|
||||
}
|
||||
|
||||
printIntegerInBase(n, 10);
|
||||
}
|
||||
|
||||
// A very simple
|
||||
void printFloat(double n)
|
||||
{
|
||||
if (n < 0) {
|
||||
serial_write('-');
|
||||
n = -n;
|
||||
}
|
||||
n += 0.5/DECIMAL_MULTIPLIER; // Add rounding factor
|
||||
|
||||
long integer_part;
|
||||
integer_part = (int)n;
|
||||
printIntegerInBase(integer_part,10);
|
||||
|
||||
serial_write('.');
|
||||
|
||||
n -= integer_part;
|
||||
int decimals = DECIMAL_PLACES;
|
||||
uint8_t decimal_part;
|
||||
while(decimals-- > 0) {
|
||||
n *= 10;
|
||||
decimal_part = (int) n;
|
||||
serial_write('0'+decimal_part);
|
||||
n -= decimal_part;
|
||||
}
|
||||
print_uint32_base10(n);
|
||||
}
|
||||
|
||||
// Convert float to string by immediately converting to a long integer, which contains
|
||||
// more digits than a float. Number of decimal places, which are tracked by a counter,
|
||||
// may be set by the user. The integer is then efficiently converted to a string.
|
||||
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
|
||||
// techniques are actually just slightly slower. Found this out the hard way.
|
||||
void printFloat(float n)
|
||||
{
|
||||
if (n < 0) {
|
||||
serial_write('-');
|
||||
n = -n;
|
||||
}
|
||||
|
||||
uint8_t decimals = settings.decimal_places;
|
||||
while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4.
|
||||
n *= 100;
|
||||
decimals -= 2;
|
||||
}
|
||||
if (decimals) { n *= 10; }
|
||||
n += 0.5; // Add rounding factor. Ensures carryover through entire value.
|
||||
|
||||
// Generate digits backwards and store in string.
|
||||
unsigned char buf[10];
|
||||
uint8_t i = 0;
|
||||
uint32_t a = (long)n;
|
||||
buf[settings.decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
|
||||
while(a > 0) {
|
||||
if (i == settings.decimal_places) { i++; } // Skip decimal point location
|
||||
buf[i++] = (a % 10) + '0'; // Get digit
|
||||
a /= 10;
|
||||
}
|
||||
while (i < settings.decimal_places) {
|
||||
buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1)
|
||||
}
|
||||
if (i == settings.decimal_places) { // Fill in leading zero, if needed.
|
||||
i++;
|
||||
buf[i++] = '0';
|
||||
}
|
||||
|
||||
// Print the generated string.
|
||||
for (; i > 0; i--)
|
||||
serial_write(buf[i-1]);
|
||||
}
|
||||
|
5
print.h
Normal file → Executable file
5
print.h
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -30,8 +31,8 @@ void printPgmString(const char *s);
|
||||
|
||||
void printInteger(long n);
|
||||
|
||||
void printIntegerInBase(unsigned long n, unsigned long base);
|
||||
void print_uint8_base2(uint8_t n);
|
||||
|
||||
void printFloat(double n);
|
||||
void printFloat(float n);
|
||||
|
||||
#endif
|
288
protocol.c
Normal file → Executable file
288
protocol.c
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,78 +20,292 @@
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "protocol.h"
|
||||
#include "gcode.h"
|
||||
#include "serial.h"
|
||||
#include "print.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include <math.h>
|
||||
#include "nuts_bolts.h"
|
||||
#include <avr/pgmspace.h>
|
||||
#define LINE_BUFFER_SIZE 50
|
||||
#include "stepper.h"
|
||||
#include "report.h"
|
||||
#include "motion_control.h"
|
||||
|
||||
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
|
||||
static uint8_t char_counter; // Last character counter in line variable.
|
||||
static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters.
|
||||
|
||||
static void status_message(int status_code)
|
||||
{
|
||||
if (status_code == 0) {
|
||||
printPgmString(PSTR("ok\r\n"));
|
||||
} else {
|
||||
printPgmString(PSTR("error: "));
|
||||
switch(status_code) {
|
||||
case STATUS_BAD_NUMBER_FORMAT:
|
||||
printPgmString(PSTR("Bad number format\r\n")); break;
|
||||
case STATUS_EXPECTED_COMMAND_LETTER:
|
||||
printPgmString(PSTR("Expected command letter\r\n")); break;
|
||||
case STATUS_UNSUPPORTED_STATEMENT:
|
||||
printPgmString(PSTR("Unsupported statement\r\n")); break;
|
||||
case STATUS_FLOATING_POINT_ERROR:
|
||||
printPgmString(PSTR("Floating point error\r\n")); break;
|
||||
default:
|
||||
printInteger(status_code);
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_init()
|
||||
{
|
||||
char_counter = 0; // Reset line input
|
||||
iscomment = false;
|
||||
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION));
|
||||
printPgmString(PSTR("\r\n"));
|
||||
report_init_message(); // Welcome message
|
||||
|
||||
PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins
|
||||
PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation.
|
||||
PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt
|
||||
PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt
|
||||
}
|
||||
|
||||
// Executes one line of input according to protocol
|
||||
// Executes user startup script, if stored.
|
||||
void protocol_execute_startup()
|
||||
{
|
||||
uint8_t n;
|
||||
for (n=0; n < N_STARTUP_LINE; n++) {
|
||||
if (!(settings_read_startup_line(n, line))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
} else {
|
||||
if (line[0] != 0) {
|
||||
printString(line); // Echo startup line to indicate execution.
|
||||
report_status_message(gc_execute_line(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
|
||||
// only the runtime command execute variable to have the main program execute these when
|
||||
// its ready. This works exactly like the character-based runtime commands when picked off
|
||||
// directly from the incoming serial data stream.
|
||||
ISR(PINOUT_INT_vect)
|
||||
{
|
||||
// Enter only if any pinout pin is actively low.
|
||||
if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) {
|
||||
if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) {
|
||||
mc_reset();
|
||||
} else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) {
|
||||
sys.execute |= EXEC_FEED_HOLD;
|
||||
} else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) {
|
||||
sys.execute |= EXEC_CYCLE_START;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Executes run-time commands, when required. This is called from various check points in the main
|
||||
// program, primarily where there may be a while loop waiting for a buffer to clear space or any
|
||||
// point where the execution time from the last check point may be more than a fraction of a second.
|
||||
// This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code
|
||||
// parsing and planning functions. This function also serves as an interface for the interrupts to
|
||||
// set the system runtime flags, where only the main program handles them, removing the need to
|
||||
// define more computationally-expensive volatile variables. This also provides a controlled way to
|
||||
// execute certain tasks without having two or more instances of the same task, such as the planner
|
||||
// recalculating the buffer upon a feedhold or override.
|
||||
// NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts,
|
||||
// limit switches, or the main program.
|
||||
void protocol_execute_runtime()
|
||||
{
|
||||
if (sys.execute) { // Enter only if any bit flag is true
|
||||
uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times
|
||||
|
||||
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
|
||||
// the source of the error to the user. If critical, Grbl disables by entering an infinite
|
||||
// loop until system reset/abort.
|
||||
if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) {
|
||||
sys.state = STATE_ALARM; // Set system alarm state
|
||||
|
||||
// Critical event. Only hard limit qualifies. Update this as new critical events surface.
|
||||
if (rt_exec & EXEC_CRIT_EVENT) {
|
||||
report_alarm_message(ALARM_HARD_LIMIT);
|
||||
report_feedback_message(MESSAGE_CRITICAL_EVENT);
|
||||
bit_false(sys.execute,EXEC_RESET); // Disable any existing reset
|
||||
do {
|
||||
// Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits
|
||||
// typically occur while unattended or not paying attention. Gives the user time
|
||||
// to do what is needed before resetting, like killing the incoming stream.
|
||||
} while (bit_isfalse(sys.execute,EXEC_RESET));
|
||||
|
||||
// Standard alarm event. Only abort during motion qualifies.
|
||||
} else {
|
||||
// Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the
|
||||
// user that position may have been lost and set alarm state to enable the alarm lockout
|
||||
// to indicate the possible severity of the problem.
|
||||
report_alarm_message(ALARM_ABORT_CYCLE);
|
||||
}
|
||||
bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT));
|
||||
}
|
||||
|
||||
// Execute system abort.
|
||||
if (rt_exec & EXEC_RESET) {
|
||||
sys.abort = true; // Only place this is set true.
|
||||
return; // Nothing else to do but exit.
|
||||
}
|
||||
|
||||
// Execute and serial print status
|
||||
if (rt_exec & EXEC_STATUS_REPORT) {
|
||||
report_realtime_status();
|
||||
bit_false(sys.execute,EXEC_STATUS_REPORT);
|
||||
}
|
||||
|
||||
// Initiate stepper feed hold
|
||||
if (rt_exec & EXEC_FEED_HOLD) {
|
||||
st_feed_hold(); // Initiate feed hold.
|
||||
bit_false(sys.execute,EXEC_FEED_HOLD);
|
||||
}
|
||||
|
||||
// Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer.
|
||||
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
|
||||
if (rt_exec & EXEC_CYCLE_STOP) {
|
||||
st_cycle_reinitialize();
|
||||
bit_false(sys.execute,EXEC_CYCLE_STOP);
|
||||
}
|
||||
|
||||
if (rt_exec & EXEC_CYCLE_START) {
|
||||
st_cycle_start(); // Issue cycle start command to stepper subsystem
|
||||
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) {
|
||||
sys.auto_start = true; // Re-enable auto start after feed hold.
|
||||
}
|
||||
bit_false(sys.execute,EXEC_CYCLE_START);
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides flag byte (sys.override) and execution should be installed here, since they
|
||||
// are runtime and require a direct and controlled interface to the main stepper program.
|
||||
}
|
||||
|
||||
|
||||
// Directs and executes one line of formatted input from protocol_process. While mostly
|
||||
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
|
||||
// settings, initiating the homing cycle, and toggling switch states. This differs from
|
||||
// the runtime command module by being susceptible to when Grbl is ready to execute the
|
||||
// next line during a cycle, so for switches like block delete, the switch only effects
|
||||
// the lines that are processed afterward, not necessarily real-time during a cycle,
|
||||
// since there are motions already stored in the buffer. However, this 'lag' should not
|
||||
// be an issue, since these commands are not typically used during a cycle.
|
||||
uint8_t protocol_execute_line(char *line)
|
||||
{
|
||||
// Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help
|
||||
if(line[0] == '$') {
|
||||
return(settings_execute_line(line)); // Delegate lines starting with '$' to the settings module
|
||||
|
||||
uint8_t char_counter = 1;
|
||||
uint8_t helper_var = 0; // Helper variable
|
||||
float parameter, value;
|
||||
switch( line[char_counter] ) {
|
||||
case 0 : report_grbl_help(); break;
|
||||
case '$' : // Prints Grbl settings
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
else { report_grbl_settings(); }
|
||||
break;
|
||||
case '#' : // Print gcode parameters
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
else { report_gcode_parameters(); }
|
||||
break;
|
||||
case 'G' : // Prints gcode parser state
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
else { report_gcode_modes(); }
|
||||
break;
|
||||
case 'C' : // Set check g-code mode
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
// Perform reset when toggling off. Check g-code mode should only work if Grbl
|
||||
// is idle and ready, regardless of alarm locks. This is mainly to keep things
|
||||
// simple and consistent.
|
||||
if ( sys.state == STATE_CHECK_MODE ) {
|
||||
mc_reset();
|
||||
report_feedback_message(MESSAGE_DISABLED);
|
||||
} else {
|
||||
if (sys.state) { return(STATUS_IDLE_ERROR); }
|
||||
sys.state = STATE_CHECK_MODE;
|
||||
report_feedback_message(MESSAGE_ENABLED);
|
||||
}
|
||||
break;
|
||||
case 'X' : // Disable alarm lock
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
if (sys.state == STATE_ALARM) {
|
||||
report_feedback_message(MESSAGE_ALARM_UNLOCK);
|
||||
sys.state = STATE_IDLE;
|
||||
// Don't run startup script. Prevents stored moves in startup from causing accidents.
|
||||
}
|
||||
break;
|
||||
case 'H' : // Perform homing cycle
|
||||
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
|
||||
// Only perform homing if Grbl is idle or lost.
|
||||
if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) {
|
||||
mc_go_home();
|
||||
if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing.
|
||||
} else { return(STATUS_IDLE_ERROR); }
|
||||
} else { return(STATUS_SETTING_DISABLED); }
|
||||
break;
|
||||
// case 'J' : break; // Jogging methods
|
||||
// TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
|
||||
// susceptible to other runtime commands except for e-stop. The jogging function is intended to
|
||||
// be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
|
||||
// steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
|
||||
// start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
|
||||
// motion by repeatedly toggling to slow the motion to the desired location. Location data would
|
||||
// need to be updated real-time and supplied to the user through status queries.
|
||||
// More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
|
||||
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the
|
||||
// block buffer without having the planner plan them. It would need to manage de/ac-celerations
|
||||
// on its own carefully. This approach could be effective and possibly size/memory efficient.
|
||||
case 'N' : // Startup lines.
|
||||
if ( line[++char_counter] == 0 ) { // Print startup lines
|
||||
for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
|
||||
if (!(settings_read_startup_line(helper_var, line))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
} else {
|
||||
report_startup_line(helper_var,line);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else { // Store startup line
|
||||
helper_var = true; // Set helper_var to flag storing method.
|
||||
// No break. Continues into default: to read remaining command characters.
|
||||
}
|
||||
default : // Storing setting methods
|
||||
if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
if (helper_var) { // Store startup line
|
||||
// Prepare sending gcode block to gcode parser by shifting all characters
|
||||
helper_var = char_counter; // Set helper variable as counter to start of gcode block
|
||||
do {
|
||||
line[char_counter-helper_var] = line[char_counter];
|
||||
} while (line[char_counter++] != 0);
|
||||
// Execute gcode block to ensure block is valid.
|
||||
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
|
||||
if (helper_var) { return(helper_var); }
|
||||
else {
|
||||
helper_var = trunc(parameter); // Set helper_var to int value of parameter
|
||||
settings_store_startup_line(helper_var,line);
|
||||
}
|
||||
} else { // Store global setting.
|
||||
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
return(settings_store_global_setting(parameter, value));
|
||||
}
|
||||
}
|
||||
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
|
||||
|
||||
} else {
|
||||
return(gc_execute_line(line)); // Everything else is gcode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process one line of incoming serial data. Remove unneeded characters and capitalize.
|
||||
// Process and report status one line of incoming serial data. Performs an initial filtering
|
||||
// by removing spaces and comments and capitalizing all letters.
|
||||
void protocol_process()
|
||||
{
|
||||
char c;
|
||||
while((c = serial_read()) != SERIAL_NO_DATA)
|
||||
{
|
||||
uint8_t c;
|
||||
while((c = serial_read()) != SERIAL_NO_DATA) {
|
||||
if ((c == '\n') || (c == '\r')) { // End of line reached
|
||||
|
||||
// Runtime command check point before executing line. Prevent any furthur line executions.
|
||||
// NOTE: If there is no line, this function should quickly return to the main program when
|
||||
// the buffer empties of non-executable data.
|
||||
protocol_execute_runtime();
|
||||
if (sys.abort) { return; } // Bail to main program upon system abort
|
||||
|
||||
if (char_counter > 0) {// Line is complete. Then execute!
|
||||
line[char_counter] = 0; // Terminate string
|
||||
status_message(protocol_execute_line(line));
|
||||
report_status_message(protocol_execute_line(line));
|
||||
} else {
|
||||
// Empty or comment line. Skip block.
|
||||
status_message(STATUS_OK); // Send status message for syncing purposes.
|
||||
report_status_message(STATUS_OK); // Send status message for syncing purposes.
|
||||
}
|
||||
char_counter = 0; // Reset line buffer index
|
||||
iscomment = false; // Reset comment flag
|
||||
|
||||
} else {
|
||||
if (iscomment) {
|
||||
// Throw away all comment characters
|
||||
@ -103,9 +317,7 @@ void protocol_process()
|
||||
if (c <= ' ') {
|
||||
// Throw away whitepace and control characters
|
||||
} else if (c == '/') {
|
||||
// Disable block delete and throw away character
|
||||
// To enable block delete, uncomment following line. Will ignore until EOL.
|
||||
// iscomment = true;
|
||||
// Block delete not supported. Ignore character.
|
||||
} else if (c == '(') {
|
||||
// Enable comments flag and ignore all characters until ')' or EOL.
|
||||
iscomment = true;
|
||||
|
24
protocol.h
Normal file → Executable file
24
protocol.h
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -21,11 +21,17 @@
|
||||
#ifndef protocol_h
|
||||
#define protocol_h
|
||||
|
||||
#define STATUS_OK 0
|
||||
#define STATUS_BAD_NUMBER_FORMAT 1
|
||||
#define STATUS_EXPECTED_COMMAND_LETTER 2
|
||||
#define STATUS_UNSUPPORTED_STATEMENT 3
|
||||
#define STATUS_FLOATING_POINT_ERROR 4
|
||||
#include <avr/sleep.h>
|
||||
|
||||
// Line buffer size from the serial input stream to be executed.
|
||||
// NOTE: Not a problem except for extreme cases, but the line buffer size can be too small
|
||||
// and g-code blocks can get truncated. Officially, the g-code standards support up to 256
|
||||
// characters. In future versions, this will be increased, when we know how much extra
|
||||
// memory space we can invest into here or we re-write the g-code parser not to have his
|
||||
// buffer.
|
||||
#ifndef LINE_BUFFER_SIZE
|
||||
#define LINE_BUFFER_SIZE 50
|
||||
#endif
|
||||
|
||||
// Initialize the serial protocol
|
||||
void protocol_init();
|
||||
@ -37,4 +43,10 @@ void protocol_process();
|
||||
// Executes one line of input according to protocol
|
||||
uint8_t protocol_execute_line(char *line);
|
||||
|
||||
// Checks and executes a runtime command at various stop points in main program
|
||||
void protocol_execute_runtime();
|
||||
|
||||
// Execute the startup script lines stored in EEPROM upon initialization
|
||||
void protocol_execute_startup();
|
||||
|
||||
#endif
|
||||
|
34
readme.textile
Normal file → Executable file
34
readme.textile
Normal file → Executable file
@ -6,17 +6,31 @@ The controller is written in highly optimized C utilizing every clever feature o
|
||||
|
||||
It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Functions and variables are not currently supported, but may be included in future releases in a form of a pre-processor.
|
||||
|
||||
Grbl includes full acceleration management with look ahead. That means the controller will look up to 16 to 20 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering.
|
||||
Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering.
|
||||
|
||||
*Changelog for v0.8 from v0.7:*
|
||||
- Major structural overhaul to allow for multi-tasking events and new feature sets.
|
||||
- Run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting (current position and state).
|
||||
- Controlled feed hold with deceleration to ensure no skipped steps and loss of location.
|
||||
- After feed hold, cycle accelerations are re-planned and may be resumed.
|
||||
- Advanced homing cycle with direction and speed configuration options. (Requires limit switches.) When enabled, homing is required before use to ensure safety.
|
||||
- Limit pins are held normal high with internal pull-up resistors. Wiring only requires a normally-open switch connected to ground. (For both ends of an axis, simply wire two in parallel into the same pin.)
|
||||
- Hard limits option and plays nice with homing cycle, so switches can be used for both homing and hard limits.
|
||||
- A check g-code mode has also been added to allow users to error check their programs.
|
||||
- Re-factored g-code parser with robust error-checking.
|
||||
- 6 work coordinate systems (G54-G59), offsets(G92), and machine coordinate system support. Work coordinate systems are stored in EEPROM and persistent.
|
||||
- G10 L2 and L20 work coordinate settings support. L2 sets one or more axes values. L20 sets the current machine position to the specified work origin.
|
||||
- G28.1 and G30.1 set home position support. These set the internal EEPROM parameter values to the current machine position. (G28 and G30 no longer perform homing cycle, '$H' does. They move to these stored positions.)
|
||||
- Program stop(M0,M2,M30) support.
|
||||
- Coolant control(M7*,M8,M9) support. (M7 is a compile-time option).
|
||||
- G-code parser state and '#' parameters feedback.
|
||||
- System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates.
|
||||
- Settings overhauled and dozens of new settings and internal commands are now available, when most were compile-time only.
|
||||
- New startup line setting. Allows users to store a custom g-code block into Grbl's startup routine. Executes immediately upon startup or reset. May be used to set g-code defaults like G20/G21.
|
||||
- Pin-outs of the cycle-start, feed-hold, and soft-reset runtime commands on pins A0-A2.
|
||||
- Misc bug fixes and removed deprecated acceleration enabled code.
|
||||
- Advanced compile-time options: XON/XOFF flow control (limited support), direction and step pulse time delay, up to 5 startup lines, and homing sequence configurability.
|
||||
|
||||
*Changelog for v0.7 from v0.6:*
|
||||
- Significantly improved and optimized planner re-factoring.
|
||||
- New robust cornering algorithm, enabling smoother and faster motions.
|
||||
- Arc acceleration planning enabled by efficient vector transformation implementation.
|
||||
- Stepper subsystem re-factoring to help remove some motion issues from pre-v0.7 builds.
|
||||
- Increased dwell times.
|
||||
- G92 coordinate system offset support.
|
||||
- (Beta) Limit switch and homing cycle support.
|
||||
- Many other bug fixes and efficiency improvements.
|
||||
|
||||
*Important note for Atmega 168 users:* Going forward, support for Atmega 168 will be dropped due to its limited memory and speed. However, legacy Grbl v0.51 "in the branch called 'v0_51' is still available for use.
|
||||
|
||||
|
330
report.c
Normal file
330
report.c
Normal file
@ -0,0 +1,330 @@
|
||||
/*
|
||||
report.c - reporting and messaging methods
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file functions as the primary feedback interface for Grbl. Any outgoing data, such
|
||||
as the protocol status messages, feedback messages, and status reports, are stored here.
|
||||
For the most part, these functions primarily are called from protocol.c methods. If a
|
||||
different style feedback is desired (i.e. JSON), then a user can change these following
|
||||
methods to accomodate their needs.
|
||||
*/
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include "report.h"
|
||||
#include "print.h"
|
||||
#include "settings.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "gcode.h"
|
||||
#include "coolant_control.h"
|
||||
|
||||
|
||||
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
|
||||
// For every incoming line, this method responds with an 'ok' for a successful command or an
|
||||
// 'error:' to indicate some error event with the line or some critical system error during
|
||||
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
|
||||
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
|
||||
// responses.
|
||||
// NOTE: In silent mode, all error codes are greater than zero.
|
||||
// TODO: Install silent mode to return only numeric values, primarily for GUIs.
|
||||
void report_status_message(uint8_t status_code)
|
||||
{
|
||||
if (status_code == 0) { // STATUS_OK
|
||||
printPgmString(PSTR("ok\r\n"));
|
||||
} else {
|
||||
printPgmString(PSTR("error: "));
|
||||
switch(status_code) {
|
||||
case STATUS_BAD_NUMBER_FORMAT:
|
||||
printPgmString(PSTR("Bad number format")); break;
|
||||
case STATUS_EXPECTED_COMMAND_LETTER:
|
||||
printPgmString(PSTR("Expected command letter")); break;
|
||||
case STATUS_UNSUPPORTED_STATEMENT:
|
||||
printPgmString(PSTR("Unsupported statement")); break;
|
||||
case STATUS_ARC_RADIUS_ERROR:
|
||||
printPgmString(PSTR("Invalid radius")); break;
|
||||
case STATUS_MODAL_GROUP_VIOLATION:
|
||||
printPgmString(PSTR("Modal group violation")); break;
|
||||
case STATUS_INVALID_STATEMENT:
|
||||
printPgmString(PSTR("Invalid statement")); break;
|
||||
case STATUS_SETTING_DISABLED:
|
||||
printPgmString(PSTR("Setting disabled")); break;
|
||||
case STATUS_SETTING_VALUE_NEG:
|
||||
printPgmString(PSTR("Value < 0.0")); break;
|
||||
case STATUS_SETTING_STEP_PULSE_MIN:
|
||||
printPgmString(PSTR("Value < 3 usec")); break;
|
||||
case STATUS_SETTING_READ_FAIL:
|
||||
printPgmString(PSTR("EEPROM read fail. Using defaults")); break;
|
||||
case STATUS_IDLE_ERROR:
|
||||
printPgmString(PSTR("Busy or queued")); break;
|
||||
case STATUS_ALARM_LOCK:
|
||||
printPgmString(PSTR("Alarm lock")); break;
|
||||
}
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Prints alarm messages.
|
||||
void report_alarm_message(int8_t alarm_code)
|
||||
{
|
||||
printPgmString(PSTR("ALARM: "));
|
||||
switch (alarm_code) {
|
||||
case ALARM_HARD_LIMIT:
|
||||
printPgmString(PSTR("Hard limit")); break;
|
||||
case ALARM_ABORT_CYCLE:
|
||||
printPgmString(PSTR("Abort during cycle")); break;
|
||||
}
|
||||
printPgmString(PSTR(". MPos?\r\n"));
|
||||
}
|
||||
|
||||
// Prints feedback messages. This serves as a centralized method to provide additional
|
||||
// user feedback for things that are not of the status/alarm message protocol. These are
|
||||
// messages such as setup warnings, switch toggling, and how to exit alarms.
|
||||
// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
|
||||
// is installed, the message number codes are less than zero.
|
||||
// TODO: Install silence feedback messages option in settings
|
||||
void report_feedback_message(uint8_t message_code)
|
||||
{
|
||||
printPgmString(PSTR("["));
|
||||
switch(message_code) {
|
||||
case MESSAGE_CRITICAL_EVENT:
|
||||
printPgmString(PSTR("Reset to continue")); break;
|
||||
case MESSAGE_ALARM_LOCK:
|
||||
printPgmString(PSTR("'$H'|'$X' to unlock")); break;
|
||||
case MESSAGE_ALARM_UNLOCK:
|
||||
printPgmString(PSTR("Caution: Unlocked")); break;
|
||||
case MESSAGE_ENABLED:
|
||||
printPgmString(PSTR("Enabled")); break;
|
||||
case MESSAGE_DISABLED:
|
||||
printPgmString(PSTR("Disabled")); break;
|
||||
}
|
||||
printPgmString(PSTR("]\r\n"));
|
||||
}
|
||||
|
||||
|
||||
// Welcome message
|
||||
void report_init_message()
|
||||
{
|
||||
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n"));
|
||||
}
|
||||
|
||||
// Grbl help message
|
||||
void report_grbl_help() {
|
||||
printPgmString(PSTR("$$ (view Grbl settings)\r\n"
|
||||
"$# (view # parameters)\r\n"
|
||||
"$G (view parser state)\r\n"
|
||||
"$N (view startup blocks)\r\n"
|
||||
"$x=value (save Grbl setting)\r\n"
|
||||
"$Nx=line (save startup block)\r\n"
|
||||
"$C (check gcode mode)\r\n"
|
||||
"$X (kill alarm lock)\r\n"
|
||||
"$H (run homing cycle)\r\n"
|
||||
"~ (cycle start)\r\n"
|
||||
"! (feed hold)\r\n"
|
||||
"? (current status)\r\n"
|
||||
"ctrl-x (reset Grbl)\r\n"));
|
||||
}
|
||||
|
||||
// Grbl global settings print out.
|
||||
// NOTE: The numbering scheme here must correlate to storing in settings.c
|
||||
void report_grbl_settings() {
|
||||
printPgmString(PSTR("$0=")); printFloat(settings.steps_per_mm[X_AXIS]);
|
||||
printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]);
|
||||
printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]);
|
||||
printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printInteger(settings.pulse_microseconds);
|
||||
printPgmString(PSTR(" (step pulse, usec)\r\n$4=")); printFloat(settings.default_feed_rate);
|
||||
printPgmString(PSTR(" (default feed, mm/min)\r\n$5=")); printFloat(settings.default_seek_rate);
|
||||
printPgmString(PSTR(" (default seek, mm/min)\r\n$6=")); printInteger(settings.invert_mask);
|
||||
printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.invert_mask);
|
||||
printPgmString(PSTR(")\r\n$7=")); printInteger(settings.stepper_idle_lock_time);
|
||||
printPgmString(PSTR(" (step idle delay, msec)\r\n$8=")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability
|
||||
printPgmString(PSTR(" (acceleration, mm/sec^2)\r\n$9=")); printFloat(settings.junction_deviation);
|
||||
printPgmString(PSTR(" (junction deviation, mm)\r\n$10=")); printFloat(settings.mm_per_arc_segment);
|
||||
printPgmString(PSTR(" (arc, mm/segment)\r\n$11=")); printInteger(settings.n_arc_correction);
|
||||
printPgmString(PSTR(" (n-arc correction, int)\r\n$12=")); printInteger(settings.decimal_places);
|
||||
printPgmString(PSTR(" (n-decimals, int)\r\n$13=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
|
||||
printPgmString(PSTR(" (report inches, bool)\r\n$14=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START));
|
||||
printPgmString(PSTR(" (auto start, bool)\r\n$15=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
|
||||
printPgmString(PSTR(" (invert step enable, bool)\r\n$16=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (hard limits, bool)\r\n$17=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
|
||||
printPgmString(PSTR(" (homing cycle, bool)\r\n$18=")); printInteger(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(")\r\n$19=")); printFloat(settings.homing_feed_rate);
|
||||
printPgmString(PSTR(" (homing feed, mm/min)\r\n$20=")); printFloat(settings.homing_seek_rate);
|
||||
printPgmString(PSTR(" (homing seek, mm/min)\r\n$21=")); printInteger(settings.homing_debounce_delay);
|
||||
printPgmString(PSTR(" (homing debounce, msec)\r\n$22=")); printFloat(settings.homing_pulloff);
|
||||
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
|
||||
}
|
||||
|
||||
|
||||
// Prints gcode coordinate offset parameters
|
||||
void report_gcode_parameters()
|
||||
{
|
||||
float coord_data[N_AXIS];
|
||||
uint8_t coord_select, i;
|
||||
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
|
||||
if (!(settings_read_coord_data(coord_select,coord_data))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
return;
|
||||
}
|
||||
printPgmString(PSTR("[G"));
|
||||
switch (coord_select) {
|
||||
case 0: printPgmString(PSTR("54:")); break;
|
||||
case 1: printPgmString(PSTR("55:")); break;
|
||||
case 2: printPgmString(PSTR("56:")); break;
|
||||
case 3: printPgmString(PSTR("57:")); break;
|
||||
case 4: printPgmString(PSTR("58:")); break;
|
||||
case 5: printPgmString(PSTR("59:")); break;
|
||||
case 6: printPgmString(PSTR("28:")); break;
|
||||
case 7: printPgmString(PSTR("30:")); break;
|
||||
// case 8: printPgmString(PSTR("92:")); break; // G92.2, G92.3 not supported. Hence not stored.
|
||||
}
|
||||
for (i=0; i<N_AXIS; i++) {
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(coord_data[i]*INCH_PER_MM); }
|
||||
else { printFloat(coord_data[i]); }
|
||||
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
|
||||
else { printPgmString(PSTR("]\r\n")); }
|
||||
}
|
||||
}
|
||||
printPgmString(PSTR("[G92:")); // Print G92,G92.1 which are not persistent in memory
|
||||
for (i=0; i<N_AXIS; i++) {
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(gc.coord_offset[i]*INCH_PER_MM); }
|
||||
else { printFloat(gc.coord_offset[i]); }
|
||||
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
|
||||
else { printPgmString(PSTR("]\r\n")); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Print current gcode parser mode state
|
||||
void report_gcode_modes()
|
||||
{
|
||||
switch (gc.motion_mode) {
|
||||
case MOTION_MODE_SEEK : printPgmString(PSTR("[G0")); break;
|
||||
case MOTION_MODE_LINEAR : printPgmString(PSTR("[G1")); break;
|
||||
case MOTION_MODE_CW_ARC : printPgmString(PSTR("[G2")); break;
|
||||
case MOTION_MODE_CCW_ARC : printPgmString(PSTR("[G3")); break;
|
||||
case MOTION_MODE_CANCEL : printPgmString(PSTR("[G80")); break;
|
||||
}
|
||||
|
||||
printPgmString(PSTR(" G"));
|
||||
printInteger(gc.coord_select+54);
|
||||
|
||||
if (gc.plane_axis_0 == X_AXIS) {
|
||||
if (gc.plane_axis_1 == Y_AXIS) { printPgmString(PSTR(" G17")); }
|
||||
else { printPgmString(PSTR(" G18")); }
|
||||
} else { printPgmString(PSTR(" G19")); }
|
||||
|
||||
if (gc.inches_mode) { printPgmString(PSTR(" G20")); }
|
||||
else { printPgmString(PSTR(" G21")); }
|
||||
|
||||
if (gc.absolute_mode) { printPgmString(PSTR(" G90")); }
|
||||
else { printPgmString(PSTR(" G91")); }
|
||||
|
||||
if (gc.inverse_feed_rate_mode) { printPgmString(PSTR(" G93")); }
|
||||
else { printPgmString(PSTR(" G94")); }
|
||||
|
||||
switch (gc.program_flow) {
|
||||
case PROGRAM_FLOW_RUNNING : printPgmString(PSTR(" M0")); break;
|
||||
case PROGRAM_FLOW_PAUSED : printPgmString(PSTR(" M1")); break;
|
||||
case PROGRAM_FLOW_COMPLETED : printPgmString(PSTR(" M2")); break;
|
||||
}
|
||||
|
||||
switch (gc.spindle_direction) {
|
||||
case 1 : printPgmString(PSTR(" M3")); break;
|
||||
case -1 : printPgmString(PSTR(" M4")); break;
|
||||
case 0 : printPgmString(PSTR(" M5")); break;
|
||||
}
|
||||
|
||||
switch (gc.coolant_mode) {
|
||||
case COOLANT_DISABLE : printPgmString(PSTR(" M9")); break;
|
||||
case COOLANT_FLOOD_ENABLE : printPgmString(PSTR(" M8")); break;
|
||||
#ifdef ENABLE_M7
|
||||
case COOLANT_MIST_ENABLE : printPgmString(PSTR(" M7")); break;
|
||||
#endif
|
||||
}
|
||||
|
||||
printPgmString(PSTR(" T"));
|
||||
printInteger(gc.tool);
|
||||
|
||||
printPgmString(PSTR(" F"));
|
||||
if (gc.inches_mode) { printFloat(gc.feed_rate*INCH_PER_MM); }
|
||||
else { printFloat(gc.feed_rate); }
|
||||
|
||||
printPgmString(PSTR("]\r\n"));
|
||||
}
|
||||
|
||||
// Prints specified startup line
|
||||
void report_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
printPgmString(PSTR("$N")); printInteger(n);
|
||||
printPgmString(PSTR("=")); printString(line);
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
|
||||
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
|
||||
// and the actual location of the CNC machine. Users may change the following function to their
|
||||
// specific needs, but the desired real-time data report must be as short as possible. This is
|
||||
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
|
||||
// especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
|
||||
void report_realtime_status()
|
||||
{
|
||||
// **Under construction** Bare-bones status report. Provides real-time machine position relative to
|
||||
// the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually
|
||||
// to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask
|
||||
// for a user to select the desired real-time data.
|
||||
uint8_t i;
|
||||
int32_t current_position[3]; // Copy current state of the system position variable
|
||||
memcpy(current_position,sys.position,sizeof(sys.position));
|
||||
float print_position[3];
|
||||
|
||||
// Report current machine state
|
||||
switch (sys.state) {
|
||||
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
|
||||
// case STATE_INIT: printPgmString(PSTR("[Init")); break; // Never observed
|
||||
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
|
||||
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
|
||||
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
|
||||
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
|
||||
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
|
||||
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
|
||||
}
|
||||
|
||||
// Report machine position
|
||||
printPgmString(PSTR(",MPos:"));
|
||||
for (i=0; i<= 2; i++) {
|
||||
print_position[i] = current_position[i]/settings.steps_per_mm[i];
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; }
|
||||
printFloat(print_position[i]);
|
||||
printPgmString(PSTR(","));
|
||||
}
|
||||
|
||||
// Report work position
|
||||
printPgmString(PSTR("WPos:"));
|
||||
for (i=0; i<= 2; i++) {
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
|
||||
print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM;
|
||||
} else {
|
||||
print_position[i] -= gc.coord_system[i]+gc.coord_offset[i];
|
||||
}
|
||||
printFloat(print_position[i]);
|
||||
if (i < 2) { printPgmString(PSTR(",")); }
|
||||
}
|
||||
|
||||
printPgmString(PSTR(">\r\n"));
|
||||
}
|
80
report.h
Normal file
80
report.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
report.h - reporting and messaging methods
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef report_h
|
||||
#define report_h
|
||||
|
||||
|
||||
// Define Grbl status codes.
|
||||
#define STATUS_OK 0
|
||||
#define STATUS_BAD_NUMBER_FORMAT 1
|
||||
#define STATUS_EXPECTED_COMMAND_LETTER 2
|
||||
#define STATUS_UNSUPPORTED_STATEMENT 3
|
||||
#define STATUS_ARC_RADIUS_ERROR 4
|
||||
#define STATUS_MODAL_GROUP_VIOLATION 5
|
||||
#define STATUS_INVALID_STATEMENT 6
|
||||
#define STATUS_SETTING_DISABLED 7
|
||||
#define STATUS_SETTING_VALUE_NEG 8
|
||||
#define STATUS_SETTING_STEP_PULSE_MIN 9
|
||||
#define STATUS_SETTING_READ_FAIL 10
|
||||
#define STATUS_IDLE_ERROR 11
|
||||
#define STATUS_ALARM_LOCK 12
|
||||
|
||||
// Define Grbl alarm codes. Less than zero to distinguish alarm error from status error.
|
||||
#define ALARM_HARD_LIMIT -1
|
||||
#define ALARM_ABORT_CYCLE -2
|
||||
|
||||
// Define Grbl feedback message codes.
|
||||
#define MESSAGE_CRITICAL_EVENT 1
|
||||
#define MESSAGE_ALARM_LOCK 2
|
||||
#define MESSAGE_ALARM_UNLOCK 3
|
||||
#define MESSAGE_ENABLED 4
|
||||
#define MESSAGE_DISABLED 5
|
||||
|
||||
// Prints system status messages.
|
||||
void report_status_message(uint8_t status_code);
|
||||
|
||||
// Prints system alarm messages.
|
||||
void report_alarm_message(int8_t alarm_code);
|
||||
|
||||
// Prints miscellaneous feedback messages.
|
||||
void report_feedback_message(uint8_t message_code);
|
||||
|
||||
// Prints welcome message
|
||||
void report_init_message();
|
||||
|
||||
// Prints Grbl help and current global settings
|
||||
void report_grbl_help();
|
||||
|
||||
// Prints Grbl global settings
|
||||
void report_grbl_settings();
|
||||
|
||||
// Prints realtime status report
|
||||
void report_realtime_status();
|
||||
|
||||
// Prints Grbl persistent coordinate parameters
|
||||
void report_gcode_parameters();
|
||||
|
||||
// Prints current g-code parser mode state
|
||||
void report_gcode_modes();
|
||||
|
||||
// Prints startup line
|
||||
void report_startup_line(uint8_t n, char *line);
|
||||
|
||||
#endif
|
0
script/Obsolete/stream.rb
Normal file → Executable file
0
script/Obsolete/stream.rb
Normal file → Executable file
0
script/Obsolete/trapezoid_simulator.rb
Normal file → Executable file
0
script/Obsolete/trapezoid_simulator.rb
Normal file → Executable file
137
serial.c
Normal file → Executable file
137
serial.c
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,17 +23,10 @@
|
||||
used to be a part of the Arduino project. */
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
#include "serial.h"
|
||||
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#define RX_BUFFER_SIZE 256
|
||||
#else
|
||||
#define RX_BUFFER_SIZE 64
|
||||
#endif
|
||||
|
||||
#define TX_BUFFER_SIZE 16
|
||||
#include "config.h"
|
||||
#include "motion_control.h"
|
||||
#include "protocol.h"
|
||||
|
||||
uint8_t rx_buffer[RX_BUFFER_SIZE];
|
||||
uint8_t rx_buffer_head = 0;
|
||||
@ -42,18 +36,31 @@ uint8_t tx_buffer[TX_BUFFER_SIZE];
|
||||
uint8_t tx_buffer_head = 0;
|
||||
volatile uint8_t tx_buffer_tail = 0;
|
||||
|
||||
static void set_baud_rate(long baud) {
|
||||
uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1);
|
||||
#ifdef ENABLE_XONXOFF
|
||||
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
|
||||
|
||||
// Returns the number of bytes in the RX buffer. This replaces a typical byte counter to prevent
|
||||
// the interrupt and main programs from writing to the counter at the same time.
|
||||
static uint8_t get_rx_buffer_count()
|
||||
{
|
||||
if (rx_buffer_head == rx_buffer_tail) { return(0); }
|
||||
if (rx_buffer_head < rx_buffer_tail) { return(rx_buffer_tail-rx_buffer_head); }
|
||||
return (RX_BUFFER_SIZE - (rx_buffer_head-rx_buffer_tail));
|
||||
}
|
||||
#endif
|
||||
|
||||
void serial_init()
|
||||
{
|
||||
// Set baud rate
|
||||
#if BAUD_RATE < 57600
|
||||
uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ;
|
||||
UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX
|
||||
#else
|
||||
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
|
||||
UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200
|
||||
#endif
|
||||
UBRR0H = UBRR0_value >> 8;
|
||||
UBRR0L = UBRR0_value;
|
||||
}
|
||||
|
||||
void serial_init(long baud)
|
||||
{
|
||||
set_baud_rate(baud);
|
||||
|
||||
/* baud doubler off - Only needed on Uno XXX */
|
||||
UCSR0A &= ~(1 << U2X0);
|
||||
|
||||
// enable rx and tx
|
||||
UCSR0B |= 1<<RXEN0;
|
||||
@ -70,8 +77,10 @@ void serial_write(uint8_t data) {
|
||||
uint8_t next_head = tx_buffer_head + 1;
|
||||
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
|
||||
|
||||
// Wait until there's a space in the buffer
|
||||
while (next_head == tx_buffer_tail) { };//sleep_mode(); };
|
||||
// Wait until there is space in the buffer
|
||||
while (next_head == tx_buffer_tail) {
|
||||
if (sys.execute & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
|
||||
}
|
||||
|
||||
// Store data and advance head
|
||||
tx_buffer[tx_buffer_head] = data;
|
||||
@ -82,21 +91,37 @@ void serial_write(uint8_t data) {
|
||||
}
|
||||
|
||||
// Data Register Empty Interrupt handler
|
||||
ISR(USART_UDRE_vect) {
|
||||
#ifdef __AVR_ATmega644P__
|
||||
ISR(USART0_UDRE_vect)
|
||||
#else
|
||||
ISR(USART_UDRE_vect)
|
||||
#endif
|
||||
{
|
||||
// Temporary tx_buffer_tail (to optimize for volatile)
|
||||
uint8_t tail = tx_buffer_tail;
|
||||
|
||||
// Send a byte from the buffer
|
||||
UDR0 = tx_buffer[tail];
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if (flow_ctrl == SEND_XOFF) {
|
||||
UDR0 = XOFF_CHAR;
|
||||
flow_ctrl = XOFF_SENT;
|
||||
} else if (flow_ctrl == SEND_XON) {
|
||||
UDR0 = XON_CHAR;
|
||||
flow_ctrl = XON_SENT;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Send a byte from the buffer
|
||||
UDR0 = tx_buffer[tail];
|
||||
|
||||
// Update tail position
|
||||
tail ++;
|
||||
if (tail == TX_BUFFER_SIZE) { tail = 0; }
|
||||
// Update tail position
|
||||
tail++;
|
||||
if (tail == TX_BUFFER_SIZE) { tail = 0; }
|
||||
|
||||
tx_buffer_tail = tail;
|
||||
}
|
||||
|
||||
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
|
||||
if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
|
||||
|
||||
tx_buffer_tail = tail;
|
||||
}
|
||||
|
||||
uint8_t serial_read()
|
||||
@ -107,19 +132,59 @@ uint8_t serial_read()
|
||||
uint8_t data = rx_buffer[rx_buffer_tail];
|
||||
rx_buffer_tail++;
|
||||
if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; }
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
|
||||
flow_ctrl = SEND_XON;
|
||||
UCSR0B |= (1 << UDRIE0); // Force TX
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __AVR_ATmega644P__
|
||||
ISR(USART0_RX_vect)
|
||||
#else
|
||||
ISR(USART_RX_vect)
|
||||
#endif
|
||||
{
|
||||
uint8_t data = UDR0;
|
||||
uint8_t next_head = rx_buffer_head + 1;
|
||||
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
|
||||
uint8_t next_head;
|
||||
|
||||
// Write data to buffer unless it is full.
|
||||
if (next_head != rx_buffer_tail) {
|
||||
rx_buffer[rx_buffer_head] = data;
|
||||
rx_buffer_head = next_head;
|
||||
// Pick off runtime command characters directly from the serial stream. These characters are
|
||||
// not passed into the buffer, but these set system state flag bits for runtime execution.
|
||||
switch (data) {
|
||||
case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
|
||||
case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true
|
||||
case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true
|
||||
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
|
||||
default: // Write character to buffer
|
||||
next_head = rx_buffer_head + 1;
|
||||
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
|
||||
|
||||
// Write data to buffer unless it is full.
|
||||
if (next_head != rx_buffer_tail) {
|
||||
rx_buffer[rx_buffer_head] = data;
|
||||
rx_buffer_head = next_head;
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
|
||||
flow_ctrl = SEND_XOFF;
|
||||
UCSR0B |= (1 << UDRIE0); // Force TX
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serial_reset_read_buffer()
|
||||
{
|
||||
rx_buffer_tail = rx_buffer_head;
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
flow_ctrl = XON_SENT;
|
||||
#endif
|
||||
}
|
||||
|
26
serial.h
Normal file → Executable file
26
serial.h
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -24,12 +25,35 @@
|
||||
#ifndef serial_h
|
||||
#define serial_h
|
||||
|
||||
#include "nuts_bolts.h"
|
||||
|
||||
#ifndef RX_BUFFER_SIZE
|
||||
#define RX_BUFFER_SIZE 128
|
||||
#endif
|
||||
#ifndef TX_BUFFER_SIZE
|
||||
#define TX_BUFFER_SIZE 64
|
||||
#endif
|
||||
|
||||
#define SERIAL_NO_DATA 0xff
|
||||
|
||||
void serial_init(long baud);
|
||||
#ifdef ENABLE_XONXOFF
|
||||
#define RX_BUFFER_FULL 96 // XOFF high watermark
|
||||
#define RX_BUFFER_LOW 64 // XON low watermark
|
||||
#define SEND_XOFF 1
|
||||
#define SEND_XON 2
|
||||
#define XOFF_SENT 3
|
||||
#define XON_SENT 4
|
||||
#define XOFF_CHAR 0x13
|
||||
#define XON_CHAR 0x11
|
||||
#endif
|
||||
|
||||
void serial_init();
|
||||
|
||||
void serial_write(uint8_t data);
|
||||
|
||||
uint8_t serial_read();
|
||||
|
||||
// Reset and empty data in read buffer. Used by e-stop and reset.
|
||||
void serial_reset_read_buffer();
|
||||
|
||||
#endif
|
||||
|
273
settings.c
273
settings.c
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -20,169 +20,202 @@
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <math.h>
|
||||
#include "protocol.h"
|
||||
#include "report.h"
|
||||
#include "stepper.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "settings.h"
|
||||
#include "eeprom.h"
|
||||
#include "print.h"
|
||||
#include <avr/pgmspace.h>
|
||||
#include "protocol.h"
|
||||
#include "config.h"
|
||||
#include "limits.h"
|
||||
|
||||
settings_t settings;
|
||||
|
||||
// Version 1 outdated settings record
|
||||
// Version 4 outdated settings record
|
||||
typedef struct {
|
||||
double steps_per_mm[3];
|
||||
float steps_per_mm[3];
|
||||
uint8_t microsteps;
|
||||
uint8_t pulse_microseconds;
|
||||
double default_feed_rate;
|
||||
double default_seek_rate;
|
||||
float default_feed_rate;
|
||||
float default_seek_rate;
|
||||
uint8_t invert_mask;
|
||||
double mm_per_arc_segment;
|
||||
} settings_v1_t;
|
||||
float mm_per_arc_segment;
|
||||
float acceleration;
|
||||
float junction_deviation;
|
||||
} settings_v4_t;
|
||||
|
||||
// Default settings (used when resetting eeprom-settings)
|
||||
#define MICROSTEPS 8
|
||||
#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||
#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||
#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 30
|
||||
#define DEFAULT_MM_PER_ARC_SEGMENT 0.1
|
||||
#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min
|
||||
#define DEFAULT_FEEDRATE 500.0
|
||||
#define DEFAULT_ACCELERATION (DEFAULT_FEEDRATE*60*60/10.0) // mm/min^2
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT))
|
||||
|
||||
void settings_reset() {
|
||||
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.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
|
||||
settings.default_feed_rate = DEFAULT_FEEDRATE;
|
||||
settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE;
|
||||
settings.acceleration = DEFAULT_ACCELERATION;
|
||||
settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT;
|
||||
settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK;
|
||||
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
|
||||
// Method to store startup lines into EEPROM
|
||||
void settings_store_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
|
||||
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void settings_dump() {
|
||||
printPgmString(PSTR("$0 = ")); printFloat(settings.steps_per_mm[X_AXIS]);
|
||||
printPgmString(PSTR(" (steps/mm x)\r\n$1 = ")); printFloat(settings.steps_per_mm[Y_AXIS]);
|
||||
printPgmString(PSTR(" (steps/mm y)\r\n$2 = ")); printFloat(settings.steps_per_mm[Z_AXIS]);
|
||||
printPgmString(PSTR(" (steps/mm z)\r\n$3 = ")); printInteger(settings.pulse_microseconds);
|
||||
printPgmString(PSTR(" (microseconds step pulse)\r\n$4 = ")); printFloat(settings.default_feed_rate);
|
||||
printPgmString(PSTR(" (mm/min default feed rate)\r\n$5 = ")); printFloat(settings.default_seek_rate);
|
||||
printPgmString(PSTR(" (mm/min default seek rate)\r\n$6 = ")); printFloat(settings.mm_per_arc_segment);
|
||||
printPgmString(PSTR(" (mm/arc segment)\r\n$7 = ")); printInteger(settings.invert_mask);
|
||||
printPgmString(PSTR(" (step port invert mask. binary = ")); printIntegerInBase(settings.invert_mask, 2);
|
||||
printPgmString(PSTR(")\r\n$8 = ")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability
|
||||
printPgmString(PSTR(" (acceleration in mm/sec^2)\r\n$9 = ")); printFloat(settings.junction_deviation);
|
||||
printPgmString(PSTR(" (cornering junction deviation in mm)"));
|
||||
printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n"));
|
||||
// Method to store coord data parameters into EEPROM
|
||||
void settings_write_coord_data(uint8_t coord_select, float *coord_data)
|
||||
{
|
||||
uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
|
||||
memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS);
|
||||
}
|
||||
|
||||
// Parameter lines are on the form '$4=374.3' or '$' to dump current settings
|
||||
uint8_t settings_execute_line(char *line) {
|
||||
uint8_t char_counter = 1;
|
||||
double parameter, value;
|
||||
if(line[0] != '$') {
|
||||
return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
if(line[char_counter] == 0) {
|
||||
settings_dump(); return(STATUS_OK);
|
||||
}
|
||||
if(!read_double(line, &char_counter, ¶meter)) {
|
||||
return(STATUS_BAD_NUMBER_FORMAT);
|
||||
};
|
||||
if(line[char_counter++] != '=') {
|
||||
return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
if(!read_double(line, &char_counter, &value)) {
|
||||
return(STATUS_BAD_NUMBER_FORMAT);
|
||||
}
|
||||
if(line[char_counter] != 0) {
|
||||
return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
settings_store_setting(parameter, value);
|
||||
return(STATUS_OK);
|
||||
}
|
||||
|
||||
void write_settings() {
|
||||
// Method to store Grbl global settings struct and version number into EEPROM
|
||||
void write_global_settings()
|
||||
{
|
||||
eeprom_put_char(0, SETTINGS_VERSION);
|
||||
memcpy_to_eeprom_with_checksum(1, (char*)&settings, sizeof(settings_t));
|
||||
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
|
||||
}
|
||||
|
||||
int read_settings() {
|
||||
// Method to reset Grbl global settings back to defaults.
|
||||
void settings_reset(bool reset_all) {
|
||||
// Reset all settings or only the migration settings to the new version.
|
||||
if (reset_all) {
|
||||
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.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
|
||||
settings.default_feed_rate = DEFAULT_FEEDRATE;
|
||||
settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE;
|
||||
settings.acceleration = DEFAULT_ACCELERATION;
|
||||
settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT;
|
||||
settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK;
|
||||
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
|
||||
}
|
||||
// New settings since last version
|
||||
settings.flags = 0;
|
||||
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
|
||||
if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
|
||||
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; }
|
||||
settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
|
||||
settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE;
|
||||
settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE;
|
||||
settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
|
||||
settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
|
||||
settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME;
|
||||
settings.decimal_places = DEFAULT_DECIMAL_PLACES;
|
||||
settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION;
|
||||
write_global_settings();
|
||||
}
|
||||
|
||||
// Reads startup line from EEPROM. Updated pointed line string data.
|
||||
uint8_t settings_read_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) {
|
||||
// Reset line with default value
|
||||
line[0] = 0;
|
||||
settings_store_startup_line(n, line);
|
||||
return(false);
|
||||
} else {
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Read selected coordinate data from EEPROM. Updates pointed coord_data value.
|
||||
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
|
||||
{
|
||||
uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) {
|
||||
// Reset with default zero vector
|
||||
clear_vector_float(coord_data);
|
||||
settings_write_coord_data(coord_select,coord_data);
|
||||
return(false);
|
||||
} else {
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Reads Grbl global settings struct from EEPROM.
|
||||
uint8_t read_global_settings() {
|
||||
// Check version-byte of eeprom
|
||||
uint8_t version = eeprom_get_char(0);
|
||||
|
||||
if (version == SETTINGS_VERSION) {
|
||||
// Read settings-record and check checksum
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) {
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) {
|
||||
return(false);
|
||||
}
|
||||
} else if (version == 1) {
|
||||
// Migrate from settings version 1
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v1_t)))) {
|
||||
return(false);
|
||||
}
|
||||
settings.acceleration = DEFAULT_ACCELERATION;
|
||||
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
|
||||
write_settings();
|
||||
} else if ((version == 2) || (version == 3)) {
|
||||
// Migrate from settings version 2 and 3
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) {
|
||||
return(false);
|
||||
}
|
||||
if (version == 2) { settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; }
|
||||
settings.acceleration *= 3600; // Convert to mm/min^2 from mm/sec^2
|
||||
write_settings();
|
||||
} else {
|
||||
return(false);
|
||||
if (version <= 4) {
|
||||
// Migrate from settings version 4 to current version.
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) {
|
||||
return(false);
|
||||
}
|
||||
settings_reset(false); // Old settings ok. Write new settings only.
|
||||
} else {
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// A helper method to set settings from command line
|
||||
void settings_store_setting(int parameter, double value) {
|
||||
uint8_t settings_store_global_setting(int parameter, float value) {
|
||||
switch(parameter) {
|
||||
case 0: case 1: case 2:
|
||||
if (value <= 0.0) {
|
||||
printPgmString(PSTR("Steps/mm must be > 0.0\r\n"));
|
||||
return;
|
||||
}
|
||||
settings.steps_per_mm[parameter] = value; break;
|
||||
if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); }
|
||||
settings.steps_per_mm[parameter] = value; break;
|
||||
case 3:
|
||||
if (value < 3) {
|
||||
printPgmString(PSTR("Step pulse must be >= 3 microseconds\r\n"));
|
||||
return;
|
||||
}
|
||||
settings.pulse_microseconds = round(value); break;
|
||||
if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
|
||||
settings.pulse_microseconds = round(value); break;
|
||||
case 4: settings.default_feed_rate = value; break;
|
||||
case 5: settings.default_seek_rate = value; break;
|
||||
case 6: settings.mm_per_arc_segment = value; break;
|
||||
case 7: settings.invert_mask = trunc(value); break;
|
||||
case 6: settings.invert_mask = trunc(value); break;
|
||||
case 7: settings.stepper_idle_lock_time = round(value); break;
|
||||
case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
|
||||
case 9: settings.junction_deviation = fabs(value); break;
|
||||
case 10: settings.mm_per_arc_segment = value; break;
|
||||
case 11: settings.n_arc_correction = round(value); break;
|
||||
case 12: settings.decimal_places = round(value); break;
|
||||
case 13:
|
||||
if (value) { settings.flags |= BITFLAG_REPORT_INCHES; }
|
||||
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
|
||||
break;
|
||||
case 14: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (value) { settings.flags |= BITFLAG_AUTO_START; }
|
||||
else { settings.flags &= ~BITFLAG_AUTO_START; }
|
||||
break;
|
||||
case 15: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
|
||||
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; }
|
||||
break;
|
||||
case 16:
|
||||
if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
|
||||
break;
|
||||
case 17:
|
||||
if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
|
||||
else { settings.flags &= ~BITFLAG_HOMING_ENABLE; }
|
||||
break;
|
||||
case 18: settings.homing_dir_mask = trunc(value); break;
|
||||
case 19: settings.homing_feed_rate = value; break;
|
||||
case 20: settings.homing_seek_rate = value; break;
|
||||
case 21: settings.homing_debounce_delay = round(value); break;
|
||||
case 22: settings.homing_pulloff = value; break;
|
||||
default:
|
||||
printPgmString(PSTR("Unknown parameter\r\n"));
|
||||
return;
|
||||
return(STATUS_INVALID_STATEMENT);
|
||||
}
|
||||
write_settings();
|
||||
printPgmString(PSTR("Stored new setting\r\n"));
|
||||
write_global_settings();
|
||||
return(STATUS_OK);
|
||||
}
|
||||
|
||||
// Initialize the config subsystem
|
||||
void settings_init() {
|
||||
if(read_settings()) {
|
||||
printPgmString(PSTR("'$' to dump current settings\r\n"));
|
||||
} else {
|
||||
printPgmString(PSTR("Warning: Failed to read EEPROM settings. Using defaults.\r\n"));
|
||||
settings_reset();
|
||||
write_settings();
|
||||
settings_dump();
|
||||
if(!read_global_settings()) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
settings_reset(true);
|
||||
report_grbl_settings();
|
||||
}
|
||||
// Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing.
|
||||
float coord_data[N_AXIS];
|
||||
uint8_t i;
|
||||
for (i=0; i<=SETTING_INDEX_NCOORD; i++) {
|
||||
if (!settings_read_coord_data(i, coord_data)) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
}
|
||||
}
|
||||
// NOTE: Startup lines are handled and called by main.c at the end of initialization.
|
||||
}
|
||||
|
76
settings.h
Normal file → Executable file
76
settings.h
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,40 +22,78 @@
|
||||
#ifndef settings_h
|
||||
#define settings_h
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <inttypes.h>
|
||||
#include "nuts_bolts.h"
|
||||
|
||||
#define GRBL_VERSION "0.7d"
|
||||
#define GRBL_VERSION "0.8c"
|
||||
|
||||
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
|
||||
// when firmware is upgraded. Always stored in byte 0 of eeprom
|
||||
#define SETTINGS_VERSION 4
|
||||
#define SETTINGS_VERSION 5
|
||||
|
||||
// Current global settings (persisted in EEPROM from byte 1 onwards)
|
||||
// Define bit flag masks for the boolean settings in settings.flag.
|
||||
#define BITFLAG_REPORT_INCHES bit(0)
|
||||
#define BITFLAG_AUTO_START bit(1)
|
||||
#define BITFLAG_INVERT_ST_ENABLE bit(2)
|
||||
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
|
||||
#define BITFLAG_HOMING_ENABLE bit(4)
|
||||
|
||||
// Define EEPROM memory address location values for Grbl settings and parameters
|
||||
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
|
||||
// the startup script. The lower half contains the global settings and space for future
|
||||
// developments.
|
||||
#define EEPROM_ADDR_GLOBAL 1
|
||||
#define EEPROM_ADDR_PARAMETERS 512
|
||||
#define EEPROM_ADDR_STARTUP_BLOCK 768
|
||||
|
||||
// Define EEPROM address indexing for coordinate parameters
|
||||
#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1)
|
||||
#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0)
|
||||
// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59)
|
||||
#define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1
|
||||
#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2
|
||||
// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported)
|
||||
|
||||
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
|
||||
typedef struct {
|
||||
double steps_per_mm[3];
|
||||
float steps_per_mm[3];
|
||||
uint8_t microsteps;
|
||||
uint8_t pulse_microseconds;
|
||||
double default_feed_rate;
|
||||
double default_seek_rate;
|
||||
float default_feed_rate;
|
||||
float default_seek_rate;
|
||||
uint8_t invert_mask;
|
||||
double mm_per_arc_segment;
|
||||
double acceleration;
|
||||
double junction_deviation;
|
||||
float mm_per_arc_segment;
|
||||
float acceleration;
|
||||
float junction_deviation;
|
||||
uint8_t flags; // Contains default boolean settings
|
||||
uint8_t homing_dir_mask;
|
||||
float homing_feed_rate;
|
||||
float homing_seek_rate;
|
||||
uint16_t homing_debounce_delay;
|
||||
float homing_pulloff;
|
||||
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
|
||||
uint8_t decimal_places;
|
||||
uint8_t n_arc_correction;
|
||||
// uint8_t status_report_mask; // Mask to indicate desired report data.
|
||||
} settings_t;
|
||||
extern settings_t settings;
|
||||
|
||||
// Initialize the configuration subsystem (load settings from EEPROM)
|
||||
void settings_init();
|
||||
|
||||
// Print current settings
|
||||
void settings_dump();
|
||||
|
||||
// Handle settings command
|
||||
uint8_t settings_execute_line(char *line);
|
||||
|
||||
// A helper method to set new settings from command line
|
||||
void settings_store_setting(int parameter, double value);
|
||||
uint8_t settings_store_global_setting(int parameter, float value);
|
||||
|
||||
// Stores the protocol line variable as a startup line in EEPROM
|
||||
void settings_store_startup_line(uint8_t n, char *line);
|
||||
|
||||
// Reads an EEPROM startup line to the protocol line variable
|
||||
uint8_t settings_read_startup_line(uint8_t n, char *line);
|
||||
|
||||
// Writes selected coordinate data to EEPROM
|
||||
void settings_write_coord_data(uint8_t coord_select, float *coord_data);
|
||||
|
||||
// Reads selected coordinate data from EEPROM
|
||||
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data);
|
||||
|
||||
#endif
|
||||
|
31
spindle_control.c
Normal file → Executable file
31
spindle_control.c
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -18,27 +19,30 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "spindle_control.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include "stepper.h"
|
||||
#include "spindle_control.h"
|
||||
#include "planner.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
static int current_direction;
|
||||
static uint8_t current_direction;
|
||||
|
||||
void spindle_init()
|
||||
{
|
||||
current_direction = 0;
|
||||
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT);
|
||||
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT);
|
||||
spindle_run(0, 0);
|
||||
spindle_stop();
|
||||
}
|
||||
|
||||
void spindle_run(int direction, uint32_t rpm)
|
||||
void spindle_stop()
|
||||
{
|
||||
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
|
||||
}
|
||||
|
||||
void spindle_run(int8_t direction) //, uint16_t rpm)
|
||||
{
|
||||
if (direction != current_direction) {
|
||||
st_synchronize();
|
||||
if(direction) {
|
||||
plan_synchronize();
|
||||
if (direction) {
|
||||
if(direction > 0) {
|
||||
SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT);
|
||||
} else {
|
||||
@ -46,13 +50,8 @@ void spindle_run(int direction, uint32_t rpm)
|
||||
}
|
||||
SPINDLE_ENABLE_PORT |= 1<<SPINDLE_ENABLE_BIT;
|
||||
} else {
|
||||
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
|
||||
spindle_stop();
|
||||
}
|
||||
current_direction = direction;
|
||||
}
|
||||
}
|
||||
|
||||
void spindle_stop()
|
||||
{
|
||||
spindle_run(0, 0);
|
||||
}
|
||||
|
3
spindle_control.h
Normal file → Executable file
3
spindle_control.h
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -24,7 +25,7 @@
|
||||
#include <avr/io.h>
|
||||
|
||||
void spindle_init();
|
||||
void spindle_run(int direction, uint32_t rpm);
|
||||
void spindle_run(int8_t direction); //, uint16_t rpm);
|
||||
void spindle_stop();
|
||||
|
||||
#endif
|
||||
|
406
stepper.c
Normal file → Executable file
406
stepper.c
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,42 +22,44 @@
|
||||
/* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith
|
||||
and Philipp Tiefenbacher. */
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include "stepper.h"
|
||||
#include "config.h"
|
||||
#include "settings.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <util/delay.h>
|
||||
#include "nuts_bolts.h"
|
||||
#include <avr/interrupt.h>
|
||||
#include "planner.h"
|
||||
#include "limits.h"
|
||||
|
||||
// Some useful constants
|
||||
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
|
||||
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
|
||||
|
||||
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
|
||||
#define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND)
|
||||
|
||||
// Stepper state variable. Contains running data and trapezoid variables.
|
||||
typedef struct {
|
||||
// Used by the bresenham line algorithm
|
||||
int32_t counter_x, // Counter variables for the bresenham line tracer
|
||||
counter_y,
|
||||
counter_z;
|
||||
uint32_t event_count;
|
||||
uint32_t step_events_completed; // The number of step events left in current motion
|
||||
|
||||
// Used by the trapezoid generator
|
||||
uint32_t cycles_per_step_event; // The number of machine cycles between each step event
|
||||
uint32_t trapezoid_tick_cycle_counter; // The cycles since last trapezoid_tick. Used to generate ticks at a steady
|
||||
// pace without allocating a separate timer
|
||||
uint32_t trapezoid_adjusted_rate; // The current rate of step_events according to the trapezoid generator
|
||||
uint32_t min_safe_rate; // Minimum safe rate for full deceleration rate reduction step. Otherwise halves step_rate.
|
||||
} stepper_t;
|
||||
|
||||
static stepper_t st;
|
||||
static block_t *current_block; // A pointer to the block currently being traced
|
||||
|
||||
// Variables used by The Stepper Driver Interrupt
|
||||
// Used by the stepper driver interrupt
|
||||
static uint8_t step_pulse_time; // Step pulse reset time after step rise
|
||||
static uint8_t out_bits; // The next stepping-bits to be output
|
||||
static int32_t counter_x, // Counter variables for the bresenham line tracer
|
||||
counter_y,
|
||||
counter_z;
|
||||
static uint32_t step_events_completed; // The number of step events executed in the current block
|
||||
static volatile uint8_t busy; // true when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler.
|
||||
static volatile uint8_t busy; // True when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler.
|
||||
|
||||
// Variables used by the trapezoid generation
|
||||
static uint32_t cycles_per_step_event; // The number of machine cycles between each step event
|
||||
static uint32_t trapezoid_tick_cycle_counter; // The cycles since last trapezoid_tick. Used to generate ticks at a steady
|
||||
// pace without allocating a separate timer
|
||||
static uint32_t trapezoid_adjusted_rate; // The current rate of step_events according to the trapezoid generator
|
||||
static uint32_t min_safe_rate; // Minimum safe rate for full deceleration rate reduction step. Otherwise halves step_rate.
|
||||
static uint8_t cycle_start; // Cycle start flag to indicate program start and block processing.
|
||||
#if STEP_PULSE_DELAY > 0
|
||||
static uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
|
||||
#endif
|
||||
|
||||
// __________________________
|
||||
// /| |\ _________________ ^
|
||||
@ -78,51 +80,60 @@ static uint8_t cycle_start; // Cycle start flag to indicate program start an
|
||||
|
||||
static void set_step_events_per_minute(uint32_t steps_per_minute);
|
||||
|
||||
// Stepper state initialization
|
||||
static void st_wake_up()
|
||||
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
|
||||
// enabled. Startup init and limits call this function but shouldn't start the cycle.
|
||||
void st_wake_up()
|
||||
{
|
||||
// Initialize stepper output bits
|
||||
out_bits = (0) ^ (settings.invert_mask);
|
||||
// Enable steppers by resetting the stepper disable port
|
||||
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
||||
// Enable stepper driver interrupt
|
||||
TIMSK1 |= (1<<OCIE1A);
|
||||
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
|
||||
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
|
||||
} else {
|
||||
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
||||
}
|
||||
if (sys.state == STATE_CYCLE) {
|
||||
// Initialize stepper output bits
|
||||
out_bits = (0) ^ (settings.invert_mask);
|
||||
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
|
||||
step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||
// Set delay between direction pin write and step command.
|
||||
OCR2A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#else // Normal operation
|
||||
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
|
||||
step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#endif
|
||||
// Enable stepper driver interrupt
|
||||
TIMSK1 |= (1<<OCIE1A);
|
||||
}
|
||||
}
|
||||
|
||||
// Stepper shutdown
|
||||
static void st_go_idle()
|
||||
void st_go_idle()
|
||||
{
|
||||
// Cycle finished. Set flag to false.
|
||||
cycle_start = false;
|
||||
// Disable stepper driver interrupt
|
||||
TIMSK1 &= ~(1<<OCIE1A);
|
||||
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
|
||||
// stop and not drift from residual inertial forces at the end of the last movement.
|
||||
#if STEPPER_IDLE_LOCK_TIME
|
||||
_delay_ms(STEPPER_IDLE_LOCK_TIME);
|
||||
#endif
|
||||
// Disable steppers by setting stepper disable
|
||||
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
|
||||
}
|
||||
|
||||
// Initializes the trapezoid generator from the current block. Called whenever a new
|
||||
// block begins.
|
||||
static void trapezoid_generator_reset()
|
||||
{
|
||||
trapezoid_adjusted_rate = current_block->initial_rate;
|
||||
min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta
|
||||
trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
|
||||
set_step_events_per_minute(trapezoid_adjusted_rate); // Initialize cycles_per_step_event
|
||||
// Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
|
||||
if ((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) {
|
||||
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
|
||||
// stop and not drift from residual inertial forces at the end of the last movement.
|
||||
delay_ms(settings.stepper_idle_lock_time);
|
||||
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
|
||||
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
||||
} else {
|
||||
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function determines an acceleration velocity change every CYCLES_PER_ACCELERATION_TICK by
|
||||
// keeping track of the number of elapsed cycles during a de/ac-celeration. The code assumes that
|
||||
// step_events occur significantly more often than the acceleration velocity iterations.
|
||||
static uint8_t iterate_trapezoid_cycle_counter()
|
||||
inline static uint8_t iterate_trapezoid_cycle_counter()
|
||||
{
|
||||
trapezoid_tick_cycle_counter += cycles_per_step_event;
|
||||
if(trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) {
|
||||
trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK;
|
||||
st.trapezoid_tick_cycle_counter += st.cycles_per_step_event;
|
||||
if(st.trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) {
|
||||
st.trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK;
|
||||
return(true);
|
||||
} else {
|
||||
return(false);
|
||||
@ -140,10 +151,14 @@ ISR(TIMER1_COMPA_vect)
|
||||
// Set the direction pins a couple of nanoseconds before we step the steppers
|
||||
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
|
||||
// Then pulse the stepping pins
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
step_bits = (STEPPING_PORT & ~STEP_MASK) | out_bits; // Store out_bits to prevent overwriting.
|
||||
#else // Normal operation
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
|
||||
#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.
|
||||
TCNT2 = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); // Reload timer counter
|
||||
TCNT2 = step_pulse_time; // Reload timer counter
|
||||
TCCR2B = (1<<CS21); // Begin timer2. Full speed, 1/8 prescaler
|
||||
|
||||
busy = true;
|
||||
@ -157,107 +172,147 @@ ISR(TIMER1_COMPA_vect)
|
||||
// Anything in the buffer? If so, initialize next motion.
|
||||
current_block = plan_get_current_block();
|
||||
if (current_block != NULL) {
|
||||
trapezoid_generator_reset();
|
||||
counter_x = -(current_block->step_event_count >> 1);
|
||||
counter_y = counter_x;
|
||||
counter_z = counter_x;
|
||||
step_events_completed = 0;
|
||||
if (sys.state == STATE_CYCLE) {
|
||||
// During feed hold, do not update rate and trap counter. Keep decelerating.
|
||||
st.trapezoid_adjusted_rate = current_block->initial_rate;
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate); // Initialize cycles_per_step_event
|
||||
st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
|
||||
}
|
||||
st.min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta
|
||||
st.counter_x = -(current_block->step_event_count >> 1);
|
||||
st.counter_y = st.counter_x;
|
||||
st.counter_z = st.counter_x;
|
||||
st.event_count = current_block->step_event_count;
|
||||
st.step_events_completed = 0;
|
||||
} else {
|
||||
st_go_idle();
|
||||
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
|
||||
}
|
||||
}
|
||||
|
||||
if (current_block != NULL) {
|
||||
// Execute step displacement profile by bresenham line algorithm
|
||||
out_bits = current_block->direction_bits;
|
||||
counter_x += current_block->steps_x;
|
||||
if (counter_x > 0) {
|
||||
st.counter_x += current_block->steps_x;
|
||||
if (st.counter_x > 0) {
|
||||
out_bits |= (1<<X_STEP_BIT);
|
||||
counter_x -= current_block->step_event_count;
|
||||
st.counter_x -= st.event_count;
|
||||
if (out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
||||
else { sys.position[X_AXIS]++; }
|
||||
}
|
||||
counter_y += current_block->steps_y;
|
||||
if (counter_y > 0) {
|
||||
st.counter_y += current_block->steps_y;
|
||||
if (st.counter_y > 0) {
|
||||
out_bits |= (1<<Y_STEP_BIT);
|
||||
counter_y -= current_block->step_event_count;
|
||||
st.counter_y -= st.event_count;
|
||||
if (out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
||||
else { sys.position[Y_AXIS]++; }
|
||||
}
|
||||
counter_z += current_block->steps_z;
|
||||
if (counter_z > 0) {
|
||||
st.counter_z += current_block->steps_z;
|
||||
if (st.counter_z > 0) {
|
||||
out_bits |= (1<<Z_STEP_BIT);
|
||||
counter_z -= current_block->step_event_count;
|
||||
st.counter_z -= st.event_count;
|
||||
if (out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
||||
else { sys.position[Z_AXIS]++; }
|
||||
}
|
||||
|
||||
step_events_completed++; // Iterate step events
|
||||
st.step_events_completed++; // Iterate step events
|
||||
|
||||
// While in block steps, check for de/ac-celeration events and execute them accordingly.
|
||||
if (step_events_completed < current_block->step_event_count) {
|
||||
// The trapezoid generator always checks step event location to ensure de/ac-celerations are
|
||||
// executed and terminated at exactly the right time. This helps prevent over/under-shooting
|
||||
// the target position and speed.
|
||||
// NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the
|
||||
// discrete velocity changes increase and accuracy can increase as well to a point. Numerical
|
||||
// round-off errors can effect this, if set too high. This is important to note if a user has
|
||||
// very high acceleration and/or feedrate requirements for their machine.
|
||||
if (step_events_completed < current_block->accelerate_until) {
|
||||
// Iterate cycle counter and check if speeds need to be increased.
|
||||
if (st.step_events_completed < current_block->step_event_count) {
|
||||
if (sys.state == STATE_HOLD) {
|
||||
// Check for and execute feed hold by enforcing a steady deceleration from the moment of
|
||||
// execution. The rate of deceleration is limited by rate_delta and will never decelerate
|
||||
// faster or slower than in normal operation. If the distance required for the feed hold
|
||||
// deceleration spans more than one block, the initial rate of the following blocks are not
|
||||
// updated and deceleration is continued according to their corresponding rate_delta.
|
||||
// NOTE: The trapezoid tick cycle counter is not updated intentionally. This ensures that
|
||||
// the deceleration is smooth regardless of where the feed hold is initiated and if the
|
||||
// deceleration distance spans multiple blocks.
|
||||
if ( iterate_trapezoid_cycle_counter() ) {
|
||||
trapezoid_adjusted_rate += current_block->rate_delta;
|
||||
if (trapezoid_adjusted_rate >= current_block->nominal_rate) {
|
||||
// Reached nominal rate a little early. Cruise at nominal rate until decelerate_after.
|
||||
trapezoid_adjusted_rate = current_block->nominal_rate;
|
||||
}
|
||||
set_step_events_per_minute(trapezoid_adjusted_rate);
|
||||
}
|
||||
} else if (step_events_completed >= current_block->decelerate_after) {
|
||||
// Reset trapezoid tick cycle counter to make sure that the deceleration is performed the
|
||||
// same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for
|
||||
// an accurate approximation of the deceleration curve.
|
||||
if (step_events_completed == current_block-> decelerate_after) {
|
||||
trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2;
|
||||
} else {
|
||||
// Iterate cycle counter and check if speeds need to be reduced.
|
||||
if ( iterate_trapezoid_cycle_counter() ) {
|
||||
// NOTE: We will only do a full speed reduction if the result is more than the minimum safe
|
||||
// rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by
|
||||
// half increments until finished. The half increments are guaranteed not to exceed the
|
||||
// CNC acceleration limits, because they will never be greater than rate_delta. This catches
|
||||
// small errors that might leave steps hanging after the last trapezoid tick or a very slow
|
||||
// step rate at the end of a full stop deceleration in certain situations. The half rate
|
||||
// reductions should only be called once or twice per block and create a nice smooth
|
||||
// end deceleration.
|
||||
if (trapezoid_adjusted_rate > min_safe_rate) {
|
||||
trapezoid_adjusted_rate -= current_block->rate_delta;
|
||||
} else {
|
||||
trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2
|
||||
}
|
||||
if (trapezoid_adjusted_rate < current_block->final_rate) {
|
||||
// Reached final rate a little early. Cruise to end of block at final rate.
|
||||
trapezoid_adjusted_rate = current_block->final_rate;
|
||||
}
|
||||
set_step_events_per_minute(trapezoid_adjusted_rate);
|
||||
// If deceleration complete, set system flags and shutdown steppers.
|
||||
if (st.trapezoid_adjusted_rate <= current_block->rate_delta) {
|
||||
// Just go idle. Do not NULL current block. The bresenham algorithm variables must
|
||||
// remain intact to ensure the stepper path is exactly the same. Feed hold is still
|
||||
// active and is released after the buffer has been reinitialized.
|
||||
st_go_idle();
|
||||
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program that feed hold is complete.
|
||||
} else {
|
||||
st.trapezoid_adjusted_rate -= current_block->rate_delta;
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// No accelerations. Make sure we cruise exactly at the nominal rate.
|
||||
if (trapezoid_adjusted_rate != current_block->nominal_rate) {
|
||||
trapezoid_adjusted_rate = current_block->nominal_rate;
|
||||
set_step_events_per_minute(trapezoid_adjusted_rate);
|
||||
// The trapezoid generator always checks step event location to ensure de/ac-celerations are
|
||||
// executed and terminated at exactly the right time. This helps prevent over/under-shooting
|
||||
// the target position and speed.
|
||||
// NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the
|
||||
// discrete velocity changes increase and accuracy can increase as well to a point. Numerical
|
||||
// round-off errors can effect this, if set too high. This is important to note if a user has
|
||||
// very high acceleration and/or feedrate requirements for their machine.
|
||||
if (st.step_events_completed < current_block->accelerate_until) {
|
||||
// Iterate cycle counter and check if speeds need to be increased.
|
||||
if ( iterate_trapezoid_cycle_counter() ) {
|
||||
st.trapezoid_adjusted_rate += current_block->rate_delta;
|
||||
if (st.trapezoid_adjusted_rate >= current_block->nominal_rate) {
|
||||
// Reached nominal rate a little early. Cruise at nominal rate until decelerate_after.
|
||||
st.trapezoid_adjusted_rate = current_block->nominal_rate;
|
||||
}
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate);
|
||||
}
|
||||
} else if (st.step_events_completed >= current_block->decelerate_after) {
|
||||
// Reset trapezoid tick cycle counter to make sure that the deceleration is performed the
|
||||
// same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for
|
||||
// an accurate approximation of the deceleration curve.
|
||||
if (st.step_events_completed == current_block-> decelerate_after) {
|
||||
st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2;
|
||||
} else {
|
||||
// Iterate cycle counter and check if speeds need to be reduced.
|
||||
if ( iterate_trapezoid_cycle_counter() ) {
|
||||
// NOTE: We will only do a full speed reduction if the result is more than the minimum safe
|
||||
// rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by
|
||||
// half increments until finished. The half increments are guaranteed not to exceed the
|
||||
// CNC acceleration limits, because they will never be greater than rate_delta. This catches
|
||||
// small errors that might leave steps hanging after the last trapezoid tick or a very slow
|
||||
// step rate at the end of a full stop deceleration in certain situations. The half rate
|
||||
// reductions should only be called once or twice per block and create a nice smooth
|
||||
// end deceleration.
|
||||
if (st.trapezoid_adjusted_rate > st.min_safe_rate) {
|
||||
st.trapezoid_adjusted_rate -= current_block->rate_delta;
|
||||
} else {
|
||||
st.trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2
|
||||
}
|
||||
if (st.trapezoid_adjusted_rate < current_block->final_rate) {
|
||||
// Reached final rate a little early. Cruise to end of block at final rate.
|
||||
st.trapezoid_adjusted_rate = current_block->final_rate;
|
||||
}
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No accelerations. Make sure we cruise exactly at the nominal rate.
|
||||
if (st.trapezoid_adjusted_rate != current_block->nominal_rate) {
|
||||
st.trapezoid_adjusted_rate = current_block->nominal_rate;
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// If current block is finished, reset pointer
|
||||
current_block = NULL;
|
||||
plan_discard_current_block();
|
||||
}
|
||||
}
|
||||
out_bits ^= settings.invert_mask; // Apply stepper invert mask
|
||||
busy=false;
|
||||
out_bits ^= settings.invert_mask; // Apply step and direction invert mask
|
||||
busy = false;
|
||||
}
|
||||
|
||||
// This interrupt is set up by ISR_TIMER1_COMPAREA when it sets the motor port bits. It resets
|
||||
// the motor port after a short period (settings.pulse_microseconds) completing one step cycle.
|
||||
// TODO: It is possible for the serial interrupts to delay this interrupt by a few microseconds, if
|
||||
// they execute right before this interrupt. Not a big deal, but could use some TLC at some point.
|
||||
// NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
|
||||
// a few microseconds, if they execute right before one another. Not a big deal, but can
|
||||
// cause issues at high step rates if another high frequency asynchronous interrupt is
|
||||
// added to Grbl.
|
||||
ISR(TIMER2_OVF_vect)
|
||||
{
|
||||
// Reset stepping pins (leave the direction pins)
|
||||
@ -265,6 +320,27 @@ ISR(TIMER2_OVF_vect)
|
||||
TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed.
|
||||
}
|
||||
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
|
||||
// initiated after the STEP_PULSE_DELAY time period has elapsed. The ISR TIMER2_OVF interrupt
|
||||
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
|
||||
// The new timing between direction, step pulse, and step complete events are setup in the
|
||||
// st_wake_up() routine.
|
||||
ISR(TIMER2_COMPA_vect)
|
||||
{
|
||||
STEPPING_PORT = step_bits; // Begin step pulse.
|
||||
}
|
||||
#endif
|
||||
|
||||
// Reset and clear stepper subsystem variables
|
||||
void st_reset()
|
||||
{
|
||||
memset(&st, 0, sizeof(st));
|
||||
set_step_events_per_minute(MINIMUM_STEPS_PER_MINUTE);
|
||||
current_block = NULL;
|
||||
busy = false;
|
||||
}
|
||||
|
||||
// Initialize and start the stepper motor subsystem
|
||||
void st_init()
|
||||
{
|
||||
@ -286,58 +362,51 @@ void st_init()
|
||||
// Configure Timer 2
|
||||
TCCR2A = 0; // Normal operation
|
||||
TCCR2B = 0; // Disable timer until needed.
|
||||
TIMSK2 |= (1<<TOIE2); // Enable Timer2 interrupt flag
|
||||
TIMSK2 |= (1<<TOIE2); // Enable Timer2 Overflow interrupt
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
|
||||
#endif
|
||||
|
||||
set_step_events_per_minute(MINIMUM_STEPS_PER_MINUTE);
|
||||
trapezoid_tick_cycle_counter = 0;
|
||||
current_block = NULL;
|
||||
busy = false;
|
||||
|
||||
// Start in the idle state
|
||||
// Start in the idle state, but first wake up to check for keep steppers enabled option.
|
||||
st_wake_up();
|
||||
st_go_idle();
|
||||
}
|
||||
|
||||
// Block until all buffered steps are executed
|
||||
void st_synchronize()
|
||||
{
|
||||
while(plan_get_current_block()) { sleep_mode(); }
|
||||
}
|
||||
|
||||
// Configures the prescaler and ceiling of timer 1 to produce the given rate as accurately as possible.
|
||||
// Returns the actual number of cycles per interrupt
|
||||
static uint32_t config_step_timer(uint32_t cycles)
|
||||
{
|
||||
uint16_t ceiling;
|
||||
uint16_t prescaler;
|
||||
uint8_t prescaler;
|
||||
uint32_t actual_cycles;
|
||||
if (cycles <= 0xffffL) {
|
||||
ceiling = cycles;
|
||||
prescaler = 0; // prescaler: 0
|
||||
prescaler = 1; // prescaler: 0
|
||||
actual_cycles = ceiling;
|
||||
} else if (cycles <= 0x7ffffL) {
|
||||
ceiling = cycles >> 3;
|
||||
prescaler = 1; // prescaler: 8
|
||||
prescaler = 2; // prescaler: 8
|
||||
actual_cycles = ceiling * 8L;
|
||||
} else if (cycles <= 0x3fffffL) {
|
||||
ceiling = cycles >> 6;
|
||||
prescaler = 2; // prescaler: 64
|
||||
prescaler = 3; // prescaler: 64
|
||||
actual_cycles = ceiling * 64L;
|
||||
} else if (cycles <= 0xffffffL) {
|
||||
ceiling = (cycles >> 8);
|
||||
prescaler = 3; // prescaler: 256
|
||||
prescaler = 4; // prescaler: 256
|
||||
actual_cycles = ceiling * 256L;
|
||||
} else if (cycles <= 0x3ffffffL) {
|
||||
ceiling = (cycles >> 10);
|
||||
prescaler = 4; // prescaler: 1024
|
||||
prescaler = 5; // prescaler: 1024
|
||||
actual_cycles = ceiling * 1024L;
|
||||
} else {
|
||||
// Okay, that was slower than we actually go. Just set the slowest speed
|
||||
ceiling = 0xffff;
|
||||
prescaler = 4;
|
||||
prescaler = 5;
|
||||
actual_cycles = 0xffff * 1024;
|
||||
}
|
||||
// Set prescaler
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | ((prescaler+1)<<CS10);
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (prescaler<<CS10);
|
||||
// Set ceiling
|
||||
OCR1A = ceiling;
|
||||
return(actual_cycles);
|
||||
@ -346,20 +415,45 @@ static uint32_t config_step_timer(uint32_t cycles)
|
||||
static void set_step_events_per_minute(uint32_t steps_per_minute)
|
||||
{
|
||||
if (steps_per_minute < MINIMUM_STEPS_PER_MINUTE) { steps_per_minute = MINIMUM_STEPS_PER_MINUTE; }
|
||||
cycles_per_step_event = config_step_timer((TICKS_PER_MICROSECOND*1000000*60)/steps_per_minute);
|
||||
st.cycles_per_step_event = config_step_timer((TICKS_PER_MICROSECOND*1000000*60)/steps_per_minute);
|
||||
}
|
||||
|
||||
void st_go_home()
|
||||
{
|
||||
limits_go_home();
|
||||
plan_set_current_position(0,0,0);
|
||||
}
|
||||
|
||||
// Planner external interface to start stepper interrupt and execute the blocks in queue.
|
||||
// Planner external interface to start stepper interrupt and execute the blocks in queue. Called
|
||||
// by the main program functions: planner auto-start and run-time command execution.
|
||||
void st_cycle_start()
|
||||
{
|
||||
if (!cycle_start) {
|
||||
cycle_start = true;
|
||||
if (sys.state == STATE_QUEUED) {
|
||||
sys.state = STATE_CYCLE;
|
||||
st_wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a feed hold with deceleration, only during cycle. Called by main program.
|
||||
void st_feed_hold()
|
||||
{
|
||||
if (sys.state == STATE_CYCLE) {
|
||||
sys.state = STATE_HOLD;
|
||||
sys.auto_start = false; // Disable planner auto start upon feed hold.
|
||||
}
|
||||
}
|
||||
|
||||
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
|
||||
// runtime command execution in the main program, ensuring that the planner re-plans safely.
|
||||
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
|
||||
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
|
||||
// Only the planner de/ac-celerations profiles and stepper rates have been updated.
|
||||
void st_cycle_reinitialize()
|
||||
{
|
||||
if (current_block != NULL) {
|
||||
// Replan buffer from the feed hold stop location.
|
||||
plan_cycle_reinitialize(current_block->step_event_count - st.step_events_completed);
|
||||
// Update initial rate and timers after feed hold.
|
||||
st.trapezoid_adjusted_rate = 0; // Resumes from rest
|
||||
set_step_events_per_minute(st.trapezoid_adjusted_rate);
|
||||
st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
|
||||
st.step_events_completed = 0;
|
||||
sys.state = STATE_QUEUED;
|
||||
} else {
|
||||
sys.state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
26
stepper.h
Normal file → Executable file
26
stepper.h
Normal file → Executable file
@ -3,6 +3,7 @@
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
|
||||
Grbl is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -22,23 +23,26 @@
|
||||
#define stepper_h
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
|
||||
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
|
||||
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
|
||||
|
||||
// Initialize and start the stepper motor subsystem
|
||||
// Initialize and setup the stepper motor subsystem
|
||||
void st_init();
|
||||
|
||||
// Block until all buffered steps are executed
|
||||
void st_synchronize();
|
||||
// Enable steppers, but cycle does not start unless called by motion control or runtime command.
|
||||
void st_wake_up();
|
||||
|
||||
// Execute the homing cycle
|
||||
void st_go_home();
|
||||
// Immediately disables steppers
|
||||
void st_go_idle();
|
||||
|
||||
// Reset the stepper subsystem variables
|
||||
void st_reset();
|
||||
|
||||
// Notify the stepper subsystem to start executing the g-code program in buffer.
|
||||
void st_cycle_start();
|
||||
|
||||
// Reinitializes the buffer after a feed hold for a resume.
|
||||
void st_cycle_reinitialize();
|
||||
|
||||
// Initiates a feed hold of the running program
|
||||
void st_feed_hold();
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user