Added generic passthru support for programming external MCU via USB > UART bridge.

Requires driver support if to be used. Adds $PTRGH system command when available.
This commit is contained in:
Terje Io
2024-11-10 11:39:21 +01:00
parent 7054af825b
commit 1444c08997
7 changed files with 258 additions and 7 deletions
+1
View File
@@ -22,6 +22,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/state_machine.c
${CMAKE_CURRENT_LIST_DIR}/stream.c
${CMAKE_CURRENT_LIST_DIR}/stream_file.c
${CMAKE_CURRENT_LIST_DIR}/stream_passthru.c
${CMAKE_CURRENT_LIST_DIR}/stepper.c
${CMAKE_CURRENT_LIST_DIR}/stepper2.c
${CMAKE_CURRENT_LIST_DIR}/system.c
+12
View File
@@ -1,5 +1,17 @@
## grblHAL changelog
<a name="20241110">Build 20241110
Core:
* Added generic passthru support for programming external MCU via USB > UART bridge. Requires driver support if to be used. Adds `$PTRGH` system command when available.
Plugins:
* Misc, ESP-AT: general improvements.
---
<a name="20241107">Build 20241107
Core:
+4
View File
@@ -181,6 +181,8 @@ typedef enum {
Output_LED_Adressable,
Output_LED0_Adressable = Output_LED_Adressable,
Output_LED1_Adressable,
Output_CoProc_Reset,
Output_CoProc_Boot0,
// Multipin peripherals
Input_MISO,
Multipin = Input_MISO,
@@ -382,6 +384,8 @@ PROGMEM static const pin_name_t pin_names[] = {
{ .function = Output_LED_W, .name = "LED W" },
{ .function = Output_LED_Adressable, .name = "LED adressable" },
{ .function = Output_LED1_Adressable, .name = "LED adressable 1" },
{ .function = Output_CoProc_Reset, .name = "CoProc Reset" },
{ .function = Output_CoProc_Boot0, .name = "CoProc Boot0" },
{ .function = Input_MISO, .name = "MISO" },
{ .function = Output_MOSI, .name = "MOSI" },
{ .function = Output_SPICLK, .name = "SPI CLK" },
+1 -1
View File
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20241107
#define GRBL_BUILD 20241110
#define GRBL_URL "https://github.com/grblHAL"
+12 -6
View File
@@ -397,23 +397,29 @@ 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 DRIVER_SPINDLE_ENABLE
{ .function = Output_SpindleOn, .aux_port = 0xFF, .pin = SPINDLE_ENABLE_PIN, .port = SPINDLE_ENABLE_PORT },
{ .function = Output_SpindleOn, .aux_port = 0xFF, .pin = SPINDLE_ENABLE_PIN, .port = SPINDLE_ENABLE_PORT },
#if DRIVER_SPINDLE_PWM_ENABLE
{ .function = Output_SpindlePWM, .aux_port = 0xFF, .pin = SPINDLE_PWM_PIN, .port = SPINDLE_PWM_PORT },
{ .function = Output_SpindlePWM, .aux_port = 0xFF, .pin = SPINDLE_PWM_PIN, .port = SPINDLE_PWM_PORT },
#endif
#if DRIVER_SPINDLE_DIR_ENABLE
{ .function = Output_SpindleDir, .aux_port = 0xFF, .pin = SPINDLE_DIRECTION_PIN, .port = SPINDLE_DIRECTION_PORT },
{ .function = Output_SpindleDir, .aux_port = 0xFF, .pin = SPINDLE_DIRECTION_PIN, .port = SPINDLE_DIRECTION_PORT },
#endif
#endif // DRIVER_SPINDLE_ENABLE
#if DRIVER_SPINDLE1_ENABLE
{ .function = Output_Spindle1On, .aux_port = 0xFF, .pin = SPINDLE1_ENABLE_PIN, .port = SPINDLE1_ENABLE_PORT },
{ .function = Output_Spindle1On, .aux_port = 0xFF, .pin = SPINDLE1_ENABLE_PIN, .port = SPINDLE1_ENABLE_PORT },
#if DRIVER_SPINDLE1_PWM_ENABLE
{ .function = Output_Spindle1PWM, .aux_port = 0xFF, .pin = SPINDLE1_PWM_PIN, .port = SPINDLE1_PWM_PORT },
{ .function = Output_Spindle1PWM, .aux_port = 0xFF, .pin = SPINDLE1_PWM_PIN, .port = SPINDLE1_PWM_PORT },
#endif
#if DRIVER_SPINDLE1_DIR_ENABLE
{ .function = Output_Spindle1Dir, .aux_port = 0xFF, .pin = SPINDLE1_DIRECTION_PIN, .port = SPINDLE1_DIRECTION_PORT },
{ .function = Output_Spindle1Dir, .aux_port = 0xFF, .pin = SPINDLE1_DIRECTION_PIN, .port = SPINDLE1_DIRECTION_PORT },
#endif
#endif // DRIVER_SPINDLE1_DIR_ENABLE
#ifdef COPROC_RESET_PIN
{ .function = Output_CoProc_Reset, .aux_port = 0xFF, .pin = COPROC_RESET_PIN, .port = COPROC_RESET_PORT },
#endif
#ifdef COPROC_BOOT0_PIN
{ .function = Output_CoProc_Boot0, .aux_port = 0xFF, .pin = COPROC_BOOT0_PIN, .port = COPROC_BOOT0_PORT },
#endif
/*
#ifdef COOLANT_FLOOD_PIN
{ .function = Output_CoolantFlood, .aux_port = 0xFF, .pin = COOLANT_FLOOD_PIN, .port = COOLANT_FLOOD_PORT },
+206
View File
@@ -0,0 +1,206 @@
/*
stream_passthru.c - stream redirector for programming coprocessor
Part of grblHAL
Copyright (c) 2024 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 "hal.h"
#include "task.h"
#include "protocol.h"
//static xbar_t *dtr, *rts;
static uint8_t boot0_port = 0xFF, reset_port = 0xFF;
static io_stream_t dest;
static bool conn_ok = false;
static on_linestate_changed_ptr on_linestate_changed;
//static on_execute_realtime_ptr on_execute_realtime = NULL;
// Weak implementation of low level function to be provided by the driver
__attribute__((weak)) void stream_passthru_enter (void)
{
// The driver implementation calls stream_passthru_init() with start parameter set true after setting up for execution.
}
// ****
/*
static void onExecuteRealtime (uint_fast16_t state)
{
static bool lock = false;
int16_t c;
if(!lock) {
lock = true;
if((c = hal.stream.read()) != SERIAL_NO_DATA)
dest.write_char(c);
lock = false;
}
on_execute_realtime(state);
}
*/
ISR_CODE static bool ISR_FUNC(forward_usb_rx)(char c)
{
dest.write_char(c);
return true;
}
ISR_CODE static bool ISR_FUNC(sink_uart_rx)(char c)
{
return true;
}
static void forward_uart_rx (void *data)
{
static char buf[64];
static uint_fast8_t idx = 0;
int16_t c;
while((c = dest.read()) != SERIAL_NO_DATA) {
idx = 1;
*buf = c;
while(idx < sizeof(buf) && (c = dest.read()) != SERIAL_NO_DATA)
buf[idx++] = c;
hal.stream.write_n(buf, idx);
}
task_add_delayed(forward_uart_rx, NULL, 8);
}
static void onLinestateChanged (serial_linestate_t state)
{
/*
Auto program
DTR RTS-->EN IO0
1 1 1 1
0 0 1 1
1 0 0 1
0 1 1 0
*/
if(conn_ok) {
if(state.dtr == state.rts) {
hal.port.digital_out(boot0_port, 0);
hal.port.digital_out(reset_port, 1);
} else if(state.dtr) {
hal.port.digital_out(reset_port, 0);
hal.port.digital_out(boot0_port, 0);
} else {
hal.port.digital_out(boot0_port, 0);
hal.port.digital_out(reset_port, 1);
}
}
}
static void passthru_start2 (void *data)
{
conn_ok = true;
hal.port.digital_out(reset_port, 1);
task_add_delayed(forward_uart_rx, NULL, 8);
// on_execute_realtime = grbl.on_execute_realtime;
// grbl.on_execute_realtime = onExecuteRealtime;
dest.set_enqueue_rt_handler(stream_buffer_all);
dest.cancel_read_buffer();
}
static void passthru_start1 (void *data)
{
hal.port.digital_out(boot0_port, 0);
hal.port.digital_out(reset_port, 0);
on_linestate_changed = hal.stream.on_linestate_changed;
hal.stream.on_linestate_changed = onLinestateChanged;
task_add_delayed(passthru_start2, NULL, 1250); // delay a bit to allow the USB stack to start
}
static bool get_ports (xbar_t *properties, uint8_t port, void *data)
{
switch(properties->function) {
case Output_CoProc_Reset:
reset_port = port;
break;
case Output_CoProc_Boot0:
boot0_port = port;
break;
default:
break;
}
return boot0_port != 0xFF && reset_port != 0xFF;
}
static status_code_t passthru_enter (sys_state_t state, char *args)
{
report_message("Entering passthru mode", Message_Warning);
hal.delay_ms(100, NULL);
stream_passthru_enter();
return Status_OK;
}
void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start)
{
static const sys_command_t passthru_command_list[] = {
{"PTRGH", passthru_enter, { .noargs = On }, { .str = "enter passthru mode" } }
};
static sys_commands_t passthru_commands = {
.n_commands = sizeof(passthru_command_list) / sizeof(sys_command_t),
.commands = passthru_command_list
};
if(hal.stream.type == StreamType_Serial &&
hal.stream.state.is_usb &&
hal.stream.state.linestate_event &&
ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .output = On }, get_ports, NULL)) {
if(start) {
io_stream_t const *stream = stream_open_instance(instance, baud_rate, NULL, "Passthru");
if(stream != NULL) {
protocol_enqueue_foreground_task(passthru_start1, NULL); // enter passthrouh mode after finished booting grblHAL
memcpy(&dest, stream, sizeof(io_stream_t));
dest.set_enqueue_rt_handler(sink_uart_rx);
hal.stream.set_enqueue_rt_handler(forward_usb_rx);
} else
report_message("Entering passthru mode failed!", Message_Warning);
} else
system_register_commands(&passthru_commands);
}
}
+22
View File
@@ -0,0 +1,22 @@
/*
stream_passthru.h - stream redirector for programming coprocessor
Part of grblHAL
Copyright (c) 2024 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/>.
*/
void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start);