diff --git a/README.md b/README.md
index 53ebe51..a17eda3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## grblHAL ##
-Latest build date is 20260227, see the [changelog](changelog.md) for details.
+Latest build date is 20260303, 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.
diff --git a/changelog.md b/changelog.md
index 3e3ab0a..154ac5e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,11 +1,37 @@
## grblHAL changelog
+Build 20260303
+
+Core:
+
+* Second step in refactoring encoder HAL/API.
+
+* Added some reentry locks.
+
+Drivers:
+
+* iMXRT1062, STM32F4xx, STM32F7xx: updated for the new encoder HAL/API.
+
+Plugins:
+
+* Encoder: updated to use the new encoder HAL/API.
+
+* Motors: fix for `M122` crashing ESP32 controllers. Ref discussion comment [#645](https://github.com/grblHAL/core/discussions/645#discussioncomment-15961675).
+
+* Networking, Wiznet: "hardened" code in an attempt to prevent rare hang.
+
+---
+
Build 20260227
Core:
* Improved handling of `G43` some more - can now be used in the same block as `M6` when the internal workflow for tool change is enabled or `M6` is ignored.
+Plugins:
+
+* Encoder: updated to use the new encoder HAL/API.
+
---
Build 20260225
diff --git a/encoders.c b/encoders.c
index 39c55ec..292be5e 100644
--- a/encoders.c
+++ b/encoders.c
@@ -32,15 +32,6 @@ static struct encoders {
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;
diff --git a/encoders.h b/encoders.h
index 509ebeb..26d0c97 100644
--- a/encoders.h
+++ b/encoders.h
@@ -79,11 +79,12 @@ typedef void (*encoder_on_event_ptr)(encoder_t *encoder, encoder_event_t *events
typedef void (*encoder_reset_ptr)(encoder_t *encoder);
/*! \brief Pointer to function for claiming an encoder.
+\param encoder pointer to a \a encoder_t struct.
\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);
+typedef bool (*encoder_claim_ptr)(encoder_t *encoder, 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.
@@ -109,6 +110,7 @@ bool encoders_enumerate (encoder_enumerate_callback_ptr callback, void *data);
uint8_t encoders_get_count (void);
struct encoder {
+ void *hw;
encoder_caps_t caps;
encoder_claim_ptr claim;
encoder_reset_ptr reset;
@@ -118,7 +120,7 @@ struct encoder {
#endif // _ENCODERS_H_
-// Quadrature Encoder Interface - static code for drivers/plugins
+// Interrupt driven Quadrature Encoder Interface - static code for driver/plugin use
#if QEI_ENABLE && defined(QEI_A_PIN) && defined(QEI_B_PIN)
@@ -150,9 +152,9 @@ typedef struct {
uint32_t vel_timestamp;
encoder_on_event_ptr on_event;
encoder_cfg_t settings;
-} qei_t;
+} iqei_t;
-static qei_t qei = {
+static iqei_t iqei = {
.port_a = IOPORT_UNASSIGNED,
.port_b = IOPORT_UNASSIGNED,
.port_select = IOPORT_UNASSIGNED,
@@ -160,155 +162,172 @@ static qei_t qei = {
.encoder.caps.bidirectional = On
};
-static void qei_post_event (void *data)
+static void iqei_select_irq (uint8_t port, bool high);
+
+static void iqei_post_event (void *data)
{
- qei.on_event(&qei.encoder, &qei.event, qei.context);
+ iqei.event.events |= ((encoder_event_t *)data)->events;
+
+ iqei.on_event(&iqei.encoder, &iqei.event, iqei.context);
}
-static void qei_dblclk_event (void *data)
+static void iqei_reset (encoder_t *encoder)
{
- qei.event.dbl_click = On;
- qei.on_event(&qei.encoder, &qei.event, qei.context);
+ iqei.vel_timeout = 0;
+ iqei.dir = QEI_DirUnknown;
+ iqei.data.position = iqei.vel_count = 0;
+ iqei.vel_timestamp = hal.get_elapsed_ticks();
+ iqei.vel_timeout = iqei.settings.vel_timeout;
}
-static void qei_reset (encoder_t *encoder)
+static bool iqei_configure (encoder_t *encoder, encoder_cfg_t *settings)
{
- 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;
-}
+ if(iqei.vel_timeout != settings->vel_timeout)
+ iqei.vel_timestamp = hal.get_elapsed_ticks();
-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));
+ memcpy(&iqei.settings, settings, sizeof(encoder_cfg_t));
return true;
}
-static encoder_data_t *qei_get_data (encoder_t *encoder)
+static encoder_data_t *iqei_get_data (encoder_t *encoder)
{
- return &qei.data;
+ return &iqei.data;
}
-static void qei_poll (void *data)
+static void iqei_poll (void *data)
{
- if(qei.vel_timeout && !(--qei.vel_timeout)) {
+ if(iqei.vel_timeout && !(--iqei.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;
+ iqei.data.velocity = abs(iqei.data.position - iqei.vel_count) * 1000 / (time - iqei.vel_timestamp);
+ iqei.vel_timestamp = time;
+ iqei.vel_timeout = iqei.settings.vel_timeout;
+ if((iqei.event.position_changed = !iqei.dbl_click_timeout || iqei.data.velocity == 0))
+ iqei.on_event(&iqei.encoder, &iqei.event, iqei.context);
+ iqei.vel_count = iqei.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);
+ if(iqei.dbl_click_timeout && !(--iqei.dbl_click_timeout)) {
+ iqei.event.click = On;
+ iqei.on_event(&iqei.encoder, &iqei.event, iqei.context);
}
}
-static void qei_ab_irq (uint8_t port, bool high)
+static void iqei_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};
+ PROGMEM static const uint8_t encoder_valid_state[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};
+ PROGMEM static const encoder_event_t dir_changed = { .direction_changed = On, .position_changed = On };
static qei_state_t state = {0};
- if(port == qei.port_a)
+ if(port == iqei.port_a)
state.a = high;
else
state.b = high;
- uint_fast8_t idx = (((qei.state << 2) & 0x0F) | state.pins);
+ uint_fast8_t idx = (((iqei.state << 2) & 0x0F) | state.pins);
if(encoder_valid_state[idx] ) {
-// int32_t count = qei.count;
+// int32_t count = iqei.count;
- qei.state = ((qei.state << 4) | idx) & 0xFF;
+ iqei.state = ((iqei.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);
+ if(iqei.state == 0x42 || iqei.state == 0xD4 || iqei.state == 0x2B || iqei.state == 0xBD) {
+ iqei.data.position--;
+ if(iqei.vel_timeout == 0 || iqei.dir == QEI_DirCW) {
+ iqei.dir = QEI_DirCCW;
+ task_add_immediate(iqei_post_event, &dir_changed);
}
- } 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);
+ } else if(iqei.state == 0x81 || iqei.state == 0x17 || iqei.state == 0xE8 || iqei.state == 0x7E) {
+ iqei.data.position++;
+ if(iqei.vel_timeout == 0 || iqei.dir == QEI_DirCCW) {
+ iqei.dir = QEI_DirCW;
+ task_add_immediate(iqei_post_event, &dir_changed);
}
}
}
}
-static void qei_select_irq (uint8_t port, bool high)
+static void iqei_select (void *data)
{
- if(high)
- return;
+ static uint8_t clicks = 0;
- 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);
+ if(!iqei.dbl_click_timeout) {
+ clicks = 1;
+ iqei.dbl_click_timeout = iqei.settings.dbl_click_window;
+ } else if(iqei.dbl_click_timeout < iqei.settings.dbl_click_window && ++clicks == 2) {
+ iqei.dbl_click_timeout = 0;
+ iqei.event.dbl_click = On;
+ iqei.on_event(&iqei.encoder, &iqei.event, iqei.context);
}
}
-static bool qei_claim (encoder_on_event_ptr event_handler, void *context)
+static void iqei_select_irq (uint8_t port, bool high)
{
- if(event_handler == NULL || qei.on_event)
+ static bool lock = false;
+
+ if(high || lock)
+ return;
+
+// lock = true;
+
+ task_add_immediate(iqei_select, NULL);
+
+// lock = false;
+}
+
+static bool iqei_claim (encoder_t *encoder, encoder_on_event_ptr event_handler, void *context)
+{
+ if(event_handler == NULL || iqei.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;
+ iqei.context = context;
+ iqei.on_event = event_handler;
+ iqei.encoder.reset = iqei_reset;
+ iqei.encoder.get_data = iqei_get_data;
+ iqei.encoder.configure = iqei_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(iqei.port_b != IOPORT_UNASSIGNED) {
+ ioport_enable_irq(iqei.port_a, IRQ_Mode_Change, iqei_ab_irq);
+ ioport_enable_irq(iqei.port_b, IRQ_Mode_Change, iqei_ab_irq);
}
- if(qei.port_select != IOPORT_UNASSIGNED)
- ioport_enable_irq(qei.port_select, IRQ_Mode_Change, qei_select_irq);
+ if(iqei.port_select != IOPORT_UNASSIGNED)
+ ioport_enable_irq(iqei.port_select, IRQ_Mode_Change, iqei_select_irq);
- task_add_systick(qei_poll, NULL);
+ task_add_systick(iqei_poll, NULL);
return true;
}
-static inline void encoder_pin_claimed (uint8_t port, xbar_t *pin)
+static inline void _encoder_pin_claimed (uint8_t port, xbar_t *pin)
{
switch(pin->function) {
case Input_QEI_A:
- qei.port_a = port;
+ iqei.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);
+ iqei.port_b = port;
+ iqei.encoder.claim = iqei_claim;
+ if(iqei.port_a != IOPORT_UNASSIGNED)
+ encoder_register(&iqei.encoder);
break;
case Input_QEI_Select:
- qei.port_select = port;
- qei.encoder.caps.select = On;
+ iqei.port_select = port;
+ iqei.encoder.caps.select = On;
+ if(pin->config) {
+ gpio_in_config_t config = {
+ .debounce = On,
+ .pull_mode = PullMode_Up
+ };
+ pin->config(pin, &config, false);
+ }
break;
default: break;
diff --git a/grbl.h b/grbl.h
index 6148906..07305d9 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20260227
+#define GRBL_BUILD 20260303
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/grbllib.c b/grbllib.c
index dbb063f..4e0a997 100644
--- a/grbllib.c
+++ b/grbllib.c
@@ -551,9 +551,15 @@ __attribute__((always_inline)) static inline core_task_t *task_alloc (void)
static void task_execute (sys_state_t state)
{
static uint32_t last_ms = 0;
+ static volatile bool lock = false;
core_task_t *task;
+ if(lock)
+ return;
+
+ lock = true;
+
if(immediate_task && sys.driver_started) {
hal.irq_disable();
@@ -566,39 +572,41 @@ static void task_execute (sys_state_t state)
}
uint32_t now = hal.get_elapsed_ticks();
- if(now == last_ms || next_task == systick_task)
- return;
+ if(!(now == last_ms || next_task == systick_task)) {
- last_ms = now;
+ last_ms = now;
- if((task = systick_task)) do {
- task->fn(task->data);
- } while((task = task->next));
+ if((task = systick_task)) do {
+ task->fn(task->data);
+ } while((task = task->next));
- while((task = next_task) && (int32_t)(task->time - now) <= 0) {
+ while((task = next_task) && (int32_t)(task->time - now) <= 0) {
- hal.irq_disable();
+ hal.irq_disable();
- if(task == next_task)
- next_task = task->next;
- else {
- core_task_t *t;
- if((t = next_task)) {
- while(t->next && t->next != task)
- t = t->next;
- if(t->next && t->next == task)
- t->next = task->next;
+ if(task == next_task)
+ next_task = task->next;
+ else {
+ core_task_t *t;
+ if((t = next_task)) {
+ while(t->next && t->next != task)
+ t = t->next;
+ if(t->next && t->next == task)
+ t->next = task->next;
+ }
}
+
+ hal.irq_enable();
+
+ void *data = task->data;
+ foreground_task_ptr fn = task->fn;
+ task_free(task);
+
+ fn(data);
}
-
- hal.irq_enable();
-
- void *data = task->data;
- foreground_task_ptr fn = task->fn;
- task_free(task);
-
- fn(data);
}
+
+ lock = false;
}
ISR_CODE bool ISR_FUNC(task_add_delayed)(foreground_task_ptr fn, void *data, uint32_t delay_ms)
diff --git a/hal.h b/hal.h
index f1033e9..f6ac8e9 100644
--- a/hal.h
+++ b/hal.h
@@ -486,9 +486,10 @@ typedef union {
up :1, //!< Timer supports upcounting
comp1 :1, //!< Timer supports compare interrupt 0
comp2 :1, //!< Timer supports compare interrupt 1
+ comp3 :1, //!< Timer supports compare interrupt 2
ext_clk :1, //!< External clock supported
- encoder :1, //!< Encode mode supported
- unused :2;
+ encoder :1, //!< Encoder mode supported
+ unused :1;
};
} timer_cap_t;
@@ -497,12 +498,15 @@ typedef void (*timer_irq_handler_ptr)(void *context);
typedef struct {
void *context; //!< Pointer to data to be passed on to the interrupt handlers
bool single_shot; //!< Set to true if timer is single shot
+ bool encoder_mode; //!< Set to true if timer is in encoder mode
uint32_t period; //!< Current value for period register
timer_irq_handler_ptr timeout_callback; //!< Pointer to main timeout callback
uint32_t irq0; //!< Compare value for compare interrupt 0
timer_irq_handler_ptr irq0_callback; //!< Pointer to compare interrupt 0 callback
- uint32_t irq1; //!< Compare value for compare interrupt 10
+ uint32_t irq1; //!< Compare value for compare interrupt 1
timer_irq_handler_ptr irq1_callback; //!< Pointer to compare interrupt 1 callback
+ uint32_t irq2; //!< Compare value for compare interrupt 2
+ timer_irq_handler_ptr irq2_callback; //!< Pointer to compare interrupt 1 callback
} timer_cfg_t;
/*! \brief Pointer to function for claiming a timer.
diff --git a/stream.c b/stream.c
index 99be6e1..4ad8da7 100644
--- a/stream.c
+++ b/stream.c
@@ -112,11 +112,16 @@ FLASHMEM bool stream_enumerate_streams (stream_enumerate_callback_ptr callback,
}
// called from stream drivers while tx is blocking, returns false to terminate
+// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
bool stream_tx_blocking (void)
{
- // TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
+ static volatile bool lock = false;
- grbl.on_execute_realtime(state_get());
+ if(!lock) {
+ lock = true;
+ grbl.on_execute_realtime(state_get());
+ lock = false;
+ }
return !(sys.rt_exec_state & EXEC_RESET);
}