#include "estop.h"
#include "homing.h"
#include "probing.h"
+#include "i2c.h"
#include "plan/jog.h"
#include "plan/calibrate.h"
#include "plan/buffer.h"
#include <stdlib.h>
+static char *_cmd = 0;
+
+
+static void _estop() {estop_trigger(ESTOP_USER);}
+static void _clear() {estop_clear();}
+static void _pause() {mach_request_feedhold();}
+static void _run() {mach_request_cycle_start();}
+static void _flush(uint16_t id) {mach_request_queue_flush(id);}
+static void _step() {}
+static void _report() {report_request_full();}
+static void _reboot() {hw_request_hard_reset();}
+
+
+static void command_i2c_cb(i2c_cmd_t cmd, uint8_t *data, uint8_t length) {
+ switch (cmd) {
+ case I2C_ESTOP: _estop(); break;
+ case I2C_CLEAR: _clear(); break;
+ case I2C_PAUSE: _pause(); break;
+ case I2C_RUN: _run(); break;
+ case I2C_FLUSH: _flush(*(uint16_t *)data); break;
+ case I2C_STEP: _step(); break;
+ case I2C_REPORT: _report(); break;
+ case I2C_HOME: break;
+ case I2C_REBOOT: _reboot(); break;
+ default: break;
+ }
+}
+
+
+void command_init() {
+ i2c_set_read_callback(command_i2c_cb);
+}
+
+
// Command forward declarations
// (Don't be afraid, X-Macros rock!)
#define CMD(NAME, ...) \
static char *_command_next() {
+ if (_cmd) return _cmd;
+
// Get next command
- char *cmd = usart_readline();
- if (!cmd) return 0;
+ _cmd = usart_readline();
+ if (!_cmd) return 0;
// Remove leading whitespace
- while (*cmd && isspace(*cmd)) cmd++;
+ while (*_cmd && isspace(*_cmd)) _cmd++;
// Remove trailing whitespace
- for (size_t len = strlen(cmd); len && isspace(cmd[len - 1]); len--)
- cmd[len - 1] = 0;
+ for (size_t len = strlen(_cmd); len && isspace(_cmd[len - 1]); len--)
+ _cmd[len - 1] = 0;
- return cmd;
+ return _cmd;
}
void command_callback() {
- char *cmd = _command_next();
- if (!cmd) return;
+ if (!_command_next()) return;
stat_t status = STAT_OK;
- switch (*cmd) {
+ switch (*_cmd) {
case 0: break; // Empty line
- case '{': status = vars_parser(cmd); break;
- case '$': status = command_parser(cmd); break;
- default:
- if (!cmd[1])
- switch (*cmd) {
- case '!': mach_request_feedhold(); return;
- case '~': mach_request_cycle_start(); return;
- case '%': mach_request_queue_flush(); return;
- }
+ case '{': status = vars_parser(_cmd); break;
+ case '$': status = command_parser(_cmd); break;
+ default:
if (estop_triggered()) status = STAT_MACHINE_ALARMED;
- else if (!mp_get_planner_buffer_room()) status = STAT_BUFFER_FULL;
- else if (mach_arc_active()) status = STAT_BUFFER_FULL;
- else if (calibrate_busy()) status = STAT_BUSY;
- else if (mp_jog_busy()) status = STAT_BUSY;
- else if (mach_is_homing()) status = STAT_BUSY;
- else if (mach_is_probing()) status = STAT_BUSY;
- else status = gc_gcode_parser(cmd);
+
+ else if (!mp_get_planner_buffer_room() ||
+ mach_arc_active() ||
+ mach_is_homing() ||
+ mach_is_probing() ||
+ calibrate_busy() ||
+ mp_jog_busy()) return; // Wait
+
+ // Parse and execute GCode command
+ status = gc_gcode_parser(_cmd);
}
+ _cmd = 0; // Command consumed
report_request();
if (status) status_error(status);
return STAT_OK;
}
- puts_P(PSTR("\nSpecial Character Commands:\n"
- " ! Feedhold (pause).\n"
- " ~ Start cycle (unpause).\n"
- " % Flush queue\n"
- "\n"
- "Character commands must be entered alone on a single line."));
-
puts_P(PSTR("\nLine editing:\n"
" ENTER Submit current command line.\n"
" BS Backspace, delete last character.\n"
uint8_t command_reboot(int argc, char *argv[]) {
- hw_request_hard_reset();
+ _reboot();
return 0;
}
if (end) printf_P(PSTR("\n{\"sync\": %lu}\n"), x);
return 0;
}
+
+
+uint8_t command_end_flush(int argc, char *argv[]) {
+ uint16_t id = 0;
+ char *end = 0;
+
+ if (argc == 2) {
+ id = strtoul(argv[1], &end, 0);
+ if (!end) return STAT_BAD_NUMBER_FORMAT;
+ }
+
+ mach_end_queue_flush(id);
+
+ return 0;
+}
CMD(calibrate, 0, 0, "Calibrate motors")
CMD(messages, 0, 0, "Dump all possible status messages")
CMD(sync, 1, 1, "Synchronize with queue processing")
+CMD(end_flush, 0, 1, "End a flush request, with optional ID")
} command_t;
+void command_init();
int command_find(const char *name);
int command_exec(int argc, char *argv[]);
void command_callback();
// Gcode defaults
#define GCODE_DEFAULT_UNITS MILLIMETERS // MILLIMETERS or INCHES
-#define GCODE_DEFAULT_PLANE PLANE_XY // See machine.h
-#define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59
+#define GCODE_DEFAULT_PLANE PLANE_XY // See machine.h
+#define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59
#define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS
#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_MODE
/// Buffers to reserve in planner before processing new input line
#define PLANNER_BUFFER_HEADROOM 4
+
+
+// I2C
+#define I2C_DEV TWIC
+#define I2C_ISR TWIC_TWIS_vect
+#define I2C_ADDR 0x2b
+#define I2C_MAX_DATA 8
#include "spindle.h"
#include "switch.h"
#include "report.h"
+#include "hardware.h"
+#include "homing.h"
#include "config.h"
#include "plan/planner.h"
+#include <avr/eeprom.h>
+
typedef struct {
bool triggered;
static estop_t estop = {0};
+static uint16_t estop_reason_eeprom EEMEM;
+
+
+static void _set_reason(estop_reason_t reason) {
+ eeprom_update_word(&estop_reason_eeprom, reason);
+}
+
+
+static estop_reason_t _get_reason() {
+ return eeprom_read_word(&estop_reason_eeprom);
+}
+
static void _switch_callback(switch_id_t id, bool active) {
- if (active) estop_trigger();
+ if (active) estop_trigger(ESTOP_SWITCH);
else estop_clear();
-
- report_request();
}
void estop_init() {
+ if (switch_is_active(SW_ESTOP)) _set_reason(ESTOP_SWITCH);
+ if (ESTOP_MAX <= _get_reason()) _set_reason(ESTOP_NONE);
+ estop.triggered = _get_reason() != ESTOP_NONE;
+
switch_set_callback(SW_ESTOP, _switch_callback);
- OUTCLR_PIN(FAULT_PIN); // Low
+ // Check switch state
+
+
+ // Fault signal
+ if (estop.triggered) OUTSET_PIN(FAULT_PIN); // High
+ else OUTCLR_PIN(FAULT_PIN); // Low
DIRSET_PIN(FAULT_PIN); // Output
}
}
-void estop_trigger() {
+void estop_trigger(estop_reason_t reason) {
estop.triggered = true;
// Hard stop the motors and the spindle
st_shutdown();
- mach_spindle_control(SPINDLE_OFF);
-
- // Stop and flush motion
- mach_request_feedhold();
- mach_request_queue_flush();
+ mach_spindle_estop();
// Set alarm state
mach_set_machine_state(MACHINE_ALARM);
- // Assert fault signal
- OUTSET_PIN(FAULT_PIN); // High
-}
+ // Set axes not homed
+ mach_set_not_homed();
+ // Save reason
+ _set_reason(reason);
-void estop_clear() {
- estop.triggered = false;
+ report_request();
+}
- // Clear motor errors
- for (int motor = 0; motor < MOTORS; motor++)
- motor_reset(motor);
- // Clear alarm state
- mach_set_machine_state(MACHINE_READY);
+void estop_clear() {
+ // Check if estop switch is set
+ if (switch_is_active(SW_ESTOP)) {
+ if (_get_reason() != ESTOP_SWITCH) _set_reason(ESTOP_SWITCH);
+ return; // Can't clear while estop switch is still active
+ }
// Clear fault signal
OUTCLR_PIN(FAULT_PIN); // Low
+
+ estop.triggered = false;
+
+ // Clear reason
+ _set_reason(ESTOP_NONE);
+
+ // Reboot
+ // Note, hardware.c waits until any spindle stop command has been delivered
+ hw_request_hard_reset();
}
void set_estop(bool value) {
- bool triggered = estop_triggered();
- estop.triggered = value;
+ if (value == estop_triggered()) return;
+ if (value) estop_trigger(ESTOP_USER);
+ else estop_clear();
+}
+
- if (triggered != estop_triggered()) {
- if (value) estop_trigger();
- else estop_clear();
+PGM_P get_estop_reason() {
+ switch (_get_reason()) {
+ case ESTOP_NONE: return PSTR("NONE");
+ case ESTOP_USER: return PSTR("USER");
+ case ESTOP_SWITCH: return PSTR("SWITCH");
+ case ESTOP_LIMIT: return PSTR("LIMIT");
+ case ESTOP_ALARM: return PSTR("ALARM");
+ default: return PSTR("INVALID");
}
}
#include <stdbool.h>
+typedef enum {
+ ESTOP_NONE,
+ ESTOP_USER,
+ ESTOP_SWITCH,
+ ESTOP_LIMIT,
+ ESTOP_ALARM,
+ ESTOP_MAX,
+} estop_reason_t;
+
+
void estop_init();
bool estop_triggered();
-void estop_trigger();
+void estop_trigger(estop_reason_t reason);
void estop_clear();
mach_set_absolute_override(false);
// do the program stops and ends : M0, M1, M2, M30, M60
- if (mach.gf.program_flow) {
- if (mach.gn.program_flow == PROGRAM_STOP) mach_program_stop();
- else mach_program_end();
- }
+ if (mach.gf.program_flow)
+ switch (mach.gn.program_flow) {
+ case PROGRAM_STOP: mach_program_stop(); break;
+ case PROGRAM_OPTIONAL_STOP: mach_optional_program_stop(); break;
+ case PROGRAM_PALLET_CHANGE_STOP: mach_pallet_change_stop(); break;
+ case PROGRAM_END: mach_program_end(); break;
+ }
return status;
}
case 'M':
switch ((uint8_t)value) {
- case 0: case 1: case 60:
+ case 0:
SET_MODAL(MODAL_GROUP_M4, program_flow, PROGRAM_STOP);
+ case 1:
+ SET_MODAL(MODAL_GROUP_M4, program_flow, PROGRAM_OPTIONAL_STOP);
+ case 60:
+ SET_MODAL(MODAL_GROUP_M4, program_flow, PROGRAM_PALLET_CHANGE_STOP);
case 2: case 30:
SET_MODAL(MODAL_GROUP_M4, program_flow, PROGRAM_END);
case 3: SET_MODAL(MODAL_GROUP_M7, spindle_mode, SPINDLE_CW);
char *msg = &none; // gcode message or 0 string
uint8_t block_delete_flag;
- // don't process Gcode blocks if in alarmed state
- if (mach.machine_state == MACHINE_ALARM) return STAT_MACHINE_ALARMED;
-
_normalize_gcode_block(str, &com, &msg, &block_delete_flag);
// Block delete omits the line if a / char is present in the first space
#include "hardware.h"
#include "rtc.h"
#include "usart.h"
+#include "huanyang.h"
#include "config.h"
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
+#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <stdbool.h>
#if defined(__CLOCK_EXTERNAL_8MHZ) // external 8 Mhx Xtal w/ 4x PLL = 32 Mhz
// 2-9 MHz crystal; 0.4-16 MHz XTAL w/ 16K CLK startup
OSC.XOSCCTRL = OSC_FRQRANGE_2TO9_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
- OSC.CTRL = OSC_XOSCEN_bm; // enable external crystal oscillator
- while (!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for oscillator ready
+ OSC.CTRL = OSC_XOSCEN_bm; // enable external crystal oscillator
+ while (!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for oscillator ready
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 4; // PLL source, 4x (32 MHz sys clock)
OSC.CTRL = OSC_PLLEN_bm | OSC_XOSCEN_bm; // Enable PLL & External Oscillator
/// Controller's rest handler
void hw_reset_handler() {
- if (hw.hard_reset) hw_hard_reset();
+ if (hw.hard_reset) {
+ while (huanyang_stopping() || !usart_tx_empty() || !eeprom_is_ready())
+ continue;
+ hw_hard_reset();
+ }
if (hw.bootloader) {
// TODO enable bootloader interrupt vectors and jump to BOOT_SECTION_START
}
+void mach_set_not_homed() {
+ // TODO save homed to EEPROM
+ for (int axis = 0; axis < AXES; axis++)
+ mach.homed[axis] = false;
+}
+
+
/// G28.2 homing cycle using limit switches
void mach_homing_cycle_start() {
// save relevant non-axis parameters from Gcode model
bool mach_is_homing();
+void mach_set_not_homed();
void mach_homing_cycle_start();
void mach_homing_cycle_start_no_set();
void mach_homing_callback();
bool connected;
bool changed;
+ bool estop;
machSpindleMode_t mode;
float speed;
void huanyang_set(machSpindleMode_t mode, float speed) {
- if (ha.mode != mode || ha.speed != speed) {
+ if ((ha.mode != mode || ha.speed != speed) && !ha.estop) {
if (ha.debug) STATUS_DEBUG("huanyang: mode=%d, speed=%0.2f", mode, speed);
ha.mode = mode;
}
+void huanyang_estop() {
+ huanyang_set(SPINDLE_OFF, 0);
+ huanyang_reset();
+ ha.estop = true;
+}
+
+
+bool huanyang_stopping() {
+ return ha.estop && (ha.changed || ha.next_command_cb == _update);
+}
+
uint8_t get_huanyang_id(int index) {
return ha.id;
void huanyang_set(machSpindleMode_t mode, float speed);
void huanyang_reset();
void huanyang_rtc_callback();
+void huanyang_estop();
+bool huanyang_stopping();
\******************************************************************************/
#include "i2c.h"
-#include "config.h"
#include <avr/interrupt.h>
typedef struct {
- spi_cb_t cb;
+ i2c_read_cb_t read_cb;
+ i2c_write_cb_t write_cb;
uint8_t data[I2C_MAX_DATA];
uint8_t length;
-} spi_t;
+ bool done;
+ bool write;
+} i2c_t;
-static spi_t spi = {0};
+static i2c_t i2c = {0};
+
+
+static void _i2c_reset_command() {
+ i2c.length = 0;
+ i2c.done = true;
+ i2c.write = false;
+}
+
+
+static void _i2c_end_command() {
+ if (i2c.length && !i2c.write && i2c.read_cb)
+ i2c.read_cb(*i2c.data, i2c.data + 1, i2c.length - 1);
+
+ _i2c_reset_command();
+}
+
+
+static void _i2c_command_byte(uint8_t byte) {
+ i2c.data[i2c.length++] = byte;
+}
ISR(I2C_ISR) {
- static bool first = false;
uint8_t status = I2C_DEV.SLAVE.STATUS;
// Error or collision
- if (status & (TWI_SLAVE_BUSERR_bm | TWI_SLAVE_COLL_bm)) return; // Ignore
+ if (status & (TWI_SLAVE_BUSERR_bm | TWI_SLAVE_COLL_bm)) {
+ _i2c_reset_command();
+ return; // Ignore
- // Address match
- else if ((status & TWI_SLAVE_APIF_bm) && (status & TWI_SLAVE_AP_bm)) {
+ } else if ((status & TWI_SLAVE_APIF_bm) && (status & TWI_SLAVE_AP_bm)) {
+ // START + address match
I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; // ACK address byte
- first = true;
- spi.length = 0;
+ _i2c_end_command(); // Handle repeated START
- } else if (status & TWI_SLAVE_APIF_bm) { // STOP interrupt
+ } else if (status & TWI_SLAVE_APIF_bm) {
+ // STOP
I2C_DEV.SLAVE.STATUS = TWI_SLAVE_APIF_bm; // Clear interrupt flag
- if (spi.cb) spi.cb(spi.data, spi.length);
+ _i2c_end_command();
- } else if (status & TWI_SLAVE_DIF_bm) { // Data interrupt
- if (status & TWI_SLAVE_DIR_bm) { // Write
+ } else if (status & TWI_SLAVE_DIF_bm) {
+ i2c.write = status & TWI_SLAVE_DIR_bm;
+
+ // DATA
+ if (i2c.write) { // Write
// Check if master ACKed last byte sent
- if (status & TWI_SLAVE_RXACK_bm && !first)
+ if (i2c.length && (status & TWI_SLAVE_RXACK_bm || i2c.done))
I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; // End transaction
else {
- I2C_DEV.SLAVE.DATA = 0; // Send some data
+ // Send some data
+ i2c.done = false;
+ I2C_DEV.SLAVE.DATA = i2c.write_cb(i2c.length++, &i2c.done);
I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; // Continue transaction
}
- first = false;
-
} else { // Read
- uint8_t data = I2C_DEV.SLAVE.DATA;
- if (spi.length < I2C_MAX_DATA) spi.data[spi.length++] = data;
+ _i2c_command_byte(I2C_DEV.SLAVE.DATA);
// ACK and continue transaction
I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc;
}
+static uint8_t _i2c_default_write_cb(uint8_t offset, bool *done) {
+ *done = true;
+ return 0;
+}
+
+
void i2c_init() {
+ i2c_set_write_callback(_i2c_default_write_cb);
+
I2C_DEV.SLAVE.CTRLA = TWI_SLAVE_INTLVL_HI_gc | TWI_SLAVE_DIEN_bm |
TWI_SLAVE_ENABLE_bm | TWI_SLAVE_APIEN_bm | TWI_SLAVE_PIEN_bm;
I2C_DEV.SLAVE.ADDR = I2C_ADDR << 1;
}
-void i2c_set_callback(spi_cb_t cb) {spi.cb = cb;}
+void i2c_set_read_callback(i2c_read_cb_t cb) {i2c.read_cb = cb;}
+void i2c_set_write_callback(i2c_write_cb_t cb) {i2c.write_cb = cb;}
#pragma once
-#include <stdint.h>
+#include "config.h"
+#include <stdbool.h>
-#define I2C_DEV TWIC
-#define I2C_ISR TWIC_TWIS_vect
-#define I2C_ADDR 0x2b
-#define I2C_MAX_DATA 8
+typedef enum {
+ I2C_NULL,
+ I2C_ESTOP,
+ I2C_CLEAR,
+ I2C_PAUSE,
+ I2C_OPTIONAL_PAUSE,
+ I2C_RUN,
+ I2C_FLUSH,
+ I2C_STEP,
+ I2C_REPORT,
+ I2C_HOME,
+ I2C_REBOOT,
+} i2c_cmd_t;
+
+
+typedef void (*i2c_read_cb_t)(i2c_cmd_t cmd, uint8_t *data, uint8_t length);
+typedef uint8_t (*i2c_write_cb_t)(uint8_t offset, bool *done);
-typedef void (*spi_cb_t)(uint8_t *data, uint8_t length);
void i2c_init();
-void i2c_set_callback(spi_cb_t cb);
+void i2c_set_read_callback(i2c_read_cb_t cb);
+void i2c_set_write_callback(i2c_write_cb_t cb);
#include <stdio.h>
-machSingleton_t mach = {
+machine_t mach = {
// Offsets
.offset = {
{}, // ABSOLUTE_COORDS
}
},
- .combined_state = COMBINED_READY,
.machine_state = MACHINE_READY,
// State
static void _exec_program_finalize(float *value, float *flag);
// Machine State functions
-
-/// Combines raw states into something a user might want to see
-machCombinedState_t mach_get_combined_state() {
- if (mach.cycle_state == CYCLE_OFF) mach.combined_state = mach.machine_state;
- else if (mach.cycle_state == CYCLE_PROBE)
- mach.combined_state = COMBINED_PROBE;
- else if (mach.cycle_state == CYCLE_HOMING)
- mach.combined_state = COMBINED_HOMING;
- else {
- if (mach.motion_state == MOTION_RUN) mach.combined_state = COMBINED_RUN;
- if (mach.motion_state == MOTION_HOLD) mach.combined_state = COMBINED_HOLD;
- }
-
- if (mach.machine_state == MACHINE_SHUTDOWN)
- mach.combined_state = COMBINED_SHUTDOWN;
-
- return mach.combined_state;
-}
-
uint32_t mach_get_line() {return mach.gm.line;}
machMachineState_t mach_get_machine_state() {return mach.machine_state;}
machCycleState_t mach_get_cycle_state() {return mach.cycle_state;}
/// Alarm state; send an exception report and stop processing input
stat_t mach_alarm(const char *location, stat_t code) {
status_message_P(location, STAT_LEVEL_ERROR, code, "ALARM");
- estop_trigger();
+ estop_trigger(ESTOP_ALARM);
return code;
}
-/// Clear soft alarm
+/// Clear alarm
stat_t mach_clear() {
if (mach.cycle_state == CYCLE_OFF)
mach.machine_state = MACHINE_PROGRAM_STOP;
/* G10 L2 Pn, delayed persistence
*
- * This function applies the offset to the GM model. You can also
- * use $g54x - $g59c config functions to change offsets.
+ * This function applies the offset to the GM model.
*
* It also does not reset the work_offsets which may be
* accomplished by calling mach_set_work_offsets() immediately
*/
void mach_set_coord_offsets(machCoordSystem_t coord_system, float offset[],
float flag[]) {
- if (coord_system < G54 || coord_system > COORD_SYSTEM_MAX)
- return; // you can't set G53
+ if (coord_system < G54 || MAX_COORDS <= coord_system) return;
for (int axis = 0; axis < AXES; axis++)
if (fp_TRUE(flag[axis]))
* functions set flags for these. The sequencing callback interprets the flags
* according to the following rules:
*
- * A feedhold request received during motion should be honored
- * A feedhold request received during a feedhold should be ignored and reset
- * A feedhold request received during a motion stop should be ignored and
- * reset
+ * Feedhold request received during motion is honored
+ * Feedhold request received during a feedhold is ignored and reset
+ * Feedhold request received during a motion stop is ignored and reset
*
- * A queue flush request received during motion should be ignored but not
- * reset
- * A queue flush request received during a feedhold should be deferred until
+ * Queue flush request received during motion is ignored but not reset
+ * Queue flush request received during a feedhold is deferred until
* the feedhold enters a HOLD state (i.e. until deceleration is complete)
- * A queue flush request received during a motion stop should be honored
+ * Queue flush request received during a motion stop is honored
*
- * A cycle start request received during motion should be ignored and reset
- * A cycle start request received during a feedhold should be deferred until
+ * Cycle start request received during motion is ignored and reset
+ * Cycle start request received during a feedhold is deferred until
* the feedhold enters a HOLD state (i.e. until deceleration is complete)
- * If a queue flush request is also present the queue flush should be done
- * first
- * A cycle start request received during a motion stop should be honored and
- * should start to run anything in the planner queue
+ * If a queue flush request is also present the queue flush is done first
+ * Cycle start request received during a motion stop is honored and starts
+ * to run anything in the planner queue
*/
/// Initiate a feedhold right now
void mach_request_feedhold() {mach.feedhold_requested = true;}
-void mach_request_queue_flush() {mach.queue_flush_requested = true;}
+
+
+void mach_request_queue_flush(uint16_t id) {
+ if (!id) mach.queue_flush_id = 0;
+ mach.queue_flush_request = id;
+}
+
+
+void mach_end_queue_flush(uint16_t id) {
+ if (mach.queue_flush_request) mach.queue_flush_id = id;
+}
+
+
+bool mach_queue_flushing() {
+ return mach.queue_flush_request &&
+ (int16_t)(mach.queue_flush_id - mach.queue_flush_request) < 0;
+}
+
+
void mach_request_cycle_start() {mach.cycle_start_requested = true;}
/// Process feedholds, cycle starts & queue flushes
void mach_feedhold_callback() {
- if (mach.feedhold_requested) {
+ if (mach.feedhold_requested || mach_queue_flushing()) {
if (mach.motion_state == MOTION_RUN && mach.hold_state == FEEDHOLD_OFF) {
mach_set_motion_state(MOTION_HOLD);
- mach.hold_state = FEEDHOLD_SYNC; // invokes hold from aline execution
+ mach.hold_state = FEEDHOLD_SYNC; // invokes hold from aline execution
}
mach.feedhold_requested = false;
}
- if (mach.queue_flush_requested) {
- if ((mach.motion_state == MOTION_STOP ||
- (mach.motion_state == MOTION_HOLD &&
- mach.hold_state == FEEDHOLD_HOLD)) &&
- !mach_get_runtime_busy()) {
- mach.queue_flush_requested = false;
- mach_queue_flush();
- }
- }
+ if (mach_queue_flushing() &&
+ (mach.motion_state == MOTION_STOP ||
+ (mach.motion_state == MOTION_HOLD &&
+ mach.hold_state == FEEDHOLD_HOLD)) &&
+ !mach_get_runtime_busy()) mach_queue_flush();
bool processing =
mach.hold_state == FEEDHOLD_SYNC ||
mach.hold_state == FEEDHOLD_PLAN ||
mach.hold_state == FEEDHOLD_DECEL;
- if (mach.cycle_start_requested && !mach.queue_flush_requested &&
- !processing) {
+ if (mach.cycle_start_requested && !mach_queue_flushing() && !processing) {
mach.cycle_start_requested = false;
mach.hold_state = FEEDHOLD_END_HOLD;
mach_cycle_start();
/* Program and cycle state functions
*
- * mach_program_end() implements M2 and M30
- * The END behaviors are defined by NIST 3.6.1 are:
+ * mach_program_end() implements M2 and M30. End behaviors are defined by
+ * NIST 3.6.1 are:
+ *
* 1. Axis offsets are set to zero (like G92.2) and origin offsets are set
* to the default (like G54)
* 2. Selected plane is set to PLANE_XY (like G17)
* 9. Coolant is turned off (like M9)
*
* mach_program_end() implments things slightly differently:
+ *
* 1. Axis offsets are set to G92.1 CANCEL offsets
* (instead of using G92.2 SUSPEND Offsets)
- * Set default coordinate system (uses $gco, not G54)
- * 2. Selected plane is set to default plane ($gpl)
- * (instead of setting it to G54)
+ * Set default coordinate system
+ * 2. Selected plane is set to default plane
* 3. Distance mode is set to MODE_ABSOLUTE (like G90)
* 4. Feed rate mode is set to UNITS_PER_MINUTE (like G94)
* 5. Not implemented
* 7. The spindle is stopped (like M5)
* 8. Motion mode is canceled like G80 (not set to G1)
* 9. Coolant is turned off (like M9)
- * + Default INCHES or MM units mode is restored ($gun)
+ * + Default INCHES or MM units mode is restored
*/
static void _exec_program_finalize(float *value, float *flag) {
- mach.machine_state = (uint8_t)value[0];
+ mach.machine_state = (machMachineState_t)value[0];
mach_set_motion_state(MOTION_STOP);
if (mach.cycle_state == CYCLE_MACHINING)
mach.cycle_state = CYCLE_OFF; // don't end cycle if homing, probing, etc
/// M1
void mach_optional_program_stop() {
- float value[AXES] = {MACHINE_PROGRAM_STOP};
- mp_queue_command(_exec_program_finalize, value, value);
+ // TODO Check for user stop signal
+ mach_program_stop();
+}
+
+
+/// M60
+void mach_pallet_change_stop() {
+ // TODO Emit pallet change signal
+ mach_program_stop();
}
/* Machine state model
*
- * The following main variables track machine state and state
- * transitions.
- * - mach.machine_state - overall state of machine and program execution
- * - mach.cycle_state - what cycle the machine is executing (or none)
- * - mach.motion_state - state of movement
+ * The following main variables track machine state and state transitions.
*
- * Allowed states and combined states:
+ * machine_state - overall state of machine and program execution
+ * cycle_state - what cycle the machine is executing (or none)
+ * motion_state - state of movement
*
- * MACHINE STATE CYCLE STATE MOTION_STATE COMBINED_STATE (FYI)
- * ------------- ------------ ------------- --------------------
- * MACHINE_UNINIT na na (U)
- * MACHINE_READY CYCLE_OFF MOTION_STOP (ROS) RESET-OFF-STOP
- * MACHINE_PROG_STOP CYCLE_OFF MOTION_STOP (SOS) STOP-OFF-STOP
- * MACHINE_PROG_END CYCLE_OFF MOTION_STOP (EOS) END-OFF-STOP
+ * Allowed states:
*
- * MACHINE_CYCLE CYCLE_STARTED MOTION_STOP (CSS) CYCLE-START-STOP
- * MACHINE_CYCLE CYCLE_STARTED MOTION_RUN (CSR) CYCLE-START-RUN
- * MACHINE_CYCLE CYCLE_STARTED MOTION_HOLD (CSH) CYCLE-START-HOLD
- * MACHINE_CYCLE CYCLE_STARTED MOTION_END_HOLD (CSE) CYCLE-START-END_HOLD
+ * MACHINE STATE CYCLE STATE MOTION_STATE
+ * ------------- ------------ -------------
+ * MACHINE_READY CYCLE_OFF MOTION_STOP
+ * MACHINE_PROG_STOP CYCLE_OFF MOTION_STOP
+ * MACHINE_PROG_END CYCLE_OFF MOTION_STOP
*
- * MACHINE_CYCLE CYCLE_HOMING MOTION_STOP (CHS) CYCLE-HOMING-STOP
- * MACHINE_CYCLE CYCLE_HOMING MOTION_RUN (CHR) CYCLE-HOMING-RUN
- * MACHINE_CYCLE CYCLE_HOMING MOTION_HOLD (CHH) CYCLE-HOMING-HOLD
- * MACHINE_CYCLE CYCLE_HOMING MOTION_END_HOLD (CHE) CYCLE-HOMING-END_HOLD
+ * MACHINE_CYCLE CYCLE_STARTED MOTION_STOP
+ * MACHINE_CYCLE CYCLE_STARTED MOTION_RUN
+ * MACHINE_CYCLE CYCLE_STARTED MOTION_HOLD
+ *
+ * MACHINE_CYCLE CYCLE_HOMING MOTION_STOP
+ * MACHINE_CYCLE CYCLE_HOMING MOTION_RUN
+ * MACHINE_CYCLE CYCLE_HOMING MOTION_HOLD
*/
-/// check alignment with messages in config.c / msg_stat strings
typedef enum {
- COMBINED_INITIALIZING, // machine is initializing
- COMBINED_READY, // machine is ready for use. Also null move STOP state
- COMBINED_ALARM, // machine in soft alarm state
- COMBINED_PROGRAM_STOP, // program stop or no more blocks
- COMBINED_PROGRAM_END, // program end
- COMBINED_RUN, // motion is running
- COMBINED_HOLD, // motion is holding
- COMBINED_PROBE, // probe cycle active
- COMBINED_CYCLE, // machine is running (cycling)
- COMBINED_HOMING, // homing is treated as a cycle
- COMBINED_SHUTDOWN, // machine in hard alarm state (shutdown)
-} machCombinedState_t;
-
-
-typedef enum {
- MACHINE_INITIALIZING, // machine is initializing
MACHINE_READY, // machine is ready for use
- MACHINE_ALARM, // machine in soft alarm state
+ MACHINE_ALARM, // machine in alarm state
MACHINE_PROGRAM_STOP, // program stop or no more blocks
MACHINE_PROGRAM_END, // program end
MACHINE_CYCLE, // machine is running (cycling)
- MACHINE_SHUTDOWN, // machine in hard alarm state (shutdown)
} machMachineState_t;
typedef enum {
MOTION_STOP, // motion has stopped
MOTION_RUN, // machine is in motion
- MOTION_HOLD // feedhold in progress
+ MOTION_HOLD, // feedhold in progress
} machMotionState_t;
FEEDHOLD_PLAN, // replan blocks for feedhold
FEEDHOLD_DECEL, // decelerate to hold point
FEEDHOLD_HOLD, // holding
- FEEDHOLD_END_HOLD // end hold (transient state to OFF)
+ FEEDHOLD_END_HOLD, // end hold (transient state to OFF)
} machFeedholdState_t;
typedef enum { // applies to mach.homing_state
- HOMING_NOT_HOMED, // machine is not homed (0=false)
- HOMING_HOMED, // machine is homed (1=true)
- HOMING_WAITING // machine waiting to be homed
+ HOMING_NOT_HOMED, // machine is not homed
+ HOMING_HOMED, // machine is homed
+ HOMING_WAITING, // machine waiting to be homed
} machHomingState_t;
} machProbeState_t;
-/* The difference between NextAction and MotionMode is that NextAction is
- * used by the current block, and may carry non-modal commands, whereas
- * MotionMode persists across blocks (as G modal group 1)
+/* The difference between machNextAction_t and machMotionMode_ is that
+ * machNextAction_t is used by the current block, and may carry non-modal
+ * commands, whereas machMotionMode_t persists across blocks as G modal group 1
*/
/// these are in order to optimized CASE statement
NEXT_ACTION_SUSPEND_ORIGIN_OFFSETS, // G92.2
NEXT_ACTION_RESUME_ORIGIN_OFFSETS, // G92.3
NEXT_ACTION_DWELL, // G4
- NEXT_ACTION_STRAIGHT_PROBE // G38.2
+ NEXT_ACTION_STRAIGHT_PROBE, // G38.2
} machNextAction_t;
#define MODAL_GROUP_COUNT (MODAL_GROUP_M9 + 1)
-// Note 1: Our G0 omits G4,G30,G53,G92.1,G92.2,G92.3 as these have no axis
+// Note 1: Our G0 omits G4, G30, G53, G92.1, G92.2, G92.3 as these have no axis
// components to error check
typedef enum { // plane - translates to:
- // axis_0 axis_1 axis_2
- PLANE_XY, // G17 X Y Z
- PLANE_XZ, // G18 X Z Y
- PLANE_YZ // G19 Y Z X
+ // axis_0 axis_1 axis_2
+ PLANE_XY, // G17 X Y Z
+ PLANE_XZ, // G18 X Z Y
+ PLANE_YZ, // G19 Y Z X
} machPlane_t;
typedef enum {
INCHES, // G20
MILLIMETERS, // G21
- DEGREES // ABC axes (this value used for displays only)
+ DEGREES, // ABC axes (this value used for displays only)
} machUnitsMode_t;
typedef enum {
ABSOLUTE_COORDS, // machine coordinate system
- G54, // G54 coordinate system
- G55, // G55 coordinate system
- G56, // G56 coordinate system
- G57, // G57 coordinate system
- G58, // G58 coordinate system
- G59 // G59 coordinate system
+ G54, G55, G56, G57, G58, G59,
+ MAX_COORDS,
} machCoordSystem_t;
-#define COORD_SYSTEM_MAX G59 // set this manually to the last one
-
/// G Modal Group 13
typedef enum {
/// G61 - hits corners but does not stop if it does not need to.
PATH_EXACT_PATH,
PATH_EXACT_STOP, // G61.1 - stops at all corners
- PATH_CONTINUOUS // G64 and typically the default mode
+ PATH_CONTINUOUS, // G64 and typically the default mode
} machPathControlMode_t;
typedef enum {
ABSOLUTE_MODE, // G90
- INCREMENTAL_MODE // G91
+ INCREMENTAL_MODE, // G91
} machDistanceMode_t;
typedef enum {
INVERSE_TIME_MODE, // G93
UNITS_PER_MINUTE_MODE, // G94
- UNITS_PER_REVOLUTION_MODE // G95 (unimplemented)
+ UNITS_PER_REVOLUTION_MODE, // G95 (unimplemented)
} machFeedRateMode_t;
ORIGIN_OFFSET_SET, // G92 - set origin offsets
ORIGIN_OFFSET_CANCEL, // G92.1 - zero out origin offsets
ORIGIN_OFFSET_SUSPEND, // G92.2 - do not apply offsets, but preserve values
- ORIGIN_OFFSET_RESUME // G92.3 - resume application of the suspended offsets
+ ORIGIN_OFFSET_RESUME, // G92.3 - resume application of the suspended offsets
} machOriginOffset_t;
typedef enum {
PROGRAM_STOP,
- PROGRAM_END
+ PROGRAM_OPTIONAL_STOP,
+ PROGRAM_PALLET_CHANGE_STOP,
+ PROGRAM_END,
} machProgramFlow_t;
typedef enum {
SPINDLE_OFF,
SPINDLE_CW,
- SPINDLE_CCW
+ SPINDLE_CCW,
} machSpindleMode_t;
COOLANT_OFF, // all coolant off
COOLANT_ON, // request coolant on or indicate both coolants are on
COOLANT_MIST, // indicates mist coolant on
- COOLANT_FLOOD // indicates flood coolant on
+ COOLANT_FLOOD, // indicates flood coolant on
} machCoolantState_t;
/// used for spindle and arc dir
typedef enum {
DIRECTION_CW,
- DIRECTION_CCW
+ DIRECTION_CCW,
} machDirection_t;
AXIS_STANDARD, // axis in coordinated motion w/standard behaviors
AXIS_INHIBITED, // axis is computed but not activated
AXIS_RADIUS, // rotary axis calibrated to circumference
- AXIS_MODE_MAX
+ AXIS_MODE_MAX,
} machAxisMode_t; // ordering must be preserved.
bool spindle_override_enable; // true = override enabled
machMotionMode_t motion_mode; // Group 1 modal motion
- machPlane_t select_plane; // G17, G18, G19
+ machPlane_t select_plane; // G17, G18, G19
machUnitsMode_t units_mode; // G20, G21
machCoordSystem_t coord_system; // G54-G59 - select coordinate system 1-9
bool absolute_override; // G53 true = move in machine coordinates
typedef struct { // struct to manage mach globals and cycles
- // coordinate systems and offsets absolute (G53) + G54,G55,G56,G57,G58,G59
+ // coordinate systems & offsets absolute (G53) + G54, G55, G56, G57, G58, G59
float offset[COORDS + 1][AXES];
- float origin_offset[AXES]; // G92 offsets
- bool origin_offset_enable; // G92 offsets enabled/disabled
+ float origin_offset[AXES]; // G92 offsets
+ bool origin_offset_enable; // G92 offsets enabled/disabled
- float position[AXES]; // model position (not used in gn or gf)
- float g28_position[AXES]; // stored machine position for G28
- float g30_position[AXES]; // stored machine position for G30
+ float position[AXES]; // model position (not used in gn or gf)
+ float g28_position[AXES]; // stored machine position for G28
+ float g30_position[AXES]; // stored machine position for G30
// settings for axes X,Y,Z,A B,C
AxisConfig_t a[AXES];
- machCombinedState_t combined_state; // combination of states for display
machMachineState_t machine_state;
machCycleState_t cycle_state;
machMotionState_t motion_state;
- machFeedholdState_t hold_state; // hold: feedhold sub-state machine
- machHomingState_t homing_state; // home: homing cycle sub-state machine
+ machFeedholdState_t hold_state; // hold: feedhold sub-state machine
+ machHomingState_t homing_state; // home: homing cycle sub-state machine
bool homed[AXES]; // individual axis homing flags
machProbeState_t probe_state;
float probe_results[AXES]; // probing results
- bool feedhold_requested; // feedhold character received
- bool queue_flush_requested; // queue flush character received
- bool cycle_start_requested; // cycle start character received
+ bool feedhold_requested;
+ uint16_t queue_flush_request; // queue flush request id
+ uint16_t queue_flush_id; // latest queue flush request id
+ bool cycle_start_requested;
// Model states
MoveState_t ms;
GCodeState_t gm; // core gcode model state
GCodeState_t gn; // gcode input values
GCodeState_t gf; // gcode input flags
-} machSingleton_t;
+} machine_t;
-extern machSingleton_t mach; // machine controller singleton
+extern machine_t mach; // machine controller singleton
// Model state getters and setters
uint32_t mach_get_line();
-machCombinedState_t mach_get_combined_state();
machMachineState_t mach_get_machine_state();
machCycleState_t mach_get_cycle_state();
machMotionState_t mach_get_motion_state();
// Program Functions (4.3.10)
void mach_request_feedhold();
-void mach_request_queue_flush();
+void mach_request_queue_flush(uint16_t id);
+void mach_end_queue_flush(uint16_t id);
void mach_request_cycle_start();
void mach_feedhold_callback();
void mach_feedhold();
void mach_program_stop();
void mach_optional_program_stop();
+void mach_pallet_change_stop();
void mach_program_end();
// Cycles
machine_init(); // gcode machine
vars_init(); // configuration variables
estop_init(); // emergency stop handler
+ command_init();
sei(); // enable interrupts
#include "machine.h"
#include "stepper.h"
#include "motor.h"
+#include "estop.h"
#include <string.h>
#include <stdbool.h>
/// Use this function to sync to the queue. If you wait until it returns
/// FALSE you know the queue is empty and the motors have stopped.
uint8_t mp_get_runtime_busy() {
+ if (estop_triggered()) return false;
return st_runtime_isbusy() || mr.move_state == MOVE_RUN;
}
float max_duty;
bool reverse;
bool enable_invert;
+ bool estop;
} spindle_t;
.max_duty = SPINDLE_MAX_DUTY,
.reverse = SPINDLE_POLARITY,
.enable_invert = false,
+ .estop = false,
};
static void _spindle_set_pwm(machSpindleMode_t mode, float speed) {
- if (mode == SPINDLE_OFF || speed < spindle.min_rpm) {
+ if (mode == SPINDLE_OFF || speed < spindle.min_rpm || spindle.estop) {
TIMER_PWM.CTRLA = 0;
return;
}
}
+void pwm_spindle_estop() {
+ spindle.estop = true;
+ _spindle_set_pwm(SPINDLE_OFF, 0);
+}
+
+
// TODO implement these
float get_max_spin(int index) {
return 0;
void pwm_spindle_init();
void pwm_spindle_set(machSpindleMode_t mode, float speed);
+void pwm_spindle_estop();
}
+void mach_spindle_estop() {
+ switch (spindle_type) {
+ case SPINDLE_TYPE_PWM: pwm_spindle_estop(); break;
+ case SPINDLE_TYPE_HUANYANG: huanyang_estop(); break;
+ }
+}
+
+
/// Queue the S parameter to the planner buffer
void mach_set_spindle_speed(float speed) {
float value[AXES] = {speed};
void mach_spindle_init();
void mach_set_spindle_speed(float speed); // S parameter
-void mach_spindle_control(machSpindleMode_t spindle_mode); // M3, M4, M5
+void mach_spindle_control(machSpindleMode_t spindle_mode); // M3, M4, M5
+void mach_spindle_estop();
#include "plan/command.h"
#include "motor.h"
#include "hardware.h"
+#include "estop.h"
#include "util.h"
#include "cpp_magic.h"
void st_shutdown() {
for (int motor = 0; motor < MOTORS; motor++)
motor_enable(motor, false);
+
+ st.dwell = 0;
+ st.move_type = MOVE_TYPE_NULL;
}
/// ADC channel 0 triggered by load ISR as a "software" interrupt.
ISR(ADCB_CH0_vect) {
mp_exec_move();
+ ADCB_CH0_INTCTRL = 0;
st.requesting = false;
}
for (int motor = 0; motor < MOTORS; motor++)
motor_end_move(motor);
+ if (estop_triggered()) {
+ st.move_type = MOVE_TYPE_NULL;
+ return;
+ }
+
// If the next move is not ready try to load it
if (!st.move_ready) {
_request_exec_move();
#define RING_BUF_SIZE USART_RX_RING_BUF_SIZE
#include "ringbuf.def"
-static int usart_flags = USART_CRLF | USART_ECHO;
+static int usart_flags = USART_CRLF;
static void _set_dre_interrupt(bool enable) {
#define USART_TX_RING_BUF_SIZE 256
#define USART_RX_RING_BUF_SIZE 256
-#define USART_ECHO_RING_BUF_SIZE 32
enum {
USART_BAUD_9600,
typedef uint8_t flags_t;
typedef const char *string;
+typedef PGM_P pstring;
// Format strings
// Type names
static const char bool_name [] PROGMEM = "<bool>";
#define TYPE_NAME(TYPE) static const char TYPE##_name [] PROGMEM = "<" #TYPE ">"
-MAP(TYPE_NAME, SEMI, flags_t, string, float, int8_t, uint8_t, uint16_t,
+MAP(TYPE_NAME, SEMI, flags_t, string, pstring, float, int8_t, uint8_t, uint16_t,
int32_t);
// String
-static void var_print_string(const char *s) {
+static void var_print_string(string s) {
printf_P(PSTR("\"%s\""), s);
}
+// Program string
+static void var_print_pstring(pstring s) {
+ printf_P(PSTR("\"%S\""), s);
+}
+
// Flags
extern void print_status_flags(uint8_t x);
VAR(hw_id, "id", string, 0, 0, 0, "Hardware ID")
VAR(echo, "ec", bool, 0, 1, 0, "Enable or disable echo")
VAR(estop, "es", bool, 0, 1, 0, "Emergency stop")
+VAR(estop_reason, "er", pstring, 0, 0, 0, "Emergency stop reason")
VAR(line, "ln", int32_t, 0, 0, 0, "Last GCode line executed")
VAR(queue, "q", uint16_t, 0, 0, 0, "Space in planner queue")