mirror of
https://github.com/grblHAL/core.git
synced 2026-03-23 03:22:56 +08:00
Added some reentry locks.
Second step in refactoring encoder HAL/API.
This commit is contained in:
@@ -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.
|
||||
|
||||
26
changelog.md
26
changelog.md
@@ -1,11 +1,37 @@
|
||||
## grblHAL changelog
|
||||
|
||||
<a name="202600303">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.
|
||||
|
||||
---
|
||||
|
||||
<a name="20260227">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.
|
||||
|
||||
---
|
||||
|
||||
<a name="20260225">Build 20260225
|
||||
|
||||
@@ -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;
|
||||
|
||||
187
encoders.h
187
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;
|
||||
|
||||
2
grbl.h
2
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"
|
||||
|
||||
|
||||
58
grbllib.c
58
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)
|
||||
|
||||
10
hal.h
10
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.
|
||||
|
||||
9
stream.c
9
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user