diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 50f64359a3916..9b6bf82387b41 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -85,6 +85,7 @@ add_subdirectory_ifdef(CONFIG_SERIAL serial) add_subdirectory_ifdef(CONFIG_SMBUS smbus) add_subdirectory_ifdef(CONFIG_SPI spi) add_subdirectory_ifdef(CONFIG_STEPPER stepper) +add_subdirectory_ifdef(CONFIG_STEPPER_CONTROL stepper_control) add_subdirectory_ifdef(CONFIG_SYSCON syscon) add_subdirectory_ifdef(CONFIG_SYS_CLOCK_EXISTS timer) add_subdirectory_ifdef(CONFIG_TEE tee) diff --git a/drivers/Kconfig b/drivers/Kconfig index 401220c49177a..c7cdfdbd85be6 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -83,6 +83,7 @@ source "drivers/sip_svc/Kconfig" source "drivers/smbus/Kconfig" source "drivers/spi/Kconfig" source "drivers/stepper/Kconfig" +source "drivers/stepper_control/Kconfig" source "drivers/syscon/Kconfig" source "drivers/timer/Kconfig" source "drivers/usb/Kconfig" diff --git a/drivers/stepper/CMakeLists.txt b/drivers/stepper/CMakeLists.txt index 1cda468fac484..765f8f6b572e1 100644 --- a/drivers/stepper/CMakeLists.txt +++ b/drivers/stepper/CMakeLists.txt @@ -14,5 +14,5 @@ zephyr_library() zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_FAKE_STEPPER fake_stepper_controller.c) -zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_controller.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_driver.c) zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c) diff --git a/drivers/stepper/Kconfig b/drivers/stepper/Kconfig index 075c9130e33f0..eaab76614f0fd 100644 --- a/drivers/stepper/Kconfig +++ b/drivers/stepper/Kconfig @@ -2,9 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig STEPPER - bool "Stepper Controller" + bool "Stepper Driver" help - Enable stepper controller + Enable stepper driver if STEPPER diff --git a/drivers/stepper/adi_tmc/CMakeLists.txt b/drivers/stepper/adi_tmc/CMakeLists.txt index c16479183f726..0720085e130c2 100644 --- a/drivers/stepper/adi_tmc/CMakeLists.txt +++ b/drivers/stepper/adi_tmc/CMakeLists.txt @@ -4,7 +4,6 @@ zephyr_library() zephyr_library_property(ALLOW_EMPTY TRUE) -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c) zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC2209 tmc22xx.c) -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx.c) +zephyr_library_sources_ifdef(CONFIG_ADI_TMC5XXX_STEPPER_DRIVER tmc5xxx_stepper_driver.c) zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx.c) diff --git a/drivers/stepper/adi_tmc/Kconfig b/drivers/stepper/adi_tmc/Kconfig index e8a86e6a43931..2077f96743b4e 100644 --- a/drivers/stepper/adi_tmc/Kconfig +++ b/drivers/stepper/adi_tmc/Kconfig @@ -15,12 +15,11 @@ config STEPPER_ADI_TMC_SPI depends on STEPPER_ADI_TMC select SPI help - A Trinamic Stepper Controller with SPI is enabled + A Trinamic Stepper Driver with SPI is enabled comment "Trinamic Stepper Drivers" rsource "Kconfig.tmc22xx" -rsource "Kconfig.tmc50xx" -rsource "Kconfig.tmc51xx" +rsource "Kconfig.tmc5xxx" endif # STEPPER_ADI_TMC diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc5xxx b/drivers/stepper/adi_tmc/Kconfig.tmc5xxx new file mode 100644 index 0000000000000..ca373d32012cd --- /dev/null +++ b/drivers/stepper/adi_tmc/Kconfig.tmc5xxx @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +config ADI_TMC5XXX_STEPPER_DRIVER + bool "Activate trinamic tmc5xxx stepper driver" + depends on DT_HAS_ADI_TMC5XXX_STEPPER_DRIVER_ENABLED + select STEPPER_ADI_TMC_SPI + default y diff --git a/drivers/stepper/adi_tmc/tmc22xx.c b/drivers/stepper/adi_tmc/tmc22xx.c index fb73dc35fc28d..137ec98127002 100644 --- a/drivers/stepper/adi_tmc/tmc22xx.c +++ b/drivers/stepper/adi_tmc/tmc22xx.c @@ -19,18 +19,17 @@ struct tmc22xx_config { }; struct tmc22xx_data { - struct step_dir_stepper_common_data common; enum stepper_micro_step_resolution resolution; }; -STEP_DIR_STEPPER_STRUCT_CHECK(struct tmc22xx_config, struct tmc22xx_data); +STEP_DIR_STEPPER_STRUCT_CHECK(struct tmc22xx_config); static int tmc22xx_stepper_enable(const struct device *dev) { const struct tmc22xx_config *config = dev->config; LOG_DBG("Enabling Stepper motor controller %s", dev->name); - return gpio_pin_set_dt(&config->enable_pin, 1); + return gpio_pin_set_dt(&config->enable_pin, 0); } static int tmc22xx_stepper_disable(const struct device *dev) @@ -38,7 +37,7 @@ static int tmc22xx_stepper_disable(const struct device *dev) const struct tmc22xx_config *config = dev->config; LOG_DBG("Disabling Stepper motor controller %s", dev->name); - return gpio_pin_set_dt(&config->enable_pin, 0); + return gpio_pin_set_dt(&config->enable_pin, 1); } static int tmc22xx_stepper_set_micro_step_res(const struct device *dev, @@ -150,15 +149,8 @@ static int tmc22xx_stepper_init(const struct device *dev) static DEVICE_API(stepper, tmc22xx_stepper_api) = { .enable = tmc22xx_stepper_enable, .disable = tmc22xx_stepper_disable, - .move_by = step_dir_stepper_common_move_by, - .is_moving = step_dir_stepper_common_is_moving, - .set_reference_position = step_dir_stepper_common_set_reference_position, - .get_actual_position = step_dir_stepper_common_get_actual_position, - .move_to = step_dir_stepper_common_move_to, - .set_microstep_interval = step_dir_stepper_common_set_microstep_interval, - .run = step_dir_stepper_common_run, - .stop = step_dir_stepper_common_stop, - .set_event_callback = step_dir_stepper_common_set_event_callback, + .step = step_dir_stepper_common_step, + .set_direction = step_dir_stepper_common_set_direction, .set_micro_step_res = tmc22xx_stepper_set_micro_step_res, .get_micro_step_res = tmc22xx_stepper_get_micro_step_res, }; @@ -183,7 +175,6 @@ static DEVICE_API(stepper, tmc22xx_stepper_api) = { (.msx_pins = tmc22xx_stepper_msx_pins_##inst)) \ }; \ static struct tmc22xx_data tmc22xx_data_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \ .resolution = DT_INST_PROP(inst, micro_step_res), \ }; \ DEVICE_DT_INST_DEFINE(inst, tmc22xx_stepper_init, NULL, &tmc22xx_data_##inst, \ diff --git a/drivers/stepper/adi_tmc/tmc50xx.c b/drivers/stepper/adi_tmc/tmc50xx.c deleted file mode 100644 index b9eaafa70be99..0000000000000 --- a/drivers/stepper/adi_tmc/tmc50xx.c +++ /dev/null @@ -1,762 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG - * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT adi_tmc50xx - -#include - -#include -#include - -#include "adi_tmc_spi.h" -#include "adi_tmc5xxx_common.h" - -#include -LOG_MODULE_REGISTER(tmc50xx, CONFIG_STEPPER_LOG_LEVEL); - -struct tmc50xx_data { - struct k_sem sem; -}; - -struct tmc50xx_config { - const uint32_t gconf; - struct spi_dt_spec spi; - const uint32_t clock_frequency; -}; - -struct tmc50xx_stepper_data { - struct k_work_delayable stallguard_dwork; - /* Work item to run the callback in a thread context. */ - struct k_work_delayable rampstat_callback_dwork; - /* device pointer required to access config in k_work */ - const struct device *stepper; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; - -struct tmc50xx_stepper_config { - const uint8_t index; - const uint16_t default_micro_step_res; - const int8_t sg_threshold; - const bool is_sg_enabled; - const uint32_t sg_velocity_check_interval_ms; - const uint32_t sg_threshold_velocity; - /* parent controller required for bus communication */ - const struct device *controller; -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - const struct tmc_ramp_generator_data default_ramp_config; -#endif -}; - -static int read_actual_position(const struct tmc50xx_stepper_config *config, int32_t *position); - -static int tmc50xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) -{ - const struct tmc50xx_config *config = dev->config; - struct tmc50xx_data *data = dev->data; - const struct spi_dt_spec bus = config->spi; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = tmc_spi_write_register(&bus, TMC5XXX_WRITE_BIT, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - return err; - } - return 0; -} - -static int tmc50xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) -{ - const struct tmc50xx_config *config = dev->config; - struct tmc50xx_data *data = dev->data; - const struct spi_dt_spec bus = config->spi; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = tmc_spi_read_register(&bus, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - return err; - } - return 0; -} - -static int tmc50xx_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct tmc50xx_stepper_data *data = dev->data; - - data->callback = callback; - data->event_cb_user_data = user_data; - return 0; -} - -static int read_vactual(const struct tmc50xx_stepper_config *config, int32_t *actual_velocity) -{ - int err; - - err = tmc50xx_read(config->controller, TMC50XX_VACTUAL(config->index), actual_velocity); - if (err) { - LOG_ERR("Failed to read VACTUAL register"); - return err; - } - - *actual_velocity = sign_extend(*actual_velocity, TMC_RAMP_VACTUAL_SHIFT); - if (actual_velocity) { - LOG_DBG("actual velocity: %d", *actual_velocity); - } - return 0; -} - -static int stallguard_enable(const struct device *dev, const bool enable) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_SWMODE(config->index), ®_value); - if (err) { - LOG_ERR("Failed to read SWMODE register"); - return -EIO; - } - - if (enable) { - reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; - - int32_t actual_velocity; - - err = read_vactual(config, &actual_velocity); - if (err) { - return -EIO; - } - if (abs(actual_velocity) < config->sg_threshold_velocity) { - return -EAGAIN; - } - } else { - reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; - } - err = tmc50xx_write(config->controller, TMC50XX_SWMODE(config->index), reg_value); - if (err) { - LOG_ERR("Failed to write SWMODE register"); - return -EIO; - } - - LOG_DBG("Stallguard %s", enable ? "enabled" : "disabled"); - return 0; -} - -static void stallguard_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct tmc50xx_stepper_data *stepper_data = - CONTAINER_OF(dwork, struct tmc50xx_stepper_data, stallguard_dwork); - int err; - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - - err = stallguard_enable(stepper_data->stepper, true); - if (err == -EAGAIN) { - k_work_reschedule(dwork, K_MSEC(stepper_config->sg_velocity_check_interval_ms)); - } - if (err == -EIO) { - LOG_ERR("Failed to enable stallguard because of I/O error"); - return; - } -} - - -static void execute_callback(const struct device *dev, const enum stepper_event event) -{ - struct tmc50xx_stepper_data *data = dev->data; - - if (!data->callback) { - LOG_WRN_ONCE("No callback registered"); - return; - } - data->callback(dev, event, data->event_cb_user_data); -} - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG - -static void log_stallguard(struct tmc50xx_stepper_data *stepper_data, const uint32_t drv_status) -{ - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - int32_t position; - int err; - - err = read_actual_position(stepper_config, &position); - if (err != 0) { - LOG_ERR("%s: Failed to read XACTUAL register", stepper_data->stepper->name); - return; - } - - const uint8_t sg_result = FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); - const bool sg_status = FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); - - LOG_DBG("%s position: %d | sg result: %3d status: %d", - stepper_data->stepper->name, position, sg_result, sg_status); -} - -#endif - -static void rampstat_work_reschedule(struct k_work_delayable *rampstat_callback_dwork) -{ - k_work_reschedule(rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -} - -static void rampstat_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - - struct tmc50xx_stepper_data *stepper_data = - CONTAINER_OF(dwork, struct tmc50xx_stepper_data, rampstat_callback_dwork); - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - - __ASSERT_NO_MSG(stepper_config->controller != NULL); - - uint32_t drv_status; - int err; - - err = tmc50xx_read(stepper_config->controller, TMC50XX_DRVSTATUS(stepper_config->index), - &drv_status); - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", stepper_data->stepper->name); - return; - } -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG - log_stallguard(stepper_data, drv_status); -#endif - if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { - LOG_INF("%s: Stall detected", stepper_data->stepper->name); - err = tmc50xx_write(stepper_config->controller, - TMC50XX_RAMPMODE(stepper_config->index), - TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - LOG_ERR("%s: Failed to stop motor", stepper_data->stepper->name); - return; - } - } - - uint32_t rampstat_value; - - err = tmc50xx_read(stepper_config->controller, TMC50XX_RAMPSTAT(stepper_config->index), - &rampstat_value); - if (err != 0) { - LOG_ERR("%s: Failed to read RAMPSTAT register", stepper_data->stepper->name); - return; - } - - const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); - - if (ramp_stat_values > 0) { - switch (ramp_stat_values) { - - case TMC5XXX_STOP_LEFT_EVENT: - LOG_DBG("RAMPSTAT %s:Left end-stop detected", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, - STEPPER_EVENT_LEFT_END_STOP_DETECTED); - break; - - case TMC5XXX_STOP_RIGHT_EVENT: - LOG_DBG("RAMPSTAT %s:Right end-stop detected", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, - STEPPER_EVENT_RIGHT_END_STOP_DETECTED); - break; - - case TMC5XXX_POS_REACHED_EVENT: - LOG_DBG("RAMPSTAT %s:Position reached", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, STEPPER_EVENT_STEPS_COMPLETED); - break; - - case TMC5XXX_STOP_SG_EVENT: - LOG_DBG("RAMPSTAT %s:Stall detected", stepper_data->stepper->name); - stallguard_enable(stepper_data->stepper, false); - execute_callback(stepper_data->stepper, STEPPER_EVENT_STALL_DETECTED); - break; - default: - LOG_ERR("Illegal ramp stat bit field"); - break; - } - } else { - rampstat_work_reschedule(&stepper_data->rampstat_callback_dwork); - } -} - -static int tmc50xx_stepper_enable(const struct device *dev) -{ - LOG_DBG("Enabling Stepper motor controller %s", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); -} - -static int tmc50xx_stepper_disable(const struct device *dev) -{ - LOG_DBG("Disabling Stepper motor controller %s", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); -} - -static int tmc50xx_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_DRVSTATUS(config->index), ®_value); - - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name); - return -EIO; - } - - *is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) != 1U); - LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving); - return 0; -} - -int tmc50xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) -{ - const struct tmc50xx_stepper_config *config = dev->config; - const struct tmc50xx_config *tmc50xx_config = config->controller->config; - const uint32_t clock_frequency = tmc50xx_config->clock_frequency; - uint32_t velocity_fclk; - int err; - - velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency); - - err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), velocity_fclk); - if (err != 0) { - LOG_ERR("%s: Failed to set max velocity", dev->name); - return -EIO; - } - return 0; -} - -static int tmc50xx_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution res) -{ - if (!VALID_MICRO_STEP_RES(res)) { - LOG_ERR("Invalid micro step resolution %d", res); - return -ENOTSUP; - } - - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; - reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res)) - << TMC5XXX_CHOPCONF_MRES_SHIFT); - - err = tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Stepper motor controller %s set micro step resolution to 0x%x", dev->name, - reg_value); - return 0; -} - -static int tmc50xx_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *res) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - reg_value &= TMC5XXX_CHOPCONF_MRES_MASK; - reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT; - *res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); - LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); - return 0; -} - -static int tmc50xx_stepper_set_reference_position(const struct device *dev, const int32_t position) -{ - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - return -EIO; - } - - err = tmc50xx_write(config->controller, TMC50XX_XACTUAL(config->index), position); - if (err != 0) { - return -EIO; - } - LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position); - return 0; -} - -static int read_actual_position(const struct tmc50xx_stepper_config *config, int32_t *position) -{ - int err; - - err = tmc50xx_read(config->controller, TMC50XX_XACTUAL(config->index), position); - if (err != 0) { - return -EIO; - } - return 0; -} - -static int tmc50xx_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = read_actual_position(config, position); - if (err != 0) { - return -EIO; - } - LOG_DBG("%s actual position: %d", dev->name, *position); - return 0; -} - -static int tmc50xx_stepper_move_to(const struct device *dev, const int32_t micro_steps) -{ - LOG_DBG("%s set target position to %d", dev->name, micro_steps); - const struct tmc50xx_stepper_config *config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - stallguard_enable(dev, false); - } - - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_POSITIONING_MODE); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_XTARGET(config->index), micro_steps); - if (err != 0) { - return -EIO; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - rampstat_work_reschedule(&data->rampstat_callback_dwork); - } - return 0; -} - -static int tmc50xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - int err; - int32_t position; - - err = stepper_get_actual_position(dev, &position); - if (err != 0) { - return -EIO; - } - int32_t target_position = position + micro_steps; - - LOG_DBG("%s moved to %d by steps: %d", dev->name, target_position, micro_steps); - - return tmc50xx_stepper_move_to(dev, target_position); -} - -static int tmc50xx_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - LOG_DBG("Stepper motor controller %s run", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - err = stallguard_enable(dev, false); - if (err != 0) { - return -EIO; - } - } - - switch (direction) { - case STEPPER_DIRECTION_POSITIVE: - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - - case STEPPER_DIRECTION_NEGATIVE: - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - rampstat_work_reschedule(&data->rampstat_callback_dwork); - } - return 0; -} - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - -int tmc50xx_stepper_set_ramp(const struct device *dev, - const struct tmc_ramp_generator_data *ramp_data) -{ - LOG_DBG("Stepper motor controller %s set ramp", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = tmc50xx_write(config->controller, TMC50XX_VSTART(config->index), ramp_data->vstart); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_A1(config->index), ramp_data->a1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_AMAX(config->index), ramp_data->amax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_D1(config->index), ramp_data->d1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_DMAX(config->index), ramp_data->dmax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_V1(config->index), ramp_data->v1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), ramp_data->vmax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VSTOP(config->index), ramp_data->vstop); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_TZEROWAIT(config->index), - ramp_data->tzerowait); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VHIGH(config->index), ramp_data->vhigh); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VCOOLTHRS(config->index), - ramp_data->vcoolthrs); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_IHOLD_IRUN(config->index), - ramp_data->iholdrun); - if (err != 0) { - return -EIO; - } - return 0; -} - -#endif - -static int tmc50xx_init(const struct device *dev) -{ - LOG_DBG("TMC50XX stepper motor controller %s initialized", dev->name); - struct tmc50xx_data *data = dev->data; - const struct tmc50xx_config *config = dev->config; - int err; - - k_sem_init(&data->sem, 1, 1); - - if (!spi_is_ready_dt(&config->spi)) { - LOG_ERR("SPI bus is not ready"); - return -ENODEV; - } - - /* Init non motor-index specific registers here. */ - LOG_DBG("GCONF: %d", config->gconf); - err = tmc50xx_write(dev, TMC5XXX_GCONF, config->gconf); - if (err != 0) { - return -EIO; - } - - /* Read GSTAT register values to clear any errors SPI Datagram. */ - uint32_t gstat_value; - - err = tmc50xx_read(dev, TMC5XXX_GSTAT, &gstat_value); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Device %s initialized", dev->name); - return 0; -} - -static int tmc50xx_stepper_init(const struct device *dev) -{ - const struct tmc50xx_stepper_config *stepper_config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - LOG_DBG("Controller: %s, Stepper: %s", stepper_config->controller->name, dev->name); - - if (stepper_config->is_sg_enabled) { - k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); - - err = tmc50xx_write(stepper_config->controller, - TMC50XX_SWMODE(stepper_config->index), BIT(10)); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Setting stall guard to %d with delay %d ms", stepper_config->sg_threshold, - stepper_config->sg_velocity_check_interval_ms); - if (!IN_RANGE(stepper_config->sg_threshold, TMC5XXX_SG_MIN_VALUE, - TMC5XXX_SG_MAX_VALUE)) { - LOG_ERR("Stallguard threshold out of range"); - return -EINVAL; - } - - int32_t stall_guard_threshold = (int32_t)stepper_config->sg_threshold; - - err = tmc50xx_write( - stepper_config->controller, TMC50XX_COOLCONF(stepper_config->index), - stall_guard_threshold << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); - if (err != 0) { - return -EIO; - } - k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); - } - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - err = tmc50xx_stepper_set_ramp(dev, &stepper_config->default_ramp_config); - if (err != 0) { - return -EIO; - } -#endif - - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - rampstat_work_reschedule(&data->rampstat_callback_dwork); - err = tmc50xx_stepper_set_micro_step_res(dev, stepper_config->default_micro_step_res); - if (err != 0) { - return -EIO; - } - return 0; -} - -static DEVICE_API(stepper, tmc50xx_stepper_api) = { - .enable = tmc50xx_stepper_enable, - .disable = tmc50xx_stepper_disable, - .is_moving = tmc50xx_stepper_is_moving, - .move_by = tmc50xx_stepper_move_by, - .set_micro_step_res = tmc50xx_stepper_set_micro_step_res, - .get_micro_step_res = tmc50xx_stepper_get_micro_step_res, - .set_reference_position = tmc50xx_stepper_set_reference_position, - .get_actual_position = tmc50xx_stepper_get_actual_position, - .move_to = tmc50xx_stepper_move_to, - .run = tmc50xx_stepper_run, - .set_event_callback = tmc50xx_stepper_set_event_callback, -}; - -#define TMC50XX_SHAFT_CONFIG(child) \ - (DT_PROP(child, invert_direction) << TMC50XX_GCONF_SHAFT_SHIFT(DT_REG_ADDR(child))) | - -#define TMC50XX_STEPPER_CONFIG_DEFINE(child) \ - COND_CODE_1(DT_PROP_EXISTS(child, stallguard_threshold_velocity), \ - BUILD_ASSERT(DT_PROP(child, stallguard_threshold_velocity), \ - "stallguard threshold velocity must be a positive value"), ()); \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \ - static const struct tmc50xx_stepper_config tmc50xx_stepper_config_##child = { \ - .controller = DEVICE_DT_GET(DT_PARENT(child)), \ - .default_micro_step_res = DT_PROP(child, micro_step_res), \ - .index = DT_REG_ADDR(child), \ - .sg_threshold = DT_PROP(child, stallguard2_threshold), \ - .sg_threshold_velocity = DT_PROP(child, stallguard_threshold_velocity), \ - .sg_velocity_check_interval_ms = DT_PROP(child, \ - stallguard_velocity_check_interval_ms), \ - .is_sg_enabled = DT_PROP(child, activate_stallguard2), \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, \ - (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC50XX(child))) }; - -#define TMC50XX_STEPPER_DATA_DEFINE(child) \ - static struct tmc50xx_stepper_data tmc50xx_stepper_data_##child = { \ - .stepper = DEVICE_DT_GET(child),}; - -#define TMC50XX_STEPPER_DEFINE(child) \ - DEVICE_DT_DEFINE(child, tmc50xx_stepper_init, NULL, &tmc50xx_stepper_data_##child, \ - &tmc50xx_stepper_config_##child, POST_KERNEL, \ - CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); - -#define TMC50XX_DEFINE(inst) \ - BUILD_ASSERT(DT_INST_CHILD_NUM(inst) <= 2, "tmc50xx can drive two steppers at max"); \ - BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ - "clock frequency must be non-zero positive value"); \ - static struct tmc50xx_data tmc50xx_data_##inst; \ - static const struct tmc50xx_config tmc50xx_config_##inst = { \ - .gconf = ( \ - (DT_INST_PROP(inst, poscmp_enable) << TMC50XX_GCONF_POSCMP_ENABLE_SHIFT) | \ - (DT_INST_PROP(inst, test_mode) << TMC50XX_GCONF_TEST_MODE_SHIFT) | \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_SHAFT_CONFIG) \ - (DT_INST_PROP(inst, lock_gconf) << TMC50XX_LOCK_GCONF_SHIFT)), \ - .spi = SPI_DT_SPEC_INST_GET(inst, (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \ - SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8)), 0), \ - .clock_frequency = DT_INST_PROP(inst, clock_frequency),}; \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_CONFIG_DEFINE); \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_DATA_DEFINE); \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_DEFINE); \ - DEVICE_DT_INST_DEFINE(inst, tmc50xx_init, NULL, &tmc50xx_data_##inst, \ - &tmc50xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,\ - NULL); - -DT_INST_FOREACH_STATUS_OKAY(TMC50XX_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc5xxx_stepper_driver.c b/drivers/stepper/adi_tmc/tmc5xxx_stepper_driver.c new file mode 100644 index 0000000000000..64cd4b36d2a26 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx_stepper_driver.c @@ -0,0 +1,149 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_tmc5xxx_stepper_driver + +#include + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(tmc50xx_stepper_driver, CONFIG_STEPPER_LOG_LEVEL); + +struct tmc5xxx_stepper_config { + const uint8_t index; + const uint16_t default_micro_step_res; + /* parent controller required for bus communication, device pointer to tmc50xx */ + const struct device *controller; +}; + +static int tmc50xx_stepper_enable(const struct device *dev) +{ + const struct tmc5xxx_stepper_config *config = dev->config; + uint32_t reg_value; + int err; + + LOG_DBG("Enabling Stepper driver %s for controller %s", dev->name, + config->controller->name); + + err = stepper_controller_read(config->controller, TMC50XX_CHOPCONF(config->index), + ®_value); + if (err != 0) { + return -EIO; + } + + reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return stepper_controller_write(config->controller, TMC50XX_CHOPCONF(config->index), + reg_value); +} + +static int tmc50xx_stepper_disable(const struct device *dev) +{ + LOG_DBG("Disabling Stepper motor controller %s", dev->name); + const struct tmc5xxx_stepper_config *config = dev->config; + uint32_t reg_value; + int err; + + err = stepper_controller_read(config->controller, TMC50XX_CHOPCONF(config->index), + ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return stepper_controller_write(config->controller, TMC50XX_CHOPCONF(config->index), + reg_value); +} + +static int tmc50xx_stepper_set_micro_step_res(const struct device *dev, + enum stepper_micro_step_resolution res) +{ + if (!VALID_MICRO_STEP_RES(res)) { + LOG_ERR("Invalid micro step resolution %d", res); + return -ENOTSUP; + } + + const struct tmc5xxx_stepper_config *config = dev->config; + uint32_t reg_value; + int err; + + err = stepper_controller_read(config->controller, TMC50XX_CHOPCONF(config->index), + ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; + reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res)) + << TMC5XXX_CHOPCONF_MRES_SHIFT); + + err = stepper_controller_write(config->controller, TMC50XX_CHOPCONF(config->index), + reg_value); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Stepper motor controller %s set micro step resolution to 0x%x", dev->name, + reg_value); + return 0; +} + +static int tmc50xx_stepper_get_micro_step_res(const struct device *dev, + enum stepper_micro_step_resolution *res) +{ + const struct tmc5xxx_stepper_config *config = dev->config; + uint32_t reg_value; + int err; + + err = stepper_controller_read(config->controller, TMC50XX_CHOPCONF(config->index), + ®_value); + if (err != 0) { + return -EIO; + } + reg_value &= TMC5XXX_CHOPCONF_MRES_MASK; + reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT; + *res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); + LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); + return 0; +} + +static int tmc50xx_stepper_init(const struct device *dev) +{ + const struct tmc5xxx_stepper_config *stepper_config = dev->config; + int err; + + LOG_DBG("Controller: %s, Stepper: %s", stepper_config->controller->name, dev->name); + + err = tmc50xx_stepper_set_micro_step_res(dev, stepper_config->default_micro_step_res); + if (err != 0) { + return -EIO; + } + return 0; +} + +static DEVICE_API(stepper, tmc50xx_stepper_api) = { + .enable = tmc50xx_stepper_enable, + .disable = tmc50xx_stepper_disable, + .set_micro_step_res = tmc50xx_stepper_set_micro_step_res, + .get_micro_step_res = tmc50xx_stepper_get_micro_step_res, +}; + +#define TMC5XXX_STEPPER_DRIVER_DEFINE(inst) \ + static const struct tmc5xxx_stepper_config tmc5xxx_stepper_config_##inst = { \ + .controller = DEVICE_DT_GET(DT_GPARENT(DT_DRV_INST(inst))), \ + .default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \ + .index = DT_REG_ADDR(DT_PARENT(DT_DRV_INST(inst))), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, tmc50xx_stepper_init, NULL, NULL, \ + &tmc5xxx_stepper_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC5XXX_STEPPER_DRIVER_DEFINE) diff --git a/drivers/stepper/allegro/a4979.c b/drivers/stepper/allegro/a4979.c index f08dbd61d37bf..8aa31d7bd6920 100644 --- a/drivers/stepper/allegro/a4979.c +++ b/drivers/stepper/allegro/a4979.c @@ -93,7 +93,6 @@ static int a4979_stepper_disable(const struct device *dev) return ret; } - config->common.timing_source->stop(dev); data->enabled = false; return 0; @@ -152,42 +151,6 @@ static int a4979_stepper_get_micro_step_res(const struct device *dev, return 0; } -static int a4979_move_to(const struct device *dev, int32_t target) -{ - struct a4979_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to move to target position, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_move_to(dev, target); -} - -static int a4979_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - struct a4979_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to move by delta, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_move_by(dev, micro_steps); -} - -static int a4979_run(const struct device *dev, enum stepper_direction direction) -{ - struct a4979_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to run stepper, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_run(dev, direction); -} - static int a4979_init(const struct device *dev) { const struct a4979_config *config = dev->config; @@ -268,17 +231,10 @@ static int a4979_init(const struct device *dev) static DEVICE_API(stepper, a4979_stepper_api) = { .enable = a4979_stepper_enable, .disable = a4979_stepper_disable, - .move_by = a4979_stepper_move_by, - .move_to = a4979_move_to, - .is_moving = step_dir_stepper_common_is_moving, - .set_reference_position = step_dir_stepper_common_set_reference_position, - .get_actual_position = step_dir_stepper_common_get_actual_position, - .set_microstep_interval = step_dir_stepper_common_set_microstep_interval, - .run = a4979_run, - .stop = step_dir_stepper_common_stop, .set_micro_step_res = a4979_stepper_set_micro_step_res, .get_micro_step_res = a4979_stepper_get_micro_step_res, - .set_event_callback = step_dir_stepper_common_set_event_callback, + .step = step_dir_stepper_common_step, + .set_direction = step_dir_stepper_common_set_direction, }; #define A4979_DEVICE(inst) \ diff --git a/drivers/stepper/gpio_stepper_controller.c b/drivers/stepper/gpio_stepper_controller.c deleted file mode 100644 index 331d2413ec1d6..0000000000000 --- a/drivers/stepper/gpio_stepper_controller.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG - * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT zephyr_gpio_stepper - -#include -#include -#include -#include -#include - -#include -LOG_MODULE_REGISTER(gpio_stepper_motor_controller, CONFIG_STEPPER_LOG_LEVEL); - -#define MAX_MICRO_STEP_RES STEPPER_MICRO_STEP_2 -#define NUM_CONTROL_PINS 4 - -static const uint8_t - half_step_lookup_table[NUM_CONTROL_PINS * MAX_MICRO_STEP_RES][NUM_CONTROL_PINS] = { - {1u, 1u, 0u, 0u}, {0u, 1u, 0u, 0u}, {0u, 1u, 1u, 0u}, {0u, 0u, 1u, 0u}, - {0u, 0u, 1u, 1u}, {0u, 0u, 0u, 1u}, {1u, 0u, 0u, 1u}, {1u, 0u, 0u, 0u}}; - -struct gpio_stepper_config { - const struct gpio_dt_spec *control_pins; - bool invert_direction; -}; - -struct gpio_stepper_data { - const struct device *dev; - struct k_spinlock lock; - enum stepper_direction direction; - enum stepper_run_mode run_mode; - uint8_t step_gap; - uint8_t coil_charge; - struct k_work_delayable stepper_dwork; - int32_t actual_position; - uint64_t delay_in_ns; - int32_t step_count; - bool is_enabled; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; - -static int stepper_motor_set_coil_charge(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - const struct gpio_stepper_config *config = dev->config; - - for (int i = 0; i < NUM_CONTROL_PINS; i++) { - (void)gpio_pin_set_dt(&config->control_pins[i], - half_step_lookup_table[data->coil_charge][i]); - } - return 0; -} - -static void increment_coil_charge(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - if (data->coil_charge == NUM_CONTROL_PINS * MAX_MICRO_STEP_RES - data->step_gap) { - data->coil_charge = 0; - } else { - data->coil_charge = data->coil_charge + data->step_gap; - } -} - -static void decrement_coil_charge(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - if (data->coil_charge == 0) { - data->coil_charge = NUM_CONTROL_PINS * MAX_MICRO_STEP_RES - data->step_gap; - } else { - data->coil_charge = data->coil_charge - data->step_gap; - } -} - -static int energize_coils(const struct device *dev, const bool energized) -{ - const struct gpio_stepper_config *config = dev->config; - - for (int i = 0; i < NUM_CONTROL_PINS; i++) { - const int err = gpio_pin_set_dt(&config->control_pins[i], energized); - - if (err != 0) { - LOG_ERR("Failed to power down coil %d", i); - return err; - } - } - return 0; -} - -static void update_coil_charge(const struct device *dev) -{ - const struct gpio_stepper_config *config = dev->config; - struct gpio_stepper_data *data = dev->data; - - if (data->direction == STEPPER_DIRECTION_POSITIVE) { - config->invert_direction ? decrement_coil_charge(dev) : increment_coil_charge(dev); - data->actual_position++; - } else if (data->direction == STEPPER_DIRECTION_NEGATIVE) { - config->invert_direction ? increment_coil_charge(dev) : decrement_coil_charge(dev); - data->actual_position--; - } -} - -static void update_remaining_steps(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - if (data->step_count > 0) { - data->step_count--; - } else if (data->step_count < 0) { - data->step_count++; - } -} - -static void update_direction_from_step_count(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - if (data->step_count > 0) { - data->direction = STEPPER_DIRECTION_POSITIVE; - } else if (data->step_count < 0) { - data->direction = STEPPER_DIRECTION_NEGATIVE; - } else { - LOG_ERR("Step count is zero"); - } -} - -static void position_mode_task(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - update_remaining_steps(dev); - (void)stepper_motor_set_coil_charge(dev); - update_coil_charge(dev); - if (data->step_count) { - (void)k_work_reschedule(&data->stepper_dwork, K_NSEC(data->delay_in_ns)); - } else { - if (data->callback) { - data->callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED, - data->event_cb_user_data); - } - (void)k_work_cancel_delayable(&data->stepper_dwork); - } -} - -static void velocity_mode_task(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - - (void)stepper_motor_set_coil_charge(dev); - update_coil_charge(dev); - (void)k_work_reschedule(&data->stepper_dwork, K_NSEC(data->delay_in_ns)); -} - -static void stepper_work_step_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct gpio_stepper_data *data = - CONTAINER_OF(dwork, struct gpio_stepper_data, stepper_dwork); - - K_SPINLOCK(&data->lock) { - switch (data->run_mode) { - case STEPPER_RUN_MODE_POSITION: - position_mode_task(data->dev); - break; - case STEPPER_RUN_MODE_VELOCITY: - velocity_mode_task(data->dev); - break; - default: - LOG_WRN("Unsupported run mode %d", data->run_mode); - break; - } - } -} - -static int gpio_stepper_move_by(const struct device *dev, int32_t micro_steps) -{ - struct gpio_stepper_data *data = dev->data; - - if (!data->is_enabled) { - LOG_ERR("Stepper motor is not enabled"); - return -ECANCELED; - } - - if (data->delay_in_ns == 0) { - LOG_ERR("Step interval not set or invalid step interval set"); - return -EINVAL; - } - K_SPINLOCK(&data->lock) { - data->run_mode = STEPPER_RUN_MODE_POSITION; - data->step_count = micro_steps; - update_direction_from_step_count(dev); - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); - } - return 0; -} - -static int gpio_stepper_set_reference_position(const struct device *dev, int32_t position) -{ - struct gpio_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - data->actual_position = position; - } - return 0; -} - -static int gpio_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - struct gpio_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - *position = data->actual_position; - } - return 0; -} - -static int gpio_stepper_move_to(const struct device *dev, int32_t micro_steps) -{ - struct gpio_stepper_data *data = dev->data; - int32_t steps_to_move; - - K_SPINLOCK(&data->lock) { - steps_to_move = micro_steps - data->actual_position; - } - return gpio_stepper_move_by(dev, steps_to_move); -} - -static int gpio_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - struct gpio_stepper_data *data = dev->data; - - *is_moving = k_work_delayable_is_pending(&data->stepper_dwork); - LOG_DBG("Motor is %s moving", *is_moving ? "" : "not"); - return 0; -} - -static int gpio_stepper_set_microstep_interval(const struct device *dev, - uint64_t microstep_interval_ns) -{ - struct gpio_stepper_data *data = dev->data; - - if (microstep_interval_ns == 0) { - LOG_ERR("Step interval is invalid."); - return -EINVAL; - } - - K_SPINLOCK(&data->lock) { - data->delay_in_ns = microstep_interval_ns; - } - LOG_DBG("Setting Motor step interval to %llu", microstep_interval_ns); - return 0; -} - -static int gpio_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - struct gpio_stepper_data *data = dev->data; - - if (!data->is_enabled) { - LOG_ERR("Stepper motor is not enabled"); - return -ECANCELED; - } - - K_SPINLOCK(&data->lock) { - data->run_mode = STEPPER_RUN_MODE_VELOCITY; - data->direction = direction; - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); - } - return 0; -} - -static int gpio_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution micro_step_res) -{ - struct gpio_stepper_data *data = dev->data; - int err = 0; - - K_SPINLOCK(&data->lock) { - switch (micro_step_res) { - case STEPPER_MICRO_STEP_1: - case STEPPER_MICRO_STEP_2: - data->step_gap = MAX_MICRO_STEP_RES >> (micro_step_res - 1); - break; - default: - LOG_ERR("Unsupported micro step resolution %d", micro_step_res); - err = -ENOTSUP; - } - } - return err; -} - -static int gpio_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *micro_step_res) -{ - struct gpio_stepper_data *data = dev->data; - *micro_step_res = MAX_MICRO_STEP_RES >> (data->step_gap - 1); - return 0; -} - -static int gpio_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct gpio_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - data->callback = callback; - } - data->event_cb_user_data = user_data; - return 0; -} - -static int gpio_stepper_enable(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - int err; - - if (data->is_enabled) { - LOG_WRN("Stepper motor is already enabled"); - return 0; - } - - K_SPINLOCK(&data->lock) { - err = energize_coils(dev, true); - if (err == 0) { - data->is_enabled = true; - } - } - return err; -} - -static int gpio_stepper_disable(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - int err; - - K_SPINLOCK(&data->lock) { - (void)k_work_cancel_delayable(&data->stepper_dwork); - err = energize_coils(dev, false); - if (err == 0) { - data->is_enabled = false; - } - } - return err; -} - -static int gpio_stepper_stop(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - int err; - - K_SPINLOCK(&data->lock) { - (void)k_work_cancel_delayable(&data->stepper_dwork); - err = energize_coils(dev, true); - - if (data->callback && !err) { - data->callback(data->dev, STEPPER_EVENT_STOPPED, data->event_cb_user_data); - } - } - return err; -} - -static int gpio_stepper_init(const struct device *dev) -{ - struct gpio_stepper_data *data = dev->data; - const struct gpio_stepper_config *config = dev->config; - - data->dev = dev; - LOG_DBG("Initializing %s gpio_stepper with %d pin", dev->name, NUM_CONTROL_PINS); - for (uint8_t n_pin = 0; n_pin < NUM_CONTROL_PINS; n_pin++) { - (void)gpio_pin_configure_dt(&config->control_pins[n_pin], GPIO_OUTPUT_INACTIVE); - } - k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); - return 0; -} - -static DEVICE_API(stepper, gpio_stepper_api) = { - .enable = gpio_stepper_enable, - .disable = gpio_stepper_disable, - .set_micro_step_res = gpio_stepper_set_micro_step_res, - .get_micro_step_res = gpio_stepper_get_micro_step_res, - .set_reference_position = gpio_stepper_set_reference_position, - .get_actual_position = gpio_stepper_get_actual_position, - .set_event_callback = gpio_stepper_set_event_callback, - .set_microstep_interval = gpio_stepper_set_microstep_interval, - .move_by = gpio_stepper_move_by, - .move_to = gpio_stepper_move_to, - .run = gpio_stepper_run, - .stop = gpio_stepper_stop, - .is_moving = gpio_stepper_is_moving, -}; - -#define GPIO_STEPPER_DEFINE(inst) \ - static const struct gpio_dt_spec gpio_stepper_motor_control_pins_##inst[] = { \ - DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \ - }; \ - BUILD_ASSERT(ARRAY_SIZE(gpio_stepper_motor_control_pins_##inst) == 4, \ - "gpio_stepper_controller driver currently supports only 4 wire configuration"); \ - static const struct gpio_stepper_config gpio_stepper_config_##inst = { \ - .invert_direction = DT_INST_PROP(inst, invert_direction), \ - .control_pins = gpio_stepper_motor_control_pins_##inst}; \ - static struct gpio_stepper_data gpio_stepper_data_##inst = { \ - .step_gap = MAX_MICRO_STEP_RES >> (DT_INST_PROP(inst, micro_step_res) - 1), \ - }; \ - BUILD_ASSERT(DT_INST_PROP(inst, micro_step_res) <= STEPPER_MICRO_STEP_2, \ - "gpio_stepper_controller driver supports up to 2 micro steps"); \ - DEVICE_DT_INST_DEFINE(inst, gpio_stepper_init, NULL, &gpio_stepper_data_##inst, \ - &gpio_stepper_config_##inst, POST_KERNEL, \ - CONFIG_STEPPER_INIT_PRIORITY, &gpio_stepper_api); - -DT_INST_FOREACH_STATUS_OKAY(GPIO_STEPPER_DEFINE) diff --git a/drivers/stepper/gpio_stepper_driver.c b/drivers/stepper/gpio_stepper_driver.c new file mode 100644 index 0000000000000..6a96f793d0257 --- /dev/null +++ b/drivers/stepper/gpio_stepper_driver.c @@ -0,0 +1,234 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_gpio_stepper + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(gpio_stepper_motor_controller, CONFIG_STEPPER_LOG_LEVEL); + +#define MAX_MICRO_STEP_RES STEPPER_MICRO_STEP_2 +#define NUM_CONTROL_PINS 4 + +static const uint8_t + half_step_lookup_table[NUM_CONTROL_PINS * MAX_MICRO_STEP_RES][NUM_CONTROL_PINS] = { + {1u, 1u, 0u, 0u}, {0u, 1u, 0u, 0u}, {0u, 1u, 1u, 0u}, {0u, 0u, 1u, 0u}, + {0u, 0u, 1u, 1u}, {0u, 0u, 0u, 1u}, {1u, 0u, 0u, 1u}, {1u, 0u, 0u, 0u}}; + +struct gpio_stepper_config { + const struct gpio_dt_spec *control_pins; + bool invert_direction; +}; + +struct gpio_stepper_data { + struct k_spinlock lock; + enum stepper_direction direction; + uint8_t step_gap; + uint8_t coil_charge; + bool is_enabled; + void *event_cb_user_data; +}; + +static int stepper_motor_set_coil_charge(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + const struct gpio_stepper_config *config = dev->config; + + for (int i = 0; i < NUM_CONTROL_PINS; i++) { + (void)gpio_pin_set_dt(&config->control_pins[i], + half_step_lookup_table[data->coil_charge][i]); + } + return 0; +} + +static void increment_coil_charge(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + + if (data->coil_charge == NUM_CONTROL_PINS * MAX_MICRO_STEP_RES - data->step_gap) { + data->coil_charge = 0; + } else { + data->coil_charge = data->coil_charge + data->step_gap; + } +} + +static void decrement_coil_charge(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + + if (data->coil_charge == 0) { + data->coil_charge = NUM_CONTROL_PINS * MAX_MICRO_STEP_RES - data->step_gap; + } else { + data->coil_charge = data->coil_charge - data->step_gap; + } +} + +static int energize_coils(const struct device *dev, const bool energized) +{ + const struct gpio_stepper_config *config = dev->config; + + for (int i = 0; i < NUM_CONTROL_PINS; i++) { + const int err = gpio_pin_set_dt(&config->control_pins[i], energized); + + if (err != 0) { + LOG_ERR("Failed to power down coil %d", i); + return err; + } + } + return 0; +} + +static void update_coil_charge(const struct device *dev) +{ + const struct gpio_stepper_config *config = dev->config; + struct gpio_stepper_data *data = dev->data; + + if (data->direction == STEPPER_DIRECTION_POSITIVE) { + config->invert_direction ? decrement_coil_charge(dev) : increment_coil_charge(dev); + } else if (data->direction == STEPPER_DIRECTION_NEGATIVE) { + config->invert_direction ? increment_coil_charge(dev) : decrement_coil_charge(dev); + } +} + +static int gpio_stepper_set_micro_step_res(const struct device *dev, + enum stepper_micro_step_resolution micro_step_res) +{ + struct gpio_stepper_data *data = dev->data; + int err = 0; + + K_SPINLOCK(&data->lock) { + switch (micro_step_res) { + case STEPPER_MICRO_STEP_1: + case STEPPER_MICRO_STEP_2: + data->step_gap = MAX_MICRO_STEP_RES >> (micro_step_res - 1); + break; + default: + LOG_ERR("Unsupported micro step resolution %d", micro_step_res); + err = -ENOTSUP; + } + } + return err; +} + +static int gpio_stepper_get_micro_step_res(const struct device *dev, + enum stepper_micro_step_resolution *micro_step_res) +{ + struct gpio_stepper_data *data = dev->data; + *micro_step_res = MAX_MICRO_STEP_RES >> (data->step_gap - 1); + return 0; +} + +static int gpio_stepper_enable(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + int err; + + if (data->is_enabled) { + LOG_WRN("Stepper motor is already enabled"); + return 0; + } + + K_SPINLOCK(&data->lock) { + err = energize_coils(dev, true); + if (err == 0) { + data->is_enabled = true; + } + } + return err; +} + +static int gpio_stepper_disable(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + int err; + + K_SPINLOCK(&data->lock) { + err = energize_coils(dev, false); + if (err == 0) { + data->is_enabled = false; + } + } + return err; +} + +static int gpio_stepper_set_direction(const struct device *dev, + const enum stepper_direction direction) +{ + struct gpio_stepper_data *data = dev->data; + + K_SPINLOCK(&data->lock) { + data->direction = direction; + } + + return 0; +} + +static int gpio_stepper_step(const struct device *dev) +{ + struct gpio_stepper_data *data = dev->data; + + if (!data->is_enabled) { + LOG_ERR("Stepper motor is not enabled"); + return -ECANCELED; + } + + K_SPINLOCK(&data->lock) { + if (data->direction == STEPPER_DIRECTION_POSITIVE) { + increment_coil_charge(dev); + } else if (data->direction == STEPPER_DIRECTION_NEGATIVE) { + decrement_coil_charge(dev); + } + stepper_motor_set_coil_charge(dev); + update_coil_charge(dev); + } + + return 0; +} + +static int gpio_stepper_init(const struct device *dev) +{ + const struct gpio_stepper_config *config = dev->config; + + LOG_DBG("Initializing %s gpio_stepper with %d pin", dev->name, NUM_CONTROL_PINS); + for (uint8_t n_pin = 0; n_pin < NUM_CONTROL_PINS; n_pin++) { + (void)gpio_pin_configure_dt(&config->control_pins[n_pin], GPIO_OUTPUT_INACTIVE); + } + return 0; +} + +static DEVICE_API(stepper, gpio_stepper_api) = { + .enable = gpio_stepper_enable, + .disable = gpio_stepper_disable, + .set_micro_step_res = gpio_stepper_set_micro_step_res, + .get_micro_step_res = gpio_stepper_get_micro_step_res, + .set_direction = gpio_stepper_set_direction, + .step = gpio_stepper_step, +}; + +#define GPIO_STEPPER_DEFINE(inst) \ + static const struct gpio_dt_spec gpio_stepper_motor_control_pins_##inst[] = { \ + DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \ + }; \ + BUILD_ASSERT( \ + ARRAY_SIZE(gpio_stepper_motor_control_pins_##inst) == 4, \ + "gpio_stepper_controller driver currently supports only 4 wire configuration"); \ + static const struct gpio_stepper_config gpio_stepper_config_##inst = { \ + .invert_direction = DT_INST_PROP(inst, invert_direction), \ + .control_pins = gpio_stepper_motor_control_pins_##inst}; \ + static struct gpio_stepper_data gpio_stepper_data_##inst = { \ + .step_gap = MAX_MICRO_STEP_RES >> (DT_INST_PROP(inst, micro_step_res) - 1), \ + }; \ + BUILD_ASSERT(DT_INST_PROP(inst, micro_step_res) <= STEPPER_MICRO_STEP_2, \ + "gpio_stepper_controller driver supports up to 2 micro steps"); \ + DEVICE_DT_INST_DEFINE(inst, gpio_stepper_init, NULL, &gpio_stepper_data_##inst, \ + &gpio_stepper_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &gpio_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_STEPPER_DEFINE) diff --git a/drivers/stepper/step_dir/CMakeLists.txt b/drivers/stepper/step_dir/CMakeLists.txt index 7daa4cc04d5b2..6bb451e000676 100644 --- a/drivers/stepper/step_dir/CMakeLists.txt +++ b/drivers/stepper/step_dir/CMakeLists.txt @@ -4,5 +4,3 @@ zephyr_library() zephyr_library_sources(step_dir_stepper_common.c) -zephyr_library_sources(step_dir_stepper_work_timing.c) -zephyr_library_sources_ifdef(CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING step_dir_stepper_counter_timing.c) diff --git a/drivers/stepper/step_dir/Kconfig b/drivers/stepper/step_dir/Kconfig index d8629c649ffd2..039143c632527 100644 --- a/drivers/stepper/step_dir/Kconfig +++ b/drivers/stepper/step_dir/Kconfig @@ -5,18 +5,3 @@ config STEP_DIR_STEPPER bool help Enable library used for step direction stepper drivers. - -if STEP_DIR_STEPPER - -config STEP_DIR_STEPPER_COUNTER_TIMING - bool "Counter use for stepping" - select COUNTER - default y - help - Enable usage of a counter device for accurate stepping. - -module = STEP_DIR -module-str = step_dir -rsource "../Kconfig.stepper_event_template" - -endif # STEP_DIR_STEPPER diff --git a/drivers/stepper/step_dir/step_dir_stepper_common.c b/drivers/stepper/step_dir/step_dir_stepper_common.c index b6b7566f07fbd..272361fa783c3 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_common.c +++ b/drivers/stepper/step_dir/step_dir_stepper_common.c @@ -8,29 +8,10 @@ #include LOG_MODULE_REGISTER(step_dir_stepper, CONFIG_STEPPER_LOG_LEVEL); -static inline int step_dir_stepper_perform_step(const struct device *dev) +int step_dir_stepper_common_step(const struct device *dev) { const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; - int ret; - - switch (data->direction) { - case STEPPER_DIRECTION_POSITIVE: - ret = gpio_pin_set_dt(&config->dir_pin, 1 ^ config->invert_direction); - break; - case STEPPER_DIRECTION_NEGATIVE: - ret = gpio_pin_set_dt(&config->dir_pin, 0 ^ config->invert_direction); - break; - default: - LOG_ERR("Unsupported direction: %d", data->direction); - return -ENOTSUP; - } - if (ret < 0) { - LOG_ERR("Failed to set direction: %d", ret); - return ret; - } - - ret = gpio_pin_toggle_dt(&config->step_pin); + int ret = gpio_pin_toggle_dt(&config->step_pin); if (ret < 0) { LOG_ERR("Failed to toggle step pin: %d", ret); return ret; @@ -44,142 +25,28 @@ static inline int step_dir_stepper_perform_step(const struct device *dev) } } - if (data->direction == STEPPER_DIRECTION_POSITIVE) { - data->actual_position++; - } else { - data->actual_position--; - } - return 0; } -void stepper_trigger_callback(const struct device *dev, enum stepper_event event) -{ - struct step_dir_stepper_common_data *data = dev->data; - - if (!data->callback) { - LOG_WRN_ONCE("No callback set"); - return; - } - - if (!k_is_in_isr()) { - data->callback(dev, event, data->event_cb_user_data); - return; - } - -#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS - /* Dispatch to msgq instead of raising directly */ - int ret = k_msgq_put(&data->event_msgq, &event, K_NO_WAIT); - - if (ret != 0) { - LOG_WRN("Failed to put event in msgq: %d", ret); - } - - ret = k_work_submit(&data->event_callback_work); - if (ret < 0) { - LOG_ERR("Failed to submit work item: %d", ret); - } -#else - LOG_WRN_ONCE("Event callback called from ISR context without ISR safe events enabled"); -#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */ -} - -#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS -static void stepper_work_event_handler(struct k_work *work) +int step_dir_stepper_common_set_direction(const struct device *dev, + const enum stepper_direction dir) { - struct step_dir_stepper_common_data *data = - CONTAINER_OF(work, struct step_dir_stepper_common_data, event_callback_work); - enum stepper_event event; - int ret; - - ret = k_msgq_get(&data->event_msgq, &event, K_NO_WAIT); - if (ret != 0) { - return; - } - - /* Run the callback */ - if (data->callback != NULL) { - data->callback(data->dev, event, data->event_cb_user_data); - } - - /* If there are more pending events, resubmit this work item to handle them */ - if (k_msgq_num_used_get(&data->event_msgq) > 0) { - k_work_submit(work); - } -} -#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */ - -static void update_remaining_steps(struct step_dir_stepper_common_data *data) -{ - const struct step_dir_stepper_common_config *config = data->dev->config; - - if (data->step_count > 0) { - data->step_count--; - } else if (data->step_count < 0) { - data->step_count++; - } else { - stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED); - config->timing_source->stop(data->dev); - } -} - -static void update_direction_from_step_count(const struct device *dev) -{ - struct step_dir_stepper_common_data *data = dev->data; - - if (data->step_count > 0) { - data->direction = STEPPER_DIRECTION_POSITIVE; - } else if (data->step_count < 0) { - data->direction = STEPPER_DIRECTION_NEGATIVE; - } else { - LOG_ERR("Step count is zero"); - } -} - -static void position_mode_task(const struct device *dev) -{ - struct step_dir_stepper_common_data *data = dev->data; const struct step_dir_stepper_common_config *config = dev->config; + int ret; - if (data->step_count) { - (void)step_dir_stepper_perform_step(dev); - } - - if (config->timing_source->needs_reschedule(dev) && data->step_count != 0) { - (void)config->timing_source->start(dev); - } - - update_remaining_steps(dev->data); -} - -static void velocity_mode_task(const struct device *dev) -{ - const struct step_dir_stepper_common_config *config = dev->config; - - (void)step_dir_stepper_perform_step(dev); - - if (config->timing_source->needs_reschedule(dev)) { - (void)config->timing_source->start(dev); + switch (dir) { + case STEPPER_DIRECTION_POSITIVE: + ret = gpio_pin_set_dt(&config->dir_pin, 1 ^ config->invert_direction); + break; + case STEPPER_DIRECTION_NEGATIVE: + ret = gpio_pin_set_dt(&config->dir_pin, 0 ^ config->invert_direction); + break; + default: + LOG_ERR("Unsupported direction: %d", dir); + return -ENOTSUP; } -} -void stepper_handle_timing_signal(const struct device *dev) -{ - struct step_dir_stepper_common_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - switch (data->run_mode) { - case STEPPER_RUN_MODE_POSITION: - position_mode_task(dev); - break; - case STEPPER_RUN_MODE_VELOCITY: - velocity_mode_task(dev); - break; - default: - LOG_WRN("Unsupported run mode: %d", data->run_mode); - break; - } - } + return ret; } int step_dir_stepper_common_init(const struct device *dev) @@ -204,144 +71,5 @@ int step_dir_stepper_common_init(const struct device *dev) return ret; } - if (config->timing_source->init) { - ret = config->timing_source->init(dev); - if (ret < 0) { - LOG_ERR("Failed to initialize timing source: %d", ret); - return ret; - } - } - -#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS - struct step_dir_stepper_common_data *data = dev->data; - - k_msgq_init(&data->event_msgq, data->event_msgq_buffer, sizeof(enum stepper_event), - CONFIG_STEPPER_STEP_DIR_EVENT_QUEUE_LEN); - k_work_init(&data->event_callback_work, stepper_work_event_handler); -#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */ - - return 0; -} - -int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micro_steps) -{ - struct step_dir_stepper_common_data *data = dev->data; - const struct step_dir_stepper_common_config *config = dev->config; - - if (data->microstep_interval_ns == 0) { - LOG_ERR("Step interval not set or invalid step interval set"); - return -EINVAL; - } - - K_SPINLOCK(&data->lock) { - data->run_mode = STEPPER_RUN_MODE_POSITION; - data->step_count = micro_steps; - config->timing_source->update(dev, data->microstep_interval_ns); - update_direction_from_step_count(dev); - config->timing_source->start(dev); - } - - return 0; -} - -int step_dir_stepper_common_set_microstep_interval(const struct device *dev, - const uint64_t microstep_interval_ns) -{ - struct step_dir_stepper_common_data *data = dev->data; - const struct step_dir_stepper_common_config *config = dev->config; - - if (microstep_interval_ns == 0) { - LOG_ERR("Step interval cannot be zero"); - return -EINVAL; - } - - K_SPINLOCK(&data->lock) { - data->microstep_interval_ns = microstep_interval_ns; - config->timing_source->update(dev, microstep_interval_ns); - } - - return 0; -} - -int step_dir_stepper_common_set_reference_position(const struct device *dev, const int32_t value) -{ - struct step_dir_stepper_common_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - data->actual_position = value; - } - - return 0; -} - -int step_dir_stepper_common_get_actual_position(const struct device *dev, int32_t *value) -{ - struct step_dir_stepper_common_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - *value = data->actual_position; - } - - return 0; -} - -int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value) -{ - struct step_dir_stepper_common_data *data = dev->data; - int32_t steps_to_move; - - /* Calculate the relative movement required */ - K_SPINLOCK(&data->lock) { - steps_to_move = value - data->actual_position; - } - - return step_dir_stepper_common_move_by(dev, steps_to_move); -} - -int step_dir_stepper_common_is_moving(const struct device *dev, bool *is_moving) -{ - const struct step_dir_stepper_common_config *config = dev->config; - - *is_moving = config->timing_source->is_running(dev); - return 0; -} - -int step_dir_stepper_common_run(const struct device *dev, const enum stepper_direction direction) -{ - struct step_dir_stepper_common_data *data = dev->data; - const struct step_dir_stepper_common_config *config = dev->config; - - K_SPINLOCK(&data->lock) { - data->run_mode = STEPPER_RUN_MODE_VELOCITY; - data->direction = direction; - config->timing_source->update(dev, data->microstep_interval_ns); - config->timing_source->start(dev); - } - - return 0; -} - -int step_dir_stepper_common_stop(const struct device *dev) -{ - const struct step_dir_stepper_common_config *config = dev->config; - int ret; - - ret = config->timing_source->stop(dev); - if (ret != 0) { - LOG_ERR("Failed to stop timing source: %d", ret); - return ret; - } - - stepper_trigger_callback(dev, STEPPER_EVENT_STOPPED); - return 0; -} - -int step_dir_stepper_common_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct step_dir_stepper_common_data *data = dev->data; - - data->callback = callback; - data->event_cb_user_data = user_data; return 0; } diff --git a/drivers/stepper/step_dir/step_dir_stepper_common.h b/drivers/stepper/step_dir/step_dir_stepper_common.h index b345cf5d68a5d..bd805716bc038 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_common.h +++ b/drivers/stepper/step_dir/step_dir_stepper_common.h @@ -17,9 +17,6 @@ #include #include #include -#include - -#include "step_dir_stepper_timing_source.h" /** * @brief Common step direction stepper config. @@ -30,8 +27,6 @@ struct step_dir_stepper_common_config { const struct gpio_dt_spec step_pin; const struct gpio_dt_spec dir_pin; bool dual_edge; - const struct stepper_timing_source_api *timing_source; - const struct device *counter; bool invert_direction; }; @@ -47,11 +42,7 @@ struct step_dir_stepper_common_config { .step_pin = GPIO_DT_SPEC_GET(node_id, step_gpios), \ .dir_pin = GPIO_DT_SPEC_GET(node_id, dir_gpios), \ .dual_edge = DT_PROP_OR(node_id, dual_edge_step, false), \ - .counter = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, counter)), \ .invert_direction = DT_PROP(node_id, invert_direction), \ - .timing_source = COND_CODE_1(DT_NODE_HAS_PROP(node_id, counter), \ - (&step_counter_timing_source_api), \ - (&step_work_timing_source_api)), \ } /** @@ -61,65 +52,14 @@ struct step_dir_stepper_common_config { #define STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst) \ STEP_DIR_STEPPER_DT_COMMON_CONFIG_INIT(DT_DRV_INST(inst)) -/** - * @brief Common step direction stepper data. - * - * This structure **must** be placed first in the driver's data structure. - */ -struct step_dir_stepper_common_data { - const struct device *dev; - struct k_spinlock lock; - enum stepper_direction direction; - enum stepper_run_mode run_mode; - int32_t actual_position; - uint64_t microstep_interval_ns; - int32_t step_count; - stepper_event_callback_t callback; - void *event_cb_user_data; - - struct k_work_delayable stepper_dwork; - -#ifdef CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING - struct counter_top_cfg counter_top_cfg; - bool counter_running; -#endif /* CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING */ - -#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS - struct k_work event_callback_work; - struct k_msgq event_msgq; - uint8_t event_msgq_buffer[CONFIG_STEPPER_STEP_DIR_EVENT_QUEUE_LEN * - sizeof(enum stepper_event)]; -#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */ -}; - -/** - * @brief Initialize common step direction stepper data from devicetree instance. - * - * @param node_id The devicetree node identifier. - */ -#define STEP_DIR_STEPPER_DT_COMMON_DATA_INIT(node_id) \ - { \ - .dev = DEVICE_DT_GET(node_id), \ - } - -/** - * @brief Initialize common step direction stepper data from devicetree instance. - * @param inst Instance. - */ -#define STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst) \ - STEP_DIR_STEPPER_DT_COMMON_DATA_INIT(DT_DRV_INST(inst)) - /** * @brief Validate the offset of the common data structures. * * @param config Name of the config structure. - * @param data Name of the data structure. */ -#define STEP_DIR_STEPPER_STRUCT_CHECK(config, data) \ +#define STEP_DIR_STEPPER_STRUCT_CHECK(config) \ BUILD_ASSERT(offsetof(config, common) == 0, \ - "struct step_dir_stepper_common_config must be placed first"); \ - BUILD_ASSERT(offsetof(data, common) == 0, \ - "struct step_dir_stepper_common_data must be placed first"); + "struct step_dir_stepper_common_config must be placed first"); /** * @brief Common function to initialize a step direction stepper device at init time. @@ -134,103 +74,30 @@ struct step_dir_stepper_common_data { int step_dir_stepper_common_init(const struct device *dev); /** - * @brief Move the stepper motor by a given number of micro_steps. - * - * @param dev Pointer to the device structure. - * @param micro_steps Number of micro_steps to move. Can be positive or negative. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micro_steps); - -/** - * @brief Set the step interval of the stepper motor. + * @brief Common function to perform a step on a step direction stepper device. * - * @param dev Pointer to the device structure. - * @param microstep_interval_ns The step interval in nanoseconds. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_set_microstep_interval(const struct device *dev, - const uint64_t microstep_interval_ns); - -/** - * @brief Set the reference position of the stepper motor. + * This function must be called to perform a step. * - * @param dev Pointer to the device structure. - * @param value The reference position value to set. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_set_reference_position(const struct device *dev, const int32_t value); - -/** - * @brief Get the actual (reference) position of the stepper motor. - * - * @param dev Pointer to the device structure. - * @param value Pointer to a variable where the position value will be stored. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_get_actual_position(const struct device *dev, int32_t *value); - -/** - * @brief Set the absolute target position of the stepper motor. - * - * @param dev Pointer to the device structure. - * @param value The target position to set. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value); - -/** - * @brief Check if the stepper motor is still moving. - * - * @param dev Pointer to the device structure. - * @param is_moving Pointer to a boolean where the movement status will be stored. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_is_moving(const struct device *dev, bool *is_moving); - -/** - * @brief Run the stepper with a given direction and step interval. + * @param dev Step direction stepper device instance. * - * @param dev Pointer to the device structure. - * @param direction The direction of movement (positive or negative). - * @return 0 on success, or a negative error code on failure. + * @retval 0 If step performed successfully. + * @retval -errno Negative errno in case of failure. */ -int step_dir_stepper_common_run(const struct device *dev, const enum stepper_direction direction); +int step_dir_stepper_common_step(const struct device *dev); /** - * @brief Stop the stepper motor. + * @brief Common function to set the direction of a step direction stepper device. * - * @param dev Pointer to the device structure. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_stop(const struct device *dev); - -/** - * @brief Set a callback function for stepper motor events. + * This function must be called to set the direction. * - * This function sets a user-defined callback that will be invoked when a stepper motor event - * occurs. + * @param dev Step direction stepper device instance. + * @param dir Direction to set. * - * @param dev Pointer to the device structure. - * @param callback The callback function to set. - * @param user_data Pointer to user-defined data that will be passed to the callback. - * @return 0 on success, or a negative error code on failure. - */ -int step_dir_stepper_common_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data); - -/** - * @brief Handle a timing signal and update the stepper position. - * @param dev Pointer to the device structure. - */ -void stepper_handle_timing_signal(const struct device *dev); - -/** - * @brief Trigger callback function for stepper motor events. - * @param dev Pointer to the device structure. - * @param event The stepper_event to rigger the callback for. + * @retval 0 If direction set successfully. + * @retval -errno Negative errno in case of failure. */ -void stepper_trigger_callback(const struct device *dev, enum stepper_event event); +int step_dir_stepper_common_set_direction(const struct device *dev, + const enum stepper_direction dir); /** @} */ diff --git a/drivers/stepper/ti/Kconfig.drv84xx b/drivers/stepper/ti/Kconfig.drv84xx index e5466890c66da..10cdd06433ee4 100644 --- a/drivers/stepper/ti/Kconfig.drv84xx +++ b/drivers/stepper/ti/Kconfig.drv84xx @@ -7,6 +7,5 @@ config DRV84XX depends on DT_HAS_TI_DRV84XX_ENABLED select STEPPER_TI select STEP_DIR_STEPPER - select STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS help Enable driver for TI DRV84XX stepper motor driver. diff --git a/drivers/stepper/ti/drv84xx.c b/drivers/stepper/ti/drv84xx.c index cc4a8651e8aa5..9c46de7bd0e0b 100644 --- a/drivers/stepper/ti/drv84xx.c +++ b/drivers/stepper/ti/drv84xx.c @@ -255,8 +255,6 @@ static int drv84xx_disable(const struct device *dev) } } - config->common.timing_source->stop(dev); - data->enabled = false; return ret; @@ -351,42 +349,6 @@ static int drv84xx_get_micro_step_res(const struct device *dev, return 0; } -static int drv84xx_move_to(const struct device *dev, int32_t target) -{ - struct drv84xx_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to move to target position, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_move_to(dev, target); -} - -static int drv84xx_move_by(const struct device *dev, int32_t steps) -{ - struct drv84xx_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to move by delta, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_move_by(dev, steps); -} - -static int drv84xx_run(const struct device *dev, enum stepper_direction direction) -{ - struct drv84xx_data *data = dev->data; - - if (!data->enabled) { - LOG_ERR("Failed to run stepper, device is not enabled"); - return -ECANCELED; - } - - return step_dir_stepper_common_run(dev, direction); -} - void fault_event(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct drv84xx_data *data = CONTAINER_OF(cb, struct drv84xx_data, fault_cb_data); @@ -478,17 +440,10 @@ static int drv84xx_init(const struct device *dev) static DEVICE_API(stepper, drv84xx_stepper_api) = { .enable = drv84xx_enable, .disable = drv84xx_disable, - .move_by = drv84xx_move_by, - .move_to = drv84xx_move_to, - .is_moving = step_dir_stepper_common_is_moving, - .set_reference_position = step_dir_stepper_common_set_reference_position, - .get_actual_position = step_dir_stepper_common_get_actual_position, - .set_microstep_interval = step_dir_stepper_common_set_microstep_interval, - .run = drv84xx_run, - .stop = step_dir_stepper_common_stop, .set_micro_step_res = drv84xx_set_micro_step_res, .get_micro_step_res = drv84xx_get_micro_step_res, - .set_event_callback = step_dir_stepper_common_set_event_callback, + .step = step_dir_stepper_common_step, + .set_direction = step_dir_stepper_common_set_direction, }; #define DRV84XX_DEVICE(inst) \ diff --git a/drivers/stepper_control/CMakeLists.txt b/drivers/stepper_control/CMakeLists.txt new file mode 100644 index 0000000000000..156c676529adb --- /dev/null +++ b/drivers/stepper_control/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper_control.h) + +# zephyr-keep-sorted-start +add_subdirectory_ifdef(CONFIG_STEPPER_CONTROL_ADI_TMC adi_tmc) +add_subdirectory_ifdef(CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL zephyr_stepper_motion_control) +# zephyr-keep-sorted-stop diff --git a/drivers/stepper_control/Kconfig b/drivers/stepper_control/Kconfig new file mode 100644 index 0000000000000..023fc66064fd8 --- /dev/null +++ b/drivers/stepper_control/Kconfig @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +menuconfig STEPPER_CONTROL + bool "Stepper Motion Controller" + select STEPPER + help + Enable support for stepper motor control. + +if STEPPER_CONTROL + +module = STEPPER_CONTROL +module-str = stepper_control +source "subsys/logging/Kconfig.template.log_config" + +config STEPPER_CONTROL_INIT_PRIORITY + int "Stepper motor control init priority" + default 90 + help + Stepper motor control initialization priority. + +comment "Stepper Control Drivers" + +# zephyr-keep-sorted-start +rsource "adi_tmc/Kconfig" +rsource "zephyr_stepper_motion_control/Kconfig" +# zephyr-keep-sorted-stop + +endif # STEPPER_CONTROL diff --git a/drivers/stepper/Kconfig.stepper_event_template b/drivers/stepper_control/Kconfig.stepper_event_template similarity index 79% rename from drivers/stepper/Kconfig.stepper_event_template rename to drivers/stepper_control/Kconfig.stepper_event_template index fb29a1e91e0e2..2fdd942f92a74 100644 --- a/drivers/stepper/Kconfig.stepper_event_template +++ b/drivers/stepper_control/Kconfig.stepper_event_template @@ -1,7 +1,7 @@ # Copyright (c) 2024 Fabian Blatz # SPDX-License-Identifier: Apache-2.0 -config STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS +config $(module)_GENERATE_ISR_SAFE_EVENTS bool "$(module-str) guarantee non ISR callbacks upon stepper events" help Enable the dispatch of stepper generated events via @@ -9,10 +9,10 @@ config STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS code is not run inside of an ISR. Can be disabled, but then registered stepper event callback must be ISR safe. -config STEPPER_$(module)_EVENT_QUEUE_LEN +config $(module)_EVENT_QUEUE_LEN int "$(module-str) maximum number of pending stepper events" default 4 - depends on STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS + depends on $(module)_GENERATE_ISR_SAFE_EVENTS help The maximum number of stepper events that can be pending before new events are dropped. diff --git a/drivers/stepper_control/adi_tmc/CMakeLists.txt b/drivers/stepper_control/adi_tmc/CMakeLists.txt new file mode 100644 index 0000000000000..46e5a19b50c8f --- /dev/null +++ b/drivers/stepper_control/adi_tmc/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_property(ALLOW_EMPTY TRUE) + +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx_stepper_control.c) diff --git a/drivers/stepper_control/adi_tmc/Kconfig b/drivers/stepper_control/adi_tmc/Kconfig new file mode 100644 index 0000000000000..7b28e3cea629b --- /dev/null +++ b/drivers/stepper_control/adi_tmc/Kconfig @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +menuconfig STEPPER_CONTROL_ADI_TMC + bool "Activate trinamic tmc stepper motion controller" + depends on STEPPER_CONTROL + default y + +if STEPPER_CONTROL_ADI_TMC + +config STEPPER_ADI_TMC_SPI + bool "Use Trinamic Stepper Controller with SPI" + depends on STEPPER_ADI_TMC + select SPI + help + A Trinamic Stepper Motion Controller with SPI is enabled + +comment "Trinamic Stepper Control Drivers" + +rsource "Kconfig.tmc50xx" + +endif # STEPPER_CONTROL_ADI_TMC diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc50xx b/drivers/stepper_control/adi_tmc/Kconfig.tmc50xx similarity index 83% rename from drivers/stepper/adi_tmc/Kconfig.tmc50xx rename to drivers/stepper_control/adi_tmc/Kconfig.tmc50xx index eeb17293456a5..dae04027df7cc 100644 --- a/drivers/stepper/adi_tmc/Kconfig.tmc50xx +++ b/drivers/stepper_control/adi_tmc/Kconfig.tmc50xx @@ -4,7 +4,7 @@ config STEPPER_ADI_TMC50XX bool "Activate trinamic tmc50xx stepper driver" - depends on DT_HAS_ADI_TMC50XX_ENABLED && STEPPER_ADI_TMC + depends on DT_HAS_ADI_TMC50XX_STEPPER_CONTROL_ENABLED && STEPPER_ADI_TMC select STEPPER_ADI_TMC_SPI default y diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template b/drivers/stepper_control/adi_tmc/Kconfig.tmc_rampgen_template similarity index 87% rename from drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template rename to drivers/stepper_control/adi_tmc/Kconfig.tmc_rampgen_template index a357f8917d87b..c9916576029a3 100644 --- a/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template +++ b/drivers/stepper_control/adi_tmc/Kconfig.tmc_rampgen_template @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya # SPDX-License-Identifier: Apache-2.0 -config STEPPER_ADI_$(module)_RAMPSTAT_POLL_INTERVAL_IN_MSEC +config ADI_$(module)_RAMPSTAT_POLL_INTERVAL_IN_MSEC int "$(module-str) poll ramp status interval in ms" depends on !$(dt_compat_any_has_prop,$(DT_COMPAT_ADI_$(module)),diag0-gpios) default 100 @@ -11,7 +11,7 @@ config STEPPER_ADI_$(module)_RAMPSTAT_POLL_INTERVAL_IN_MSEC polls the ramp status register to detect events like position reached, stall or end stop triggered. -config STEPPER_ADI_$(module)_RAMPSTAT_POLL_STALLGUARD_LOG +config ADI_$(module)_RAMPSTAT_POLL_STALLGUARD_LOG bool "log $(module-str) stallguard" default n help diff --git a/drivers/stepper/adi_tmc/adi_tmc5xxx_common.h b/drivers/stepper_control/adi_tmc/adi_tmc5xxx_common.h similarity index 83% rename from drivers/stepper/adi_tmc/adi_tmc5xxx_common.h rename to drivers/stepper_control/adi_tmc/adi_tmc5xxx_common.h index 7eafe62326eb8..14b51823a7f9d 100644 --- a/drivers/stepper/adi_tmc/adi_tmc5xxx_common.h +++ b/drivers/stepper_control/adi_tmc/adi_tmc5xxx_common.h @@ -12,7 +12,7 @@ #ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_COMMON_H_ #define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_COMMON_H_ -#include "adi_tmc_reg.h" +#include #ifdef __cplusplus extern "C" { @@ -25,6 +25,10 @@ extern "C" { * @{ */ +int tmc5xxx_stallguard_enable(const struct device *dev, const bool enable); + +void tmc5xxx_log_stallguard(const struct device *dev, const uint32_t drv_status); + /** * @brief Calculate the velocity in full clock cycles from the velocity in Hz * diff --git a/drivers/stepper/adi_tmc/adi_tmc_spi.c b/drivers/stepper_control/adi_tmc/adi_tmc_spi.c similarity index 100% rename from drivers/stepper/adi_tmc/adi_tmc_spi.c rename to drivers/stepper_control/adi_tmc/adi_tmc_spi.c diff --git a/drivers/stepper/adi_tmc/adi_tmc_spi.h b/drivers/stepper_control/adi_tmc/adi_tmc_spi.h similarity index 100% rename from drivers/stepper/adi_tmc/adi_tmc_spi.h rename to drivers/stepper_control/adi_tmc/adi_tmc_spi.h diff --git a/drivers/stepper_control/adi_tmc/tmc50xx_stepper_control.c b/drivers/stepper_control/adi_tmc/tmc50xx_stepper_control.c new file mode 100644 index 0000000000000..1fe304eb25e6f --- /dev/null +++ b/drivers/stepper_control/adi_tmc/tmc50xx_stepper_control.c @@ -0,0 +1,720 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_tmc50xx_stepper_control + +#include + +#include +#include + +#include "adi_tmc_spi.h" +#include "adi_tmc5xxx_common.h" + +#include +LOG_MODULE_REGISTER(tmc50xx_stepper_control, CONFIG_STEPPER_CONTROL_LOG_LEVEL); + +struct tmc50xx_data { + struct k_sem sem; +}; + +struct tmc50xx_config { + const uint32_t gconf; + struct spi_dt_spec spi; + const uint32_t clock_frequency; +}; + +struct tmc50xx_stepper_control_data { + struct k_work_delayable stallguard_dwork; + /* Work item to run the callback in a thread context. */ + struct k_work_delayable rampstat_callback_dwork; + /* device pointer required to access config in k_work */ + const struct device *motion_controller; + stepper_motion_control_event_callback_t callback; + void *event_cb_user_data; +}; + +struct tmc50xx_stepper_control_config { + const uint8_t index; + const int8_t sg_threshold; + const bool is_sg_enabled; + const uint32_t sg_velocity_check_interval_ms; + const uint32_t sg_threshold_velocity; + /* parent controller required for bus communication */ + const struct device *controller; +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + const struct tmc_ramp_generator_data default_ramp_config; +#endif +}; + +static int read_actual_position(const struct tmc50xx_stepper_control_config *config, + int32_t *position); + +static int tmc50xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) +{ + const struct tmc50xx_config *config = dev->config; + struct tmc50xx_data *data = dev->data; + const struct spi_dt_spec bus = config->spi; + int err; + + k_sem_take(&data->sem, K_FOREVER); + + err = tmc_spi_write_register(&bus, TMC5XXX_WRITE_BIT, reg_addr, reg_val); + + k_sem_give(&data->sem); + + if (err < 0) { + LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); + return err; + } + return 0; +} + +static int tmc50xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) +{ + const struct tmc50xx_config *config = dev->config; + struct tmc50xx_data *data = dev->data; + const struct spi_dt_spec bus = config->spi; + int err; + + k_sem_take(&data->sem, K_FOREVER); + + err = tmc_spi_read_register(&bus, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); + + k_sem_give(&data->sem); + + if (err < 0) { + LOG_ERR("Failed to read register 0x%x", reg_addr); + return err; + } + return 0; +} + +static int tmc50xx_stepper_control_set_event_callback( + const struct device *dev, stepper_motion_control_event_callback_t callback, void *user_data) +{ + struct tmc50xx_stepper_control_data *data = dev->data; + + data->callback = callback; + data->event_cb_user_data = user_data; + return 0; +} + +static int read_vactual(const struct tmc50xx_stepper_control_config *config, + int32_t *actual_velocity) +{ + int err; + + err = stepper_controller_read(config->controller, TMC50XX_VACTUAL(config->index), + actual_velocity); + if (err) { + LOG_ERR("Failed to read VACTUAL register"); + return err; + } + + *actual_velocity = sign_extend(*actual_velocity, TMC_RAMP_VACTUAL_SHIFT); + if (actual_velocity) { + LOG_DBG("actual velocity: %d", *actual_velocity); + } + return 0; +} + +static int stallguard_enable(const struct device *dev, const bool enable) +{ + const struct tmc50xx_stepper_control_config *config = dev->config; + uint32_t reg_value; + int err; + + err = stepper_controller_read(config->controller, TMC50XX_SWMODE(config->index), + ®_value); + if (err) { + LOG_ERR("Failed to read SWMODE register"); + return -EIO; + } + + if (enable) { + reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; + + int32_t actual_velocity; + + err = read_vactual(config, &actual_velocity); + if (err) { + return -EIO; + } + if (abs(actual_velocity) < config->sg_threshold_velocity) { + return -EAGAIN; + } + } else { + reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; + } + err = stepper_controller_write(config->controller, TMC50XX_SWMODE(config->index), + reg_value); + if (err) { + LOG_ERR("Failed to write SWMODE register"); + return -EIO; + } + + LOG_DBG("Stallguard %s", enable ? "enabled" : "disabled"); + return 0; +} + +static void stallguard_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct tmc50xx_stepper_control_data *stepper_control_data = + CONTAINER_OF(dwork, struct tmc50xx_stepper_control_data, stallguard_dwork); + int err; + const struct tmc50xx_stepper_control_config *stepper_control_config = + stepper_control_data->motion_controller->config; + + err = stallguard_enable(stepper_control_data->motion_controller, true); + if (err == -EAGAIN) { + k_work_reschedule(dwork, + K_MSEC(stepper_control_config->sg_velocity_check_interval_ms)); + } + if (err == -EIO) { + LOG_ERR("Failed to enable stallguard because of I/O error"); + return; + } +} + +static void execute_callback(const struct device *dev, + const enum stepper_motion_control_event event) +{ + struct tmc50xx_stepper_control_data *data = dev->data; + + if (!data->callback) { + LOG_WRN_ONCE("No callback registered"); + return; + } + data->callback(dev, event, data->event_cb_user_data); +} + +#ifdef CONFIG_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG + +static void log_stallguard(struct tmc50xx_stepper_control_data *stepper_control_data, + const uint32_t drv_status) +{ + const struct tmc50xx_stepper_control_config *stepper_control_config = + stepper_control_data->motion_controller->config; + int32_t position; + int err; + + err = read_actual_position(stepper_control_config, &position); + if (err != 0) { + LOG_ERR("%s: Failed to read XACTUAL register", + stepper_control_data->motion_controller->name); + return; + } + + const uint8_t sg_result = FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); + const bool sg_status = FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); + + LOG_DBG("%s position: %d | sg result: %3d status: %d", + stepper_control_data->motion_controller->name, position, sg_result, sg_status); +} + +#endif + +static void rampstat_work_reschedule(struct k_work_delayable *rampstat_callback_dwork) +{ + k_work_reschedule(rampstat_callback_dwork, + K_MSEC(CONFIG_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); +} + +static void rampstat_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + + struct tmc50xx_stepper_control_data *stepper_control_data = + CONTAINER_OF(dwork, struct tmc50xx_stepper_control_data, rampstat_callback_dwork); + const struct tmc50xx_stepper_control_config *stepper_control_config = + stepper_control_data->motion_controller->config; + + __ASSERT_NO_MSG(stepper_control_config->controller != NULL); + + uint32_t drv_status; + int err; + + err = stepper_controller_read(stepper_control_config->controller, + TMC50XX_DRVSTATUS(stepper_control_config->index), + &drv_status); + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", + stepper_control_data->motion_controller->name); + return; + } +#ifdef CONFIG_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG + log_stallguard(stepper_control_data, drv_status); +#endif + if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { + LOG_INF("%s: Stall detected", stepper_control_data->motion_controller->name); + err = stepper_controller_write(stepper_control_config->controller, + TMC50XX_RAMPMODE(stepper_control_config->index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + LOG_ERR("%s: Failed to stop motor", + stepper_control_data->motion_controller->name); + return; + } + } + + uint32_t rampstat_value; + + err = stepper_controller_read(stepper_control_config->controller, + TMC50XX_RAMPSTAT(stepper_control_config->index), + &rampstat_value); + if (err != 0) { + LOG_ERR("%s: Failed to read RAMPSTAT register", + stepper_control_data->motion_controller->name); + return; + } + + const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); + + if (ramp_stat_values > 0) { + switch (ramp_stat_values) { + + case TMC5XXX_STOP_LEFT_EVENT: + LOG_DBG("RAMPSTAT %s:Left end-stop detected", + stepper_control_data->motion_controller->name); + execute_callback(stepper_control_data->motion_controller, + STEPPER_MOTION_CONTROL_EVENT_LEFT_END_STOP_DETECTED); + break; + + case TMC5XXX_STOP_RIGHT_EVENT: + LOG_DBG("RAMPSTAT %s:Right end-stop detected", + stepper_control_data->motion_controller->name); + execute_callback(stepper_control_data->motion_controller, + STEPPER_MOTION_CONTROL_EVENT_RIGHT_END_STOP_DETECTED); + break; + + case TMC5XXX_POS_REACHED_EVENT: + LOG_DBG("RAMPSTAT %s:Position reached", + stepper_control_data->motion_controller->name); + execute_callback(stepper_control_data->motion_controller, + STEPPER_MOTION_CONTROL_EVENT_STEPS_COMPLETED); + break; + + case TMC5XXX_STOP_SG_EVENT: + LOG_DBG("RAMPSTAT %s:Stall detected", + stepper_control_data->motion_controller->name); + stallguard_enable(stepper_control_data->motion_controller, false); + execute_callback(stepper_control_data->motion_controller, + STEPPER_MOTION_CONTROL_EVENT_STALL_DETECTED); + break; + default: + LOG_ERR("Illegal ramp stat bit field"); + break; + } + } else { + rampstat_work_reschedule(&stepper_control_data->rampstat_callback_dwork); + } +} + +static int tmc50xx_stepper_control_is_moving(const struct device *dev, bool *is_moving) +{ + const struct tmc50xx_stepper_control_config *config = dev->config; + uint32_t reg_value; + int err; + + err = stepper_controller_read(config->controller, TMC50XX_DRVSTATUS(config->index), + ®_value); + + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name); + return -EIO; + } + + *is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) != 1U); + LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving); + return 0; +} + +int tmc50xx_stepper_motion_control_set_max_velocity(const struct device *dev, uint32_t velocity) +{ + const struct tmc50xx_stepper_control_config *config = dev->config; + const struct tmc50xx_config *tmc50xx_config = config->controller->config; + const uint32_t clock_frequency = tmc50xx_config->clock_frequency; + uint32_t velocity_fclk; + int err; + + velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency); + + err = stepper_controller_write(config->controller, TMC50XX_VMAX(config->index), + velocity_fclk); + if (err != 0) { + LOG_ERR("%s: Failed to set max velocity", dev->name); + return -EIO; + } + return 0; +} + +static int tmc50xx_stepper_control_set_reference_position(const struct device *dev, + const int32_t position) +{ + const struct tmc50xx_stepper_control_config *config = dev->config; + int err; + + err = stepper_controller_write(config->controller, TMC50XX_RAMPMODE(config->index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + return -EIO; + } + + err = stepper_controller_write(config->controller, TMC50XX_XACTUAL(config->index), + position); + if (err != 0) { + return -EIO; + } + LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position); + return 0; +} + +static int read_actual_position(const struct tmc50xx_stepper_control_config *config, + int32_t *position) +{ + int err; + + err = stepper_controller_read(config->controller, TMC50XX_XACTUAL(config->index), position); + if (err != 0) { + return -EIO; + } + return 0; +} + +static int tmc50xx_stepper_control_get_actual_position(const struct device *dev, int32_t *position) +{ + const struct tmc50xx_stepper_control_config *config = dev->config; + int err; + + err = read_actual_position(config, position); + if (err != 0) { + return -EIO; + } + LOG_DBG("%s actual position: %d", dev->name, *position); + return 0; +} + +static int tmc50xx_stepper_control_move_to(const struct device *dev, const int32_t micro_steps) +{ + LOG_DBG("%s set target position to %d", dev->name, micro_steps); + const struct tmc50xx_stepper_control_config *config = dev->config; + struct tmc50xx_stepper_control_data *data = dev->data; + int err; + + if (config->is_sg_enabled) { + stallguard_enable(dev, false); + } + + err = stepper_controller_write(config->controller, TMC50XX_RAMPMODE(config->index), + TMC5XXX_RAMPMODE_POSITIONING_MODE); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_XTARGET(config->index), + micro_steps); + if (err != 0) { + return -EIO; + } + + if (config->is_sg_enabled) { + k_work_reschedule(&data->stallguard_dwork, + K_MSEC(config->sg_velocity_check_interval_ms)); + } + if (data->callback) { + rampstat_work_reschedule(&data->rampstat_callback_dwork); + } + return 0; +} + +static int tmc50xx_stepper_control_move_by(const struct device *dev, const int32_t micro_steps) +{ + int err; + int32_t position; + + err = stepper_motion_control_get_actual_position(dev, &position); + if (err != 0) { + return -EIO; + } + int32_t target_position = position + micro_steps; + + LOG_DBG("%s moved to %d by steps: %d", dev->name, target_position, micro_steps); + + return tmc50xx_stepper_control_move_to(dev, target_position); +} + +static int tmc50xx_stepper_control_run(const struct device *dev, + const enum stepper_direction direction) +{ + LOG_DBG("Stepper motor controller %s run", dev->name); + const struct tmc50xx_stepper_control_config *config = dev->config; + struct tmc50xx_stepper_control_data *data = dev->data; + int err; + + if (config->is_sg_enabled) { + err = stallguard_enable(dev, false); + if (err != 0) { + return -EIO; + } + } + + switch (direction) { + case STEPPER_DIRECTION_POSITIVE: + err = stepper_controller_write(config->controller, TMC50XX_RAMPMODE(config->index), + TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); + if (err != 0) { + return -EIO; + } + break; + + case STEPPER_DIRECTION_NEGATIVE: + err = stepper_controller_write(config->controller, TMC50XX_RAMPMODE(config->index), + TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE); + if (err != 0) { + return -EIO; + } + break; + } + + if (config->is_sg_enabled) { + k_work_reschedule(&data->stallguard_dwork, + K_MSEC(config->sg_velocity_check_interval_ms)); + } + if (data->callback) { + rampstat_work_reschedule(&data->rampstat_callback_dwork); + } + return 0; +} + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + +int tmc50xx_stepper_set_ramp(const struct device *dev, + const struct tmc_ramp_generator_data *ramp_data) +{ + LOG_DBG("Stepper motor controller %s set ramp", dev->name); + const struct tmc50xx_stepper_control_config *config = dev->config; + int err; + + err = stepper_controller_write(config->controller, TMC50XX_VSTART(config->index), + ramp_data->vstart); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_A1(config->index), + ramp_data->a1); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_AMAX(config->index), + ramp_data->amax); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_D1(config->index), + ramp_data->d1); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_DMAX(config->index), + ramp_data->dmax); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_V1(config->index), + ramp_data->v1); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_VMAX(config->index), + ramp_data->vmax); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_VSTOP(config->index), + ramp_data->vstop); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_TZEROWAIT(config->index), + ramp_data->tzerowait); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_VHIGH(config->index), + ramp_data->vhigh); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_VCOOLTHRS(config->index), + ramp_data->vcoolthrs); + if (err != 0) { + return -EIO; + } + err = stepper_controller_write(config->controller, TMC50XX_IHOLD_IRUN(config->index), + ramp_data->iholdrun); + if (err != 0) { + return -EIO; + } + return 0; +} + +#endif + +static int tmc50xx_init(const struct device *dev) +{ + LOG_DBG("TMC50XX stepper motor controller %s initialized", dev->name); + struct tmc50xx_data *data = dev->data; + const struct tmc50xx_config *config = dev->config; + int err; + + k_sem_init(&data->sem, 1, 1); + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI bus is not ready"); + return -ENODEV; + } + + /* Init non motor-index specific registers here. */ + LOG_DBG("GCONF: %d", config->gconf); + err = stepper_controller_write(dev, TMC5XXX_GCONF, config->gconf); + if (err != 0) { + return -EIO; + } + + /* Read GSTAT register values to clear any errors SPI Datagram. */ + uint32_t gstat_value; + + err = stepper_controller_read(dev, TMC5XXX_GSTAT, &gstat_value); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Device %s initialized", dev->name); + return 0; +} + +static int tmc50xx_stepper_init(const struct device *dev) +{ + const struct tmc50xx_stepper_control_config *stepper_control_config = dev->config; + struct tmc50xx_stepper_control_data *data = dev->data; + int err; + + LOG_DBG("Controller: %s, Stepper: %s", stepper_control_config->controller->name, dev->name); + + if (stepper_control_config->is_sg_enabled) { + k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); + + err = stepper_controller_write(stepper_control_config->controller, + TMC50XX_SWMODE(stepper_control_config->index), + BIT(10)); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Setting stall guard to %d with delay %d ms", + stepper_control_config->sg_threshold, + stepper_control_config->sg_velocity_check_interval_ms); + if (!IN_RANGE(stepper_control_config->sg_threshold, TMC5XXX_SG_MIN_VALUE, + TMC5XXX_SG_MAX_VALUE)) { + LOG_ERR("Stallguard threshold out of range"); + return -EINVAL; + } + + int32_t stall_guard_threshold = (int32_t)stepper_control_config->sg_threshold; + + err = stepper_controller_write( + stepper_control_config->controller, + TMC50XX_COOLCONF(stepper_control_config->index), + stall_guard_threshold << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + if (err != 0) { + return -EIO; + } + k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); + } + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + err = tmc50xx_stepper_set_ramp(dev, &stepper_control_config->default_ramp_config); + if (err != 0) { + return -EIO; + } +#endif + + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + rampstat_work_reschedule(&data->rampstat_callback_dwork); + if (err != 0) { + return -EIO; + } + return 0; +} + +static DEVICE_API(stepper_motion_control, tmc50xx_stepper_api) = { + .is_moving = tmc50xx_stepper_control_is_moving, + .move_by = tmc50xx_stepper_control_move_by, + .set_reference_position = tmc50xx_stepper_control_set_reference_position, + .get_actual_position = tmc50xx_stepper_control_get_actual_position, + .move_to = tmc50xx_stepper_control_move_to, + .run = tmc50xx_stepper_control_run, + .set_event_callback = tmc50xx_stepper_control_set_event_callback, +}; + +static DEVICE_API(stepper_controller, tmc50xx_api) = { + .write = tmc50xx_write, + .read = tmc50xx_read, +}; + +#define TMC50XX_STEPPER_CONTROL_CONFIG_DEFINE(child) \ + COND_CODE_1(DT_PROP_EXISTS(child, stallguard_threshold_velocity), \ + BUILD_ASSERT(DT_PROP(child, stallguard_threshold_velocity), \ + "stallguard threshold velocity must be a positive value"), ()); \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \ + static const struct tmc50xx_stepper_control_config \ + tmc50xx_stepper_control_config_##child = { \ + .controller = DEVICE_DT_GET(DT_PARENT(child)), \ + .index = DT_REG_ADDR(child), \ + .sg_threshold = DT_PROP(child, stallguard2_threshold), \ + .sg_threshold_velocity = DT_PROP(child, stallguard_threshold_velocity), \ + .sg_velocity_check_interval_ms = \ + DT_PROP(child, stallguard_velocity_check_interval_ms), \ + .is_sg_enabled = DT_PROP(child, activate_stallguard2), \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, \ + (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC50XX(child))) }; + +#define TMC50XX_STEPPER_CONTROL_DATA_DEFINE(child) \ + static struct tmc50xx_stepper_control_data tmc50xx_stepper_control_data_##child = { \ + .motion_controller = DEVICE_DT_GET(child), \ + }; + +#define TMC50XX_STEPPER_DEFINE(child) \ + DEVICE_DT_DEFINE(child, tmc50xx_stepper_init, NULL, &tmc50xx_stepper_control_data_##child, \ + &tmc50xx_stepper_control_config_##child, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); + +#define TMC50XX_DEFINE(inst) \ + BUILD_ASSERT(DT_INST_CHILD_NUM(inst) <= 2, "tmc50xx can drive two steppers at max"); \ + BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ + "clock frequency must be non-zero positive value"); \ + static struct tmc50xx_data tmc50xx_data_##inst; \ + static const struct tmc50xx_config tmc50xx_config_##inst = { \ + .gconf = ((DT_INST_PROP(inst, poscmp_enable) \ + << TMC50XX_GCONF_POSCMP_ENABLE_SHIFT) | \ + (DT_INST_PROP(inst, test_mode) << TMC50XX_GCONF_TEST_MODE_SHIFT) | \ + (DT_INST_PROP(inst, shaft1) << TMC50XX_GCONF_SHAFT_SHIFT(0)) | \ + (DT_INST_PROP(inst, shaft2) << TMC50XX_GCONF_SHAFT_SHIFT(1)) | \ + (DT_INST_PROP(inst, lock_gconf) << TMC50XX_LOCK_GCONF_SHIFT)), \ + .spi = SPI_DT_SPEC_INST_GET(inst, \ + (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8)), \ + 0), \ + .clock_frequency = DT_INST_PROP(inst, clock_frequency), \ + }; \ + DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_CONTROL_CONFIG_DEFINE); \ + DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_CONTROL_DATA_DEFINE); \ + DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_DEFINE); \ + DEVICE_DT_INST_DEFINE(inst, tmc50xx_init, NULL, &tmc50xx_data_##inst, \ + &tmc50xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ + &tmc50xx_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC50XX_DEFINE) diff --git a/drivers/stepper_control/zephyr_stepper_motion_control/CMakeLists.txt b/drivers/stepper_control/zephyr_stepper_motion_control/CMakeLists.txt new file mode 100644 index 0000000000000..ddc177135b506 --- /dev/null +++ b/drivers/stepper_control/zephyr_stepper_motion_control/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(stepper_control.c) +zephyr_library_sources(stepper_motion_control_work_timing.c) +zephyr_library_sources_ifdef(CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING stepper_motion_control_counter_timing.c) diff --git a/drivers/stepper_control/zephyr_stepper_motion_control/Kconfig b/drivers/stepper_control/zephyr_stepper_motion_control/Kconfig new file mode 100644 index 0000000000000..f9568231dbfa8 --- /dev/null +++ b/drivers/stepper_control/zephyr_stepper_motion_control/Kconfig @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +config ZEPHYR_STEPPER_MOTION_CONTROL + bool "Zephyr SW based Stepper motor control" + depends on DT_HAS_ZEPHYR_STEPPER_CONTROL_ENABLED + default y + +if ZEPHYR_STEPPER_MOTION_CONTROL + +config ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING + bool "Counter use for stepping" + select COUNTER + default y + help + Enable usage of a counter device for accurate stepping. + +module = ZEPHYR_STEPPER_MOTION_CONTROL +module-str = zephyr_stepper_motion_control + +rsource "../Kconfig.stepper_event_template" + +endif diff --git a/drivers/stepper_control/zephyr_stepper_motion_control/stepper_control.c b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_control.c new file mode 100644 index 0000000000000..2f8486f36331b --- /dev/null +++ b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_control.c @@ -0,0 +1,393 @@ +/** + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_stepper_control + +#include +#include +#include + +#include "stepper_motion_control_timing_source.h" + +#include +LOG_MODULE_REGISTER(stepper_motion_control, CONFIG_STEPPER_CONTROL_LOG_LEVEL); + +struct stepper_motion_control_config { + const struct device *stepper; + const struct timing_source_config timing_config; + const struct stepper_timing_source_api *timing_source; +}; + +struct stepper_motion_control_data { + const struct device *dev; + struct k_spinlock lock; + int32_t step_count; + enum stepper_direction direction; + enum stepper_run_mode run_mode; + int32_t actual_position; + int32_t reference_position; + enum stepper_motion_control_mode mode; + stepper_motion_control_event_callback_t callback; + void *event_cb_user_data; + struct timing_source_data timing_data; +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS + struct k_work event_callback_work; + struct k_msgq event_msgq; + uint8_t event_msgq_buffer[CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_EVENT_QUEUE_LEN * + sizeof(enum stepper_motion_control_event)]; +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS */ +}; + +void stepper_trigger_callback(const struct device *dev, enum stepper_motion_control_event event) +{ + struct stepper_motion_control_data *data = dev->data; + + if (!data->callback) { + LOG_WRN_ONCE("No callback set"); + return; + } + + if (!k_is_in_isr()) { + data->callback(dev, event, data->event_cb_user_data); + return; + } + +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS + /* Dispatch to msgq instead of raising directly */ + int ret = k_msgq_put(&data->event_msgq, &event, K_NO_WAIT); + + if (ret != 0) { + LOG_WRN("Failed to put event in msgq: %d", ret); + } + + ret = k_work_submit(&data->event_callback_work); + if (ret < 0) { + LOG_ERR("Failed to submit work item: %d", ret); + } +#else + LOG_WRN_ONCE("Event callback called from ISR context without ISR safe events enabled"); +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS */ +} + +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS +static void stepper_work_event_handler(struct k_work *work) +{ + struct stepper_motion_control_data *data = + CONTAINER_OF(work, struct stepper_motion_control_data, event_callback_work); + enum stepper_motion_control_event event; + int ret; + + ret = k_msgq_get(&data->event_msgq, &event, K_NO_WAIT); + if (ret != 0) { + return; + } + + /* Run the callback */ + if (data->callback != NULL) { + data->callback(data->dev, event, data->event_cb_user_data); + } + + /* If there are more pending events, resubmit this work item to handle them */ + if (k_msgq_num_used_get(&data->event_msgq) > 0) { + k_work_submit(work); + } +} +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS */ + +static void update_direction_from_step_count(const struct device *dev) +{ + struct stepper_motion_control_data *data = dev->data; + + if (data->step_count > 0) { + data->direction = STEPPER_DIRECTION_POSITIVE; + } else if (data->step_count < 0) { + data->direction = STEPPER_DIRECTION_NEGATIVE; + } else { + LOG_ERR("Step count is zero"); + } +} + +static int z_stepper_motion_control_move_by(const struct device *dev, int32_t micro_steps) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + /** Todo: Introduce a stepper driver call to check if the motor is enabled, if stepper is + * not enabled return -ECANCELED + */ + + if (data->timing_data.microstep_interval_ns == 0) { + LOG_ERR("Step interval not set or invalid step interval set"); + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + data->run_mode = STEPPER_RUN_MODE_POSITION; + data->step_count = micro_steps; + config->timing_source->update(&config->timing_config, &data->timing_data, + data->timing_data.microstep_interval_ns); + update_direction_from_step_count(dev); + stepper_set_direction(config->stepper, data->direction); + config->timing_source->start(&config->timing_config, &data->timing_data); + } + return 0; +} + +static int z_stepper_motion_control_move_to(const struct device *dev, int32_t micro_steps) +{ + struct stepper_motion_control_data *data = dev->data; + int32_t steps_to_move; + + /** Todo: Introduce a stepper driver call to check if the motor is enabled, if stepper is + * not enabled return -ECANCELED + */ + + K_SPINLOCK(&data->lock) { + steps_to_move = micro_steps - data->actual_position; + } + return z_stepper_motion_control_move_by(dev, steps_to_move); +} + +static int z_stepper_motion_control_run(const struct device *dev, + const enum stepper_direction direction) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + /** Todo: Introduce a stepper driver call to check if the motor is enabled, if stepper is + * not enabled return -ECANCELED + */ + + K_SPINLOCK(&data->lock) { + data->run_mode = STEPPER_RUN_MODE_VELOCITY; + data->direction = direction; + config->timing_source->update(&config->timing_config, &data->timing_data, + data->timing_data.microstep_interval_ns); + stepper_set_direction(config->stepper, direction); + config->timing_source->start(&config->timing_config, &data->timing_data); + } + return 0; +} + +static int z_stepper_motion_control_stop(const struct device *dev) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + int ret; + + ret = config->timing_source->stop(&config->timing_config, &data->timing_data); + if (ret != 0) { + LOG_ERR("Failed to stop timing source: %d", ret); + return ret; + } + stepper_trigger_callback(dev, STEPPER_MOTION_CONTROL_EVENT_STOPPED); + + return 0; +} + +static int z_stepper_motion_control_set_reference_position(const struct device *dev, + int32_t position) +{ + struct stepper_motion_control_data *data = dev->data; + + K_SPINLOCK(&data->lock) { + data->actual_position = position; + } + return 0; +} + +static int z_stepper_motion_control_get_actual_position(const struct device *dev, int32_t *position) +{ + struct stepper_motion_control_data *data = dev->data; + + K_SPINLOCK(&data->lock) { + *position = data->actual_position; + } + return 0; +} + +static int z_stepper_motion_control_set_step_interval(const struct device *dev, + uint64_t microstep_interval_ns) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + if (microstep_interval_ns == 0) { + LOG_ERR("Step interval is invalid."); + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + data->timing_data.microstep_interval_ns = microstep_interval_ns; + config->timing_source->update(&config->timing_config, &data->timing_data, + microstep_interval_ns); + } + LOG_DBG("Setting Motor step interval to %llu", microstep_interval_ns); + return 0; +} + +static int z_stepper_motion_control_is_moving(const struct device *dev, bool *is_moving) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + *is_moving = config->timing_source->is_running(&data->timing_data); + LOG_DBG("Motor is %s moving", *is_moving ? "" : "not"); + return 0; +} + +static void update_remaining_steps(const struct device *dev) +{ + struct stepper_motion_control_data *data = dev->data; + + if (data->step_count > 0) { + data->step_count--; + } else if (data->step_count < 0) { + data->step_count++; + } +} + +static void update_actual_position(const struct device *dev) +{ + struct stepper_motion_control_data *data = dev->data; + + if (data->direction == STEPPER_DIRECTION_POSITIVE) { + data->actual_position++; + } else if (data->direction == STEPPER_DIRECTION_NEGATIVE) { + data->actual_position--; + } +} + +static void position_mode_task(const struct device *dev) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + stepper_step(config->stepper); + update_actual_position(dev); + + if (config->timing_source->needs_reschedule(dev) && data->step_count != 0) { + config->timing_source->start(&config->timing_config, &data->timing_data); + } else if (data->step_count == 0) { + stepper_trigger_callback(data->dev, STEPPER_MOTION_CONTROL_EVENT_STEPS_COMPLETED); + config->timing_source->stop(&config->timing_config, &data->timing_data); + } + + update_remaining_steps(dev); +} + +static void velocity_mode_task(const struct device *dev) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + + stepper_step(config->stepper); + update_actual_position(dev); + + if (config->timing_source->needs_reschedule(dev)) { + (void)config->timing_source->start(&config->timing_config, &data->timing_data); + } +} + +static int z_stepper_motion_control_set_mode(const struct device *dev, + enum stepper_motion_control_mode mode) +{ + if (mode == STEPPER_MOTION_CONTROL_MODE_RAMP) { + return -ENOTSUP; + } + return 0; +} + +static int z_stepper_motion_control_set_event_callback(const struct device *dev, + stepper_motion_control_event_callback_t cb, + void *user_data) +{ + struct stepper_motion_control_data *data = dev->data; + + K_SPINLOCK(&data->lock) { + data->callback = cb; + } + data->event_cb_user_data = user_data; + return 0; +} + +void stepper_handle_timing_signal(const struct device *dev) +{ + struct stepper_motion_control_data *data = dev->data; + + K_SPINLOCK(&data->lock) { + switch (data->run_mode) { + case STEPPER_RUN_MODE_POSITION: + position_mode_task(dev); + break; + case STEPPER_RUN_MODE_VELOCITY: + velocity_mode_task(dev); + break; + default: + LOG_WRN("Unsupported run mode: %d", data->run_mode); + break; + } + } +} + +static int stepper_motion_control_init(const struct device *dev) +{ + const struct stepper_motion_control_config *config = dev->config; + struct stepper_motion_control_data *data = dev->data; + int ret; + + data->dev = dev; + + if (config->timing_source->init) { + ret = config->timing_source->init(&config->timing_config, &data->timing_data); + if (ret < 0) { + LOG_ERR("Failed to initialize timing source: %d", ret); + return ret; + } + } +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS + + k_msgq_init(&data->event_msgq, data->event_msgq_buffer, + sizeof(enum stepper_motion_control_event), + CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_EVENT_QUEUE_LEN); + k_work_init(&data->event_callback_work, stepper_work_event_handler); +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS */ + + LOG_DBG("Stepper Control initialized for stepper driver %s", config->stepper->name); + return 0; +} + +static DEVICE_API(stepper_motion_control, stepper_motion_control_api) = { + .move_to = z_stepper_motion_control_move_to, + .move_by = z_stepper_motion_control_move_by, + .run = z_stepper_motion_control_run, + .set_step_interval = z_stepper_motion_control_set_step_interval, + .get_actual_position = z_stepper_motion_control_get_actual_position, + .set_reference_position = z_stepper_motion_control_set_reference_position, + .set_mode = z_stepper_motion_control_set_mode, + .is_moving = z_stepper_motion_control_is_moving, + .stop = z_stepper_motion_control_stop, + .set_event_callback = z_stepper_motion_control_set_event_callback, +}; + +#define STEPPER_MOTION_CONTROL_DEFINE(inst) \ + static const struct stepper_motion_control_config stepper_motion_control_config_##inst = { \ + .stepper = DEVICE_DT_GET(DT_INST_CHILD(inst, stepper)), \ + .timing_config.counter = \ + DEVICE_DT_GET_OR_NULL(DT_PHANDLE(DT_DRV_INST(inst), counter)), \ + .timing_source = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, counter), \ + (&step_counter_timing_source_api), (&step_work_timing_source_api)), \ + }; \ + struct stepper_motion_control_data stepper_motion_control_data_##inst = { \ + .timing_data.microstep_interval_ns = DT_INST_PROP(inst, step_tick_ns), \ + .timing_data.motion_control_dev = DEVICE_DT_GET(DT_DRV_INST(inst)), \ + .timing_data.stepper_handle_timing_signal_cb = stepper_handle_timing_signal, \ + }; \ + DEVICE_DT_INST_DEFINE(inst, stepper_motion_control_init, NULL, \ + &stepper_motion_control_data_##inst, \ + &stepper_motion_control_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_CONTROL_INIT_PRIORITY, &stepper_motion_control_api); + +DT_INST_FOREACH_STATUS_OKAY(STEPPER_MOTION_CONTROL_DEFINE) diff --git a/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_counter_timing.c similarity index 63% rename from drivers/stepper/step_dir/step_dir_stepper_counter_timing.c rename to drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_counter_timing.c index bad216961e575..b0e0baa645166 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c +++ b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_counter_timing.c @@ -3,25 +3,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include "step_dir_stepper_common.h" +#include "stepper_motion_control_timing_source.h" #include -LOG_MODULE_DECLARE(step_dir_stepper); +LOG_MODULE_REGISTER(stepper_counter_timing, CONFIG_STEPPER_CONTROL_LOG_LEVEL); static void step_counter_top_interrupt(const struct device *dev, void *user_data) { ARG_UNUSED(dev); - struct step_dir_stepper_common_data *data = user_data; + struct timing_source_data *data = user_data; - stepper_handle_timing_signal(data->dev); + data->stepper_handle_timing_signal_cb(data->motion_control_dev); } -int step_counter_timing_source_update(const struct device *dev, +int step_counter_timing_source_update(const struct timing_source_config *config, + struct timing_source_data *data, const uint64_t microstep_interval_ns) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; int ret; if (microstep_interval_ns == 0) { @@ -39,17 +37,17 @@ int step_counter_timing_source_update(const struct device *dev, irq_unlock(key); if (ret != 0) { - LOG_ERR("%s: Failed to set counter top value (error: %d)", dev->name, ret); + LOG_ERR("%s: Failed to set counter top value (error: %d)", + data->motion_control_dev->name, ret); return ret; } return 0; } -int step_counter_timing_source_start(const struct device *dev) +int step_counter_timing_source_start(const struct timing_source_config *config, + struct timing_source_data *data) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; int ret; ret = counter_start(config->counter); @@ -63,10 +61,9 @@ int step_counter_timing_source_start(const struct device *dev) return 0; } -int step_counter_timing_source_stop(const struct device *dev) +int step_counter_timing_source_stop(const struct timing_source_config *config, + struct timing_source_data *data) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; int ret; ret = counter_stop(config->counter); @@ -86,18 +83,14 @@ bool step_counter_timing_source_needs_reschedule(const struct device *dev) return false; } -bool step_counter_timing_source_is_running(const struct device *dev) +bool step_counter_timing_source_is_running(struct timing_source_data *data) { - struct step_dir_stepper_common_data *data = dev->data; - return data->counter_running; } -int step_counter_timing_source_init(const struct device *dev) +int step_counter_timing_source_init(const struct timing_source_config *config, + struct timing_source_data *data) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; - if (!device_is_ready(config->counter)) { LOG_ERR("Counter device is not ready"); return -ENODEV; diff --git a/drivers/stepper/step_dir/step_dir_stepper_timing_source.h b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_timing_source.h similarity index 50% rename from drivers/stepper/step_dir/step_dir_stepper_timing_source.h rename to drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_timing_source.h index 4c77993614afc..37bdda5d351e9 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_timing_source.h +++ b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_timing_source.h @@ -7,32 +7,56 @@ #define ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ #include +#include +#include + +struct timing_source_config { + const struct device *counter; +}; + +struct timing_source_data { + const struct device *motion_control_dev; + uint64_t microstep_interval_ns; + struct k_work_delayable stepper_dwork; + void (*stepper_handle_timing_signal_cb)(const struct device *dev); + +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING + struct counter_top_cfg counter_top_cfg; + bool counter_running; +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING */ +}; /** * @brief Initialize the stepper timing source. * - * @param dev Pointer to the device structure. + * @param config Pointer to the timing source configuration structure. + * @param data Pointer to the timing source data structure. * @return 0 on success, or a negative error code on failure. */ -typedef int (*stepper_timing_source_init)(const struct device *dev); +typedef int (*stepper_timing_source_init)(const struct timing_source_config *config, + struct timing_source_data *data); /** * @brief Update the stepper timing source. * - * @param dev Pointer to the device structure. + * @param config Pointer to the timing source configuration structure. + * @param data Pointer to the timing source data structure. * @param microstep_interval_ns Step interval in nanoseconds. * @return 0 on success, or a negative error code on failure. */ -typedef int (*stepper_timing_source_update)(const struct device *dev, +typedef int (*stepper_timing_source_update)(const struct timing_source_config *config, + struct timing_source_data *data, uint64_t microstep_interval_ns); /** * @brief Start the stepper timing source. * - * @param dev Pointer to the device structure. + * @param config Pointer to the timing source configuration structure. + * @param data Pointer to the timing source data structure. * @return 0 on success, or a negative error code on failure. */ -typedef int (*stepper_timing_source_start)(const struct device *dev); +typedef int (*stepper_timing_source_start)(const struct timing_source_config *config, + struct timing_source_data *data); /** * @brief Whether the stepper timing source requires rescheduling (keeps running @@ -46,18 +70,20 @@ typedef bool (*stepper_timing_sources_requires_reschedule)(const struct device * /** * @brief Stop the stepper timing source. * - * @param dev Pointer to the device structure. + * @param config Pointer to the timing source configuration structure. + * @param data Pointer to the timing source data structure. * @return 0 on success, or a negative error code on failure. */ -typedef int (*stepper_timing_source_stop)(const struct device *dev); +typedef int (*stepper_timing_source_stop)(const struct timing_source_config *config, + struct timing_source_data *data); /** * @brief Check if the stepper timing source is running. * - * @param dev Pointer to the device structure. + * @param data Pointer to the timing source data structure. * @return true if the timing source is running, false otherwise. */ -typedef bool (*stepper_timing_source_is_running)(const struct device *dev); +typedef bool (*stepper_timing_source_is_running)(struct timing_source_data *data); /** * @brief Stepper timing source API. @@ -72,8 +98,8 @@ struct stepper_timing_source_api { }; extern const struct stepper_timing_source_api step_work_timing_source_api; -#ifdef CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING +#ifdef CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING extern const struct stepper_timing_source_api step_counter_timing_source_api; -#endif /* CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING */ +#endif /* CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_COUNTER_TIMING */ #endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ */ diff --git a/drivers/stepper/step_dir/step_dir_stepper_work_timing.c b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_work_timing.c similarity index 54% rename from drivers/stepper/step_dir/step_dir_stepper_work_timing.c rename to drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_work_timing.c index ac605f1f49727..df7bd5a916060 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_work_timing.c +++ b/drivers/stepper_control/zephyr_stepper_motion_control/stepper_motion_control_work_timing.c @@ -3,13 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "step_dir_stepper_timing_source.h" -#include "step_dir_stepper_common.h" +#include "stepper_motion_control_timing_source.h" -static k_timeout_t stepper_movement_delay(const struct device *dev) +static k_timeout_t stepper_movement_delay(const struct timing_source_data *data) { - const struct step_dir_stepper_common_data *data = dev->data; - if (data->microstep_interval_ns == 0) { return K_FOREVER; } @@ -20,39 +17,40 @@ static k_timeout_t stepper_movement_delay(const struct device *dev) static void stepper_work_step_handler(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct step_dir_stepper_common_data *data = - CONTAINER_OF(dwork, struct step_dir_stepper_common_data, stepper_dwork); + struct timing_source_data *data = + CONTAINER_OF(dwork, struct timing_source_data, stepper_dwork); - stepper_handle_timing_signal(data->dev); + data->stepper_handle_timing_signal_cb(data->motion_control_dev); } -int step_work_timing_source_init(const struct device *dev) +int step_work_timing_source_init(const struct timing_source_config *config, + struct timing_source_data *data) { - struct step_dir_stepper_common_data *data = dev->data; - k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); return 0; } -int step_work_timing_source_update(const struct device *dev, const uint64_t microstep_interval_ns) +int step_work_timing_source_update(const struct timing_source_config *config, + struct timing_source_data *data, + const uint64_t microstep_interval_ns) { - ARG_UNUSED(dev); + ARG_UNUSED(config); + ARG_UNUSED(data); ARG_UNUSED(microstep_interval_ns); return 0; } -int step_work_timing_source_start(const struct device *dev) +int step_work_timing_source_start(const struct timing_source_config *config, + struct timing_source_data *data) { - struct step_dir_stepper_common_data *data = dev->data; - - return k_work_reschedule(&data->stepper_dwork, stepper_movement_delay(dev)); + ARG_UNUSED(config); + return k_work_reschedule(&data->stepper_dwork, stepper_movement_delay(data)); } -int step_work_timing_source_stop(const struct device *dev) +int step_work_timing_source_stop(const struct timing_source_config *config, + struct timing_source_data *data) { - struct step_dir_stepper_common_data *data = dev->data; - return k_work_cancel_delayable(&data->stepper_dwork); } @@ -62,10 +60,8 @@ bool step_work_timing_source_needs_reschedule(const struct device *dev) return true; } -bool step_work_timing_source_is_running(const struct device *dev) +bool step_work_timing_source_is_running(struct timing_source_data *data) { - struct step_dir_stepper_common_data *data = dev->data; - return k_work_delayable_is_pending(&data->stepper_dwork); } diff --git a/dts/bindings/stepper/adi/adi,tmc2209.yaml b/dts/bindings/stepper/adi/adi,tmc2209.yaml index 17e44b273082f..f9e74c82cbe54 100644 --- a/dts/bindings/stepper/adi/adi,tmc2209.yaml +++ b/dts/bindings/stepper/adi/adi,tmc2209.yaml @@ -18,7 +18,7 @@ description: | compatible: "adi,tmc2209" include: - - name: stepper-controller.yaml + - name: stepper.yaml properties: msx-gpios: diff --git a/dts/bindings/stepper/adi/adi,tmc51xx.yaml b/dts/bindings/stepper/adi/adi,tmc51xx.yaml index 003a91ea74a5d..eba7241226622 100644 --- a/dts/bindings/stepper/adi/adi,tmc51xx.yaml +++ b/dts/bindings/stepper/adi/adi,tmc51xx.yaml @@ -60,7 +60,7 @@ include: property-allowlist: - en-pwm-mode - test-mode - - name: stepper-controller.yaml + - name: stepper.yaml - name: base.yaml property-allowlist: - reg diff --git a/dts/bindings/stepper/adi/adi,tmc5xxx-stepper-driver.yaml b/dts/bindings/stepper/adi/adi,tmc5xxx-stepper-driver.yaml new file mode 100644 index 0000000000000..a4eba51207ec3 --- /dev/null +++ b/dts/bindings/stepper/adi/adi,tmc5xxx-stepper-driver.yaml @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG +# SPDX-License-Identifier: Apache-2.0 + +description: | + Analog Devices TMC50XX Stepper Motor Driver + + Example: + + &spi0 { + /* SPI bus options here, not shown */ + + /* Dual controller/driver for up to two 2-phase bipolar stepper motors */ + tmc50xx: tmc50xx@0 { + compatible = "adi,tmc50xx"; + reg = <0>; + spi-max-frequency = ; /* Maximum SPI bus frequency */ + + #address-cells = <1>; + #size-cells = <0>; + + poscmp-enable; test-mode; lock-gconf; /* ADI TMC Global configuration flags */ + clock-frequency = ; /* Internal/External Clock frequency */ + + motor: motor@0 { + status = "okay"; + reg = <0>; + + /* common stepper controller settings */ + invert-direction; + micro-step-res = <256>; + + /* ADI TMC stallguard settings specific to TMC50XX */ + activate-stallguard2; + stallguard-velocity-check-interval-ms=<100>; + stallguard2-threshold=<9>; + stallguard-threshold-velocity=<500000>; + + /* ADI TMC ramp generator as well as current settings */ + vstart = <10>; + a1 = <20>; + v1 = <30>; + d1 = <40>; + vmax = <50>; + amax = <60>; + dmax = <70>; + tzerowait = <80>; + vhigh = <90>; + vcoolthrs = <100>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; + }; + }; + + +compatible: "adi,tmc5xxx-stepper-driver" + +include: + - name: stepper.yaml + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - activate-stallguard2 + - stallguard2-threshold + - stallguard-threshold-velocity + - stallguard-velocity-check-interval-ms diff --git a/dts/bindings/stepper/allegro/allegro,a4979.yml b/dts/bindings/stepper/allegro/allegro,a4979.yml index c9e53b7a066c4..5ce264b50094b 100644 --- a/dts/bindings/stepper/allegro/allegro,a4979.yml +++ b/dts/bindings/stepper/allegro/allegro,a4979.yml @@ -24,7 +24,7 @@ description: | compatible: "allegro,a4979" include: - - name: stepper-controller.yaml + - name: stepper.yaml properties: m0-gpios: diff --git a/dts/bindings/stepper/stepper-controller.yaml b/dts/bindings/stepper/stepper.yaml similarity index 90% rename from dts/bindings/stepper/stepper-controller.yaml rename to dts/bindings/stepper/stepper.yaml index a73037bb6b975..eae57a18a25c9 100644 --- a/dts/bindings/stepper/stepper-controller.yaml +++ b/dts/bindings/stepper/stepper.yaml @@ -40,7 +40,3 @@ properties: description: | The GPIO pins used to send direction signals to the stepper motor. Pin will be driven high for forward direction and low for reverse direction. - - counter: - type: phandle - description: Counter used for generating step-accurate pulse signals. diff --git a/dts/bindings/stepper/ti/ti,drv84xx.yaml b/dts/bindings/stepper/ti/ti,drv84xx.yaml index 3b891d1d17c36..668c3d8861ef5 100644 --- a/dts/bindings/stepper/ti/ti,drv84xx.yaml +++ b/dts/bindings/stepper/ti/ti,drv84xx.yaml @@ -30,7 +30,7 @@ description: | compatible: "ti,drv84xx" include: - - name: stepper-controller.yaml + - name: stepper.yaml properties: fault-gpios: diff --git a/dts/bindings/stepper/zephyr,fake-stepper.yaml b/dts/bindings/stepper/zephyr,fake-stepper.yaml index d286dfbfeb4c5..3c6d01ff08593 100644 --- a/dts/bindings/stepper/zephyr,fake-stepper.yaml +++ b/dts/bindings/stepper/zephyr,fake-stepper.yaml @@ -7,4 +7,4 @@ description: | compatible: "zephyr,fake-stepper" -include: stepper-controller.yaml +include: stepper.yaml diff --git a/dts/bindings/stepper/zephyr,gpio-stepper.yaml b/dts/bindings/stepper/zephyr,gpio-stepper.yaml index 95477d213998b..00cc6f576e9e6 100644 --- a/dts/bindings/stepper/zephyr,gpio-stepper.yaml +++ b/dts/bindings/stepper/zephyr,gpio-stepper.yaml @@ -17,7 +17,7 @@ description: | compatible: "zephyr,gpio-stepper" -include: stepper-controller.yaml +include: stepper.yaml properties: gpios: diff --git a/dts/bindings/stepper/adi/adi,tmc50xx.yaml b/dts/bindings/stepper_control/adi/adi,tmc50xx-stepper-control.yaml similarity index 95% rename from dts/bindings/stepper/adi/adi,tmc50xx.yaml rename to dts/bindings/stepper_control/adi/adi,tmc50xx-stepper-control.yaml index eba9cd3f664c2..beaad67a4ee8d 100644 --- a/dts/bindings/stepper/adi/adi,tmc50xx.yaml +++ b/dts/bindings/stepper_control/adi/adi,tmc50xx-stepper-control.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya # SPDX-License-Identifier: Apache-2.0 description: | @@ -54,7 +54,7 @@ description: | }; -compatible: "adi,tmc50xx" +compatible: "adi,tmc50xx-stepper-control" include: - name: spi-device.yaml @@ -63,6 +63,8 @@ include: - poscmp-enable - test-mode - lock-gconf + - shaft1 + - shaft2 properties: "#address-cells": @@ -85,7 +87,6 @@ properties: child-binding: include: - - name: stepper-controller.yaml - name: base.yaml property-allowlist: - reg diff --git a/dts/bindings/stepper/adi/adi,trinamic-gconf.yaml b/dts/bindings/stepper_control/adi/adi,trinamic-gconf.yaml similarity index 93% rename from dts/bindings/stepper/adi/adi,trinamic-gconf.yaml rename to dts/bindings/stepper_control/adi/adi,trinamic-gconf.yaml index be25692bb0d05..405d2d365d644 100644 --- a/dts/bindings/stepper/adi/adi,trinamic-gconf.yaml +++ b/dts/bindings/stepper_control/adi/adi,trinamic-gconf.yaml @@ -81,3 +81,13 @@ properties: (depending on velocity thresholds). Switch from off to on state while in stand-still and at IHOLD= nominal IRUN current, only. + + shaft1: + type: boolean + description: | + Invert the direction of the motor shaft. + + shaft2: + type: boolean + description: | + Invert the direction of the motor shaft. diff --git a/dts/bindings/stepper/adi/adi,trinamic-ramp-generator.yaml b/dts/bindings/stepper_control/adi/adi,trinamic-ramp-generator.yaml similarity index 100% rename from dts/bindings/stepper/adi/adi,trinamic-ramp-generator.yaml rename to dts/bindings/stepper_control/adi/adi,trinamic-ramp-generator.yaml diff --git a/dts/bindings/stepper_control/zephyr,stepper-control.yaml b/dts/bindings/stepper_control/zephyr,stepper-control.yaml new file mode 100644 index 0000000000000..5dec8f82bdcde --- /dev/null +++ b/dts/bindings/stepper_control/zephyr,stepper-control.yaml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya + +description: | + Stepper Controller for stepper motor drivers + This binding is used to configure the stepper controller in Zephyr. + It is used to control the stepper motor driver. + + Example: + stepper_control: stepper_control { + compatible = "zephyr,stepper-control"; + stepper = <&gpio_stepper>; + step-tick-ns = <1000000>; + }; + +compatible: "zephyr,stepper-control" + +properties: + step-tick-ns: + type: int + required: true + description: | + The time in nanoseconds between each step signal. + + counter: + type: phandle + description: Counter used for generating step-accurate pulse signals. diff --git a/include/zephyr/drivers/stepper.h b/include/zephyr/drivers/stepper.h index 16632e2579ffc..2abc60d359313 100644 --- a/include/zephyr/drivers/stepper.h +++ b/include/zephyr/drivers/stepper.h @@ -88,24 +88,6 @@ enum stepper_run_mode { STEPPER_RUN_MODE_VELOCITY = 2, }; -/** - * @brief Stepper Events - */ -enum stepper_event { - /** Steps set using move_by or move_to have been executed */ - STEPPER_EVENT_STEPS_COMPLETED = 0, - /** Stall detected */ - STEPPER_EVENT_STALL_DETECTED = 1, - /** Left end switch status changes to pressed */ - STEPPER_EVENT_LEFT_END_STOP_DETECTED = 2, - /** Right end switch status changes to pressed */ - STEPPER_EVENT_RIGHT_END_STOP_DETECTED = 3, - /** Stepper has stopped */ - STEPPER_EVENT_STOPPED = 4, - /** Fault with the stepper controller detected */ - STEPPER_EVENT_FAULT_DETECTED = 5, -}; - /** * @cond INTERNAL_HIDDEN * @@ -127,6 +109,13 @@ typedef int (*stepper_enable_t)(const struct device *dev); */ typedef int (*stepper_disable_t)(const struct device *dev); +/** + * @brief Stop the stepper + * + * @see stepper_stop() for details. + */ +typedef int (*stepper_stop_t)(const struct device *dev); + /** * @brief Set the micro-step resolution * @@ -142,74 +131,21 @@ typedef int (*stepper_set_micro_step_res_t)(const struct device *dev, */ typedef int (*stepper_get_micro_step_res_t)(const struct device *dev, enum stepper_micro_step_resolution *resolution); -/** - * @brief Set the reference position of the stepper - * - * @see stepper_set_actual_position() for details. - */ -typedef int (*stepper_set_reference_position_t)(const struct device *dev, const int32_t value); - -/** - * @brief Get the actual a.k.a reference position of the stepper - * - * @see stepper_get_actual_position() for details. - */ -typedef int (*stepper_get_actual_position_t)(const struct device *dev, int32_t *value); - -/** - * @brief Callback function for stepper events - */ -typedef void (*stepper_event_callback_t)(const struct device *dev, const enum stepper_event event, - void *user_data); - -/** - * @brief Set the callback function to be called when a stepper event occurs - * - * @see stepper_set_event_callback() for details. - */ -typedef int (*stepper_set_event_callback_t)(const struct device *dev, - stepper_event_callback_t callback, void *user_data); -/** - * @brief Set the time interval between steps in nanoseconds. - * - * @see stepper_set_microstep_interval() for details. - */ -typedef int (*stepper_set_microstep_interval_t)(const struct device *dev, - const uint64_t microstep_interval_ns); -/** - * @brief Move the stepper relatively by a given number of micro-steps. - * - * @see stepper_move_by() for details. - */ -typedef int (*stepper_move_by_t)(const struct device *dev, const int32_t micro_steps); - -/** - * @brief Move the stepper to an absolute position in micro-steps. - * - * @see stepper_move_to() for details. - */ -typedef int (*stepper_move_to_t)(const struct device *dev, const int32_t micro_steps); /** - * @brief Run the stepper with a given step interval in a given direction - * - * @see stepper_run() for details. - */ -typedef int (*stepper_run_t)(const struct device *dev, const enum stepper_direction direction); - -/** - * @brief Stop the stepper + * @brief Set the stepper direction * - * @see stepper_stop() for details. + * @see stepper_set_direction() for details. */ -typedef int (*stepper_stop_t)(const struct device *dev); +typedef int (*stepper_set_direction_t)(const struct device *dev, + const enum stepper_direction direction); /** - * @brief Is the target position fo the stepper reached + * @brief Do a step * - * @see stepper_is_moving() for details. + * @see stepper_step() for details. */ -typedef int (*stepper_is_moving_t)(const struct device *dev, bool *is_moving); +typedef int (*stepper_step_t)(const struct device *dev); /** * @brief Stepper Driver API @@ -219,15 +155,8 @@ __subsystem struct stepper_driver_api { stepper_disable_t disable; stepper_set_micro_step_res_t set_micro_step_res; stepper_get_micro_step_res_t get_micro_step_res; - stepper_set_reference_position_t set_reference_position; - stepper_get_actual_position_t get_actual_position; - stepper_set_event_callback_t set_event_callback; - stepper_set_microstep_interval_t set_microstep_interval; - stepper_move_by_t move_by; - stepper_move_to_t move_to; - stepper_run_t run; - stepper_stop_t stop; - stepper_is_moving_t is_moving; + stepper_set_direction_t set_direction; + stepper_step_t step; }; /** @@ -322,222 +251,44 @@ static inline int z_impl_stepper_get_micro_step_res(const struct device *dev, } /** - * @brief Set the reference position of the stepper - * - * @param dev Pointer to the stepper driver instance. - * @param value The reference position to set in micro-steps. - * - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_set_reference_position(const struct device *dev, int32_t value); - -static inline int z_impl_stepper_set_reference_position(const struct device *dev, - const int32_t value) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->set_reference_position == NULL) { - return -ENOSYS; - } - return api->set_reference_position(dev, value); -} - -/** - * @brief Get the actual a.k.a reference position of the stepper - * - * @param dev pointer to the stepper driver instance - * @param value The actual position to get in micro-steps - * - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_get_actual_position(const struct device *dev, int32_t *value); - -static inline int z_impl_stepper_get_actual_position(const struct device *dev, int32_t *value) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->get_actual_position == NULL) { - return -ENOSYS; - } - return api->get_actual_position(dev, value); -} - -/** - * @brief Set the callback function to be called when a stepper event occurs - * - * @param dev pointer to the stepper driver instance - * @param callback Callback function to be called when a stepper event occurs - * passing NULL will disable the callback - * @param user_data User data to be passed to the callback function - * - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data); - -static inline int z_impl_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, - void *user_data) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->set_event_callback == NULL) { - return -ENOSYS; - } - return api->set_event_callback(dev, callback, user_data); -} - -/** - * @brief Set the time interval between steps in nanoseconds with immediate effect. - * - * @note Setting step interval does not set the stepper into motion, a combination of - * set_microstep_interval and move is required to set the stepper into motion. - * - * @param dev pointer to the stepper driver instance - * @param microstep_interval_ns time interval between steps in nanoseconds - * - * @retval -EIO General input / output error - * @retval -EINVAL If the requested step interval is not supported - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_set_microstep_interval(const struct device *dev, - uint64_t microstep_interval_ns); - -static inline int z_impl_stepper_set_microstep_interval(const struct device *dev, - const uint64_t microstep_interval_ns) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->set_microstep_interval == NULL) { - return -ENOSYS; - } - return api->set_microstep_interval(dev, microstep_interval_ns); -} - -/** - * @brief Set the micro-steps to be moved from the current position i.e. relative movement - * - * @details The stepper will move by the given number of micro-steps from the current position. - * This function is non-blocking. - * - * @param dev pointer to the stepper driver instance - * @param micro_steps target micro-steps to be moved from the current position - * - * @retval -ECANCELED If the stepper is disabled - * @retval -EIO General input / output error - * @retval 0 Success - */ -__syscall int stepper_move_by(const struct device *dev, int32_t micro_steps); - -static inline int z_impl_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - return api->move_by(dev, micro_steps); -} - -/** - * @brief Set the absolute target position of the stepper - * - * @details The stepper will move to the given micro-steps position from the reference position. - * This function is non-blocking. - * - * @param dev pointer to the stepper driver instance - * @param micro_steps target position to set in micro-steps - * - * @retval -ECANCELED If the stepper is disabled - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_move_to(const struct device *dev, int32_t micro_steps); - -static inline int z_impl_stepper_move_to(const struct device *dev, const int32_t micro_steps) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->move_to == NULL) { - return -ENOSYS; - } - return api->move_to(dev, micro_steps); -} - -/** - * @brief Run the stepper with a given step interval in a given direction - * - * @details The stepper shall be set into motion and run continuously until - * stalled or stopped using some other command, for instance, stepper_enable(false). This - * function is non-blocking. + * @brief Set the stepper direction * + * @note If STEPPER_CONTROL is enabled, check if the motor is not moving before changing the + * direction. * @param dev pointer to the stepper driver instance - * @param direction The direction to set + * @param direction Direction to set * - * @retval -ECANCELED If the stepper is disabled * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver * @retval 0 Success */ -__syscall int stepper_run(const struct device *dev, enum stepper_direction direction); +__syscall int stepper_set_direction(const struct device *dev, + const enum stepper_direction direction); -static inline int z_impl_stepper_run(const struct device *dev, - const enum stepper_direction direction) +static inline int z_impl_stepper_set_direction(const struct device *dev, + const enum stepper_direction direction) { const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - if (api->run == NULL) { - return -ENOSYS; - } - return api->run(dev, direction); + return api->set_direction(dev, direction); } /** - * @brief Stop the stepper - * @details Cancel all active movements, however keep the coils energized. - * - * @param dev pointer to the stepper driver instance + * @brief Do a step * - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_stop(const struct device *dev); - -static inline int z_impl_stepper_stop(const struct device *dev) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->stop == NULL) { - return -ENOSYS; - } - return api->stop(dev); -} - -/** - * @brief Check if the stepper is currently moving + * @note If STEPPER_CONTROL is enabled, calling this function will cause the stepper to move one + * step without position being tracked. * * @param dev pointer to the stepper driver instance - * @param is_moving Pointer to a boolean to store the moving status of the stepper - * * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver * @retval 0 Success */ -__syscall int stepper_is_moving(const struct device *dev, bool *is_moving); +__syscall int stepper_step(const struct device *dev); -static inline int z_impl_stepper_is_moving(const struct device *dev, bool *is_moving) +static inline int z_impl_stepper_step(const struct device *dev) { const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - if (api->is_moving == NULL) { - return -ENOSYS; - } - return api->is_moving(dev, is_moving); + return api->step(dev); } /** diff --git a/drivers/stepper/adi_tmc/adi_tmc_reg.h b/include/zephyr/drivers/stepper/adi_tmc_reg.h similarity index 65% rename from drivers/stepper/adi_tmc/adi_tmc_reg.h rename to include/zephyr/drivers/stepper/adi_tmc_reg.h index d26b5da2700d1..9212ffb5c4e86 100644 --- a/drivers/stepper/adi_tmc/adi_tmc_reg.h +++ b/include/zephyr/drivers/stepper/adi_tmc_reg.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_ -#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_ +#ifndef ZEPHYR_INCLUDE_DRIVERS_STEPPER_ADI_TMC_REG_H_ +#define ZEPHYR_INCLUDE_DRIVERS_STEPPER_ADI_TMC_REG_H_ #ifdef __cplusplus extern "C" { @@ -21,8 +21,8 @@ extern "C" { /** Common Registers for TMC50XX and TMC51XX */ #if defined(CONFIG_STEPPER_ADI_TMC50XX) || defined(CONFIG_STEPPER_ADI_TMC51XX) -#define TMC5XXX_WRITE_BIT 0x80U -#define TMC5XXX_ADDRESS_MASK 0x7FU +#define TMC5XXX_WRITE_BIT 0x80U +#define TMC5XXX_ADDRESS_MASK 0x7FU #define TMC5XXX_CLOCK_FREQ_SHIFT 24 @@ -34,8 +34,8 @@ extern "C" { #define TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE 2 #define TMC5XXX_RAMPMODE_HOLD_MODE 3 -#define TMC5XXX_SG_MIN_VALUE -64 -#define TMC5XXX_SG_MAX_VALUE 63 +#define TMC5XXX_SG_MIN_VALUE -64 +#define TMC5XXX_SG_MAX_VALUE 63 #define TMC5XXX_SW_MODE_SG_STOP_ENABLE BIT(10) #define TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT 16 @@ -83,24 +83,24 @@ extern "C" { #define TMC50XX_MOTOR_ADDR(m) (0x20 << (m)) #define TMC50XX_MOTOR_ADDR_DRV(m) ((m) << 4) -#define TMC50XX_RAMPMODE(motor) (0x00 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_XACTUAL(motor) (0x01 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VACTUAL(motor) (0x02 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VSTART(motor) (0x03 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_A1(motor) (0x04 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_V1(motor) (0x05 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_AMAX(motor) (0x06 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VMAX(motor) (0x07 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_DMAX(motor) (0x08 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_D1(motor) (0x0A | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VSTOP(motor) (0x0B | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_TZEROWAIT(motor) (0x0C | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_XTARGET(motor) (0x0D | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_SWMODE(motor) (0x14 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_RAMPSTAT(motor) (0x15 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_CHOPCONF(motor) (0x6C | TMC50XX_MOTOR_ADDR_DRV(motor)) -#define TMC50XX_COOLCONF(motor) (0x6D | TMC50XX_MOTOR_ADDR_DRV(motor)) -#define TMC50XX_DRVSTATUS(motor) (0x6F | TMC50XX_MOTOR_ADDR_DRV(motor)) +#define TMC50XX_RAMPMODE(motor) (0x00 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_XACTUAL(motor) (0x01 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_VACTUAL(motor) (0x02 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_VSTART(motor) (0x03 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_A1(motor) (0x04 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_V1(motor) (0x05 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_AMAX(motor) (0x06 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_VMAX(motor) (0x07 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_DMAX(motor) (0x08 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_D1(motor) (0x0A | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_VSTOP(motor) (0x0B | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_TZEROWAIT(motor) (0x0C | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_XTARGET(motor) (0x0D | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_SWMODE(motor) (0x14 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_RAMPSTAT(motor) (0x15 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_CHOPCONF(motor) (0x6C | TMC50XX_MOTOR_ADDR_DRV(motor)) +#define TMC50XX_COOLCONF(motor) (0x6D | TMC50XX_MOTOR_ADDR_DRV(motor)) +#define TMC50XX_DRVSTATUS(motor) (0x6F | TMC50XX_MOTOR_ADDR_DRV(motor)) #endif @@ -150,31 +150,31 @@ extern "C" { #define TMC51XX_GCONF_DIAG0_INT_PUSHPULL_SHIFT 12 #define TMC51XX_GCONF_TEST_MODE_SHIFT 17 -#define TMC51XX_IHOLD_IRUN 0x10 -#define TMC51XX_TPOWER_DOWN 0x11 -#define TMC51XX_TSTEP 0x12 -#define TMC51XX_TPWMTHRS 0x13 -#define TMC51XX_TCOOLTHRS 0x14 -#define TMC51XX_THIGH 0x15 - -#define TMC51XX_RAMPMODE TMC50XX_RAMPMODE(0) -#define TMC51XX_XACTUAL TMC50XX_XACTUAL(0) -#define TMC51XX_VACTUAL TMC50XX_VACTUAL(0) -#define TMC51XX_VSTART TMC50XX_VSTART(0) -#define TMC51XX_A1 TMC50XX_A1(0) -#define TMC51XX_V1 TMC50XX_V1(0) -#define TMC51XX_AMAX TMC50XX_AMAX(0) -#define TMC51XX_VMAX TMC50XX_VMAX(0) -#define TMC51XX_DMAX TMC50XX_DMAX(0) -#define TMC51XX_D1 TMC50XX_D1(0) -#define TMC51XX_VSTOP TMC50XX_VSTOP(0) -#define TMC51XX_TZEROWAIT TMC50XX_TZEROWAIT(0) -#define TMC51XX_XTARGET TMC50XX_XTARGET(0) -#define TMC51XX_SWMODE TMC50XX_SWMODE(0) -#define TMC51XX_RAMPSTAT TMC50XX_RAMPSTAT(0) -#define TMC51XX_CHOPCONF TMC50XX_CHOPCONF(0) -#define TMC51XX_COOLCONF TMC50XX_COOLCONF(0) -#define TMC51XX_DRVSTATUS TMC50XX_DRVSTATUS(0) +#define TMC51XX_IHOLD_IRUN 0x10 +#define TMC51XX_TPOWER_DOWN 0x11 +#define TMC51XX_TSTEP 0x12 +#define TMC51XX_TPWMTHRS 0x13 +#define TMC51XX_TCOOLTHRS 0x14 +#define TMC51XX_THIGH 0x15 + +#define TMC51XX_RAMPMODE TMC50XX_RAMPMODE(0) +#define TMC51XX_XACTUAL TMC50XX_XACTUAL(0) +#define TMC51XX_VACTUAL TMC50XX_VACTUAL(0) +#define TMC51XX_VSTART TMC50XX_VSTART(0) +#define TMC51XX_A1 TMC50XX_A1(0) +#define TMC51XX_V1 TMC50XX_V1(0) +#define TMC51XX_AMAX TMC50XX_AMAX(0) +#define TMC51XX_VMAX TMC50XX_VMAX(0) +#define TMC51XX_DMAX TMC50XX_DMAX(0) +#define TMC51XX_D1 TMC50XX_D1(0) +#define TMC51XX_VSTOP TMC50XX_VSTOP(0) +#define TMC51XX_TZEROWAIT TMC50XX_TZEROWAIT(0) +#define TMC51XX_XTARGET TMC50XX_XTARGET(0) +#define TMC51XX_SWMODE TMC50XX_SWMODE(0) +#define TMC51XX_RAMPSTAT TMC50XX_RAMPSTAT(0) +#define TMC51XX_CHOPCONF TMC50XX_CHOPCONF(0) +#define TMC51XX_COOLCONF TMC50XX_COOLCONF(0) +#define TMC51XX_DRVSTATUS TMC50XX_DRVSTATUS(0) #endif /* CONFIG_STEPPER_ADI_TMC51XX */ @@ -186,4 +186,4 @@ extern "C" { } #endif -#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_ */ +#endif /* ZEPHYR_INCLUDE_DRIVERS_STEPPER_ADI_TMC_REG_H_ */ diff --git a/include/zephyr/drivers/stepper/stepper_trinamic.h b/include/zephyr/drivers/stepper/stepper_trinamic.h index 7a27ef8de9c87..2b434ddb746cd 100644 --- a/include/zephyr/drivers/stepper/stepper_trinamic.h +++ b/include/zephyr/drivers/stepper/stepper_trinamic.h @@ -1,5 +1,5 @@ /** - * @file drivers/stepper/stepper_trinamic.h + * @file include/zephyr/drivers/stepper/stepper_trinamic.h * * @brief Public API for Trinamic Stepper Controller Specific Functions * @@ -223,7 +223,7 @@ int tmc50xx_stepper_set_ramp(const struct device *dev, * @retval -EIO General input / output error * @retval 0 Success */ -int tmc50xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity); +int tmc50xx_stepper_motion_control_set_max_velocity(const struct device *dev, uint32_t velocity); /** * @} diff --git a/include/zephyr/drivers/stepper_control.h b/include/zephyr/drivers/stepper_control.h new file mode 100644 index 0000000000000..65b1165abf5b2 --- /dev/null +++ b/include/zephyr/drivers/stepper_control.h @@ -0,0 +1,392 @@ +/* + * SPDX-FileCopyright-Text: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-File-Identifier: Apache-2.0 + */ + +/** + * @file drivers/stepper-control.h + * @brief Public API for Stepper Control Driver + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_STEPPER_CONTROL_H_ +#define ZEPHYR_INCLUDE_DRIVERS_STEPPER_CONTROL_H_ + +/** + * @brief Stepper Control Driver Interface + * @defgroup stepper_controller_interface Stepper Control Driver Interface + * @ingroup io_interfaces + * @{ + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum stepper_motion_control_mode { + /** Constant speed mode */ + STEPPER_MOTION_CONTROL_MODE_CONSTANT_SPEED = 0, + /** Ramp mode */ + STEPPER_MOTION_CONTROL_MODE_RAMP = 1, +}; + +/** + * @brief Stepper Events + */ +enum stepper_motion_control_event { + /** Steps set using move_by or move_to have been executed */ + STEPPER_MOTION_CONTROL_EVENT_STEPS_COMPLETED = 0, + /** Stall detected */ + STEPPER_MOTION_CONTROL_EVENT_STALL_DETECTED = 1, + /** Left end switch status changes to pressed */ + STEPPER_MOTION_CONTROL_EVENT_LEFT_END_STOP_DETECTED = 2, + /** Right end switch status changes to pressed */ + STEPPER_MOTION_CONTROL_EVENT_RIGHT_END_STOP_DETECTED = 3, + /** Stepper has stopped */ + STEPPER_MOTION_CONTROL_EVENT_STOPPED = 4, +}; + +/** + * @cond INTERNAL_HIDDEN + * + * Stepper Control driver API definition and system call entry points. + * + */ + +/** + * @brief Move the stepper to a specific position + */ +typedef int (*stepper_motion_control_move_to_t)(const struct device *dev, const int32_t micro_steps); + +/** + * @brief Move the stepper by a specific number of steps + */ +typedef int (*stepper_motion_control_move_by_t)(const struct device *dev, const int32_t micro_steps); + +/** + * @brief Run the stepper in a specific direction + */ +typedef int (*stepper_motion_control_run_t)(const struct device *dev, + const enum stepper_direction direction); + +/** + * @brief Stop the stepper + * + * @see stepper_stop() for details. + */ +typedef int (*stepper_motion_control_stop_t)(const struct device *dev); + +/** + * @brief Check if the stepper is currently moving + */ +typedef int (*stepper_motion_control_is_moving_t)(const struct device *dev, bool *is_moving); + +/** + * @brief Set the step interval of the stepper motor + */ +typedef int (*stepper_motion_control_set_step_interval_t)(const struct device *dev, + const uint64_t microstep_interval_ns); + +/** + * @brief Get the actual position of the stepper motor + */ +typedef int (*stepper_motion_control_get_actual_position_t)(const struct device *dev, int32_t *value); + +/** + * @brief Set the reference position of the stepper motor + */ +typedef int (*stepper_motion_control_set_reference_position_t)(const struct device *dev, + const int32_t value); + +/** + * @brief Set the stepper control mode + */ +typedef int (*stepper_motion_control_set_mode_t)(const struct device *dev, + const enum stepper_motion_control_mode mode); + +/** + * @brief Callback function for stepper events + */ +typedef void (*stepper_motion_control_event_callback_t)(const struct device *dev, + const enum stepper_motion_control_event event, + void *user_data); + +/** + * @brief Set the callback function to be called when a stepper event occurs + * + * @see stepper_set_event_callback() for details. + */ +typedef int (*stepper_motion_control_set_event_callback_t)(const struct device *dev, + stepper_motion_control_event_callback_t callback, + void *user_data); + +/** + * @brief Stepper Control Driver API + */ +__subsystem struct stepper_motion_control_driver_api { + stepper_motion_control_move_to_t move_to; + stepper_motion_control_move_by_t move_by; + stepper_motion_control_run_t run; + stepper_motion_control_stop_t stop; + stepper_motion_control_set_step_interval_t set_step_interval; + stepper_motion_control_get_actual_position_t get_actual_position; + stepper_motion_control_set_reference_position_t set_reference_position; + stepper_motion_control_set_mode_t set_mode; + stepper_motion_control_is_moving_t is_moving; + stepper_motion_control_set_event_callback_t set_event_callback; +}; + +/** + * @endcond + */ + +/** + * @brief Set the absolute target position of the stepper + * + * @details The stepper will move to the given micro-steps position from the reference position. + * This function is non-blocking. + * + * @param dev pointer to the stepper driver instance + * @param micro_steps target position to set in micro-steps + * + * @retval -ECANCELED If the stepper is disabled + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_move_to(const struct device *dev, const int32_t micro_steps); + +static inline int z_impl_stepper_motion_control_move_to(const struct device *dev, + const int32_t micro_steps) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->move_to(dev, micro_steps); +} + +/** + * @brief Set the micro-steps to be moved from the current position i.e. relative movement + * + * @details The stepper will move by the given number of micro-steps from the current position. + * This function is non-blocking. + * + * @param dev pointer to the stepper driver instance + * @param micro_steps target micro-steps to be moved from the current position + * + * @retval -ECANCELED If the stepper is disabled + * @retval -EIO General input / output error + * @retval 0 Success + */ +__syscall int stepper_motion_control_move_by(const struct device *dev, const int32_t micro_steps); + +static inline int z_impl_stepper_motion_control_move_by(const struct device *dev, + const int32_t micro_steps) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->move_by(dev, micro_steps); +} + +/** + * @brief Run the stepper with a given step interval in a given direction + * + * @details The stepper shall be set into motion and run continuously until + * stalled or stopped using some other command, for instance, stepper_enable(false). This + * function is non-blocking. + * + * @param dev pointer to the stepper driver instance + * @param direction The direction to set + * + * @retval -ECANCELED If the stepper is disabled + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_run(const struct device *dev, const enum stepper_direction direction); + +static inline int z_impl_stepper_motion_control_run(const struct device *dev, + const enum stepper_direction direction) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->run(dev, direction); +} + +/** + * @brief Stop the stepper + * + * @details The stepper shall be stopped immediately. This function is non-blocking. + * + * @param dev pointer to the stepper driver instance + * + * @retval -ECANCELED If the stepper is disabled + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_stop(const struct device *dev); + +static inline int z_impl_stepper_motion_control_stop(const struct device *dev) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->stop(dev); +} + +/** + * @brief Set the time interval between steps in nanoseconds with immediate effect. + * + * @note Setting step interval does not set the stepper into motion, a combination of + * set_microstep_interval and move is required to set the stepper into motion. + * + * @param dev pointer to the stepper driver instance + * @param microstep_interval_ns time interval between steps in nanoseconds + * + * @retval -EIO General input / output error + * @retval -EINVAL If the requested step interval is not supported + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_set_step_interval(const struct device *dev, + const uint64_t microstep_interval_ns); +static inline int z_impl_stepper_motion_control_set_step_interval(const struct device *dev, + const uint64_t microstep_interval_ns) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->set_step_interval(dev, microstep_interval_ns); +} + +/** + * @brief Get the actual a.k.a reference position of the stepper + * + * @param dev pointer to the stepper driver instance + * @param value The actual position to get in micro-steps + * + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_get_actual_position(const struct device *dev, int32_t *value); +static inline int z_impl_stepper_motion_control_get_actual_position(const struct device *dev, + int32_t *value) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->get_actual_position(dev, value); +} + +/** + * @brief Set the reference position of the stepper + * + * @param dev Pointer to the stepper driver instance. + * @param value The reference position to set in micro-steps. + * + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_set_reference_position(const struct device *dev, const int32_t value); + +static inline int z_impl_stepper_motion_control_set_reference_position(const struct device *dev, + const int32_t value) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->set_reference_position(dev, value); +} + +__syscall int stepper_motion_control_set_mode(const struct device *dev, + const enum stepper_motion_control_mode mode); +static inline int z_impl_stepper_motion_control_set_mode(const struct device *dev, + const enum stepper_motion_control_mode mode) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->set_mode(dev, mode); +} + +/** + * @brief Check if the stepper is currently moving + * + * @param dev pointer to the stepper driver instance + * @param is_moving Pointer to a boolean to store the moving status of the stepper + * + * @retval -EIO General input / output error + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_is_moving(const struct device *dev, bool *is_moving); + +static inline int z_impl_stepper_motion_control_is_moving(const struct device *dev, bool *is_moving) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->is_moving(dev, is_moving); +} + +/** + * @brief Set the callback function to be called when a stepper event occurs + * + * @param dev pointer to the stepper driver instance + * @param callback Callback function to be called when a stepper event occurs + * passing NULL will disable the callback + * @param user_data User data to be passed to the callback function + * + * @retval -ENOSYS If not implemented by device driver + * @retval 0 Success + */ +__syscall int stepper_motion_control_set_event_callback(const struct device *dev, + stepper_motion_control_event_callback_t callback, + void *user_data); + +static inline int z_impl_stepper_motion_control_set_event_callback( + const struct device *dev, stepper_motion_control_event_callback_t callback, void *user_data) +{ + const struct stepper_motion_control_driver_api *api = dev->api; + + return api->set_event_callback(dev, callback, user_data); +} + +__subsystem struct stepper_controller_driver_api { + int (*write)(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val); + int (*read)(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val); +}; + + +__syscall int stepper_controller_read(const struct device *dev, const uint8_t reg_addr, + uint32_t *reg_val); + +static inline int z_impl_stepper_controller_read(const struct device *dev, const uint8_t reg_addr, + uint32_t *reg_val) +{ + const struct stepper_controller_driver_api *api = dev->api; + + return api->read(dev, reg_addr, reg_val); +} + +__syscall int stepper_controller_write(const struct device *dev, const uint8_t reg_addr, + const uint32_t reg_val); + +static inline int z_impl_stepper_controller_write(const struct device *dev, const uint8_t reg_addr, + const uint32_t reg_val) +{ + const struct stepper_controller_driver_api *api = dev->api; + + return api->write(dev, reg_addr, reg_val); +} +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_STEPPER_CONTROL_H_ */ diff --git a/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay index e392a028cb32a..e927a2e28e3bd 100644 --- a/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay +++ b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay @@ -1,17 +1,31 @@ / { aliases { stepper = &gpio_stepper; + stepper-motion-control = &stepper_motion_control; + }; +}; + +&timers7 { + st,prescaler = <79>; + counter: counter { + status = "okay"; }; }; / { - gpio_stepper: gpio_stepper { - compatible = "zephyr,gpio-stepper"; + stepper_motion_control: stepper_motion_control { + compatible = "zephyr,stepper-control"; status = "okay"; - micro-step-res = <2>; - gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>, /* D8 */ - <&gpioc 7 GPIO_ACTIVE_HIGH>, /* D9 */ - <&gpiob 0 GPIO_ACTIVE_HIGH>, /* D10 */ - <&gpioa 7 GPIO_ACTIVE_HIGH>; /* D11 */ + step-tick-ns = <1000000>; + counter = <&counter>; + gpio_stepper: stepper { + compatible = "zephyr,gpio-stepper"; + status = "okay"; + micro-step-res = <2>; + gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>, /* D8 */ + <&gpioc 7 GPIO_ACTIVE_HIGH>, /* D9 */ + <&gpiob 0 GPIO_ACTIVE_HIGH>, /* D10 */ + <&gpioa 7 GPIO_ACTIVE_HIGH>; /* D11 */ + }; }; }; diff --git a/samples/drivers/stepper/generic/prj.conf b/samples/drivers/stepper/generic/prj.conf index 7b579deb99fba..c9151d72b4b5e 100644 --- a/samples/drivers/stepper/generic/prj.conf +++ b/samples/drivers/stepper/generic/prj.conf @@ -1,3 +1,4 @@ -CONFIG_STEPPER=y +CONFIG_ZEPHYR_STEPPER_MOTION_CONTROL_GENERATE_ISR_SAFE_EVENTS=y +CONFIG_STEPPER_CONTROL=y CONFIG_LOG=y CONFIG_INPUT=y diff --git a/samples/drivers/stepper/generic/src/main.c b/samples/drivers/stepper/generic/src/main.c index f1323e50850a2..5b36b3afde4b4 100644 --- a/samples/drivers/stepper/generic/src/main.c +++ b/samples/drivers/stepper/generic/src/main.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,8 @@ LOG_MODULE_REGISTER(stepper, CONFIG_STEPPER_LOG_LEVEL); static const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); +static const struct device *stepper_motion_control = + DEVICE_DT_GET(DT_ALIAS(stepper_motion_control)); enum stepper_mode { STEPPER_MODE_ENABLE, @@ -31,11 +34,12 @@ static int32_t ping_pong_target_position = static K_SEM_DEFINE(stepper_generic_sem, 0, 1); -static void stepper_callback(const struct device *dev, const enum stepper_event event, - void *user_data) +static void stepper_motion_control_callback(const struct device *dev, + const enum stepper_motion_control_event event, + void *user_data) { switch (event) { - case STEPPER_EVENT_STEPS_COMPLETED: + case STEPPER_MOTION_CONTROL_EVENT_STEPS_COMPLETED: k_sem_give(&stepper_generic_sem); break; default: @@ -71,9 +75,15 @@ int main(void) } LOG_DBG("stepper is %p, name is %s\n", stepper, stepper->name); - stepper_set_event_callback(stepper, stepper_callback, NULL); - stepper_set_reference_position(stepper, 0); - stepper_set_microstep_interval(stepper, CONFIG_STEP_INTERVAL_NS); + if (!device_is_ready(stepper_motion_control)) { + LOG_ERR("Device %s is not ready\n", stepper_motion_control->name); + return -ENODEV; + } + + stepper_motion_control_set_event_callback(stepper_motion_control, + stepper_motion_control_callback, NULL); + stepper_motion_control_set_reference_position(stepper_motion_control, 0); + stepper_motion_control_set_step_interval(stepper_motion_control, CONFIG_STEP_INTERVAL_NS); for (;;) { k_sem_take(&stepper_generic_sem, K_FOREVER); @@ -83,25 +93,29 @@ int main(void) LOG_INF("mode: enable\n"); break; case STEPPER_MODE_STOP: - stepper_stop(stepper); + stepper_motion_control_stop(stepper_motion_control); LOG_INF("mode: stop\n"); break; case STEPPER_MODE_ROTATE_CW: - stepper_run(stepper, STEPPER_DIRECTION_POSITIVE); + stepper_motion_control_run(stepper_motion_control, + STEPPER_DIRECTION_POSITIVE); LOG_INF("mode: rotate cw\n"); break; case STEPPER_MODE_ROTATE_CCW: - stepper_run(stepper, STEPPER_DIRECTION_NEGATIVE); + stepper_motion_control_run(stepper_motion_control, + STEPPER_DIRECTION_NEGATIVE); LOG_INF("mode: rotate ccw\n"); break; case STEPPER_MODE_PING_PONG_RELATIVE: ping_pong_target_position *= -1; - stepper_move_by(stepper, ping_pong_target_position); + stepper_motion_control_move_by(stepper_motion_control, + ping_pong_target_position); LOG_INF("mode: ping pong relative\n"); break; case STEPPER_MODE_PING_PONG_ABSOLUTE: ping_pong_target_position *= -1; - stepper_move_to(stepper, ping_pong_target_position); + stepper_motion_control_move_to(stepper_motion_control, + ping_pong_target_position); LOG_INF("mode: ping pong absolute\n"); break; case STEPPER_MODE_DISABLE: @@ -118,7 +132,8 @@ static void monitor_thread(void) for (;;) { int32_t actual_position; - stepper_get_actual_position(stepper, &actual_position); + stepper_motion_control_get_actual_position(stepper_motion_control, + &actual_position); LOG_DBG("Actual position: %d\n", actual_position); k_sleep(K_MSEC(CONFIG_MONITOR_THREAD_TIMEOUT_MS)); } diff --git a/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay b/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay index e3b70a3553140..5582550cc8bfe 100644 --- a/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay +++ b/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay @@ -1,6 +1,7 @@ / { aliases { - stepper = &tmc_stepper; + stepper = &tmc5xxx_stepper_custom; + stepper-motion-controller = &tmc_stepper_motion_controller; }; }; @@ -10,7 +11,7 @@ cs-gpios = <&gpiob 0 GPIO_ACTIVE_LOW>; status = "okay"; tmc50xx: tmc50xx@0 { - compatible = "adi,tmc50xx"; + compatible = "adi,tmc50xx-stepper-control"; reg = <0>; spi-max-frequency = ; /* Maximum SPI bus frequency */ @@ -19,19 +20,9 @@ clock-frequency = ; /* Internal/External Clock frequency */ - tmc_stepper: tmc_stepper@0 { - status = "okay"; + tmc_stepper_motion_controller: tmc_stepper_motion_controller@0 { reg = <0>; - /* common stepper controller settings */ - micro-step-res = <256>; - - /* ADI TMC stallguard settings specific to TMC50XX */ - activate-stallguard2; - stallguard-velocity-check-interval-ms=<1000>; - stallguard2-threshold=<30>; - stallguard-threshold-velocity=<200000>; - /* ADI TMC ramp generator as well as current settings */ vstart = <1000>; vstop = <10>; @@ -47,6 +38,17 @@ ihold = <1>; irun = <10>; iholddelay = <1>; + + activate-stallguard2; + stallguard-velocity-check-interval-ms=<1000>; + stallguard2-threshold=<63>; + stallguard-threshold-velocity=<200000>; + + tmc5xxx_stepper_custom: tmc5xxx_stepper { + compatible = "adi,tmc5xxx-stepper-driver"; + /* common stepper controller settings */ + micro-step-res = <256>; + }; }; }; }; diff --git a/samples/drivers/stepper/tmc50xx/prj.conf b/samples/drivers/stepper/tmc50xx/prj.conf index f17d0707cf5d4..d24ab1a4c7634 100644 --- a/samples/drivers/stepper/tmc50xx/prj.conf +++ b/samples/drivers/stepper/tmc50xx/prj.conf @@ -1,2 +1,3 @@ +CONFIG_STEPPER_CONTROL=y CONFIG_STEPPER=y CONFIG_LOG=y diff --git a/samples/drivers/stepper/tmc50xx/src/main.c b/samples/drivers/stepper/tmc50xx/src/main.c index 8604f610c9670..87075b435edaf 100644 --- a/samples/drivers/stepper/tmc50xx/src/main.c +++ b/samples/drivers/stepper/tmc50xx/src/main.c @@ -5,21 +5,27 @@ #include #include +#include #include #include #include +#include +LOG_MODULE_REGISTER(tmc50xx_sample, CONFIG_STEPPER_LOG_LEVEL); + const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); +const struct device *stepper_control = DEVICE_DT_GET(DT_ALIAS(stepper_motion_controller)); -int32_t ping_pong_target_position = CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * - DT_PROP(DT_ALIAS(stepper), micro_step_res); +int32_t ping_pong_target_position = + CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * DT_PROP(DT_ALIAS(stepper), micro_step_res); K_SEM_DEFINE(steps_completed_sem, 0, 1); -void stepper_callback(const struct device *dev, const enum stepper_event event, void *user_data) +void stepper_callback(const struct device *dev, const enum stepper_motion_control_event event, + void *user_data) { switch (event) { - case STEPPER_EVENT_STEPS_COMPLETED: + case STEPPER_MOTION_CONTROL_EVENT_STEPS_COMPLETED: k_sem_give(&steps_completed_sem); break; default: @@ -34,21 +40,34 @@ int main(void) printf("Device %s is not ready\n", stepper->name); return -ENODEV; } - printf("stepper is %p, name is %s\n", stepper, stepper->name); + LOG_DBG("stepper is %p, name is %s\n", stepper, stepper->name); + + if (!device_is_ready(stepper_control)) { + printf("Device %s is not ready\n", stepper_control->name); + return -ENODEV; + } - stepper_set_event_callback(stepper, stepper_callback, NULL); + LOG_DBG("Stepper controller is %p, name is %s\n", stepper_control, stepper_control->name); + stepper_motion_control_set_event_callback(stepper_control, stepper_callback, NULL); + + LOG_DBG("Enabling stepper driver %s", stepper->name); stepper_enable(stepper); - stepper_set_reference_position(stepper, 0); - stepper_move_by(stepper, ping_pong_target_position); + + LOG_DBG("Setting reference position to 0 for %s", stepper_control->name); + stepper_motion_control_set_reference_position(stepper_control, 0); + + LOG_DBG("Moving to %d", ping_pong_target_position); + stepper_motion_control_move_by(stepper_control, ping_pong_target_position); /* Change Max Velocity during runtime */ - int32_t tmc_velocity = DT_PROP(DT_ALIAS(stepper), vmax) * CONFIG_MAX_VELOCITY_MULTIPLIER; - (void)tmc50xx_stepper_set_max_velocity(stepper, tmc_velocity); + int32_t tmc_velocity = + DT_PROP(DT_ALIAS(stepper_motion_controller), vmax) * CONFIG_MAX_VELOCITY_MULTIPLIER; + (void)tmc50xx_stepper_motion_control_set_max_velocity(stepper_control, tmc_velocity); for (;;) { if (k_sem_take(&steps_completed_sem, K_FOREVER) == 0) { ping_pong_target_position *= -1; - stepper_move_by(stepper, ping_pong_target_position); + stepper_motion_control_move_by(stepper_control, ping_pong_target_position); } } return 0;