diff --git a/CMakeLists.txt b/CMakeLists.txt index dc731ae..eb64962 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ target_sources(grbl INTERFACE ${CMAKE_CURRENT_LIST_DIR}/regex.c ${CMAKE_CURRENT_LIST_DIR}/ioports.c ${CMAKE_CURRENT_LIST_DIR}/vfs.c + ${CMAKE_CURRENT_LIST_DIR}/canbus.c ${CMAKE_CURRENT_LIST_DIR}/kinematics/corexy.c ${CMAKE_CURRENT_LIST_DIR}/kinematics/wall_plotter.c ${CMAKE_CURRENT_LIST_DIR}/kinematics/delta.c diff --git a/README.md b/README.md index ee5fadf..df8c0aa 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ It has been written to complement grblHAL and has features such as proper keyboa --- -Latest build date is 20240624, see the [changelog](changelog.md) for details. +Latest build date is 20240704, see the [changelog](changelog.md) for details. __NOTE:__ Build 20240222 has moved the probe input to the ioPorts pool of inputs and will be allocated from it when configured. The change is major and _potentially dangerous_, it may damage your probe, so please _verify correct operation_ after installing this, or later, builds. diff --git a/canbus.c b/canbus.c new file mode 100644 index 0000000..dc38595 --- /dev/null +++ b/canbus.c @@ -0,0 +1,209 @@ +/* + + canbus.c - + + Part of grblHAL + + Copyright (c) 2022 Jon Escombe + 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 . + +*/ + +#include "hal.h" +#include "task.h" +#include "protocol.h" +#include "canbus.h" + +#ifndef CANBUS_BUFFER_LEN +#define CANBUS_BUFFER_LEN 8 +#endif +#ifndef CANBUS_BAUDRATE +#define CANBUS_BAUDRATE 0 // 125,000 +#endif + +typedef struct { + volatile canbus_message_t message; + can_rx_ptr callback; +} canbus_rx_message; + +typedef struct { + volatile canbus_message_t message; + bool ext_id; +} canbus_tx_message; + +typedef struct { + volatile uint8_t head; + volatile uint8_t tail; + volatile canbus_tx_message tx[CANBUS_BUFFER_LEN]; +} canbus_tx_buffer_t; + +typedef struct { + volatile uint8_t head; + volatile uint8_t tail; + volatile canbus_rx_message rx[CANBUS_BUFFER_LEN]; +} canbus_rx_buffer_t; + +static bool isEnabled = false; +static const uint32_t baud[] = { 125000, 250000, 500000, 1000000 }; +static canbus_tx_buffer_t tx_buffer = {0}; +static canbus_rx_buffer_t rx_buffer = {0}; + +// Weak implementations of low level functions to be provided by the driver + +__attribute__((weak)) bool can_start (uint32_t baud, can_rx_enqueue_fn callback) +{ + return false; +} + +__attribute__((weak)) bool can_stop (void) +{ + return false; +} + +__attribute__((weak)) bool can_set_baud (uint32_t baud) +{ + return false; +} + +__attribute__((weak)) bool can_put (canbus_message_t msg, bool ext_id) +{ + return false; +} + +__attribute__((weak)) bool can_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback) +{ + return false; +} + +// --- + +ISR_CODE static bool ISR_FUNC(canbus_queue_rx)(canbus_message_t message, can_rx_ptr callback) +{ + bool ok; + uint8_t next_head = (rx_buffer.head + 1) % CANBUS_BUFFER_LEN; + + if((ok = next_head != rx_buffer.tail)) { + rx_buffer.rx[next_head].callback = callback; + rx_buffer.rx[next_head].message = message; + + rx_buffer.head = next_head; + } + + return ok; +} + +// called every 1 ms +static void canbus_poll (void *data) +{ + /* if have TX data, sends one message per iteration.. */ + if(tx_buffer.head != tx_buffer.tail && can_put(tx_buffer.tx[tx_buffer.tail].message, tx_buffer.tx[tx_buffer.tail].ext_id)) + tx_buffer.tail = (tx_buffer.tail + 1) % CANBUS_BUFFER_LEN; + + /* if have RX data, process one message per iteration.. */ + if(rx_buffer.head != rx_buffer.tail) { + if(rx_buffer.rx[rx_buffer.tail].callback) + rx_buffer.rx[rx_buffer.tail].callback(rx_buffer.rx[rx_buffer.tail].message); + rx_buffer.tail = (rx_buffer.tail + 1) % CANBUS_BUFFER_LEN; + } +} + +static bool canbus_start (uint32_t baud) +{ + if((isEnabled = can_start(baud, canbus_queue_rx))) + task_add_systick(canbus_poll, NULL); + + return isEnabled; +} + +static status_code_t canbus_set_baud (setting_id_t id, uint_fast16_t value) +{ + settings.canbus_baud = value; + + can_set_baud(baud[settings.canbus_baud]); + + return can_set_baud(baud[settings.canbus_baud]) ? Status_OK : Status_SettingValueOutOfRange; +} + +static uint32_t canbus_get_baud (setting_id_t setting) +{ + return settings.canbus_baud < (sizeof(baud) / sizeof(uint32_t)) ? settings.canbus_baud : CANBUS_BAUDRATE; +} + +static const setting_group_detail_t canbus_groups [] = { + { Group_Root, Group_CANbus, "CAN bus"} +}; + +static const setting_detail_t canbus_setting_detail[] = { + { Setting_CANbus_BaudRate, Group_CANbus, "CAN bus baud rate", NULL, Format_RadioButtons, "125000,250000,500000,1000000", NULL, NULL, Setting_NonCoreFn, canbus_set_baud, canbus_get_baud, NULL }, +}; + +static void canbus_settings_restore (void) +{ + settings.canbus_baud = CANBUS_BAUDRATE; + + settings_write_global(); +} + +static void canbus_settings_load (void) +{ + canbus_start(baud[canbus_get_baud(Setting_CANbus_BaudRate)]); +} + +static setting_details_t setting_details = { + .groups = canbus_groups, + .n_groups = sizeof(canbus_groups) / sizeof(setting_group_detail_t), + .settings = canbus_setting_detail, + .n_settings = sizeof(canbus_setting_detail) / sizeof(setting_detail_t), + .save = settings_write_global, + .load = canbus_settings_load, + .restore = canbus_settings_restore +}; + +// Public API + +bool canbus_enabled (void) +{ + return isEnabled; +} + +bool canbus_queue_tx (canbus_message_t message, bool ext_id) +{ + bool ok; + uint8_t next_head = (tx_buffer.head + 1) % CANBUS_BUFFER_LEN; + + if((ok = next_head != tx_buffer.tail)) { + tx_buffer.tx[tx_buffer.head].ext_id = ext_id; + tx_buffer.tx[tx_buffer.head].message = message; + tx_buffer.head = next_head; + } + + return ok; +} + +bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback) +{ + return can_add_filter(id, mask, ext_id, callback); +} + +void canbus_init (void) +{ + static bool init_ok = false; + + if(!init_ok) { + init_ok = true; + settings_register(&setting_details); + } +} diff --git a/canbus.h b/canbus.h new file mode 100644 index 0000000..f402f26 --- /dev/null +++ b/canbus.h @@ -0,0 +1,48 @@ +/* + + canbus.h - + + Part of grblHAL + + Copyright (c) 2022 Jon Escombe + 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 . + +*/ + +#ifndef _CANBUS_H_ +#define _CANBUS_H_ + +#include +#include + +typedef struct { + uint32_t id; + uint8_t len; + uint8_t data[8]; +} canbus_message_t; + +typedef bool (*can_rx_ptr)(canbus_message_t); +typedef bool (*can_rx_enqueue_fn)(canbus_message_t msg, can_rx_ptr callback); // used internally by the driver + +/* + * Function prototypes + */ +void canbus_init (void); +bool canbus_enabled (void); +bool canbus_queue_tx (canbus_message_t message, bool ext_id); +bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback); + +#endif /* _CANBUS_H_ */ diff --git a/changelog.md b/changelog.md index 271bf13..7f93d3d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,23 @@ ## grblHAL changelog +Build 20240704 + +Core: + +* Added high level CANbus API for plugin use. If driver/board combo provides the required lowlevel HAL API the `NEWOPT` string in the `$I` output will contain the `CAN` element when CAN is enabled. +Ref. [issue #179](https://github.com/grblHAL/STM32F4xx/issues/179). + +* Added soft limits check for corexy kinematics, ref. [discussion #536](https://github.com/grblHAL/core/discussions/536). + +Drivers: + +* ESP32: fixed WebUI regression. Ref. [issue #116](https://github.com/grblHAL/ESP32/issues/116). + +* STM32F7xx, STM32F4xx: Added lowlevel CANbus API and enabled it for some boards. + +--- + + Build 20240624 Core: diff --git a/grbl.h b/grbl.h index 88e7d18..fdc0090 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20240624 +#define GRBL_BUILD 20240704 #define GRBL_URL "https://github.com/grblHAL" diff --git a/kinematics/corexy.c b/kinematics/corexy.c index 9350bdf..be35bbb 100644 --- a/kinematics/corexy.c +++ b/kinematics/corexy.c @@ -37,6 +37,7 @@ #define B_MOTOR Y_AXIS // Must be Y_AXIS static on_report_options_ptr on_report_options; +static travel_limits_ptr check_travel_limits; // Returns x or y-axis "steps" based on CoreXY motor steps. inline static int32_t corexy_convert_to_a_motor_steps (int32_t *steps) @@ -77,12 +78,25 @@ static inline float *transform_from_cartesian (float *target, float *position) return target; } +// Transform position from motor (corexy) coordinate system to cartesian coordinate system +static inline float *transform_to_cartesian (float *target, float *position) +{ + uint_fast8_t idx; + + target[X_AXIS] = (position[X_AXIS] + position[Y_AXIS]) * 0.5f; + target[Y_AXIS] = (position[X_AXIS] - position[Y_AXIS]) * 0.5f; + + for(idx = Z_AXIS; idx < N_AXIS; idx++) + target[idx] = position[idx]; + + return target; +} + static uint_fast8_t corexy_limits_get_axis_mask (uint_fast8_t idx) { return ((idx == A_MOTOR) || (idx == B_MOTOR)) ? (bit(X_AXIS) | bit(Y_AXIS)) : bit(idx); } - static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name? { int32_t axis_position; @@ -102,6 +116,19 @@ static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name? } } +// Checks and reports if target array exceeds machine travel limits. Returns false if check failed. +// NOTE: target for axes X and Y are in motor coordinates if is_cartesian is false. +static bool corexy_check_travel_limits (float *target, axes_signals_t axes, bool is_cartesian) +{ + if(is_cartesian) + return check_travel_limits(target, axes, true); + + float cartesian_coords[N_AXIS]; + + transform_to_cartesian(cartesian_coords, target); + + return check_travel_limits(cartesian_coords, axes, true); +} // Set machine positions for homed limit switches. Don't update non-homed axes. // NOTE: settings.max_travel[] is stored as a negative value. @@ -207,7 +234,7 @@ static void report_options (bool newopt) on_report_options(newopt); if(!newopt) - hal.stream.write("[KINEMATICS:CoreXY v2.00]" ASCII_EOL); + hal.stream.write("[KINEMATICS:CoreXY v2.01]" ASCII_EOL); } // Initialize API pointers for CoreXY kinematics @@ -222,6 +249,9 @@ void corexy_init (void) kinematics.homing_cycle_validate = homing_cycle_validate; kinematics.homing_cycle_get_feedrate = homing_cycle_get_feedrate; + check_travel_limits = grbl.check_travel_limits; + grbl.check_travel_limits = corexy_check_travel_limits; + on_report_options = grbl.on_report_options; grbl.on_report_options = report_options; } diff --git a/report.c b/report.c index aafa32c..edbd558 100644 --- a/report.c +++ b/report.c @@ -38,6 +38,7 @@ #include "nvs_buffer.h" #include "machine_limits.h" #include "state_machine.h" +#include "canbus.h" #include "regex.h" #if ENABLE_SPINDLE_LINEARIZATION @@ -985,6 +986,9 @@ void report_build_info (char *line, bool extended) if(hal.rtc.get_datetime) strcat(buf, "RTC,"); + if(canbus_enabled()) + strcat(buf, "CAN,"); + #ifdef PID_LOG strcat(buf, "PID,"); #endif diff --git a/settings.h b/settings.h index 8c7710b..eb799f5 100644 --- a/settings.h +++ b/settings.h @@ -779,7 +779,8 @@ typedef struct { control_signals_t control_disable_pullup; coolant_state_t coolant_invert; axes_signals_t home_invert; - uint16_t hole_1; + uint8_t modbus_baud; + uint8_t canbus_baud; spindle_settings_t spindle; stepper_settings_t steppers; reportmask_t status_report; // Mask to indicate desired report data.