diff --git a/changelog.md b/changelog.md index 2ba62678..ff4d7a99 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,23 @@ ## grblHAL changelog -2021-03-14: +Build 20210515: + +* Fixed G43 "bug" by changing to LinuxCNC specification. NIST specification is ambiguous. +* Added acceleration override handling, to be used by OpenPNP plugin M-code. + +Build 20210505: + +* Some bug fixes: relative canned cycles repeat error, comment handling... +* Check mode changes to allow use with SD card streaming. +* Internal buffer changes for reducing RAM footprint. Some HAL extensions. + +Build 20210314: * Added support for tool change protocol to networking protocols using shared stream buffer. * Added `$S` system command for toggling single step mode, when enabled cycle start has to be issued after each block. * Some bug fixes. -2021-02-07: +Build 20210207: * Added `#define BOARD_MY_MACHINE` option in _my_machine.h_ for building using _my_machine-map.c_, this for simpler handling of user defined pin mappings. _my_machine-map.c_ is __*not*__ part of the distributed source and must by added to the project by the user before enabled, typically by copying an existing map file. diff --git a/drivers/ESP32/main/CMakeLists.txt b/drivers/ESP32/main/CMakeLists.txt index b9231541..f2e4d0d8 100644 --- a/drivers/ESP32/main/CMakeLists.txt +++ b/drivers/ESP32/main/CMakeLists.txt @@ -8,6 +8,7 @@ OPTION(BOARD_BDRING_V4 "Compile for bdring v4 3-axis board" ON) OPTION(BOARD_BDRING_I2S6A "Compile for bdring 6-axis I2S board - untested!" OFF) OPTION(BOARD_CNC_BOOSTERPACK "Compile for CNC BoosterPack" OFF) OPTION(BOARD_ESPDUINO32 "Compile for ESPDUINO-32 Wemos D1 R32" OFF) +OPTION(BOARD_SOURCERABBIT_4AXIS "Compile for SourceRabbit 4-axis board" OFF) OPTION(BOARD_MY_MACHINE "Compile for my_machine_map.h" OFF) # The following plugins/options are supported: @@ -36,7 +37,7 @@ OPTION(EEPROM "Compile with I2C EEPROM support" OFF) OPTION(FRAM "Compile with I2C FRAM support" OFF) OPTION(NOPROBE "Compile without probe support" OFF) -set(SDCARD_SOURCE sdcard/sdcard.c) +set(SDCARD_SOURCE sdcard/sdcard.c sdcard/ymodem.c) set(KEYPAD_SOURCE keypad/keypad.c) set(TRINAMIC_SOURCE motors/trinamic.c trinamic/tmc2130.c trinamic/tmc2130hal.c trinamic/common.c trinamic/tmc_interface.c) set(NETWORKING_SOURCE wifi.c dns_server.c web/backend.c web/upload.c networking/TCPStream.c networking/WsStream.c networking/base64.c networking/sha1.c networking/urldecode.c networking/strutils.c networking/utils.c networking/multipartparser.c ) @@ -104,8 +105,7 @@ if(EEPROM OR FRAM OR BOARD_CNC_BOOSTERPACK) list (APPEND SRCS ${EEPROM_SOURCE}) endif() -idf_component_register(SRCS "${SRCS}" - INCLUDE_DIRS ".") +idf_component_register(SRCS "${SRCS}" INCLUDE_DIRS ".") target_compile_definitions("${COMPONENT_LIB}" PUBLIC GRBL_ESP32) target_compile_definitions("${COMPONENT_LIB}" PUBLIC OVERRIDE_MY_MACHINE) @@ -120,6 +120,8 @@ elseif(BOARD_ESPDUINO32) target_compile_definitions("${COMPONENT_LIB}" PUBLIC BOARD_ESPDUINO32) elseif(BOARD_CNC_BOOSTERPACK) target_compile_definitions("${COMPONENT_LIB}" PUBLIC BOARD_CNC_BOOSTERPACK) +elseif(BOARD_SOURCERABBIT_4AXIS) +target_compile_definitions("${COMPONENT_LIB}" PUBLIC BOARD_SOURCERABBIT_4AXIS) elseif(BOARD_MY_MACHINE) target_compile_definitions("${COMPONENT_LIB}" PUBLIC BOARD_MY_MACHINE) endif() @@ -190,6 +192,7 @@ unset(BOARD_BDRING_V4 CACHE) unset(BOARD_BDRING_I2S6A CACHE) unset(BOARD_CNC_BOOSTERPACK CACHE) unset(BOARD_ESPDUINO32 CACHE) +unset(BOARD_SOURCERABBIT_4AXIS CACHE) unset(BOARD_MY_MACHINE CACHE) unset(Networking CACHE) diff --git a/drivers/ESP32/main/bluetooth.c b/drivers/ESP32/main/bluetooth.c index 24e8d88d..0eb5d4d4 100644 --- a/drivers/ESP32/main/bluetooth.c +++ b/drivers/ESP32/main/bluetooth.c @@ -152,7 +152,7 @@ bool BTStreamPutC (const char c) if(txbuffer.head < BT_TX_BUFFER_SIZE) txbuffer.data[txbuffer.head++] = c; - if(c == '\n') { + if(c == ASCII_LF) { enqueue_tx_chunk(txbuffer.head, (uint8_t *)txbuffer.data); txbuffer.head = 0; } @@ -261,7 +261,7 @@ static void esp_spp_cb (esp_spp_cb_event_t event, esp_spp_cb_param_t *param) case ESP_SPP_INIT_EVT: esp_bt_dev_set_device_name(bluetooth.device_name); - esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); esp_spp_start_srv(ESP_SPP_SEC_AUTHENTICATE, ESP_SPP_ROLE_SLAVE, 0, bluetooth.service_name); break; diff --git a/drivers/ESP32/main/bluetooth.h b/drivers/ESP32/main/bluetooth.h index b7cbd537..2dd6e65d 100644 --- a/drivers/ESP32/main/bluetooth.h +++ b/drivers/ESP32/main/bluetooth.h @@ -5,7 +5,7 @@ Part of grblHAL - Copyright (c) 2018-2020 Terje Io + Copyright (c) 2018-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,6 +34,7 @@ char *bluetooth_get_client_mac (void); uint32_t BTStreamAvailable (void); uint16_t BTStreamRXFree (void); int16_t BTStreamGetC (void); +bool BTStreamPutC (const char c); bool BTStreamSuspendInput (bool suspend); void BTStreamWriteS (const char *data); void BTStreamFlush (void); diff --git a/drivers/ESP32/main/driver.c b/drivers/ESP32/main/driver.c index f940d8f1..8c9ab58c 100644 --- a/drivers/ESP32/main/driver.c +++ b/drivers/ESP32/main/driver.c @@ -157,6 +157,7 @@ const io_stream_t serial_stream = { .read = serialRead, .write = serialWriteS, .write_all = serialWriteS, + .write_char = serialPutC, .get_rx_buffer_available = serialRXFree, .reset_read_buffer = serialFlush, .cancel_read_buffer = serialCancel, @@ -187,6 +188,7 @@ const io_stream_t telnet_stream = { .read = TCPStreamGetC, .write = TCPStreamWriteS, .write_all = tcpStreamWriteS, + .write_char = TCPStreamPutC, .get_rx_buffer_available = TCPStreamRxFree, .reset_read_buffer = TCPStreamRxFlush, .cancel_read_buffer = TCPStreamRxCancel, @@ -201,6 +203,7 @@ const io_stream_t websocket_stream = { .read = WsStreamGetC, .write = WsStreamWriteS, .write_all = tcpStreamWriteS, + .write_char = WsStreamPutC, .get_rx_buffer_available = WsStreamRxFree, .reset_read_buffer = WsStreamRxFlush, .cancel_read_buffer = WsStreamRxCancel, @@ -223,6 +226,7 @@ const io_stream_t bluetooth_stream = { .read = BTStreamGetC, .write = BTStreamWriteS, .write_all = btStreamWriteS, + .write_char = BTStreamPutC, .get_rx_buffer_available = BTStreamRXFree, .reset_read_buffer = BTStreamFlush, .cancel_read_buffer = BTStreamCancel, @@ -1474,7 +1478,7 @@ bool driver_init (void) serialInit(); hal.info = "ESP32"; - hal.driver_version = "210314"; + hal.driver_version = "210423"; #ifdef BOARD_NAME hal.board = BOARD_NAME; #endif @@ -1622,7 +1626,7 @@ bool driver_init (void) // grbl_esp32_if_init(); - my_plugin_init(); +// my_plugin_init(); causes run time crash for some silly reason // no need to move version check before init - compiler will fail any mismatch for existing entries return hal.version == 8; diff --git a/drivers/ESP32/main/driver.h b/drivers/ESP32/main/driver.h index 6a2cc92c..b9bde447 100644 --- a/drivers/ESP32/main/driver.h +++ b/drivers/ESP32/main/driver.h @@ -270,6 +270,8 @@ typedef struct { #include "bdring_i2s_6_axis_map.h" #elif defined(BOARD_ESPDUINO32) #include "espduino-32_wemos_d1_r32_uno_map.h" +#elif defined(BOARD_SOURCERABBIT_4AXIS) + #include "sourcerabbit_4axis.h" #elif defined(BOARD_MY_MACHINE) #include "my_machine_map.h" #else // default board - NOTE: NOT FINAL VERSION! diff --git a/drivers/ESP32/main/grbl/config.h b/drivers/ESP32/main/grbl/config.h index 2c1662b7..ef0e9192 100644 --- a/drivers/ESP32/main/grbl/config.h +++ b/drivers/ESP32/main/grbl/config.h @@ -579,6 +579,7 @@ // for professional CNC machines, regardless of where the limit switches are located. Set this // define to 1 to force Grbl to always set the machine origin at the homed location despite switch orientation. //#define HOMING_FORCE_SET_ORIGIN // Default disabled. Uncomment to enable. +#define HOMING_FORCE_SET_ORIGIN 1 // To prevent the homing cycle from racking the dual axis, when one limit triggers before the // other due to switch failure or noise, the homing cycle will automatically abort if the second diff --git a/drivers/ESP32/main/grbl/crossbar.h b/drivers/ESP32/main/grbl/crossbar.h index 1b650b21..70949a96 100644 --- a/drivers/ESP32/main/grbl/crossbar.h +++ b/drivers/ESP32/main/grbl/crossbar.h @@ -24,6 +24,60 @@ #ifndef _CROSSBAR_H_ #define _CROSSBAR_H_ +typedef enum { + Input_Probe = 0, + Input_Reset, + Input_FeedHold, + Input_CycleStart, + Input_SafetyDoor, + Input_LimitsOverride, + Input_EStop, + Input_ModeSelect, + Input_LimitX, + Input_LimitX_Max, + Input_LimitY, + Input_LimitY_Max, + Input_LimitZ, + Input_LimitZ_Max, + Input_LimitA, + Input_LimitA_Max, + Input_LimitB, + Input_LimitB_Max, + Input_LimitC, + Input_LimitC_Max, + Input_KeypadStrobe, + Input_QEI_A, + Input_QEI_B, + Input_QEI_Select, + Input_QEI_Index, + Input_SpindleIndex, + Input_Aux0, + Input_Aux1, + Input_Aux2, + Input_Aux3 +} pin_function_t; + +typedef enum { + IRQ_Mode_None = 0b00, + IRQ_Mode_Change = 0b01, + IRQ_Mode_Rising = 0b10, + IRQ_Mode_Falling = 0b11 +} pin_irq_mode_t; + +typedef enum { + PinGroup_Control = (1<<0), + PinGroup_Limit = (1<<1), + PinGroup_Probe = (1<<2), + PinGroup_Keypad = (1<<3), + PinGroup_MPG = (1<<4), + PinGroup_QEI = (1<<5), + PinGroup_QEI_Select = (1<<6), + PinGroup_SpindlePulse = (1<<7), + PinGroup_SpindleIndex = (1<<8), + PinGroup_AuxInput = (1<<9), + PinGroup_AuxOutput = (1<<10) +} pin_group_t; + typedef bool (*xbar_get_value_ptr)(void); typedef void (*xbar_set_value_ptr)(bool on); typedef void (*xbar_event_ptr)(bool on); diff --git a/drivers/ESP32/main/grbl/errors.h b/drivers/ESP32/main/grbl/errors.h index d47fb52f..ea7bcb4b 100644 --- a/drivers/ESP32/main/grbl/errors.h +++ b/drivers/ESP32/main/grbl/errors.h @@ -83,6 +83,7 @@ typedef enum { Status_MotorFault = 51, Status_SettingValueOutOfRange = 52, Status_SettingDisabled = 53, + Status_GcodeInvalidRetractPosition = 54, // Some error codes as defined in bdring's ESP32 port Status_SDMountError = 60, @@ -156,6 +157,7 @@ PROGMEM static const status_detail_t status_detail[] = { { Status_MotorFault, "Motor fault", "Motor fault." }, { Status_SettingValueOutOfRange, "Value out of range.", "Setting value is out of range." }, { Status_SettingDisabled, "Setting disabled", "Setting is not available, possibly due to limited driver support." }, + { Status_GcodeInvalidRetractPosition, "Invalid gcode ID:54", "Retract position is less than drill depth." }, { Status_SDMountError, "SD Card", "SD Card mount failed." }, { Status_SDReadError, "SD Card", "SD Card file open/read failed." }, { Status_SDFailedOpenDir, "SD Card", "SD Card directory listing failed." }, diff --git a/drivers/ESP32/main/grbl/gcode.c b/drivers/ESP32/main/grbl/gcode.c index df927e09..de2fef35 100644 --- a/drivers/ESP32/main/grbl/gcode.c +++ b/drivers/ESP32/main/grbl/gcode.c @@ -636,11 +636,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. - - if (axis_command) - FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } - - axis_command = AxisCommand_ToolLengthOffset; + // NOTE: cannot find the NIST statement referenced above, changed to match LinuxCNC behaviour in build 20210513. if (int_value == 49) // G49 gc_block.modal.tool_offset_mode = ToolLengthOffset_Cancel; #ifdef N_TOOLS @@ -649,9 +645,12 @@ status_code_t gc_execute_block(char *block, char *message) else if (mantissa == 20) // G43.2 gc_block.modal.tool_offset_mode = ToolLengthOffset_ApplyAdditional; #endif - else if (mantissa == 10) // G43.1 + else if (mantissa == 10) { // G43.1 + if (axis_command) + FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } + axis_command = AxisCommand_ToolLengthOffset; gc_block.modal.tool_offset_mode = ToolLengthOffset_EnableDynamic; - else + } else FAIL(Status_GcodeUnsupportedCommand); // [Unsupported G43.x command] mantissa = 0; // Set to zero to indicate valid non-integer G command. break; @@ -1316,7 +1315,9 @@ status_code_t gc_execute_block(char *block, char *message) // Pre-convert XYZ coordinate values to millimeters, if applicable. uint_fast8_t idx = N_AXIS; if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used. - if (bit_istrue(axis_words.mask, bit(--idx))) + idx--; +// if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (bit_istrue(axis_words.mask, bit(idx))) gc_block.values.xyz[idx] *= MM_PER_INCH; } while(idx); @@ -1412,13 +1413,13 @@ status_code_t gc_execute_block(char *block, char *message) // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if N_TOOLS defined. // [G43.1 Errors]: Motion command in same line. - // [G43.2 Errors]: Motion command in same line. Tool number not in the tool table, - // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid - // axis that is configured (in config.h). There should be an error if the configured axis - // is absent or if any of the other axis words are present. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates called in block. + // [G43.2 Errors]: Tool number not in the tool table, + if (command_words.G8) { // Indicates called in block. #ifdef TOOL_LENGTH_OFFSET_AXIS + // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid + // axis that is configured (in config.h). There should be an error if the configured axis + // is absent or if any of the other axis words are present. if(gc_block.modal.tool_offset_mode == ToolLengthOffset_EnableDynamic) { if (axis_words.mask ^ bit(TOOL_LENGTH_OFFSET_AXIS)) FAIL(Status_GcodeG43DynamicAxisError); @@ -1819,12 +1820,10 @@ status_code_t gc_execute_block(char *block, char *message) else if(gc_block.values.l <= 0) FAIL(Status_NonPositiveValue); // [L <= 0] - if (value_words.r) { - gc_state.canned.retract_position = gc_block.values.r; - if(gc_state.modal.distance_incremental) - gc_state.canned.retract_position += gc_state.position[plane.axis_linear]; - gc_state.canned.retract_position = gc_block.modal.coord_system.xyz[plane.axis_linear] + gc_state.canned.retract_position; - } + if(value_words.r) + gc_state.canned.retract_position = gc_block.values.r + (gc_block.modal.distance_incremental + ? gc_state.position[plane.axis_linear] + : gc_get_block_offset(&gc_block, plane.axis_linear)); idx = N_AXIS; do { @@ -1832,11 +1831,13 @@ status_code_t gc_execute_block(char *block, char *message) gc_state.canned.xyz[idx] = gc_block.values.xyz[idx]; if(idx != plane.axis_linear) gc_state.canned.xyz[idx] -= gc_state.position[idx]; + else if(gc_block.modal.distance_incremental) + gc_state.canned.xyz[idx] = gc_state.canned.retract_position + (gc_state.canned.xyz[idx] - gc_state.position[idx]); } } while(idx); if(gc_state.canned.retract_position < gc_state.canned.xyz[plane.axis_linear]) - FAIL(Status_GcodeAxisCommandConflict); + FAIL(Status_GcodeInvalidRetractPosition); value_words.r = value_words.l = Off; // Remove single-meaning value words. @@ -2030,7 +2031,9 @@ status_code_t gc_execute_block(char *block, char *message) if (gc_block.modal.units_imperial) { idx = 3; do { // Axes indices are consistent, so loop may be used to save flash space. - if (ijk_words.mask & bit(--idx)) + idx--; +// if (ijk_words.mask & bit(idx) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (ijk_words.mask & bit(idx)) gc_block.values.ijk[idx] *= MM_PER_INCH; } while(idx); } @@ -2412,7 +2415,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms // of execution. The error-checking step would simply load the offset value into the correct // axis of the block XYZ value array. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates a change. + if (command_words.G8) { // Indicates a change. bool tlo_changed = false; @@ -2725,11 +2728,11 @@ status_code_t gc_execute_block(char *block, char *message) hal.coolant.set_state(gc_state.modal.coolant); sys.report.spindle = On; // Set to report change immediately sys.report.coolant = On; // ... - - if(grbl.on_program_completed) - grbl.on_program_completed(gc_state.modal.program_flow); } + if(grbl.on_program_completed) + grbl.on_program_completed(gc_state.modal.program_flow, check_mode); + // Clear any pending output commands while(output_commands) { output_command_t *next = output_commands->next; diff --git a/drivers/ESP32/main/grbl/grbl.h b/drivers/ESP32/main/grbl/grbl.h index 46556f33..b8522819 100644 --- a/drivers/ESP32/main/grbl/grbl.h +++ b/drivers/ESP32/main/grbl/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_VERSION_BUILD "20210313" +#define GRBL_VERSION_BUILD "20210513" // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/drivers/ESP32/main/grbl/hal.h b/drivers/ESP32/main/grbl/hal.h index 1b6de673..baa34852 100644 --- a/drivers/ESP32/main/grbl/hal.h +++ b/drivers/ESP32/main/grbl/hal.h @@ -73,16 +73,18 @@ typedef void (*driver_reset_ptr)(void); // I/O stream +typedef int16_t (*stream_read_ptr)(void); typedef void (*stream_write_ptr)(const char *s); +typedef bool (*stream_write_char_ptr)(const char c); typedef bool (*enqueue_realtime_command_ptr)(char data); typedef struct { stream_type_t type; uint16_t (*get_rx_buffer_available)(void); -// bool (*stream_write)(char c); - stream_write_ptr write; // write string to current I/O stream only. - stream_write_ptr write_all; // write string to all active output streams. - int16_t (*read)(void); + stream_write_ptr write; // write string to current I/O stream only. + stream_write_ptr write_all; // write string to all active output streams. + stream_write_char_ptr write_char; // write single character to current I/O stream only. + stream_read_ptr read; void (*reset_read_buffer)(void); void (*cancel_read_buffer)(void); bool (*suspend_read)(bool await); @@ -340,14 +342,15 @@ typedef struct { typedef void (*on_state_change_ptr)(sys_state_t state); typedef void (*on_probe_completed_ptr)(void); -typedef void (*on_program_completed_ptr)(program_flow_t program_flow); +typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode); typedef void (*on_execute_realtime_ptr)(sys_state_t state); typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd); +typedef bool (*on_unknown_realtime_cmd_ptr)(char c); typedef void (*on_report_options_ptr)(bool newopt); typedef void (*on_report_command_help_ptr)(void); typedef void (*on_global_settings_restore_ptr)(void); typedef setting_details_t *(*on_get_settings_ptr)(void); // NOTE: this must match the signature of the same definition in - // the setting_details_t structure in settings.h! + // the setting_details_t structure in settings.h! typedef void (*on_realtime_report_ptr)(stream_write_ptr stream_write, report_tracking_flags_t report); typedef void (*on_unknown_feedback_message_ptr)(stream_write_ptr stream_write); typedef bool (*on_laser_ppi_enable_ptr)(uint_fast16_t ppi, uint_fast16_t pulse_length); @@ -370,6 +373,7 @@ typedef struct { on_get_settings_ptr on_get_settings; on_realtime_report_ptr on_realtime_report; on_unknown_feedback_message_ptr on_unknown_feedback_message; + on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; on_unknown_sys_command_ptr on_unknown_sys_command; // return Status_Unhandled if not handled. on_get_commands_ptr on_get_commands; on_user_command_ptr on_user_command; diff --git a/drivers/ESP32/main/grbl/limits.c b/drivers/ESP32/main/grbl/limits.c index 340be0f3..7289e710 100644 --- a/drivers/ESP32/main/grbl/limits.c +++ b/drivers/ESP32/main/grbl/limits.c @@ -277,8 +277,8 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar if(auto_square.mask) { float fail_distance = (-settings.homing.dual_axis.fail_length_percent / 100.0f) * settings.axis[dual_motor_axis].max_travel; - fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_min); - fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_min); autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm); } diff --git a/drivers/ESP32/main/grbl/motion_control.c b/drivers/ESP32/main/grbl/motion_control.c index ef4b8414..fc2ce283 100644 --- a/drivers/ESP32/main/grbl/motion_control.c +++ b/drivers/ESP32/main/grbl/motion_control.c @@ -562,7 +562,6 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ if(repeats && gc_state.modal.distance_incremental) { position[plane.axis_0] += canned->xyz[plane.axis_0]; position[plane.axis_1] += canned->xyz[plane.axis_1]; - position[plane.axis_linear] = canned->prev_position; if(!mc_line(position, pl_data)) return; } diff --git a/drivers/ESP32/main/grbl/protocol.c b/drivers/ESP32/main/grbl/protocol.c index a15f70e9..030b947c 100644 --- a/drivers/ESP32/main/grbl/protocol.c +++ b/drivers/ESP32/main/grbl/protocol.c @@ -45,9 +45,8 @@ typedef union { uint8_t overflow :1, comment_parentheses :1, comment_semicolon :1, - line_is_comment :1, block_delete :1, - unassigned :3; + unassigned :4; }; } line_flags_t; @@ -164,7 +163,7 @@ bool protocol_main_loop (void) int16_t c; char eol = '\0'; line_flags_t line_flags = {0}; - bool nocaps = false; + bool nocaps = false, line_is_comment = false; xcommand[0] = '\0'; user_message.show = keep_rt_commands = false; @@ -178,7 +177,7 @@ bool protocol_main_loop (void) if(c == ASCII_CAN) { eol = xcommand[0] = '\0'; - keep_rt_commands = nocaps = user_message.show = false; + keep_rt_commands = nocaps = line_is_comment = user_message.show = false; char_counter = line_flags.value = 0; gc_state.last_error = Status_OK; @@ -207,7 +206,7 @@ bool protocol_main_loop (void) // Direct and execute one line of formatted input, and report status of execution. if (line_flags.overflow) // Report line overflow error. gc_state.last_error = Status_Overflow; - else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_flags.line_is_comment) // Empty or comment line. For syncing purposes. + else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_is_comment) // Empty or comment line. For syncing purposes. gc_state.last_error = Status_OK; else if (line[0] == '$') {// Grbl '$' system command if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) { @@ -275,7 +274,7 @@ bool protocol_main_loop (void) case '(': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; if(!keep_rt_commands) { // Enable comments flag and ignore all characters until ')' or EOL unless it is a message. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. @@ -297,7 +296,7 @@ bool protocol_main_loop (void) case ';': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. if(!keep_rt_commands) { if((line_flags.comment_semicolon = !line_flags.comment_parentheses)) @@ -855,13 +854,13 @@ ISR_CODE bool protocol_enqueue_realtime_command (char c) default: if(c < ' ' || (c >= 0x7F && c <= 0xBF)) - drop = true; + drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c); break; } // 2. Process printable ASCII characters and top-bit set characters // If legacy realtime commands are disabled they are returned to the input stream - // when appering in settings ($ commands) or comments + // when appearing in settings ($ commands) or comments if(!drop) switch ((unsigned char)c) { diff --git a/drivers/ESP32/main/grbl/settings.c b/drivers/ESP32/main/grbl/settings.c index 395d44fc..e0144ae8 100644 --- a/drivers/ESP32/main/grbl/settings.c +++ b/drivers/ESP32/main/grbl/settings.c @@ -32,6 +32,7 @@ #include "limits.h" #include "nvs_buffer.h" #include "tool_change.h" +#include "state_machine.h" #ifdef ENABLE_BACKLASH_COMPENSATION #include "motion_control.h" #endif @@ -99,6 +100,7 @@ PROGMEM const settings_t defaults = { .steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK, .steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK, .steppers.deenergize.mask = ST_DEENERGIZE_MASK, +// .steppers.is_rotational.mask = 0, #if DEFAULT_HOMING_ENABLE .homing.flags.enabled = DEFAULT_HOMING_ENABLE, .homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK, @@ -446,6 +448,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL }, { Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL }, { Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL } +// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL } }; static setting_details_t details = { @@ -456,6 +459,56 @@ static setting_details_t details = { .save = settings_write_global }; +// Acceleration override + +static struct { + bool valid; + float acceleration[N_AXIS]; +} override_backup = { .valid = false }; + +static void save_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + do { + idx--; + override_backup.acceleration[idx] = settings.axis[idx].acceleration; + } while(idx); + + override_backup.valid = true; +} + +static void restore_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + if(override_backup.valid) do { + idx--; + settings.axis[idx].acceleration = override_backup.acceleration[idx]; + } while(idx); +} + +// Temporarily override acceleration, if 0 restore to setting value. +// Note: only allowed when current state is idle. +bool settings_override_acceleration (uint8_t axis, float acceleration) +{ + if(state_get() != STATE_IDLE) + return false; + + if(acceleration <= 0.0f) { + if(override_backup.valid) + settings.axis[axis].acceleration = override_backup.acceleration[axis]; + } else { + if(!override_backup.valid) + save_override_backup(); + settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value? + } + + return true; +} + +// --- + setting_details_t *settings_get_details (void) { details.on_get_settings = grbl.on_get_settings; @@ -796,7 +849,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisAcceleration: - settings.axis[idx].acceleration = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. + settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. break; case Setting_AxisMaxTravel: @@ -1232,6 +1285,9 @@ bool read_global_settings () // Write Grbl global settings and version number to persistent storage void settings_write_global (void) { + if(override_backup.valid) + restore_override_backup(); + if(hal.nvs.type != NVS_None) { hal.nvs.put_byte(0, SETTINGS_VERSION); hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true); diff --git a/drivers/ESP32/main/grbl/settings.h b/drivers/ESP32/main/grbl/settings.h index f62a6015..dfadc1ea 100644 --- a/drivers/ESP32/main/grbl/settings.h +++ b/drivers/ESP32/main/grbl/settings.h @@ -226,6 +226,7 @@ typedef enum { Settings_IoPort_OD_Enable = 373, Settings_ModBus_BaudRate = 374, Settings_ModBus_RXTimeout = 375, + Settings_Axis_Rotational = 376, Setting_EncoderSettingsBase = 400, // NOTE: Reserving settings values >= 400 for encoder settings. Up to 449. Setting_EncoderSettingsMax = 449, @@ -441,6 +442,7 @@ typedef struct { axes_signals_t dir_invert; axes_signals_t enable_invert; axes_signals_t deenergize; +// axes_signals_t is_rotational; or add to axis_settings_t below as bitmap union? rotational axes are not scaled in imperial mode float pulse_microseconds; float pulse_delay_microseconds; uint16_t idle_lock_time; // If value = 255, steppers do not disable. @@ -708,6 +710,9 @@ bool settings_write_tool_data (tool_data_t *tool_data); // Read selected tool data from persistent storage bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data); +// Temporarily override acceleration, if 0 restore to configured setting value +bool settings_override_acceleration (uint8_t axis, float acceleration); + setting_details_t *settings_get_details (void); bool settings_is_group_available (setting_group_t group); bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data); diff --git a/drivers/ESP32/main/grbl/stream.c b/drivers/ESP32/main/grbl/stream.c index ba9b6046..da86fe65 100644 --- a/drivers/ESP32/main/grbl/stream.c +++ b/drivers/ESP32/main/grbl/stream.c @@ -26,7 +26,7 @@ static stream_rx_buffer_t rxbackup; // "dummy" version of serialGetC -static int16_t stream_get_null (void) +int16_t stream_get_null (void) { return SERIAL_NO_DATA; } diff --git a/drivers/ESP32/main/grbl/stream.h b/drivers/ESP32/main/grbl/stream.h index 07e62c08..f89270c2 100644 --- a/drivers/ESP32/main/grbl/stream.h +++ b/drivers/ESP32/main/grbl/stream.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ #ifndef _STREAM_H_ #define _STREAM_H_ +#define ASCII_SOH 0x01 +#define ASCII_STX 0x02 #define ASCII_ETX 0x03 +#define ASCII_EOT 0x04 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 @@ -104,7 +107,16 @@ typedef struct { char data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; +#ifdef __cplusplus +extern "C" { +#endif + +int16_t stream_get_null (void); bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); void stream_rx_backup (stream_rx_buffer_t *rxbuffer); +#ifdef __cplusplus +} +#endif + #endif diff --git a/drivers/ESP32/main/my_machine.h b/drivers/ESP32/main/my_machine.h index c129b425..2ecbc3bc 100644 --- a/drivers/ESP32/main/my_machine.h +++ b/drivers/ESP32/main/my_machine.h @@ -27,6 +27,7 @@ //#define BOARD_BDRING_V3P5 //#define BOARD_BDRING_V4 //#define BOARD_BDRING_I2S6A // NOT production ready! +//#define BOARD_SOURCERABBIT_4AXIS //#define BOARD_MY_MACHINE // Add my_machine_map.h before enabling this! // Configuration diff --git a/drivers/ESP32/main/sdcard/sdcard.c b/drivers/ESP32/main/sdcard/sdcard.c index 11f757d6..010b2866 100644 --- a/drivers/ESP32/main/sdcard/sdcard.c +++ b/drivers/ESP32/main/sdcard/sdcard.c @@ -46,6 +46,8 @@ #include "grbl/state_machine.h" #endif +#include "sdcard/ymodem.h" + #ifdef __IMXRT1062__ const char *dev = "1:/"; #elif defined(NEW_FATFS) @@ -124,7 +126,7 @@ static on_report_options_ptr on_report_options; static void sdcard_end_job (void); static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report); static void trap_state_change_request(uint_fast16_t state); -static void sdcard_on_program_completed (program_flow_t program_flow); +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode); //static report_t active_reports; #ifdef __MSP432E401Y__ @@ -379,8 +381,10 @@ static int16_t sdcard_read (void) c = '\n'; } - } else if(state == STATE_IDLE) // TODO: end on ok count match line count? - sdcard_end_job(); + } else if((state == STATE_IDLE || state == STATE_CHECK_MODE) && grbl.on_program_completed == sdcard_on_program_completed) { // TODO: end on ok count match line count? + sdcard_on_program_completed(ProgramFlow_CompletedM30, state == STATE_CHECK_MODE); + grbl.report.feedback_message(Message_ProgramEnd); + } return c; } @@ -429,7 +433,9 @@ static status_code_t trap_status_report (status_code_t status_code) static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report) { - if(hal.stream.read != await_cycle_start) { + if(hal.stream.read == await_cycle_start) + stream_write("|SD:Pending"); + else { char *pct_done = ftoa((float)file.pos / (float)file.size * 100.0f, 1); if(state_get() != STATE_IDLE && !strncmp(pct_done, "100.0", 5)) @@ -450,7 +456,7 @@ static void sdcard_restart_msg (sys_state_t state) report_feedback_message(Message_CycleStartToRerun); } -static void sdcard_on_program_completed (program_flow_t program_flow) +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode) { frewind = frewind || program_flow == ProgramFlow_CompletedM2; // || program_flow == ProgramFlow_CompletedM30; @@ -468,7 +474,7 @@ static void sdcard_on_program_completed (program_flow_t program_flow) sdcard_end_job(); if(on_program_completed) - on_program_completed(program_flow); + on_program_completed(program_flow, check_mode); } #if M6_ENABLE @@ -566,14 +572,29 @@ static status_code_t sd_cmd_to_output (sys_state_t state, char *args) return retval; } +static status_code_t sd_cmd_unlink (sys_state_t state, char *args) +{ + status_code_t retval = Status_Unhandled; + +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + if (!(state == STATE_IDLE || state == STATE_CHECK_MODE)) + retval = Status_SystemGClock; + else if(args) + retval = f_unlink(args) ? Status_OK : Status_SDReadError; +#endif + + return retval; +} + static void sdcard_reset (void) { if(hal.stream.type == StreamType_SDCard) { if(file.line > 0) { char buf[70]; - sprintf(buf, "[MSG:Reset during streaming of SD file at line: " UINT32FMT "]" ASCII_EOL, file.line); - hal.stream.write(buf); - } + sprintf(buf, "Reset during streaming of SD file at line: " UINT32FMT, file.line); + report_message(buf, Message_Plain); + } else if(frewind) + report_feedback_message(Message_None); sdcard_end_job(); } @@ -585,6 +606,9 @@ static void onReportCommandHelp (void) hal.stream.write("$F - list files on SD card" ASCII_EOL); hal.stream.write("$F= - run SD card file" ASCII_EOL); hal.stream.write("$FM - mount SD card" ASCII_EOL); +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + hal.stream.write("$FD= - delete SD card file" ASCII_EOL); +#endif hal.stream.write("$FR - enable rewind mode for next SD card file to run" ASCII_EOL); hal.stream.write("$F<= - dump SD card file to output" ASCII_EOL); @@ -597,15 +621,20 @@ static void onReportOptions (bool newopt) on_report_options(newopt); if(newopt) +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + hal.stream.write(hal.stream.write_char == NULL ? ",SD" : ",SD,YM"); +#else hal.stream.write(",SD"); +#endif else - hal.stream.write("[PLUGIN:SDCARD v1.00]" ASCII_EOL); + hal.stream.write("[PLUGIN:SDCARD v1.02]" ASCII_EOL); } const sys_command_t sdcard_command_list[] = { {"F", false, sd_cmd_file}, {"FM", true, sd_cmd_mount}, {"FR", true, sd_cmd_rewind}, + {"FD", false, sd_cmd_unlink}, {"F<", false, sd_cmd_to_output}, }; @@ -632,6 +661,11 @@ void sdcard_init (void) on_report_options = grbl.on_report_options; grbl.on_report_options = onReportOptions; + +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + if(hal.stream.write_char != NULL) + ymodem_init(); +#endif } FATFS *sdcard_getfs (void) diff --git a/drivers/ESP32/main/sdcard/ymodem.c b/drivers/ESP32/main/sdcard/ymodem.c new file mode 100644 index 00000000..41cea855 --- /dev/null +++ b/drivers/ESP32/main/sdcard/ymodem.c @@ -0,0 +1,386 @@ +/* + ymodem.c - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Specification: http://wiki.synchro.net/ref:ymodem + + NOTE: Receiver only, does not send initial 'C' to start transfer. + Start transfer by sending SOH or STX. + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + + +#include "sdcard.h" + +#if SDCARD_ENABLE == 2 + +#include +#include + +typedef enum { + YModem_NOOP = 0, + YModem_ACK, + YModem_ACKFile, + YModem_NoFile, + YModem_Purge, + YModem_NAK, + YModem_EOT, + YModem_CAN +} ymodem_status_t; + +typedef ymodem_status_t (*process_data_ptr)(uint8_t c); + +typedef struct { + FATFS *fs; + FIL *handle; + char filename[32]; + uint32_t filelength; + uint32_t received; + uint16_t crc; + uint_fast16_t idx; + uint_fast16_t errors; + uint_fast16_t packet_len; + uint8_t packet_num; + uint32_t next_timeout; + process_data_ptr process; + bool seq_inv; + bool crc_lsb; + bool completed; + bool repeated; + uint8_t payload[1024]; +} ymodem_t; + +static ymodem_t ymodem; + +static driver_reset_ptr driver_reset; +static on_execute_realtime_ptr on_execute_realtime; +static on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; +static io_stream_t active_stream; + +static ymodem_status_t await_soh (uint8_t c); +static ymodem_status_t await_packetnum (uint8_t c); +static ymodem_status_t get_payload (uint8_t c); +static ymodem_status_t await_crc (uint8_t c); +static ymodem_status_t await_eot (uint8_t c); + +// Fast CRC16 implementation +// Original Code: Ashley Roll +// Optimisations: Scott Dattalo +// From http://www.ccsinfo.com/forum/viewtopic.php?t=24977 +static uint16_t crc16 (const uint8_t *buf, uint16_t len) +{ + uint16_t x, crc = 0; + + while(len--) { + x = (crc >> 8) ^ *buf++; + x ^= x >> 4; + crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x; + } + + return crc; +} + +// End transfer handler. +static void end_transfer (bool send_ack) +{ + if(ymodem.handle) { + f_close(ymodem.handle); + ymodem.handle = NULL; + } + + // Restore stream handlers and detach protocol loop from foreground process. + memcpy(&hal.stream, &active_stream, sizeof(io_stream_t)); + grbl.on_execute_realtime = on_execute_realtime; + + if(send_ack) { + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + } +} + +// Cancel handler. Waits for second cancel character. +static ymodem_status_t await_cancel (uint8_t c) +{ + if(c == ASCII_CAN) + end_transfer(false); + else + ymodem.process = await_soh; + + return YModem_NOOP; +} + +// Purge handler. Sinks incoming characters. +static ymodem_status_t purge (uint8_t c) +{ + return YModem_NOOP; +} + +// +// Packet processing +// + +// Start of header handler. +static ymodem_status_t await_soh (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(c == ASCII_SOH || c == ASCII_STX) { + ymodem.idx = ymodem.crc = 0; + ymodem.crc_lsb = ymodem.seq_inv = ymodem.repeated = false; + ymodem.packet_len = c == ASCII_SOH ? 128 : 1024; + ymodem.process = await_packetnum; // Set active handler to wait for packet number. + } else if(c == ASCII_EOT) + end_transfer(true); + else if(c == ASCII_CAN) + ymodem.process = await_cancel; // Set active handler to wait for second CAN character. + else + status = YModem_Purge; + + return status; +} + +// Packet number handler. Reads and validates packet number. +static ymodem_status_t await_packetnum (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(ymodem.seq_inv) { + c ^= 0xFF; + if((ymodem.repeated = ymodem.packet_num == c - 1)) + c++; + if(c == ymodem.packet_num) + ymodem.process = get_payload; // Set active handler to fetch payload + else + status = YModem_Purge; + } else if(!(ymodem.seq_inv = (c == ymodem.packet_num || ymodem.packet_num == c - 1))) + status = YModem_Purge; + + return status; +} + +// Payload handler. Saves incoming characters in payload buffer. +static ymodem_status_t get_payload (uint8_t c) +{ + ymodem.payload[ymodem.idx++] = c; + + if(ymodem.idx == ymodem.packet_len) + ymodem.process = await_crc; + + return YModem_NOOP; +} + +// CRC handler. Reads and validates CRC, open file on packet 0 writes payload to file when valid. +static ymodem_status_t await_crc (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(!ymodem.crc_lsb) { + ymodem.crc_lsb = true; + ymodem.crc = c; + } else { + + ymodem.process = await_soh; // Set active handler to wait for next packet + ymodem.crc = (ymodem.crc << 8) | c; + + if(crc16((const uint8_t *)&ymodem.payload, ymodem.packet_len) != ymodem.crc) // If CRC invalid + return YModem_Purge; // purge input stream and return NAK. + + if(ymodem.packet_num == 0 && *ymodem.filename == 0) { // Open file or end transfer + + const char *data = (const char *)ymodem.payload; + + // If no filename present in payload end transfer by sending ACK else send ACK + C if file could be opened, CAN if not. + if((status = *data == '\0' ? YModem_NoFile : YModem_ACKFile) == YModem_ACKFile) { + + strcpy(ymodem.filename, (const char *)data); // Save filename + + data += strlen(ymodem.filename) + 1; // Save file length if present + if(*data != '\0') + ymodem.filelength = atoi(data); + + static FIL handle; + if(ymodem.fs != NULL && f_open(&handle, ymodem.filename, FA_CREATE_ALWAYS|FA_WRITE) == FR_OK) + ymodem.handle = &handle; + else + status = YModem_CAN; + } + + ymodem.packet_num++; + + } else { // Write payload to file + + UINT wrlen; + + if(!ymodem.repeated) { + ymodem.packet_num++; + ymodem.received += ymodem.packet_len; + + // Trim packet length to match file length if last packet + if((ymodem.completed = ymodem.filelength && ymodem.received > ymodem.filelength)) + ymodem.packet_len -= ymodem.received - ymodem.filelength; + + // Write payload + if(f_write(ymodem.handle, ymodem.payload, ymodem.packet_len, &wrlen) == FR_OK) { + status = YModem_ACK; + if(ymodem.completed) { + ymodem.idx = 0; + ymodem.process = await_eot; // Set active handler to wait for end of transfer + } + } else + status = YModem_CAN; + } else + status = YModem_ACK; + } + } + + return status; +} + +// End of transmission handler. +static ymodem_status_t await_eot (uint8_t c) +{ + if(c == ASCII_EOT) + end_transfer(true); + + return YModem_NOOP; +} + +// Main YModem protocol loop. +// Reads characters off input stream and dispatches them to the appropriate handler. +// Handles timeouts. +static void protocol_loop (sys_state_t state) +{ + int16_t c; + + if(hal.get_elapsed_ticks() >= ymodem.next_timeout) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + ymodem.errors++; + if(ymodem.errors > 10) + end_transfer(false); + else { + ymodem.process = await_soh; + hal.stream.write_char(ASCII_NAK); + } + } + + while((c = active_stream.read()) != SERIAL_NO_DATA) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + switch(ymodem.process(c)) { + + case YModem_ACK: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + break; + + case YModem_ACKFile: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + break; + + case YModem_NoFile: + hal.stream.write_char(ASCII_ACK); + end_transfer(false); + break; + + case YModem_NAK: + ymodem.errors++; + hal.stream.write_char(ASCII_NAK); + break; + + case YModem_CAN: + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + break; + + case YModem_Purge: + ymodem.errors++; + ymodem.process = purge; + hal.stream.cancel_read_buffer(); + break; + + default: + break; + } + + if(grbl.on_execute_realtime != protocol_loop) + break; + } + + on_execute_realtime(state); +} + +// Buffer all received characters. +static bool buffer_all (char c) +{ + return false; +} + +// Terminate any ongoing transfer on a soft reset. +static void on_soft_reset (void) +{ + if(grbl.on_execute_realtime == protocol_loop) { + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + } + + driver_reset(); +} + +// Check input stream for file YModem start of header (soh) characters. +// Redirect input stream and start protocol handler when found. +static bool trap_initial_soh (char c) +{ + if(c == ASCII_SOH || c == ASCII_STX) { + + memcpy(&active_stream, &hal.stream, sizeof(io_stream_t)); // Save current stream pointers, + hal.stream.enqueue_realtime_command = buffer_all; // stop core real-time command handling and + hal.stream.read = stream_get_null; // block core protocol loop from reading from input + + on_execute_realtime = grbl.on_execute_realtime; // Add YModem protocol loop + grbl.on_execute_realtime = protocol_loop; // to grblHAL foreground process + + memset(&ymodem, 0, sizeof(ymodem_t)); // Init YModem variables + ymodem.process = await_soh; + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + ymodem.fs = sdcard_getfs(); + + return false; // Return false to add character to input buffer + } + + return on_unknown_realtime_cmd == NULL || on_unknown_realtime_cmd(c); +} + +// Add YModem protocol to chain of unknown real-time command handlers +void ymodem_init (void) +{ + driver_reset = hal.driver_reset; + hal.driver_reset = on_soft_reset; + + on_unknown_realtime_cmd = grbl.on_unknown_realtime_cmd; + grbl.on_unknown_realtime_cmd = trap_initial_soh; +} + +#endif + diff --git a/drivers/ESP32/main/sdcard/ymodem.h b/drivers/ESP32/main/sdcard/ymodem.h new file mode 100644 index 00000000..de91553a --- /dev/null +++ b/drivers/ESP32/main/sdcard/ymodem.h @@ -0,0 +1,22 @@ +/* + ymodem.h - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +void ymodem_init (void); diff --git a/drivers/ESP32/main/sourcerabbit_4axis.h b/drivers/ESP32/main/sourcerabbit_4axis.h new file mode 100644 index 00000000..5891d92b --- /dev/null +++ b/drivers/ESP32/main/sourcerabbit_4axis.h @@ -0,0 +1,82 @@ +/* + sourcerabbit_4axis.h - An embedded CNC Controller with rs274/ngc (g-code) support + + Driver code for ESP32 + + Part of grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#define BOARD_NAME "SourceRabbit 4-axis CNC" + +// timer definitions +#define STEP_TIMER_GROUP TIMER_GROUP_0 +#define STEP_TIMER_INDEX TIMER_0 + +#if MODBUS_ENABLE +#error VFD Spindle not supported! +#endif + +#if KEYPAD_ENABLE +#error Keypad not supported! +#endif + +#if SDCARD_ENABLE +#error SD card not supported! +#endif + +// Define step pulse output pins. +#define X_STEP_PIN GPIO_NUM_0 +#define Y_STEP_PIN GPIO_NUM_25 +#define Z_STEP_PIN GPIO_NUM_27 +#if NUM_AXES > 3 +#define A_STEP_PIN GPIO_NUM_12 +#endif + +// Define step direction output pins. NOTE: All direction pins must be on the same port. +#define X_DIRECTION_PIN GPIO_NUM_33 +#define Y_DIRECTION_PIN GPIO_NUM_26 +#define Z_DIRECTION_PIN GPIO_NUM_14 +#if NUM_AXES > 3 +#define A_DIRECTION_PIN GPIO_NUM_13 +#endif + +// Define stepper driver enable/disable output pin(s). +#define STEPPERS_DISABLE_PIN GPIO_NUM_15 + +// Define homing/hard limit switch input pins and limit interrupt vectors. +#define X_LIMIT_PIN GPIO_NUM_36 +#define Y_LIMIT_PIN GPIO_NUM_39 +#define Z_LIMIT_PIN GPIO_NUM_34 + +// Define spindle enable and spindle direction output pins. + +#define SPINDLE_ENABLE_PIN GPIO_NUM_21 +#define SPINDLEPWMPIN GPIO_NUM_2 + +// Define flood and mist coolant enable output pins. + +#define COOLANT_FLOOD_PIN GPIO_NUM_22 +#define COOLANT_MIST_PIN GPIO_NUM_23 + +// Define user-control CONTROLs (cycle start, reset, feed hold) input pins. +// N/A + +// Define probe switch input pin. +#if PROBE_ENABLE +#define PROBE_PIN GPIO_NUM_32 +#endif diff --git a/drivers/ESP32/main/trinamic/tmc26x.c b/drivers/ESP32/main/trinamic/tmc26x.c index 109cc93f..e3ee13d2 100644 --- a/drivers/ESP32/main/trinamic/tmc26x.c +++ b/drivers/ESP32/main/trinamic/tmc26x.c @@ -38,7 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include +#include "tmc26x.h" static SPI_driver_t io; diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/T40X101_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/T40X101_map.h index 60c16340..45a0320a 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/T40X101_map.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/T40X101_map.h @@ -5,7 +5,7 @@ Board by Phil Barrett: https://github.com/phil-barrett/grblHAL-teensy-4.x - Copyright (c) 2020 Terje Io + Copyright (c) 2020-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +31,10 @@ #error No pins available for encoder input! #endif +#if SPINDLE_SYNC_ENABLE +#error Spindle sync is not supported for T40X101 +#endif + // Default pin assignments allow only one axis to be ganged or auto squared. // A axis pin numbers are used for the ganged/auto squared axis. // If a second axis is to be ganged/auto squared pin assignments needs to be changed! @@ -74,7 +78,9 @@ #if Z_GANGED || Z_AUTO_SQUARE #define Z2_STEP_PIN (8u) #define Z2_DIRECTION_PIN (9u) -#define Z2_LIMIT_PIN (23u) +#if Z_AUTO_SQUARE + #define Z2_LIMIT_PIN (23u) +#endif #endif #if N_AXIS > 3 @@ -113,6 +119,6 @@ #if EEPROM_ENABLE || KEYPAD_ENABLE #define I2C_PORT 4 -#define I2C_SCL4 (24u) // Not used, for info only -#define I2C_SDA4 (25u) // Not used, for info only +#define I2C_SCL4 (24u) // Not referenced, for info only +#define I2C_SDA4 (25u) // Not referenced, for info only #endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41BB5X_Pro_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41BB5X_Pro_map.h new file mode 100644 index 00000000..4d7cb947 --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41BB5X_Pro_map.h @@ -0,0 +1,164 @@ +/* + T41BB5X_Pro_map.h - driver code for IMXRT1062 processor (on Teensy 4.1 board) + + Part of grblHAL + + Board by Phil Barrett: https://github.com/phil-barrett/grblHAL-teensy-4.x + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#define BOARD_NAME "T41BB5X Pro" +#define HAS_BOARD_INIT + +#if N_AXIS > 5 +#error Max number of axes is 5 for T41U5XBB +#endif + +#if QEI_ENABLE && SPINDLE_SYNC_ENABLE +#error Quadrature encoder and spindle sync cannot be enabled at the same time +#endif + +// Board has 2K FRAM +#undef EEPROM_ENABLE +#undef EEPROM_IS_FRAM +#define EEPROM_ENABLE 1 +#define EEPROM_IS_FRAM 1 + +// Default pin assignments allow only one axis to be ganged or auto squared. +// B axis pin numbers are used for the ganged/auto squared axis. +// If a second axis is to be ganged/auto squared pin assignments needs to be changed! +// Set to 1 to enable, 0 to disable. +#define X_GANGED 0 +#define X_AUTO_SQUARE 0 +#define Y_GANGED 0 +#define Y_AUTO_SQUARE 0 +#define Z_GANGED 0 +#define Z_AUTO_SQUARE 0 +// + +#define X_STEP_PIN (2u) +#define X_DIRECTION_PIN (3u) +#define X_ENABLE_PIN (10u) +#define X_LIMIT_PIN (20u) + +#if X_GANGED || X_AUTO_SQUARE +#define X2_STEP_PIN (26u) +#define X2_DIRECTION_PIN (27u) +#define X2_ENABLE_PIN (37u) +#if X_AUTO_SQUARE + #define X2_LIMIT_PIN (28u) +#endif +#endif + +#define Y_STEP_PIN (4u) +#define Y_DIRECTION_PIN (5u) +#define Y_ENABLE_PIN (35u) +#define Y_LIMIT_PIN (21u) + +#if Y_GANGED || Y_AUTO_SQUARE +#define Y2_STEP_PIN (26u) +#define Y2_DIRECTION_PIN (27u) +#define Y2_ENABLE_PIN (37u) +#if Y_AUTO_SQUARE + #define Y2_LIMIT_PIN (28u) +#endif +#endif + +#define Z_STEP_PIN (6u) +#define Z_DIRECTION_PIN (7u) +#define Z_ENABLE_PIN (39u) +#define Z_LIMIT_PIN (22u) + +#if Z_GANGED || Z_AUTO_SQUARE +#define Z2_STEP_PIN (26u) +#define Z2_DIRECTION_PIN (27u) +#define Z2_ENABLE_PIN (37u) +#if Z_AUTO_SQUARE + #define Z2_LIMIT_PIN (28u) +#endif +#endif + +#if N_AXIS > 3 +#define A_STEP_PIN (8u) +#define A_DIRECTION_PIN (9u) +#define A_ENABLE_PIN (38u) +#define A_LIMIT_PIN (23u) +#endif + +#if N_AXIS > 4 +#define B_STEP_PIN (26u) +#define B_DIRECTION_PIN (27u) +#define B_ENABLE_PIN (37u) +#define B_LIMIT_PIN (28u) +#endif + +// Define spindle enable and spindle direction output pins. +#define SPINDLE_ENABLE_PIN (12u) +#define SPINDLE_DIRECTION_PIN (11u) +#define SPINDLEPWMPIN (13u) // NOTE: only pin 12 or pin 13 can be assigned! + +// Define flood and mist coolant enable output pins. +#define COOLANT_FLOOD_PIN (19u) +#define COOLANT_MIST_PIN (18u) + +// Define user-control CONTROLs (cycle start, reset, feed hold, door) input pins. +#define RESET_PIN (40u) +#define FEED_HOLD_PIN (16u) +#define CYCLE_START_PIN (17u) +#define SAFETY_DOOR_PIN (29u) + +// Define probe switch input pin. +#define PROBE_PIN (15u) + +#if QEI_ENABLE +#define QEI_A_PIN (36u) +#define QEI_B_PIN (30u) +//#define QEI_INDEX_PIN (36u) +#define QEI_SELECT_PIN (31u) +#endif + +#if SPINDLE_SYNC_ENABLE +#define SPINDLE_INDEX_PIN (31u) // ST2 +#define SPINDLE_PULSE_PIN (14u) // ST3 +#endif + +// Define auxillary input pins +#define AUXINPUT0_PIN (36u) // ST0 +#if !QEI_ENABLE +#define AUXINPUT1_PIN (30u) // ST1 +#if !SPINDLE_SYNC_ENABLE +#define AUXINPUT2_PIN (31u) // ST2 +#define AUXINPUT3_PIN (14u) // ST3 +#endif +#endif + +// Define auxillary output pins +#define AUXOUTPUT0_PIN (32U) +#define AUXOUTPUT1_PIN (33U) +#define AUXOUTPUT2_PIN (34U) +#define AUX_N_OUT 3 +#define AUX_OUT_MASK 0b111 + +#if KEYPAD_ENABLE +#define KEYPAD_STROBE_PIN (41U) +#endif + +#if EEPROM_ENABLE || KEYPAD_ENABLE +#define I2C_PORT 4 +#define I2C_SCL4 (24u) // Not used, for info only +#define I2C_SDA4 (25u) // Not used, for info only +#endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB.c index b85d35f6..0ce25b58 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB.c @@ -23,7 +23,7 @@ #include "driver.h" -#ifdef BOARD_T41U5XBB +#if defined(BOARD_T41U5XBB) || defined(BOARD_T41U5XBB_SS) || defined(BOARD_T41BB5X_PRO) //#include "Arduino.h" #include @@ -31,32 +31,20 @@ #include "grbl/protocol.h" -static gpio_t stx[AUX_N_IN]; +static input_signal_t *stx; static gpio_t aux_out[AUX_N_OUT]; static void aux_settings_load (void); static status_code_t aux_set_invert_out (setting_id_t id, uint_fast16_t int_value); static uint32_t aux_get_invert_out (setting_id_t setting); - -static input_signal_t aux_in[] = { - { .id = Input_Aux0, .port = &stx[0], .pin = AUXINPUT0_PIN, .group = 0 } -#if AUX_N_IN == 4 - , { .id = Input_Aux1, .port = &stx[1], .pin = AUXINPUT1_PIN, .group = 0 }, - { .id = Input_Aux2, .port = &stx[2], .pin = AUXINPUT2_PIN, .group = 0 }, - { .id = Input_Aux3, .port = &stx[3], .pin = AUXINPUT3_PIN, .group = 0 } -#endif -}; +static char input_ports[30]; // static const setting_group_detail_t aux_groups[] = { { Group_Root, Group_AuxPorts, "Aux ports"} }; static const setting_detail_t aux_settings[] = { -#if AUX_N_IN == 4 - { Settings_IoPort_InvertIn, Group_AuxPorts, "Invert I/O Port inputs", NULL, Format_Bitfield, "Port 0,Port 1,Port 2,Port 3", NULL, NULL, Setting_NonCore, &settings.ioport.invert_in.mask }, -#else - { Settings_IoPort_InvertIn, Group_AuxPorts, "Invert I/O Port inputs", NULL, Format_Bitfield, "Port 0", NULL, NULL, Setting_NonCore, &settings.ioport.invert_in.mask }, -#endif + { Settings_IoPort_InvertIn, Group_AuxPorts, "Invert I/O Port inputs", NULL, Format_Bitfield, input_ports, NULL, NULL, Setting_NonCore, &settings.ioport.invert_in.mask }, { Settings_IoPort_InvertOut, Group_AuxPorts, "Invert I/O Port outputs", NULL, Format_Bitfield, "Port 0,Port 1,Port 2", NULL, NULL, Setting_NonCoreFn, aux_set_invert_out, aux_get_invert_out }, }; @@ -142,8 +130,8 @@ static int32_t wait_on_input (bool digital, uint8_t port, wait_mode_t wait_mode, int32_t value = -1; if(digital) { - if(port < AUX_N_IN) - value = get_input(aux_in[port].port, (settings.ioport.invert_in.mask << port) & 0x01, wait_mode, timeout); + if(port < hal.port.num_digital_in) + value = get_input(stx[port].port, (settings.ioport.invert_in.mask << port) & 0x01, wait_mode, timeout); } // else if(port == 0) // value = analogRead(41); @@ -157,32 +145,25 @@ static int32_t wait_on_input (bool digital, uint8_t port, wait_mode_t wait_mode, return value; } -void board_init (void) +void board_init (pin_group_pins_t *aux_inputs) { + stx = aux_inputs->pins; + hal.port.digital_out = digital_out; hal.port.wait_on_input = wait_on_input; // hal.port.num_analog_in = 1; - hal.port.num_digital_in = AUX_N_IN; + hal.port.num_digital_in = aux_inputs->n_pins; hal.port.num_digital_out = AUX_N_OUT; details.on_get_settings = grbl.on_get_settings; grbl.on_get_settings = on_get_settings; - bool pullup; - uint32_t i = AUX_N_IN; - input_signal_t *signal; - do { - pullup = true; //?? - signal = &aux_in[--i]; - signal->irq_mode = IRQ_Mode_None; - - pinMode(signal->pin, pullup ? INPUT_PULLUP : INPUT_PULLDOWN); - signal->gpio.reg = (gpio_reg_t *)digital_pin_to_info_PGM[signal->pin].reg; - signal->gpio.bit = digital_pin_to_info_PGM[signal->pin].mask; + uint32_t i; - if(signal->port != NULL) - memcpy(signal->port, &signal->gpio, sizeof(gpio_t)); - } while(i); + for(i = 0; i < hal.port.num_digital_in; i++) { + strcat(input_ports, i == 0 ? "Port " : ",Port "); + strcat(input_ports, uitoa(i)); + } pinModeOutput(&aux_out[0], AUXOUTPUT0_PIN); pinModeOutput(&aux_out[1], AUXOUTPUT1_PIN); diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_map.h index d17b420e..ffd91d00 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_map.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_map.h @@ -5,7 +5,7 @@ Board by Phil Barrett: https://github.com/phil-barrett/grblHAL-teensy-4.x - Copyright (c) 2020 Terje Io + Copyright (c) 2020-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,12 +24,14 @@ #define BOARD_NAME "T41U5XBB" #define HAS_BOARD_INIT -void board_init (void); - #if N_AXIS > 5 #error Max number of axes is 5 for T41U5XBB #endif +#if SPINDLE_SYNC_ENABLE +#error Spindle sync is not supported for T41U5XBB +#endif + // Default pin assignments allow only one axis to be ganged or auto squared. // B axis pin numbers are used for the ganged/auto squared axis. // If a second axis is to be ganged/auto squared pin assignments needs to be changed! @@ -61,12 +63,13 @@ void board_init (void); #define Y_ENABLE_PIN (40u) #define Y_LIMIT_PIN (21u) +// Changed to use A pins rather than B pins #if Y_GANGED || Y_AUTO_SQUARE -#define Y2_STEP_PIN (26u) -#define Y2_DIRECTION_PIN (27u) -#define Y2_ENABLE_PIN (37u) +#define Y2_STEP_PIN (8u) +#define Y2_DIRECTION_PIN (9u) +#define Y2_ENABLE_PIN (38u) #if Y_AUTO_SQUARE - #define Y2_LIMIT_PIN (28u) + #define Y2_LIMIT_PIN (23u) #endif #endif @@ -79,7 +82,9 @@ void board_init (void); #define Z2_STEP_PIN (26u) #define Z2_DIRECTION_PIN (27u) #define Z2_ENABLE_PIN (37u) -#define Z2_LIMIT_PIN (28u) +#if Z_AUTO_SQUARE + #define Z2_LIMIT_PIN (28u) +#endif #endif #if N_AXIS > 3 @@ -112,7 +117,13 @@ void board_init (void); #define SAFETY_DOOR_PIN (29u) // Define probe switch input pin. -#define PROBE_PIN (15U) +#define PROBE_PIN (15u) + +#if QEI_ENABLE +#define QEI_A_PIN (30u) // ST1 +#define QEI_B_PIN (34u) // ST2 +#define QEI_SELECT_PIN (35u) // ST3 +#endif // Define auxillary input pins #define AUXINPUT0_PIN (36u) // ST0 @@ -120,11 +131,6 @@ void board_init (void); #define AUXINPUT1_PIN (30u) // ST1 #define AUXINPUT2_PIN (34u) // ST2 #define AUXINPUT3_PIN (35u) // ST3 -#define AUX_N_IN 4 -#define AUX_IN_MASK 0b1111 -#else -#define AUX_N_IN 1 -#define AUX_IN_MASK 0b1 #endif #define AUXOUTPUT0_PIN (31u) // AUX0 @@ -139,12 +145,6 @@ void board_init (void); #if EEPROM_ENABLE || KEYPAD_ENABLE #define I2C_PORT 4 -#define I2C_SCL4 (24u) // Not used, for info only -#define I2C_SDA4 (25u) // Not used, for info only -#endif - -#if QEI_ENABLE -#define QEI_A_PIN (30u) // ST1 -#define QEI_B_PIN (34u) // ST2 -#define QEI_SELECT_PIN (35u) // ST3 +#define I2C_SCL4 (24u) // Not referenced, for info only +#define I2C_SDA4 (25u) // Not referenced, for info only #endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_ss_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_ss_map.h new file mode 100644 index 00000000..3fd25062 --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/T41U5XBB_ss_map.h @@ -0,0 +1,159 @@ +/* + T41U5XBB_ss_map.h - driver code for IMXRT1062 processor (on Teensy 4.1 board) + + Part of grblHAL + + !!IMPORTANT!! This map is for a modified board, see https://github.com/terjeio/grblHAL/discussions/289 for details. + + Board by Phil Barrett: https://github.com/phil-barrett/grblHAL-teensy-4.x + + Copyright (c) 2020-2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#define BOARD_NAME "T41U5XBB" +#define HAS_BOARD_INIT + +#if N_AXIS > 5 +#error Max number of axes is 5 for T41U5XBB +#endif + +#if QEI_ENABLE && SPINDLE_SYNC_ENABLE +#error Quadrature encoder and spindle sync cannot be enabled at the same time +#endif + +// Default pin assignments allow only one axis to be ganged or auto squared. +// B axis pin numbers are used for the ganged/auto squared axis. +// If a second axis is to be ganged/auto squared pin assignments needs to be changed! +// Set to 1 to enable, 0 to disable. +#define X_GANGED 0 +#define X_AUTO_SQUARE 0 +#define Y_GANGED 0 +#define Y_AUTO_SQUARE 0 +#define Z_GANGED 0 +#define Z_AUTO_SQUARE 0 +// + +#define X_STEP_PIN (2u) +#define X_DIRECTION_PIN (3u) +#define X_ENABLE_PIN (10u) +#define X_LIMIT_PIN (20u) + +#if X_GANGED || X_AUTO_SQUARE +#define X2_STEP_PIN (26u) +#define X2_DIRECTION_PIN (27u) +#define X2_ENABLE_PIN (37u) +#if X_AUTO_SQUARE + #define X2_LIMIT_PIN (28u) +#endif +#endif + +#define Y_STEP_PIN (4u) +#define Y_DIRECTION_PIN (5u) +#define Y_ENABLE_PIN (40u) +#define Y_LIMIT_PIN (21u) + +// Changed to use A pins rather than B pins +#if Y_GANGED || Y_AUTO_SQUARE +#define Y2_STEP_PIN (8u) +#define Y2_DIRECTION_PIN (9u) +#define Y2_ENABLE_PIN (38u) +#if Y_AUTO_SQUARE + #define Y2_LIMIT_PIN (23u) +#endif +#endif + +#define Z_STEP_PIN (6u) +#define Z_DIRECTION_PIN (7u) +#define Z_ENABLE_PIN (39u) +#define Z_LIMIT_PIN (22u) + +#if Z_GANGED || Z_AUTO_SQUARE +#define Z2_STEP_PIN (26u) +#define Z2_DIRECTION_PIN (27u) +#define Z2_ENABLE_PIN (37u) +#if Z_AUTO_SQUARE + #define Z2_LIMIT_PIN (28u) +#endif +#endif + +#if N_AXIS > 3 +#define A_STEP_PIN (8u) +#define A_DIRECTION_PIN (9u) +#define A_ENABLE_PIN (38u) +#define A_LIMIT_PIN (23u) +#endif + +#if N_AXIS > 4 +#define B_STEP_PIN (26u) +#define B_DIRECTION_PIN (27u) +#define B_ENABLE_PIN (37u) +#define B_LIMIT_PIN (28u) +#endif + +// Define spindle enable and spindle direction output pins. +#define SPINDLE_ENABLE_PIN (12u) +#define SPINDLE_DIRECTION_PIN (11u) +#define SPINDLEPWMPIN (13u) // NOTE: only pin 12 or pin 13 can be assigned! + +// Define flood and mist coolant enable output pins. +#define COOLANT_FLOOD_PIN (19u) +#define COOLANT_MIST_PIN (18u) + +// Define user-control CONTROLs (cycle start, reset, feed hold, door) input pins. +#define RESET_PIN (35u) +#define FEED_HOLD_PIN (16u) +#define CYCLE_START_PIN (17u) +#define SAFETY_DOOR_PIN (29u) + +// Define probe switch input pin. +#define PROBE_PIN (15u) + +#if QEI_ENABLE +#define QEI_A_PIN (30u) // ST1 +#define QEI_B_PIN (34u) // ST2 +#define QEI_SELECT_PIN (14u) // ST3 +#endif + +#if SPINDLE_SYNC_ENABLE +#define SPINDLE_INDEX_PIN (34u) // ST2 +#define SPINDLE_PULSE_PIN (14u) // ST3 +#endif + +// Define auxillary input pins +#define AUXINPUT0_PIN (36u) // ST0 +#if !QEI_ENABLE +#define AUXINPUT1_PIN (30u) // ST1 +#if !SPINDLE_SYNC_ENABLE +#define AUXINPUT2_PIN (34u) // ST2 +#define AUXINPUT3_PIN (14u) // ST3 +#endif +#endif + +#define AUXOUTPUT0_PIN (31u) // AUX0 +#define AUXOUTPUT1_PIN (32u) // AUX1 +#define AUXOUTPUT2_PIN (33u) // AUX2 +#define AUX_N_OUT 3 +#define AUX_OUT_MASK 0b111 + +#if KEYPAD_ENABLE +#define KEYPAD_STROBE_PIN (41u) // I2C ST +#endif + +#if EEPROM_ENABLE || KEYPAD_ENABLE +#define I2C_PORT 4 +#define I2C_SCL4 (24u) // Not referenced, for info only +#define I2C_SDA4 (25u) // Not referenced, for info only +#endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/cnc_boosterpack_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/cnc_boosterpack_map.h index 6fe12ebc..c94254ab 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/cnc_boosterpack_map.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/cnc_boosterpack_map.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2020 Terje Io + Copyright (c) 2020-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,6 +25,10 @@ #error Max number of axes is 3 for CNC BoosterPack #endif +#if SPINDLE_SYNC_ENABLE +#error Spindle sync is not supported for CNC BoosterPack +#endif + #ifdef EEPROM_ENABLE #undef EEPROM_ENABLE #endif @@ -73,13 +77,13 @@ #if EEPROM_ENABLE || KEYPAD_ENABLE #define I2C_PORT 0 -#define I2C_SCL0 (19u) // Not used, for info only -#define I2C_SDA0 (18u) // Not used, for info only +#define I2C_SCL0 (19u) // Not referenced, for info only +#define I2C_SDA0 (18u) // Not referenced, for info only #endif #define UART_PORT 5 -#define UART_RX5 (21u) // Not used, for info only -#define UART_TX5 (20u) // Not used, for info only +#define UART_RX5 (21u) // Not referenced, for info only +#define UART_TX5 (20u) // Not referenced, for info only #define GPIO0_PIN (3u) #define GPIO1_PIN (29u) diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.c index 02733871..90f31a35 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.c @@ -63,6 +63,10 @@ static void ppi_timeout_isr (void); #include "odometer/odometer.h" #endif +#if OPENPNP_ENABLE +#include "openpnp/openpnp.h" +#endif + #if ETHERNET_ENABLE #include "enet.h" #if TELNET_ENABLE @@ -104,15 +108,6 @@ static void ppi_timeout_isr (void); #endif #endif -#define INPUT_GROUP_CONTROL (1 << 0) -#define INPUT_GROUP_PROBE (1 << 1) -#define INPUT_GROUP_LIMIT (1 << 2) -#define INPUT_GROUP_KEYPAD (1 << 3) -#define INPUT_GROUP_MPG (1 << 4) -#define INPUT_GROUP_QEI (1 << 5) -#define INPUT_GROUP_QEI_SELECT (1 << 6) -#define INPUT_GROUP_SPINDLE_INDEX (1 << 7) - typedef struct { volatile uint_fast8_t head; volatile uint_fast8_t tail; @@ -259,63 +254,93 @@ static gpio_t QEI_A, QEI_B; static gpio_t SpindleIndex; #endif -static input_signal_t inputpin[] = { +#ifdef AUXINPUT0_PIN + static gpio_t AuxIn0; +#endif +#ifdef AUXINPUT1_PIN + static gpio_t AuxIn1; +#endif +#ifdef AUXINPUT2_PIN + static gpio_t AuxIn2; +#endif +#ifdef AUXINPUT3_PIN + static gpio_t AuxIn3; +#endif + +input_signal_t inputpin[] = { #if ESTOP_ENABLE - { .id = Input_EStop, .port = &Reset, .pin = RESET_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_EStop, .port = &Reset, .pin = RESET_PIN, .group = PinGroup_Control }, #else - { .id = Input_Reset, .port = &Reset, .pin = RESET_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_Reset, .port = &Reset, .pin = RESET_PIN, .group = PinGroup_Control }, #endif - { .id = Input_FeedHold, .port = &FeedHold, .pin = FEED_HOLD_PIN, .group = INPUT_GROUP_CONTROL }, - { .id = Input_CycleStart, .port = &CycleStart, .pin = CYCLE_START_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_FeedHold, .port = &FeedHold, .pin = FEED_HOLD_PIN, .group = PinGroup_Control }, + { .id = Input_CycleStart, .port = &CycleStart, .pin = CYCLE_START_PIN, .group = PinGroup_Control }, #if SAFETY_DOOR_ENABLE - { .id = Input_SafetyDoor, .port = &SafetyDoor, .pin = SAFETY_DOOR_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_SafetyDoor, .port = &SafetyDoor, .pin = SAFETY_DOOR_PIN, .group = PinGroup_Control }, #endif #if defined(LIMITS_OVERRIDE_PIN) - { .id = Input_LimitsOverride, .port = &LimitsOverride, .pin = LIMITS_OVERRIDE_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_LimitsOverride, .port = &LimitsOverride, .pin = LIMITS_OVERRIDE_PIN, .group = PinGroup_Control }, #endif - { .id = Input_Probe, .port = &Probe, .pin = PROBE_PIN, .group = INPUT_GROUP_PROBE }, - { .id = Input_LimitX, .port = &LimitX, .pin = X_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_Probe, .port = &Probe, .pin = PROBE_PIN, .group = PinGroup_Probe }, +// Limit input pins must be consecutive + { .id = Input_LimitX, .port = &LimitX, .pin = X_LIMIT_PIN, .group = PinGroup_Limit }, #ifdef X2_LIMIT_PIN - { .id = Input_LimitX_Max, .port = &LimitX2, .pin = X2_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitX_Max, .port = &LimitX2, .pin = X2_LIMIT_PIN, .group = PinGroup_Limit }, #endif - { .id = Input_LimitY, .port = &LimitY, .pin = Y_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitY, .port = &LimitY, .pin = Y_LIMIT_PIN, .group = PinGroup_Limit }, #ifdef Y2_LIMIT_PIN - { .id = Input_LimitY_Max, .port = &LimitY2, .pin = Y2_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitY_Max, .port = &LimitY2, .pin = Y2_LIMIT_PIN, .group = PinGroup_Limit }, #endif - { .id = Input_LimitZ, .port = &LimitZ, .pin = Z_LIMIT_PIN, .group = INPUT_GROUP_LIMIT } + { .id = Input_LimitZ, .port = &LimitZ, .pin = Z_LIMIT_PIN, .group = PinGroup_Limit } #ifdef Z2_LIMIT_PIN - , { .id = Input_LimitZ_Max, .port = &LimitZ2, .pin = Z2_LIMIT_PIN, .group = INPUT_GROUP_LIMIT } + , { .id = Input_LimitZ_Max, .port = &LimitZ2, .pin = Z2_LIMIT_PIN, .group = PinGroup_Limit } #endif #ifdef A_LIMIT_PIN - , { .id = Input_LimitA, .port = &LimitA, .pin = A_LIMIT_PIN, .group = INPUT_GROUP_LIMIT } + , { .id = Input_LimitA, .port = &LimitA, .pin = A_LIMIT_PIN, .group = PinGroup_Limit } #endif #ifdef B_LIMIT_PIN - , { .id = Input_LimitB, .port = &LimitB, .pin = B_LIMIT_PIN, .group = INPUT_GROUP_LIMIT } + , { .id = Input_LimitB, .port = &LimitB, .pin = B_LIMIT_PIN, .group = PinGroup_Limit } #endif #ifdef C_LIMIT_PIN - , { .id = Input_LimitC, .port = &LimitC, .pin = C_LIMIT_PIN, .group = INPUT_GROUP_LIMIT } + , { .id = Input_LimitC, .port = &LimitC, .pin = C_LIMIT_PIN, .group = PinGroup_Limit } #endif +// End limit pin definitions #if MPG_MODE_ENABLE - , { .id = Input_ModeSelect, .port = &ModeSelect, .pin = MODE_PIN, .group = INPUT_GROUP_MPG } + , { .id = Input_ModeSelect, .port = &ModeSelect, .pin = MODE_PIN, .group = PinGroup_MPG } #endif #if KEYPAD_ENABLE && defined(KEYPAD_STROBE_PIN) - , { .id = Input_KeypadStrobe, .port = &KeypadStrobe, .pin = KEYPAD_STROBE_PIN, .group = INPUT_GROUP_KEYPAD } + , { .id = Input_KeypadStrobe, .port = &KeypadStrobe, .pin = KEYPAD_STROBE_PIN, .group = PinGroup_Keypad } #endif #ifdef SPINDLE_INDEX_PIN - , { .id = Input_SpindleIndex, .port = &SpindleIndex, .pin = SPINDLE_INDEX_PIN, .group = INPUT_GROUP_SPINDLE_INDEX } + , { .id = Input_SpindleIndex, .port = &SpindleIndex, .pin = SPINDLE_INDEX_PIN, .group = PinGroup_SpindleIndex } #endif #if QEI_ENABLE - , { .id = Input_QEI_A, .port = &QEI_A, .pin = QEI_A_PIN, .group = INPUT_GROUP_QEI } - , { .id = Input_QEI_B, .port = &QEI_B, .pin = QEI_B_PIN, .group = INPUT_GROUP_QEI } + , { .id = Input_QEI_A, .port = &QEI_A, .pin = QEI_A_PIN, .group = PinGroup_QEI } + , { .id = Input_QEI_B, .port = &QEI_B, .pin = QEI_B_PIN, .group = PinGroup_QEI } #if QEI_SELECT_ENABLED - , { .id = Input_QEI_Select, .port = &QEI_Select, .pin = QEI_SELECT_PIN, .group = INPUT_GROUP_QEI_SELECT } + , { .id = Input_QEI_Select, .port = &QEI_Select, .pin = QEI_SELECT_PIN, .group = PinGroup_QEI_Select } #endif #if QEI_INDEX_ENABLED - , { .id = Input_QEI_Index, .port = &QEI_Index, .pin = QEI_INDEX_PIN, .group = INPUT_GROUP_QEI } + , { .id = Input_QEI_Index, .port = &QEI_Index, .pin = QEI_INDEX_PIN, .group = PinGroup_QEI } #endif #endif +// Aux input pins must be consecutive +#ifdef AUXINPUT0_PIN + , { .id = Input_Aux0, .port = &AuxIn0, .pin = AUXINPUT0_PIN, .group = PinGroup_AuxInput } +#endif +#ifdef AUXINPUT1_PIN + , { .id = Input_Aux1, .port = &AuxIn1, .pin = AUXINPUT1_PIN, .group = PinGroup_AuxInput } +#endif +#ifdef AUXINPUT2_PIN + , { .id = Input_Aux2, .port = &AuxIn2, .pin = AUXINPUT2_PIN, .group = PinGroup_AuxInput } +#endif +#ifdef AUXINPUT3_PIN + , { .id = Input_Aux3, .port = &AuxIn3, .pin = AUXINPUT3_PIN, .group = PinGroup_AuxInput } +#endif }; +static pin_group_pins_t aux_inputs = {0}, limit_inputs = {0}; + #if USB_SERIAL_CDC || QEI_ENABLE #define ADD_MSEVENT 1 static volatile bool ms_event = false; @@ -344,7 +369,7 @@ static void spindle_set_speed (uint_fast16_t pwm_value); static modbus_stream_t modbus_stream = {0}; #endif -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE #include "grbl/spindle_sync.h" @@ -389,6 +414,7 @@ static void enetStreamWriteS (const char *data) .read = TCPStreamGetC, .write = TCPStreamWriteS, .write_all = enetStreamWriteS, + .write_char = TCPStreamPutC, .get_rx_buffer_available = TCPStreamRxFree, .reset_read_buffer = TCPStreamRxFlush, .cancel_read_buffer = TCPStreamRxCancel, @@ -403,6 +429,7 @@ static void enetStreamWriteS (const char *data) .read = WsStreamGetC, .write = WsStreamWriteS, .write_all = enetStreamWriteS, + .write_char = WsStreamPutC, .get_rx_buffer_available = WsStreamRxFree, .reset_read_buffer = WsStreamRxFlush, .cancel_read_buffer = WsStreamRxCancel, @@ -418,6 +445,7 @@ static void enetStreamWriteS (const char *data) .type = StreamType_Serial, .read = usb_serialGetC, .write = usb_serialWriteS, + .write_char = usb_serialPutC, #if ETHERNET_ENABLE .write_all = enetStreamWriteS, #else @@ -434,6 +462,7 @@ const io_stream_t serial_stream = { .type = StreamType_Serial, .read = serialGetC, .write = serialWriteS, + .write_char = serialPutC, #if ETHERNET_ENABLE .write_all = enetStreamWriteS, #else @@ -698,7 +727,7 @@ static void stepperCyclesPerTick (uint32_t cycles_per_tick) // stepper_t struct is defined in grbl/stepper.h static void stepperPulseStart (stepper_t *stepper) { -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE if(stepper->new_block && stepper->exec_segment->spindle_sync) { spindle_tracker.stepper_pulse_start_normal = hal.stepper.pulse_start; hal.stepper.pulse_start = stepperPulseStartSynchronized; @@ -723,7 +752,7 @@ static void stepperPulseStart (stepper_t *stepper) // stepper_t struct is defined in grbl/stepper.h static void stepperPulseStartDelayed (stepper_t *stepper) { -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE if(stepper->new_block && stepper->exec_segment->spindle_sync) { spindle_tracker.stepper_pulse_start_normal = hal.stepper.pulse_start; hal.stepper.pulse_start = stepperPulseStartSynchronized; @@ -755,7 +784,7 @@ static void stepperPulseStartDelayed (stepper_t *stepper) } } -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE // Spindle sync version: sets stepper direction and pulse pins and starts a step pulse. // Switches back to "normal" version if spindle synchronized motion is finished. @@ -975,18 +1004,17 @@ static void StepperDisableMotors (axes_signals_t axes, squaring_mode_t mode) // stepper drivers for sensorless homing. static void limitsEnable (bool on, bool homing) { - uint32_t i = sizeof(inputpin) / sizeof(input_signal_t); + uint32_t i = limit_inputs.n_pins; on &= settings.limits.flags.hard_enabled; do { - if(inputpin[--i].group == INPUT_GROUP_LIMIT) { - inputpin[i].gpio.reg->ISR = inputpin[i].gpio.bit; // Clear interrupt. - if(on) - inputpin[i].gpio.reg->IMR |= inputpin[i].gpio.bit; // Enable interrupt. - else - inputpin[i].gpio.reg->IMR &= ~inputpin[i].gpio.bit; // Disable interrupt. - } + i--; + limit_inputs.pins[i].gpio.reg->ISR = limit_inputs.pins[i].gpio.bit; // Clear interrupt. + if(on) + limit_inputs.pins[i].gpio.reg->IMR |= limit_inputs.pins[i].gpio.bit; // Enable interrupt. + else + limit_inputs.pins[i].gpio.reg->IMR &= ~limit_inputs.pins[i].gpio.bit; // Disable interrupt. } while(i); } @@ -1055,7 +1083,7 @@ inline static void spindle_off () inline static void spindle_on () { DIGITAL_OUT(spindleEnable, !settings.spindle.invert.on); -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE spindleDataReset(); #endif } @@ -1155,7 +1183,7 @@ static void spindleSetStateVariable (spindle_state_t state, float rpm) spindle_set_speed(spindle_compute_pwm_value(&spindle_pwm, rpm, false)); } -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE if(settings.spindle.at_speed_tolerance > 0.0f) { float tolerance = rpm * settings.spindle.at_speed_tolerance / 100.0f; spindle_data.rpm_low_limit = rpm - tolerance; @@ -1181,7 +1209,7 @@ static spindle_state_t spindleGetState (void) if(pwmEnabled) state.on |= pwmEnabled; -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE float rpm = spindleGetData(SpindleData_RPM)->rpm; state.at_speed = settings.spindle.at_speed_tolerance <= 0.0f || (rpm >= spindle_data.rpm_low_limit && rpm <= spindle_data.rpm_high_limit); #endif @@ -1206,7 +1234,7 @@ static void spindlePulseOn (uint_fast16_t pulse_length) #endif -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE static spindle_data_t *spindleGetData (spindle_data_request_t request) { @@ -1397,7 +1425,7 @@ static void settings_changed (settings_t *settings) hal.spindle.set_state = spindleSetState; #endif -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE if((hal.spindle.get_data = settings->spindle.ppr > 0 ? spindleGetData : NULL) && spindle_encoder.ppr != settings->spindle.ppr) { @@ -1630,7 +1658,7 @@ static void settings_changed (settings_t *settings) signal->gpio.reg->ISR = signal->gpio.bit; // Clear interrupt. - if(signal->group != INPUT_GROUP_LIMIT) // If pin is not a limit pin + if(signal->group != PinGroup_Limit) // If pin is not a limit pin signal->gpio.reg->IMR |= signal->gpio.bit; // enable interrupt signal->active = (signal->gpio.reg->DR & signal->gpio.bit) != 0; @@ -1877,7 +1905,7 @@ static bool driver_setup (settings_t *settings) *(portConfigRegister(SPINDLEPWMPIN)) = 1; -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE CCM_CCGR1 |= CCM_CCGR1_GPT1_BUS(CCM_CCGR_ON); CCM_CMEOR |= CCM_CMEOR_MOD_EN_OV_GPT; @@ -2078,7 +2106,7 @@ bool driver_init (void) options[strlen(options) - 1] = '\0'; hal.info = "iMXRT1062"; - hal.driver_version = "210313"; + hal.driver_version = "210509"; #ifdef BOARD_NAME hal.board = BOARD_NAME; #endif @@ -2122,7 +2150,7 @@ bool driver_init (void) hal.spindle.pulse_on = spindlePulseOn; #endif #endif -#ifdef SPINDLE_SYNC_ENABLE +#if SPINDLE_SYNC_ENABLE hal.driver_cap.spindle_sync = On; hal.driver_cap.spindle_at_speed = On; #endif @@ -2226,8 +2254,27 @@ bool driver_init (void) uart_debug_write(ASCII_EOL "UART Debug:" ASCII_EOL); #endif + uint32_t i; + input_signal_t *signal; + + for(i = 0 ; i < sizeof(inputpin) / sizeof(input_signal_t); i++) { + signal = &inputpin[i]; + + if(signal->group == PinGroup_AuxInput) { + if(aux_inputs.pins == NULL) + aux_inputs.pins = signal; + aux_inputs.n_pins++; + } + + if(signal->group == PinGroup_Limit) { + if(limit_inputs.pins == NULL) + limit_inputs.pins = signal; + limit_inputs.n_pins++; + } + } + #ifdef HAS_BOARD_INIT - board_init(); + board_init(&aux_inputs); #endif #if ETHERNET_ENABLE @@ -2247,7 +2294,9 @@ bool driver_init (void) plasma_init(); #endif - my_plugin_init(); +#if OPENPNP_ENABLE + openpnp_init(); +#endif #if ODOMETER_ENABLE odometer_init(); // NOTE: this *must* be last plugin to be initialized as it claims storage at the end of NVS. @@ -2298,7 +2347,7 @@ static void stepper_pulse_isr_delayed (void) TMR4_CTRL0 |= TMR_CTRL_CM(0b001); } -#if defined(SPINDLE_SYNC_ENABLE) && SPINDLE_PULSE_PIN == 14 +#if SPINDLE_SYNC_ENABLE && SPINDLE_PULSE_PIN == 14 static void spindle_pulse_isr (void) { @@ -2386,15 +2435,15 @@ static void debounce_isr (void) grp |= signal->group; } - if(grp & INPUT_GROUP_LIMIT) + if(grp & PinGroup_Limit) hal.limits.interrupt_callback(limitsGetState()); - if(grp & INPUT_GROUP_CONTROL) + if(grp & PinGroup_Control) hal.control.interrupt_callback(systemGetState()); #if QEI_SELECT_ENABLED - if(grp & INPUT_GROUP_QEI_SELECT) { + if(grp & PinGroup_QEI_Select) { if(!qei.dbl_click_timeout) qei.dbl_click_timeout = qei.encoder.settings->dbl_click_window; else if(qei.dbl_click_timeout < qei.encoder.settings->dbl_click_window - 40) { @@ -2437,7 +2486,7 @@ static void gpio_isr (void) debounce = true; } else { #if QEI_ENABLE - if(inputpin[i].group & INPUT_GROUP_QEI) { + if(inputpin[i].group & PinGroup_QEI) { qei_update(); /* QEI_A.reg->IMR &= ~QEI_A.bit; // Switch off @@ -2450,8 +2499,8 @@ static void gpio_isr (void) } else #endif -#if defined(SPINDLE_SYNC_ENABLE) && defined(SPINDLE_INDEX_PIN) - if(inputpin[i].group & INPUT_GROUP_SPINDLE_INDEX) { +#if SPINDLE_SYNC_ENABLE && defined(SPINDLE_INDEX_PIN) + if(inputpin[i].group & PinGroup_SpindleIndex) { spindleLock = true; spindle_encoder.counter.index_count++; spindle_encoder.counter.last_index = GPT2_CNT; @@ -2469,18 +2518,18 @@ static void gpio_isr (void) TMR3_CTRL0 |= TMR_CTRL_CM(0b001); } - if(grp & INPUT_GROUP_LIMIT) { + if(grp & PinGroup_Limit) { limit_signals_t state = limitsGetState(); if(limit_signals_merge(state).value) //TODO: add check for limit switches having same state as when limit_isr were invoked? hal.limits.interrupt_callback(state); } - if(grp & INPUT_GROUP_CONTROL) + if(grp & PinGroup_Control) hal.control.interrupt_callback(systemGetState()); #if QEI_SELECT_ENABLED - if(grp & INPUT_GROUP_QEI_SELECT) { + if(grp & PinGroup_QEI_Select) { if(!qei.dbl_click_timeout) qei.dbl_click_timeout = qei.encoder.settings->dbl_click_window; else if(qei.dbl_click_timeout < qei.encoder.settings->dbl_click_window - 40) { @@ -2496,7 +2545,7 @@ static void gpio_isr (void) static bool mpg_mutex = false; - if((grp & INPUT_GROUP_MPG) && !mpg_mutex) { + if((grp & PinGroup_MPG) && !mpg_mutex) { mpg_mutex = true; modeChange(); // hal.delay_ms(50, modeChange); @@ -2505,7 +2554,7 @@ static void gpio_isr (void) #endif #if KEYPAD_ENABLE - if(grp & INPUT_GROUP_KEYPAD) + if(grp & PinGroup_Keypad) keypad_keyclick_handler(!(KeypadStrobe.reg->DR & KeypadStrobe.bit)); #endif } diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.h index 8a9641f0..e45f7daa 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/driver.h @@ -1,5 +1,5 @@ /* - driver.h - driver code for IMXRT1062 processor (on Teensy 4.0 board) + driver.h - driver code for IMXRT1062 processor (on Teensy 4.x board) Part of grblHAL @@ -36,7 +36,7 @@ #include "grbl/hal.h" #include "grbl/nuts_bolts.h" - +#include "grbl/crossbar.h" #define DIGITAL_IN(gpio) (!!(gpio.reg->DR & gpio.bit)) #define DIGITAL_OUT(gpio, on) { if(on) gpio.reg->DR_SET = gpio.bit; else gpio.reg->DR_CLEAR = gpio.bit; } @@ -66,6 +66,9 @@ #ifndef ODOMETER_ENABLE #define ODOMETER_ENABLE 0 #endif +#ifndef OPENPNP_ENABLE +#define OPENPNP_ENABLE 0 +#endif #ifndef ETHERNET_ENABLE #define ETHERNET_ENABLE 0 @@ -89,6 +92,9 @@ #ifndef EEPROM_IS_FRAM #define EEPROM_IS_FRAM 0 #endif +#ifndef SPINDLE_SYNC_ENABLE +#define SPINDLE_SYNC_ENABLE 0 +#endif #ifndef TRINAMIC_ENABLE #define TRINAMIC_ENABLE 0 #endif @@ -160,10 +166,10 @@ #include "T40X101_map.h" #elif defined(BOARD_T41U5XBB) #include "T41U5XBB_map.h" -#elif defined(BOARD_T41U5XSS) - #include "T41U5XSS_map.h" -#elif defined(BOARD_T41PROBB) - #include "T41ProBB_map.h" +#elif defined(BOARD_T41U5XBB_SS) + #include "T41U5XBB_ss_map.h" +#elif defined(BOARD_T41BB5X_PRO) + #include "T41BB5X_Pro_map.h" #elif defined(BOARD_MY_MACHINE) #include "my_machine_map.h" #else // default board @@ -245,46 +251,6 @@ #error "QEI_ENABLE requires encoder input pins A and B to be defined!" #endif -typedef enum { - Input_Probe = 0, - Input_Reset, - Input_FeedHold, - Input_CycleStart, - Input_SafetyDoor, - Input_LimitsOverride, - Input_EStop, - Input_ModeSelect, - Input_LimitX, - Input_LimitX_Max, - Input_LimitY, - Input_LimitY_Max, - Input_LimitZ, - Input_LimitZ_Max, - Input_LimitA, - Input_LimitA_Max, - Input_LimitB, - Input_LimitB_Max, - Input_LimitC, - Input_LimitC_Max, - Input_KeypadStrobe, - Input_QEI_A, - Input_QEI_B, - Input_QEI_Select, - Input_QEI_Index, - Input_SpindleIndex, - Input_Aux0, - Input_Aux1, - Input_Aux2, - Input_Aux3 -} input_t; - -typedef enum { - IRQ_Mode_None = 0b00, - IRQ_Mode_Change = 0b01, - IRQ_Mode_Rising = 0b10, - IRQ_Mode_Falling = 0b11 -} irq_mode_t; - typedef struct { volatile uint32_t DR; volatile uint32_t GDIR; @@ -306,17 +272,22 @@ typedef struct { } gpio_t; typedef struct { - input_t id; - uint8_t group; + pin_function_t id; + pin_group_t group; uint8_t pin; gpio_t *port; gpio_t gpio; // doubled up for now for speed... - irq_mode_t irq_mode; + pin_irq_mode_t irq_mode; uint8_t offset; volatile bool active; volatile bool debounce; } input_signal_t; +typedef struct { + uint8_t n_pins; + input_signal_t *pins; +} pin_group_pins_t; + // The following struct is pulled from the Teensy Library core, Copyright (c) 2019 PJRC.COM, LLC. typedef struct { @@ -330,9 +301,12 @@ typedef struct { void selectStream (stream_type_t stream); void pinModeOutput (gpio_t *gpio, uint8_t pin); - uint32_t xTaskGetTickCount(); +#ifdef HAS_BOARD_INIT +void board_init(pin_group_pins_t *aux_inputs); +#endif + #ifdef UART_DEBUG void uart_debug_write (char *s); #endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/generic_map.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/generic_map.h index bd051dc4..ce6f8445 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/generic_map.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/generic_map.h @@ -1,9 +1,9 @@ /* - generic_map.h - driver code for IMXRT1062 processor (on Teensy 4.0 board) + generic_map.h - driver code for IMXRT1062 processor (on Teensy 4.x board) Part of grblHAL - Copyright (c) 2020 Terje Io + Copyright (c) 2020-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ along with Grbl. If not, see . */ +#if SPINDLE_SYNC_ENABLE +#error Spindle sync is not supported +#endif + // Define step pulse output pins. #define X_STEP_PIN (2u) #define Y_STEP_PIN (4u) @@ -69,8 +73,8 @@ #if EEPROM_ENABLE || KEYPAD_ENABLE #define I2C_PORT 4 -#define I2C_SCL4 (24u) // Not used, for info only -#define I2C_SDA4 (25u) // Not used, for info only +#define I2C_SCL4 (24u) // Not referenced, for info only +#define I2C_SDA4 (25u) // Not referenced, for info only #endif #if QEI_ENABLE diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/config.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/config.h index 2c1662b7..ef0e9192 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/config.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/config.h @@ -579,6 +579,7 @@ // for professional CNC machines, regardless of where the limit switches are located. Set this // define to 1 to force Grbl to always set the machine origin at the homed location despite switch orientation. //#define HOMING_FORCE_SET_ORIGIN // Default disabled. Uncomment to enable. +#define HOMING_FORCE_SET_ORIGIN 1 // To prevent the homing cycle from racking the dual axis, when one limit triggers before the // other due to switch failure or noise, the homing cycle will automatically abort if the second diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/crossbar.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/crossbar.h index 1b650b21..70949a96 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/crossbar.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/crossbar.h @@ -24,6 +24,60 @@ #ifndef _CROSSBAR_H_ #define _CROSSBAR_H_ +typedef enum { + Input_Probe = 0, + Input_Reset, + Input_FeedHold, + Input_CycleStart, + Input_SafetyDoor, + Input_LimitsOverride, + Input_EStop, + Input_ModeSelect, + Input_LimitX, + Input_LimitX_Max, + Input_LimitY, + Input_LimitY_Max, + Input_LimitZ, + Input_LimitZ_Max, + Input_LimitA, + Input_LimitA_Max, + Input_LimitB, + Input_LimitB_Max, + Input_LimitC, + Input_LimitC_Max, + Input_KeypadStrobe, + Input_QEI_A, + Input_QEI_B, + Input_QEI_Select, + Input_QEI_Index, + Input_SpindleIndex, + Input_Aux0, + Input_Aux1, + Input_Aux2, + Input_Aux3 +} pin_function_t; + +typedef enum { + IRQ_Mode_None = 0b00, + IRQ_Mode_Change = 0b01, + IRQ_Mode_Rising = 0b10, + IRQ_Mode_Falling = 0b11 +} pin_irq_mode_t; + +typedef enum { + PinGroup_Control = (1<<0), + PinGroup_Limit = (1<<1), + PinGroup_Probe = (1<<2), + PinGroup_Keypad = (1<<3), + PinGroup_MPG = (1<<4), + PinGroup_QEI = (1<<5), + PinGroup_QEI_Select = (1<<6), + PinGroup_SpindlePulse = (1<<7), + PinGroup_SpindleIndex = (1<<8), + PinGroup_AuxInput = (1<<9), + PinGroup_AuxOutput = (1<<10) +} pin_group_t; + typedef bool (*xbar_get_value_ptr)(void); typedef void (*xbar_set_value_ptr)(bool on); typedef void (*xbar_event_ptr)(bool on); diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/errors.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/errors.h index d47fb52f..ea7bcb4b 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/errors.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/errors.h @@ -83,6 +83,7 @@ typedef enum { Status_MotorFault = 51, Status_SettingValueOutOfRange = 52, Status_SettingDisabled = 53, + Status_GcodeInvalidRetractPosition = 54, // Some error codes as defined in bdring's ESP32 port Status_SDMountError = 60, @@ -156,6 +157,7 @@ PROGMEM static const status_detail_t status_detail[] = { { Status_MotorFault, "Motor fault", "Motor fault." }, { Status_SettingValueOutOfRange, "Value out of range.", "Setting value is out of range." }, { Status_SettingDisabled, "Setting disabled", "Setting is not available, possibly due to limited driver support." }, + { Status_GcodeInvalidRetractPosition, "Invalid gcode ID:54", "Retract position is less than drill depth." }, { Status_SDMountError, "SD Card", "SD Card mount failed." }, { Status_SDReadError, "SD Card", "SD Card file open/read failed." }, { Status_SDFailedOpenDir, "SD Card", "SD Card directory listing failed." }, diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/gcode.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/gcode.c index df927e09..de2fef35 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/gcode.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/gcode.c @@ -636,11 +636,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. - - if (axis_command) - FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } - - axis_command = AxisCommand_ToolLengthOffset; + // NOTE: cannot find the NIST statement referenced above, changed to match LinuxCNC behaviour in build 20210513. if (int_value == 49) // G49 gc_block.modal.tool_offset_mode = ToolLengthOffset_Cancel; #ifdef N_TOOLS @@ -649,9 +645,12 @@ status_code_t gc_execute_block(char *block, char *message) else if (mantissa == 20) // G43.2 gc_block.modal.tool_offset_mode = ToolLengthOffset_ApplyAdditional; #endif - else if (mantissa == 10) // G43.1 + else if (mantissa == 10) { // G43.1 + if (axis_command) + FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } + axis_command = AxisCommand_ToolLengthOffset; gc_block.modal.tool_offset_mode = ToolLengthOffset_EnableDynamic; - else + } else FAIL(Status_GcodeUnsupportedCommand); // [Unsupported G43.x command] mantissa = 0; // Set to zero to indicate valid non-integer G command. break; @@ -1316,7 +1315,9 @@ status_code_t gc_execute_block(char *block, char *message) // Pre-convert XYZ coordinate values to millimeters, if applicable. uint_fast8_t idx = N_AXIS; if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used. - if (bit_istrue(axis_words.mask, bit(--idx))) + idx--; +// if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (bit_istrue(axis_words.mask, bit(idx))) gc_block.values.xyz[idx] *= MM_PER_INCH; } while(idx); @@ -1412,13 +1413,13 @@ status_code_t gc_execute_block(char *block, char *message) // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if N_TOOLS defined. // [G43.1 Errors]: Motion command in same line. - // [G43.2 Errors]: Motion command in same line. Tool number not in the tool table, - // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid - // axis that is configured (in config.h). There should be an error if the configured axis - // is absent or if any of the other axis words are present. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates called in block. + // [G43.2 Errors]: Tool number not in the tool table, + if (command_words.G8) { // Indicates called in block. #ifdef TOOL_LENGTH_OFFSET_AXIS + // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid + // axis that is configured (in config.h). There should be an error if the configured axis + // is absent or if any of the other axis words are present. if(gc_block.modal.tool_offset_mode == ToolLengthOffset_EnableDynamic) { if (axis_words.mask ^ bit(TOOL_LENGTH_OFFSET_AXIS)) FAIL(Status_GcodeG43DynamicAxisError); @@ -1819,12 +1820,10 @@ status_code_t gc_execute_block(char *block, char *message) else if(gc_block.values.l <= 0) FAIL(Status_NonPositiveValue); // [L <= 0] - if (value_words.r) { - gc_state.canned.retract_position = gc_block.values.r; - if(gc_state.modal.distance_incremental) - gc_state.canned.retract_position += gc_state.position[plane.axis_linear]; - gc_state.canned.retract_position = gc_block.modal.coord_system.xyz[plane.axis_linear] + gc_state.canned.retract_position; - } + if(value_words.r) + gc_state.canned.retract_position = gc_block.values.r + (gc_block.modal.distance_incremental + ? gc_state.position[plane.axis_linear] + : gc_get_block_offset(&gc_block, plane.axis_linear)); idx = N_AXIS; do { @@ -1832,11 +1831,13 @@ status_code_t gc_execute_block(char *block, char *message) gc_state.canned.xyz[idx] = gc_block.values.xyz[idx]; if(idx != plane.axis_linear) gc_state.canned.xyz[idx] -= gc_state.position[idx]; + else if(gc_block.modal.distance_incremental) + gc_state.canned.xyz[idx] = gc_state.canned.retract_position + (gc_state.canned.xyz[idx] - gc_state.position[idx]); } } while(idx); if(gc_state.canned.retract_position < gc_state.canned.xyz[plane.axis_linear]) - FAIL(Status_GcodeAxisCommandConflict); + FAIL(Status_GcodeInvalidRetractPosition); value_words.r = value_words.l = Off; // Remove single-meaning value words. @@ -2030,7 +2031,9 @@ status_code_t gc_execute_block(char *block, char *message) if (gc_block.modal.units_imperial) { idx = 3; do { // Axes indices are consistent, so loop may be used to save flash space. - if (ijk_words.mask & bit(--idx)) + idx--; +// if (ijk_words.mask & bit(idx) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (ijk_words.mask & bit(idx)) gc_block.values.ijk[idx] *= MM_PER_INCH; } while(idx); } @@ -2412,7 +2415,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms // of execution. The error-checking step would simply load the offset value into the correct // axis of the block XYZ value array. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates a change. + if (command_words.G8) { // Indicates a change. bool tlo_changed = false; @@ -2725,11 +2728,11 @@ status_code_t gc_execute_block(char *block, char *message) hal.coolant.set_state(gc_state.modal.coolant); sys.report.spindle = On; // Set to report change immediately sys.report.coolant = On; // ... - - if(grbl.on_program_completed) - grbl.on_program_completed(gc_state.modal.program_flow); } + if(grbl.on_program_completed) + grbl.on_program_completed(gc_state.modal.program_flow, check_mode); + // Clear any pending output commands while(output_commands) { output_command_t *next = output_commands->next; diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/grbl.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/grbl.h index 46556f33..b8522819 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/grbl.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_VERSION_BUILD "20210313" +#define GRBL_VERSION_BUILD "20210513" // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/hal.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/hal.h index 1b6de673..baa34852 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/hal.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/hal.h @@ -73,16 +73,18 @@ typedef void (*driver_reset_ptr)(void); // I/O stream +typedef int16_t (*stream_read_ptr)(void); typedef void (*stream_write_ptr)(const char *s); +typedef bool (*stream_write_char_ptr)(const char c); typedef bool (*enqueue_realtime_command_ptr)(char data); typedef struct { stream_type_t type; uint16_t (*get_rx_buffer_available)(void); -// bool (*stream_write)(char c); - stream_write_ptr write; // write string to current I/O stream only. - stream_write_ptr write_all; // write string to all active output streams. - int16_t (*read)(void); + stream_write_ptr write; // write string to current I/O stream only. + stream_write_ptr write_all; // write string to all active output streams. + stream_write_char_ptr write_char; // write single character to current I/O stream only. + stream_read_ptr read; void (*reset_read_buffer)(void); void (*cancel_read_buffer)(void); bool (*suspend_read)(bool await); @@ -340,14 +342,15 @@ typedef struct { typedef void (*on_state_change_ptr)(sys_state_t state); typedef void (*on_probe_completed_ptr)(void); -typedef void (*on_program_completed_ptr)(program_flow_t program_flow); +typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode); typedef void (*on_execute_realtime_ptr)(sys_state_t state); typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd); +typedef bool (*on_unknown_realtime_cmd_ptr)(char c); typedef void (*on_report_options_ptr)(bool newopt); typedef void (*on_report_command_help_ptr)(void); typedef void (*on_global_settings_restore_ptr)(void); typedef setting_details_t *(*on_get_settings_ptr)(void); // NOTE: this must match the signature of the same definition in - // the setting_details_t structure in settings.h! + // the setting_details_t structure in settings.h! typedef void (*on_realtime_report_ptr)(stream_write_ptr stream_write, report_tracking_flags_t report); typedef void (*on_unknown_feedback_message_ptr)(stream_write_ptr stream_write); typedef bool (*on_laser_ppi_enable_ptr)(uint_fast16_t ppi, uint_fast16_t pulse_length); @@ -370,6 +373,7 @@ typedef struct { on_get_settings_ptr on_get_settings; on_realtime_report_ptr on_realtime_report; on_unknown_feedback_message_ptr on_unknown_feedback_message; + on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; on_unknown_sys_command_ptr on_unknown_sys_command; // return Status_Unhandled if not handled. on_get_commands_ptr on_get_commands; on_user_command_ptr on_user_command; diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/limits.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/limits.c index 340be0f3..7289e710 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/limits.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/limits.c @@ -277,8 +277,8 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar if(auto_square.mask) { float fail_distance = (-settings.homing.dual_axis.fail_length_percent / 100.0f) * settings.axis[dual_motor_axis].max_travel; - fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_min); - fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_min); autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm); } diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/motion_control.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/motion_control.c index ef4b8414..fc2ce283 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/motion_control.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/motion_control.c @@ -562,7 +562,6 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ if(repeats && gc_state.modal.distance_incremental) { position[plane.axis_0] += canned->xyz[plane.axis_0]; position[plane.axis_1] += canned->xyz[plane.axis_1]; - position[plane.axis_linear] = canned->prev_position; if(!mc_line(position, pl_data)) return; } diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/protocol.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/protocol.c index a15f70e9..030b947c 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/protocol.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/protocol.c @@ -45,9 +45,8 @@ typedef union { uint8_t overflow :1, comment_parentheses :1, comment_semicolon :1, - line_is_comment :1, block_delete :1, - unassigned :3; + unassigned :4; }; } line_flags_t; @@ -164,7 +163,7 @@ bool protocol_main_loop (void) int16_t c; char eol = '\0'; line_flags_t line_flags = {0}; - bool nocaps = false; + bool nocaps = false, line_is_comment = false; xcommand[0] = '\0'; user_message.show = keep_rt_commands = false; @@ -178,7 +177,7 @@ bool protocol_main_loop (void) if(c == ASCII_CAN) { eol = xcommand[0] = '\0'; - keep_rt_commands = nocaps = user_message.show = false; + keep_rt_commands = nocaps = line_is_comment = user_message.show = false; char_counter = line_flags.value = 0; gc_state.last_error = Status_OK; @@ -207,7 +206,7 @@ bool protocol_main_loop (void) // Direct and execute one line of formatted input, and report status of execution. if (line_flags.overflow) // Report line overflow error. gc_state.last_error = Status_Overflow; - else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_flags.line_is_comment) // Empty or comment line. For syncing purposes. + else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_is_comment) // Empty or comment line. For syncing purposes. gc_state.last_error = Status_OK; else if (line[0] == '$') {// Grbl '$' system command if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) { @@ -275,7 +274,7 @@ bool protocol_main_loop (void) case '(': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; if(!keep_rt_commands) { // Enable comments flag and ignore all characters until ')' or EOL unless it is a message. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. @@ -297,7 +296,7 @@ bool protocol_main_loop (void) case ';': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. if(!keep_rt_commands) { if((line_flags.comment_semicolon = !line_flags.comment_parentheses)) @@ -855,13 +854,13 @@ ISR_CODE bool protocol_enqueue_realtime_command (char c) default: if(c < ' ' || (c >= 0x7F && c <= 0xBF)) - drop = true; + drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c); break; } // 2. Process printable ASCII characters and top-bit set characters // If legacy realtime commands are disabled they are returned to the input stream - // when appering in settings ($ commands) or comments + // when appearing in settings ($ commands) or comments if(!drop) switch ((unsigned char)c) { diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.c index 395d44fc..e0144ae8 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.c @@ -32,6 +32,7 @@ #include "limits.h" #include "nvs_buffer.h" #include "tool_change.h" +#include "state_machine.h" #ifdef ENABLE_BACKLASH_COMPENSATION #include "motion_control.h" #endif @@ -99,6 +100,7 @@ PROGMEM const settings_t defaults = { .steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK, .steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK, .steppers.deenergize.mask = ST_DEENERGIZE_MASK, +// .steppers.is_rotational.mask = 0, #if DEFAULT_HOMING_ENABLE .homing.flags.enabled = DEFAULT_HOMING_ENABLE, .homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK, @@ -446,6 +448,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL }, { Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL }, { Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL } +// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL } }; static setting_details_t details = { @@ -456,6 +459,56 @@ static setting_details_t details = { .save = settings_write_global }; +// Acceleration override + +static struct { + bool valid; + float acceleration[N_AXIS]; +} override_backup = { .valid = false }; + +static void save_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + do { + idx--; + override_backup.acceleration[idx] = settings.axis[idx].acceleration; + } while(idx); + + override_backup.valid = true; +} + +static void restore_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + if(override_backup.valid) do { + idx--; + settings.axis[idx].acceleration = override_backup.acceleration[idx]; + } while(idx); +} + +// Temporarily override acceleration, if 0 restore to setting value. +// Note: only allowed when current state is idle. +bool settings_override_acceleration (uint8_t axis, float acceleration) +{ + if(state_get() != STATE_IDLE) + return false; + + if(acceleration <= 0.0f) { + if(override_backup.valid) + settings.axis[axis].acceleration = override_backup.acceleration[axis]; + } else { + if(!override_backup.valid) + save_override_backup(); + settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value? + } + + return true; +} + +// --- + setting_details_t *settings_get_details (void) { details.on_get_settings = grbl.on_get_settings; @@ -796,7 +849,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisAcceleration: - settings.axis[idx].acceleration = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. + settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. break; case Setting_AxisMaxTravel: @@ -1232,6 +1285,9 @@ bool read_global_settings () // Write Grbl global settings and version number to persistent storage void settings_write_global (void) { + if(override_backup.valid) + restore_override_backup(); + if(hal.nvs.type != NVS_None) { hal.nvs.put_byte(0, SETTINGS_VERSION); hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true); diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.h index f62a6015..dfadc1ea 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/settings.h @@ -226,6 +226,7 @@ typedef enum { Settings_IoPort_OD_Enable = 373, Settings_ModBus_BaudRate = 374, Settings_ModBus_RXTimeout = 375, + Settings_Axis_Rotational = 376, Setting_EncoderSettingsBase = 400, // NOTE: Reserving settings values >= 400 for encoder settings. Up to 449. Setting_EncoderSettingsMax = 449, @@ -441,6 +442,7 @@ typedef struct { axes_signals_t dir_invert; axes_signals_t enable_invert; axes_signals_t deenergize; +// axes_signals_t is_rotational; or add to axis_settings_t below as bitmap union? rotational axes are not scaled in imperial mode float pulse_microseconds; float pulse_delay_microseconds; uint16_t idle_lock_time; // If value = 255, steppers do not disable. @@ -708,6 +710,9 @@ bool settings_write_tool_data (tool_data_t *tool_data); // Read selected tool data from persistent storage bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data); +// Temporarily override acceleration, if 0 restore to configured setting value +bool settings_override_acceleration (uint8_t axis, float acceleration); + setting_details_t *settings_get_details (void); bool settings_is_group_available (setting_group_t group); bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data); diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.c index ba9b6046..da86fe65 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.c @@ -26,7 +26,7 @@ static stream_rx_buffer_t rxbackup; // "dummy" version of serialGetC -static int16_t stream_get_null (void) +int16_t stream_get_null (void) { return SERIAL_NO_DATA; } diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.h index 07e62c08..f89270c2 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/grbl/stream.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ #ifndef _STREAM_H_ #define _STREAM_H_ +#define ASCII_SOH 0x01 +#define ASCII_STX 0x02 #define ASCII_ETX 0x03 +#define ASCII_EOT 0x04 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 @@ -104,7 +107,16 @@ typedef struct { char data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; +#ifdef __cplusplus +extern "C" { +#endif + +int16_t stream_get_null (void); bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); void stream_rx_backup (stream_rx_buffer_t *rxbuffer); +#ifdef __cplusplus +} +#endif + #endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/laser/coolant.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/laser/coolant.c index 32ffc934..fc59771c 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/laser/coolant.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/laser/coolant.c @@ -136,17 +136,17 @@ static void driverReset (void) } } -static void onProgramCompleted (program_flow_t program_flow) +static void onProgramCompleted (program_flow_t program_flow, bool check_mode) { // Keep coolant and exhaust (flood) on? Setting? Delayed task? - if(coolant_on) { + if(coolant_on && !check_mode) { coolant_on = false; hal.port.digital_out(LASER_COOLANT_ON_PORT, false); sys.report.coolant = On; // Set to report change immediately } if(on_program_completed) - on_program_completed(program_flow); + on_program_completed(program_flow, check_mode); } static void onRealtimeReport (stream_write_ptr stream_write, report_tracking_flags_t report) diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/my_machine.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/my_machine.h index ce499b6a..16c58615 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/my_machine.h +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/my_machine.h @@ -25,6 +25,8 @@ // If none is enabled pin mappings from generic_map.h will be used //#define BOARD_T40X101 #define BOARD_T41U5XBB +//#define BOARD_T41U5XBB_SS // For a modified T41U5XBB board, allows spindle sync to be enabled. +//#define BOARD_T41BB5X_PRO //#define BOARD_CNC_BOOSTERPACK //#define BOARD_MY_MACHINE // Add my_machine_map.h before enabling this! @@ -36,9 +38,10 @@ ----------------------|-----------|---------|--------|--------|--------| BOARD_T40X101 | no | no | yes | yes³ | max 4 | BOARD_T41U5XBB | yes | yes | yes | yes³ | max 5 | +BOARD_T41BB5X_PRO | yes | yes | yes | yes | max 5 | BOARD_CNC_BOOSTERPACK | yes² | yes | yes | yes | max 3 | -¹ Teensy 4.1 only. +¹ Teensy 4.1 only ² External magjack. ³ EEPROM is optional and must be added to the board. @@ -46,21 +49,23 @@ N_AXIS has a default value of 3, edit grbl\config.h to increase. */ -#define USB_SERIAL_CDC 2 // 1 for Arduino class library and 2 for PJRC C library. Comment out to use UART communication. -//#define USB_SERIAL_WAIT 1 // Wait for USB connection before starting grblHAL. -//#define SPINDLE_HUANYANG 1 // Set to 1 or 2 for Huanyang VFD spindle. Requires spindle plugin. -//#define QEI_ENABLE 1 // Enable quadrature encoder interfaces. Max value is 1. Requires encoder plugin. -//#define ETHERNET_ENABLE 1 // Ethernet streaming. Requires networking plugin. -//#define SDCARD_ENABLE 1 // Run gcode programs from SD card, requires sdcard plugin. -//#define KEYPAD_ENABLE 1 // I2C keypad for jogging etc., requires keypad plugin. -//#define PLASMA_ENABLE 1 // Plasma/THC plugin. To be completed. -//#define PPI_ENABLE 1 // Laser PPI plugin. To be completed. -//#define ODOMETER_ENABLE 1 // Odometer plugin. To be completed. -//#define EEPROM_ENABLE 1 // I2C EEPROM support. Set to 1 for 24LC16(2K), 2 for larger sizes. Requires eeprom plugin. -//#define EEPROM_IS_FRAM 1 // Uncomment when EEPROM is enabled and chip is FRAM, this to remove write delay. - -//#define ESTOP_ENABLE 0 // When enabled only real-time report requests will be executed when the reset pin is asserted. - // Note: if left commented out the default setting is determined from COMPATIBILITY_LEVEL. +#define USB_SERIAL_CDC 2 // 1 for Arduino class library and 2 for PJRC C library. Comment out to use UART communication. +//#define USB_SERIAL_WAIT 1 // Wait for USB connection before starting grblHAL. +//#define SPINDLE_HUANYANG 1 // Set to 1 or 2 for Huanyang VFD spindle. Requires spindle plugin. +//#define QEI_ENABLE 1 // Enable quadrature encoder interfaces. Max value is 1. Requires encoder plugin. +//#define ETHERNET_ENABLE 1 // Ethernet streaming. Requires networking plugin. +//#define SDCARD_ENABLE 2 // Run gcode programs from SD card, requires sdcard plugin. +//#define KEYPAD_ENABLE 1 // I2C keypad for jogging etc., requires keypad plugin. +//#define PLASMA_ENABLE 1 // Plasma/THC plugin. To be completed. +//#define PPI_ENABLE 1 // Laser PPI plugin. To be completed. +//#define ODOMETER_ENABLE 1 // Odometer plugin. +//#define OPENPNP_ENABLE 1 // OpenPNP plugin. To be completed. +//#define EEPROM_ENABLE 1 // I2C EEPROM support. Set to 1 for 24LC16(2K), 2 for larger sizes. Requires eeprom plugin. +//#define EEPROM_IS_FRAM 1 // Uncomment when EEPROM is enabled and chip is FRAM, this to remove write delay. +//#define SPINDLE_SYNC_ENABLE 1 // Enable spindle sync support (G33, G76). !! NOTE: Alpha quality - enable only for test or verification. + // Currently only available for BOARD_T41BB5X_PRO and BOARD_T41U5XBB_SS. +//#define ESTOP_ENABLE 0 // When enabled only real-time report requests will be executed when the reset pin is asserted. + // Note: if left commented out the default setting is determined from COMPATIBILITY_LEVEL. #if ETHERNET_ENABLE > 0 #define TELNET_ENABLE 1 // Telnet daemon - requires Ethernet streaming enabled. diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.c new file mode 100644 index 00000000..ab80cd0f --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.c @@ -0,0 +1,234 @@ +/* + + openpnp.c - plugin for for OpenPNP required M-codes + + Part of grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ + +#include "driver.h" + +#if OPENPNP_ENABLE + +#include + +#include "grbl/hal.h" +#include "grbl/protocol.h" + +static user_mcode_ptrs_t user_mcode; +static on_report_options_ptr on_report_options; +static parameter_words_t m204_words; +static uint8_t tport; + +static user_mcode_t userMCodeCheck (user_mcode_t mcode) +{ + return (uint32_t)mcode == 42 || (uint32_t)mcode == 105 || (uint32_t)mcode == 114 || (uint32_t)mcode == 115 || + (uint32_t)mcode == 204 || mcode == (uint32_t)400 || mcode == (uint32_t)502 + ? mcode + : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Ignore); +} + +static status_code_t userMCodeValidate (parser_block_t *gc_block, parameter_words_t *value_words) +{ + status_code_t state = Status_GcodeValueWordMissing; + + switch((uint32_t)gc_block->user_mcode) { + + case 42: + if((*value_words).p && (*value_words).s) { + + if((*value_words).p && isnan(gc_block->values.p)) + state = Status_BadNumberFormat; + + if((*value_words).s && isnan(gc_block->values.s)) + state = Status_BadNumberFormat; + + if(state != Status_BadNumberFormat) { + if(gc_block->values.p <= 255.0f && (uint8_t)gc_block->values.p < hal.port.num_digital_out) { + (*value_words).p = (*value_words).s = Off; + state = Status_OK; + } else + state = Status_InvalidStatement; + } + } + break; + + case 105: + if((*value_words).t) { + if(gc_block->values.t < hal.port.num_analog_in) { + (*value_words).t = Off; + state = Status_OK; + } else + state = Status_InvalidStatement; + } + break; + + case 114: + (*value_words).d = Off; + // check if d value is 0 or 1? + state = Status_OK; + break; + + case 204: + if((*value_words).p && isnan(gc_block->values.p)) + state = Status_BadNumberFormat; + + if((*value_words).r && isnan(gc_block->values.r)) + state = Status_BadNumberFormat; + + if((*value_words).s && isnan(gc_block->values.s)) + state = Status_BadNumberFormat; + + if(state != Status_BadNumberFormat) { + m204_words = *value_words; + (*value_words).p = (*value_words).r = (*value_words).s = (*value_words).t = Off; + // TODO: add validation + state = Status_OK; + } + break; + + case 115: + case 400: + case 502: + state = Status_OK; + break; + + default: + state = Status_Unhandled; + break; + } + + return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block, value_words) : state; +} + +static void report_position (void) +{ + uint_fast8_t idx; + int32_t current_position[N_AXIS]; + float print_position[N_AXIS]; + char buf[(STRLEN_COORDVALUE + 4) * N_AXIS]; + + memcpy(current_position, sys.position, sizeof(sys.position)); + system_convert_array_steps_to_mpos(print_position, current_position); + + *buf = '\0'; + for (idx = 0; idx < N_AXIS; idx++) { + print_position[idx] -= gc_get_offset(idx); + strcat(buf, axis_letter[idx]); + strcat(buf, ":"); + strcat(buf, ftoa(print_position[idx], N_DECIMAL_COORDVALUE_MM)); // always mm and 4 decimals? + strcat(buf, idx == N_AXIS - 1 ? ASCII_EOL : " "); + } + + hal.stream.write(buf); +} + +static void report_temperature (sys_state_t state) +{ + int32_t v = hal.port.wait_on_input(false, tport, WaitMode_Immediate, 0.0f); + // format output -> T:21.17 /0.0000 B:21.04 /0.0000 @:0 B@:0 +} + +static void userMCodeExecute (uint_fast16_t state, parser_block_t *gc_block) +{ + bool handled = true; + + if (state != STATE_CHECK_MODE) + switch((uint32_t)gc_block->user_mcode) { + + case 42: + hal.port.digital_out(gc_block->values.p, gc_block->values.s != 0.0f); + break; + + + case 105: // Request temperature report + tport = gc_block->values.t; + protocol_enqueue_rt_command(report_temperature); + break; + + case 114: + report_position(); + break; + + case 115: + hal.stream.write("FIRMWARE_NAME:grblHAL "); + hal.stream.write("FIRMWARE_URL:https%3A//github.com/grblHAL "); + hal.stream.write("FIRMWARE_VERSION:" GRBL_VERSION " "); + hal.stream.write("FIRMWARE_BUILD:" GRBL_VERSION_BUILD ASCII_EOL); + break; + + case 204: // Set acceleration + { + uint_fast8_t idx = N_AXIS; + + protocol_buffer_synchronize(); + do { + idx--; + if(m204_words.s || idx == X_AXIS || idx == Y_AXIS) + settings_override_acceleration(idx, m204_words.s ? gc_block->values.s : gc_block->values.t); + else + settings_override_acceleration(idx, gc_block->values.p); + } while(idx); + } + break; + + case 400: // Wait for buffered motions to complete + protocol_buffer_synchronize(); + break; + + case 502: // Restore acceleration to configured values + { + uint_fast8_t idx = N_AXIS; + + protocol_buffer_synchronize(); + do { + settings_override_acceleration(--idx, 0.0f); + } while(idx); + } + break; + + default: + handled = false; + break; + } + + if(!handled && user_mcode.execute) + user_mcode.execute(state, gc_block); +} + +static void onReportOptions (bool newopt) +{ + on_report_options(newopt); + + if(!newopt) + hal.stream.write("[PLUGIN:OpenPNP v0.01]" ASCII_EOL); +} + +void openpnp_init (void) +{ + memcpy(&user_mcode, &hal.user_mcode, sizeof(user_mcode_ptrs_t)); + + hal.user_mcode.check = userMCodeCheck; + hal.user_mcode.validate = userMCodeValidate; + hal.user_mcode.execute = userMCodeExecute; + + on_report_options = grbl.on_report_options; + grbl.on_report_options = onReportOptions; +} + +#endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.h new file mode 100644 index 00000000..72c87bb0 --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/openpnp/openpnp.h @@ -0,0 +1,29 @@ +/* + + openpnp.h - plugin for for OpenPNP required M-codes + + Part of grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . + +*/ + +#ifndef _OPENPNP_H_ +#define _OPENPNP_H_ + +void openpnp_init (void); + +#endif diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/sdcard.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/sdcard.c index 11f757d6..010b2866 100644 --- a/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/sdcard.c +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/sdcard.c @@ -46,6 +46,8 @@ #include "grbl/state_machine.h" #endif +#include "sdcard/ymodem.h" + #ifdef __IMXRT1062__ const char *dev = "1:/"; #elif defined(NEW_FATFS) @@ -124,7 +126,7 @@ static on_report_options_ptr on_report_options; static void sdcard_end_job (void); static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report); static void trap_state_change_request(uint_fast16_t state); -static void sdcard_on_program_completed (program_flow_t program_flow); +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode); //static report_t active_reports; #ifdef __MSP432E401Y__ @@ -379,8 +381,10 @@ static int16_t sdcard_read (void) c = '\n'; } - } else if(state == STATE_IDLE) // TODO: end on ok count match line count? - sdcard_end_job(); + } else if((state == STATE_IDLE || state == STATE_CHECK_MODE) && grbl.on_program_completed == sdcard_on_program_completed) { // TODO: end on ok count match line count? + sdcard_on_program_completed(ProgramFlow_CompletedM30, state == STATE_CHECK_MODE); + grbl.report.feedback_message(Message_ProgramEnd); + } return c; } @@ -429,7 +433,9 @@ static status_code_t trap_status_report (status_code_t status_code) static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report) { - if(hal.stream.read != await_cycle_start) { + if(hal.stream.read == await_cycle_start) + stream_write("|SD:Pending"); + else { char *pct_done = ftoa((float)file.pos / (float)file.size * 100.0f, 1); if(state_get() != STATE_IDLE && !strncmp(pct_done, "100.0", 5)) @@ -450,7 +456,7 @@ static void sdcard_restart_msg (sys_state_t state) report_feedback_message(Message_CycleStartToRerun); } -static void sdcard_on_program_completed (program_flow_t program_flow) +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode) { frewind = frewind || program_flow == ProgramFlow_CompletedM2; // || program_flow == ProgramFlow_CompletedM30; @@ -468,7 +474,7 @@ static void sdcard_on_program_completed (program_flow_t program_flow) sdcard_end_job(); if(on_program_completed) - on_program_completed(program_flow); + on_program_completed(program_flow, check_mode); } #if M6_ENABLE @@ -566,14 +572,29 @@ static status_code_t sd_cmd_to_output (sys_state_t state, char *args) return retval; } +static status_code_t sd_cmd_unlink (sys_state_t state, char *args) +{ + status_code_t retval = Status_Unhandled; + +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + if (!(state == STATE_IDLE || state == STATE_CHECK_MODE)) + retval = Status_SystemGClock; + else if(args) + retval = f_unlink(args) ? Status_OK : Status_SDReadError; +#endif + + return retval; +} + static void sdcard_reset (void) { if(hal.stream.type == StreamType_SDCard) { if(file.line > 0) { char buf[70]; - sprintf(buf, "[MSG:Reset during streaming of SD file at line: " UINT32FMT "]" ASCII_EOL, file.line); - hal.stream.write(buf); - } + sprintf(buf, "Reset during streaming of SD file at line: " UINT32FMT, file.line); + report_message(buf, Message_Plain); + } else if(frewind) + report_feedback_message(Message_None); sdcard_end_job(); } @@ -585,6 +606,9 @@ static void onReportCommandHelp (void) hal.stream.write("$F - list files on SD card" ASCII_EOL); hal.stream.write("$F= - run SD card file" ASCII_EOL); hal.stream.write("$FM - mount SD card" ASCII_EOL); +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + hal.stream.write("$FD= - delete SD card file" ASCII_EOL); +#endif hal.stream.write("$FR - enable rewind mode for next SD card file to run" ASCII_EOL); hal.stream.write("$F<= - dump SD card file to output" ASCII_EOL); @@ -597,15 +621,20 @@ static void onReportOptions (bool newopt) on_report_options(newopt); if(newopt) +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + hal.stream.write(hal.stream.write_char == NULL ? ",SD" : ",SD,YM"); +#else hal.stream.write(",SD"); +#endif else - hal.stream.write("[PLUGIN:SDCARD v1.00]" ASCII_EOL); + hal.stream.write("[PLUGIN:SDCARD v1.02]" ASCII_EOL); } const sys_command_t sdcard_command_list[] = { {"F", false, sd_cmd_file}, {"FM", true, sd_cmd_mount}, {"FR", true, sd_cmd_rewind}, + {"FD", false, sd_cmd_unlink}, {"F<", false, sd_cmd_to_output}, }; @@ -632,6 +661,11 @@ void sdcard_init (void) on_report_options = grbl.on_report_options; grbl.on_report_options = onReportOptions; + +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + if(hal.stream.write_char != NULL) + ymodem_init(); +#endif } FATFS *sdcard_getfs (void) diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.c b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.c new file mode 100644 index 00000000..41cea855 --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.c @@ -0,0 +1,386 @@ +/* + ymodem.c - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Specification: http://wiki.synchro.net/ref:ymodem + + NOTE: Receiver only, does not send initial 'C' to start transfer. + Start transfer by sending SOH or STX. + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + + +#include "sdcard.h" + +#if SDCARD_ENABLE == 2 + +#include +#include + +typedef enum { + YModem_NOOP = 0, + YModem_ACK, + YModem_ACKFile, + YModem_NoFile, + YModem_Purge, + YModem_NAK, + YModem_EOT, + YModem_CAN +} ymodem_status_t; + +typedef ymodem_status_t (*process_data_ptr)(uint8_t c); + +typedef struct { + FATFS *fs; + FIL *handle; + char filename[32]; + uint32_t filelength; + uint32_t received; + uint16_t crc; + uint_fast16_t idx; + uint_fast16_t errors; + uint_fast16_t packet_len; + uint8_t packet_num; + uint32_t next_timeout; + process_data_ptr process; + bool seq_inv; + bool crc_lsb; + bool completed; + bool repeated; + uint8_t payload[1024]; +} ymodem_t; + +static ymodem_t ymodem; + +static driver_reset_ptr driver_reset; +static on_execute_realtime_ptr on_execute_realtime; +static on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; +static io_stream_t active_stream; + +static ymodem_status_t await_soh (uint8_t c); +static ymodem_status_t await_packetnum (uint8_t c); +static ymodem_status_t get_payload (uint8_t c); +static ymodem_status_t await_crc (uint8_t c); +static ymodem_status_t await_eot (uint8_t c); + +// Fast CRC16 implementation +// Original Code: Ashley Roll +// Optimisations: Scott Dattalo +// From http://www.ccsinfo.com/forum/viewtopic.php?t=24977 +static uint16_t crc16 (const uint8_t *buf, uint16_t len) +{ + uint16_t x, crc = 0; + + while(len--) { + x = (crc >> 8) ^ *buf++; + x ^= x >> 4; + crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x; + } + + return crc; +} + +// End transfer handler. +static void end_transfer (bool send_ack) +{ + if(ymodem.handle) { + f_close(ymodem.handle); + ymodem.handle = NULL; + } + + // Restore stream handlers and detach protocol loop from foreground process. + memcpy(&hal.stream, &active_stream, sizeof(io_stream_t)); + grbl.on_execute_realtime = on_execute_realtime; + + if(send_ack) { + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + } +} + +// Cancel handler. Waits for second cancel character. +static ymodem_status_t await_cancel (uint8_t c) +{ + if(c == ASCII_CAN) + end_transfer(false); + else + ymodem.process = await_soh; + + return YModem_NOOP; +} + +// Purge handler. Sinks incoming characters. +static ymodem_status_t purge (uint8_t c) +{ + return YModem_NOOP; +} + +// +// Packet processing +// + +// Start of header handler. +static ymodem_status_t await_soh (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(c == ASCII_SOH || c == ASCII_STX) { + ymodem.idx = ymodem.crc = 0; + ymodem.crc_lsb = ymodem.seq_inv = ymodem.repeated = false; + ymodem.packet_len = c == ASCII_SOH ? 128 : 1024; + ymodem.process = await_packetnum; // Set active handler to wait for packet number. + } else if(c == ASCII_EOT) + end_transfer(true); + else if(c == ASCII_CAN) + ymodem.process = await_cancel; // Set active handler to wait for second CAN character. + else + status = YModem_Purge; + + return status; +} + +// Packet number handler. Reads and validates packet number. +static ymodem_status_t await_packetnum (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(ymodem.seq_inv) { + c ^= 0xFF; + if((ymodem.repeated = ymodem.packet_num == c - 1)) + c++; + if(c == ymodem.packet_num) + ymodem.process = get_payload; // Set active handler to fetch payload + else + status = YModem_Purge; + } else if(!(ymodem.seq_inv = (c == ymodem.packet_num || ymodem.packet_num == c - 1))) + status = YModem_Purge; + + return status; +} + +// Payload handler. Saves incoming characters in payload buffer. +static ymodem_status_t get_payload (uint8_t c) +{ + ymodem.payload[ymodem.idx++] = c; + + if(ymodem.idx == ymodem.packet_len) + ymodem.process = await_crc; + + return YModem_NOOP; +} + +// CRC handler. Reads and validates CRC, open file on packet 0 writes payload to file when valid. +static ymodem_status_t await_crc (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(!ymodem.crc_lsb) { + ymodem.crc_lsb = true; + ymodem.crc = c; + } else { + + ymodem.process = await_soh; // Set active handler to wait for next packet + ymodem.crc = (ymodem.crc << 8) | c; + + if(crc16((const uint8_t *)&ymodem.payload, ymodem.packet_len) != ymodem.crc) // If CRC invalid + return YModem_Purge; // purge input stream and return NAK. + + if(ymodem.packet_num == 0 && *ymodem.filename == 0) { // Open file or end transfer + + const char *data = (const char *)ymodem.payload; + + // If no filename present in payload end transfer by sending ACK else send ACK + C if file could be opened, CAN if not. + if((status = *data == '\0' ? YModem_NoFile : YModem_ACKFile) == YModem_ACKFile) { + + strcpy(ymodem.filename, (const char *)data); // Save filename + + data += strlen(ymodem.filename) + 1; // Save file length if present + if(*data != '\0') + ymodem.filelength = atoi(data); + + static FIL handle; + if(ymodem.fs != NULL && f_open(&handle, ymodem.filename, FA_CREATE_ALWAYS|FA_WRITE) == FR_OK) + ymodem.handle = &handle; + else + status = YModem_CAN; + } + + ymodem.packet_num++; + + } else { // Write payload to file + + UINT wrlen; + + if(!ymodem.repeated) { + ymodem.packet_num++; + ymodem.received += ymodem.packet_len; + + // Trim packet length to match file length if last packet + if((ymodem.completed = ymodem.filelength && ymodem.received > ymodem.filelength)) + ymodem.packet_len -= ymodem.received - ymodem.filelength; + + // Write payload + if(f_write(ymodem.handle, ymodem.payload, ymodem.packet_len, &wrlen) == FR_OK) { + status = YModem_ACK; + if(ymodem.completed) { + ymodem.idx = 0; + ymodem.process = await_eot; // Set active handler to wait for end of transfer + } + } else + status = YModem_CAN; + } else + status = YModem_ACK; + } + } + + return status; +} + +// End of transmission handler. +static ymodem_status_t await_eot (uint8_t c) +{ + if(c == ASCII_EOT) + end_transfer(true); + + return YModem_NOOP; +} + +// Main YModem protocol loop. +// Reads characters off input stream and dispatches them to the appropriate handler. +// Handles timeouts. +static void protocol_loop (sys_state_t state) +{ + int16_t c; + + if(hal.get_elapsed_ticks() >= ymodem.next_timeout) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + ymodem.errors++; + if(ymodem.errors > 10) + end_transfer(false); + else { + ymodem.process = await_soh; + hal.stream.write_char(ASCII_NAK); + } + } + + while((c = active_stream.read()) != SERIAL_NO_DATA) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + switch(ymodem.process(c)) { + + case YModem_ACK: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + break; + + case YModem_ACKFile: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + break; + + case YModem_NoFile: + hal.stream.write_char(ASCII_ACK); + end_transfer(false); + break; + + case YModem_NAK: + ymodem.errors++; + hal.stream.write_char(ASCII_NAK); + break; + + case YModem_CAN: + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + break; + + case YModem_Purge: + ymodem.errors++; + ymodem.process = purge; + hal.stream.cancel_read_buffer(); + break; + + default: + break; + } + + if(grbl.on_execute_realtime != protocol_loop) + break; + } + + on_execute_realtime(state); +} + +// Buffer all received characters. +static bool buffer_all (char c) +{ + return false; +} + +// Terminate any ongoing transfer on a soft reset. +static void on_soft_reset (void) +{ + if(grbl.on_execute_realtime == protocol_loop) { + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + } + + driver_reset(); +} + +// Check input stream for file YModem start of header (soh) characters. +// Redirect input stream and start protocol handler when found. +static bool trap_initial_soh (char c) +{ + if(c == ASCII_SOH || c == ASCII_STX) { + + memcpy(&active_stream, &hal.stream, sizeof(io_stream_t)); // Save current stream pointers, + hal.stream.enqueue_realtime_command = buffer_all; // stop core real-time command handling and + hal.stream.read = stream_get_null; // block core protocol loop from reading from input + + on_execute_realtime = grbl.on_execute_realtime; // Add YModem protocol loop + grbl.on_execute_realtime = protocol_loop; // to grblHAL foreground process + + memset(&ymodem, 0, sizeof(ymodem_t)); // Init YModem variables + ymodem.process = await_soh; + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + ymodem.fs = sdcard_getfs(); + + return false; // Return false to add character to input buffer + } + + return on_unknown_realtime_cmd == NULL || on_unknown_realtime_cmd(c); +} + +// Add YModem protocol to chain of unknown real-time command handlers +void ymodem_init (void) +{ + driver_reset = hal.driver_reset; + hal.driver_reset = on_soft_reset; + + on_unknown_realtime_cmd = grbl.on_unknown_realtime_cmd; + grbl.on_unknown_realtime_cmd = trap_initial_soh; +} + +#endif + diff --git a/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.h b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.h new file mode 100644 index 00000000..de91553a --- /dev/null +++ b/drivers/IMXRT1062/grblHAL_Teensy4/src/sdcard/ymodem.h @@ -0,0 +1,22 @@ +/* + ymodem.h - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +void ymodem_init (void); diff --git a/drivers/LPC1769/src/btt_skr_1.3_map.h b/drivers/LPC1769/src/btt_skr_1.3_map.h index d86f4e4a..3fdc4898 100644 --- a/drivers/LPC1769/src/btt_skr_1.3_map.h +++ b/drivers/LPC1769/src/btt_skr_1.3_map.h @@ -207,7 +207,7 @@ void board_init (void); #define SPINDLE_PWM_USE_PRIMARY_PIN false #define SPINDLE_PWM_USE_SECONDARY_PIN true -#ifdef SDCARD_ENABLE +#if SDCARD_ENABLE #define SD_SPI_PORT 1 #define SD_CS_PN 0 #define SD_CS_PORT port(SD_CS_PN) diff --git a/drivers/LPC1769/src/btt_skr_1.4_turbo_map.h b/drivers/LPC1769/src/btt_skr_1.4_turbo_map.h index 82031396..c3562478 100644 --- a/drivers/LPC1769/src/btt_skr_1.4_turbo_map.h +++ b/drivers/LPC1769/src/btt_skr_1.4_turbo_map.h @@ -200,7 +200,7 @@ void board_init (void); #define SPINDLE_PWM_USE_PRIMARY_PIN false #define SPINDLE_PWM_USE_SECONDARY_PIN true -#ifdef SDCARD_ENABLE +#if SDCARD_ENABLE #define SD_SPI_PORT 1 #define SD_CS_PN 0 #define SD_CS_PORT port(SD_CS_PN) diff --git a/drivers/LPC1769/src/diskio.c b/drivers/LPC1769/src/diskio.c index c34ca336..17150ffa 100644 --- a/drivers/LPC1769/src/diskio.c +++ b/drivers/LPC1769/src/diskio.c @@ -64,14 +64,14 @@ static inline void SELECT (void) { - BITBAND_GPIO(SD_CS_PORT->PIN, SD_CS_PIN) = 0; + DIGITAL_OUT(SD_CS_PORT, SD_CS_BIT, 0); } /* de-asserts the CS pin to the card */ static inline void DESELECT (void) { - BITBAND_GPIO(SD_CS_PORT->PIN, SD_CS_PIN) = 1; + DIGITAL_OUT(SD_CS_PORT, SD_CS_BIT, 1); } /*-------------------------------------------------------------------------- diff --git a/drivers/LPC1769/src/driver.c b/drivers/LPC1769/src/driver.c index 979c45a9..93f41e15 100644 --- a/drivers/LPC1769/src/driver.c +++ b/drivers/LPC1769/src/driver.c @@ -29,6 +29,7 @@ #include "grbl-lpc/pwm_driver.h" #include "grbl/limits.h" +#include "grbl/crossbar.h" #if SDCARD_ENABLE #include "sdcard/sdcard.h" @@ -89,33 +90,6 @@ static delay_t delay = { .ms = 1, .callback = NULL }; // NOTE: initial ms set to #define DEBOUNCE_QUEUE 8 // Must be a power of 2 -#define INPUT_GROUP_CONTROL 1 -#define INPUT_GROUP_PROBE 2 -#define INPUT_GROUP_LIMIT 4 -#define INPUT_GROUP_KEYPAD 8 - -typedef enum { - Input_Unassigned = 0, - Input_Probe, - Input_Reset, - Input_FeedHold, - Input_CycleStart, - Input_SafetyDoor, - Input_LimitX, - Input_LimitX_Max, - Input_LimitY, - Input_LimitY_Max, - Input_LimitZ, - Input_LimitZ_Max, - Input_LimitA, - Input_LimitA_Max, - Input_LimitB, - Input_LimitB_Max, - Input_LimitC, - Input_LimitC_Max, - Input_KeypadStrobe -} input_t; - typedef enum { GPIO_Intr_None = 0, GPIO_Intr_Falling, @@ -127,8 +101,8 @@ typedef struct { LPC_GPIO_T *port; uint32_t pin; uint32_t bit; - input_t id; - uint8_t group; + pin_function_t id; + pin_group_t group; bool debounce; gpio_intr_t intr_type; } input_signal_t; @@ -147,52 +121,52 @@ static input_signal_t gpio0_signals[10] = {0}, gpio1_signals[10] = {0}, gpio2_si static input_signal_t inputpin[] = { #ifdef PROBE_PIN - { .id = Input_Probe, .port = PROBE_PORT, .pin = PROBE_PIN, .group = INPUT_GROUP_PROBE }, + { .id = Input_Probe, .port = PROBE_PORT, .pin = PROBE_PIN, .group = PinGroup_Probe }, #endif #ifdef RESET_PIN - { .id = Input_Reset, .port = RESET_PORT, .pin = RESET_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_Reset, .port = RESET_PORT, .pin = RESET_PIN, .group = PinGroup_Control }, #endif #ifdef FEED_HOLD_PIN - { .id = Input_FeedHold, .port = FEED_HOLD_PORT, .pin = FEED_HOLD_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_FeedHold, .port = FEED_HOLD_PORT, .pin = FEED_HOLD_PIN, .group = PinGroup_Control }, #endif #ifdef CYCLE_START_PIN - { .id = Input_CycleStart, .port = CYCLE_START_PORT, .pin = CYCLE_START_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_CycleStart, .port = CYCLE_START_PORT, .pin = CYCLE_START_PIN, .group = PinGroup_Control }, #endif #ifdef SAFETY_DOOR_PIN - { .id = Input_SafetyDoor, .port = SAFETY_DOOR_PORT, .pin = SAFETY_DOOR_PIN, .group = INPUT_GROUP_CONTROL }, + { .id = Input_SafetyDoor, .port = SAFETY_DOOR_PORT, .pin = SAFETY_DOOR_PIN, .group = PinGroup_Control }, #endif - { .id = Input_LimitX, .port = X_LIMIT_PORT, .pin = X_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitX, .port = X_LIMIT_PORT, .pin = X_LIMIT_PIN, .group = PinGroup_Limit }, #ifdef X_LIMIT_PIN_MAX - { .id = Input_LimitX_Max, .port = X_LIMIT_PORT_MAX, .pin = X_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitX_Max, .port = X_LIMIT_PORT_MAX, .pin = X_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif - { .id = Input_LimitY, .port = Y_LIMIT_PORT, .pin = Y_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitY, .port = Y_LIMIT_PORT, .pin = Y_LIMIT_PIN, .group = PinGroup_Limit }, #ifdef Y_LIMIT_PIN_MAX - { .id = Input_LimitY_Max, .port = Y_LIMIT_PORT_MAX, .pin = Y_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitY_Max, .port = Y_LIMIT_PORT_MAX, .pin = Y_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif - { .id = Input_LimitZ, .port = Z_LIMIT_PORT, .pin = Z_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitZ, .port = Z_LIMIT_PORT, .pin = Z_LIMIT_PIN, .group = PinGroup_Limit }, #ifdef Z_LIMIT_PIN_MAX - { .id = Input_LimitZ_Max, .port = Z_LIMIT_PORT_MAX, .pin = Z_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitZ_Max, .port = Z_LIMIT_PORT_MAX, .pin = Z_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif #ifdef A_LIMIT_PIN - { .id = Input_LimitA, .port = A_LIMIT_PORT, .pin = A_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitA, .port = A_LIMIT_PORT, .pin = A_LIMIT_PIN, .group = PinGroup_Limit }, #endif #ifdef A_LIMIT_PIN_MAX - { .id = Input_LimitA_Max, .port = A_LIMIT_PORT_MAX, .pin = A_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitA_Max, .port = A_LIMIT_PORT_MAX, .pin = A_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif #ifdef B_LIMIT_PIN - { .id = Input_LimitB, .port = B_LIMIT_PORT, .pin = B_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitB, .port = B_LIMIT_PORT, .pin = B_LIMIT_PIN, .group = PinGroup_Limit }, #endif #ifdef B_LIMIT_PIN_MAX - { .id = Input_LimitB_Max, .port = B_LIMIT_PORT_MAX, .pin = B_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitB_Max, .port = B_LIMIT_PORT_MAX, .pin = B_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif #ifdef C_LIMIT_PIN - { .id = Input_LimitC, .port = C_LIMIT_PORT, .pin = C_LIMIT_PIN, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitC, .port = C_LIMIT_PORT, .pin = C_LIMIT_PIN, .group = PinGroup_Limit }, #endif #ifdef C_LIMIT_PIN_MAX - { .id = Input_LimitC_Max, .port = C_LIMIT_PORT_MAX, .pin = C_LIMIT_PIN_MAX, .group = INPUT_GROUP_LIMIT }, + { .id = Input_LimitC_Max, .port = C_LIMIT_PORT_MAX, .pin = C_LIMIT_PIN_MAX, .group = PinGroup_Limit }, #endif #if KEYPAD_ENABLE - , { .id = Input_KeypadStrobe, .port = KEYPAD_STROBE_PORT .pin = KEYPAD_STROBE_PIN, .group = INPUT_GROUP_KEYPAD } + , { .id = Input_KeypadStrobe, .port = KEYPAD_STROBE_PORT .pin = KEYPAD_STROBE_PIN, .group = PinGroup_Keypad } #endif }; @@ -991,13 +965,13 @@ void settings_changed (settings_t *settings) while(true); } - if(pin->group == INPUT_GROUP_LIMIT) + if(pin->group == PinGroup_Limit) pin->intr_type = GPIO_Intr_None; } if(pin->intr_type != GPIO_Intr_None) { - pin->debounce = hal.driver_cap.software_debounce && !(pin->group == INPUT_GROUP_PROBE || pin->group == INPUT_GROUP_KEYPAD); + pin->debounce = hal.driver_cap.software_debounce && !(pin->group == PinGroup_Probe || pin->group == PinGroup_Keypad); if(pin->port == LPC_GPIO0) { gpio0_int_enable(pin->bit, pin->intr_type); @@ -1149,8 +1123,8 @@ static bool driver_setup (settings_t *settings) #endif #if SDCARD_ENABLE - BITBAND_GPIO(SD_CS_PORT->DIR, SD_CS_PIN) = 1; - BITBAND_GPIO(SD_CS_PORT->PIN, SD_CS_PIN) = 1; + SD_CS_PORT->DIR |= SD_CS_BIT; + DIGITAL_OUT(SD_CS_PORT, SD_CS_BIT, 1); sdcard_init(); #endif @@ -1194,7 +1168,7 @@ bool driver_init (void) { #endif hal.info = "LCP1769"; - hal.driver_version = "210219"; + hal.driver_version = "210504"; hal.driver_setup = driver_setup; #ifdef BOARD_NAME hal.board = BOARD_NAME; @@ -1235,6 +1209,7 @@ bool driver_init (void) { hal.stream.read = usbGetC; hal.stream.write = usbWriteS; hal.stream.write_all = usbWriteS; + hal.stream.write_char = usbPutC; hal.stream.get_rx_buffer_available = usbRxFree; hal.stream.reset_read_buffer = usbRxFlush; hal.stream.cancel_read_buffer = usbRxCancel; @@ -1244,6 +1219,7 @@ bool driver_init (void) { hal.stream.read = serialGetC; hal.stream.write = serialWriteS; hal.stream.write_all = serialWriteS; + hal.stream.write_char = serialPutC; hal.stream.get_rx_buffer_available = serialRxFree; hal.stream.reset_read_buffer = serialRxFlush; hal.stream.cancel_read_buffer = serialRxCancel; @@ -1391,7 +1367,7 @@ void DEBOUNCE_IRQHandler (void) if(DIGITAL_IN(signal->port, signal->bit) == (signal->intr_type == GPIO_Intr_Falling ? 0 : 1)) switch(signal->group) { - case INPUT_GROUP_LIMIT: + case PinGroup_Limit: { limit_signals_t state = limitsGetState(); if(limit_signals_merge(state).value) //TODO: add check for limit switches having same state as when limit_isr were invoked? @@ -1399,9 +1375,12 @@ void DEBOUNCE_IRQHandler (void) } break; - case INPUT_GROUP_CONTROL: + case PinGroup_Control: hal.control.interrupt_callback(systemGetState()); break; + + default: + break; } } } @@ -1453,14 +1432,14 @@ void GPIO_IRQHandler (void) DEBOUNCE_TIMER->TCR = 0b01; } - if(grp & INPUT_GROUP_LIMIT) + if(grp & PinGroup_Limit) hal.limits.interrupt_callback(limitsGetState()); - if(grp & INPUT_GROUP_CONTROL) + if(grp & PinGroup_Control) hal.control.interrupt_callback(systemGetState()); #if KEYPAD_ENABLE - if(grp & INPUT_GROUP_KEYPAD) + if(grp & PinGroup_Keypad) keypad_keyclick_handler(BITBAND_PERI(KEYPAD_PORT->PIO_PDSR, KEYPAD_PIN)); #endif } diff --git a/drivers/LPC1769/src/driver.h b/drivers/LPC1769/src/driver.h index 0d2abc7b..1fa76e66 100644 --- a/drivers/LPC1769/src/driver.h +++ b/drivers/LPC1769/src/driver.h @@ -111,6 +111,10 @@ #define I2C_ENABLE 0 #endif +#ifndef SDCARD_ENABLE +#define SDCARD_ENABLE 0 +#endif + #if TRINAMIC_ENABLE #ifndef TRINAMIC_MIXED_DRIVERS #define TRINAMIC_MIXED_DRIVERS 1 @@ -208,6 +212,10 @@ #define CYCLE_START_INTCLR CONTROL_INTCLR #endif +#ifdef SD_CS_PIN +#define SD_CS_BIT (1<next; diff --git a/drivers/LPC1769/src/grbl/grbl.h b/drivers/LPC1769/src/grbl/grbl.h index 46556f33..b8522819 100644 --- a/drivers/LPC1769/src/grbl/grbl.h +++ b/drivers/LPC1769/src/grbl/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_VERSION_BUILD "20210313" +#define GRBL_VERSION_BUILD "20210513" // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/drivers/LPC1769/src/grbl/hal.h b/drivers/LPC1769/src/grbl/hal.h index 1b6de673..baa34852 100644 --- a/drivers/LPC1769/src/grbl/hal.h +++ b/drivers/LPC1769/src/grbl/hal.h @@ -73,16 +73,18 @@ typedef void (*driver_reset_ptr)(void); // I/O stream +typedef int16_t (*stream_read_ptr)(void); typedef void (*stream_write_ptr)(const char *s); +typedef bool (*stream_write_char_ptr)(const char c); typedef bool (*enqueue_realtime_command_ptr)(char data); typedef struct { stream_type_t type; uint16_t (*get_rx_buffer_available)(void); -// bool (*stream_write)(char c); - stream_write_ptr write; // write string to current I/O stream only. - stream_write_ptr write_all; // write string to all active output streams. - int16_t (*read)(void); + stream_write_ptr write; // write string to current I/O stream only. + stream_write_ptr write_all; // write string to all active output streams. + stream_write_char_ptr write_char; // write single character to current I/O stream only. + stream_read_ptr read; void (*reset_read_buffer)(void); void (*cancel_read_buffer)(void); bool (*suspend_read)(bool await); @@ -340,14 +342,15 @@ typedef struct { typedef void (*on_state_change_ptr)(sys_state_t state); typedef void (*on_probe_completed_ptr)(void); -typedef void (*on_program_completed_ptr)(program_flow_t program_flow); +typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode); typedef void (*on_execute_realtime_ptr)(sys_state_t state); typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd); +typedef bool (*on_unknown_realtime_cmd_ptr)(char c); typedef void (*on_report_options_ptr)(bool newopt); typedef void (*on_report_command_help_ptr)(void); typedef void (*on_global_settings_restore_ptr)(void); typedef setting_details_t *(*on_get_settings_ptr)(void); // NOTE: this must match the signature of the same definition in - // the setting_details_t structure in settings.h! + // the setting_details_t structure in settings.h! typedef void (*on_realtime_report_ptr)(stream_write_ptr stream_write, report_tracking_flags_t report); typedef void (*on_unknown_feedback_message_ptr)(stream_write_ptr stream_write); typedef bool (*on_laser_ppi_enable_ptr)(uint_fast16_t ppi, uint_fast16_t pulse_length); @@ -370,6 +373,7 @@ typedef struct { on_get_settings_ptr on_get_settings; on_realtime_report_ptr on_realtime_report; on_unknown_feedback_message_ptr on_unknown_feedback_message; + on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; on_unknown_sys_command_ptr on_unknown_sys_command; // return Status_Unhandled if not handled. on_get_commands_ptr on_get_commands; on_user_command_ptr on_user_command; diff --git a/drivers/LPC1769/src/grbl/limits.c b/drivers/LPC1769/src/grbl/limits.c index 340be0f3..7289e710 100644 --- a/drivers/LPC1769/src/grbl/limits.c +++ b/drivers/LPC1769/src/grbl/limits.c @@ -277,8 +277,8 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar if(auto_square.mask) { float fail_distance = (-settings.homing.dual_axis.fail_length_percent / 100.0f) * settings.axis[dual_motor_axis].max_travel; - fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_min); - fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_min); autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm); } diff --git a/drivers/LPC1769/src/grbl/motion_control.c b/drivers/LPC1769/src/grbl/motion_control.c index ef4b8414..fc2ce283 100644 --- a/drivers/LPC1769/src/grbl/motion_control.c +++ b/drivers/LPC1769/src/grbl/motion_control.c @@ -562,7 +562,6 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ if(repeats && gc_state.modal.distance_incremental) { position[plane.axis_0] += canned->xyz[plane.axis_0]; position[plane.axis_1] += canned->xyz[plane.axis_1]; - position[plane.axis_linear] = canned->prev_position; if(!mc_line(position, pl_data)) return; } diff --git a/drivers/LPC1769/src/grbl/protocol.c b/drivers/LPC1769/src/grbl/protocol.c index a15f70e9..030b947c 100644 --- a/drivers/LPC1769/src/grbl/protocol.c +++ b/drivers/LPC1769/src/grbl/protocol.c @@ -45,9 +45,8 @@ typedef union { uint8_t overflow :1, comment_parentheses :1, comment_semicolon :1, - line_is_comment :1, block_delete :1, - unassigned :3; + unassigned :4; }; } line_flags_t; @@ -164,7 +163,7 @@ bool protocol_main_loop (void) int16_t c; char eol = '\0'; line_flags_t line_flags = {0}; - bool nocaps = false; + bool nocaps = false, line_is_comment = false; xcommand[0] = '\0'; user_message.show = keep_rt_commands = false; @@ -178,7 +177,7 @@ bool protocol_main_loop (void) if(c == ASCII_CAN) { eol = xcommand[0] = '\0'; - keep_rt_commands = nocaps = user_message.show = false; + keep_rt_commands = nocaps = line_is_comment = user_message.show = false; char_counter = line_flags.value = 0; gc_state.last_error = Status_OK; @@ -207,7 +206,7 @@ bool protocol_main_loop (void) // Direct and execute one line of formatted input, and report status of execution. if (line_flags.overflow) // Report line overflow error. gc_state.last_error = Status_Overflow; - else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_flags.line_is_comment) // Empty or comment line. For syncing purposes. + else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_is_comment) // Empty or comment line. For syncing purposes. gc_state.last_error = Status_OK; else if (line[0] == '$') {// Grbl '$' system command if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) { @@ -275,7 +274,7 @@ bool protocol_main_loop (void) case '(': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; if(!keep_rt_commands) { // Enable comments flag and ignore all characters until ')' or EOL unless it is a message. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. @@ -297,7 +296,7 @@ bool protocol_main_loop (void) case ';': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. if(!keep_rt_commands) { if((line_flags.comment_semicolon = !line_flags.comment_parentheses)) @@ -855,13 +854,13 @@ ISR_CODE bool protocol_enqueue_realtime_command (char c) default: if(c < ' ' || (c >= 0x7F && c <= 0xBF)) - drop = true; + drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c); break; } // 2. Process printable ASCII characters and top-bit set characters // If legacy realtime commands are disabled they are returned to the input stream - // when appering in settings ($ commands) or comments + // when appearing in settings ($ commands) or comments if(!drop) switch ((unsigned char)c) { diff --git a/drivers/LPC1769/src/grbl/settings.c b/drivers/LPC1769/src/grbl/settings.c index 395d44fc..e0144ae8 100644 --- a/drivers/LPC1769/src/grbl/settings.c +++ b/drivers/LPC1769/src/grbl/settings.c @@ -32,6 +32,7 @@ #include "limits.h" #include "nvs_buffer.h" #include "tool_change.h" +#include "state_machine.h" #ifdef ENABLE_BACKLASH_COMPENSATION #include "motion_control.h" #endif @@ -99,6 +100,7 @@ PROGMEM const settings_t defaults = { .steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK, .steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK, .steppers.deenergize.mask = ST_DEENERGIZE_MASK, +// .steppers.is_rotational.mask = 0, #if DEFAULT_HOMING_ENABLE .homing.flags.enabled = DEFAULT_HOMING_ENABLE, .homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK, @@ -446,6 +448,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL }, { Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL }, { Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL } +// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL } }; static setting_details_t details = { @@ -456,6 +459,56 @@ static setting_details_t details = { .save = settings_write_global }; +// Acceleration override + +static struct { + bool valid; + float acceleration[N_AXIS]; +} override_backup = { .valid = false }; + +static void save_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + do { + idx--; + override_backup.acceleration[idx] = settings.axis[idx].acceleration; + } while(idx); + + override_backup.valid = true; +} + +static void restore_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + if(override_backup.valid) do { + idx--; + settings.axis[idx].acceleration = override_backup.acceleration[idx]; + } while(idx); +} + +// Temporarily override acceleration, if 0 restore to setting value. +// Note: only allowed when current state is idle. +bool settings_override_acceleration (uint8_t axis, float acceleration) +{ + if(state_get() != STATE_IDLE) + return false; + + if(acceleration <= 0.0f) { + if(override_backup.valid) + settings.axis[axis].acceleration = override_backup.acceleration[axis]; + } else { + if(!override_backup.valid) + save_override_backup(); + settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value? + } + + return true; +} + +// --- + setting_details_t *settings_get_details (void) { details.on_get_settings = grbl.on_get_settings; @@ -796,7 +849,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisAcceleration: - settings.axis[idx].acceleration = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. + settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. break; case Setting_AxisMaxTravel: @@ -1232,6 +1285,9 @@ bool read_global_settings () // Write Grbl global settings and version number to persistent storage void settings_write_global (void) { + if(override_backup.valid) + restore_override_backup(); + if(hal.nvs.type != NVS_None) { hal.nvs.put_byte(0, SETTINGS_VERSION); hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true); diff --git a/drivers/LPC1769/src/grbl/settings.h b/drivers/LPC1769/src/grbl/settings.h index f62a6015..dfadc1ea 100644 --- a/drivers/LPC1769/src/grbl/settings.h +++ b/drivers/LPC1769/src/grbl/settings.h @@ -226,6 +226,7 @@ typedef enum { Settings_IoPort_OD_Enable = 373, Settings_ModBus_BaudRate = 374, Settings_ModBus_RXTimeout = 375, + Settings_Axis_Rotational = 376, Setting_EncoderSettingsBase = 400, // NOTE: Reserving settings values >= 400 for encoder settings. Up to 449. Setting_EncoderSettingsMax = 449, @@ -441,6 +442,7 @@ typedef struct { axes_signals_t dir_invert; axes_signals_t enable_invert; axes_signals_t deenergize; +// axes_signals_t is_rotational; or add to axis_settings_t below as bitmap union? rotational axes are not scaled in imperial mode float pulse_microseconds; float pulse_delay_microseconds; uint16_t idle_lock_time; // If value = 255, steppers do not disable. @@ -708,6 +710,9 @@ bool settings_write_tool_data (tool_data_t *tool_data); // Read selected tool data from persistent storage bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data); +// Temporarily override acceleration, if 0 restore to configured setting value +bool settings_override_acceleration (uint8_t axis, float acceleration); + setting_details_t *settings_get_details (void); bool settings_is_group_available (setting_group_t group); bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data); diff --git a/drivers/LPC1769/src/grbl/stream.c b/drivers/LPC1769/src/grbl/stream.c index ba9b6046..da86fe65 100644 --- a/drivers/LPC1769/src/grbl/stream.c +++ b/drivers/LPC1769/src/grbl/stream.c @@ -26,7 +26,7 @@ static stream_rx_buffer_t rxbackup; // "dummy" version of serialGetC -static int16_t stream_get_null (void) +int16_t stream_get_null (void) { return SERIAL_NO_DATA; } diff --git a/drivers/LPC1769/src/grbl/stream.h b/drivers/LPC1769/src/grbl/stream.h index 07e62c08..f89270c2 100644 --- a/drivers/LPC1769/src/grbl/stream.h +++ b/drivers/LPC1769/src/grbl/stream.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ #ifndef _STREAM_H_ #define _STREAM_H_ +#define ASCII_SOH 0x01 +#define ASCII_STX 0x02 #define ASCII_ETX 0x03 +#define ASCII_EOT 0x04 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 @@ -104,7 +107,16 @@ typedef struct { char data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; +#ifdef __cplusplus +extern "C" { +#endif + +int16_t stream_get_null (void); bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); void stream_rx_backup (stream_rx_buffer_t *rxbuffer); +#ifdef __cplusplus +} +#endif + #endif diff --git a/drivers/LPC1769/src/mks_sbase.c b/drivers/LPC1769/src/mks_sbase.c index 8cd927ce..8623fb95 100644 --- a/drivers/LPC1769/src/mks_sbase.c +++ b/drivers/LPC1769/src/mks_sbase.c @@ -40,66 +40,90 @@ typedef struct { } mks_settings_t; static mks_settings_t mks; -static driver_setting_ptrs_t driver_settings; +static nvs_address_t nvs_address; + +static void mks_settings_restore (void); +static void mks_settings_load (void); +static status_code_t set_axis_setting (setting_id_t setting, float value); +static float get_axis_setting (setting_id_t setting); static const setting_detail_t mks_settings[] = { - { Setting_AxisStepperCurrentBase, Group_Axis0, "?-axis motor current", "A", Format_Decimal, "0.0#", "0", "2.2" } + { Setting_AxisStepperCurrent, Group_Axis0, "?-axis motor current", "A", Format_Decimal, "0.0#", "0", "2.2", Setting_NonCoreFn, set_axis_setting, get_axis_setting } }; +static void mks_settings_save (void) +{ + hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&mks, sizeof(mks_settings_t), true); +} + static setting_details_t details = { .settings = mks_settings, - .n_settings = sizeof(mks_settings) / sizeof(setting_detail_t) + .n_settings = sizeof(mks_settings) / sizeof(setting_detail_t), + .load = mks_settings_load, + .save = mks_settings_save, + .restore = mks_settings_restore }; -static setting_details_t *on_report_settings (void) +static setting_details_t *on_get_settings (void) { return &details; } static void mks_set_current (uint_fast8_t axis, float current) { - static const uint8_t wiper_registers[] = {0x09, 0x01, 0x06, 0x07}; + static const uint8_t wiper_registers[] = {0x00, 0x01, 0x06, 0x07}; //TODO: add support for B axis? uint8_t cmd[2]; cmd[0] = wiper_registers[axis] << 4; - cmd[1] = (uint8_t)min(255.0f, roundf(current * 113.33f)); + cmd[1] = (uint8_t)min(255.0f, roundf(current * 100.85f)); i2c_write(MCP44XX_I2C_ADDR, cmd, sizeof(cmd)); } // Parse and set driver specific parameters -static status_code_t mks_setting (setting_id_t setting, float value, char *svalue) +static status_code_t set_axis_setting (setting_id_t setting, float value) { + uint_fast8_t idx; status_code_t status = Status_OK; - if((setting_id_t)setting >= Setting_AxisSettingsBase && (setting_id_t)setting <= Setting_AxisSettingsMax) { + switch(settings_get_axis_base(setting, &idx)) { - uint_fast16_t base_idx = (uint_fast16_t)setting - (uint_fast16_t)Setting_AxisSettingsBase; - uint_fast8_t idx = base_idx % AXIS_SETTINGS_INCREMENT; + case Setting_AxisStepperCurrent: + mks.driver[idx].current = value; + mks_set_current(idx, value); + break; - if(idx < N_AXIS) switch((base_idx - idx) / AXIS_SETTINGS_INCREMENT) { + default: + status = Status_Unhandled; + break; + } - case AxisSetting_StepperCurrent: - mks.driver[idx].current = value; - mks_set_current(idx, value); - break; - default: - status = Status_Unhandled; - break; - } - } else - status = Status_Unhandled; + return status; +} - if(status == Status_OK) - hal.nvs.memcpy_to_nvs(driver_settings.nvs_address, (uint8_t *)&mks, sizeof(mks_settings_t), true); +static float get_axis_setting (setting_id_t setting) +{ + float value = 0.0f; + uint_fast8_t idx; + + switch(settings_get_axis_base(setting, &idx)) { + + case Setting_AxisStepperCurrent: + value = mks.driver[idx].current; + break; + + default: + break; + } - return status == Status_Unhandled && driver_settings.set ? driver_settings.set(setting, value, svalue) : status; + return value; } + // Initialize default EEPROM settings static void mks_settings_restore (void) { @@ -109,15 +133,12 @@ static void mks_settings_restore (void) mks.driver[--idx].current = 0.6f; } while(idx); - hal.nvs.memcpy_to_nvs(driver_settings.nvs_address, (uint8_t *)&mks, sizeof(mks_settings_t), true); - - if(driver_settings.restore) - driver_settings.restore(); + hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&mks, sizeof(mks_settings_t), true); } static void mks_settings_load (void) { - if(hal.nvs.memcpy_from_nvs((uint8_t *)&mks, driver_settings.nvs_address, sizeof(mks_settings_t), true) != NVS_TransferResult_OK) + if(hal.nvs.memcpy_from_nvs((uint8_t *)&mks, nvs_address, sizeof(mks_settings_t), true) != NVS_TransferResult_OK) mks_settings_restore(); uint_fast8_t idx = N_AXIS; @@ -126,44 +147,14 @@ static void mks_settings_load (void) idx--; mks_set_current(idx, mks.driver[idx].current); } while(idx); - - if(driver_settings.load) - driver_settings.load(); -} - -static void mks_axis_settings_report (axis_setting_id_t setting, uint8_t axis_idx) -{ - bool reported = true; - setting_id_t basetype = (setting_id_t)(Setting_AxisSettingsBase + setting * AXIS_SETTINGS_INCREMENT); - - switch(setting) { - - case AxisSetting_StepperCurrent: - report_float_setting((setting_id_t)(basetype + axis_idx), mks.driver[axis_idx].current, 1); - break; - - default: - reported = false; - break; - } - - if(!reported && driver_settings.axis_report) - driver_settings.axis_report(setting, axis_idx); } void board_init (void) { - if((hal.driver_settings.nvs_address = nvs_alloc(sizeof(mks_settings_t)))) { - - memcpy(&driver_settings, &hal.driver_settings, sizeof(driver_setting_ptrs_t)); - - hal.driver_settings.set = mks_setting; - hal.driver_settings.axis_report = mks_axis_settings_report; - hal.driver_settings.load = mks_settings_load; - hal.driver_settings.restore = mks_settings_restore; + if((nvs_address = nvs_alloc(sizeof(mks_settings_t)))) { - details.on_report_settings = grbl.on_report_settings; - grbl.on_report_settings = on_report_settings; + details.on_get_settings = grbl.on_get_settings; + grbl.on_get_settings = on_get_settings; } } diff --git a/drivers/LPC1769/src/sdcard/sdcard.c b/drivers/LPC1769/src/sdcard/sdcard.c index 11f757d6..010b2866 100644 --- a/drivers/LPC1769/src/sdcard/sdcard.c +++ b/drivers/LPC1769/src/sdcard/sdcard.c @@ -46,6 +46,8 @@ #include "grbl/state_machine.h" #endif +#include "sdcard/ymodem.h" + #ifdef __IMXRT1062__ const char *dev = "1:/"; #elif defined(NEW_FATFS) @@ -124,7 +126,7 @@ static on_report_options_ptr on_report_options; static void sdcard_end_job (void); static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report); static void trap_state_change_request(uint_fast16_t state); -static void sdcard_on_program_completed (program_flow_t program_flow); +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode); //static report_t active_reports; #ifdef __MSP432E401Y__ @@ -379,8 +381,10 @@ static int16_t sdcard_read (void) c = '\n'; } - } else if(state == STATE_IDLE) // TODO: end on ok count match line count? - sdcard_end_job(); + } else if((state == STATE_IDLE || state == STATE_CHECK_MODE) && grbl.on_program_completed == sdcard_on_program_completed) { // TODO: end on ok count match line count? + sdcard_on_program_completed(ProgramFlow_CompletedM30, state == STATE_CHECK_MODE); + grbl.report.feedback_message(Message_ProgramEnd); + } return c; } @@ -429,7 +433,9 @@ static status_code_t trap_status_report (status_code_t status_code) static void sdcard_report (stream_write_ptr stream_write, report_tracking_flags_t report) { - if(hal.stream.read != await_cycle_start) { + if(hal.stream.read == await_cycle_start) + stream_write("|SD:Pending"); + else { char *pct_done = ftoa((float)file.pos / (float)file.size * 100.0f, 1); if(state_get() != STATE_IDLE && !strncmp(pct_done, "100.0", 5)) @@ -450,7 +456,7 @@ static void sdcard_restart_msg (sys_state_t state) report_feedback_message(Message_CycleStartToRerun); } -static void sdcard_on_program_completed (program_flow_t program_flow) +static void sdcard_on_program_completed (program_flow_t program_flow, bool check_mode) { frewind = frewind || program_flow == ProgramFlow_CompletedM2; // || program_flow == ProgramFlow_CompletedM30; @@ -468,7 +474,7 @@ static void sdcard_on_program_completed (program_flow_t program_flow) sdcard_end_job(); if(on_program_completed) - on_program_completed(program_flow); + on_program_completed(program_flow, check_mode); } #if M6_ENABLE @@ -566,14 +572,29 @@ static status_code_t sd_cmd_to_output (sys_state_t state, char *args) return retval; } +static status_code_t sd_cmd_unlink (sys_state_t state, char *args) +{ + status_code_t retval = Status_Unhandled; + +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + if (!(state == STATE_IDLE || state == STATE_CHECK_MODE)) + retval = Status_SystemGClock; + else if(args) + retval = f_unlink(args) ? Status_OK : Status_SDReadError; +#endif + + return retval; +} + static void sdcard_reset (void) { if(hal.stream.type == StreamType_SDCard) { if(file.line > 0) { char buf[70]; - sprintf(buf, "[MSG:Reset during streaming of SD file at line: " UINT32FMT "]" ASCII_EOL, file.line); - hal.stream.write(buf); - } + sprintf(buf, "Reset during streaming of SD file at line: " UINT32FMT, file.line); + report_message(buf, Message_Plain); + } else if(frewind) + report_feedback_message(Message_None); sdcard_end_job(); } @@ -585,6 +606,9 @@ static void onReportCommandHelp (void) hal.stream.write("$F - list files on SD card" ASCII_EOL); hal.stream.write("$F= - run SD card file" ASCII_EOL); hal.stream.write("$FM - mount SD card" ASCII_EOL); +#if FF_FS_READONLY == 0 && FF_FS_MINIMIZE == 0 + hal.stream.write("$FD= - delete SD card file" ASCII_EOL); +#endif hal.stream.write("$FR - enable rewind mode for next SD card file to run" ASCII_EOL); hal.stream.write("$F<= - dump SD card file to output" ASCII_EOL); @@ -597,15 +621,20 @@ static void onReportOptions (bool newopt) on_report_options(newopt); if(newopt) +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + hal.stream.write(hal.stream.write_char == NULL ? ",SD" : ",SD,YM"); +#else hal.stream.write(",SD"); +#endif else - hal.stream.write("[PLUGIN:SDCARD v1.00]" ASCII_EOL); + hal.stream.write("[PLUGIN:SDCARD v1.02]" ASCII_EOL); } const sys_command_t sdcard_command_list[] = { {"F", false, sd_cmd_file}, {"FM", true, sd_cmd_mount}, {"FR", true, sd_cmd_rewind}, + {"FD", false, sd_cmd_unlink}, {"F<", false, sd_cmd_to_output}, }; @@ -632,6 +661,11 @@ void sdcard_init (void) on_report_options = grbl.on_report_options; grbl.on_report_options = onReportOptions; + +#if SDCARD_ENABLE == 2 && FF_FS_READONLY == 0 + if(hal.stream.write_char != NULL) + ymodem_init(); +#endif } FATFS *sdcard_getfs (void) diff --git a/drivers/LPC1769/src/sdcard/ymodem.c b/drivers/LPC1769/src/sdcard/ymodem.c new file mode 100644 index 00000000..41cea855 --- /dev/null +++ b/drivers/LPC1769/src/sdcard/ymodem.c @@ -0,0 +1,386 @@ +/* + ymodem.c - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Specification: http://wiki.synchro.net/ref:ymodem + + NOTE: Receiver only, does not send initial 'C' to start transfer. + Start transfer by sending SOH or STX. + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + + +#include "sdcard.h" + +#if SDCARD_ENABLE == 2 + +#include +#include + +typedef enum { + YModem_NOOP = 0, + YModem_ACK, + YModem_ACKFile, + YModem_NoFile, + YModem_Purge, + YModem_NAK, + YModem_EOT, + YModem_CAN +} ymodem_status_t; + +typedef ymodem_status_t (*process_data_ptr)(uint8_t c); + +typedef struct { + FATFS *fs; + FIL *handle; + char filename[32]; + uint32_t filelength; + uint32_t received; + uint16_t crc; + uint_fast16_t idx; + uint_fast16_t errors; + uint_fast16_t packet_len; + uint8_t packet_num; + uint32_t next_timeout; + process_data_ptr process; + bool seq_inv; + bool crc_lsb; + bool completed; + bool repeated; + uint8_t payload[1024]; +} ymodem_t; + +static ymodem_t ymodem; + +static driver_reset_ptr driver_reset; +static on_execute_realtime_ptr on_execute_realtime; +static on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; +static io_stream_t active_stream; + +static ymodem_status_t await_soh (uint8_t c); +static ymodem_status_t await_packetnum (uint8_t c); +static ymodem_status_t get_payload (uint8_t c); +static ymodem_status_t await_crc (uint8_t c); +static ymodem_status_t await_eot (uint8_t c); + +// Fast CRC16 implementation +// Original Code: Ashley Roll +// Optimisations: Scott Dattalo +// From http://www.ccsinfo.com/forum/viewtopic.php?t=24977 +static uint16_t crc16 (const uint8_t *buf, uint16_t len) +{ + uint16_t x, crc = 0; + + while(len--) { + x = (crc >> 8) ^ *buf++; + x ^= x >> 4; + crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x; + } + + return crc; +} + +// End transfer handler. +static void end_transfer (bool send_ack) +{ + if(ymodem.handle) { + f_close(ymodem.handle); + ymodem.handle = NULL; + } + + // Restore stream handlers and detach protocol loop from foreground process. + memcpy(&hal.stream, &active_stream, sizeof(io_stream_t)); + grbl.on_execute_realtime = on_execute_realtime; + + if(send_ack) { + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + } +} + +// Cancel handler. Waits for second cancel character. +static ymodem_status_t await_cancel (uint8_t c) +{ + if(c == ASCII_CAN) + end_transfer(false); + else + ymodem.process = await_soh; + + return YModem_NOOP; +} + +// Purge handler. Sinks incoming characters. +static ymodem_status_t purge (uint8_t c) +{ + return YModem_NOOP; +} + +// +// Packet processing +// + +// Start of header handler. +static ymodem_status_t await_soh (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(c == ASCII_SOH || c == ASCII_STX) { + ymodem.idx = ymodem.crc = 0; + ymodem.crc_lsb = ymodem.seq_inv = ymodem.repeated = false; + ymodem.packet_len = c == ASCII_SOH ? 128 : 1024; + ymodem.process = await_packetnum; // Set active handler to wait for packet number. + } else if(c == ASCII_EOT) + end_transfer(true); + else if(c == ASCII_CAN) + ymodem.process = await_cancel; // Set active handler to wait for second CAN character. + else + status = YModem_Purge; + + return status; +} + +// Packet number handler. Reads and validates packet number. +static ymodem_status_t await_packetnum (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(ymodem.seq_inv) { + c ^= 0xFF; + if((ymodem.repeated = ymodem.packet_num == c - 1)) + c++; + if(c == ymodem.packet_num) + ymodem.process = get_payload; // Set active handler to fetch payload + else + status = YModem_Purge; + } else if(!(ymodem.seq_inv = (c == ymodem.packet_num || ymodem.packet_num == c - 1))) + status = YModem_Purge; + + return status; +} + +// Payload handler. Saves incoming characters in payload buffer. +static ymodem_status_t get_payload (uint8_t c) +{ + ymodem.payload[ymodem.idx++] = c; + + if(ymodem.idx == ymodem.packet_len) + ymodem.process = await_crc; + + return YModem_NOOP; +} + +// CRC handler. Reads and validates CRC, open file on packet 0 writes payload to file when valid. +static ymodem_status_t await_crc (uint8_t c) +{ + ymodem_status_t status = YModem_NOOP; + + if(!ymodem.crc_lsb) { + ymodem.crc_lsb = true; + ymodem.crc = c; + } else { + + ymodem.process = await_soh; // Set active handler to wait for next packet + ymodem.crc = (ymodem.crc << 8) | c; + + if(crc16((const uint8_t *)&ymodem.payload, ymodem.packet_len) != ymodem.crc) // If CRC invalid + return YModem_Purge; // purge input stream and return NAK. + + if(ymodem.packet_num == 0 && *ymodem.filename == 0) { // Open file or end transfer + + const char *data = (const char *)ymodem.payload; + + // If no filename present in payload end transfer by sending ACK else send ACK + C if file could be opened, CAN if not. + if((status = *data == '\0' ? YModem_NoFile : YModem_ACKFile) == YModem_ACKFile) { + + strcpy(ymodem.filename, (const char *)data); // Save filename + + data += strlen(ymodem.filename) + 1; // Save file length if present + if(*data != '\0') + ymodem.filelength = atoi(data); + + static FIL handle; + if(ymodem.fs != NULL && f_open(&handle, ymodem.filename, FA_CREATE_ALWAYS|FA_WRITE) == FR_OK) + ymodem.handle = &handle; + else + status = YModem_CAN; + } + + ymodem.packet_num++; + + } else { // Write payload to file + + UINT wrlen; + + if(!ymodem.repeated) { + ymodem.packet_num++; + ymodem.received += ymodem.packet_len; + + // Trim packet length to match file length if last packet + if((ymodem.completed = ymodem.filelength && ymodem.received > ymodem.filelength)) + ymodem.packet_len -= ymodem.received - ymodem.filelength; + + // Write payload + if(f_write(ymodem.handle, ymodem.payload, ymodem.packet_len, &wrlen) == FR_OK) { + status = YModem_ACK; + if(ymodem.completed) { + ymodem.idx = 0; + ymodem.process = await_eot; // Set active handler to wait for end of transfer + } + } else + status = YModem_CAN; + } else + status = YModem_ACK; + } + } + + return status; +} + +// End of transmission handler. +static ymodem_status_t await_eot (uint8_t c) +{ + if(c == ASCII_EOT) + end_transfer(true); + + return YModem_NOOP; +} + +// Main YModem protocol loop. +// Reads characters off input stream and dispatches them to the appropriate handler. +// Handles timeouts. +static void protocol_loop (sys_state_t state) +{ + int16_t c; + + if(hal.get_elapsed_ticks() >= ymodem.next_timeout) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + ymodem.errors++; + if(ymodem.errors > 10) + end_transfer(false); + else { + ymodem.process = await_soh; + hal.stream.write_char(ASCII_NAK); + } + } + + while((c = active_stream.read()) != SERIAL_NO_DATA) { + + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + + switch(ymodem.process(c)) { + + case YModem_ACK: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + break; + + case YModem_ACKFile: + ymodem.errors = 0; + hal.stream.write_char(ASCII_ACK); + hal.stream.write_char('C'); + break; + + case YModem_NoFile: + hal.stream.write_char(ASCII_ACK); + end_transfer(false); + break; + + case YModem_NAK: + ymodem.errors++; + hal.stream.write_char(ASCII_NAK); + break; + + case YModem_CAN: + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + break; + + case YModem_Purge: + ymodem.errors++; + ymodem.process = purge; + hal.stream.cancel_read_buffer(); + break; + + default: + break; + } + + if(grbl.on_execute_realtime != protocol_loop) + break; + } + + on_execute_realtime(state); +} + +// Buffer all received characters. +static bool buffer_all (char c) +{ + return false; +} + +// Terminate any ongoing transfer on a soft reset. +static void on_soft_reset (void) +{ + if(grbl.on_execute_realtime == protocol_loop) { + hal.stream.write_char(ASCII_CAN); + hal.stream.write_char(ASCII_CAN); + end_transfer(false); + } + + driver_reset(); +} + +// Check input stream for file YModem start of header (soh) characters. +// Redirect input stream and start protocol handler when found. +static bool trap_initial_soh (char c) +{ + if(c == ASCII_SOH || c == ASCII_STX) { + + memcpy(&active_stream, &hal.stream, sizeof(io_stream_t)); // Save current stream pointers, + hal.stream.enqueue_realtime_command = buffer_all; // stop core real-time command handling and + hal.stream.read = stream_get_null; // block core protocol loop from reading from input + + on_execute_realtime = grbl.on_execute_realtime; // Add YModem protocol loop + grbl.on_execute_realtime = protocol_loop; // to grblHAL foreground process + + memset(&ymodem, 0, sizeof(ymodem_t)); // Init YModem variables + ymodem.process = await_soh; + ymodem.next_timeout = hal.get_elapsed_ticks() + 1000; + ymodem.fs = sdcard_getfs(); + + return false; // Return false to add character to input buffer + } + + return on_unknown_realtime_cmd == NULL || on_unknown_realtime_cmd(c); +} + +// Add YModem protocol to chain of unknown real-time command handlers +void ymodem_init (void) +{ + driver_reset = hal.driver_reset; + hal.driver_reset = on_soft_reset; + + on_unknown_realtime_cmd = grbl.on_unknown_realtime_cmd; + grbl.on_unknown_realtime_cmd = trap_initial_soh; +} + +#endif + diff --git a/drivers/LPC1769/src/sdcard/ymodem.h b/drivers/LPC1769/src/sdcard/ymodem.h new file mode 100644 index 00000000..de91553a --- /dev/null +++ b/drivers/LPC1769/src/sdcard/ymodem.h @@ -0,0 +1,22 @@ +/* + ymodem.h - simple file transfer protocol + + Part of SDCard plugin for grblHAL + + Copyright (c) 2021 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +void ymodem_init (void); diff --git a/drivers/LPC1769/src/smoothieboard_map.h b/drivers/LPC1769/src/smoothieboard_map.h index 9ee775a5..63e0e33c 100644 --- a/drivers/LPC1769/src/smoothieboard_map.h +++ b/drivers/LPC1769/src/smoothieboard_map.h @@ -27,73 +27,79 @@ // Define step pulse output pins. -#define STEP_PN 2 -#define STEP_PORT port(STEP_PN) -#define X_STEP_PIN 0 -#define X_STEP_BIT (1< -#include +#include "tmc26x.h" static SPI_driver_t io; diff --git a/drivers/LPC1769/src/usb_serial.c b/drivers/LPC1769/src/usb_serial.c index 16fad8ab..5c9f5621 100644 --- a/drivers/LPC1769/src/usb_serial.c +++ b/drivers/LPC1769/src/usb_serial.c @@ -189,6 +189,23 @@ void usbRxCancel (void) rxbuf.head = (rxbuf.tail + 1) & (RX_BUFFER_SIZE - 1); } +// +// Writes a single character to the USB output stream, blocks if buffer full +// +bool usbPutC (const char c) +{ + static uint8_t buf[1]; + + *buf = c; + + while(vcom_write(buf, 1) != 1) { + if(!hal.stream_blocking_callback()) + return false; + } + + return true; +} + // // Writes a null terminated string to the USB output stream, blocks if buffer full // Buffers string up to EOL (LF) before transmitting diff --git a/drivers/LPC1769/src/usb_serial.h b/drivers/LPC1769/src/usb_serial.h index 8a035faf..ddaa85b2 100644 --- a/drivers/LPC1769/src/usb_serial.h +++ b/drivers/LPC1769/src/usb_serial.h @@ -4,7 +4,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,10 +25,11 @@ #include void usbInit (void); -int16_t usbGetC(void); -void usbWriteS(const char *s); +int16_t usbGetC (void); +bool usbPutC (const char c); +void usbWriteS (const char *s); uint16_t usbRxFree (void); -void usbRxFlush(void); -void usbRxCancel(void); +void usbRxFlush (void); +void usbRxCancel (void); void usbBufferInput (uint8_t *data, uint32_t length); bool usbSuspendInput (bool suspend); diff --git a/drivers/MSP430F5529/driver.c b/drivers/MSP430F5529/driver.c index 674371e6..389565e2 100644 --- a/drivers/MSP430F5529/driver.c +++ b/drivers/MSP430F5529/driver.c @@ -674,7 +674,7 @@ bool driver_init (void) serialInit(); hal.info = "MSP430F5529"; - hal.driver_version = "210219"; + hal.driver_version = "210423"; hal.driver_setup = driver_setup; hal.f_step_timer = 24000000; hal.rx_buffer_size = RX_BUFFER_SIZE; @@ -710,6 +710,7 @@ bool driver_init (void) hal.stream.read = serialGetC; hal.stream.write = serialWriteS; hal.stream.write_all = serialWriteS; + hal.stream.write_char = serialPutC; hal.stream.get_rx_buffer_available = serialRxFree; hal.stream.reset_read_buffer = serialRxFlush; hal.stream.cancel_read_buffer = serialRxCancel; diff --git a/drivers/MSP430F5529/grbl/config.h b/drivers/MSP430F5529/grbl/config.h index 2c1662b7..ef0e9192 100644 --- a/drivers/MSP430F5529/grbl/config.h +++ b/drivers/MSP430F5529/grbl/config.h @@ -579,6 +579,7 @@ // for professional CNC machines, regardless of where the limit switches are located. Set this // define to 1 to force Grbl to always set the machine origin at the homed location despite switch orientation. //#define HOMING_FORCE_SET_ORIGIN // Default disabled. Uncomment to enable. +#define HOMING_FORCE_SET_ORIGIN 1 // To prevent the homing cycle from racking the dual axis, when one limit triggers before the // other due to switch failure or noise, the homing cycle will automatically abort if the second diff --git a/drivers/MSP430F5529/grbl/crossbar.h b/drivers/MSP430F5529/grbl/crossbar.h index 1b650b21..70949a96 100644 --- a/drivers/MSP430F5529/grbl/crossbar.h +++ b/drivers/MSP430F5529/grbl/crossbar.h @@ -24,6 +24,60 @@ #ifndef _CROSSBAR_H_ #define _CROSSBAR_H_ +typedef enum { + Input_Probe = 0, + Input_Reset, + Input_FeedHold, + Input_CycleStart, + Input_SafetyDoor, + Input_LimitsOverride, + Input_EStop, + Input_ModeSelect, + Input_LimitX, + Input_LimitX_Max, + Input_LimitY, + Input_LimitY_Max, + Input_LimitZ, + Input_LimitZ_Max, + Input_LimitA, + Input_LimitA_Max, + Input_LimitB, + Input_LimitB_Max, + Input_LimitC, + Input_LimitC_Max, + Input_KeypadStrobe, + Input_QEI_A, + Input_QEI_B, + Input_QEI_Select, + Input_QEI_Index, + Input_SpindleIndex, + Input_Aux0, + Input_Aux1, + Input_Aux2, + Input_Aux3 +} pin_function_t; + +typedef enum { + IRQ_Mode_None = 0b00, + IRQ_Mode_Change = 0b01, + IRQ_Mode_Rising = 0b10, + IRQ_Mode_Falling = 0b11 +} pin_irq_mode_t; + +typedef enum { + PinGroup_Control = (1<<0), + PinGroup_Limit = (1<<1), + PinGroup_Probe = (1<<2), + PinGroup_Keypad = (1<<3), + PinGroup_MPG = (1<<4), + PinGroup_QEI = (1<<5), + PinGroup_QEI_Select = (1<<6), + PinGroup_SpindlePulse = (1<<7), + PinGroup_SpindleIndex = (1<<8), + PinGroup_AuxInput = (1<<9), + PinGroup_AuxOutput = (1<<10) +} pin_group_t; + typedef bool (*xbar_get_value_ptr)(void); typedef void (*xbar_set_value_ptr)(bool on); typedef void (*xbar_event_ptr)(bool on); diff --git a/drivers/MSP430F5529/grbl/errors.h b/drivers/MSP430F5529/grbl/errors.h index d47fb52f..ea7bcb4b 100644 --- a/drivers/MSP430F5529/grbl/errors.h +++ b/drivers/MSP430F5529/grbl/errors.h @@ -83,6 +83,7 @@ typedef enum { Status_MotorFault = 51, Status_SettingValueOutOfRange = 52, Status_SettingDisabled = 53, + Status_GcodeInvalidRetractPosition = 54, // Some error codes as defined in bdring's ESP32 port Status_SDMountError = 60, @@ -156,6 +157,7 @@ PROGMEM static const status_detail_t status_detail[] = { { Status_MotorFault, "Motor fault", "Motor fault." }, { Status_SettingValueOutOfRange, "Value out of range.", "Setting value is out of range." }, { Status_SettingDisabled, "Setting disabled", "Setting is not available, possibly due to limited driver support." }, + { Status_GcodeInvalidRetractPosition, "Invalid gcode ID:54", "Retract position is less than drill depth." }, { Status_SDMountError, "SD Card", "SD Card mount failed." }, { Status_SDReadError, "SD Card", "SD Card file open/read failed." }, { Status_SDFailedOpenDir, "SD Card", "SD Card directory listing failed." }, diff --git a/drivers/MSP430F5529/grbl/gcode.c b/drivers/MSP430F5529/grbl/gcode.c index df927e09..de2fef35 100644 --- a/drivers/MSP430F5529/grbl/gcode.c +++ b/drivers/MSP430F5529/grbl/gcode.c @@ -636,11 +636,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. - - if (axis_command) - FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } - - axis_command = AxisCommand_ToolLengthOffset; + // NOTE: cannot find the NIST statement referenced above, changed to match LinuxCNC behaviour in build 20210513. if (int_value == 49) // G49 gc_block.modal.tool_offset_mode = ToolLengthOffset_Cancel; #ifdef N_TOOLS @@ -649,9 +645,12 @@ status_code_t gc_execute_block(char *block, char *message) else if (mantissa == 20) // G43.2 gc_block.modal.tool_offset_mode = ToolLengthOffset_ApplyAdditional; #endif - else if (mantissa == 10) // G43.1 + else if (mantissa == 10) { // G43.1 + if (axis_command) + FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } + axis_command = AxisCommand_ToolLengthOffset; gc_block.modal.tool_offset_mode = ToolLengthOffset_EnableDynamic; - else + } else FAIL(Status_GcodeUnsupportedCommand); // [Unsupported G43.x command] mantissa = 0; // Set to zero to indicate valid non-integer G command. break; @@ -1316,7 +1315,9 @@ status_code_t gc_execute_block(char *block, char *message) // Pre-convert XYZ coordinate values to millimeters, if applicable. uint_fast8_t idx = N_AXIS; if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used. - if (bit_istrue(axis_words.mask, bit(--idx))) + idx--; +// if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (bit_istrue(axis_words.mask, bit(idx))) gc_block.values.xyz[idx] *= MM_PER_INCH; } while(idx); @@ -1412,13 +1413,13 @@ status_code_t gc_execute_block(char *block, char *message) // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if N_TOOLS defined. // [G43.1 Errors]: Motion command in same line. - // [G43.2 Errors]: Motion command in same line. Tool number not in the tool table, - // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid - // axis that is configured (in config.h). There should be an error if the configured axis - // is absent or if any of the other axis words are present. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates called in block. + // [G43.2 Errors]: Tool number not in the tool table, + if (command_words.G8) { // Indicates called in block. #ifdef TOOL_LENGTH_OFFSET_AXIS + // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid + // axis that is configured (in config.h). There should be an error if the configured axis + // is absent or if any of the other axis words are present. if(gc_block.modal.tool_offset_mode == ToolLengthOffset_EnableDynamic) { if (axis_words.mask ^ bit(TOOL_LENGTH_OFFSET_AXIS)) FAIL(Status_GcodeG43DynamicAxisError); @@ -1819,12 +1820,10 @@ status_code_t gc_execute_block(char *block, char *message) else if(gc_block.values.l <= 0) FAIL(Status_NonPositiveValue); // [L <= 0] - if (value_words.r) { - gc_state.canned.retract_position = gc_block.values.r; - if(gc_state.modal.distance_incremental) - gc_state.canned.retract_position += gc_state.position[plane.axis_linear]; - gc_state.canned.retract_position = gc_block.modal.coord_system.xyz[plane.axis_linear] + gc_state.canned.retract_position; - } + if(value_words.r) + gc_state.canned.retract_position = gc_block.values.r + (gc_block.modal.distance_incremental + ? gc_state.position[plane.axis_linear] + : gc_get_block_offset(&gc_block, plane.axis_linear)); idx = N_AXIS; do { @@ -1832,11 +1831,13 @@ status_code_t gc_execute_block(char *block, char *message) gc_state.canned.xyz[idx] = gc_block.values.xyz[idx]; if(idx != plane.axis_linear) gc_state.canned.xyz[idx] -= gc_state.position[idx]; + else if(gc_block.modal.distance_incremental) + gc_state.canned.xyz[idx] = gc_state.canned.retract_position + (gc_state.canned.xyz[idx] - gc_state.position[idx]); } } while(idx); if(gc_state.canned.retract_position < gc_state.canned.xyz[plane.axis_linear]) - FAIL(Status_GcodeAxisCommandConflict); + FAIL(Status_GcodeInvalidRetractPosition); value_words.r = value_words.l = Off; // Remove single-meaning value words. @@ -2030,7 +2031,9 @@ status_code_t gc_execute_block(char *block, char *message) if (gc_block.modal.units_imperial) { idx = 3; do { // Axes indices are consistent, so loop may be used to save flash space. - if (ijk_words.mask & bit(--idx)) + idx--; +// if (ijk_words.mask & bit(idx) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (ijk_words.mask & bit(idx)) gc_block.values.ijk[idx] *= MM_PER_INCH; } while(idx); } @@ -2412,7 +2415,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms // of execution. The error-checking step would simply load the offset value into the correct // axis of the block XYZ value array. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates a change. + if (command_words.G8) { // Indicates a change. bool tlo_changed = false; @@ -2725,11 +2728,11 @@ status_code_t gc_execute_block(char *block, char *message) hal.coolant.set_state(gc_state.modal.coolant); sys.report.spindle = On; // Set to report change immediately sys.report.coolant = On; // ... - - if(grbl.on_program_completed) - grbl.on_program_completed(gc_state.modal.program_flow); } + if(grbl.on_program_completed) + grbl.on_program_completed(gc_state.modal.program_flow, check_mode); + // Clear any pending output commands while(output_commands) { output_command_t *next = output_commands->next; diff --git a/drivers/MSP430F5529/grbl/grbl.h b/drivers/MSP430F5529/grbl/grbl.h index 46556f33..b8522819 100644 --- a/drivers/MSP430F5529/grbl/grbl.h +++ b/drivers/MSP430F5529/grbl/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_VERSION_BUILD "20210313" +#define GRBL_VERSION_BUILD "20210513" // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/drivers/MSP430F5529/grbl/hal.h b/drivers/MSP430F5529/grbl/hal.h index 1b6de673..baa34852 100644 --- a/drivers/MSP430F5529/grbl/hal.h +++ b/drivers/MSP430F5529/grbl/hal.h @@ -73,16 +73,18 @@ typedef void (*driver_reset_ptr)(void); // I/O stream +typedef int16_t (*stream_read_ptr)(void); typedef void (*stream_write_ptr)(const char *s); +typedef bool (*stream_write_char_ptr)(const char c); typedef bool (*enqueue_realtime_command_ptr)(char data); typedef struct { stream_type_t type; uint16_t (*get_rx_buffer_available)(void); -// bool (*stream_write)(char c); - stream_write_ptr write; // write string to current I/O stream only. - stream_write_ptr write_all; // write string to all active output streams. - int16_t (*read)(void); + stream_write_ptr write; // write string to current I/O stream only. + stream_write_ptr write_all; // write string to all active output streams. + stream_write_char_ptr write_char; // write single character to current I/O stream only. + stream_read_ptr read; void (*reset_read_buffer)(void); void (*cancel_read_buffer)(void); bool (*suspend_read)(bool await); @@ -340,14 +342,15 @@ typedef struct { typedef void (*on_state_change_ptr)(sys_state_t state); typedef void (*on_probe_completed_ptr)(void); -typedef void (*on_program_completed_ptr)(program_flow_t program_flow); +typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode); typedef void (*on_execute_realtime_ptr)(sys_state_t state); typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd); +typedef bool (*on_unknown_realtime_cmd_ptr)(char c); typedef void (*on_report_options_ptr)(bool newopt); typedef void (*on_report_command_help_ptr)(void); typedef void (*on_global_settings_restore_ptr)(void); typedef setting_details_t *(*on_get_settings_ptr)(void); // NOTE: this must match the signature of the same definition in - // the setting_details_t structure in settings.h! + // the setting_details_t structure in settings.h! typedef void (*on_realtime_report_ptr)(stream_write_ptr stream_write, report_tracking_flags_t report); typedef void (*on_unknown_feedback_message_ptr)(stream_write_ptr stream_write); typedef bool (*on_laser_ppi_enable_ptr)(uint_fast16_t ppi, uint_fast16_t pulse_length); @@ -370,6 +373,7 @@ typedef struct { on_get_settings_ptr on_get_settings; on_realtime_report_ptr on_realtime_report; on_unknown_feedback_message_ptr on_unknown_feedback_message; + on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; on_unknown_sys_command_ptr on_unknown_sys_command; // return Status_Unhandled if not handled. on_get_commands_ptr on_get_commands; on_user_command_ptr on_user_command; diff --git a/drivers/MSP430F5529/grbl/limits.c b/drivers/MSP430F5529/grbl/limits.c index 340be0f3..7289e710 100644 --- a/drivers/MSP430F5529/grbl/limits.c +++ b/drivers/MSP430F5529/grbl/limits.c @@ -277,8 +277,8 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar if(auto_square.mask) { float fail_distance = (-settings.homing.dual_axis.fail_length_percent / 100.0f) * settings.axis[dual_motor_axis].max_travel; - fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_min); - fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_min); autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm); } diff --git a/drivers/MSP430F5529/grbl/motion_control.c b/drivers/MSP430F5529/grbl/motion_control.c index ef4b8414..fc2ce283 100644 --- a/drivers/MSP430F5529/grbl/motion_control.c +++ b/drivers/MSP430F5529/grbl/motion_control.c @@ -562,7 +562,6 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ if(repeats && gc_state.modal.distance_incremental) { position[plane.axis_0] += canned->xyz[plane.axis_0]; position[plane.axis_1] += canned->xyz[plane.axis_1]; - position[plane.axis_linear] = canned->prev_position; if(!mc_line(position, pl_data)) return; } diff --git a/drivers/MSP430F5529/grbl/protocol.c b/drivers/MSP430F5529/grbl/protocol.c index a15f70e9..030b947c 100644 --- a/drivers/MSP430F5529/grbl/protocol.c +++ b/drivers/MSP430F5529/grbl/protocol.c @@ -45,9 +45,8 @@ typedef union { uint8_t overflow :1, comment_parentheses :1, comment_semicolon :1, - line_is_comment :1, block_delete :1, - unassigned :3; + unassigned :4; }; } line_flags_t; @@ -164,7 +163,7 @@ bool protocol_main_loop (void) int16_t c; char eol = '\0'; line_flags_t line_flags = {0}; - bool nocaps = false; + bool nocaps = false, line_is_comment = false; xcommand[0] = '\0'; user_message.show = keep_rt_commands = false; @@ -178,7 +177,7 @@ bool protocol_main_loop (void) if(c == ASCII_CAN) { eol = xcommand[0] = '\0'; - keep_rt_commands = nocaps = user_message.show = false; + keep_rt_commands = nocaps = line_is_comment = user_message.show = false; char_counter = line_flags.value = 0; gc_state.last_error = Status_OK; @@ -207,7 +206,7 @@ bool protocol_main_loop (void) // Direct and execute one line of formatted input, and report status of execution. if (line_flags.overflow) // Report line overflow error. gc_state.last_error = Status_Overflow; - else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_flags.line_is_comment) // Empty or comment line. For syncing purposes. + else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_is_comment) // Empty or comment line. For syncing purposes. gc_state.last_error = Status_OK; else if (line[0] == '$') {// Grbl '$' system command if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) { @@ -275,7 +274,7 @@ bool protocol_main_loop (void) case '(': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; if(!keep_rt_commands) { // Enable comments flag and ignore all characters until ')' or EOL unless it is a message. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. @@ -297,7 +296,7 @@ bool protocol_main_loop (void) case ';': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. if(!keep_rt_commands) { if((line_flags.comment_semicolon = !line_flags.comment_parentheses)) @@ -855,13 +854,13 @@ ISR_CODE bool protocol_enqueue_realtime_command (char c) default: if(c < ' ' || (c >= 0x7F && c <= 0xBF)) - drop = true; + drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c); break; } // 2. Process printable ASCII characters and top-bit set characters // If legacy realtime commands are disabled they are returned to the input stream - // when appering in settings ($ commands) or comments + // when appearing in settings ($ commands) or comments if(!drop) switch ((unsigned char)c) { diff --git a/drivers/MSP430F5529/grbl/settings.c b/drivers/MSP430F5529/grbl/settings.c index 395d44fc..e0144ae8 100644 --- a/drivers/MSP430F5529/grbl/settings.c +++ b/drivers/MSP430F5529/grbl/settings.c @@ -32,6 +32,7 @@ #include "limits.h" #include "nvs_buffer.h" #include "tool_change.h" +#include "state_machine.h" #ifdef ENABLE_BACKLASH_COMPENSATION #include "motion_control.h" #endif @@ -99,6 +100,7 @@ PROGMEM const settings_t defaults = { .steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK, .steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK, .steppers.deenergize.mask = ST_DEENERGIZE_MASK, +// .steppers.is_rotational.mask = 0, #if DEFAULT_HOMING_ENABLE .homing.flags.enabled = DEFAULT_HOMING_ENABLE, .homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK, @@ -446,6 +448,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL }, { Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL }, { Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL } +// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL } }; static setting_details_t details = { @@ -456,6 +459,56 @@ static setting_details_t details = { .save = settings_write_global }; +// Acceleration override + +static struct { + bool valid; + float acceleration[N_AXIS]; +} override_backup = { .valid = false }; + +static void save_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + do { + idx--; + override_backup.acceleration[idx] = settings.axis[idx].acceleration; + } while(idx); + + override_backup.valid = true; +} + +static void restore_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + if(override_backup.valid) do { + idx--; + settings.axis[idx].acceleration = override_backup.acceleration[idx]; + } while(idx); +} + +// Temporarily override acceleration, if 0 restore to setting value. +// Note: only allowed when current state is idle. +bool settings_override_acceleration (uint8_t axis, float acceleration) +{ + if(state_get() != STATE_IDLE) + return false; + + if(acceleration <= 0.0f) { + if(override_backup.valid) + settings.axis[axis].acceleration = override_backup.acceleration[axis]; + } else { + if(!override_backup.valid) + save_override_backup(); + settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value? + } + + return true; +} + +// --- + setting_details_t *settings_get_details (void) { details.on_get_settings = grbl.on_get_settings; @@ -796,7 +849,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisAcceleration: - settings.axis[idx].acceleration = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. + settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. break; case Setting_AxisMaxTravel: @@ -1232,6 +1285,9 @@ bool read_global_settings () // Write Grbl global settings and version number to persistent storage void settings_write_global (void) { + if(override_backup.valid) + restore_override_backup(); + if(hal.nvs.type != NVS_None) { hal.nvs.put_byte(0, SETTINGS_VERSION); hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true); diff --git a/drivers/MSP430F5529/grbl/settings.h b/drivers/MSP430F5529/grbl/settings.h index f62a6015..dfadc1ea 100644 --- a/drivers/MSP430F5529/grbl/settings.h +++ b/drivers/MSP430F5529/grbl/settings.h @@ -226,6 +226,7 @@ typedef enum { Settings_IoPort_OD_Enable = 373, Settings_ModBus_BaudRate = 374, Settings_ModBus_RXTimeout = 375, + Settings_Axis_Rotational = 376, Setting_EncoderSettingsBase = 400, // NOTE: Reserving settings values >= 400 for encoder settings. Up to 449. Setting_EncoderSettingsMax = 449, @@ -441,6 +442,7 @@ typedef struct { axes_signals_t dir_invert; axes_signals_t enable_invert; axes_signals_t deenergize; +// axes_signals_t is_rotational; or add to axis_settings_t below as bitmap union? rotational axes are not scaled in imperial mode float pulse_microseconds; float pulse_delay_microseconds; uint16_t idle_lock_time; // If value = 255, steppers do not disable. @@ -708,6 +710,9 @@ bool settings_write_tool_data (tool_data_t *tool_data); // Read selected tool data from persistent storage bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data); +// Temporarily override acceleration, if 0 restore to configured setting value +bool settings_override_acceleration (uint8_t axis, float acceleration); + setting_details_t *settings_get_details (void); bool settings_is_group_available (setting_group_t group); bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data); diff --git a/drivers/MSP430F5529/grbl/stream.c b/drivers/MSP430F5529/grbl/stream.c index ba9b6046..da86fe65 100644 --- a/drivers/MSP430F5529/grbl/stream.c +++ b/drivers/MSP430F5529/grbl/stream.c @@ -26,7 +26,7 @@ static stream_rx_buffer_t rxbackup; // "dummy" version of serialGetC -static int16_t stream_get_null (void) +int16_t stream_get_null (void) { return SERIAL_NO_DATA; } diff --git a/drivers/MSP430F5529/grbl/stream.h b/drivers/MSP430F5529/grbl/stream.h index 07e62c08..f89270c2 100644 --- a/drivers/MSP430F5529/grbl/stream.h +++ b/drivers/MSP430F5529/grbl/stream.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ #ifndef _STREAM_H_ #define _STREAM_H_ +#define ASCII_SOH 0x01 +#define ASCII_STX 0x02 #define ASCII_ETX 0x03 +#define ASCII_EOT 0x04 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 @@ -104,7 +107,16 @@ typedef struct { char data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; +#ifdef __cplusplus +extern "C" { +#endif + +int16_t stream_get_null (void); bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); void stream_rx_backup (stream_rx_buffer_t *rxbuffer); +#ifdef __cplusplus +} +#endif + #endif diff --git a/drivers/MSP432/driver.c b/drivers/MSP432/driver.c index aff3c7ab..b41fb079 100644 --- a/drivers/MSP432/driver.c +++ b/drivers/MSP432/driver.c @@ -1420,7 +1420,7 @@ bool driver_init (void) #endif hal.info = "MSP432"; - hal.driver_version = "210206"; + hal.driver_version = "210423"; #ifdef BOARD_NAME hal.board = BOARD_NAME; #endif @@ -1465,6 +1465,7 @@ bool driver_init (void) hal.stream.cancel_read_buffer = serialRxCancel; hal.stream.write = serialWriteS; hal.stream.write_all = serialWriteS; + hal.stream.write_char = serialPutC; hal.stream.suspend_read = serialSuspendInput; hal.irq_enable = enable_irq; diff --git a/drivers/MSP432/grbl/config.h b/drivers/MSP432/grbl/config.h index 2c1662b7..ef0e9192 100644 --- a/drivers/MSP432/grbl/config.h +++ b/drivers/MSP432/grbl/config.h @@ -579,6 +579,7 @@ // for professional CNC machines, regardless of where the limit switches are located. Set this // define to 1 to force Grbl to always set the machine origin at the homed location despite switch orientation. //#define HOMING_FORCE_SET_ORIGIN // Default disabled. Uncomment to enable. +#define HOMING_FORCE_SET_ORIGIN 1 // To prevent the homing cycle from racking the dual axis, when one limit triggers before the // other due to switch failure or noise, the homing cycle will automatically abort if the second diff --git a/drivers/MSP432/grbl/crossbar.h b/drivers/MSP432/grbl/crossbar.h index 1b650b21..70949a96 100644 --- a/drivers/MSP432/grbl/crossbar.h +++ b/drivers/MSP432/grbl/crossbar.h @@ -24,6 +24,60 @@ #ifndef _CROSSBAR_H_ #define _CROSSBAR_H_ +typedef enum { + Input_Probe = 0, + Input_Reset, + Input_FeedHold, + Input_CycleStart, + Input_SafetyDoor, + Input_LimitsOverride, + Input_EStop, + Input_ModeSelect, + Input_LimitX, + Input_LimitX_Max, + Input_LimitY, + Input_LimitY_Max, + Input_LimitZ, + Input_LimitZ_Max, + Input_LimitA, + Input_LimitA_Max, + Input_LimitB, + Input_LimitB_Max, + Input_LimitC, + Input_LimitC_Max, + Input_KeypadStrobe, + Input_QEI_A, + Input_QEI_B, + Input_QEI_Select, + Input_QEI_Index, + Input_SpindleIndex, + Input_Aux0, + Input_Aux1, + Input_Aux2, + Input_Aux3 +} pin_function_t; + +typedef enum { + IRQ_Mode_None = 0b00, + IRQ_Mode_Change = 0b01, + IRQ_Mode_Rising = 0b10, + IRQ_Mode_Falling = 0b11 +} pin_irq_mode_t; + +typedef enum { + PinGroup_Control = (1<<0), + PinGroup_Limit = (1<<1), + PinGroup_Probe = (1<<2), + PinGroup_Keypad = (1<<3), + PinGroup_MPG = (1<<4), + PinGroup_QEI = (1<<5), + PinGroup_QEI_Select = (1<<6), + PinGroup_SpindlePulse = (1<<7), + PinGroup_SpindleIndex = (1<<8), + PinGroup_AuxInput = (1<<9), + PinGroup_AuxOutput = (1<<10) +} pin_group_t; + typedef bool (*xbar_get_value_ptr)(void); typedef void (*xbar_set_value_ptr)(bool on); typedef void (*xbar_event_ptr)(bool on); diff --git a/drivers/MSP432/grbl/errors.h b/drivers/MSP432/grbl/errors.h index d47fb52f..ea7bcb4b 100644 --- a/drivers/MSP432/grbl/errors.h +++ b/drivers/MSP432/grbl/errors.h @@ -83,6 +83,7 @@ typedef enum { Status_MotorFault = 51, Status_SettingValueOutOfRange = 52, Status_SettingDisabled = 53, + Status_GcodeInvalidRetractPosition = 54, // Some error codes as defined in bdring's ESP32 port Status_SDMountError = 60, @@ -156,6 +157,7 @@ PROGMEM static const status_detail_t status_detail[] = { { Status_MotorFault, "Motor fault", "Motor fault." }, { Status_SettingValueOutOfRange, "Value out of range.", "Setting value is out of range." }, { Status_SettingDisabled, "Setting disabled", "Setting is not available, possibly due to limited driver support." }, + { Status_GcodeInvalidRetractPosition, "Invalid gcode ID:54", "Retract position is less than drill depth." }, { Status_SDMountError, "SD Card", "SD Card mount failed." }, { Status_SDReadError, "SD Card", "SD Card file open/read failed." }, { Status_SDFailedOpenDir, "SD Card", "SD Card directory listing failed." }, diff --git a/drivers/MSP432/grbl/gcode.c b/drivers/MSP432/grbl/gcode.c index df927e09..de2fef35 100644 --- a/drivers/MSP432/grbl/gcode.c +++ b/drivers/MSP432/grbl/gcode.c @@ -636,11 +636,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. - - if (axis_command) - FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } - - axis_command = AxisCommand_ToolLengthOffset; + // NOTE: cannot find the NIST statement referenced above, changed to match LinuxCNC behaviour in build 20210513. if (int_value == 49) // G49 gc_block.modal.tool_offset_mode = ToolLengthOffset_Cancel; #ifdef N_TOOLS @@ -649,9 +645,12 @@ status_code_t gc_execute_block(char *block, char *message) else if (mantissa == 20) // G43.2 gc_block.modal.tool_offset_mode = ToolLengthOffset_ApplyAdditional; #endif - else if (mantissa == 10) // G43.1 + else if (mantissa == 10) { // G43.1 + if (axis_command) + FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] } + axis_command = AxisCommand_ToolLengthOffset; gc_block.modal.tool_offset_mode = ToolLengthOffset_EnableDynamic; - else + } else FAIL(Status_GcodeUnsupportedCommand); // [Unsupported G43.x command] mantissa = 0; // Set to zero to indicate valid non-integer G command. break; @@ -1316,7 +1315,9 @@ status_code_t gc_execute_block(char *block, char *message) // Pre-convert XYZ coordinate values to millimeters, if applicable. uint_fast8_t idx = N_AXIS; if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used. - if (bit_istrue(axis_words.mask, bit(--idx))) + idx--; +// if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (bit_istrue(axis_words.mask, bit(idx))) gc_block.values.xyz[idx] *= MM_PER_INCH; } while(idx); @@ -1412,13 +1413,13 @@ status_code_t gc_execute_block(char *block, char *message) // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if N_TOOLS defined. // [G43.1 Errors]: Motion command in same line. - // [G43.2 Errors]: Motion command in same line. Tool number not in the tool table, - // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid - // axis that is configured (in config.h). There should be an error if the configured axis - // is absent or if any of the other axis words are present. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates called in block. + // [G43.2 Errors]: Tool number not in the tool table, + if (command_words.G8) { // Indicates called in block. #ifdef TOOL_LENGTH_OFFSET_AXIS + // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid + // axis that is configured (in config.h). There should be an error if the configured axis + // is absent or if any of the other axis words are present. if(gc_block.modal.tool_offset_mode == ToolLengthOffset_EnableDynamic) { if (axis_words.mask ^ bit(TOOL_LENGTH_OFFSET_AXIS)) FAIL(Status_GcodeG43DynamicAxisError); @@ -1819,12 +1820,10 @@ status_code_t gc_execute_block(char *block, char *message) else if(gc_block.values.l <= 0) FAIL(Status_NonPositiveValue); // [L <= 0] - if (value_words.r) { - gc_state.canned.retract_position = gc_block.values.r; - if(gc_state.modal.distance_incremental) - gc_state.canned.retract_position += gc_state.position[plane.axis_linear]; - gc_state.canned.retract_position = gc_block.modal.coord_system.xyz[plane.axis_linear] + gc_state.canned.retract_position; - } + if(value_words.r) + gc_state.canned.retract_position = gc_block.values.r + (gc_block.modal.distance_incremental + ? gc_state.position[plane.axis_linear] + : gc_get_block_offset(&gc_block, plane.axis_linear)); idx = N_AXIS; do { @@ -1832,11 +1831,13 @@ status_code_t gc_execute_block(char *block, char *message) gc_state.canned.xyz[idx] = gc_block.values.xyz[idx]; if(idx != plane.axis_linear) gc_state.canned.xyz[idx] -= gc_state.position[idx]; + else if(gc_block.modal.distance_incremental) + gc_state.canned.xyz[idx] = gc_state.canned.retract_position + (gc_state.canned.xyz[idx] - gc_state.position[idx]); } } while(idx); if(gc_state.canned.retract_position < gc_state.canned.xyz[plane.axis_linear]) - FAIL(Status_GcodeAxisCommandConflict); + FAIL(Status_GcodeInvalidRetractPosition); value_words.r = value_words.l = Off; // Remove single-meaning value words. @@ -2030,7 +2031,9 @@ status_code_t gc_execute_block(char *block, char *message) if (gc_block.modal.units_imperial) { idx = 3; do { // Axes indices are consistent, so loop may be used to save flash space. - if (ijk_words.mask & bit(--idx)) + idx--; +// if (ijk_words.mask & bit(idx) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) + if (ijk_words.mask & bit(idx)) gc_block.values.ijk[idx] *= MM_PER_INCH; } while(idx); } @@ -2412,7 +2415,7 @@ status_code_t gc_execute_block(char *block, char *message) // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms // of execution. The error-checking step would simply load the offset value into the correct // axis of the block XYZ value array. - if (axis_command == AxisCommand_ToolLengthOffset) { // Indicates a change. + if (command_words.G8) { // Indicates a change. bool tlo_changed = false; @@ -2725,11 +2728,11 @@ status_code_t gc_execute_block(char *block, char *message) hal.coolant.set_state(gc_state.modal.coolant); sys.report.spindle = On; // Set to report change immediately sys.report.coolant = On; // ... - - if(grbl.on_program_completed) - grbl.on_program_completed(gc_state.modal.program_flow); } + if(grbl.on_program_completed) + grbl.on_program_completed(gc_state.modal.program_flow, check_mode); + // Clear any pending output commands while(output_commands) { output_command_t *next = output_commands->next; diff --git a/drivers/MSP432/grbl/grbl.h b/drivers/MSP432/grbl/grbl.h index 46556f33..b8522819 100644 --- a/drivers/MSP432/grbl/grbl.h +++ b/drivers/MSP432/grbl/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_VERSION_BUILD "20210313" +#define GRBL_VERSION_BUILD "20210513" // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/drivers/MSP432/grbl/hal.h b/drivers/MSP432/grbl/hal.h index 1b6de673..baa34852 100644 --- a/drivers/MSP432/grbl/hal.h +++ b/drivers/MSP432/grbl/hal.h @@ -73,16 +73,18 @@ typedef void (*driver_reset_ptr)(void); // I/O stream +typedef int16_t (*stream_read_ptr)(void); typedef void (*stream_write_ptr)(const char *s); +typedef bool (*stream_write_char_ptr)(const char c); typedef bool (*enqueue_realtime_command_ptr)(char data); typedef struct { stream_type_t type; uint16_t (*get_rx_buffer_available)(void); -// bool (*stream_write)(char c); - stream_write_ptr write; // write string to current I/O stream only. - stream_write_ptr write_all; // write string to all active output streams. - int16_t (*read)(void); + stream_write_ptr write; // write string to current I/O stream only. + stream_write_ptr write_all; // write string to all active output streams. + stream_write_char_ptr write_char; // write single character to current I/O stream only. + stream_read_ptr read; void (*reset_read_buffer)(void); void (*cancel_read_buffer)(void); bool (*suspend_read)(bool await); @@ -340,14 +342,15 @@ typedef struct { typedef void (*on_state_change_ptr)(sys_state_t state); typedef void (*on_probe_completed_ptr)(void); -typedef void (*on_program_completed_ptr)(program_flow_t program_flow); +typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode); typedef void (*on_execute_realtime_ptr)(sys_state_t state); typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd); +typedef bool (*on_unknown_realtime_cmd_ptr)(char c); typedef void (*on_report_options_ptr)(bool newopt); typedef void (*on_report_command_help_ptr)(void); typedef void (*on_global_settings_restore_ptr)(void); typedef setting_details_t *(*on_get_settings_ptr)(void); // NOTE: this must match the signature of the same definition in - // the setting_details_t structure in settings.h! + // the setting_details_t structure in settings.h! typedef void (*on_realtime_report_ptr)(stream_write_ptr stream_write, report_tracking_flags_t report); typedef void (*on_unknown_feedback_message_ptr)(stream_write_ptr stream_write); typedef bool (*on_laser_ppi_enable_ptr)(uint_fast16_t ppi, uint_fast16_t pulse_length); @@ -370,6 +373,7 @@ typedef struct { on_get_settings_ptr on_get_settings; on_realtime_report_ptr on_realtime_report; on_unknown_feedback_message_ptr on_unknown_feedback_message; + on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; on_unknown_sys_command_ptr on_unknown_sys_command; // return Status_Unhandled if not handled. on_get_commands_ptr on_get_commands; on_user_command_ptr on_user_command; diff --git a/drivers/MSP432/grbl/limits.c b/drivers/MSP432/grbl/limits.c index 340be0f3..7289e710 100644 --- a/drivers/MSP432/grbl/limits.c +++ b/drivers/MSP432/grbl/limits.c @@ -277,8 +277,8 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar if(auto_square.mask) { float fail_distance = (-settings.homing.dual_axis.fail_length_percent / 100.0f) * settings.axis[dual_motor_axis].max_travel; - fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_min); - fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = min(fail_distance, settings.homing.dual_axis.fail_distance_max); + fail_distance = max(fail_distance, settings.homing.dual_axis.fail_distance_min); autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm); } diff --git a/drivers/MSP432/grbl/motion_control.c b/drivers/MSP432/grbl/motion_control.c index ef4b8414..fc2ce283 100644 --- a/drivers/MSP432/grbl/motion_control.c +++ b/drivers/MSP432/grbl/motion_control.c @@ -562,7 +562,6 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ if(repeats && gc_state.modal.distance_incremental) { position[plane.axis_0] += canned->xyz[plane.axis_0]; position[plane.axis_1] += canned->xyz[plane.axis_1]; - position[plane.axis_linear] = canned->prev_position; if(!mc_line(position, pl_data)) return; } diff --git a/drivers/MSP432/grbl/protocol.c b/drivers/MSP432/grbl/protocol.c index a15f70e9..030b947c 100644 --- a/drivers/MSP432/grbl/protocol.c +++ b/drivers/MSP432/grbl/protocol.c @@ -45,9 +45,8 @@ typedef union { uint8_t overflow :1, comment_parentheses :1, comment_semicolon :1, - line_is_comment :1, block_delete :1, - unassigned :3; + unassigned :4; }; } line_flags_t; @@ -164,7 +163,7 @@ bool protocol_main_loop (void) int16_t c; char eol = '\0'; line_flags_t line_flags = {0}; - bool nocaps = false; + bool nocaps = false, line_is_comment = false; xcommand[0] = '\0'; user_message.show = keep_rt_commands = false; @@ -178,7 +177,7 @@ bool protocol_main_loop (void) if(c == ASCII_CAN) { eol = xcommand[0] = '\0'; - keep_rt_commands = nocaps = user_message.show = false; + keep_rt_commands = nocaps = line_is_comment = user_message.show = false; char_counter = line_flags.value = 0; gc_state.last_error = Status_OK; @@ -207,7 +206,7 @@ bool protocol_main_loop (void) // Direct and execute one line of formatted input, and report status of execution. if (line_flags.overflow) // Report line overflow error. gc_state.last_error = Status_Overflow; - else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_flags.line_is_comment) // Empty or comment line. For syncing purposes. + else if ((line[0] == '\0' || char_counter == 0) && !user_message.show && !line_is_comment) // Empty or comment line. For syncing purposes. gc_state.last_error = Status_OK; else if (line[0] == '$') {// Grbl '$' system command if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) { @@ -275,7 +274,7 @@ bool protocol_main_loop (void) case '(': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; if(!keep_rt_commands) { // Enable comments flag and ignore all characters until ')' or EOL unless it is a message. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. @@ -297,7 +296,7 @@ bool protocol_main_loop (void) case ';': if(char_counter == 0) - line_flags.line_is_comment = On; + line_is_comment = On; // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. if(!keep_rt_commands) { if((line_flags.comment_semicolon = !line_flags.comment_parentheses)) @@ -855,13 +854,13 @@ ISR_CODE bool protocol_enqueue_realtime_command (char c) default: if(c < ' ' || (c >= 0x7F && c <= 0xBF)) - drop = true; + drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c); break; } // 2. Process printable ASCII characters and top-bit set characters // If legacy realtime commands are disabled they are returned to the input stream - // when appering in settings ($ commands) or comments + // when appearing in settings ($ commands) or comments if(!drop) switch ((unsigned char)c) { diff --git a/drivers/MSP432/grbl/settings.c b/drivers/MSP432/grbl/settings.c index 395d44fc..e0144ae8 100644 --- a/drivers/MSP432/grbl/settings.c +++ b/drivers/MSP432/grbl/settings.c @@ -32,6 +32,7 @@ #include "limits.h" #include "nvs_buffer.h" #include "tool_change.h" +#include "state_machine.h" #ifdef ENABLE_BACKLASH_COMPENSATION #include "motion_control.h" #endif @@ -99,6 +100,7 @@ PROGMEM const settings_t defaults = { .steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK, .steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK, .steppers.deenergize.mask = ST_DEENERGIZE_MASK, +// .steppers.is_rotational.mask = 0, #if DEFAULT_HOMING_ENABLE .homing.flags.enabled = DEFAULT_HOMING_ENABLE, .homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK, @@ -446,6 +448,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL }, { Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL }, { Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL } +// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL } }; static setting_details_t details = { @@ -456,6 +459,56 @@ static setting_details_t details = { .save = settings_write_global }; +// Acceleration override + +static struct { + bool valid; + float acceleration[N_AXIS]; +} override_backup = { .valid = false }; + +static void save_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + do { + idx--; + override_backup.acceleration[idx] = settings.axis[idx].acceleration; + } while(idx); + + override_backup.valid = true; +} + +static void restore_override_backup (void) +{ + uint_fast8_t idx = N_AXIS; + + if(override_backup.valid) do { + idx--; + settings.axis[idx].acceleration = override_backup.acceleration[idx]; + } while(idx); +} + +// Temporarily override acceleration, if 0 restore to setting value. +// Note: only allowed when current state is idle. +bool settings_override_acceleration (uint8_t axis, float acceleration) +{ + if(state_get() != STATE_IDLE) + return false; + + if(acceleration <= 0.0f) { + if(override_backup.valid) + settings.axis[axis].acceleration = override_backup.acceleration[axis]; + } else { + if(!override_backup.valid) + save_override_backup(); + settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value? + } + + return true; +} + +// --- + setting_details_t *settings_get_details (void) { details.on_get_settings = grbl.on_get_settings; @@ -796,7 +849,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisAcceleration: - settings.axis[idx].acceleration = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. + settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use. break; case Setting_AxisMaxTravel: @@ -1232,6 +1285,9 @@ bool read_global_settings () // Write Grbl global settings and version number to persistent storage void settings_write_global (void) { + if(override_backup.valid) + restore_override_backup(); + if(hal.nvs.type != NVS_None) { hal.nvs.put_byte(0, SETTINGS_VERSION); hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true); diff --git a/drivers/MSP432/grbl/settings.h b/drivers/MSP432/grbl/settings.h index f62a6015..dfadc1ea 100644 --- a/drivers/MSP432/grbl/settings.h +++ b/drivers/MSP432/grbl/settings.h @@ -226,6 +226,7 @@ typedef enum { Settings_IoPort_OD_Enable = 373, Settings_ModBus_BaudRate = 374, Settings_ModBus_RXTimeout = 375, + Settings_Axis_Rotational = 376, Setting_EncoderSettingsBase = 400, // NOTE: Reserving settings values >= 400 for encoder settings. Up to 449. Setting_EncoderSettingsMax = 449, @@ -441,6 +442,7 @@ typedef struct { axes_signals_t dir_invert; axes_signals_t enable_invert; axes_signals_t deenergize; +// axes_signals_t is_rotational; or add to axis_settings_t below as bitmap union? rotational axes are not scaled in imperial mode float pulse_microseconds; float pulse_delay_microseconds; uint16_t idle_lock_time; // If value = 255, steppers do not disable. @@ -708,6 +710,9 @@ bool settings_write_tool_data (tool_data_t *tool_data); // Read selected tool data from persistent storage bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data); +// Temporarily override acceleration, if 0 restore to configured setting value +bool settings_override_acceleration (uint8_t axis, float acceleration); + setting_details_t *settings_get_details (void); bool settings_is_group_available (setting_group_t group); bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data); diff --git a/drivers/MSP432/grbl/stream.c b/drivers/MSP432/grbl/stream.c index ba9b6046..da86fe65 100644 --- a/drivers/MSP432/grbl/stream.c +++ b/drivers/MSP432/grbl/stream.c @@ -26,7 +26,7 @@ static stream_rx_buffer_t rxbackup; // "dummy" version of serialGetC -static int16_t stream_get_null (void) +int16_t stream_get_null (void) { return SERIAL_NO_DATA; } diff --git a/drivers/MSP432/grbl/stream.h b/drivers/MSP432/grbl/stream.h index 07e62c08..f89270c2 100644 --- a/drivers/MSP432/grbl/stream.h +++ b/drivers/MSP432/grbl/stream.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2020 Terje Io + Copyright (c) 2019-2021 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ #ifndef _STREAM_H_ #define _STREAM_H_ +#define ASCII_SOH 0x01 +#define ASCII_STX 0x02 #define ASCII_ETX 0x03 +#define ASCII_EOT 0x04 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 @@ -104,7 +107,16 @@ typedef struct { char data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; +#ifdef __cplusplus +extern "C" { +#endif + +int16_t stream_get_null (void); bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); void stream_rx_backup (stream_rx_buffer_t *rxbuffer); +#ifdef __cplusplus +} +#endif + #endif diff --git a/drivers/MSP432/trinamic/tmc26x.c b/drivers/MSP432/trinamic/tmc26x.c index 109cc93f..e3ee13d2 100644 --- a/drivers/MSP432/trinamic/tmc26x.c +++ b/drivers/MSP432/trinamic/tmc26x.c @@ -38,7 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include +#include "tmc26x.h" static SPI_driver_t io; diff --git a/drivers/MSP432E401Y/.cproject b/drivers/MSP432E401Y/.cproject index 44926400..4e77ba22 100644 --- a/drivers/MSP432E401Y/.cproject +++ b/drivers/MSP432E401Y/.cproject @@ -15,34 +15,34 @@ - - - + @@ -125,7 +126,7 @@ -