From 95011b570b57f05c4f7278bbcc25b3c65d3a656b Mon Sep 17 00:00:00 2001 From: Terje Io Date: Wed, 9 Apr 2025 11:49:10 +0200 Subject: [PATCH] Added new ioport capabilities. Added registration scheme for 3rd party I/O expander plugins. Fixed minor bug in ioport numbering (P/E) for plugin based ioports. --- changelog.md | 23 ++++++++++++++ crossbar.h | 21 +++++++++++-- driver_opts.h | 1 + expanders_init.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ grbl.h | 2 +- ioports.c | 6 ++-- pin_bits_masks.h | 65 +++++++++++++++++++++++++++++---------- 7 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 expanders_init.h diff --git a/changelog.md b/changelog.md index 73be42f..bf8243f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,28 @@ ## grblHAL changelog +Build 20250409 + +Core: + +* For developers: added new ioport capabilities, added optional support for drivers/boards wanting to claim basic pins \(stepper enable, spindle, coolant, ...\) from expander plugins. +Added registration scheme for 3rd party I/O expander plugins in [expanders_init.h](https://github.com/grblHAL/core/blob/master/expanders_init.h). + +* Fixed minor bug in ioport numbering \(`P`/`E`\) for plugin based ioports. + +Drivers: + +* iMXRT1062: fixed compilation error when Laser PPI mode was enabled. Ref. issue comment [#645, 12764973](https://github.com/grblHAL/core/discussions/645#discussioncomment-12764973). + +* ESP32, iMXRT1062, LPC176x, RP2040, SAMX3X8E, STM32F4xx and STM32F7xx: updated to use new scheme for I/O expander plugin initialization. + +* ESP32, RP2040: added initial support for claiming basic pins from IO expanders. CNC BoosterPack board: switched to use the generic PCA9654E I2C IO expander plugin instead of a driver specific plugin. + +Plugins: + +* Misc: updated MCP3221 I2C ADC and PCA9654E I2C IO expander plugins to match core changes. + +--- + Build 20250407 Core: diff --git a/crossbar.h b/crossbar.h index 028ad00..a2d4d12 100644 --- a/crossbar.h +++ b/crossbar.h @@ -573,6 +573,20 @@ typedef enum { PullMode_UpDown = 0b11 //!< 0b11 (0x03) - only used to report port capability. } pull_mode_t; +//! ADC/DAC resolution or multi pin register width. +typedef enum { + Resolution_4bit = 0, //!< 0 - 4 bit + Resolution_8bit, //!< 1 - 8 bit + Resolution_10bit, //!< 2 - 10 bit + Resolution_12bit, //!< 3 - 12 bit + Resolution_14bit, //!< 4 - 14 bit + Resolution_16bit, //!< 5 - 16 bit + Resolution_18bit, //!< 6 - 18 bit + Resolution_20bit, //!< 7 - 20 bit + Resolution_24bit, //!< 8 - 24 bit + Resolution_32bit //!< 9 - 32 bit, NOTE: "wait for input" can only return 31 bits +} resolution_t; + #define PINMODE_NONE (0) #define PINMODE_OUTPUT (1U<<1) #ifndef __LPC17XX__ @@ -590,8 +604,8 @@ typedef union { uint32_t input :1, output :1, open_drain :1, - pull_mode :2, - irq_mode :5, + pull_mode :2, //!< pull_mode_t - pull up/down modes + irq_mode :5, //!< pin_irq_mode_t - IRQ modes invert :1, analog :1, pwm :1, @@ -600,7 +614,8 @@ typedef union { debounce :1, external :1, async :1, - unused :14; + resolution :4, //!< resolution_t - ADC/DAC resolution + unused :10; }; } pin_cap_t; diff --git a/driver_opts.h b/driver_opts.h index 1a93fe0..0229df8 100644 --- a/driver_opts.h +++ b/driver_opts.h @@ -29,6 +29,7 @@ #include "hal.h" #include "nuts_bolts.h" +#include "expanders_init.h" #ifdef OPTS_POSTPROCESSING #define NO_OPTS_POST 0 diff --git a/expanders_init.h b/expanders_init.h new file mode 100644 index 0000000..40f6f4b --- /dev/null +++ b/expanders_init.h @@ -0,0 +1,79 @@ +/* + expanders_init.h - An embedded CNC Controller with rs274/ngc (g-code) support + + Calls the init function of enabled expanders, this file is typically included early in driver.h + io_expanders_init() should be called at the end of the drivers driver_init() implementation, + just before the driver claims ports. + + These are NOT referenced in the core grbl code + + Part of grblHAL + + Copyright (c) 2025 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 . +*/ + +#pragma once + +// I2C expanders + +#if PCA9654E_ENABLE || MCP3221_ENABLE + +#if defined(I2C_ENABLE) && !I2C_ENABLE +#undef I2C_ENABLE +#endif + +#ifndef I2C_ENABLE +#define I2C_ENABLE 1 +#endif + +#if MCP3221_ENABLE +extern void mcp3221_init (void); +#endif + +#if PCA9654E_ENABLE +extern void pca9654e_init(void); +#endif + +// Third party I2C expander plugins goes after this line + +#endif // I2C expanders + +// SPI expanders + +// + +// ModBus expanders + +// + +// CANBus expanders + +// + +// Other expanders + +// + +static inline void io_expanders_init (void) +{ +#if MCP3221_ENABLE + mcp3221_init(); +#endif + +#if PCA9654E_ENABLE + pca9654e_init(); +#endif +} diff --git a/grbl.h b/grbl.h index 36adacf..07b3a8a 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20250407 +#define GRBL_BUILD 20250409 #define GRBL_URL "https://github.com/grblHAL" diff --git a/ioports.c b/ioports.c index 524b258..ef95394 100644 --- a/ioports.c +++ b/ioports.c @@ -480,9 +480,9 @@ static bool io_claim (io_port_type_t type, io_port_direction_t dir, uint8_t *por while(cfg->map[idx] != *port) idx++; - for(; idx < cfg->last_claimed; idx++) { - if((cfg->map[idx] = cfg->map[idx + 1]) <= io_port->port[cfg->type].n_end) - io_port->hal.set_pin_description(dir, cfg->map[idx], (cfg->pnum + (idx * 3) + (idx > 9 ? idx - 10 : 0))); + for(; idx < cfg->last_claimed ; idx++) { + if((cfg->map[idx] = cfg->map[idx + 1]) != 255) + io_set_pin_description(type, dir, idx, (cfg->pnum + (idx * 3) + (idx > 9 ? idx - 10 : 0))); } io_port->hal.set_pin_description(dir, *port - io_port->port[cfg->type].n_start, description); diff --git a/pin_bits_masks.h b/pin_bits_masks.h index 88c2dca..1551c6b 100644 --- a/pin_bits_masks.h +++ b/pin_bits_masks.h @@ -51,6 +51,8 @@ #error "Encoder select input is not supported in this configuration!" #endif +#define EXPANDER_PORT 1 + // Control input signals // Define the CONTROL_PORT symbol as a shorthand in the *_map.h file if all control inputs share the same port. @@ -410,41 +412,58 @@ static inline control_signals_t aux_ctrl_scan_status (control_signals_t signals) // The following pins are bound explicitly to aux output pins static aux_ctrl_out_t aux_ctrl_out[] = { +#if defined(ESP_PLATFORM) || defined(RP2040) // for now +#if defined(STEPPERS_ENABLE_PIN) && STEPPERS_ENABLE_PORT == EXPANDER_PORT + { .function = Output_StepperEnable, .aux_port = 0xFF, .pin = STEPPERS_ENABLE_PIN, .port = (void *)STEPPERS_ENABLE_PORT }, +#endif +#if defined(X_ENABLE_PIN) && X_ENABLE_PORT == EXPANDER_PORT + { .function = Output_StepperEnableX, .aux_port = 0xFF, .pin = X_ENABLE_PIN, .port = (void *)X_ENABLE_PORT }, +#endif +#if defined(Y_ENABLE_PIN) && Y_ENABLE_PORT == EXPANDER_PORT + { .function = Output_StepperEnableY, .aux_port = 0xFF, .pin = Y_ENABLE_PIN, .port = (void *)Y_ENABLE_PORT }, +#endif +#if defined(XY_ENABLE_PIN) && XY_ENABLE_PORT == EXPANDER_PORT + { .function = Output_StepperEnableXY, .aux_port = 0xFF, .pin = XY_ENABLE_PIN, .port = (void *)XY_ENABLE_PORT }, +#endif +#if defined(Z_ENABLE_PIN) && Z_ENABLE_PORT == EXPANDER_PORT + { .function = Output_StepperEnableZ, .aux_port = 0xFF, .pin = Z_ENABLE_PIN, .port = (void *)Z_ENABLE_PORT }, +#endif +#endif #if AUX_CONTROLS & AUX_CONTROL_SPINDLE #ifdef SPINDLE_ENABLE_PIN #ifndef SPINDLE_ENABLE_PORT - #define SPINDLE_ENABLE_PORT NULL + #define SPINDLE_ENABLE_PORT 0 #endif { .function = Output_SpindleOn, .aux_port = 0xFF, .pin = SPINDLE_ENABLE_PIN, .port = (void *)SPINDLE_ENABLE_PORT }, #endif #ifdef SPINDLE_PWM_PIN #ifndef SPINDLE_PWM_PORT - #define SPINDLE_PWM_PORT NULL + #define SPINDLE_PWM_PORT 0 #endif { .function = Output_SpindlePWM, .aux_port = 0xFF, .pin = SPINDLE_PWM_PIN, .port = (void *)SPINDLE_PWM_PORT }, #endif #ifdef SPINDLE_DIRECTION_PIN #ifndef SPINDLE_DIRECTION_PORT - #define SPINDLE_DIRECTION_PORT NULL + #define SPINDLE_DIRECTION_PORT 0 #endif { .function = Output_SpindleDir, .aux_port = 0xFF, .pin = SPINDLE_DIRECTION_PIN, .port = (void *)SPINDLE_DIRECTION_PORT }, #endif #ifdef SPINDLE1_ENABLE_PIN #ifndef SPINDLE1_ENABLE_PORT - #define SPINDLE1_ENABLE_PORT NULL + #define SPINDLE1_ENABLE_PORT 0 #endif { .function = Output_Spindle1On, .aux_port = 0xFF, .pin = SPINDLE1_ENABLE_PIN, .port = (void *)SPINDLE1_ENABLE_PORT }, #endif #ifdef SPINDLE1_PWM_PIN #ifndef SPINDLE1_PWM_PORT - #define SPINDLE1_PWM_PORT NULL + #define SPINDLE1_PWM_PORT 0 #endif { .function = Output_Spindle1PWM, .aux_port = 0xFF, .pin = SPINDLE1_PWM_PIN, .port = (void *)SPINDLE1_PWM_PORT }, #endif #ifdef SPINDLE1_DIRECTION_PIN #ifndef SPINDLE1_DIRECTION_PORT - #define SPINDLE1_DIRECTION_PORT NULL + #define SPINDLE1_DIRECTION_PORT 0 #endif { .function = Output_Spindle1Dir, .aux_port = 0xFF, .pin = SPINDLE1_DIRECTION_PIN, .port = (void *)SPINDLE1_DIRECTION_PORT }, #endif @@ -453,13 +472,13 @@ static aux_ctrl_out_t aux_ctrl_out[] = { #if AUX_CONTROLS & AUX_CONTROL_COOLANT #ifdef COOLANT_FLOOD_PIN #ifndef COOLANT_FLOOD_PORT - #define COOLANT_FLOOD_PORT NULL + #define COOLANT_FLOOD_PORT 0 #endif { .function = Output_CoolantFlood, .aux_port = 0xFF, .pin = COOLANT_FLOOD_PIN, .port = (void *)COOLANT_FLOOD_PORT }, #endif #ifdef COOLANT_MIST_PIN #ifndef COOLANT_MIST_PORT - #define COOLANT_MIST_PORT NULL + #define COOLANT_MIST_PORT 0 #endif { .function = Output_CoolantMist, .aux_port = 0xFF, .pin = COOLANT_MIST_PIN, .port = (void *)COOLANT_MIST_PORT }, #endif @@ -467,13 +486,13 @@ static aux_ctrl_out_t aux_ctrl_out[] = { #ifdef COPROC_RESET_PIN #ifndef COPROC_RESET_PORT - #define COPROC_RESET_PORT NULL + #define COPROC_RESET_PORT 0 #endif { .function = Output_CoProc_Reset, .aux_port = 0xFF, .pin = COPROC_RESET_PIN, .port = (void *)COPROC_RESET_PORT }, #endif #ifdef COPROC_BOOT0_PIN #ifndef COPROC_BOOT0_PORT - #define COPROC_BOOT0_PORT NULL + #define COPROC_BOOT0_PORT 0 #endif { .function = Output_CoProc_Boot0, .aux_port = 0xFF, .pin = COPROC_BOOT0_PIN, .port = (void *)COPROC_BOOT0_PORT }, #endif @@ -501,10 +520,13 @@ typedef bool (*aux_claim_explicit_out_ptr)(aux_ctrl_out_t *aux_ctrl); static bool aux_ctrl_claim_out_port (xbar_t *properties, uint8_t port, void *data) { - if(ioport_claim(Port_Digital, Port_Output, &port, xbar_fn_to_pinname(((aux_ctrl_t *)data)->function))) - ((aux_ctrl_t *)data)->aux_port = port; + if(((aux_ctrl_out_t *)data)->port == (void *)EXPANDER_PORT) { + if(((aux_ctrl_out_t *)data)->pin == properties->pin && properties->set_value) + ((aux_ctrl_out_t *)data)->aux_port = port; + } else if(ioport_claim(Port_Digital, Port_Output, &port, xbar_fn_to_pinname(((aux_ctrl_out_t *)data)->function))) + ((aux_ctrl_out_t *)data)->aux_port = port; - return ((aux_ctrl_t *)data)->aux_port != 0xFF; + return ((aux_ctrl_out_t *)data)->aux_port != 0xFF; } static inline void aux_ctrl_claim_out_ports (aux_claim_explicit_out_ptr aux_claim_explicit, ioports_enumerate_callback_ptr aux_claim) @@ -515,9 +537,20 @@ static inline void aux_ctrl_claim_out_ports (aux_claim_explicit_out_ptr aux_clai aux_claim = aux_ctrl_claim_out_port; for(idx = 0; idx < sizeof(aux_ctrl_out) / sizeof(aux_ctrl_out_t); idx++) { - if(aux_ctrl_out[idx].pin == 0xFF) - ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .claimable = On }, aux_claim, (void *)&aux_ctrl_out[idx]); - else if(aux_ctrl_out[idx].aux_port != 0xFF) + if(aux_ctrl_out[idx].port == (void *)EXPANDER_PORT) { + if(ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .external = On, .claimable = On }, aux_claim, &aux_ctrl_out[idx])) { + if(ioport_claim(Port_Digital, Port_Output, &aux_ctrl_out[idx].aux_port, ""/*xbar_fn_to_pinname(aux_ctrl_out[idx].function)*/)) { + aux_ctrl_out[idx].output = hal.port.get_pin_info(Port_Digital, Port_Output, aux_ctrl_out[idx].aux_port); + if(((xbar_t *)aux_ctrl_out[idx].output)->set_function) + ((xbar_t *)aux_ctrl_out[idx].output)->set_function((xbar_t *)aux_ctrl_out[idx].output, aux_ctrl_out[idx].function); + // TODO: else set description? + aux_claim_explicit(&aux_ctrl_out[idx]); + } + } + } else if(aux_ctrl_out[idx].pin == 0xFF) { + if(ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .claimable = On }, aux_claim, &aux_ctrl_out[idx])) + aux_claim_explicit(&aux_ctrl_out[idx]); + } else if(aux_ctrl_out[idx].aux_port != 0xFF) aux_claim_explicit(&aux_ctrl_out[idx]); } }