mirror of
https://github.com/grblHAL/core.git
synced 2026-03-23 03:22:56 +08:00
Fixed incorrect behaviour when a tool change using the built-in workflow is aborted, the selected tool was set as the current tool.
Added support for G66.1 macro call. Changed behaviour of G50 and G51 (and the G48 and G49 shortcuts) controlling feed rate and spindle RPM overrides. Added support for G10L0, reload file based tool table. Fixed buggy handling of G43 - apply tool offset from tool table. Refactored the encoder HAL/API to make it more flexible and as a first step to support rigid tapping. See the changelog for further details.
This commit is contained in:
@@ -42,6 +42,7 @@ target_sources(grbl INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/utf8.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/vfs.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/canbus.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/encoders.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/pid.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/fs_device.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/kinematics/corexy.c
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## grblHAL ##
|
||||
|
||||
Latest build date is 20260218, see the [changelog](changelog.md) for details.
|
||||
Latest build date is 20260225, see the [changelog](changelog.md) for details.
|
||||
|
||||
> [!NOTE]
|
||||
> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
|
||||
|
||||
47
changelog.md
47
changelog.md
@@ -1,10 +1,55 @@
|
||||
## grblHAL changelog
|
||||
|
||||
<a name="20260225">Build 20260225
|
||||
|
||||
Core:
|
||||
|
||||
* Fix incorrect behaviour when a tool change using the built-in workflow is aborted, the selected tool was set as the current tool.
|
||||
|
||||
* Clarification: `G66` macros calls are not run on their first invocation and will only be run on subsequent blocks containing axis words for `G0` or `G1` motion.
|
||||
Subsequent blocks may contain either the `G0` or `G1` command word, if not their modal state will be used. The macro will be called after the motion has been sent to the planner.
|
||||
|
||||
* Added support for `G66.1` macro call. Unlike `G66` this is run on the first invocation and for each subsequent block containing parameter word\(s\) used in the first invocation.
|
||||
Parameter values changed in subsequent blocks are "sticky", that is they will keep their value for subsequent blocks until changed again.
|
||||
|
||||
> [!NOTE]
|
||||
> `G66` and `G66.1` behaviour may change in later builds since I have not found any definite specification, it seems that there are different implementations between controllers.
|
||||
> grbHAL aims to adopt the Fanuc behaviour, but this has not been verified.
|
||||
|
||||
* Changed behaviour of `G50` and `G51` \(and the `G48` and `G49` shortcuts\) controlling feed rate and spindle RPM overrides.
|
||||
When used with `P0` to turn off the feed rate and/or the spindle RPM will be reverted to their programmed values.
|
||||
The override values can still be changed and any changed value will be output in the real time report.
|
||||
When turned back on the current override value\(s\) will be reapplied.
|
||||
|
||||
* Added support for `G10L0`, reload file based tool table. Can only be used when no tool (tool 0) is in the spindle.
|
||||
|
||||
* Fixed buggy handling of `G43` - apply tool offset from tool table.
|
||||
|
||||
* Refactored the encoder HAL/API to make it more flexible and as a first step to support rigid tapping.
|
||||
For programmers: encoders are now registered with the core allowing them to be added from both drivers and plugins.
|
||||
Plugins can now claim encoders, and code has been added to the core to make it easy for drivers and plugins to bind a slow encoder to interrupt capable auxiliary inputs.
|
||||
> [!NOTE]
|
||||
> The new HAL/API will be extended as needed later.
|
||||
|
||||
Drivers:
|
||||
|
||||
* iMXRT1062, STM32F4xx, STM32F7xx: updated for the new encoder HAL/API.
|
||||
|
||||
* RP2040: fixed typos affecting limit switch inversion for A+ axes.
|
||||
|
||||
Plugins:
|
||||
|
||||
* Encoder: updated to use the new encoder HAL/API.
|
||||
|
||||
* Misc, Tool table: added support for `G10L0`, reload tool table.
|
||||
|
||||
---
|
||||
|
||||
<a name="20260218">Build 20260218
|
||||
|
||||
Core:
|
||||
|
||||
* Changed `$pinstates` command output, now reports pin number instead of pin id.
|
||||
* Changed `$pinstate` command output, now reports pin number instead of pin id.
|
||||
|
||||
* For developers: added API call that returns Modbus stream handlers.
|
||||
|
||||
|
||||
@@ -143,6 +143,7 @@ typedef tool_table_entry_t *(*get_tool_ptr)(tool_id_t tool_id);
|
||||
typedef tool_table_entry_t *(*get_tool_by_idx_ptr)(uint32_t idx);
|
||||
typedef bool (*set_tool_data_ptr)(tool_data_t *tool_data);
|
||||
typedef bool (*clear_tool_data_ptr)(void);
|
||||
typedef status_code_t (*reload_tool_data_ptr)(void);
|
||||
|
||||
typedef struct {
|
||||
uint32_t n_tools;
|
||||
@@ -150,6 +151,7 @@ typedef struct {
|
||||
get_tool_by_idx_ptr get_tool_by_idx;
|
||||
set_tool_data_ptr set_tool;
|
||||
clear_tool_data_ptr clear;
|
||||
reload_tool_data_ptr reload;
|
||||
} tool_table_t;
|
||||
|
||||
/*****************
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2020-2025 Terje Io
|
||||
Copyright (c) 2020-2026 Terje Io
|
||||
|
||||
grblHAL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -450,12 +450,18 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_ENABLE
|
||||
#define ENCODER_ENABLE 0
|
||||
#endif
|
||||
|
||||
#ifndef QEI_ENABLE
|
||||
#if ENCODER_ENABLE
|
||||
#define QEI_ENABLE 1
|
||||
#else
|
||||
#define QEI_ENABLE 0
|
||||
#endif
|
||||
#ifndef QEI_SELECT_ENABLE
|
||||
#define QEI_SELECT_ENABLE 0
|
||||
#endif
|
||||
|
||||
#ifndef ODOMETER_ENABLE
|
||||
#define ODOMETER_ENABLE 0
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2024-2025 Terje Io
|
||||
Copyright (c) 2024-2026 Terje Io
|
||||
|
||||
grblHAL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -43,6 +43,10 @@
|
||||
#warning "Selected spindle 1 is not supported!"
|
||||
#endif
|
||||
|
||||
#if ENCODER_ENABLE > 0 && !(defined(QEI_A_PIN) && defined(QEI_B_PIN))
|
||||
#warning "ENCODER_ENABLE requires encoder input pins A and B to be defined!"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if MPG_ENABLE == 1 && !defined(MPG_MODE_PIN)
|
||||
|
||||
79
encoders.c
Normal file
79
encoders.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
encoders.c - quadrature encoder interface (API)
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2026 Terje Io
|
||||
|
||||
grblHAL 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.
|
||||
|
||||
grblHAL 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 grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "encoders.h"
|
||||
|
||||
static struct encoders {
|
||||
encoder_t *encoder;
|
||||
struct encoders *next;
|
||||
} *encoders = NULL;
|
||||
|
||||
static uint8_t n_encoders = 0;
|
||||
|
||||
typedef bool (*encoder_enumerate_callback_ptr)(encoder_t *properties, void *data);
|
||||
|
||||
static void dummy_event_handler (encoder_t *encoder, encoder_event_t *events)
|
||||
{
|
||||
UNUSED(encoder);
|
||||
|
||||
events->value = 0;
|
||||
}
|
||||
|
||||
void encoder_register (encoder_t *encoder)
|
||||
{
|
||||
struct encoders *add, *last;
|
||||
|
||||
if((add = malloc(sizeof(struct encoders)))) {
|
||||
|
||||
add->next = NULL;
|
||||
add->encoder = encoder;
|
||||
|
||||
if((last = encoders)) {
|
||||
while(last->next)
|
||||
last = last->next;
|
||||
last->next = add;
|
||||
} else
|
||||
encoders = add;
|
||||
|
||||
n_encoders++;
|
||||
}
|
||||
}
|
||||
|
||||
bool encoders_enumerate (encoder_enumerate_callback_ptr callback, void *data)
|
||||
{
|
||||
bool ok = false;
|
||||
struct encoders *encoder = encoders;
|
||||
|
||||
if(encoder) do {
|
||||
ok = callback(encoder->encoder, data);
|
||||
} while(!ok && (encoder = encoder->next));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint8_t encoders_get_count (void)
|
||||
{
|
||||
return n_encoders;
|
||||
}
|
||||
318
encoders.h
Normal file
318
encoders.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
encoders.c - quadrature encoders interface (API)
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2026 Terje Io
|
||||
|
||||
grblHAL 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.
|
||||
|
||||
grblHAL 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 grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _ENCODERS_H_
|
||||
#define _ENCODERS_H_
|
||||
|
||||
#include "plugins.h"
|
||||
|
||||
// Quadrature encoder interface
|
||||
|
||||
typedef union {
|
||||
uint8_t value;
|
||||
uint8_t events;
|
||||
struct {
|
||||
uint8_t position_changed :1,
|
||||
direction_changed :1,
|
||||
click :1,
|
||||
dbl_click :1,
|
||||
long_click :1,
|
||||
index_pulse :1,
|
||||
unused :2;
|
||||
};
|
||||
} encoder_event_t;
|
||||
|
||||
typedef union {
|
||||
uint8_t value;
|
||||
uint8_t mask;
|
||||
struct {
|
||||
uint8_t bidirectional :1,
|
||||
select :1,
|
||||
index :1,
|
||||
spindle_rpm :1,
|
||||
spindle_pos :1,
|
||||
unused :3;
|
||||
};
|
||||
} encoder_caps_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t vel_timeout;
|
||||
uint32_t dbl_click_window; //!< ms.
|
||||
} encoder_cfg_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t position;
|
||||
uint32_t velocity;
|
||||
} encoder_data_t;
|
||||
|
||||
struct encoder;
|
||||
typedef struct encoder encoder_t;
|
||||
|
||||
/*! \brief Pointer to callback function to receive encoder events.
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
\param events pointer to a \a encoder_event_t struct.
|
||||
\param context pointer to the context passed to the encoders claim function.
|
||||
*/
|
||||
typedef void (*encoder_on_event_ptr)(encoder_t *encoder, encoder_event_t *events, void *context);
|
||||
|
||||
/*! \brief Pointer to function for resetting encoder data.
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
*/
|
||||
typedef void (*encoder_reset_ptr)(encoder_t *encoder);
|
||||
|
||||
/*! \brief Pointer to function for claiming an encoder.
|
||||
\param event_handler pointer to to the event handler callback.
|
||||
\param context pointer to the context to be passed to event handler.
|
||||
\returns \a true when claim was successful, \a false to otherwise.
|
||||
*/
|
||||
typedef bool (*encoder_claim_ptr)(encoder_on_event_ptr event_handler, void *context);
|
||||
|
||||
/*! \brief Pointer to function for getting encoder data.
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
\returns pointer to a \a encoder_data_t struct containing the data.
|
||||
*/
|
||||
typedef encoder_data_t *(*encoder_get_data_ptr)(encoder_t *encoder);
|
||||
|
||||
/*! \brief Pointer to the callbak function to be called by encoders_enumerate().
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
\returns \a true to stop the enumeration and return true from encoders_enumerate(), \a false otherwise.
|
||||
*/
|
||||
typedef bool (*encoder_enumerate_callback_ptr)(encoder_t *encoder, void *data);
|
||||
|
||||
/*! \brief Pointer to function for configuring an encoder.
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
\param encoder pointer to a \a encoder_cfg_t struct.
|
||||
\returns \a true when claim was successful, \a false to otherwise.
|
||||
*/
|
||||
typedef bool (*encoder_configure_ptr)(encoder_t *encoder, encoder_cfg_t *settings);
|
||||
|
||||
void encoder_register (encoder_t *encoder);
|
||||
bool encoders_enumerate (encoder_enumerate_callback_ptr callback, void *data);
|
||||
uint8_t encoders_get_count (void);
|
||||
|
||||
struct encoder {
|
||||
encoder_caps_t caps;
|
||||
encoder_claim_ptr claim;
|
||||
encoder_reset_ptr reset;
|
||||
encoder_get_data_ptr get_data;
|
||||
encoder_configure_ptr configure;
|
||||
};
|
||||
|
||||
#endif // _ENCODERS_H_
|
||||
|
||||
// Quadrature Encoder Interface - static code for drivers/plugins
|
||||
|
||||
#if QEI_ENABLE && defined(QEI_A_PIN) && defined(QEI_B_PIN)
|
||||
|
||||
typedef enum {
|
||||
QEI_DirUnknown = 0,
|
||||
QEI_DirCW,
|
||||
QEI_DirCCW
|
||||
} qei_dir_t;
|
||||
|
||||
typedef union {
|
||||
uint_fast8_t pins;
|
||||
struct {
|
||||
uint_fast8_t a :1,
|
||||
b :1;
|
||||
};
|
||||
} qei_state_t;
|
||||
|
||||
typedef struct {
|
||||
encoder_t encoder;
|
||||
encoder_data_t data;
|
||||
encoder_event_t event;
|
||||
void *context;
|
||||
int32_t vel_count;
|
||||
uint_fast16_t state;
|
||||
qei_dir_t dir;
|
||||
uint8_t port_a, port_b, port_select;
|
||||
volatile uint32_t dbl_click_timeout;
|
||||
volatile uint32_t vel_timeout;
|
||||
uint32_t vel_timestamp;
|
||||
encoder_on_event_ptr on_event;
|
||||
encoder_cfg_t settings;
|
||||
} qei_t;
|
||||
|
||||
static qei_t qei = {
|
||||
.port_a = IOPORT_UNASSIGNED,
|
||||
.port_b = IOPORT_UNASSIGNED,
|
||||
.port_select = IOPORT_UNASSIGNED,
|
||||
.settings.dbl_click_window = 500,
|
||||
.encoder.caps.bidirectional = On
|
||||
};
|
||||
|
||||
static void qei_post_event (void *data)
|
||||
{
|
||||
qei.on_event(&qei.encoder, &qei.event, qei.context);
|
||||
}
|
||||
|
||||
static void qei_dblclk_event (void *data)
|
||||
{
|
||||
qei.event.dbl_click = On;
|
||||
qei.on_event(&qei.encoder, &qei.event, qei.context);
|
||||
}
|
||||
|
||||
static void qei_reset (encoder_t *encoder)
|
||||
{
|
||||
qei.vel_timeout = 0;
|
||||
qei.dir = QEI_DirUnknown;
|
||||
qei.data.position = qei.vel_count = 0;
|
||||
qei.vel_timestamp = hal.get_elapsed_ticks();
|
||||
qei.vel_timeout = qei.settings.vel_timeout;
|
||||
}
|
||||
|
||||
static bool qei_configure (encoder_t *encoder, encoder_cfg_t *settings)
|
||||
{
|
||||
if(qei.vel_timeout != settings->vel_timeout)
|
||||
qei.vel_timestamp = hal.get_elapsed_ticks();
|
||||
|
||||
memcpy(&qei.settings, settings, sizeof(encoder_cfg_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static encoder_data_t *qei_get_data (encoder_t *encoder)
|
||||
{
|
||||
return &qei.data;
|
||||
}
|
||||
|
||||
static void qei_poll (void *data)
|
||||
{
|
||||
if(qei.vel_timeout && !(--qei.vel_timeout)) {
|
||||
|
||||
uint32_t time = hal.get_elapsed_ticks();
|
||||
|
||||
qei.data.velocity = abs(qei.data.position - qei.vel_count) * 1000 / (time - qei.vel_timestamp);
|
||||
qei.vel_timestamp = time;
|
||||
qei.vel_timeout = qei.settings.vel_timeout;
|
||||
if((qei.event.position_changed = !qei.dbl_click_timeout || qei.data.velocity == 0))
|
||||
qei.on_event(&qei.encoder, &qei.event, qei.context);
|
||||
qei.vel_count = qei.data.position;
|
||||
}
|
||||
|
||||
if(qei.dbl_click_timeout && !(--qei.dbl_click_timeout)) {
|
||||
qei.event.click = On;
|
||||
task_delete(qei_dblclk_event, NULL);
|
||||
qei.on_event(&qei.encoder, &qei.event, qei.context);
|
||||
}
|
||||
}
|
||||
|
||||
static void qei_ab_irq (uint8_t port, bool high)
|
||||
{
|
||||
const uint8_t encoder_valid_state[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};
|
||||
|
||||
static qei_state_t state = {0};
|
||||
|
||||
if(port == qei.port_a)
|
||||
state.a = high;
|
||||
else
|
||||
state.b = high;
|
||||
|
||||
uint_fast8_t idx = (((qei.state << 2) & 0x0F) | state.pins);
|
||||
|
||||
if(encoder_valid_state[idx] ) {
|
||||
|
||||
// int32_t count = qei.count;
|
||||
|
||||
qei.state = ((qei.state << 4) | idx) & 0xFF;
|
||||
|
||||
if(qei.state == 0x42 || qei.state == 0xD4 || qei.state == 0x2B || qei.state == 0xBD) {
|
||||
qei.data.position--;
|
||||
if(qei.vel_timeout == 0 || qei.dir == QEI_DirCW) {
|
||||
qei.dir = QEI_DirCCW;
|
||||
qei.event.position_changed = On;
|
||||
task_add_immediate(qei_post_event, NULL);
|
||||
}
|
||||
} else if(qei.state == 0x81 || qei.state == 0x17 || qei.state == 0xE8 || qei.state == 0x7E) {
|
||||
qei.data.position++;
|
||||
if(qei.vel_timeout == 0 || qei.dir == QEI_DirCCW) {
|
||||
qei.dir = QEI_DirCW;
|
||||
qei.event.position_changed = On;
|
||||
task_add_immediate(qei_post_event, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void qei_select_irq (uint8_t port, bool high)
|
||||
{
|
||||
if(high)
|
||||
return;
|
||||
|
||||
if(!qei.dbl_click_timeout) {
|
||||
qei.dbl_click_timeout = qei.settings.dbl_click_window;
|
||||
} else if(qei.dbl_click_timeout < qei.settings.dbl_click_window) {
|
||||
qei.dbl_click_timeout = 0;
|
||||
task_delete(qei_dblclk_event, NULL);
|
||||
task_add_immediate(qei_dblclk_event, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static bool qei_claim (encoder_on_event_ptr event_handler, void *context)
|
||||
{
|
||||
if(event_handler == NULL || qei.on_event)
|
||||
return false;
|
||||
|
||||
qei.context = context;
|
||||
qei.on_event = event_handler;
|
||||
qei.encoder.reset = qei_reset;
|
||||
qei.encoder.get_data = qei_get_data;
|
||||
qei.encoder.configure = qei_configure;
|
||||
|
||||
if(qei.port_b != IOPORT_UNASSIGNED) {
|
||||
ioport_enable_irq(qei.port_a, IRQ_Mode_Change, qei_ab_irq);
|
||||
ioport_enable_irq(qei.port_b, IRQ_Mode_Change, qei_ab_irq);
|
||||
}
|
||||
|
||||
if(qei.port_select != IOPORT_UNASSIGNED)
|
||||
ioport_enable_irq(qei.port_select, IRQ_Mode_Change, qei_select_irq);
|
||||
|
||||
task_add_systick(qei_poll, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void encoder_pin_claimed (uint8_t port, xbar_t *pin)
|
||||
{
|
||||
switch(pin->function) {
|
||||
|
||||
case Input_QEI_A:
|
||||
qei.port_a = port;
|
||||
break;
|
||||
|
||||
case Input_QEI_B:
|
||||
qei.port_b = port;
|
||||
qei.encoder.claim = qei_claim;
|
||||
if(qei.port_a != IOPORT_UNASSIGNED)
|
||||
encoder_register(&qei.encoder);
|
||||
break;
|
||||
|
||||
case Input_QEI_Select:
|
||||
qei.port_select = port;
|
||||
qei.encoder.caps.select = On;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
6
errors.c
6
errors.c
@@ -3,7 +3,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2017-2025 Terje Io
|
||||
Copyright (c) 2017-2026 Terje Io
|
||||
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
|
||||
@@ -107,7 +107,9 @@ PROGMEM static const status_detail_t status_detail[] = {
|
||||
#endif
|
||||
{ Status_FileOpenFailed, "Could not open file." },
|
||||
{ Status_UserException, "User defined error occured." },
|
||||
{ Status_AuxiliaryPortUnusable, "Port is not usable." }
|
||||
{ Status_AuxiliaryPortUnusable, "Port is not usable." },
|
||||
{ Status_ToolInSPindle, "Tool in spindle." },
|
||||
{ Status_NoToolInSPindle, "No tool in spindle." }
|
||||
};
|
||||
|
||||
static error_details_t details = {
|
||||
|
||||
6
errors.h
6
errors.h
@@ -3,7 +3,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2017-2025 Terje Io
|
||||
Copyright (c) 2017-2026 Terje Io
|
||||
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
|
||||
@@ -119,7 +119,9 @@ typedef enum {
|
||||
Status_FileOpenFailed = 84,
|
||||
Status_FsFormatFailed = 85,
|
||||
Status_AuxiliaryPortUnusable = 86,
|
||||
Status_StatusMax = Status_AuxiliaryPortUnusable,
|
||||
Status_ToolInSPindle = 87,
|
||||
Status_NoToolInSPindle = 88,
|
||||
Status_StatusMax = Status_NoToolInSPindle,
|
||||
Status_UserException = 253,
|
||||
Status_Handled, // For internal use only
|
||||
Status_Unhandled // For internal use only
|
||||
|
||||
28
gcode.h
28
gcode.h
@@ -51,27 +51,36 @@ Do not alter values!
|
||||
typedef enum {
|
||||
NonModal_NoAction = 0, //!< 0 - Default, must be zero
|
||||
NonModal_Dwell = 4, //!< 4 - G4
|
||||
NonModal_SetCoordinateData = 10, //!< 10 - G10
|
||||
NonModal_Settings = 10, //!< 10 - G10
|
||||
NonModal_GoHome_0 = 28, //!< 28 - G28
|
||||
NonModal_SetHome_0 = 38, //!< 38 - G28.1
|
||||
NonModal_GoHome_1 = 30, //!< 30 - G30
|
||||
NonModal_SetHome_1 = 40, //!< 40 - G30.1
|
||||
NonModal_AbsoluteOverride = 53, //!< 53 - G53
|
||||
NonModal_MacroCall = 65, //!< 65 - G65
|
||||
Modal_MacroCall = 66, //!< 66 - G66
|
||||
Modal_MacroEnd = 67, //!< 67 - G67
|
||||
NonModal_SetCoordinateOffset = 92, //!< 92 - G92
|
||||
NonModal_MacroCall2 = 98, //!< 98 - M98
|
||||
NonModal_ResetCoordinateOffset = 102, //!< 102 - G92.1
|
||||
NonModal_ClearCoordinateOffset = 112, //!< 112 - G92.2
|
||||
#if ENABLE_ACCELERATION_PROFILES
|
||||
NonModal_RestoreCoordinateOffset = 122, //!< 122 - G92.3
|
||||
NonModal_SetAccelerationProfile = 187 //!< 187 - G187
|
||||
#else
|
||||
NonModal_RestoreCoordinateOffset = 122 //!< 122 - G92.3
|
||||
NonModal_RestoreCoordinateOffset = 122 //!< 122 - G92.3
|
||||
#endif
|
||||
} non_modal_t;
|
||||
|
||||
typedef enum {
|
||||
ToolAction_None = 0, //!< 0 - Default, must be zero
|
||||
ToolAction_Change = 6, //!< 6 - M6
|
||||
ToolAction_Set = 61, //!< 61 - M61
|
||||
} tool_action_t;
|
||||
|
||||
typedef enum {
|
||||
MacroCall_End = 0, //!< 0 - Default, must be zero (G67)
|
||||
MacroCall_NonModal = 65, //!< 65 - G65
|
||||
MacroCall_Modal = 66, //!< 66 - G66
|
||||
MacroCall_Modal1 = 166, //!< 166 - G66.1
|
||||
MacroCall_NonModal98 = 98, //!< 98 - M98
|
||||
} macro_call_t;
|
||||
|
||||
typedef enum {
|
||||
ModalState_NoAction = 0, //!< 0 - Default, must be zero
|
||||
@@ -602,6 +611,7 @@ typedef struct {
|
||||
|
||||
typedef struct g66_arguments
|
||||
{
|
||||
macro_call_t call;
|
||||
uint32_t call_level;
|
||||
gc_values_t values;
|
||||
parameter_words_t words;
|
||||
@@ -624,10 +634,10 @@ typedef struct {
|
||||
float path_tolerance; //!< Path blending tolerance
|
||||
float cam_tolerance; //!< Naive CAM tolerance
|
||||
#endif
|
||||
uint32_t line_number; //!< Last line number sent
|
||||
uint32_t line_number; //!< Last line number sent
|
||||
tool_id_t tool_pending; //!< Tool to be selected on next M6
|
||||
#if NGC_EXPRESSIONS_ENABLE
|
||||
uint32_t g43_pending; //!< Tool offset to be selected on next M6, for macro ATC
|
||||
tool_id_t g43_pending; //!< Tool offset to be selected on next M6, for macro ATC
|
||||
#endif
|
||||
bool file_run; //!< Tracks % command
|
||||
bool file_stream; //!< Tracks streaming from file
|
||||
@@ -663,6 +673,7 @@ It will also be passed to mc_jog_execute() and any user M-code validation and ex
|
||||
*/
|
||||
typedef struct {
|
||||
non_modal_t non_modal_command; //!< Non modal command
|
||||
tool_action_t tool_action; //!< Non modal tool change
|
||||
override_mode_t override_command; //!< Override command TODO: add to non_modal above?
|
||||
user_mcode_t user_mcode; //!< Set > 0 if a user M-code is found.
|
||||
bool user_mcode_sync; //!< Set to \a true by M-code validation handler if M-code is to be executed after synchronization.
|
||||
@@ -674,6 +685,7 @@ typedef struct {
|
||||
uint32_t arc_turns; //
|
||||
parameter_words_t g65_words; //!< Parameter words to pass to G65 macro.
|
||||
#if NGC_PARAMETERS_ENABLE
|
||||
macro_call_t macro_call;
|
||||
modal_state_action_t state_action; //!< M70-M73 modal state action
|
||||
#endif
|
||||
#if N_AXIS > 3
|
||||
|
||||
2
grbl.h
2
grbl.h
@@ -42,7 +42,7 @@
|
||||
#else
|
||||
#define GRBL_VERSION "1.1f"
|
||||
#endif
|
||||
#define GRBL_BUILD 20260218
|
||||
#define GRBL_BUILD 20260225
|
||||
|
||||
#define GRBL_URL "https://github.com/grblHAL"
|
||||
|
||||
|
||||
29
hal.h
29
hal.h
@@ -453,32 +453,6 @@ typedef struct {
|
||||
atc_get_state_ptr atc_get_state; //!< Optional handler for checking ATC status.
|
||||
} tool_ptrs_t;
|
||||
|
||||
/*******************
|
||||
* Encoder input *
|
||||
*******************/
|
||||
|
||||
/*! \brief Pointer to function for getting number of encoders supported.
|
||||
\returns number of encoders.
|
||||
*/
|
||||
typedef uint8_t (*encoder_get_n_encoders_ptr)(void);
|
||||
|
||||
/*! \brief Pointer to callback function to receive encoder events.
|
||||
\param encoder pointer to a \a encoder_t struct.
|
||||
\param position encoder position.
|
||||
*/
|
||||
typedef void (*encoder_on_event_ptr)(encoder_t *encoder, int32_t position);
|
||||
|
||||
/*! \brief Pointer to function for resetting encoder data.
|
||||
\param id encoder id.
|
||||
*/
|
||||
typedef void (*encoder_reset_ptr)(uint_fast8_t id);
|
||||
|
||||
typedef struct {
|
||||
encoder_get_n_encoders_ptr get_n_encoders; //!< Optional handler for getting number of encoders supported.
|
||||
encoder_on_event_ptr on_event; //!< Optional callback handler for receiving encoder events.
|
||||
encoder_reset_ptr reset; //!< Optional handler for resetting data for an encoder.
|
||||
} encoder_ptrs_t;
|
||||
|
||||
/*! \brief Pointer to callback function to receive spindle encoder index events.
|
||||
\param count index pulse count.
|
||||
*/
|
||||
@@ -513,7 +487,7 @@ typedef union {
|
||||
comp1 :1, //!< Timer supports compare interrupt 0
|
||||
comp2 :1, //!< Timer supports compare interrupt 1
|
||||
ext_clk :1, //!< External clock supported
|
||||
encoder :1, //!< Emcode mode supported
|
||||
encoder :1, //!< Encode mode supported
|
||||
unused :2;
|
||||
};
|
||||
} timer_cap_t;
|
||||
@@ -691,7 +665,6 @@ typedef struct {
|
||||
pallet_shuttle_ptr pallet_shuttle; //!< Optional handler for performing a pallet shuttle on program end (M60).
|
||||
void (*reboot)(void); //!< Optoional handler for rebooting the controller. This will be called when #ASCII_ESC followed by #CMD_REBOOT is received.
|
||||
|
||||
encoder_ptrs_t encoder; //!< Optional handlers for encoder support.
|
||||
spindle_encoder_on_index_ptr spindle_encoder_on_index; //!< Optional handler (callback) to receive spindle encoder index event.
|
||||
|
||||
/*! \brief Optional handler for getting the current axis positions.
|
||||
|
||||
10
modbus.c
10
modbus.c
@@ -223,14 +223,14 @@ FLASHMEM static void rx_packet (modbus_message_t *msg)
|
||||
break;
|
||||
case ModBus_ReadCoils:
|
||||
case ModBus_ReadDiscreteInputs:
|
||||
response.num_values = min(msg->adu[2], 3); // byte count, not /2
|
||||
response.num_values = min(msg->adu[2], MODBUS_MAX_REGISTERS); // byte count
|
||||
for(idx = 0; idx < response.num_values; idx++)
|
||||
response.values[idx] = msg->adu[3 + idx]; // single bytes, not u16
|
||||
response.values[idx] = msg->adu[3 + idx];
|
||||
break;
|
||||
|
||||
default:;
|
||||
response.num_values = cmds[response.function].single_register || cmds[response.function].is_write ? 2 : msg->adu[2] / 2;
|
||||
response.num_values = min(response.num_values, 3);
|
||||
response.num_values = min(response.num_values, MODBUS_MAX_REGISTERS);
|
||||
uint_fast8_t pos = cmds[response.function].single_register || cmds[response.function].is_write ? 2 : 3;
|
||||
for(idx = 0; idx < response.num_values; idx++)
|
||||
response.values[idx] = modbus_read_u16(&msg->adu[pos + (idx << 1)]);
|
||||
@@ -303,9 +303,9 @@ FLASHMEM status_code_t modbus_message (uint8_t server, modbus_function_t functio
|
||||
cmd.adu[5] = (uint8_t)(registers & 0xFF);
|
||||
|
||||
if(function == ModBus_ReadCoils || function == ModBus_ReadDiscreteInputs)
|
||||
cmd.rx_length = 5 + ((registers + 7) / 8); // bit-packed, ceil(n/8) bytes
|
||||
cmd.rx_length = 5 + ((registers + 7) / 8); // bit-packed, ceil(n/8) bytes
|
||||
else
|
||||
cmd.rx_length = 5 + (registers << 1); // 2 bytes per register
|
||||
cmd.rx_length = 5 + (registers << 1); // 2 bytes per register
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2021-2025 Terje Io
|
||||
Copyright (c) 2021-2026 Terje Io
|
||||
|
||||
grblHAL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -49,7 +49,7 @@
|
||||
#error "MPG mode input is not supported in this configuration!"
|
||||
#endif
|
||||
|
||||
#if QEI_SELECT_ENABLE && !defined(QEI_SELECT_PIN)
|
||||
#if (ENCODER_ENABLE & 1) && !defined(QEI_SELECT_PIN)
|
||||
#error "Encoder select input is not supported in this configuration!"
|
||||
#endif
|
||||
|
||||
@@ -157,12 +157,16 @@ static aux_ctrl_t aux_ctrl[] = {
|
||||
#if MPG_ENABLE == 1 && defined(MPG_MODE_PIN)
|
||||
add_aux_input(Input_MPGSelect, MPG_MODE, IRQ_Mode_Change, 0)
|
||||
#endif
|
||||
#if QEI_SELECT_ENABLE && defined(QEI_SELECT_PIN)
|
||||
#if QEI_ENABLE && defined(QEI_A_PIN) && defined(QEI_B_PIN)
|
||||
add_aux_input(Input_QEI_A, QEI_A, IRQ_Mode_Change, 0)
|
||||
add_aux_input(Input_QEI_B, QEI_B, IRQ_Mode_Change, 0)
|
||||
#endif
|
||||
#if (ENCODER_ENABLE & 1) && defined(QEI_SELECT_PIN)
|
||||
add_aux_input(Input_QEI_Select, QEI_SELECT, IRQ_Mode_RisingFalling, 0)
|
||||
#endif
|
||||
// Probe pins can be bound explicitly and can be "degraded" to not interrupt capable.
|
||||
#if PROBE_ENABLE && defined(PROBE_PIN)
|
||||
add_aux_input(Input_Probe, PROBE, IRQ_Mode_RisingFalling, 0)
|
||||
add_aux_input(Input_Probe, PROBE, IRQ_Mode_Change, 0)
|
||||
#endif
|
||||
#if PROBE2_ENABLE && defined(PROBE2_PIN)
|
||||
add_aux_input(Input_Probe2, PROBE2, IRQ_Mode_RisingFalling, 0)
|
||||
@@ -198,11 +202,19 @@ static aux_ctrl_t aux_ctrl[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// General inputs
|
||||
|
||||
static inline bool aux_ctrl_is_probe (pin_function_t function)
|
||||
{
|
||||
return function == Input_Probe || function == Input_Probe2 || function == Input_Toolsetter;
|
||||
}
|
||||
|
||||
static inline bool aux_ctrl_is_encoder (pin_function_t function)
|
||||
{
|
||||
return function == Input_QEI_A || function == Input_QEI_B || function == Input_QEI_Select;
|
||||
}
|
||||
|
||||
#ifdef STM32_PLATFORM
|
||||
|
||||
static inline aux_ctrl_t *aux_ctrl_get_fn (aux_gpio_t gpio)
|
||||
@@ -228,11 +240,10 @@ static inline xbar_t *aux_ctrl_claim_port (aux_ctrl_t *aux_ctrl)
|
||||
|
||||
if(aux_ctrl) {
|
||||
if(aux_ctrl->port != IOPORT_UNASSIGNED && (pin = ioport_claim(Port_Digital, Port_Input, &aux_ctrl->port, NULL))) {
|
||||
|
||||
aux_ctrl->gpio.port = pin->port;
|
||||
aux_ctrl->gpio.pin = pin->pin;
|
||||
|
||||
ioport_set_function(pin, aux_ctrl->function, &aux_ctrl->signal);
|
||||
if(ioport_set_function(pin, aux_ctrl->function, &aux_ctrl->signal))
|
||||
pin->function = aux_ctrl->function;
|
||||
} else
|
||||
aux_ctrl->port = IOPORT_UNASSIGNED;
|
||||
}
|
||||
@@ -279,7 +290,7 @@ static inline void aux_ctrl_irq_enable (settings_t *settings, ioport_interrupt_c
|
||||
|
||||
if(idx) do {
|
||||
if(aux_ctrl[--idx].port != 0xFF && aux_ctrl[idx].irq_mode != IRQ_Mode_None) {
|
||||
if(!aux_ctrl_is_probe(aux_ctrl[idx].function)) {
|
||||
if(!(aux_ctrl_is_probe(aux_ctrl[idx].function) || aux_ctrl_is_encoder(aux_ctrl[idx].function))) {
|
||||
pin_irq_mode_t irq_mode;
|
||||
if((irq_mode = aux_ctrl[idx].irq_mode) & IRQ_Mode_RisingFalling)
|
||||
irq_mode = (settings->control_invert.mask & aux_ctrl[idx].signal.mask) ? IRQ_Mode_Falling : IRQ_Mode_Rising;
|
||||
@@ -560,7 +571,7 @@ static inline void aux_ctrl_claim_out_ports (aux_claim_explicit_out_ptr aux_clai
|
||||
#endif
|
||||
|
||||
// IRQ enabled input singnals
|
||||
|
||||
/*
|
||||
#if QEI_ENABLE
|
||||
#ifndef QEI_A_BIT
|
||||
#define QEI_A_BIT (1<<QEI_A_PIN)
|
||||
@@ -568,10 +579,10 @@ static inline void aux_ctrl_claim_out_ports (aux_claim_explicit_out_ptr aux_clai
|
||||
#ifndef QEI_B_BIT
|
||||
#define QEI_B_BIT (1<<QEI_B_PIN)
|
||||
#endif
|
||||
#else
|
||||
#else*/
|
||||
#define QEI_A_BIT 0
|
||||
#define QEI_B_BIT 0
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
#ifndef QEI_SELECT_BIT
|
||||
#define QEI_SELECT_BIT 0
|
||||
|
||||
54
planner.c
54
planner.c
@@ -252,6 +252,9 @@ FLASHMEM bool plan_reset (void)
|
||||
|
||||
memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
|
||||
|
||||
pl.override.feed_rate = sys.override.feed_rate;
|
||||
pl.override.rapid_rate = sys.override.rapid_rate;
|
||||
|
||||
plan_reset_buffer(&block_buffer, block_buffer.blocks[0].next == NULL);
|
||||
|
||||
return true;
|
||||
@@ -313,12 +316,12 @@ float plan_compute_profile_nominal_speed (plan_block_t *block)
|
||||
: block->programmed_rate;
|
||||
|
||||
if(block->condition.rapid_motion)
|
||||
nominal_speed *= (0.01f * (float)sys.override.rapid_rate);
|
||||
nominal_speed *= (0.01f * (float)pl.override.rapid_rate);
|
||||
else {
|
||||
if(sys.override.feed_rate != 100 && !block->condition.no_feed_override) {
|
||||
if(pl.override.feed_rate != 100 && !block->condition.no_feed_override) {
|
||||
if(nominal_speed > block->rapid_rate)
|
||||
nominal_speed = block->rapid_rate;
|
||||
nominal_speed *= (0.01f * (float)sys.override.feed_rate);
|
||||
nominal_speed *= (0.01f * (float)pl.override.feed_rate);
|
||||
}
|
||||
if(nominal_speed > block->rapid_rate)
|
||||
nominal_speed = block->rapid_rate;
|
||||
@@ -741,13 +744,29 @@ void plan_sync_velocity (void *block)
|
||||
}
|
||||
|
||||
// Set feed overrides
|
||||
FLASHMEM static void _plan_feed_override (override_t feed_rate, override_t rapid_rate)
|
||||
{
|
||||
bool feedrate_changed, rapidrate_changed = false;
|
||||
|
||||
if(((feedrate_changed = feed_rate != pl.override.feed_rate) || (rapidrate_changed = rapid_rate != pl.override.rapid_rate))) {
|
||||
|
||||
pl.override.feed_rate = feed_rate;
|
||||
pl.override.rapid_rate = rapid_rate;
|
||||
|
||||
if(plan_update_velocity_profile_parameters())
|
||||
plan_cycle_reinitialize();
|
||||
|
||||
if(grbl.on_override_changed) {
|
||||
if(feedrate_changed)
|
||||
grbl.on_override_changed(OverrideChanged_FeedRate);
|
||||
if(rapidrate_changed)
|
||||
grbl.on_override_changed(OverrideChanged_RapidRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FLASHMEM void plan_feed_override (override_t feed_override, override_t rapid_override)
|
||||
{
|
||||
bool feedrate_changed = false, rapidrate_changed = false;
|
||||
|
||||
if(sys.override.control.feed_rates_disable)
|
||||
return;
|
||||
|
||||
if(feed_override == 0)
|
||||
feed_override = sys.override.feed_rate;
|
||||
else
|
||||
@@ -758,20 +777,18 @@ FLASHMEM void plan_feed_override (override_t feed_override, override_t rapid_ove
|
||||
else
|
||||
rapid_override = constrain(rapid_override, 5, 100);
|
||||
|
||||
if((feedrate_changed = feed_override != sys.override.feed_rate) ||
|
||||
(rapidrate_changed = rapid_override != sys.override.rapid_rate)) {
|
||||
if(feed_override != sys.override.feed_rate || rapid_override != sys.override.rapid_rate) {
|
||||
|
||||
sys.override.feed_rate = feed_override;
|
||||
sys.override.rapid_rate = rapid_override;
|
||||
|
||||
report_add_realtime(Report_Overrides); // Set to report change immediately
|
||||
if(plan_update_velocity_profile_parameters())
|
||||
plan_cycle_reinitialize();
|
||||
if(grbl.on_override_changed) {
|
||||
if(feedrate_changed)
|
||||
grbl.on_override_changed(OverrideChanged_FeedRate);
|
||||
if(rapidrate_changed)
|
||||
grbl.on_override_changed(OverrideChanged_RapidRate);
|
||||
}
|
||||
}
|
||||
|
||||
if(sys.override.control.feed_rates_disable)
|
||||
_plan_feed_override(DEFAULT_FEED_OVERRIDE, DEFAULT_RAPID_OVERRIDE);
|
||||
else
|
||||
_plan_feed_override(sys.override.feed_rate, sys.override.rapid_rate);
|
||||
}
|
||||
|
||||
FLASHMEM void plan_data_init (plan_line_data_t *plan_data)
|
||||
@@ -779,6 +796,7 @@ FLASHMEM void plan_data_init (plan_line_data_t *plan_data)
|
||||
memset(plan_data, 0, sizeof(plan_line_data_t));
|
||||
plan_data->offset_id = gc_state.offset_id;
|
||||
plan_data->spindle.hal = gc_spindle_get(-1)->hal;
|
||||
plan_data->condition.no_feed_override = sys.override.control.feed_rates_disable;
|
||||
plan_data->condition.target_validated = plan_data->condition.target_valid = sys.soft_limits.mask == 0;
|
||||
#ifdef KINEMATICS_API
|
||||
plan_data->rate_multiplier = 1.0f;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2019-2025 Terje Io
|
||||
Copyright (c) 2019-2026 Terje Io
|
||||
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
|
||||
@@ -162,6 +162,10 @@ typedef struct {
|
||||
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
|
||||
float previous_nominal_speed; // Nominal speed of previous path line segment
|
||||
float actual_rpm; // Updated when in units per revolution blocks (G95) mode.
|
||||
struct {
|
||||
override_t feed_rate; //!< Feed rate override value in percent
|
||||
override_t rapid_rate; //!< Rapids override value in percent
|
||||
} override;
|
||||
} planner_t;
|
||||
|
||||
// Initialize and reset the motion plan subsystem
|
||||
|
||||
61
plugins.h
61
plugins.h
@@ -7,7 +7,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2020-2025 Terje Io
|
||||
Copyright (c) 2020-2026 Terje Io
|
||||
|
||||
grblHAL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -154,25 +154,7 @@ typedef struct {
|
||||
uint8_t id;
|
||||
} modbus_tcp_settings_t;
|
||||
|
||||
// Quadrature encoder interface
|
||||
|
||||
typedef enum {
|
||||
Encoder_Universal = 0,
|
||||
Encoder_FeedRate,
|
||||
Encoder_RapidRate,
|
||||
Encoder_Spindle_RPM,
|
||||
Encoder_MPG,
|
||||
Encoder_MPG_X,
|
||||
Encoder_MPG_Y,
|
||||
Encoder_MPG_Z,
|
||||
Encoder_MPG_A,
|
||||
Encoder_MPG_B,
|
||||
Encoder_MPG_C,
|
||||
Encoder_MPG_U,
|
||||
Encoder_MPG_V,
|
||||
Encoder_MPG_W,
|
||||
Encoder_Spindle_Position
|
||||
} encoder_mode_t;
|
||||
// Encoder settings offsets
|
||||
|
||||
typedef enum {
|
||||
Setting_EncoderMode = 0,
|
||||
@@ -181,45 +163,6 @@ typedef enum {
|
||||
Setting_EncoderDblClickWindow = 3 // ms
|
||||
} encoder_setting_id_t;
|
||||
|
||||
typedef union {
|
||||
uint8_t events;
|
||||
struct {
|
||||
uint8_t position_changed :1,
|
||||
direction_changed :1,
|
||||
click :1,
|
||||
dbl_click :1,
|
||||
long_click :1,
|
||||
index_pulse :1,
|
||||
unused :2;
|
||||
};
|
||||
} encoder_event_t;
|
||||
|
||||
typedef union {
|
||||
uint8_t flags;
|
||||
uint8_t value;
|
||||
struct {
|
||||
uint8_t single_count_per_detent :1;
|
||||
};
|
||||
} encoder_flags_t;
|
||||
|
||||
typedef struct {
|
||||
encoder_mode_t mode;
|
||||
uint32_t cpr; //!< Count per revolution.
|
||||
uint32_t cpd; //!< Count per detent.
|
||||
uint32_t dbl_click_window; //!< ms.
|
||||
encoder_flags_t flags;
|
||||
} encoder_settings_t;
|
||||
|
||||
typedef struct {
|
||||
encoder_mode_t mode;
|
||||
uint_fast8_t id;
|
||||
uint_fast8_t axis; //!< Axis index for MPG encoders, 0xFF for others.
|
||||
int32_t position;
|
||||
uint32_t velocity;
|
||||
encoder_event_t event;
|
||||
encoder_settings_t *settings;
|
||||
} encoder_t;
|
||||
|
||||
// DISPLAYS:
|
||||
|
||||
// Interfaces
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2021-2025 Terje Io
|
||||
Copyright (c) 2021-2026 Terje Io
|
||||
|
||||
grblHAL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -181,6 +181,11 @@
|
||||
homing_pulloff_init();
|
||||
#endif
|
||||
|
||||
#if ENCODER_ENABLE
|
||||
extern bool encoder_init (void);
|
||||
encoder_init();
|
||||
#endif
|
||||
|
||||
extern void my_plugin_init (void);
|
||||
my_plugin_init();
|
||||
|
||||
|
||||
52
settings.c
52
settings.c
@@ -29,10 +29,12 @@
|
||||
|
||||
#include "hal.h"
|
||||
#include "config.h"
|
||||
#include "encoders.h"
|
||||
#include "machine_limits.h"
|
||||
#include "nvs_buffer.h"
|
||||
#include "tool_change.h"
|
||||
#include "state_machine.h"
|
||||
#include "strutils.h"
|
||||
#if ENABLE_BACKLASH_COMPENSATION
|
||||
#include "motion_control.h"
|
||||
#endif
|
||||
@@ -738,17 +740,26 @@ static status_code_t set_pwm_mode (setting_id_t id, uint_fast16_t int_value)
|
||||
static status_code_t set_pwm_options (setting_id_t id, uint_fast16_t int_value)
|
||||
{
|
||||
if(int_value & 0b0001) {
|
||||
if(int_value > 0b1111)
|
||||
#if N_SPINDLE > 1
|
||||
if(int_value > 0b11111)
|
||||
return Status_SettingValueOutOfRange;
|
||||
#else
|
||||
if(int_value > 0b01111)
|
||||
return Status_SettingValueOutOfRange;
|
||||
#endif
|
||||
settings.pwm_spindle.flags.pwm_disable = Off;
|
||||
settings.pwm_spindle.flags.enable_rpm_controlled = !!(int_value & 0b0010);
|
||||
settings.pwm_spindle.flags.laser_mode_disable = !!(int_value & 0b0100);
|
||||
settings.pwm_spindle.flags.pwm_ramped = !!(int_value & 0b1000);
|
||||
settings.pwm_spindle.flags.enable_rpm_controlled = !!(int_value & 0b00010);
|
||||
settings.pwm_spindle.flags.laser_mode_disable = !!(int_value & 0b00100);
|
||||
settings.pwm_spindle.flags.pwm_ramped = !!(int_value & 0b01000);
|
||||
#if N_SPINDLE > 1
|
||||
settings.pwm_spindle.flags.ignore_delays = !!(int_value & 0b10000);
|
||||
#endif
|
||||
} else {
|
||||
settings.pwm_spindle.flags.pwm_disable = On;
|
||||
settings.pwm_spindle.flags.enable_rpm_controlled =
|
||||
settings.pwm_spindle.flags.laser_mode_disable =
|
||||
settings.pwm_spindle.flags.pwm_ramped = Off;
|
||||
settings.pwm_spindle.flags.pwm_ramped =
|
||||
settings.pwm_spindle.flags.ignore_delays = Off;
|
||||
}
|
||||
|
||||
return Status_OK;
|
||||
@@ -1520,9 +1531,10 @@ FLASHMEM static uint32_t get_int (setting_id_t id)
|
||||
value = settings.pwm_spindle.flags.pwm_disable
|
||||
? 0
|
||||
: (0b0001 |
|
||||
(settings.pwm_spindle.flags.enable_rpm_controlled ? 0b0010 : 0) |
|
||||
(settings.pwm_spindle.flags.laser_mode_disable ? 0b0100 : 0) |
|
||||
(settings.pwm_spindle.flags.pwm_ramped ? 0b1000 : 0));
|
||||
(settings.pwm_spindle.flags.enable_rpm_controlled ? 0b00010 : 0) |
|
||||
(settings.pwm_spindle.flags.laser_mode_disable ? 0b00100 : 0) |
|
||||
(settings.pwm_spindle.flags.pwm_ramped ? 0b01000 : 0) |
|
||||
(settings.pwm_spindle.flags.ignore_delays ? 0b10000 : 0));
|
||||
break;
|
||||
|
||||
case Setting_Mode:
|
||||
@@ -2068,7 +2080,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
|
||||
{ Setting_InvertProbePin, Group_Probing, "Invert probe inputs", NULL, Format_Bitfield, probe_signals, NULL, NULL, Setting_IsLegacyFn, set_probe_invert, get_int, is_setting_available },
|
||||
{ Setting_SpindlePWMBehaviour, Group_Spindle, "Deprecated", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_pwm_mode, get_int, is_setting_available },
|
||||
{ Setting_GangedDirInvertMask, Group_Stepper, "Ganged axes direction invert", NULL, Format_Bitfield, ganged_axes, NULL, NULL, Setting_IsExtendedFn, set_ganged_dir_invert, get_int, is_setting_available },
|
||||
{ Setting_SpindlePWMOptions, Group_Spindle, "PWM spindle options", NULL, Format_XBitfield, "Enable,RPM controls spindle enable signal,Disable laser mode capability,Enable ramping", NULL, NULL, Setting_IsExtendedFn, set_pwm_options, get_int, is_setting_available },
|
||||
{ Setting_SpindlePWMOptions, Group_Spindle, "PWM spindle options", NULL, Format_XBitfield, "Enable,RPM controls spindle enable signal,Disable laser mode capability,Enable ramping" PWM_SPINDLE_NO_DELAYS, NULL, NULL, Setting_IsExtendedFn, set_pwm_options, get_int, is_setting_available },
|
||||
#if COMPATIBILITY_LEVEL <= 1
|
||||
{ Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state,Line numbers,Feed & speed,Pin state,Work coordinate offset,Overrides,Probe coordinates,Buffer sync on WCO change,Parser state,Alarm substatus,Run substatus,Enable when homing,Distance-to-go", NULL, NULL, Setting_IsExtendedFn, set_report_mask, get_int, NULL },
|
||||
#else
|
||||
@@ -2685,6 +2697,10 @@ FLASHMEM static void sanity_check (void)
|
||||
settings.steppers.rotary_wrap.mask &= settings.steppers.is_rotary.mask;
|
||||
#endif
|
||||
|
||||
#if N_SPINDLE == 1
|
||||
settings.pwm_spindle.flags.ignore_delays = Off;
|
||||
#endif
|
||||
|
||||
settings.control_invert.mask |= limits_override.mask;
|
||||
settings.control_disable_pullup.mask &= ~limits_override.mask;
|
||||
}
|
||||
@@ -2906,7 +2922,7 @@ static inline const setting_detail_t *_setting_get_details (setting_id_t id, uin
|
||||
if(details->settings[idx].group == Group_Axis0 && grbl.on_set_axis_setting_unit)
|
||||
set_axis_unit(&details->settings[idx], grbl.on_set_axis_setting_unit(details->settings[idx].id, offset));
|
||||
|
||||
if(offset && details->iterator == NULL && offset >= (details->settings[idx].group == Group_Encoder0 ? hal.encoder.get_n_encoders() : N_AXIS))
|
||||
if(offset && details->iterator == NULL && offset >= (details->settings[idx].group == Group_Encoder0 ? encoders_get_count() : N_AXIS))
|
||||
return NULL;
|
||||
|
||||
if(set)
|
||||
@@ -3062,22 +3078,6 @@ FLASHMEM static status_code_t validate_uint_value (const setting_detail_t *setti
|
||||
return Status_OK;
|
||||
}
|
||||
|
||||
FLASHMEM static uint32_t strnumentries (const char *s, const char delimiter)
|
||||
{
|
||||
if(s == NULL || *s == '\0')
|
||||
return 0;
|
||||
|
||||
char *p = (char *)s;
|
||||
uint32_t entries = 1;
|
||||
|
||||
while((p = strchr(p, delimiter))) {
|
||||
p++;
|
||||
entries++;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
FLASHMEM setting_datatype_t setting_datatype_to_external (setting_datatype_t datatype)
|
||||
{
|
||||
switch(datatype) {
|
||||
|
||||
@@ -117,9 +117,14 @@ FLASHMEM static bool spindle_activate (spindle_id_t spindle_id, spindle_num_t sp
|
||||
sys_spindle[spindle_num].param.hal = &sys_spindle[spindle_num].hal;
|
||||
if(sys_spindle[spindle_num].param.override_pct == 0)
|
||||
sys_spindle[spindle_num].param.override_pct = DEFAULT_SPINDLE_RPM_OVERRIDE;
|
||||
if(spindle_hal.type == SpindleType_PWM && !spindle_hal.cap.laser && spindle_hal.context.pwm->flags.ramp_pwm ) {
|
||||
sys_spindle[spindle_num].param.ramp_up = settings.spindle.on_delay > 0;
|
||||
sys_spindle[spindle_num].param.ramp_down = settings.spindle.off_delay > 0;
|
||||
sys_spindle[spindle_num].param.option.ramp_up = sys_spindle[spindle_num].param.option.ramp_down = sys_spindle[spindle_num].param.option.ignore_delays = Off;
|
||||
if(spindle_hal.type == SpindleType_PWM) {
|
||||
if(!spindle_hal.cap.laser && spindle_hal.context.pwm->flags.ramp_pwm ) {
|
||||
sys_spindle[spindle_num].param.option.ramp_up = settings.spindle.on_delay > 0;
|
||||
sys_spindle[spindle_num].param.option.ramp_down = settings.spindle.off_delay > 0;
|
||||
}
|
||||
if(!sys_spindle[spindle_num].param.option.ramp_up)
|
||||
sys_spindle[spindle_num].param.option.ignore_delays = spindle_hal.context.pwm->settings->flags.ignore_delays;
|
||||
}
|
||||
spindle_hal.param = &sys_spindle[spindle_num].param;
|
||||
memcpy(&sys_spindle[spindle_num].hal, &spindle_hal, sizeof(spindle_ptrs_t));
|
||||
@@ -291,11 +296,12 @@ FLASHMEM void spindle_update_caps (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_c
|
||||
sys_spindle[idx].hal.rpm_max = spindle->rpm_max;
|
||||
sys_spindle[idx].hal.pwm_off_value = spindle->pwm_off_value;
|
||||
if((sys_spindle[idx].hal.cap.laser = spindle->cap.laser) || spindle->type != SpindleType_PWM || !spindle->context.pwm->flags.ramp_pwm)
|
||||
sys_spindle[idx].param.ramp_up = sys_spindle[idx].param.ramp_down = Off;
|
||||
sys_spindle[idx].param.option.ramp_up = sys_spindle[idx].param.option.ramp_down = Off;
|
||||
else {
|
||||
sys_spindle[idx].param.ramp_up = settings.spindle.on_delay > 0;
|
||||
sys_spindle[idx].param.ramp_down = settings.spindle.off_delay > 0;
|
||||
sys_spindle[idx].param.option.ramp_up = settings.spindle.on_delay > 0;
|
||||
sys_spindle[idx].param.option.ramp_down = settings.spindle.off_delay > 0;
|
||||
}
|
||||
sys_spindle[idx].param.option.ignore_delays = spindle->type == SpindleType_PWM && !sys_spindle[idx].param.option.ramp_up && spindle->context.pwm->settings->flags.ignore_delays;
|
||||
break;
|
||||
}
|
||||
} while(idx);
|
||||
@@ -609,37 +615,33 @@ __NOTE:__ Unlike motion overrides, spindle overrides do not require a planner re
|
||||
*/
|
||||
FLASHMEM float spindle_set_override (spindle_ptrs_t *spindle, override_t speed_override)
|
||||
{
|
||||
if(speed_override == DEFAULT_SPINDLE_RPM_OVERRIDE || !spindle->param->state.override_disable) {
|
||||
speed_override = speed_override == 0 ? spindle->param->override_pct : constrain(speed_override, MIN_SPINDLE_RPM_OVERRIDE, MAX_SPINDLE_RPM_OVERRIDE);
|
||||
|
||||
speed_override = constrain(speed_override, MIN_SPINDLE_RPM_OVERRIDE, MAX_SPINDLE_RPM_OVERRIDE);
|
||||
if((uint8_t)speed_override != spindle->param->override_pct || spindle->param->state.override_disable != spindle->param->option.override_disable) {
|
||||
|
||||
if((uint8_t)speed_override != spindle->param->override_pct) {
|
||||
float rpm = spindle->param->rpm_overridden;
|
||||
|
||||
float rpm = spindle->param->rpm_overridden;
|
||||
sys_state_t state = state_get();
|
||||
spindle_set_rpm(spindle, spindle->param->rpm, speed_override);
|
||||
|
||||
spindle_set_rpm(spindle, spindle->param->rpm, speed_override);
|
||||
|
||||
if(!(spindle->param->ramp_up &&
|
||||
spindle->get_state(spindle).on &&
|
||||
spindle_ramp_override(spindle, rpm, spindle->param->rpm_overridden))) {
|
||||
if(state == STATE_IDLE) {
|
||||
if(spindle->get_pwm && spindle->update_pwm)
|
||||
spindle->update_pwm(spindle, spindle->get_pwm(spindle, spindle->param->rpm_overridden));
|
||||
else if(spindle->update_rpm)
|
||||
spindle->update_rpm(spindle, spindle->param->rpm_overridden);
|
||||
} else
|
||||
sys.step_control.update_spindle_rpm = On;
|
||||
}
|
||||
|
||||
report_add_realtime(Report_Overrides); // Set to report change immediately
|
||||
|
||||
// if(grbl.on_spindle_programmed)
|
||||
// grbl.on_spindle_programmed(spindle, spindle->param->state, spindle->param->rpm, spindle->param->rpm_mode);
|
||||
|
||||
if(grbl.on_override_changed)
|
||||
grbl.on_override_changed(OverrideChanged_SpindleRPM);
|
||||
if(!(spindle->param->option.ramp_up &&
|
||||
spindle->get_state(spindle).on &&
|
||||
spindle_ramp_override(spindle, rpm, spindle->param->rpm_overridden))) {
|
||||
if(state_get() == STATE_IDLE) {
|
||||
if(spindle->get_pwm && spindle->update_pwm)
|
||||
spindle->update_pwm(spindle, spindle->get_pwm(spindle, spindle->param->rpm_overridden));
|
||||
else if(spindle->update_rpm)
|
||||
spindle->update_rpm(spindle, spindle->param->rpm_overridden);
|
||||
} else
|
||||
sys.step_control.update_spindle_rpm = On;
|
||||
}
|
||||
|
||||
report_add_realtime(Report_Overrides); // Set to report change immediately
|
||||
|
||||
// if(grbl.on_spindle_programmed)
|
||||
// grbl.on_spindle_programmed(spindle, spindle->param->state, spindle->param->rpm, spindle->param->rpm_mode);
|
||||
|
||||
if(grbl.on_override_changed)
|
||||
grbl.on_override_changed(OverrideChanged_SpindleRPM);
|
||||
}
|
||||
|
||||
return spindle->param->rpm_overridden;
|
||||
@@ -647,10 +649,8 @@ FLASHMEM float spindle_set_override (spindle_ptrs_t *spindle, override_t speed_o
|
||||
|
||||
FLASHMEM bool spindle_override_disable (spindle_ptrs_t *spindle, bool disable)
|
||||
{
|
||||
if(disable && !spindle->param->state.override_disable)
|
||||
spindle_set_override(spindle, DEFAULT_SPINDLE_RPM_OVERRIDE);
|
||||
|
||||
spindle->param->state.override_disable = disable;
|
||||
spindle->param->option.override_disable = disable;
|
||||
spindle_set_override(spindle, spindle->param->override_pct);
|
||||
|
||||
return disable;
|
||||
}
|
||||
@@ -723,7 +723,7 @@ FLASHMEM static bool _spindle_set_state (spindle_ptrs_t *spindle, spindle_state_
|
||||
|
||||
if(!state.on) { // Halt or set spindle direction and rpm.
|
||||
rpm = 0.0f;
|
||||
if(spindle->param->ramp_down)
|
||||
if(spindle->param->option.ramp_down)
|
||||
spindle_ramp(spindle, spindle->param->state, spindle->param->rpm_overridden, 0.0f, settings.spindle.off_delay);
|
||||
spindle->set_state(spindle, (spindle_state_t){0}, (spindle->param->rpm_overridden = 0.0f));
|
||||
} else {
|
||||
@@ -732,7 +732,7 @@ FLASHMEM static bool _spindle_set_state (spindle_ptrs_t *spindle, spindle_state_
|
||||
if(spindle->cap.laser && state.ccw)
|
||||
rpm = 0.0f; // TODO: May need to be rpm_min*(100/MAX_SPINDLE_RPM_OVERRIDE);
|
||||
|
||||
if(spindle->param->ramp_up) {
|
||||
if(spindle->param->option.ramp_up) {
|
||||
if(spindle->param->state.on && spindle->param->state.ccw != state.ccw) {
|
||||
spindle_ramp(spindle, spindle->param->state, spindle->param->rpm_overridden, 0.0f, settings.spindle.off_delay);
|
||||
spindle->param->rpm = spindle->param->rpm_overridden = 0.0f;
|
||||
@@ -775,7 +775,9 @@ FLASHMEM static bool spindle_set_state_wait (spindle_ptrs_t *spindle, spindle_st
|
||||
|
||||
if((ok = _spindle_set_state(spindle, state, rpm, delay_ms))) {
|
||||
|
||||
if(sys.override.control.spindle_wait_disable || (state.on ? spindle->param->ramp_up : spindle->param->ramp_down)) {
|
||||
if(sys.override.control.spindle_wait_disable ||
|
||||
spindle->param->option.ignore_delays ||
|
||||
(state.on ? spindle->param->option.ramp_up : spindle->param->option.ramp_down)) {
|
||||
sys.override.control.spindle_wait_disable = Off;
|
||||
} else {
|
||||
|
||||
@@ -859,15 +861,16 @@ FLASHMEM bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, f
|
||||
*/
|
||||
float spindle_set_rpm (spindle_ptrs_t *spindle, float rpm, override_t override_pct)
|
||||
{
|
||||
if(override_pct != 100)
|
||||
if(override_pct != 100 && !spindle->param->option.override_disable)
|
||||
rpm *= 0.01f * (float)override_pct; // Scale RPM by override value.
|
||||
|
||||
rpm = rpm <= 0.0f ? 0.0f : constrain(rpm, spindle->rpm_min, spindle->rpm_max);
|
||||
|
||||
spindle->param->rpm_overridden = rpm;
|
||||
spindle->param->override_pct = override_pct;
|
||||
spindle->param->state.override_disable = spindle->param->option.override_disable;
|
||||
|
||||
return spindle->param->rpm_overridden;
|
||||
return rpm;
|
||||
}
|
||||
|
||||
/*! \brief Turn off all enabled spindles.
|
||||
@@ -880,7 +883,7 @@ FLASHMEM void spindle_all_off (bool reset)
|
||||
do {
|
||||
if((spindle = spindle_get(--spindle_num))) {
|
||||
|
||||
if(!reset && spindle->param->ramp_down)
|
||||
if(!reset && spindle->param->option.ramp_down)
|
||||
spindle_set_state(spindle, (spindle_state_t){0}, 0.0f);
|
||||
|
||||
spindle->param->rpm = spindle->param->rpm_overridden = 0.0f;
|
||||
@@ -1118,17 +1121,26 @@ FLASHMEM static status_code_t set_spindle_invert (setting_id_t id, uint_fast16_t
|
||||
FLASHMEM static status_code_t set_pwm_options (setting_id_t id, uint_fast16_t int_value)
|
||||
{
|
||||
if(int_value & 0b0001) {
|
||||
if(int_value > 0b1111)
|
||||
#if N_SPINDLE > 1
|
||||
if(int_value > 0b11111)
|
||||
return Status_SettingValueOutOfRange;
|
||||
#else
|
||||
if(int_value > 0b01111)
|
||||
return Status_SettingValueOutOfRange;
|
||||
#endif
|
||||
sp1_settings.cfg.flags.pwm_disable = Off;
|
||||
sp1_settings.cfg.flags.enable_rpm_controlled = !!(int_value & 0b0010);
|
||||
sp1_settings.cfg.flags.laser_mode_disable = !!(int_value & 0b0100);
|
||||
sp1_settings.cfg.flags.pwm_ramped = !!(int_value & 0b1000);
|
||||
} else {
|
||||
sp1_settings.cfg.flags.enable_rpm_controlled = !!(int_value & 0b00010);
|
||||
sp1_settings.cfg.flags.laser_mode_disable = !!(int_value & 0b00100);
|
||||
sp1_settings.cfg.flags.pwm_ramped = !!(int_value & 0b01000);
|
||||
#if N_SPINDLE > 1
|
||||
sp1_settings.cfg.flags.ignore_delays = !!(int_value & 0b10000);
|
||||
#endif
|
||||
} else {
|
||||
sp1_settings.cfg.flags.pwm_disable = On;
|
||||
sp1_settings.cfg.flags.enable_rpm_controlled =
|
||||
sp1_settings.cfg.flags.laser_mode_disable =
|
||||
sp1_settings.cfg.flags.pwm_ramped = Off;
|
||||
sp1_settings.cfg.flags.pwm_ramped =
|
||||
sp1_settings.cfg.flags.ignore_delays = Off;
|
||||
}
|
||||
|
||||
return Status_OK;
|
||||
@@ -1144,9 +1156,10 @@ FLASHMEM static uint32_t get_int (setting_id_t id)
|
||||
value = sp1_settings.cfg.flags.pwm_disable
|
||||
? 0
|
||||
: (0b0001 |
|
||||
(sp1_settings.cfg.flags.enable_rpm_controlled ? 0b0010 : 0) |
|
||||
(sp1_settings.cfg.flags.laser_mode_disable ? 0b0100 : 0) |
|
||||
(sp1_settings.cfg.flags.pwm_ramped ? 0b1000 : 0));
|
||||
(sp1_settings.cfg.flags.enable_rpm_controlled ? 0b00010 : 0) |
|
||||
(sp1_settings.cfg.flags.laser_mode_disable ? 0b00100 : 0) |
|
||||
(sp1_settings.cfg.flags.pwm_ramped ? 0b01000 : 0) |
|
||||
(sp1_settings.cfg.flags.ignore_delays ? 0b10000 : 0));
|
||||
break;
|
||||
|
||||
case Setting_SpindleInvertMask1:
|
||||
@@ -1232,7 +1245,7 @@ PROGMEM static const setting_detail_t spindle1_settings[] = {
|
||||
{ Setting_Spindle_DirPort, Group_AuxPorts, "PWM2 spindle direction port", NULL, Format_Decimal, "-#0", "-1", max_dport, Setting_NonCoreFn, set_port, get_port, has_ports, { .reboot_required = On } },
|
||||
{ Setting_SpindleInvertMask1, Group_Spindle, "PWM2 spindle signals invert", NULL, Format_Bitfield, spindle_signals, NULL, NULL, Setting_IsExtendedFn, set_spindle_invert, get_int, NULL, { .reboot_required = On } },
|
||||
{ Setting_Spindle_PWMPort, Group_AuxPorts, "PWM2 spindle PWM port", NULL, Format_Decimal, "-#0", "0", max_aport, Setting_NonCoreFn, set_port, get_port, has_ports, { .reboot_required = On } },
|
||||
{ Setting_SpindlePWMOptions1, Group_Spindle, "PWM2 spindle options", NULL, Format_XBitfield, "Enable,RPM controls spindle enable signal,Disable laser mode capability,Enable ramping", NULL, NULL, Setting_IsExtendedFn, set_pwm_options, get_int, has_pwm },
|
||||
{ Setting_SpindlePWMOptions1, Group_Spindle, "PWM2 spindle options", NULL, Format_XBitfield, "Enable,RPM controls spindle enable signal,Disable laser mode capability,Enable ramping" PWM_SPINDLE_NO_DELAYS, NULL, NULL, Setting_IsExtendedFn, set_pwm_options, get_int, has_pwm },
|
||||
{ Setting_RpmMax1, Group_Spindle, "PWM2 spindle max speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &sp1_settings.cfg.rpm_max, NULL, has_pwm },
|
||||
{ Setting_RpmMin1, Group_Spindle, "PWM2 spindle min speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &sp1_settings.cfg.rpm_min, NULL, has_pwm },
|
||||
{ Setting_PWMFreq1, Group_Spindle, "PWM2 spindle PWM frequency", "Hz", Format_Decimal, "#####0", NULL, NULL, Setting_IsExtended, &sp1_settings.cfg.pwm_freq, NULL, has_freq },
|
||||
@@ -1363,6 +1376,10 @@ FLASHMEM static void spindle1_settings_load (void)
|
||||
{
|
||||
if((hal.nvs.memcpy_from_nvs((uint8_t *)&sp1_settings, nvs_address, sizeof(spindle1_pwm_settings_t), true) != NVS_TransferResult_OK))
|
||||
spindle1_settings_restore();
|
||||
|
||||
#if N_SPINDLE == 1
|
||||
sp1_settings.cfg.flags.ignore_delays = Off;
|
||||
#endif
|
||||
}
|
||||
|
||||
FLASHMEM spindle1_pwm_settings_t *spindle1_settings_add (bool claim_ports)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
Part of grblHAL
|
||||
|
||||
Copyright (c) 2017-2025 Terje Io
|
||||
Copyright (c) 2017-2026 Terje Io
|
||||
Copyright (c) 2012-2015 Sungeun K. Jeon
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
|
||||
@@ -220,10 +220,18 @@ typedef union {
|
||||
pwm_disable :1, // PWM spindle only
|
||||
g92offset :1,
|
||||
pwm_ramped :1, // PWM spindle only
|
||||
unassigned :3;
|
||||
ignore_delays :1, // PWM spindle only
|
||||
unassigned :2;
|
||||
};
|
||||
} spindle_settings_flags_t;
|
||||
|
||||
#if N_SPINDLE == 1
|
||||
#define PWM_SPINDLE_NO_DELAYS
|
||||
#else
|
||||
#define PWM_SPINDLE_NO_DELAYS ",Ignore on/off delays"
|
||||
#endif
|
||||
|
||||
|
||||
typedef union {
|
||||
uint8_t value;
|
||||
uint8_t mask;
|
||||
@@ -348,8 +356,12 @@ typedef struct spindle_param {
|
||||
spindle_state_t state;
|
||||
override_t override_pct; //!< Spindle RPM override value in percent
|
||||
spindle_css_data_t css; //!< Data used for Constant Surface Speed Mode (CSS) calculations, NULL if not in CSS mode.
|
||||
bool ramp_up;
|
||||
bool ramp_down;
|
||||
struct {
|
||||
uint8_t ramp_up :1,
|
||||
ramp_down :1,
|
||||
ignore_delays :1,
|
||||
override_disable :1;
|
||||
} option;
|
||||
spindle_ptrs_t *hal;
|
||||
} spindle_param_t;
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "state_machine.h"
|
||||
#include "override.h"
|
||||
|
||||
extern void gc_tool_changed (void);
|
||||
|
||||
static void state_idle (uint_fast16_t new_state);
|
||||
static void state_cycle (uint_fast16_t rt_exec);
|
||||
static void state_await_hold (uint_fast16_t rt_exec);
|
||||
@@ -456,16 +458,13 @@ static void state_cycle (uint_fast16_t rt_exec)
|
||||
*/
|
||||
FLASHMEM static void state_await_toolchanged (uint_fast16_t rt_exec)
|
||||
{
|
||||
if (rt_exec & EXEC_CYCLE_START) {
|
||||
if (!gc_state.tool_change) {
|
||||
if(rt_exec & EXEC_CYCLE_START) {
|
||||
if(!gc_state.tool_change) {
|
||||
|
||||
if (hal.stream.suspend_read)
|
||||
if(hal.stream.suspend_read)
|
||||
hal.stream.suspend_read(false); // Tool change complete, restore "normal" stream input.
|
||||
|
||||
if(grbl.on_tool_changed)
|
||||
grbl.on_tool_changed(gc_state.tool);
|
||||
|
||||
report_add_realtime(Report_Tool);
|
||||
gc_tool_changed();
|
||||
}
|
||||
pending_state = gc_state.tool_change ? STATE_TOOL_CHANGE : STATE_IDLE;
|
||||
state_set(STATE_IDLE);
|
||||
|
||||
30
strutils.c
30
strutils.c
@@ -25,16 +25,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "grbl.h"
|
||||
#include "strutils.h"
|
||||
|
||||
#define CAPS(c) ((c >= 'a' && c <= 'z') ? c & 0x5F : c)
|
||||
|
||||
/*! \brief Case insensitive search for first occurrence of a string inside another.
|
||||
\param s1 pointer to string to search.
|
||||
\param s2 pointer to string to search for.
|
||||
\returns pointer to found string or NULL if not found.
|
||||
*/
|
||||
char *stristr (const char *s1, const char *s2)
|
||||
FLASHMEM char *stristr (const char *s1, const char *s2)
|
||||
{
|
||||
const char *s = s1, *p = s2, *r = NULL;
|
||||
|
||||
@@ -69,7 +68,7 @@ char *stristr (const char *s1, const char *s2)
|
||||
\param len max. number of characters to compare.
|
||||
\returns pointer to found string or NULL if not found.
|
||||
*/
|
||||
char *strnistr (const char *s1, const char *s2, size_t len)
|
||||
FLASHMEM char *strnistr (const char *s1, const char *s2, size_t len)
|
||||
{
|
||||
size_t slen = len;
|
||||
const char *s = s1, *p = s2, *r = NULL;
|
||||
@@ -102,8 +101,9 @@ char *strnistr (const char *s1, const char *s2, size_t len)
|
||||
return slen == 0 || *p == '\0' ? (char *)r : NULL;
|
||||
}
|
||||
|
||||
// NOTE: ensure buf is large enough to hold concatenated strings!
|
||||
char *strappend (char *buf, int argc, ...)
|
||||
// NOTE: Ensure buf is large enough to hold concatenated strings!
|
||||
// Do NOT use for several int/float conversions as these share the same underlying buffer!
|
||||
FLASHMEM char *strappend (char *buf, int argc, ...)
|
||||
{
|
||||
char c, *s = buf, *arg;
|
||||
|
||||
@@ -123,10 +123,10 @@ char *strappend (char *buf, int argc, ...)
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint32_t strnumentries (const char *s, const char delimiter)
|
||||
FLASHMEM uint32_t strnumentries (const char *s, const char delimiter)
|
||||
{
|
||||
char *p = (char *)s;
|
||||
uint32_t entries = *s ? 1 : 0;
|
||||
uint32_t entries = (s && *s) ? 1 : 0;
|
||||
|
||||
while(entries && (p = strchr(p, delimiter))) {
|
||||
p++;
|
||||
@@ -136,7 +136,7 @@ uint32_t strnumentries (const char *s, const char delimiter)
|
||||
return entries;
|
||||
}
|
||||
|
||||
char *strgetentry (char *res, const char *s, uint32_t entry, const char delimiter)
|
||||
FLASHMEM char *strgetentry (char *res, const char *s, uint32_t entry, const char delimiter)
|
||||
{
|
||||
char *e, *p = (char *)s;
|
||||
|
||||
@@ -163,7 +163,7 @@ char *strgetentry (char *res, const char *s, uint32_t entry, const char delimite
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t strlookup (const char *s1, const char *s2, const char delimiter)
|
||||
FLASHMEM int32_t strlookup (const char *s1, const char *s2, const char delimiter)
|
||||
{
|
||||
bool found = false;
|
||||
char *e, *p = (char *)s2;
|
||||
@@ -188,7 +188,7 @@ int32_t strlookup (const char *s1, const char *s2, const char delimiter)
|
||||
return found ? entry : -1;
|
||||
}
|
||||
|
||||
bool strtotime (char *s, struct tm *time)
|
||||
FLASHMEM bool strtotime (char *s, struct tm *time)
|
||||
{
|
||||
char c, *s1 = s;
|
||||
uint_fast16_t idx = 0;
|
||||
@@ -285,7 +285,7 @@ bool strtotime (char *s, struct tm *time)
|
||||
return idx >= 5;
|
||||
}
|
||||
|
||||
char *strtoisodt (struct tm *dt)
|
||||
FLASHMEM char *strtoisodt (struct tm *dt)
|
||||
{
|
||||
static char buf[21];
|
||||
|
||||
@@ -295,9 +295,9 @@ char *strtoisodt (struct tm *dt)
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *strtointernetdt (struct tm *dt)
|
||||
FLASHMEM char *strtointernetdt (struct tm *dt)
|
||||
{
|
||||
static const char *month_table[12] = {
|
||||
PROGMEM static const char *month_table[12] = {
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
@@ -312,7 +312,7 @@ char *strtointernetdt (struct tm *dt)
|
||||
"Dec"
|
||||
};
|
||||
|
||||
static const char *day_table[7] = {
|
||||
PROGMEM static const char *day_table[7] = {
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
|
||||
@@ -115,23 +115,10 @@ FLASHMEM static void change_completed (void)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reset claimed HAL entry points and restore previous tool if needed on soft restart.
|
||||
// Reset claimed HAL entry points on soft restart.
|
||||
// Called from EXEC_RESET and EXEC_STOP handlers (via HAL).
|
||||
FLASHMEM static void reset (void)
|
||||
{
|
||||
if(next_tool) { //TODO: move to gc_xxx() function?
|
||||
// Restore previous tool if reset is during change
|
||||
if(current_tool.tool_id != next_tool->tool_id) {
|
||||
if(grbl.tool_table.n_tools)
|
||||
memcpy(gc_state.tool, ¤t_tool, sizeof(tool_data_t));
|
||||
else
|
||||
memcpy(next_tool, ¤t_tool, sizeof(tool_data_t));
|
||||
report_add_realtime(Report_Tool);
|
||||
}
|
||||
gc_state.tool_pending = gc_state.tool->tool_id;
|
||||
next_tool = NULL;
|
||||
}
|
||||
|
||||
change_completed();
|
||||
driver_reset();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user