Incomplete push but working. Lots more stuff. More to come.
- NEW! An active multi-axis step smoothing algorithm that automatically adjusts dependent on step frequency. This solves the long standing issue to aliasing when moving with multiple axes. Similar in scheme to Smoothieware, but more advanced in ensuring a more consistent CPU overhead throughout all frequencies while maintaining step exactness. - Switched from Timer2 to Timer0 for the Step Port Reset Interrupt. Mainly to free up hardware PWM pins. - Seperated the direction and step pin assignments, so we can now move them to seperate ports. This means that we can more easily support 4+ axes in the future. - Added a setting for inverting the limit pins, as so many users have request. Better late than never. - Bug fix related to EEPROM calls when in cycle. The EEPROM would kill the stepper motion. Now protocol mandates that the system be either in IDLE or ALARM to access or change any settings. - Bug fix related to resuming the cycle after a spindle or dwell command if auto start has been disabled. This fix is somewhat temporary or more of a patch. Doesn’t work with a straight call-response streaming protocol, but works fine with serial buffer pre-filling streaming that most clients use. - Renamed the pin_map.h to cpu_map.h to more accurately describe what the file is. - Pushed an auto start bug fix upon re-initialization. - Much more polishing to do!
This commit is contained in:
parent
5ab2bb7767
commit
47cd40c8dc
37
config.h
37
config.h
@ -34,9 +34,9 @@
|
||||
// Serial baud rate
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
// Default pin mappings. Grbl officially supports the Arduino Uno only. Other processor types
|
||||
// may exist from user-supplied templates or directly user-defined in pin_map.h
|
||||
#define PIN_MAP_ARDUINO_UNO
|
||||
// Default cpu mappings. Grbl officially supports the Arduino Uno only. Other processor types
|
||||
// may exist from user-supplied templates or directly user-defined in cpu_map.h
|
||||
#define CPU_MAP_ATMEGA328P // Arduino Uno CPU
|
||||
|
||||
// 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
|
||||
@ -49,12 +49,6 @@
|
||||
#define CMD_CYCLE_START '~'
|
||||
#define CMD_RESET 0x18 // ctrl-x.
|
||||
|
||||
// Uncomment the following define if you are using hardware that drives high when your limits
|
||||
// are reached. You will need to ensure that you have appropriate pull-down resistors on the
|
||||
// limit switch input pins, or that your hardware drives the pins low when they are open (non-
|
||||
// triggered).
|
||||
// #define LIMIT_SWITCHES_ACTIVE_HIGH // Uncomment to enable
|
||||
|
||||
// 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.
|
||||
@ -94,16 +88,8 @@
|
||||
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
|
||||
#define ACCELERATION_TICKS_PER_SECOND 100
|
||||
|
||||
// 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!)
|
||||
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
|
||||
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
|
||||
// values for certain setups have ranged from 5 to 20us.
|
||||
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
|
||||
#define ACTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
#define ENABLE_SOFTWARE_DEBOUNCE
|
||||
|
||||
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
|
||||
// every buffer block junction, except for starting from rest and end of the buffer, which are always
|
||||
@ -127,6 +113,17 @@
|
||||
// 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)
|
||||
|
||||
// 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!)
|
||||
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
|
||||
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
|
||||
// values for certain setups have ranged from 5 to 20us.
|
||||
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
|
||||
|
||||
// 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 Mega or Sanguino. Or decrease if the Arduino
|
||||
@ -171,8 +168,6 @@
|
||||
// case, please report any successes to grbl administrators!
|
||||
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
|
||||
|
||||
#define ENABLE_SOFTWARE_DEBOUNCE
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
// TODO: Install compile-time option to send numeric status codes rather than strings.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
pin_map.h - Pin mapping configuration file
|
||||
cpu_map.h - CPU and pin mapping configuration file
|
||||
Part of Grbl
|
||||
|
||||
Copyright (c) 2013 Sungeun K. Jeon
|
||||
@ -18,14 +18,17 @@
|
||||
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The pin_map.h file serves as a central pin mapping settings file for different processor
|
||||
/* The cpu_map.h file serves as a central pin mapping settings file for different processor
|
||||
types, i.e. AVR 328p or AVR Mega 2560. Grbl officially supports the Arduino Uno, but the
|
||||
other supplied pin mappings are supplied by users, so your results may vary. */
|
||||
|
||||
#ifndef pin_map_h
|
||||
#define pin_map_h
|
||||
// NOTE: This is still a work in progress. We are still centralizing the configurations to
|
||||
// this file, so your success may vary for other CPUs.
|
||||
|
||||
#ifdef PIN_MAP_ARDUINO_UNO // AVR 328p, Officially supported by Grbl.
|
||||
#ifndef cpu_map_h
|
||||
#define cpu_map_h
|
||||
|
||||
#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl.
|
||||
|
||||
// Serial port pins
|
||||
#define SERIAL_RX USART_RX_vect
|
||||
@ -37,12 +40,14 @@
|
||||
#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 STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||
|
||||
#define DIRECTION_DDR DDRD
|
||||
#define DIRECTION_PORT PORTD
|
||||
#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 STEPPERS_DISABLE_DDR DDRB
|
||||
#define STEPPERS_DISABLE_PORT PORTB
|
||||
@ -82,7 +87,7 @@
|
||||
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
|
||||
#endif
|
||||
|
||||
// NOTE: All pinouts pins must be on the same port
|
||||
// NOTE: All pinouts pins must be on the same port, but cannot be on same port as limit pins.
|
||||
#define PINOUT_DDR DDRC
|
||||
#define PINOUT_PIN PINC
|
||||
#define PINOUT_PORT PORTC
|
||||
@ -97,7 +102,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PIN_MAP_ARDUINO_MEGA_2560 // Working @EliteEng
|
||||
#ifdef CPU_MAP_ATMEGA2560 // (Arduino Mega 2560) Working @EliteEng
|
||||
|
||||
// Serial port pins
|
||||
#define SERIAL_RX USART0_RX_vect
|
||||
@ -109,19 +114,23 @@
|
||||
#define BLOCK_BUFFER_SIZE 36
|
||||
#define LINE_BUFFER_SIZE 100
|
||||
|
||||
// NOTE: All step bit and direction pins must be on the same port.
|
||||
// NOTE: All step pins must be on the same port.
|
||||
#define STEPPING_DDR DDRA
|
||||
#define STEPPING_PORT PORTA
|
||||
#define STEPPING_PIN PINA
|
||||
#define X_STEP_BIT 2 // MEGA2560 Digital Pin 24
|
||||
#define Y_STEP_BIT 3 // MEGA2560 Digital Pin 25
|
||||
#define Z_STEP_BIT 4 // MEGA2560 Digital Pin 26
|
||||
#define STEPPING_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||
|
||||
// NOTE: All direction pins must be on the same port.
|
||||
#define DIRECTION_DDR DDRA
|
||||
#define DIRECTION_PORT PORTA
|
||||
#define DIRECTION_PIN PINA
|
||||
#define X_DIRECTION_BIT 5 // MEGA2560 Digital Pin 27
|
||||
#define Y_DIRECTION_BIT 6 // MEGA2560 Digital Pin 28
|
||||
#define Z_DIRECTION_BIT 7 // MEGA2560 Digital Pin 29
|
||||
#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 STEPPERS_DISABLE_DDR DDRB
|
||||
#define STEPPERS_DISABLE_PORT PORTB
|
||||
@ -159,7 +168,7 @@
|
||||
#define COOLANT_MIST_BIT 3 // MEGA2560 Digital Pin 34
|
||||
#endif
|
||||
|
||||
// NOTE: All pinouts pins must be on the same port
|
||||
// NOTE: All pinouts pins must be on the same port and cannot be on same port as limit pins.
|
||||
#define PINOUT_DDR DDRK
|
||||
#define PINOUT_PIN PINK
|
||||
#define PINOUT_PORT PORTK
|
||||
@ -174,8 +183,8 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
#ifdef PIN_MAP_CUSTOM_PROC
|
||||
// For a custom pin map or different processor, copy and paste one of the default pin map
|
||||
#ifdef CPU_MAP_CUSTOM_PROC
|
||||
// For a custom pin map or different processor, copy and paste one of the default cpu map
|
||||
// settings above and modify it to your needs. Then, make sure the defined name is also
|
||||
// changed in the config.h file.
|
||||
#endif
|
17
defaults.h
17
defaults.h
@ -43,7 +43,8 @@
|
||||
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_FEEDRATE 250.0 // mm/min
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
|
||||
@ -51,6 +52,7 @@
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
|
||||
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
@ -81,7 +83,8 @@
|
||||
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_FEEDRATE 254.0 // mm/min (10 ipm)
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
|
||||
@ -89,6 +92,7 @@
|
||||
#define DEFAULT_REPORT_INCHES 0 // true
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
|
||||
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
@ -122,7 +126,8 @@
|
||||
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_FEEDRATE 250.0
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
|
||||
@ -130,6 +135,7 @@
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
|
||||
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
@ -171,6 +177,7 @@
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
|
||||
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
@ -202,7 +209,8 @@
|
||||
#define DEFAULT_Z_MAX_TRAVEL 150.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_FEEDRATE 1000.0 // mm/min
|
||||
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
|
||||
@ -210,6 +218,7 @@
|
||||
#define DEFAULT_REPORT_INCHES 0 // false
|
||||
#define DEFAULT_AUTO_START 1 // true
|
||||
#define DEFAULT_INVERT_ST_ENABLE 0 // false
|
||||
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
|
||||
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
|
||||
#define DEFAULT_HOMING_ENABLE 0 // false
|
||||
|
22
limits.c
22
limits.c
@ -39,11 +39,11 @@ void limits_init()
|
||||
{
|
||||
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
|
||||
|
||||
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
||||
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
|
||||
#else
|
||||
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) {
|
||||
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
|
||||
#endif
|
||||
} else {
|
||||
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
|
||||
@ -123,13 +123,14 @@ void limits_disable()
|
||||
// asynchronous stops are handled by a system level axis lock mask, which prevents the stepper
|
||||
// algorithm from executing step pulses.
|
||||
// NOTE: Only the abort runtime command can interrupt this process.
|
||||
void limits_go_home(uint8_t cycle_mask, bool approach, bool invert_pin, float homing_rate)
|
||||
void limits_go_home(uint8_t cycle_mask, bool approach, float homing_rate)
|
||||
{
|
||||
if (sys.execute & EXEC_RESET) { return; }
|
||||
uint8_t limit_state;
|
||||
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
||||
invert_pin = !invert_pin;
|
||||
#endif
|
||||
|
||||
uint8_t invert_pin;
|
||||
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { invert_pin = approach; }
|
||||
else { invert_pin = !approach; }
|
||||
|
||||
// Determine travel distance to the furthest homing switch based on user max travel settings.
|
||||
float max_travel = settings.max_travel[X_AXIS];
|
||||
if (max_travel < settings.max_travel[Y_AXIS]) { max_travel = settings.max_travel[Y_AXIS]; }
|
||||
@ -155,13 +156,14 @@ void limits_go_home(uint8_t cycle_mask, bool approach, bool invert_pin, float ho
|
||||
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
|
||||
|
||||
// Setup homing axis locks based on cycle mask.
|
||||
uint8_t axislock = (STEPPING_MASK & ~STEP_MASK);
|
||||
uint8_t axislock = 0;
|
||||
if (bit_istrue(cycle_mask,bit(X_AXIS))) { axislock |= (1<<X_STEP_BIT); }
|
||||
if (bit_istrue(cycle_mask,bit(Y_AXIS))) { axislock |= (1<<Y_STEP_BIT); }
|
||||
if (bit_istrue(cycle_mask,bit(Z_AXIS))) { axislock |= (1<<Z_STEP_BIT); }
|
||||
sys.homing_axis_lock = axislock;
|
||||
|
||||
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
|
||||
uint8_t limit_state;
|
||||
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
|
||||
st_prep_buffer(); // Prep first segment from newly planned block.
|
||||
st_wake_up(); // Initiate motion
|
||||
|
2
limits.h
2
limits.h
@ -28,7 +28,7 @@ void limits_init();
|
||||
void limits_disable();
|
||||
|
||||
// Perform one portion of the homing cycle based on the input settings.
|
||||
void limits_go_home(uint8_t cycle_mask, bool approach, bool invert_pin, float homing_rate);
|
||||
void limits_go_home(uint8_t cycle_mask, bool approach, float homing_rate);
|
||||
|
||||
// Check for soft limit violations
|
||||
void limits_soft_check(float *target);
|
||||
|
1
main.c
1
main.c
@ -88,6 +88,7 @@ int main(void)
|
||||
sys.abort = false;
|
||||
sys.execute = 0;
|
||||
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
|
||||
else { sys.auto_start = false; }
|
||||
|
||||
// Check for and report alarm state after a reset, error, or an initial power up.
|
||||
if (sys.state == STATE_ALARM) {
|
||||
|
@ -224,12 +224,12 @@ void mc_homing_cycle()
|
||||
// Perform homing routine. NOTE: Special motion case. Only system reset works.
|
||||
|
||||
// Search to engage all axes limit switches at faster homing seek rate.
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_0, true, settings.homing_seek_rate); // Search cycle 0
|
||||
#ifdef HOMING_SEARCH_CYCLE_1
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_1, true, settings.homing_seek_rate); // Search cycle 1
|
||||
#endif
|
||||
#ifdef HOMING_SEARCH_CYCLE_2
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2
|
||||
limits_go_home(HOMING_SEARCH_CYCLE_2, true, settings.homing_seek_rate); // Search cycle 2
|
||||
#endif
|
||||
|
||||
// Now in proximity of all limits. Carefully leave and approach switches in multiple cycles
|
||||
@ -237,11 +237,11 @@ void mc_homing_cycle()
|
||||
int8_t n_cycle = N_HOMING_LOCATE_CYCLE;
|
||||
while (n_cycle--) {
|
||||
// Leave all switches to release them. After cycles complete, this is machine zero.
|
||||
limits_go_home(HOMING_LOCATE_CYCLE, false, true, settings.homing_feed_rate);
|
||||
limits_go_home(HOMING_LOCATE_CYCLE, false, settings.homing_feed_rate);
|
||||
|
||||
if (n_cycle > 0) {
|
||||
// Re-approach all switches to re-engage them.
|
||||
limits_go_home(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate);
|
||||
limits_go_home(HOMING_LOCATE_CYCLE, true, settings.homing_feed_rate);
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------
|
||||
@ -291,15 +291,23 @@ void mc_homing_cycle()
|
||||
}
|
||||
|
||||
|
||||
// Auto-cycle start is a user setting that automatically begins the cycle when a user enters
|
||||
// a valid motion command either manually or by a streaming tool. This is intended as a beginners
|
||||
// feature to help new users to understand g-code. It can be disabled. Otherwise, the normal
|
||||
// operation of cycle start is manually issuing a cycle start command whenever the user is
|
||||
// ready and there is a valid motion command in the planner queue.
|
||||
// Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that
|
||||
// requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that
|
||||
// automatically begins the cycle when a user enters a valid motion command manually. This is
|
||||
// intended as a beginners feature to help new users to understand g-code. It can be disabled
|
||||
// as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is
|
||||
// manually issuing a cycle start command whenever the user is ready and there is a valid motion
|
||||
// command in the planner queue.
|
||||
// NOTE: This function is called from the main loop and mc_line() only and executes when one of
|
||||
// two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished,
|
||||
// single commands), or the planner buffer is full and ready to go.
|
||||
void mc_auto_cycle_start() { if (sys.auto_start) { st_cycle_start(); } }
|
||||
void mc_auto_cycle_start()
|
||||
{
|
||||
if (sys.auto_start) {
|
||||
st_cycle_start();
|
||||
if (bit_isfalse(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = false; } // Reset auto start per settings.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method to ready the system to reset by setting the runtime reset command and killing any
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "defaults.h"
|
||||
#include "pin_map.h"
|
||||
#include "cpu_map.h"
|
||||
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
@ -255,6 +255,7 @@ uint8_t plan_check_full_buffer()
|
||||
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
||||
void plan_synchronize()
|
||||
{
|
||||
sys.auto_start = true; // Set auto start to resume cycle after synchronize and caller completes.
|
||||
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
|
||||
|
42
protocol.c
42
protocol.c
@ -192,16 +192,10 @@ 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] == '$') {
|
||||
|
||||
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(); }
|
||||
@ -210,6 +204,27 @@ uint8_t protocol_execute_line(char *line)
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
else { report_gcode_modes(); }
|
||||
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.
|
||||
default :
|
||||
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
|
||||
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
|
||||
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 '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
|
||||
@ -235,24 +250,10 @@ uint8_t protocol_execute_line(char *line)
|
||||
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_homing_cycle();
|
||||
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++) {
|
||||
@ -289,6 +290,7 @@ uint8_t protocol_execute_line(char *line)
|
||||
return(settings_store_global_setting(parameter, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
|
||||
|
||||
} else {
|
||||
|
39
report.c
39
report.c
@ -71,7 +71,7 @@ void report_status_message(uint8_t status_code)
|
||||
case STATUS_SETTING_READ_FAIL:
|
||||
printPgmString(PSTR("EEPROM read fail. Using defaults")); break;
|
||||
case STATUS_IDLE_ERROR:
|
||||
printPgmString(PSTR("Busy or queued")); break;
|
||||
printPgmString(PSTR("Not idle")); break;
|
||||
case STATUS_ALARM_LOCK:
|
||||
printPgmString(PSTR("Alarm lock")); break;
|
||||
case STATUS_SOFT_LIMIT_ERROR:
|
||||
@ -162,24 +162,27 @@ void report_grbl_settings() {
|
||||
printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat(-settings.max_travel[Z_AXIS]); // Grbl internally store this as negative.
|
||||
printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds);
|
||||
printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate);
|
||||
printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.invert_mask);
|
||||
printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.invert_mask);
|
||||
printPgmString(PSTR(")\r\n$15=")); printInteger(settings.stepper_idle_lock_time);
|
||||
printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat(settings.junction_deviation);
|
||||
printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat(settings.arc_tolerance);
|
||||
printPgmString(PSTR(" (arc tolerance, mm)\r\n$18=")); printInteger(settings.decimal_places);
|
||||
printPgmString(PSTR(" (n-decimals, int)\r\n$19=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
|
||||
printPgmString(PSTR(" (report inches, bool)\r\n$20=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START));
|
||||
printPgmString(PSTR(" (auto start, bool)\r\n$21=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
|
||||
printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); printInteger(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (soft limits, bool)\r\n$23=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (hard limits, bool)\r\n$24=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
|
||||
printPgmString(PSTR(" (homing cycle, bool)\r\n$25=")); printInteger(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.step_invert_mask);
|
||||
printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.step_invert_mask);
|
||||
printPgmString(PSTR(")\r\n$15=")); printInteger(settings.dir_invert_mask);
|
||||
printPgmString(PSTR(" (dir port invert mask, int:")); print_uint8_base2(settings.dir_invert_mask);
|
||||
printPgmString(PSTR(")\r\n$16=")); printInteger(settings.stepper_idle_lock_time);
|
||||
printPgmString(PSTR(" (step idle delay, msec)\r\n$17=")); printFloat(settings.junction_deviation);
|
||||
printPgmString(PSTR(" (junction deviation, mm)\r\n$18=")); printFloat(settings.arc_tolerance);
|
||||
printPgmString(PSTR(" (arc tolerance, mm)\r\n$19=")); printInteger(settings.decimal_places);
|
||||
printPgmString(PSTR(" (n-decimals, int)\r\n$20=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
|
||||
printPgmString(PSTR(" (report inches, bool)\r\n$21=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START));
|
||||
printPgmString(PSTR(" (auto start, bool)\r\n$22=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
|
||||
printPgmString(PSTR(" (invert step enable, bool)\r\n$23=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
|
||||
printPgmString(PSTR(" (invert limit pins, bool)\r\n$24=")); printInteger(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (soft limits, bool)\r\n$25=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (hard limits, bool)\r\n$26=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
|
||||
printPgmString(PSTR(" (homing cycle, bool)\r\n$27=")); printInteger(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(")\r\n$26=")); printFloat(settings.homing_feed_rate);
|
||||
printPgmString(PSTR(" (homing feed, mm/min)\r\n$27=")); printFloat(settings.homing_seek_rate);
|
||||
printPgmString(PSTR(" (homing seek, mm/min)\r\n$28=")); printInteger(settings.homing_debounce_delay);
|
||||
printPgmString(PSTR(" (homing debounce, msec)\r\n$29=")); printFloat(settings.homing_pulloff);
|
||||
printPgmString(PSTR(")\r\n$28=")); printFloat(settings.homing_feed_rate);
|
||||
printPgmString(PSTR(" (homing feed, mm/min)\r\n$29=")); printFloat(settings.homing_seek_rate);
|
||||
printPgmString(PSTR(" (homing seek, mm/min)\r\n$30=")); printInteger(settings.homing_debounce_delay);
|
||||
printPgmString(PSTR(" (homing debounce, msec)\r\n$31=")); printFloat(settings.homing_pulloff);
|
||||
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
|
||||
}
|
||||
|
||||
|
43
settings.c
43
settings.c
@ -80,7 +80,8 @@ void settings_reset(bool reset_all) {
|
||||
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
|
||||
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
|
||||
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
|
||||
settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK;
|
||||
settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
|
||||
settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
|
||||
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
|
||||
}
|
||||
// New settings since last version
|
||||
@ -88,6 +89,7 @@ void settings_reset(bool reset_all) {
|
||||
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_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
|
||||
if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; }
|
||||
if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; }
|
||||
@ -176,46 +178,51 @@ uint8_t settings_store_global_setting(int parameter, float value) {
|
||||
if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
|
||||
settings.pulse_microseconds = round(value); break;
|
||||
case 13: settings.default_feed_rate = value; break;
|
||||
case 14: settings.invert_mask = trunc(value); break;
|
||||
case 15: settings.stepper_idle_lock_time = round(value); break;
|
||||
case 16: settings.junction_deviation = fabs(value); break;
|
||||
case 17: settings.arc_tolerance = value; break;
|
||||
case 18: settings.decimal_places = round(value); break;
|
||||
case 19:
|
||||
case 14: settings.step_invert_mask = trunc(value); break;
|
||||
case 15: settings.dir_invert_mask = trunc(value); break;
|
||||
case 16: settings.stepper_idle_lock_time = round(value); break;
|
||||
case 17: settings.junction_deviation = fabs(value); break;
|
||||
case 18: settings.arc_tolerance = value; break;
|
||||
case 19: settings.decimal_places = round(value); break;
|
||||
case 20:
|
||||
if (value) { settings.flags |= BITFLAG_REPORT_INCHES; }
|
||||
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
|
||||
break;
|
||||
case 20: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
case 21: // 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 21: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
case 22: // 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 22:
|
||||
case 23: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
|
||||
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
|
||||
break;
|
||||
case 24:
|
||||
if (value) {
|
||||
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
|
||||
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
|
||||
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
|
||||
break;
|
||||
case 23:
|
||||
case 25:
|
||||
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 24:
|
||||
case 26:
|
||||
if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
|
||||
else {
|
||||
settings.flags &= ~BITFLAG_HOMING_ENABLE;
|
||||
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE;
|
||||
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
|
||||
}
|
||||
break;
|
||||
case 25: settings.homing_dir_mask = trunc(value); break;
|
||||
case 26: settings.homing_feed_rate = value; break;
|
||||
case 27: settings.homing_seek_rate = value; break;
|
||||
case 28: settings.homing_debounce_delay = round(value); break;
|
||||
case 29: settings.homing_pulloff = value; break;
|
||||
case 27: settings.homing_dir_mask = trunc(value); break;
|
||||
case 28: settings.homing_feed_rate = value; break;
|
||||
case 29: settings.homing_seek_rate = value; break;
|
||||
case 30: settings.homing_debounce_delay = round(value); break;
|
||||
case 31: settings.homing_pulloff = value; break;
|
||||
default:
|
||||
return(STATUS_INVALID_STATEMENT);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
// 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 56
|
||||
#define SETTINGS_VERSION 57
|
||||
|
||||
// Define bit flag masks for the boolean settings in settings.flag.
|
||||
#define BITFLAG_REPORT_INCHES bit(0)
|
||||
@ -39,6 +39,7 @@
|
||||
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
|
||||
#define BITFLAG_HOMING_ENABLE bit(4)
|
||||
#define BITFLAG_SOFT_LIMIT_ENABLE bit(5)
|
||||
#define BITFLAG_INVERT_LIMIT_PINS bit(6)
|
||||
|
||||
// Define EEPROM memory address location values for Grbl settings and parameters
|
||||
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
|
||||
@ -64,7 +65,8 @@ typedef struct {
|
||||
float max_travel[N_AXIS];
|
||||
uint8_t pulse_microseconds;
|
||||
float default_feed_rate;
|
||||
uint8_t invert_mask;
|
||||
uint8_t step_invert_mask;
|
||||
uint8_t dir_invert_mask;
|
||||
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
|
||||
float junction_deviation;
|
||||
float arc_tolerance;
|
||||
|
191
stepper.c
191
stepper.c
@ -35,6 +35,12 @@
|
||||
#define RAMP_DECEL 2
|
||||
|
||||
|
||||
#define MAX_AMASS_LEVEL 3
|
||||
#define AMASS_LEVEL1 (F_CPU/10000)
|
||||
#define AMASS_LEVEL2 (F_CPU/5000)
|
||||
#define AMASS_LEVEL3 (F_CPU/2500)
|
||||
|
||||
|
||||
// Stores the planner block Bresenham algorithm execution data for the segments in the segment
|
||||
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
|
||||
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
|
||||
@ -57,7 +63,7 @@ typedef struct {
|
||||
uint16_t n_step; // Number of step events to be executed for this segment
|
||||
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
|
||||
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
||||
uint8_t prescaler;
|
||||
uint8_t amass_level;
|
||||
} segment_t;
|
||||
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
||||
|
||||
@ -68,14 +74,16 @@ typedef struct {
|
||||
counter_y,
|
||||
counter_z;
|
||||
|
||||
#if STEP_PULSE_DELAY > 0
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
|
||||
#endif
|
||||
|
||||
// Used by the stepper driver interrupt
|
||||
uint8_t execute_step; // Flags step execution for each interrupt.
|
||||
uint8_t step_pulse_time; // Step pulse reset time after step rise
|
||||
uint8_t out_bits; // The next stepping-bits to be output
|
||||
uint8_t step_outbits; // The next stepping-bits to be output
|
||||
uint8_t dir_outbits;
|
||||
uint32_t steps[N_AXIS];
|
||||
|
||||
uint16_t step_count; // Steps remaining in line segment motion
|
||||
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
|
||||
@ -120,6 +128,8 @@ typedef struct {
|
||||
static st_prep_t prep;
|
||||
|
||||
|
||||
static void st_config_step_timer(uint32_t cycles);
|
||||
|
||||
/* BLOCK VELOCITY PROFILE DEFINITION
|
||||
__________________________
|
||||
/| |\ _________________ ^
|
||||
@ -158,6 +168,7 @@ static st_prep_t prep;
|
||||
are shown and defined in the above illustration.
|
||||
*/
|
||||
|
||||
|
||||
// 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()
|
||||
@ -170,20 +181,22 @@ void st_wake_up()
|
||||
}
|
||||
if (sys.state & (STATE_CYCLE | STATE_HOMING)){
|
||||
// Initialize stepper output bits
|
||||
st.out_bits = settings.invert_mask;
|
||||
st.dir_outbits = settings.dir_invert_mask;
|
||||
st.step_outbits = settings.step_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.
|
||||
st.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);
|
||||
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#else // Normal operation
|
||||
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
|
||||
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#endif
|
||||
|
||||
// Enable stepper driver interrupt
|
||||
// Enable Stepper Driver Interrupt
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (1<<CS10); // Set prescaler
|
||||
TIMSK1 |= (1<<OCIE1A);
|
||||
}
|
||||
}
|
||||
@ -192,10 +205,8 @@ void st_wake_up()
|
||||
// Stepper shutdown
|
||||
void st_go_idle()
|
||||
{
|
||||
// Disable stepper driver interrupt. Allow Timer0 to finish. It will disable itself.
|
||||
// Disable Timer1 Stepper Driver Interrupt. Allow Timer0 to finish. It will disable itself.
|
||||
TIMSK1 &= ~(1<<OCIE1A);
|
||||
// TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt
|
||||
// TCCR2B = 0; // Disable Timer2
|
||||
busy = false;
|
||||
|
||||
// Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
|
||||
@ -242,18 +253,19 @@ ISR(TIMER1_COMPA_vect)
|
||||
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
||||
|
||||
// Set the direction pins a couple of nanoseconds before we step the steppers
|
||||
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (st.out_bits & DIRECTION_MASK);
|
||||
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
|
||||
|
||||
// Then pulse the stepping pins
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
st.step_bits = (STEPPING_PORT & ~STEP_MASK) | st.out_bits; // Store out_bits to prevent overwriting.
|
||||
st.step_bits = (STEPPING_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
|
||||
#else // Normal operation
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | st.out_bits;
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | st.step_outbits;
|
||||
#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 = st.step_pulse_time; // Reload timer counter
|
||||
TCCR2B = (1<<CS21); // Begin timer2. Full speed, 1/8 prescaler
|
||||
TCNT0 = st.step_pulse_time; // Reload Timer0 counter
|
||||
TCCR0B = (1<<CS21); // Begin Timer0. Full speed, 1/8 prescaler
|
||||
|
||||
busy = true;
|
||||
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
|
||||
@ -266,7 +278,7 @@ ISR(TIMER1_COMPA_vect)
|
||||
// Initialize new step segment and load number of steps to execute
|
||||
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
||||
// Initialize step segment timing per step and load number of steps to execute.
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
|
||||
// TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
|
||||
OCR1A = st.exec_segment->cycles_per_tick;
|
||||
st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
|
||||
// If the new segment starts a new planner block, initialize stepper variables and counters.
|
||||
@ -274,11 +286,15 @@ ISR(TIMER1_COMPA_vect)
|
||||
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
|
||||
st.exec_block_index = st.exec_segment->st_block_index;
|
||||
st.exec_block = &st_block_buffer[st.exec_block_index];
|
||||
st.dir_outbits = st.exec_block->direction_bits ^ settings.dir_invert_mask;
|
||||
// Initialize Bresenham line and distance counters
|
||||
st.counter_x = (st.exec_block->step_event_count >> 1);
|
||||
st.counter_y = st.counter_x;
|
||||
st.counter_z = st.counter_x;
|
||||
}
|
||||
st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
|
||||
st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level;
|
||||
st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level;
|
||||
} else {
|
||||
// Segment buffer empty. Shutdown.
|
||||
st_go_idle();
|
||||
@ -287,46 +303,43 @@ ISR(TIMER1_COMPA_vect)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset out_bits and reload direction bits
|
||||
st.out_bits = st.exec_block->direction_bits;
|
||||
// Reset step out bits.
|
||||
st.step_outbits = 0;
|
||||
|
||||
// Execute step displacement profile by Bresenham line algorithm
|
||||
if (st.step_count > 0) {
|
||||
st.step_count--; // Decrement step events count
|
||||
|
||||
st.counter_x += st.exec_block->steps[X_AXIS];
|
||||
st.counter_x += st.steps[X_AXIS];
|
||||
if (st.counter_x > st.exec_block->step_event_count) {
|
||||
st.out_bits |= (1<<X_STEP_BIT);
|
||||
st.step_outbits |= (1<<X_STEP_BIT);
|
||||
st.counter_x -= st.exec_block->step_event_count;
|
||||
if (st.out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
||||
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
||||
else { sys.position[X_AXIS]++; }
|
||||
}
|
||||
st.counter_y += st.exec_block->steps[Y_AXIS];
|
||||
st.counter_y += st.steps[Y_AXIS];
|
||||
if (st.counter_y > st.exec_block->step_event_count) {
|
||||
st.out_bits |= (1<<Y_STEP_BIT);
|
||||
st.step_outbits |= (1<<Y_STEP_BIT);
|
||||
st.counter_y -= st.exec_block->step_event_count;
|
||||
if (st.out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
||||
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
||||
else { sys.position[Y_AXIS]++; }
|
||||
}
|
||||
st.counter_z += st.exec_block->steps[Z_AXIS];
|
||||
st.counter_z += st.steps[Z_AXIS];
|
||||
if (st.counter_z > st.exec_block->step_event_count) {
|
||||
st.out_bits |= (1<<Z_STEP_BIT);
|
||||
st.step_outbits |= (1<<Z_STEP_BIT);
|
||||
st.counter_z -= st.exec_block->step_event_count;
|
||||
if (st.out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
||||
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
||||
else { sys.position[Z_AXIS]++; }
|
||||
}
|
||||
|
||||
// During a homing cycle, lock out and prevent desired axes from moving.
|
||||
if (sys.state == STATE_HOMING) { st.out_bits &= sys.homing_axis_lock; }
|
||||
}
|
||||
if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; }
|
||||
|
||||
st.step_count--; // Decrement step events count
|
||||
if (st.step_count == 0) {
|
||||
// Segment is complete. Discard current segment and advance segment indexing.
|
||||
st.exec_segment = NULL;
|
||||
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
||||
}
|
||||
|
||||
st.out_bits ^= settings.invert_mask; // Apply step port invert mask
|
||||
st.step_outbits ^= settings.step_invert_mask; // Apply step port invert mask
|
||||
busy = false;
|
||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT;
|
||||
}
|
||||
@ -335,12 +348,6 @@ ISR(TIMER1_COMPA_vect)
|
||||
/* The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
|
||||
pulse. This should always trigger before the next Timer2 COMPA interrupt and independently
|
||||
finish, if Timer2 is disabled after completing a move. */
|
||||
// ISR(TIMER0_OVF_vect)
|
||||
// {
|
||||
// STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
||||
// TCCR0B = 0; // Disable timer until needed.
|
||||
// }
|
||||
|
||||
|
||||
// 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.
|
||||
@ -348,11 +355,11 @@ ISR(TIMER1_COMPA_vect)
|
||||
// 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)
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
// Reset stepping pins (leave the direction pins)
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
||||
TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed.
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.step_invert_mask & STEP_MASK);
|
||||
TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed.
|
||||
}
|
||||
|
||||
|
||||
@ -362,7 +369,7 @@ ISR(TIMER2_OVF_vect)
|
||||
// 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)
|
||||
ISR(TIMER0_COMPA_vect)
|
||||
{
|
||||
STEPPING_PORT = st.step_bits; // Begin step pulse.
|
||||
}
|
||||
@ -387,12 +394,14 @@ void st_reset()
|
||||
// Initialize and start the stepper motor subsystem
|
||||
void st_init()
|
||||
{
|
||||
// Configure directions of interface pins
|
||||
STEPPING_DDR |= STEPPING_MASK;
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
|
||||
// Configure step and direction interface pins
|
||||
STEPPING_DDR |= STEP_MASK;
|
||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | settings.step_invert_mask;
|
||||
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
|
||||
DIRECTION_DDR |= DIRECTION_MASK;
|
||||
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | settings.dir_invert_mask;
|
||||
|
||||
// Configure Timer 1
|
||||
// Configure Timer 1: Stepper Driver Interrupt
|
||||
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
|
||||
TCCR1B |= (1<<WGM12);
|
||||
TCCR1A &= ~(1<<WGM11);
|
||||
@ -401,12 +410,13 @@ void st_init()
|
||||
TCCR1A &= ~(3<<COM1B0);
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (1<<CS10);
|
||||
|
||||
// Configure Timer 2
|
||||
TCCR2A = 0; // Normal operation
|
||||
TCCR2B = 0; // Disable timer until needed.
|
||||
TIMSK2 |= (1<<TOIE2); // Enable Timer2 Overflow interrupt
|
||||
// Configure Timer 0: Stepper Port Reset Interrupt
|
||||
TIMSK0 &= ~(1<<TOIE0);
|
||||
TCCR0A = 0; // Normal operation
|
||||
TCCR0B = 0; // Disable Timer0 until needed
|
||||
TIMSK0 |= (1<<TOIE0); // Enable Timer0 overflow interrupt
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
|
||||
TIMSK0 |= (1<<OCIE0A); // Enable Timer0 Compare Match A interrupt
|
||||
#endif
|
||||
|
||||
// Start in the idle state, but first wake up to check for keep steppers enabled option.
|
||||
@ -510,10 +520,17 @@ void st_prep_buffer()
|
||||
// when the segment buffer completes the planner block, it may be discarded immediately.
|
||||
st_prep_block = &st_block_buffer[prep.st_block_index];
|
||||
st_prep_block->direction_bits = pl_block->direction_bits;
|
||||
#ifdef ACTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
|
||||
#else
|
||||
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
|
||||
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
|
||||
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
|
||||
st_prep_block->step_event_count = pl_block->step_event_count;
|
||||
#endif
|
||||
|
||||
// Initialize segment buffer data for generating the segments.
|
||||
prep.steps_remaining = pl_block->step_event_count;
|
||||
@ -678,12 +695,27 @@ void st_prep_buffer()
|
||||
uint32_t cycles;
|
||||
float steps_remaining = prep.step_per_mm*mm_remaining;
|
||||
|
||||
float inv_rate = dt/(prep.steps_remaining-steps_remaining);
|
||||
cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (ftol_mult*step/isr_tic)
|
||||
|
||||
// Compute number of steps to execute and segment step phase correction.
|
||||
prep_segment->n_step = ceil(prep.steps_remaining)-ceil(steps_remaining);
|
||||
|
||||
float inv_rate = dt/(prep.steps_remaining-steps_remaining);
|
||||
cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
|
||||
|
||||
#ifdef ACTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
// Compute step timing and multi-axis smoothing level.
|
||||
// NOTE: Only one prescalar is required with AMASS enabled.
|
||||
if (cycles > AMASS_LEVEL1) {
|
||||
if (cycles > AMASS_LEVEL2) {
|
||||
if (cycles > AMASS_LEVEL3) { prep_segment->amass_level = 3; }
|
||||
else { prep_segment->amass_level = 2; }
|
||||
} else { prep_segment->amass_level = 1; }
|
||||
cycles >>= prep_segment->amass_level;
|
||||
prep_segment->n_step <<= prep_segment->amass_level;
|
||||
} else { prep_segment->amass_level = 0; }
|
||||
if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; }
|
||||
else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible. (4.1ms @ 16MHz)
|
||||
#endif
|
||||
|
||||
// Determine end of segment conditions. Setup initial conditions for next segment.
|
||||
if (mm_remaining > prep.mm_complete) {
|
||||
// Normal operation. Block incomplete. Distance remaining to be executed.
|
||||
@ -715,29 +747,6 @@ void st_prep_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Compile-time checks to what prescalers need to be compiled in.
|
||||
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
|
||||
prep_segment->prescaler = 1; // prescaler: 0
|
||||
prep_segment->cycles_per_tick = cycles;
|
||||
} else {
|
||||
prep_segment->prescaler = 2; // prescaler: 8
|
||||
if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
|
||||
prep_segment->cycles_per_tick = cycles >> 3;
|
||||
// } else if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
|
||||
// prep_segment->prescaler = 3; // prescaler: 64
|
||||
// prep_segment->cycles_per_tick = cycles >> 6;
|
||||
// } else if (cycles < (1UL << 24)) { // < 16777216 (1.05sec@16MHz)
|
||||
// prep_segment->prescaler = 4; // prescaler: 256
|
||||
// prep_segment->cycles_per_tick = (cycles >> 8);
|
||||
// } else {
|
||||
// prep_segment->prescaler = 5; // prescaler: 1024
|
||||
// if (cycles < (1UL << 26)) { // < 67108864 (4.19sec@16MHz)
|
||||
// prep_segment->cycles_per_tick = (cycles >> 10);
|
||||
} else { // Just set the slowest speed possible.
|
||||
prep_segment->cycles_per_tick = 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
// New step segment initialization completed. Increment segment buffer indices.
|
||||
segment_buffer_head = segment_next_head;
|
||||
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
|
||||
@ -776,3 +785,31 @@ void st_prep_buffer()
|
||||
we know when the plan is feasible in the context of what's already in the code and not
|
||||
require too much more code?
|
||||
*/
|
||||
|
||||
// static void st_config_step_timer(uint32_t cycles)
|
||||
// {
|
||||
// if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
|
||||
// prep_segment->prescaler = 1; // prescaler: 0
|
||||
// prep_segment->cycles_per_tick = cycles;
|
||||
// } else {
|
||||
// prep_segment->prescaler = 2; // prescaler: 8
|
||||
// if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
|
||||
// prep_segment->cycles_per_tick = cycles >> 3;
|
||||
//
|
||||
// // } else if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
|
||||
// // prep_segment->prescaler = 3; // prescaler: 64
|
||||
// // prep_segment->cycles_per_tick = cycles >> 6;
|
||||
// // } else if (cycles < (1UL << 24)) { // < 16777216 (1.05sec@16MHz)
|
||||
// // prep_segment->prescaler = 4; // prescaler: 256
|
||||
// // prep_segment->cycles_per_tick = (cycles >> 8);
|
||||
// // } else {
|
||||
// // prep_segment->prescaler = 5; // prescaler: 1024
|
||||
// // if (cycles < (1UL << 26)) { // < 67108864 (4.19sec@16MHz)
|
||||
// // prep_segment->cycles_per_tick = (cycles >> 10);
|
||||
//
|
||||
// } else { // Just set the slowest speed possible.
|
||||
// prep_segment->cycles_per_tick = 0xffff;
|
||||
// }
|
||||
// printString("X");
|
||||
// }
|
||||
// }
|
Loading…
Reference in New Issue
Block a user