diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 0beea159b462a..cebedf9f50bf0 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -2400,7 +2400,7 @@ Documentation Infrastructure: - include/zephyr/drivers/stepper/ - include/zephyr/drivers/stepper.h - dts/bindings/stepper/ - - doc/hardware/peripherals/stepper.rst + - doc/hardware/peripherals/stepper/ - samples/drivers/stepper/ - tests/drivers/build_all/stepper/ - tests/drivers/stepper/ diff --git a/doc/hardware/peripherals/index.rst b/doc/hardware/peripherals/index.rst index 2ef752c27dfed..7286d88f5b308 100644 --- a/doc/hardware/peripherals/index.rst +++ b/doc/hardware/peripherals/index.rst @@ -58,7 +58,7 @@ Peripherals sensor/index.rst sent.rst spi.rst - stepper.rst + stepper/index.rst smbus.rst uart.rst usbc_vbus.rst diff --git a/doc/hardware/peripherals/stepper.rst b/doc/hardware/peripherals/stepper/index.rst similarity index 77% rename from doc/hardware/peripherals/stepper.rst rename to doc/hardware/peripherals/stepper/index.rst index 5e399b2d594df..d98a0ab487e13 100644 --- a/doc/hardware/peripherals/stepper.rst +++ b/doc/hardware/peripherals/stepper/index.rst @@ -3,22 +3,27 @@ Steppers ######## -The stepper driver API provides a set of functions for controlling and configuring stepper drivers. +The stepper driver subsystem consists of two device driver APIs: -Configure Stepper Driver -======================== +Stepper-DRV API +*************** + +The stepper driver API provides a common interface for stepper drivers. + +- Configure **micro-stepping resolution** using :c:func:`stepper_drv_set_micro_step_res` + and :c:func:`stepper_drv_get_micro_step_res`. +- **Enable** the stepper driver using :c:func:`stepper_drv_enable`. +- **Disable** the stepper driver using :c:func:`stepper_drv_disable`. +- Register an **event callback** using :c:func:`stepper_drv_set_event_cb`. + +Stepper API +*********** + +The stepper API provides a common interface for stepper controllers. -- Configure **micro-stepping resolution** using :c:func:`stepper_set_micro_step_res` - and :c:func:`stepper_get_micro_step_res`. - Configure **reference position** in microsteps using :c:func:`stepper_set_reference_position` and :c:func:`stepper_get_actual_position`. - Set **step interval** in nanoseconds between steps using :c:func:`stepper_set_microstep_interval` -- **Enable** the stepper driver using :c:func:`stepper_enable`. -- **Disable** the stepper driver using :c:func:`stepper_disable`. - -Control Stepper -=============== - - **Move by** +/- micro-steps also known as **relative movement** using :c:func:`stepper_move_by`. - **Move to** a specific position also known as **absolute movement** using :c:func:`stepper_move_to`. - Run continuously with a **constant step interval** in a specific direction until @@ -27,8 +32,10 @@ Control Stepper - Check if the stepper is **moving** using :c:func:`stepper_is_moving`. - Register an **event callback** using :c:func:`stepper_set_event_callback`. +.. _stepper-device-tree: + Device Tree -=========== +*********** In the context of stepper controllers device tree provides the initial hardware configuration for stepper drivers on a per device level. Each device must specify @@ -36,10 +43,16 @@ a device tree binding in Zephyr, and ideally, a set of hardware configuration op for things such as current settings, ramp parameters and furthermore. These can then be used in a boards devicetree to configure a stepper driver to its initial state. -See examples in: +Driver Composition Scenarios +============================ + +Below are two typical scenarios: + +.. toctree:: + :maxdepth: 1 -- :dtcompatible:`zephyr,h-bridge-stepper` -- :dtcompatible:`adi,tmc50xx` + integrated_controller_driver.rst + individual_controller_driver.rst Discord ======= @@ -48,9 +61,10 @@ Zephyr has a `stepper discord`_ channel for stepper related discussions, which is open to all. .. _stepper-api-reference: +.. _stepper-drv-api-reference: Stepper API Test Suite -====================== +********************** The stepper API test suite provides a set of tests that can be used to verify the functionality of stepper drivers. @@ -93,6 +107,10 @@ API Reference A common set of functions which should be implemented by all stepper drivers. +.. doxygengroup:: stepper_drv_interface + +A common set of functions which should be implemented by all stepper controllers. + .. doxygengroup:: stepper_interface Stepper controller specific APIs diff --git a/doc/hardware/peripherals/stepper/individual_controller_driver.rst b/doc/hardware/peripherals/stepper/individual_controller_driver.rst new file mode 100644 index 0000000000000..967e198ee36db --- /dev/null +++ b/doc/hardware/peripherals/stepper/individual_controller_driver.rst @@ -0,0 +1,48 @@ +.. _stepper-individual-controller-driver: + +Individual Stepper Motion Controller and Driver +############################################### + +A motion control driver implements ``stepper`` API, for instance, :dtcompatible:`zephyr,gpio-step-dir-controller` +and a hardware driver implements ``stepper_drv`` API, for instance, :dtcompatible:`adi,tmc2209`. + +Following is an example of a device tree configuration for a stepper driver with a dedicated stepper motion +controller: + +.. code-block:: dts + + / { + aliases { + stepper_drv = &tmc2209 + stepper = &step_dir_motion_control; + }; + + /* DEVICE_API: stepper_drv api */ + tmc2209: tmc2209 { + compatible = "adi,tmc2209"; + enable-gpios = <&gpioa 6 GPIO_ACTIVE_HIGH>; + msx-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>, <&gpioa 7 GPIO_ACTIVE_HIGH>; + }; + + /* DEVICE_API: stepper api */ + step_dir_motion_control: step_dir_motion_control { + compatible = "zephyr,gpio-step-dir-controller"; + step-gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>; + dir-gpios = <&gpioc 7 GPIO_ACTIVE_HIGH>; + invert-direction; + dual-edge-step; + step-width-ns = <1000>; + }; + }; + +Following the aforementioned configurations, the stepper driver subsystem can be used in the application code +as follows: + +.. code-block:: c + + static const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); + static const struct device *stepper_drv = DEVICE_DT_GET(DT_ALIAS(stepper_drv)); + ... + stepper_move_to(stepper, 200); + stepper_stop(stepper); + stepper_drv_disable(stepper_drv); diff --git a/doc/hardware/peripherals/stepper/integrated_controller_driver.rst b/doc/hardware/peripherals/stepper/integrated_controller_driver.rst new file mode 100644 index 0000000000000..5832a333b5cbc --- /dev/null +++ b/doc/hardware/peripherals/stepper/integrated_controller_driver.rst @@ -0,0 +1,90 @@ +.. _stepper-integrated-controller-driver: + +Integrated Stepper Motion Control and Driver +############################################ + +Devices which comprise of both motion controller and a stepper driver in a single IC. These devices +have to be modelled as multi-functional-device in device tree, implementing both ``stepper`` and +``stepper_drv`` APIs. An example of such a device is :dtcompatible:`adi,tmc50xx`. ``stepper`` API is +implemented by :dtcompatible:`adi,tmc50xx-motion-controller` and ``stepper_drv`` API is implemented by +:dtcompatible:`adi,tmc50xx-stepper-driver`. + +.. code-block:: dts + + / { + aliases { + x_axis_stepper_motor = &tmc50xx_0_motion_controller; + y_axis_stepper_motor = &tmc50xx_1_motion_controller; + x_axis_stepper_driver = &tmc50xx_0_stepper_driver; + y_axis_stepper_driver = &tmc50xx_1_stepper_driver; + }; + }; + + &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 */ + + poscmp-enable; test-mode; lock-gconf; /* ADI TMC Global configuration flags */ + clock-frequency = ; /* Internal/External Clock frequency */ + + /* DEVICE_API: stepper_drv api */ + tmc50xx_0_stepper_driver: tmc50xx_0_stepper_driver { + idx = <0>; + compatible = "adi,tmc50xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC50XX */ + stallguard2-threshold=<30>; + }; + + /* DEVICE_API: stepper api */ + tmc50xx_0_motion_controller: tmc50xx_0_motion_controller { + idx = <0>; + compatible = "adi,tmc50xx-motion-controller"; + ... + vmax = <900000>; + amax = <50000>; + ... + activate-stallguard2; + ... + }; + + /* DEVICE_API: stepper_drv api */ + tmc50xx_1_stepper_driver: tmc50xx_1_stepper_driver { + idx = <1>; + compatible = "adi,tmc50xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC50XX */ + stallguard2-threshold=<30>; + }; + + /* DEVICE_API: stepper api */ + tmc50xx_1_motion_controller: tmc50xx_1_motion_controller { + idx = <1>; + compatible = "adi,tmc50xx-motion-controller"; + ... + vstart = <1000>; + ... + stallguard-threshold-velocity=<200000>; + }; + }; + }; + +Following the aforementioned configurations, the stepper driver subsystem can be used in the application code +as follows: + +.. code-block:: c + + static const struct device *x_stepper = DEVICE_DT_GET(DT_ALIAS(x_axis_stepper_motor)); + static const struct device *x_stepper_drv = DEVICE_DT_GET(DT_ALIAS(x_axis_stepper_driver)); + static const struct device *y_stepper = DEVICE_DT_GET(DT_ALIAS(y_axis_stepper_motor)); + static const struct device *y_stepper_drv = DEVICE_DT_GET(DT_ALIAS(y_axis_stepper_driver)); + ... + stepper_move_to(x_stepper, 200); + stepper_stop(x_stepper); + stepper_drv_disable(x_stepper_drv); + stepper_drv_disable(y_stepper_drv); diff --git a/drivers/stepper/CMakeLists.txt b/drivers/stepper/CMakeLists.txt index 30dc1602277c9..4054d4deef488 100644 --- a/drivers/stepper/CMakeLists.txt +++ b/drivers/stepper/CMakeLists.txt @@ -6,16 +6,12 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h) # zephyr-keep-sorted-start add_subdirectory(adi_tmc) add_subdirectory(allegro) +add_subdirectory(gpio_stepper) add_subdirectory(ti) # zephyr-keep-sorted-stop -# zephyr-keep-sorted-start -add_subdirectory(step_dir) -# zephyr-keep-sorted-stop - zephyr_library() zephyr_library_property(ALLOW_EMPTY TRUE) -zephyr_library_sources_ifdef(CONFIG_FAKE_STEPPER fake_stepper_controller.c) -zephyr_library_sources_ifdef(CONFIG_H_BRIDGE_STEPPER h_bridge_stepper.c) +zephyr_library_sources_ifdef(CONFIG_FAKE_STEPPER fake_stepper.c) zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c) diff --git a/drivers/stepper/Kconfig b/drivers/stepper/Kconfig index f35ec8f8d3662..81c6114500b6d 100644 --- a/drivers/stepper/Kconfig +++ b/drivers/stepper/Kconfig @@ -24,17 +24,13 @@ config STEPPER_SHELL help Enable stepper shell for testing. -comment "Stepper Driver Common" - -rsource "step_dir/Kconfig" - comment "Stepper Drivers" # zephyr-keep-sorted-start rsource "Kconfig.fake" -rsource "Kconfig.h_bridge" rsource "adi_tmc/Kconfig" rsource "allegro/Kconfig" +rsource "gpio_stepper/Kconfig" rsource "ti/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/stepper/Kconfig.fake b/drivers/stepper/Kconfig.fake index 942a556f1be2f..c4de62b602f53 100644 --- a/drivers/stepper/Kconfig.fake +++ b/drivers/stepper/Kconfig.fake @@ -4,8 +4,8 @@ # SPDX-License-Identifier: Apache-2.0 config FAKE_STEPPER - bool "Fake stepper driver" + bool "Fake stepper" default y - depends on DT_HAS_ZEPHYR_FAKE_STEPPER_ENABLED + depends on DT_HAS_ZEPHYR_FAKE_STEPPER_DRIVER_ENABLED || DT_HAS_ZEPHYR_FAKE_STEPPER_CONTROLLER_ENABLED help - Enable support for the FFF-based fake stepper driver. + Enable support for the FFF-based fake stepper controller & driver. diff --git a/drivers/stepper/adi_tmc/CMakeLists.txt b/drivers/stepper/adi_tmc/CMakeLists.txt index f11ca9e51486d..e75b04a11941a 100644 --- a/drivers/stepper/adi_tmc/CMakeLists.txt +++ b/drivers/stepper/adi_tmc/CMakeLists.txt @@ -5,8 +5,8 @@ zephyr_library() zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC2209 tmc22xx.c) -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx.c) -add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx) +add_subdirectory(tmc50xx) +add_subdirectory(tmc51xx) add_subdirectory(bus) add_subdirectory(common) diff --git a/drivers/stepper/adi_tmc/Kconfig b/drivers/stepper/adi_tmc/Kconfig index 004b736eaf2a7..180c05508035e 100644 --- a/drivers/stepper/adi_tmc/Kconfig +++ b/drivers/stepper/adi_tmc/Kconfig @@ -6,5 +6,5 @@ comment "ADI Trinamic Stepper Drivers" rsource "bus/Kconfig" rsource "Kconfig.tmc22xx" -rsource "Kconfig.tmc50xx" +rsource "tmc50xx/Kconfig.tmc50xx" rsource "tmc51xx/Kconfig.tmc51xx" diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc22xx b/drivers/stepper/adi_tmc/Kconfig.tmc22xx index 081d4f5a52e7e..0ab030fc93434 100644 --- a/drivers/stepper/adi_tmc/Kconfig.tmc22xx +++ b/drivers/stepper/adi_tmc/Kconfig.tmc22xx @@ -4,7 +4,6 @@ config STEPPER_ADI_TMC2209 bool "Activate trinamic tmc2209 stepper driver" depends on DT_HAS_ADI_TMC2209_ENABLED - select STEP_DIR_STEPPER default y help Stepper driver for TMC2209. diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc50xx b/drivers/stepper/adi_tmc/Kconfig.tmc50xx deleted file mode 100644 index a423c88430597..0000000000000 --- a/drivers/stepper/adi_tmc/Kconfig.tmc50xx +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz -# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya -# SPDX-License-Identifier: Apache-2.0 - -config STEPPER_ADI_TMC50XX - bool "Activate trinamic tmc50xx stepper driver" - depends on DT_HAS_ADI_TMC50XX_ENABLED - select STEPPER_ADI_TMC_SPI - default y - -if STEPPER_ADI_TMC50XX - -module = TMC50XX -module-str = tmc50xx -rsource "Kconfig.tmc_rampgen_template" - -endif # STEPPER_ADI_TMC51XX diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template b/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template index a357f8917d87b..a3a79bd490674 100644 --- a/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template +++ b/drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template @@ -3,7 +3,6 @@ config STEPPER_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 help When DIAG0 pin is not available, the driver automatically falls back to diff --git a/drivers/stepper/adi_tmc/tmc22xx.c b/drivers/stepper/adi_tmc/tmc22xx.c index fb73dc35fc28d..9d4b2cd3afd8f 100644 --- a/drivers/stepper/adi_tmc/tmc22xx.c +++ b/drivers/stepper/adi_tmc/tmc22xx.c @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "../step_dir/step_dir_stepper_common.h" +#include +#include #include LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL); @@ -12,20 +13,16 @@ LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL); #define MSX_PIN_STATE_COUNT 4 struct tmc22xx_config { - struct step_dir_stepper_common_config common; const struct gpio_dt_spec enable_pin; const struct gpio_dt_spec *msx_pins; - enum stepper_micro_step_resolution *msx_resolutions; + enum stepper_drv_micro_step_resolution *msx_resolutions; }; struct tmc22xx_data { - struct step_dir_stepper_common_data common; - enum stepper_micro_step_resolution resolution; + enum stepper_drv_micro_step_resolution resolution; }; -STEP_DIR_STEPPER_STRUCT_CHECK(struct tmc22xx_config, struct tmc22xx_data); - -static int tmc22xx_stepper_enable(const struct device *dev) +static int tmc22xx_enable(const struct device *dev) { const struct tmc22xx_config *config = dev->config; @@ -33,7 +30,7 @@ static int tmc22xx_stepper_enable(const struct device *dev) return gpio_pin_set_dt(&config->enable_pin, 1); } -static int tmc22xx_stepper_disable(const struct device *dev) +static int tmc22xx_disable(const struct device *dev) { const struct tmc22xx_config *config = dev->config; @@ -41,8 +38,8 @@ static int tmc22xx_stepper_disable(const struct device *dev) return gpio_pin_set_dt(&config->enable_pin, 0); } -static int tmc22xx_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution micro_step_res) +static int tmc22xx_set_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution micro_step_res) { struct tmc22xx_data *data = dev->data; const struct tmc22xx_config *config = dev->config; @@ -78,8 +75,8 @@ static int tmc22xx_stepper_set_micro_step_res(const struct device *dev, return -ENOTSUP; } -static int tmc22xx_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *micro_step_res) +static int tmc22xx_get_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution *micro_step_res) { struct tmc22xx_data *data = dev->data; @@ -131,36 +128,21 @@ static int tmc22xx_stepper_init(const struct device *dev) return ret; } - ret = tmc22xx_stepper_set_micro_step_res(dev, data->resolution); + ret = tmc22xx_set_micro_step_res(dev, data->resolution); if (ret < 0) { LOG_ERR("Failed to set microstep resolution: %d", ret); return ret; } } - ret = step_dir_stepper_common_init(dev); - if (ret < 0) { - LOG_ERR("Failed to init step dir common stepper: %d", ret); - return ret; - } - return 0; } -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, - .set_micro_step_res = tmc22xx_stepper_set_micro_step_res, - .get_micro_step_res = tmc22xx_stepper_get_micro_step_res, +static DEVICE_API(stepper_drv, tmc22xx_stepper_api) = { + .enable = tmc22xx_enable, + .disable = tmc22xx_disable, + .set_micro_step_res = tmc22xx_set_micro_step_res, + .get_micro_step_res = tmc22xx_get_micro_step_res, }; #define TMC22XX_STEPPER_DEFINE(inst, msx_table) \ @@ -176,14 +158,12 @@ static DEVICE_API(stepper, tmc22xx_stepper_api) = { )) \ \ static const struct tmc22xx_config tmc22xx_config_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ .enable_pin = GPIO_DT_SPEC_INST_GET(inst, en_gpios), \ .msx_resolutions = msx_table, \ IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, msx_gpios), \ (.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, \ @@ -191,11 +171,11 @@ static DEVICE_API(stepper, tmc22xx_stepper_api) = { &tmc22xx_stepper_api); #define DT_DRV_COMPAT adi_tmc2209 -static enum stepper_micro_step_resolution tmc2209_msx_resolutions[MSX_PIN_STATE_COUNT] = { - STEPPER_MICRO_STEP_8, - STEPPER_MICRO_STEP_32, - STEPPER_MICRO_STEP_64, - STEPPER_MICRO_STEP_16, +static enum stepper_drv_micro_step_resolution tmc2209_msx_resolutions[MSX_PIN_STATE_COUNT] = { + STEPPER_DRV_MICRO_STEP_8, + STEPPER_DRV_MICRO_STEP_32, + STEPPER_DRV_MICRO_STEP_64, + STEPPER_DRV_MICRO_STEP_16, }; DT_INST_FOREACH_STATUS_OKAY_VARGS(TMC22XX_STEPPER_DEFINE, tmc2209_msx_resolutions) #undef DT_DRV_COMPAT diff --git a/drivers/stepper/adi_tmc/tmc50xx.c b/drivers/stepper/adi_tmc/tmc50xx.c deleted file mode 100644 index 2a2cdefe19600..0000000000000 --- a/drivers/stepper/adi_tmc/tmc50xx.c +++ /dev/null @@ -1,779 +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 -#include - -#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) -{ - __ASSERT(actual_velocity != NULL, "actual_velocity pointer must not be NULL"); - 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: - case TMC5XXX_POS_REACHED: - case TMC5XXX_POS_REACHED_AND_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) -{ - 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; -} - -static int tmc50xx_stepper_stop(const struct device *dev) -{ - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - - err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), 0); - if (err != 0) { - return -EIO; - } - - 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, - .stop = tmc50xx_stepper_stop, - .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))), \ - .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/tmc50xx/CMakeLists.txt b/drivers/stepper/adi_tmc/tmc50xx/CMakeLists.txt new file mode 100644 index 0000000000000..d527946d80151 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX_MOTION_CONTROLLER tmc50xx_motion_controller.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX_STEPPER_DRIVER tmc50xx_stepper_driver.c) diff --git a/drivers/stepper/adi_tmc/tmc50xx/Kconfig.tmc50xx b/drivers/stepper/adi_tmc/tmc50xx/Kconfig.tmc50xx new file mode 100644 index 0000000000000..f7f75a3800ffc --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/Kconfig.tmc50xx @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +config STEPPER_ADI_TMC50XX + bool "Activate trinamic tmc50xx stepper driver" + depends on DT_HAS_ADI_TMC50XX_ENABLED + select STEPPER_ADI_TMC_SPI + default y + +config STEPPER_ADI_TMC50XX_MOTION_CONTROLLER + bool "Activate trinamic tmc50xx motion controller" + depends on DT_HAS_ADI_TMC50XX_MOTION_CONTROLLER_ENABLED && STEPPER_ADI_TMC50XX + default y + +config STEPPER_ADI_TMC50XX_STEPPER_DRIVER + bool "Activate trinamic tmc50xx stepper driver" + depends on DT_HAS_ADI_TMC50XX_STEPPER_DRIVER_ENABLED && STEPPER_ADI_TMC50XX + default y + +if STEPPER_ADI_TMC50XX + +module = TMC50XX +module-str = tmc50xx +source "drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template" + +endif # STEPPER_ADI_TMC50XX diff --git a/drivers/stepper/adi_tmc/tmc50xx/tmc50xx.c b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx.c new file mode 100644 index 0000000000000..31e7fb66906e3 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx.c @@ -0,0 +1,309 @@ +/* + * 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 +#include "tmc50xx.h" +#include + +#include +LOG_MODULE_REGISTER(tmc50xx, CONFIG_STEPPER_LOG_LEVEL); + +struct tmc50xx_data { + struct k_sem sem; + /* Work item to run the callback in a thread context. */ + struct k_work_delayable rampstat_callback_dwork; + const struct device *dev; + uint8_t work_index; +}; + +struct tmc50xx_config { + const uint32_t gconf; + struct spi_dt_spec spi; + const uint32_t clock_frequency; + const struct device **stepper_drivers; + uint8_t num_steppers; + const struct device **motion_controllers; + uint8_t num_motion_controllers; +}; + +int tmc50xx_read_actual_position(const struct device *dev, const uint8_t index, int32_t *position) +{ + int err; + + err = tmc50xx_read(dev, TMC50XX_XACTUAL(index), position); + if (err != 0) { + return -EIO; + } + return 0; +} + +int tmc50xx_get_clock_frequency(const struct device *dev) +{ + const struct tmc50xx_config *config = dev->config; + + return config->clock_frequency; +} + +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; +} + +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; +} + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG + +static void log_stallguard(const struct device *dev, const uint32_t drv_status) +{ + int32_t position; + int err; + + err = read_actual_position(dev, &position); + if (err != 0) { + LOG_ERR("%s: Failed to read XACTUAL register", dev->name); + return; + } + + __maybe_unused const uint8_t sg_result = + FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); + __maybe_unused const bool sg_status = + FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); + + LOG_DBG("%s position: %d | sg result: %3d status: %d", dev->name, position, sg_result, + sg_status); +} + +#endif + +void tmc50xx_rampstat_work_reschedule(const struct device *dev) +{ + struct tmc50xx_data *data = dev->data; + + k_work_reschedule(&data->rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); +} + +static void rampstat_work(const struct device *dev) +{ + struct tmc50xx_data *data = dev->data; + __maybe_unused const struct tmc50xx_config *config = dev->config; + uint32_t drv_status; + int err; + + err = tmc50xx_read(dev, TMC50XX_DRVSTATUS(data->work_index), &drv_status); + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", data->dev->name); + return; + } +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG + log_stallguard(dev, drv_status); +#endif + if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { + LOG_INF("%s: Stall detected", data->dev->name); + err = tmc50xx_write(dev, TMC50XX_RAMPMODE(data->work_index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + LOG_ERR("%s: Failed to stop motor", data->dev->name); + return; + } + } + + uint32_t rampstat_value; + + err = tmc50xx_read(dev, TMC50XX_RAMPSTAT(data->work_index), &rampstat_value); + if (err != 0) { + LOG_ERR("%s: Failed to read RAMPSTAT register", data->dev->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) { +#ifdef CONFIG_STEPPER_ADI_TMC50XX_MOTION_CONTROLLER + case TMC5XXX_STOP_LEFT_EVENT: + LOG_DBG("RAMPSTAT %s:Left end-stop detected", data->dev->name); + tmc50xx_motion_controller_trigger_cb( + config->motion_controllers[data->work_index], + STEPPER_EVENT_LEFT_END_STOP_DETECTED); + break; + + case TMC5XXX_STOP_RIGHT_EVENT: + LOG_DBG("RAMPSTAT %s:Right end-stop detected", data->dev->name); + tmc50xx_motion_controller_trigger_cb( + config->motion_controllers[data->work_index], + STEPPER_EVENT_RIGHT_END_STOP_DETECTED); + break; + + case TMC5XXX_POS_REACHED_EVENT: + case TMC5XXX_POS_REACHED: + case TMC5XXX_POS_REACHED_AND_EVENT: + LOG_DBG("RAMPSTAT %s:Position reached", data->dev->name); + tmc50xx_motion_controller_trigger_cb( + config->motion_controllers[data->work_index], + STEPPER_EVENT_STEPS_COMPLETED); + break; +#endif +#ifdef CONFIG_STEPPER_ADI_TMC50XX_STEPPER_DRIVER + case TMC5XXX_STOP_SG_EVENT: + LOG_DBG("RAMPSTAT %s:Stall detected", data->dev->name); + tmc50xx_stallguard_enable(data->dev, false); + tmc50xx_driver_trigger_cb(config->stepper_drivers[data->work_index], + STEPPER_DRV_EVENT_STALL_DETECTED); + break; +#endif + default: + LOG_ERR("Illegal ramp stat bit field 0x%x", ramp_stat_values); + break; + } + } else { + tmc50xx_rampstat_work_reschedule(dev); + } +} + +static void rampstat_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct tmc50xx_data *data = + CONTAINER_OF(dwork, struct tmc50xx_data, rampstat_callback_dwork); + const struct device *dev = data->dev; + const struct tmc50xx_config *config = dev->config; + + for (uint8_t i = 0; i < config->num_steppers; i++) { + data->work_index = motion_controller_index(config->motion_controllers[i]); + rampstat_work(dev); + } +} + +static int tmc50xx_init(const struct device *dev) +{ + struct tmc50xx_data *data = dev->data; + const struct tmc50xx_config *config = dev->config; + int err; + + LOG_DBG("Initializing TMC50XX stepper motor controller %s", dev->name); + 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("Num of motion controllers: %d %s", config->num_motion_controllers, + config->motion_controllers[0]->name); + for (uint8_t i = 0; i < config->num_steppers; i++) { + if (config->stepper_drivers[i] != NULL) { + LOG_DBG("Stepper driver %s", config->stepper_drivers[i]->name); + } + } + + for (uint8_t i = 0; i < config->num_motion_controllers; i++) { + if (config->motion_controllers[i] != NULL) { + LOG_DBG("Motion controller %s", config->motion_controllers[i]->name); + } + } + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + + LOG_DBG("Device %s initialized", dev->name); + return 0; +} + +#define TMC50XX_CHILD_DEVICES_ARRAY(inst, compat) \ + {DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(inst, TMC50XX_CHILD_DEVICE_GET, compat)} + +#define TMC50XX_CHILD_DEVICE_GET(node_id, compat) \ + COND_CODE_1(DT_NODE_HAS_COMPAT(node_id, compat), (DEVICE_DT_GET(node_id),), ()) + +#define TMC50XX_DEFINE(inst) \ + static const struct device *tmc50xx_stepper_drivers_##inst[] = \ + TMC50XX_CHILD_DEVICES_ARRAY(inst, adi_tmc50xx_stepper_driver); \ + static const struct device *tmc50xx_motion_controllers_##inst[] = \ + TMC50XX_CHILD_DEVICES_ARRAY(inst, adi_tmc50xx_motion_controller); \ + BUILD_ASSERT(ARRAY_SIZE(tmc50xx_motion_controllers_##inst) <= 2, \ + "tmc50xx can drive two steppers at max"); \ + BUILD_ASSERT(ARRAY_SIZE(tmc50xx_stepper_drivers_##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 = { \ + .dev = DEVICE_DT_GET(DT_DRV_INST(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))), \ + .clock_frequency = \ + DT_INST_PROP(inst, clock_frequency), /* Child device references */ \ + .stepper_drivers = tmc50xx_stepper_drivers_##inst, \ + .num_steppers = ARRAY_SIZE(tmc50xx_stepper_drivers_##inst), \ + .motion_controllers = tmc50xx_motion_controllers_##inst, \ + .num_motion_controllers = ARRAY_SIZE(tmc50xx_motion_controllers_##inst), \ + }; \ + 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/tmc50xx/tmc50xx.h b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx.h new file mode 100644 index 0000000000000..37bdf13064d68 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx.h @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC50XX_H_ +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC50XX_H_ + +#include +#include +#include + +/* + * @brief Write a value to a TMC50XX register. + * @param dev Pointer to the TMC50XX device. + * @param reg_addr Register address to write to. + * @param reg_val Value to write to the register. + * @retval -EIO on failure, 0 on success + */ +int tmc50xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val); + +/* + * @brief Read a value from a TMC50XX register. + * @param dev Pointer to the TMC50XX device. + * @param reg_addr Register address to read from. + * @param reg_val Pointer to store the read value. + * @retval -EIO on failure, 0 on success + */ +int tmc50xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val); + +/** + * @brief Trigger the registered callback for motion controller events. + * + * @param dev Pointer to the TMC50XX motion controller device. + * @param event The stepper event that occurred. + */ +void tmc50xx_motion_controller_trigger_cb(const struct device *dev, const enum stepper_event event); + +/** + * @brief Trigger the registered callback for stepper driver events. + * + * @param dev Pointer to the TMC50XX stepper driver device. + * @param event The stepper driver event that occurred. + */ +void tmc50xx_driver_trigger_cb(const struct device *dev, const enum stepper_drv_event event); + +/** + * @brief Enable or disable stallguard feature. + * + * @param dev Pointer to the TMC50XX motion controller device. + * @param enable true to enable, false to disable + * @retval -EIO on failure, -EAGAIN if velocity is too low, 0 on success + */ +int tmc50xx_stallguard_enable(const struct device *dev, const bool enable); + +/** + * @brief Read the actual position from the TMC50XX device. + * + * @param dev Pointer to tmc50xx device + * @return actual position in micro-steps + * @retval -EIO on failure, 0 on success + */ +int tmc50xx_read_actual_position(const struct device *dev, const uint8_t index, int32_t *position); + +/** + * @brief Get the clock frequency in Hz of the TMC50XX device. + * + * @param dev Pointer to tmc50xx device + * @return Clock frequency in Hz + */ +int tmc50xx_get_clock_frequency(const struct device *dev); + +/** + * @brief Get the motion controller index for the given device. + * + * @param dev Pointer to tmc50xx motion controller device + * @return index of the motion controller + */ +int motion_controller_index(const struct device *dev); + +/** + * @brief Reschedule the ramp status work for the TMC50XX device. + * + * @param dev Pointer to tmc50xx motion controller device + */ +void tmc50xx_rampstat_work_reschedule(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC50XX_H_ */ diff --git a/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_motion_controller.c b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_motion_controller.c new file mode 100644 index 0000000000000..65c1fcf29c1d9 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_motion_controller.c @@ -0,0 +1,447 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_tmc50xx_motion_controller + +#include + +#include "tmc50xx.h" +#include + +#include +#include + +#include +LOG_MODULE_DECLARE(tmc50xx, CONFIG_STEPPER_LOG_LEVEL); + +struct tmc50xx_motion_controller_data { + struct k_work_delayable stallguard_dwork; + const struct device *dev; + stepper_event_callback_t callback; + void *event_cb_user_data; +}; + +struct tmc50xx_motion_controller_config { + const uint8_t index; + const bool is_sg_enabled; + const uint32_t sg_velocity_check_interval_ms; + const uint32_t sg_threshold_velocity; +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + const struct tmc_ramp_generator_data default_ramp_config; +#endif + /* parent controller required for bus communication */ + const struct device *controller; +}; + +int motion_controller_index(const struct device *dev) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + + return config->index; +} + +int tmc50xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + const uint32_t clock_frequency = tmc50xx_get_clock_frequency(dev); + 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 read_vactual(const struct device *dev, int32_t *actual_velocity) +{ + __ASSERT(actual_velocity != NULL, "actual_velocity pointer must not be NULL"); + const struct tmc50xx_motion_controller_config *config = dev->config; + 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 void stallguard_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct tmc50xx_motion_controller_data *data = + CONTAINER_OF(dwork, struct tmc50xx_motion_controller_data, stallguard_dwork); + const struct tmc50xx_motion_controller_config *config = data->dev->config; + int err; + + err = tmc50xx_stallguard_enable(data->dev, true); + if (err == -EAGAIN) { + k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms)); + } + if (err == -EIO) { + LOG_ERR("Failed to enable stallguard because of I/O error"); + return; + } +} + +int tmc50xx_stallguard_enable(const struct device *dev, const bool enable) +{ + const struct tmc50xx_motion_controller_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(dev, &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; +} + +void tmc50xx_motion_controller_trigger_cb(const struct device *dev, const enum stepper_event event) +{ + if (dev == NULL) { + return; + } + struct tmc50xx_motion_controller_data *data = dev->data; + + if (!data->callback) { + LOG_WRN_ONCE("No %s callback registered", "motion controller"); + return; + } + data->callback(dev, event, data->event_cb_user_data); +} + +static int tmc50xx_set_event_callback(const struct device *dev, stepper_event_callback_t callback, + void *user_data) +{ + struct tmc50xx_motion_controller_data *data = dev->data; + + data->callback = callback; + data->event_cb_user_data = user_data; + + return 0; +} + +static int tmc50xx_stepper_is_moving(const struct device *dev, bool *is_moving) +{ + const struct tmc50xx_motion_controller_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; +} + +static int tmc50xx_stepper_set_reference_position(const struct device *dev, const int32_t position) +{ + const struct tmc50xx_motion_controller_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 tmc50xx_stepper_get_actual_position(const struct device *dev, int32_t *position) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + int err; + + err = tmc50xx_read_actual_position(config->controller, config->index, 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) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + struct tmc50xx_motion_controller_data *data = dev->data; + int err; + + LOG_DBG("%s set target position to %d", dev->name, micro_steps); + + if (config->is_sg_enabled) { + tmc50xx_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) { + tmc50xx_rampstat_work_reschedule(config->controller); + } + return 0; +} + +static int tmc50xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) +{ + int32_t position; + int err; + + err = tmc50xx_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) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + struct tmc50xx_motion_controller_data *data = dev->data; + int err; + + LOG_DBG("Stepper motor controller %s run", dev->name); + + if (config->is_sg_enabled) { + err = tmc50xx_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) { + tmc50xx_rampstat_work_reschedule(config->controller); + } + return 0; +} + +static int tmc50xx_stepper_stop(const struct device *dev) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + int err; + + err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), + TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); + if (err != 0) { + return -EIO; + } + + err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), 0); + if (err != 0) { + return -EIO; + } + + 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) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + int err; + + LOG_DBG("Stepper motor controller %s set ramp", dev->name); + + err = tmc50xx_write(controller, TMC50XX_VSTART(config->index), ramp_data->vstart); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_A1(config->index), ramp_data->a1); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_AMAX(config->index), ramp_data->amax); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_D1(config->index), ramp_data->d1); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_DMAX(config->index), ramp_data->dmax); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_V1(config->index), ramp_data->v1); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_VMAX(config->index), ramp_data->vmax); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_VSTOP(config->index), ramp_data->vstop); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_TZEROWAIT(config->index), ramp_data->tzerowait); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_VHIGH(config->index), ramp_data->vhigh); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_VCOOLTHRS(config->index), ramp_data->vcoolthrs); + if (err != 0) { + return -EIO; + } + err = tmc50xx_write(controller, TMC50XX_IHOLD_IRUN(config->index), ramp_data->iholdrun); + if (err != 0) { + return -EIO; + } + + return 0; +} + +#endif + +static int tmc50xx_motion_controller_init(const struct device *dev) +{ + const struct tmc50xx_motion_controller_config *config = dev->config; + struct tmc50xx_motion_controller_data *data = dev->data; + int err; + + LOG_DBG("Controller: %s, Motion Controller: %s", config->controller->name, dev->name); + data->dev = dev; + + if (config->is_sg_enabled) { + k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); + + err = tmc50xx_write(config->controller, TMC50XX_SWMODE(config->index), BIT(10)); + if (err != 0) { + return -EIO; + } + + LOG_DBG("stallguard delay %d ms", config->sg_velocity_check_interval_ms); + k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); + } + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + err = tmc50xx_stepper_set_ramp(dev, &config->default_ramp_config); + if (err != 0) { + return -EIO; + } +#endif + return 0; +} + +static DEVICE_API(stepper, tmc50xx_stepper_api) = { + .is_moving = tmc50xx_stepper_is_moving, + .move_by = tmc50xx_stepper_move_by, + .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, + .stop = tmc50xx_stepper_stop, + .set_event_callback = tmc50xx_set_event_callback, +}; + +#define TMC50XX_MOTION_CONTROLLER_DEFINE(inst) \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(DT_DRV_INST(inst)))); \ + static const struct tmc50xx_motion_controller_config tmc50xx_motion_ctrl_cfg_##inst = { \ + .controller = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))), \ + .index = DT_INST_PROP(inst, idx), \ + .sg_threshold_velocity = DT_INST_PROP(inst, stallguard_threshold_velocity), \ + .sg_velocity_check_interval_ms = \ + DT_INST_PROP(inst, stallguard_velocity_check_interval_ms), \ + .is_sg_enabled = DT_INST_PROP(inst, activate_stallguard2), \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, \ + (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC50XX(DT_DRV_INST(inst)))) }; \ + static struct tmc50xx_motion_controller_data tmc50xx_motion_controller_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, tmc50xx_motion_controller_init, NULL, \ + &tmc50xx_motion_controller_data_##inst, \ + &tmc50xx_motion_ctrl_cfg_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC50XX_MOTION_CONTROLLER_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_stepper_driver.c b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_stepper_driver.c new file mode 100644 index 0000000000000..c4642995fe016 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc50xx/tmc50xx_stepper_driver.c @@ -0,0 +1,184 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_tmc50xx_stepper_driver + +#include "tmc50xx.h" +#include + +#include + +#include +LOG_MODULE_DECLARE(tmc50xx, CONFIG_STEPPER_LOG_LEVEL); + +struct tmc50xx_stepper_data { + stepper_drv_event_cb_t drv_event_cb; + void *drv_event_cb_user_data; +}; + +struct tmc50xx_stepper_config { + const uint8_t index; + const uint16_t default_micro_step_res; + const int8_t sg_threshold; + /* parent controller required for bus communication */ + const struct device *controller; +}; + +void tmc50xx_driver_trigger_cb(const struct device *dev, const enum stepper_drv_event event) +{ + if (dev == NULL) { + return; + } + + struct tmc50xx_stepper_data *data = dev->data; + + if (!data->drv_event_cb) { + LOG_WRN_ONCE("No %s callback registered", "stepper driver"); + return; + } + + data->drv_event_cb(dev, event, data->drv_event_cb_user_data); +} + +static int tmc50xx_set_stepper_drv_event_callback(const struct device *stepper, + stepper_drv_event_cb_t callback, void *user_data) +{ + struct tmc50xx_stepper_data *data = stepper->data; + + data->drv_event_cb = callback; + data->drv_event_cb_user_data = user_data; + + return 0; +} + +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_set_micro_step_res(const struct device *dev, + enum stepper_drv_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 |= ((MICRO_STEP_RES_INDEX(STEPPER_DRV_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_drv_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_DRV_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 tmc50xx_stepper_config *config = dev->config; + int err; + + LOG_DBG("Controller: %s, Stepper: %s", config->controller->name, dev->name); + if (!IN_RANGE(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)config->sg_threshold; + + err = tmc50xx_write(config->controller, TMC50XX_COOLCONF(config->index), + stall_guard_threshold << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + if (err != 0) { + return -EIO; + } + err = tmc50xx_stepper_set_micro_step_res(dev, config->default_micro_step_res); + if (err != 0) { + return -EIO; + } + LOG_DBG("Setting stallguard %d", config->sg_threshold); + return 0; +} + +static DEVICE_API(stepper_drv, tmc50xx_stepper_drv_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, + .set_event_cb = tmc50xx_set_stepper_drv_event_callback, +}; + +#define TMC50XX_STEPPER_DEFINE(inst) \ + COND_CODE_1(DT_PROP_EXISTS(inst, stallguard_threshold_velocity), \ + BUILD_ASSERT(DT_PROP(inst, stallguard_threshold_velocity), \ + "stallguard threshold velocity must be a positive value"), ()); \ + static const struct tmc50xx_stepper_config tmc50xx_stepper_config_##inst = { \ + .controller = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))), \ + .default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \ + .index = DT_INST_PROP(inst, idx), \ + .sg_threshold = DT_INST_PROP(inst, stallguard2_threshold), \ + }; \ + static struct tmc50xx_stepper_data tmc50xx_stepper_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, tmc50xx_stepper_init, NULL, &tmc50xx_stepper_data_##inst, \ + &tmc50xx_stepper_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_drv_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC50XX_STEPPER_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt b/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt index 027a7be4b51fd..5584b9ec9f08d 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt +++ b/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt @@ -1,4 +1,6 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty # SPDX-License-Identifier: Apache-2.0 -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx.c tmc51xx_spi.c tmc51xx_uart.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX_MOTION_CONTROLLER tmc51xx_motion_controller.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX_STEPPER_DRIVER tmc51xx_stepper_driver.c) diff --git a/drivers/stepper/adi_tmc/tmc51xx/Kconfig.tmc51xx b/drivers/stepper/adi_tmc/tmc51xx/Kconfig.tmc51xx index 702329d86e7c1..654b026ba0fc3 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/Kconfig.tmc51xx +++ b/drivers/stepper/adi_tmc/tmc51xx/Kconfig.tmc51xx @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya # SPDX-License-Identifier: Apache-2.0 config STEPPER_ADI_TMC51XX @@ -8,10 +9,20 @@ config STEPPER_ADI_TMC51XX select STEPPER_ADI_TMC_SPI if $(dt_compat_on_bus,$(DT_COMPAT_ADI_TMC51XX),spi) default y +config STEPPER_ADI_TMC51XX_MOTION_CONTROLLER + bool "Activate trinamic tmc51xx motion controller" + depends on DT_HAS_ADI_TMC51XX_MOTION_CONTROLLER_ENABLED && STEPPER_ADI_TMC51XX + default y + +config STEPPER_ADI_TMC51XX_STEPPER_DRIVER + bool "Activate trinamic tmc51xx stepper driver" + depends on DT_HAS_ADI_TMC51XX_STEPPER_DRIVER_ENABLED && STEPPER_ADI_TMC51XX + default y + if STEPPER_ADI_TMC51XX module = TMC51XX module-str = tmc51xx -rsource "../Kconfig.tmc_rampgen_template" +source "drivers/stepper/adi_tmc/Kconfig.tmc_rampgen_template" endif # STEPPER_ADI_TMC51XX diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c index b9c002e477b20..baa3af5c6d723 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c +++ b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c @@ -4,205 +4,253 @@ * SPDX-License-Identifier: Apache-2.0 */ +#define DT_DRV_COMPAT adi_tmc51xx + #include #include #include +#include +#include #include + #include "tmc51xx.h" #include LOG_MODULE_REGISTER(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); -static inline int tmc51xx_bus_check(const struct device *dev) -{ - const struct tmc51xx_config *config = dev->config; +/* Check for supported bus types */ +#define TMC51XX_BUS_SPI DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#define TMC51XX_BUS_UART DT_ANY_INST_ON_BUS_STATUS_OKAY(uart) - return config->bus_io->check(&config->bus, config->comm_type); -} +#if TMC51XX_BUS_SPI +/* SPI bus I/O operations for TMC51xx devices */ +const struct tmc_bus_io tmc51xx_spi_bus_io; +#endif -static int read_actual_position(const struct device *dev, int32_t *position); -static void rampstat_work_handler(struct k_work *work); -static void tmc51xx_diag0_gpio_callback_handler(const struct device *port, struct gpio_callback *cb, - gpio_port_pins_t pins); -static int rampstat_read_clear(const struct device *dev, uint32_t *rampstat_value); +#if TMC51XX_BUS_UART +/* UART bus I/O operations for TMC51xx devices */ +const struct tmc_bus_io tmc51xx_uart_bus_io; +#endif -static int tmc51xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) -{ - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; +/* Common configuration structure for TMC51xx */ +struct tmc51xx_config { + union tmc_bus bus; + const struct tmc_bus_io *bus_io; + uint8_t comm_type; + const uint32_t gconf; + const uint32_t clock_frequency; - k_sem_take(&data->sem, K_FOREVER); +#if TMC51XX_BUS_UART + const struct gpio_dt_spec sw_sel_gpio; + uint8_t uart_addr; +#endif +#if TMC51XX_BUS_SPI + struct gpio_dt_spec diag0_gpio; +#endif + const struct device *motion_controller; + const struct device *stepper_driver; +}; - err = config->bus_io->write(dev, reg_addr, reg_val); +struct tmc51xx_data { + struct k_sem sem; + struct k_work_delayable rampstat_callback_dwork; + struct gpio_callback diag0_cb; + const struct device *dev; +}; - k_sem_give(&data->sem); +#if TMC51XX_BUS_SPI - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - return err; +static int tmc51xx_bus_check_spi(const union tmc_bus *bus, uint8_t comm_type) +{ + if (comm_type != TMC_COMM_SPI) { + return -ENOTSUP; } - return 0; + return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV; } -static int tmc51xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) +static int tmc51xx_reg_write_spi(const struct device *dev, const uint8_t reg_addr, + const uint32_t reg_val) { const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; int err; - k_sem_take(&data->sem, K_FOREVER); - - err = config->bus_io->read(dev, reg_addr, reg_val); - - k_sem_give(&data->sem); + err = tmc_spi_write_register(&config->bus.spi, TMC5XXX_WRITE_BIT, reg_addr, reg_val); if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - return err; + LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); } - return 0; + return err; } -static int tmc51xx_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) +static int tmc51xx_reg_read_spi(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) { - struct tmc51xx_data *data = dev->data; - __maybe_unused const struct tmc51xx_config *config = dev->config; - - __maybe_unused int err; - - data->callback = callback; - data->event_cb_user_data = user_data; + const struct tmc51xx_config *config = dev->config; + int err; - /* Configure DIAG0 GPIO interrupt pin */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if ((config->comm_type == TMC_COMM_SPI) && config->diag0_gpio.port) { - LOG_INF("Configuring DIAG0 GPIO interrupt pin"); - if (!gpio_is_ready_dt(&config->diag0_gpio)) { - LOG_ERR("DIAG0 interrupt GPIO not ready"); - return -ENODEV; - } + err = tmc_spi_read_register(&config->bus.spi, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); - err = gpio_pin_configure_dt(&config->diag0_gpio, GPIO_INPUT); - if (err < 0) { - LOG_ERR("Could not configure DIAG0 GPIO (%d)", err); - return err; - } - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + if (err < 0) { + LOG_ERR("Failed to read register 0x%x", reg_addr); + } + return err; +} - err = gpio_pin_interrupt_configure_dt(&config->diag0_gpio, GPIO_INT_EDGE_RISING); - if (err) { - LOG_ERR("failed to configure DIAG0 interrupt (err %d)", err); - return -EIO; - } +const struct tmc_bus_io tmc51xx_spi_bus_io = { + .check = tmc51xx_bus_check_spi, + .read = tmc51xx_reg_read_spi, + .write = tmc51xx_reg_write_spi, +}; +#endif /* TMC51XX_BUS_SPI */ - /* Initialize and add GPIO callback */ - gpio_init_callback(&data->diag0_cb, tmc51xx_diag0_gpio_callback_handler, - BIT(config->diag0_gpio.pin)); +#if TMC51XX_BUS_UART - err = gpio_add_callback(config->diag0_gpio.port, &data->diag0_cb); - if (err < 0) { - LOG_ERR("Could not add DIAG0 pin GPIO callback (%d)", err); - return -EIO; - } +static int tmc51xx_bus_check_uart(const union tmc_bus *bus, uint8_t comm_type) +{ + if (comm_type != TMC_COMM_UART) { + return -ENOTSUP; + } + return device_is_ready(bus->uart) ? 0 : -ENODEV; +} - /* Clear any pending interrupts */ - uint32_t rampstat_value; +static int tmc51xx_reg_write_uart(const struct device *dev, const uint8_t reg_addr, + const uint32_t reg_val) +{ + const struct tmc51xx_config *config = dev->config; + int err; - err = rampstat_read_clear(dev, &rampstat_value); - if (err != 0) { - return -EIO; - } - }})) + /* Route to the adi_tmc_uart.h implementation */ + err = tmc_uart_write_register(config->bus.uart, config->uart_addr, reg_addr, reg_val); - return 0; + if (err < 0) { + LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); + } + /* Wait for the write to complete */ + k_sleep(K_MSEC(1)); + return err; } -static int read_vactual(const struct device *dev, int32_t *actual_velocity) +static int tmc51xx_reg_read_uart(const struct device *dev, const uint8_t reg_addr, + uint32_t *reg_val) { + const struct tmc51xx_config *config = dev->config; int err; - uint32_t raw_value; - err = tmc51xx_read(dev, TMC51XX_VACTUAL, &raw_value); - if (err) { - LOG_ERR("Failed to read VACTUAL register"); - return err; - } + /* Route to the adi_tmc_uart.h implementation */ + err = tmc_uart_read_register(config->bus.uart, config->uart_addr, reg_addr, reg_val); - *actual_velocity = sign_extend(raw_value, TMC_RAMP_VACTUAL_SHIFT); - if (*actual_velocity) { - LOG_DBG("actual velocity: %d", *actual_velocity); + if (err < 0) { + LOG_ERR("Failed to read register 0x%x", reg_addr); } - return 0; + /* Wait for the read to complete */ + k_sleep(K_MSEC(1)); + return err; } -static int stallguard_enable(const struct device *dev, const bool enable) +const struct tmc_bus_io tmc51xx_uart_bus_io = { + .check = tmc51xx_bus_check_uart, + .read = tmc51xx_reg_read_uart, + .write = tmc51xx_reg_write_uart, +}; +#endif /* TMC51XX_BUS_UART */ + +static inline int tmc51xx_bus_check(const struct device *dev) { const struct tmc51xx_config *config = dev->config; - uint32_t reg_value; - int err; - err = tmc51xx_read(dev, TMC51XX_SWMODE, ®_value); - if (err) { - LOG_ERR("Failed to read SWMODE register"); - return -EIO; - } + return config->bus_io->check(&config->bus, config->comm_type); +} - if (enable) { - reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; +int tmc51xx_read_actual_position(const struct device *dev, int32_t *position) +{ + const struct tmc51xx_config *config = dev->config; + const struct device *motion_controller = config->motion_controller; + int err; + uint32_t raw_value; - int32_t actual_velocity; + /* Check if device is using UART and is currently moving */ + if (config->comm_type == TMC_COMM_UART) { + bool is_moving; - err = read_vactual(dev, &actual_velocity); - if (err) { + err = stepper_is_moving(motion_controller, &is_moving); + if (err != 0) { return -EIO; } - if (abs(actual_velocity) < config->sg_threshold_velocity) { - return -EAGAIN; + + if (is_moving) { + LOG_WRN("%s: Reading position while moving over UART is not supported", + dev->name); + return -ENOTSUP; } - } else { - reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; } - err = tmc51xx_write(dev, TMC51XX_SWMODE, reg_value); - if (err) { - LOG_ERR("Failed to write SWMODE register"); + + err = tmc51xx_read(dev, TMC51XX_XACTUAL, &raw_value); + if (err != 0) { return -EIO; } - LOG_DBG("Stallguard %s", enable ? "enabled" : "disabled"); + *position = sign_extend(raw_value, TMC_RAMP_XACTUAL_SHIFT); return 0; } -static void stallguard_work_handler(struct k_work *work) +bool tmc51xx_is_interrupt_driven(const struct device *dev) +{ + __maybe_unused const struct tmc51xx_config *config = dev->config; + + IF_ENABLED(TMC51XX_BUS_SPI, ({ +if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { + /* Using interrupt-driven approach - no polling needed */ + return true; +} +})) + return false; +} + +void tmc51xx_reschedule_rampstat_callback(const struct device *dev) +{ + struct tmc51xx_data *data = dev->data; + + k_work_reschedule(&data->rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); +} + +int tmc51xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) { - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct tmc51xx_data const *stepper_data = - CONTAINER_OF(dwork, struct tmc51xx_data, stallguard_dwork); - const struct device *dev = stepper_data->stepper; const struct tmc51xx_config *config = dev->config; + struct tmc51xx_data *data = dev->data; int err; - err = stallguard_enable(dev, true); - if (err == -EAGAIN) { - k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (err == -EIO) { - LOG_ERR("Failed to enable stallguard because of I/O error"); + k_sem_take(&data->sem, K_FOREVER); + + err = config->bus_io->write(dev, 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 void stepper_trigger_callback(const struct device *dev, const enum stepper_event event) +int tmc51xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) { + const struct tmc51xx_config *config = dev->config; struct tmc51xx_data *data = dev->data; + int err; - if (!data->callback) { - LOG_WRN_ONCE("No callback registered"); - return; + k_sem_take(&data->sem, K_FOREVER); + + err = config->bus_io->read(dev, reg_addr, reg_val); + + k_sem_give(&data->sem); + + if (err < 0) { + LOG_ERR("Failed to read register 0x%x", reg_addr); + return err; } - data->callback(dev, event, data->event_cb_user_data); + return 0; } #ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG @@ -244,8 +292,10 @@ static void rampstat_work_handler(struct k_work *work) struct tmc51xx_data *stepper_data = CONTAINER_OF(dwork, struct tmc51xx_data, rampstat_callback_dwork); - const struct device *dev = stepper_data->stepper; + const struct device *dev = stepper_data->dev; __maybe_unused const struct tmc51xx_config *config = dev->config; + const struct device *motion_controller = config->motion_controller; + const struct device *stepper_driver = config->stepper_driver; __ASSERT_NO_MSG(dev); @@ -281,28 +331,34 @@ static void rampstat_work_handler(struct k_work *work) if (ramp_stat_values > 0) { switch (ramp_stat_values) { +#ifdef CONFIG_STEPPER_ADI_TMC51XX_MOTION_CONTROLLER case TMC5XXX_STOP_LEFT_EVENT: LOG_DBG("RAMPSTAT %s:Left end-stop detected", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_LEFT_END_STOP_DETECTED); + tmc51xx_motion_controller_trigger_cb(motion_controller, + STEPPER_EVENT_LEFT_END_STOP_DETECTED); break; case TMC5XXX_STOP_RIGHT_EVENT: LOG_DBG("RAMPSTAT %s:Right end-stop detected", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_RIGHT_END_STOP_DETECTED); + tmc51xx_motion_controller_trigger_cb(motion_controller, + STEPPER_EVENT_RIGHT_END_STOP_DETECTED); break; case TMC5XXX_POS_REACHED_EVENT: case TMC5XXX_POS_REACHED: case TMC5XXX_POS_REACHED_AND_EVENT: LOG_DBG("RAMPSTAT %s:Position reached", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_STEPS_COMPLETED); + tmc51xx_motion_controller_trigger_cb(motion_controller, + STEPPER_EVENT_STEPS_COMPLETED); break; - +#endif /* CONFIG_STEPPER_ADI_TMC51XX_MOTION_CONTROLLER */ +#ifdef CONFIG_STEPPER_ADI_TMC51XX_STEPPER_DRIVER case TMC5XXX_STOP_SG_EVENT: LOG_DBG("RAMPSTAT %s:Stall detected", dev->name); - stallguard_enable(dev, false); - stepper_trigger_callback(dev, STEPPER_EVENT_STALL_DETECTED); + tmc51xx_stallguard_enable(dev, false); + tmc51xx_driver_trigger_cb(stepper_driver, STEPPER_DRV_EVENT_STALL_DETECTED); break; +#endif /* CONFIG_STEPPER_ADI_TMC51XX_STEPPER_DRIVER */ default: LOG_ERR("Illegal ramp stat bit field 0x%x", ramp_stat_values); break; @@ -337,55 +393,6 @@ static void __maybe_unused tmc51xx_diag0_gpio_callback_handler(const struct devi k_work_reschedule(&stepper_data->rampstat_callback_dwork, K_NO_WAIT); } -static int tmc51xx_stepper_enable(const struct device *dev) -{ - LOG_DBG("Enabling Stepper motor controller %s", dev->name); - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - - reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); -} - -static int tmc51xx_stepper_disable(const struct device *dev) -{ - LOG_DBG("Disabling Stepper motor controller %s", dev->name); - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); -} - -static int tmc51xx_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_DRVSTATUS, ®_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 tmc51xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) { const struct tmc51xx_config *config = dev->config; @@ -403,297 +410,14 @@ int tmc51xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity return 0; } -static int tmc51xx_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution res) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_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 = tmc51xx_write(dev, TMC51XX_CHOPCONF, 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 tmc51xx_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *res) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_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 tmc51xx_stepper_set_reference_position(const struct device *dev, const int32_t position) -{ - int err; - - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - return -EIO; - } - - err = tmc51xx_write(dev, TMC51XX_XACTUAL, 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 device *dev, int32_t *position) -{ - const struct tmc51xx_config *config = dev->config; - - int err; - uint32_t raw_value; - - /* Check if device is using UART and is currently moving */ - if (config->comm_type == TMC_COMM_UART) { - bool is_moving; - - err = tmc51xx_stepper_is_moving(dev, &is_moving); - if (err != 0) { - return -EIO; - } - - if (is_moving) { - LOG_WRN("%s: Reading position while moving over UART is not supported", - dev->name); - return -ENOTSUP; - } - } - - err = tmc51xx_read(dev, TMC51XX_XACTUAL, &raw_value); - if (err != 0) { - return -EIO; - } - - *position = sign_extend(raw_value, TMC_RAMP_XACTUAL_SHIFT); - return 0; -} - -static int tmc51xx_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - int err; - - err = read_actual_position(dev, position); - if (err != 0) { - return -EIO; - } - LOG_DBG("%s actual position: %d", dev->name, *position); - return 0; -} - -static int tmc51xx_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 tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - stallguard_enable(dev, false); - } - - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIONING_MODE); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_XTARGET, 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) { - /* For SPI with DIAG0 pin, we use interrupt-driven approach */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { - /* Using interrupt-driven approach - no polling needed */ - return 0; - } - })) - - /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC - k_work_reschedule( - &data->rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ - } - return 0; -} - -static int tmc51xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - int err; - int32_t position; - - err = tmc51xx_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 tmc51xx_stepper_move_to(dev, target_position); -} - -static int tmc51xx_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - LOG_DBG("Stepper motor controller %s run", dev->name); - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_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 = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - - case STEPPER_DIRECTION_NEGATIVE: - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, 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) { - /* For SPI with DIAG0 pin, we use interrupt-driven approach */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { - /* Using interrupt-driven approach - no polling needed */ - return 0; - } - })) - - /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC - k_work_reschedule( - &data->rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ - } - return 0; -} - -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - -int tmc51xx_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); - int err; - - err = tmc51xx_write(dev, TMC51XX_VSTART, ramp_data->vstart); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_A1, ramp_data->a1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_AMAX, ramp_data->amax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_D1, ramp_data->d1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_DMAX, ramp_data->dmax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_V1, ramp_data->v1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_VMAX, ramp_data->vmax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_VSTOP, ramp_data->vstop); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TZEROWAIT, ramp_data->tzerowait); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_THIGH, ramp_data->thigh); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TCOOLTHRS, ramp_data->tcoolthrs); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TPWMTHRS, ramp_data->tpwmthrs); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TPOWER_DOWN, ramp_data->tpowerdown); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_IHOLD_IRUN, ramp_data->iholdrun); - if (err != 0) { - return -EIO; - } - return 0; -} - -#endif - static int tmc51xx_init(const struct device *dev) { - LOG_DBG("TMC51XX stepper motor controller %s initialized", dev->name); - struct tmc51xx_data *data = dev->data; const struct tmc51xx_config *config = dev->config; + struct tmc51xx_data *data = dev->data; int err; + LOG_DBG("Initializing TMC51XX stepper motor controller %s, stepper motor driver %s", + config->motion_controller->name, config->stepper_driver->name); k_sem_init(&data->sem, 1, 1); err = tmc51xx_bus_check(dev); @@ -718,107 +442,84 @@ static int tmc51xx_init(const struct device *dev) } #endif - LOG_DBG("GCONF: %d", config->gconf); - err = tmc51xx_write(dev, TMC5XXX_GCONF, config->gconf); - if (err != 0) { - return -EIO; - } - - /* Read and write GSTAT register to clear any SPI Datagram errors. */ - uint32_t gstat_value; - - err = tmc51xx_read(dev, TMC5XXX_GSTAT, &gstat_value); - if (err != 0) { - return -EIO; - } - - err = tmc51xx_write(dev, TMC5XXX_GSTAT, gstat_value); - if (err != 0) { - return -EIO; - } + /* Configure DIAG0 GPIO interrupt pin */ + IF_ENABLED(TMC51XX_BUS_SPI, ({ + if ((config->comm_type == TMC_COMM_SPI) && config->diag0_gpio.port) { + LOG_INF("Configuring DIAG0 GPIO interrupt pin"); + if (!gpio_is_ready_dt(&config->diag0_gpio)) { + LOG_ERR("DIAG0 interrupt GPIO not ready"); + return -ENODEV; + } - if (config->is_sg_enabled) { - k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); + err = gpio_pin_configure_dt(&config->diag0_gpio, GPIO_INPUT); + if (err < 0) { + LOG_ERR("Could not configure DIAG0 GPIO (%d)", err); + return err; + } + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - err = tmc51xx_write(dev, TMC51XX_SWMODE, BIT(10)); - if (err != 0) { + err = gpio_pin_interrupt_configure_dt(&config->diag0_gpio, GPIO_INT_EDGE_RISING); + if (err) { + LOG_ERR("failed to configure DIAG0 interrupt (err %d)", err); return -EIO; } - LOG_DBG("Setting stall guard to %d with delay %d ms", config->sg_threshold, - config->sg_velocity_check_interval_ms); - if (!IN_RANGE(config->sg_threshold, TMC5XXX_SG_MIN_VALUE, TMC5XXX_SG_MAX_VALUE)) { - LOG_ERR("Stallguard threshold out of range"); - return -EINVAL; + /* Initialize and add GPIO callback */ + gpio_init_callback(&data->diag0_cb, tmc51xx_diag0_gpio_callback_handler, + BIT(config->diag0_gpio.pin)); + + err = gpio_add_callback(config->diag0_gpio.port, &data->diag0_cb); + if (err < 0) { + LOG_ERR("Could not add DIAG0 pin GPIO callback (%d)", err); + return -EIO; } - int32_t stall_guard_threshold = (int32_t)config->sg_threshold; + /* Clear any pending interrupts */ + uint32_t rampstat_value; - err = tmc51xx_write(dev, TMC51XX_COOLCONF, - stall_guard_threshold - << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + err = rampstat_read_clear(dev, &rampstat_value); if (err != 0) { return -EIO; } - k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); - } + }})) -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - err = tmc51xx_stepper_set_ramp(dev, &config->default_ramp_config); + LOG_DBG("GCONF: %d", config->gconf); + err = tmc51xx_write(dev, TMC5XXX_GCONF, config->gconf); if (err != 0) { return -EIO; } -#endif - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - uint32_t rampstat_value; - (void)rampstat_read_clear(dev, &rampstat_value); + /* Read and write GSTAT register to clear any SPI Datagram errors. */ + uint32_t gstat_value; - err = tmc51xx_stepper_set_micro_step_res(dev, config->default_micro_step_res); + err = tmc51xx_read(dev, TMC5XXX_GSTAT, &gstat_value); if (err != 0) { return -EIO; } - return 0; -} -static int tmc51xx_stepper_stop(const struct device *dev) -{ - int err; - - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); + err = tmc51xx_write(dev, TMC5XXX_GSTAT, gstat_value); if (err != 0) { return -EIO; } - err = tmc51xx_write(dev, TMC51XX_VMAX, 0); - if (err != 0) { - return -EIO; - } + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + uint32_t rampstat_value; + (void)rampstat_read_clear(dev, &rampstat_value); return 0; } -static DEVICE_API(stepper, tmc51xx_api) = { - .enable = tmc51xx_stepper_enable, - .disable = tmc51xx_stepper_disable, - .is_moving = tmc51xx_stepper_is_moving, - .move_by = tmc51xx_stepper_move_by, - .set_micro_step_res = tmc51xx_stepper_set_micro_step_res, - .get_micro_step_res = tmc51xx_stepper_get_micro_step_res, - .set_reference_position = tmc51xx_stepper_set_reference_position, - .get_actual_position = tmc51xx_stepper_get_actual_position, - .move_to = tmc51xx_stepper_move_to, - .run = tmc51xx_stepper_run, - .stop = tmc51xx_stepper_stop, - .set_event_callback = tmc51xx_stepper_set_event_callback, -}; +#define DT_CHILD_BY_COMPATIBLE(parent_node_id, compat) \ + DT_FOREACH_CHILD_STATUS_OKAY_VARGS(parent_node_id, _DT_CHILD_BY_COMPAT_HELPER, compat) + +#define _DT_CHILD_BY_COMPAT_HELPER(node_id, compat) \ + COND_CODE_1(DT_NODE_HAS_COMPAT(node_id, compat), (node_id), ()) /* Initializes a struct tmc51xx_config for an instance on a SPI bus. */ #define TMC51XX_CONFIG_SPI(inst) \ .comm_type = TMC_COMM_SPI, \ - .bus.spi = SPI_DT_SPEC_INST_GET(inst, \ - (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_MODE_CPOL | \ - SPI_MODE_CPHA | SPI_WORD_SET(8))), \ + .bus.spi = SPI_DT_SPEC_INST_GET(inst, (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8))), \ .bus_io = &tmc51xx_spi_bus_io, \ .diag0_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, diag0_gpios, {0}) @@ -833,32 +534,28 @@ static DEVICE_API(stepper, tmc51xx_api) = { BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ "clock frequency must be non-zero positive value"); \ static struct tmc51xx_data tmc51xx_data_##inst = { \ - .stepper = DEVICE_DT_GET(DT_DRV_INST(inst))}; \ + .dev = DEVICE_DT_GET(DT_DRV_INST(inst))}; \ COND_CODE_1(DT_PROP_EXISTS(inst, stallguard_threshold_velocity), \ BUILD_ASSERT(DT_PROP(inst, stallguard_threshold_velocity), \ "stallguard threshold velocity must be a positive value"), ()); \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(inst))); \ static const struct tmc51xx_config tmc51xx_config_##inst = {COND_CODE_1 \ (DT_INST_ON_BUS(inst, spi), \ (TMC51XX_CONFIG_SPI(inst)), \ - (TMC51XX_CONFIG_UART(inst))), \ + (TMC51XX_CONFIG_UART(inst))), \ .gconf = ((DT_INST_PROP(inst, en_pwm_mode) << TMC51XX_GCONF_EN_PWM_MODE_SHIFT) | \ (DT_INST_PROP(inst, test_mode) << TMC51XX_GCONF_TEST_MODE_SHIFT) | \ - (DT_INST_PROP(inst, invert_direction) << TMC51XX_GCONF_SHAFT_SHIFT) | \ + (DT_INST_PROP(inst, shaft) << TMC51XX_GCONF_SHAFT_SHIFT) | \ (DT_INST_NODE_HAS_PROP(inst, diag0_gpios) \ ? BIT(TMC51XX_GCONF_DIAG0_INT_PUSHPULL_SHIFT) \ : 0)), \ .clock_frequency = DT_INST_PROP(inst, clock_frequency), \ - .default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \ - .sg_threshold = DT_INST_PROP(inst, stallguard2_threshold), \ - .sg_threshold_velocity = DT_INST_PROP(inst, stallguard_threshold_velocity), \ - .sg_velocity_check_interval_ms = \ - DT_INST_PROP(inst, stallguard_velocity_check_interval_ms), \ - .is_sg_enabled = DT_INST_PROP(inst, activate_stallguard2), \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, \ - (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC51XX(inst)))}; \ + .motion_controller = DEVICE_DT_GET_OR_NULL(DT_CHILD_BY_COMPATIBLE( \ + DT_DRV_INST(inst), adi_tmc51xx_motion_controller)), \ + .stepper_driver = DEVICE_DT_GET_OR_NULL( \ + DT_CHILD_BY_COMPATIBLE(DT_DRV_INST(inst), adi_tmc51xx_stepper_driver))}; \ DEVICE_DT_INST_DEFINE(inst, tmc51xx_init, NULL, &tmc51xx_data_##inst, \ &tmc51xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ - &tmc51xx_api); + NULL); DT_INST_FOREACH_STATUS_OKAY(TMC51XX_DEFINE) +#undef DT_DRV_COMPAT diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h index 43c26255dfd85..d1dc4220dd25e 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h +++ b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h @@ -7,59 +7,73 @@ #ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H #define ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H -#include #include -#include +/** + * @brief Trigger the registered callback for stepper events. + * + * @param dev Pointer to the stepper driver device. + * @param event The stepper driver event that occurred. + */ +void tmc51xx_driver_trigger_cb(const struct device *dev, const enum stepper_drv_event event); + +/** + * @brief Trigger the registered callback for motion controller events. + * + * @param dev Pointer to the motion controller device. + * @param event The stepper event that occurred. + */ +void tmc51xx_motion_controller_trigger_cb(const struct device *dev, const enum stepper_event event); -#define DT_DRV_COMPAT adi_tmc51xx +/** + * @brief Enable or disable stallguard feature. + * + * @param dev Pointer to the motion controller device. + * @param enable true to enable, false to disable + * @retval -EIO on failure, -EAGAIN if velocity is too low, 0 on success + */ +int tmc51xx_stallguard_enable(const struct device *dev, const bool enable); + +/** + * @brief Read the actual position from the TMC51xx device. + * + * @param dev Pointer to tmc51xx device + * @param position Pointer to store the actual position in microsteps + * @retval -EIO on failure, -ENOTSUP if reading while moving over UART is attempted, 0 on success + */ +int tmc51xx_read_actual_position(const struct device *dev, int32_t *position); -/* Check for supported bus types */ -#define TMC51XX_BUS_SPI DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) -#define TMC51XX_BUS_UART DT_ANY_INST_ON_BUS_STATUS_OKAY(uart) +/** + * @brief Reschedule the ramp status callback work item. + * + * @param dev Pointer to the tmc51xx device. + */ +void tmc51xx_reschedule_rampstat_callback(const struct device *dev); -/* Common configuration structure for TMC51xx */ -struct tmc51xx_config { - union tmc_bus bus; - const struct tmc_bus_io *bus_io; - uint8_t comm_type; - const uint32_t gconf; - const uint32_t clock_frequency; - 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; -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - const struct tmc_ramp_generator_data default_ramp_config; -#endif -#if TMC51XX_BUS_UART - const struct gpio_dt_spec sw_sel_gpio; - uint8_t uart_addr; -#endif -#if TMC51XX_BUS_SPI - struct gpio_dt_spec diag0_gpio; -#endif -}; +/** + * @brief Write a 32-bit value to a TMC51xx register. + * + * @param dev Pointer to the tmc51xx device. + * @param reg_addr Register address to write to. + * @param reg_val Value to write to the register. + * @retval -EIO on failure, 0 on success + */ +int tmc51xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val); -struct tmc51xx_data { - struct k_sem sem; - struct k_work_delayable stallguard_dwork; - struct k_work_delayable rampstat_callback_dwork; - struct gpio_callback diag0_cb; - const struct device *stepper; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; +/** + * @brief Read a 32-bit value from a TMC51xx register. + * + * @retval -EIO on failure, 0 on success + */ +int tmc51xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val); -#if TMC51XX_BUS_SPI -/* SPI bus I/O operations for TMC51xx devices */ -extern const struct tmc_bus_io tmc51xx_spi_bus_io; -#endif +/** + * @brief Check if the TMC51xx driver is interrupt driven. + * + * @param dev Pointer to the TMC51xx device. + * @return true if interrupt driven, false otherwise. + */ +bool tmc51xx_is_interrupt_driven(const struct device *dev); -#if TMC51XX_BUS_UART -/* UART bus I/O operations for TMC51xx devices */ -extern const struct tmc_bus_io tmc51xx_uart_bus_io; -#endif #endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H */ diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_motion_controller.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_motion_controller.c new file mode 100644 index 0000000000000..5e6ccbd18a0da --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_motion_controller.c @@ -0,0 +1,443 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include "tmc51xx.h" +#include + +#include +LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); + +#define DT_DRV_COMPAT adi_tmc51xx_motion_controller + +struct tmc51xx_motion_controller_config { + const bool is_sg_enabled; + const uint32_t sg_velocity_check_interval_ms; + const uint32_t sg_threshold_velocity; +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN + const struct tmc_ramp_generator_data default_ramp_config; +#endif + /* parent controller required for bus communication */ + const struct device *controller; +}; + +struct tmc51xx_motion_controller_data { + struct k_work_delayable stallguard_dwork; + /* Work item to run the callback in a thread context. */ + const struct device *dev; + stepper_event_callback_t callback; + void *event_cb_user_data; +}; + +void tmc51xx_motion_controller_trigger_cb(const struct device *dev, const enum stepper_event event) +{ + struct tmc51xx_motion_controller_data *data = dev->data; + + if (!data->callback) { + LOG_WRN_ONCE("No %s callback registered", "motion controller"); + return; + } + data->callback(dev, event, data->event_cb_user_data); +} + +static int read_vactual(const struct device *dev, int32_t *actual_velocity) +{ + __ASSERT(actual_velocity != NULL, "actual_velocity pointer must not be NULL"); + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + int err; + uint32_t raw_value; + + err = tmc51xx_read(controller, TMC51XX_VACTUAL, &raw_value); + if (err) { + LOG_ERR("Failed to read VACTUAL register"); + return err; + } + + *actual_velocity = sign_extend(raw_value, TMC_RAMP_VACTUAL_SHIFT); + if (*actual_velocity) { + LOG_DBG("actual velocity: %d", *actual_velocity); + } + return 0; +} + +int tmc51xx_stallguard_enable(const struct device *dev, const bool enable) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + uint32_t reg_value; + int err; + + err = tmc51xx_read(controller, TMC51XX_SWMODE, ®_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(dev, &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 = tmc51xx_write(controller, TMC51XX_SWMODE, 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 tmc51xx_motion_controller_data const *data = + CONTAINER_OF(dwork, struct tmc51xx_motion_controller_data, stallguard_dwork); + const struct device *dev = data->dev; + const struct tmc51xx_motion_controller_config *config = dev->config; + int err; + + err = tmc51xx_stallguard_enable(dev, true); + if (err == -EAGAIN) { + k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms)); + } + if (err == -EIO) { + LOG_ERR("Failed to enable stallguard because of I/O error"); + } +} + +static int tmc51xx_stepper_set_event_callback(const struct device *dev, + stepper_event_callback_t callback, void *user_data) +{ + struct tmc51xx_motion_controller_data *data = dev->data; + + data->callback = callback; + data->event_cb_user_data = user_data; + + return 0; +} + +static int tmc51xx_stepper_is_moving(const struct device *dev, bool *is_moving) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + uint32_t reg_value; + int err; + + err = tmc51xx_read(controller, TMC51XX_DRVSTATUS, ®_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; +} + +static int tmc51xx_stepper_set_reference_position(const struct device *dev, const int32_t position) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + int err; + + err = tmc51xx_write(controller, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + return -EIO; + } + + err = tmc51xx_write(controller, TMC51XX_XACTUAL, position); + if (err != 0) { + return -EIO; + } + LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position); + return 0; +} + +static int tmc51xx_stepper_get_actual_position(const struct device *dev, int32_t *position) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + int err; + + err = tmc51xx_read_actual_position(config->controller, position); + if (err != 0) { + return -EIO; + } + LOG_DBG("%s actual position: %d", dev->name, *position); + return 0; +} + +static int tmc51xx_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 tmc51xx_motion_controller_config *config = dev->config; + struct tmc51xx_motion_controller_data *data = dev->data; + const struct device *comm_device = config->controller; + int err; + + if (config->is_sg_enabled) { + tmc51xx_stallguard_enable(dev, false); + } + + err = tmc51xx_write(comm_device, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIONING_MODE); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(comm_device, TMC51XX_XTARGET, 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) { + /* For SPI with DIAG0 pin, we use interrupt-driven approach */ + + if (tmc51xx_is_interrupt_driven(config->controller)) { + return 0; + } + /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC + tmc51xx_reschedule_rampstat_callback(config->controller); +#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ + } + return 0; +} + +static int tmc51xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) +{ + int err; + int32_t position; + + err = tmc51xx_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 tmc51xx_stepper_move_to(dev, target_position); +} + +static int tmc51xx_stepper_run(const struct device *dev, const enum stepper_direction direction) +{ + LOG_DBG("Stepper motor controller %s run", dev->name); + const struct tmc51xx_motion_controller_config *config = dev->config; + struct tmc51xx_motion_controller_data *data = dev->data; + const struct device *controller = config->controller; + int err; + + if (config->is_sg_enabled) { + err = tmc51xx_stallguard_enable(dev, false); + if (err != 0) { + return -EIO; + } + } + + switch (direction) { + case STEPPER_DIRECTION_POSITIVE: + err = tmc51xx_write(controller, TMC51XX_RAMPMODE, + TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); + if (err != 0) { + return -EIO; + } + break; + + case STEPPER_DIRECTION_NEGATIVE: + err = tmc51xx_write(controller, TMC51XX_RAMPMODE, + 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) { + /* For SPI with DIAG0 pin, we use interrupt-driven approach */ + if (tmc51xx_is_interrupt_driven(controller)) { + return 0; + } + + /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC + tmc51xx_reschedule_rampstat_callback(config->controller); +#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ + } + return 0; +} + +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN + +int tmc51xx_stepper_set_ramp(const struct device *dev, + const struct tmc_ramp_generator_data *ramp_data) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + int err; + + LOG_DBG("Stepper motor controller %s set ramp", dev->name); + + err = tmc51xx_write(controller, TMC51XX_VSTART, ramp_data->vstart); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_A1, ramp_data->a1); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_AMAX, ramp_data->amax); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_D1, ramp_data->d1); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_DMAX, ramp_data->dmax); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_V1, ramp_data->v1); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_VMAX, ramp_data->vmax); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_VSTOP, ramp_data->vstop); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_TZEROWAIT, ramp_data->tzerowait); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_THIGH, ramp_data->thigh); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_TCOOLTHRS, ramp_data->tcoolthrs); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_TPWMTHRS, ramp_data->tpwmthrs); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_TPOWER_DOWN, ramp_data->tpowerdown); + if (err != 0) { + return -EIO; + } + err = tmc51xx_write(controller, TMC51XX_IHOLD_IRUN, ramp_data->iholdrun); + if (err != 0) { + return -EIO; + } + return 0; +} + +#endif + +static int tmc51xx_motion_controller_init(const struct device *dev) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + struct tmc51xx_motion_controller_data *data = dev->data; + const struct device *controller = config->controller; + int err; + + data->dev = dev; + + if (config->is_sg_enabled) { + k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); + + err = tmc51xx_write(controller, TMC51XX_SWMODE, BIT(10)); + if (err != 0) { + return -EIO; + } + + LOG_DBG("stallguard delay %d ms", config->sg_velocity_check_interval_ms); + + k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); + } + +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN + err = tmc51xx_stepper_set_ramp(dev, &config->default_ramp_config); + if (err != 0) { + return -EIO; + } +#endif + return 0; +} + +static int tmc51xx_stepper_stop(const struct device *dev) +{ + const struct tmc51xx_motion_controller_config *config = dev->config; + const struct device *controller = config->controller; + int err; + + err = tmc51xx_write(controller, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); + if (err != 0) { + return -EIO; + } + + err = tmc51xx_write(controller, TMC51XX_VMAX, 0); + if (err != 0) { + return -EIO; + } + + return 0; +} + +static DEVICE_API(stepper, tmc51xx_stepper_api) = { + .is_moving = tmc51xx_stepper_is_moving, + .move_by = tmc51xx_stepper_move_by, + .set_reference_position = tmc51xx_stepper_set_reference_position, + .get_actual_position = tmc51xx_stepper_get_actual_position, + .move_to = tmc51xx_stepper_move_to, + .run = tmc51xx_stepper_run, + .stop = tmc51xx_stepper_stop, + .set_event_callback = tmc51xx_stepper_set_event_callback, +}; + +#define TMC51XX_MOTION_CONTROLLER_DEFINE(inst) \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(inst))); \ + static const struct tmc51xx_motion_controller_config tmc51xx_motion_ctrl_cfg_##inst = { \ + .controller = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))), \ + .sg_threshold_velocity = DT_INST_PROP(inst, stallguard_threshold_velocity), \ + .sg_velocity_check_interval_ms = \ + DT_INST_PROP(inst, stallguard_velocity_check_interval_ms), \ + .is_sg_enabled = DT_INST_PROP(inst, activate_stallguard2), \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, \ + (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC51XX(inst))) }; \ + static struct tmc51xx_motion_controller_data tmc51xx_motion_controller_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, tmc51xx_motion_controller_init, NULL, \ + &tmc51xx_motion_controller_data_##inst, \ + &tmc51xx_motion_ctrl_cfg_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc51xx_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC51XX_MOTION_CONTROLLER_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c deleted file mode 100644 index c0c9931192ccd..0000000000000 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include -#include -#include - -#include "tmc51xx.h" - -#if TMC51XX_BUS_SPI -LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); - -static int tmc51xx_bus_check_spi(const union tmc_bus *bus, uint8_t comm_type) -{ - if (comm_type != TMC_COMM_SPI) { - return -ENOTSUP; - } - return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV; -} - -static int tmc51xx_reg_write_spi(const struct device *dev, const uint8_t reg_addr, - const uint32_t reg_val) -{ - const struct tmc51xx_config *config = dev->config; - int err; - - err = tmc_spi_write_register(&config->bus.spi, TMC5XXX_WRITE_BIT, reg_addr, reg_val); - - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - } - return err; -} - -static int tmc51xx_reg_read_spi(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) -{ - const struct tmc51xx_config *config = dev->config; - int err; - - err = tmc_spi_read_register(&config->bus.spi, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); - - if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - } - return err; -} - -const struct tmc_bus_io tmc51xx_spi_bus_io = { - .check = tmc51xx_bus_check_spi, - .read = tmc51xx_reg_read_spi, - .write = tmc51xx_reg_write_spi, -}; -#endif /* TMC51XX_BUS_SPI */ diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_stepper_driver.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_stepper_driver.c new file mode 100644 index 0000000000000..302599d70385d --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_stepper_driver.c @@ -0,0 +1,181 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "tmc51xx.h" +#include + +#include +LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); + +#define DT_DRV_COMPAT adi_tmc51xx_stepper_driver + +struct tmc51xx_stepper_config { + const uint16_t default_micro_step_res; + const int8_t sg_threshold; + /* parent controller required for bus communication */ + const struct device *controller; +}; + +struct tmc51xx_stepper_data { + stepper_drv_event_cb_t drv_event_cb; + void *drv_event_cb_user_data; +}; + +void tmc51xx_driver_trigger_cb(const struct device *dev, const enum stepper_drv_event event) +{ + struct tmc51xx_stepper_data *data = dev->data; + + if (!data->drv_event_cb) { + LOG_WRN_ONCE("No %s callback registered", "stepper driver"); + return; + } + data->drv_event_cb(dev, event, data->drv_event_cb_user_data); +} + +static int tmc51xx_set_stepper_drv_event_callback(const struct device *stepper, + stepper_drv_event_cb_t callback, void *user_data) +{ + struct tmc51xx_stepper_data *data = stepper->data; + + data->drv_event_cb = callback; + data->drv_event_cb_user_data = user_data; + + return 0; +} + +static int tmc51xx_stepper_enable(const struct device *dev) +{ + const struct tmc51xx_stepper_config *config = dev->config; + const struct device *controller = config->controller; + uint32_t reg_value; + int err; + + LOG_DBG("Enabling Stepper motor controller %s", dev->name); + + err = tmc51xx_read(controller, TMC51XX_CHOPCONF, ®_value); + if (err != 0) { + return -EIO; + } + + reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return tmc51xx_write(controller, TMC51XX_CHOPCONF, reg_value); +} + +static int tmc51xx_stepper_disable(const struct device *dev) +{ + LOG_DBG("Disabling Stepper motor controller %s", dev->name); + uint32_t reg_value; + int err; + + err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); +} + +static int tmc51xx_stepper_set_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution res) +{ + const struct tmc51xx_stepper_config *config = dev->config; + const struct device *controller = config->controller; + uint32_t reg_value; + int err; + + err = tmc51xx_read(controller, TMC51XX_CHOPCONF, ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; + reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_DRV_MICRO_STEP_256) - LOG2(res)) + << TMC5XXX_CHOPCONF_MRES_SHIFT); + + err = tmc51xx_write(controller, TMC51XX_CHOPCONF, 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 tmc51xx_stepper_get_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution *res) +{ + const struct tmc51xx_stepper_config *config = dev->config; + const struct device *controller = config->controller; + uint32_t reg_value; + int err; + + err = tmc51xx_read(controller, TMC51XX_CHOPCONF, ®_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_DRV_MICRO_STEP_256) - reg_value)); + LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); + return 0; +} + +static int tmc51xx_stepper_init(const struct device *dev) +{ + const struct tmc51xx_stepper_config *config = dev->config; + const struct device *controller = config->controller; + int err; + + if (!IN_RANGE(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)config->sg_threshold; + + err = tmc51xx_write(controller, TMC51XX_COOLCONF, + stall_guard_threshold << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + if (err != 0) { + return -EIO; + } + + err = tmc51xx_stepper_set_micro_step_res(dev, config->default_micro_step_res); + if (err != 0) { + return -EIO; + } + LOG_DBG("Setting stallguard %d", config->sg_threshold); + return 0; +} + +static DEVICE_API(stepper_drv, tmc51xx_stepper_drv_api) = { + .enable = tmc51xx_stepper_enable, + .disable = tmc51xx_stepper_disable, + .set_micro_step_res = tmc51xx_stepper_set_micro_step_res, + .get_micro_step_res = tmc51xx_stepper_get_micro_step_res, + .set_event_cb = tmc51xx_set_stepper_drv_event_callback, +}; + +#define TMC51XX_STEPPER_DRIVER_DEFINE(inst) \ + COND_CODE_1(DT_PROP_EXISTS(inst, stallguard_threshold_velocity), \ + BUILD_ASSERT(DT_PROP(inst, stallguard_threshold_velocity), \ + "stallguard threshold velocity must be a positive value"), ()); \ + static const struct tmc51xx_stepper_config tmc51xx_stepper_config_##inst = { \ + .controller = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))), \ + .default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \ + .sg_threshold = DT_INST_PROP(inst, stallguard2_threshold), \ + }; \ + static struct tmc51xx_stepper_data tmc51xx_stepper_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, tmc51xx_stepper_init, NULL, &tmc51xx_stepper_data_##inst, \ + &tmc51xx_stepper_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc51xx_stepper_drv_api); + +DT_INST_FOREACH_STATUS_OKAY(TMC51XX_STEPPER_DRIVER_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c deleted file mode 100644 index bc523bfe73356..0000000000000 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "tmc51xx.h" -#include -#include - -#if TMC51XX_BUS_UART -LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); - -static int tmc51xx_bus_check_uart(const union tmc_bus *bus, uint8_t comm_type) -{ - if (comm_type != TMC_COMM_UART) { - return -ENOTSUP; - } - return device_is_ready(bus->uart) ? 0 : -ENODEV; -} - -static int tmc51xx_reg_write_uart(const struct device *dev, const uint8_t reg_addr, - const uint32_t reg_val) -{ - const struct tmc51xx_config *config = dev->config; - int err; - - /* Route to the adi_tmc_uart.h implementation */ - err = tmc_uart_write_register(config->bus.uart, config->uart_addr, reg_addr, reg_val); - - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - } - /* Wait for the write to complete */ - k_sleep(K_MSEC(1)); - return err; -} - -static int tmc51xx_reg_read_uart(const struct device *dev, const uint8_t reg_addr, - uint32_t *reg_val) -{ - const struct tmc51xx_config *config = dev->config; - int err; - - /* Route to the adi_tmc_uart.h implementation */ - err = tmc_uart_read_register(config->bus.uart, config->uart_addr, reg_addr, reg_val); - - if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - } - /* Wait for the read to complete */ - k_sleep(K_MSEC(1)); - return err; -} - -const struct tmc_bus_io tmc51xx_uart_bus_io = { - .check = tmc51xx_bus_check_uart, - .read = tmc51xx_reg_read_uart, - .write = tmc51xx_reg_write_uart, -}; -#endif /* TMC51XX_BUS_UART */ diff --git a/drivers/stepper/allegro/Kconfig.a4979 b/drivers/stepper/allegro/Kconfig.a4979 index 1abca3f87ef15..d20ec32dd5c77 100644 --- a/drivers/stepper/allegro/Kconfig.a4979 +++ b/drivers/stepper/allegro/Kconfig.a4979 @@ -5,6 +5,5 @@ config A4979_STEPPER bool "Activate allegro A4979 stepper driver" default y depends on DT_HAS_ALLEGRO_A4979_ENABLED - select STEP_DIR_STEPPER help Microstepping motor driver for stepper motors. diff --git a/drivers/stepper/allegro/a4979.c b/drivers/stepper/allegro/a4979.c index 45f062270c1ec..0e40f7b301d9f 100644 --- a/drivers/stepper/allegro/a4979.c +++ b/drivers/stepper/allegro/a4979.c @@ -8,13 +8,11 @@ #include #include #include -#include "../step_dir/step_dir_stepper_common.h" #include LOG_MODULE_REGISTER(a4979, CONFIG_STEPPER_LOG_LEVEL); struct a4979_config { - const struct step_dir_stepper_common_config common; const struct gpio_dt_spec en_pin; const struct gpio_dt_spec reset_pin; const struct gpio_dt_spec m0_pin; @@ -22,12 +20,9 @@ struct a4979_config { }; struct a4979_data { - const struct step_dir_stepper_common_data common; - enum stepper_micro_step_resolution micro_step_res; + enum stepper_drv_micro_step_resolution micro_step_res; }; -STEP_DIR_STEPPER_STRUCT_CHECK(struct a4979_config, struct a4979_data); - static int a4979_set_microstep_pin(const struct device *dev, const struct gpio_dt_spec *pin, int value) { @@ -49,7 +44,7 @@ static int a4979_set_microstep_pin(const struct device *dev, const struct gpio_d return 0; } -static int a4979_stepper_enable(const struct device *dev) +static int a4979_enable(const struct device *dev) { int ret; const struct a4979_config *config = dev->config; @@ -70,7 +65,7 @@ static int a4979_stepper_enable(const struct device *dev) return 0; } -static int a4979_stepper_disable(const struct device *dev) +static int a4979_disable(const struct device *dev) { int ret; const struct a4979_config *config = dev->config; @@ -91,8 +86,8 @@ static int a4979_stepper_disable(const struct device *dev) return 0; } -static int a4979_stepper_set_micro_step_res(const struct device *dev, - const enum stepper_micro_step_resolution micro_step_res) +static int a4979_set_micro_step_res(const struct device *dev, + const enum stepper_drv_micro_step_resolution micro_step_res) { const struct a4979_config *config = dev->config; struct a4979_data *data = dev->data; @@ -102,19 +97,19 @@ static int a4979_stepper_set_micro_step_res(const struct device *dev, uint8_t m1_value = 0; switch (micro_step_res) { - case STEPPER_MICRO_STEP_1: + case STEPPER_DRV_MICRO_STEP_1: m0_value = 0; m1_value = 0; break; - case STEPPER_MICRO_STEP_2: + case STEPPER_DRV_MICRO_STEP_2: m0_value = 1; m1_value = 0; break; - case STEPPER_MICRO_STEP_4: + case STEPPER_DRV_MICRO_STEP_4: m0_value = 0; m1_value = 1; break; - case STEPPER_MICRO_STEP_16: + case STEPPER_DRV_MICRO_STEP_16: m0_value = 1; m1_value = 1; break; @@ -136,10 +131,10 @@ static int a4979_stepper_set_micro_step_res(const struct device *dev, return 0; } -static int a4979_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *micro_step_res) +static int a4979_get_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution *micro_step_res) { - struct a4979_data *data = dev->data; + const struct a4979_data *data = dev->data; *micro_step_res = data->micro_step_res; return 0; @@ -148,7 +143,7 @@ static int a4979_stepper_get_micro_step_res(const struct device *dev, static int a4979_init(const struct device *dev) { const struct a4979_config *config = dev->config; - struct a4979_data *data = dev->data; + const struct a4979_data *data = dev->data; int ret; bool has_enable_pin = config->en_pin.port != NULL; @@ -206,42 +201,25 @@ static int a4979_init(const struct device *dev) return ret; } - ret = a4979_stepper_set_micro_step_res(dev, data->micro_step_res); + ret = a4979_set_micro_step_res(dev, data->micro_step_res); if (ret != 0) { LOG_ERR("Failed to set micro step resolution: %d", ret); return ret; } - ret = step_dir_stepper_common_init(dev); - if (ret != 0) { - LOG_ERR("Failed to initialize common stepper data: %d", ret); - return ret; - } - gpio_pin_set_dt(&config->common.step_pin, 0); - return 0; } -static DEVICE_API(stepper, a4979_stepper_api) = { - .enable = a4979_stepper_enable, - .disable = a4979_stepper_disable, - .move_by = step_dir_stepper_common_move_by, - .move_to = step_dir_stepper_common_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 = step_dir_stepper_common_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, +static DEVICE_API(stepper_drv, a4979_stepper_api) = { + .enable = a4979_enable, + .disable = a4979_disable, + .set_micro_step_res = a4979_set_micro_step_res, + .get_micro_step_res = a4979_get_micro_step_res, }; #define A4979_DEVICE(inst) \ \ static const struct a4979_config a4979_config_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ .en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}), \ .reset_pin = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \ .m0_pin = GPIO_DT_SPEC_INST_GET(inst, m0_gpios), \ @@ -249,7 +227,6 @@ static DEVICE_API(stepper, a4979_stepper_api) = { }; \ \ static struct a4979_data a4979_data_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \ .micro_step_res = DT_INST_PROP(inst, micro_step_res), \ }; \ \ diff --git a/drivers/stepper/fake_stepper.c b/drivers/stepper/fake_stepper.c new file mode 100644 index 0000000000000..a981ce1ee2a2c --- /dev/null +++ b/drivers/stepper/fake_stepper.c @@ -0,0 +1,190 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#ifdef CONFIG_ZTEST +#include +#endif /* CONFIG_ZTEST */ + +struct fake_stepper_driver_data { + enum stepper_drv_micro_step_resolution micro_step_res; +}; + +struct fake_stepper_controller_data { + int32_t actual_position; +}; + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_drv_enable, const struct device *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_drv_disable, const struct device *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_drv_set_micro_step_res, const struct device *, + enum stepper_drv_micro_step_resolution); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_drv_get_micro_step_res, const struct device *, + enum stepper_drv_micro_step_resolution *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_drv_set_event_cb, const struct device *, + stepper_drv_event_cb_t, void *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_is_moving, const struct device *, bool *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_move_by, const struct device *, int32_t); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_microstep_interval, const struct device *, uint64_t); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_reference_position, const struct device *, int32_t); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_get_actual_position, const struct device *, int32_t *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_move_to, const struct device *, int32_t); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_run, const struct device *, enum stepper_direction); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_stop, const struct device *); + +DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_event_callback, const struct device *, + stepper_event_callback_t, void *); + +static int +fake_stepper_drv_set_micro_step_res_delegate(const struct device *dev, + const enum stepper_drv_micro_step_resolution res) +{ + struct fake_stepper_driver_data *data = dev->data; + + data->micro_step_res = res; + + return 0; +} + +static int fake_stepper_drv_get_micro_step_res_delegate(const struct device *dev, + enum stepper_drv_micro_step_resolution *res) +{ + struct fake_stepper_driver_data *data = dev->data; + + *res = data->micro_step_res; + + return 0; +} + +static int fake_stepper_set_reference_position_delegate(const struct device *dev, const int32_t pos) +{ + struct fake_stepper_controller_data *data = dev->data; + + data->actual_position = pos; + + return 0; +} + +static int fake_stepper_get_actual_position_delegate(const struct device *dev, int32_t *pos) +{ + struct fake_stepper_controller_data *data = dev->data; + + *pos = data->actual_position; + + return 0; +} + +#ifdef CONFIG_ZTEST +static void fake_stepper_reset_rule_before(const struct ztest_unit_test *test, void *fixture) +{ + ARG_UNUSED(test); + ARG_UNUSED(fixture); + + RESET_FAKE(fake_stepper_drv_enable); + RESET_FAKE(fake_stepper_drv_disable); + RESET_FAKE(fake_stepper_drv_set_micro_step_res); + RESET_FAKE(fake_stepper_drv_get_micro_step_res); + RESET_FAKE(fake_stepper_drv_set_event_cb); + RESET_FAKE(fake_stepper_move_by); + RESET_FAKE(fake_stepper_is_moving); + RESET_FAKE(fake_stepper_set_microstep_interval); + RESET_FAKE(fake_stepper_set_reference_position); + RESET_FAKE(fake_stepper_get_actual_position); + RESET_FAKE(fake_stepper_move_to); + RESET_FAKE(fake_stepper_run); + RESET_FAKE(fake_stepper_stop); + + /* Install custom fakes for the setter and getter functions */ + fake_stepper_drv_set_micro_step_res_fake.custom_fake = + fake_stepper_drv_set_micro_step_res_delegate; + fake_stepper_drv_get_micro_step_res_fake.custom_fake = + fake_stepper_drv_get_micro_step_res_delegate; + fake_stepper_set_reference_position_fake.custom_fake = + fake_stepper_set_reference_position_delegate; + fake_stepper_get_actual_position_fake.custom_fake = + fake_stepper_get_actual_position_delegate; +} + +ZTEST_RULE(fake_stepper_reset_rule, fake_stepper_reset_rule_before, NULL); +#endif /* CONFIG_ZTEST */ + +static int fake_stepper_driver_init(const struct device *dev) +{ + fake_stepper_drv_set_micro_step_res_fake.custom_fake = + fake_stepper_drv_set_micro_step_res_delegate; + fake_stepper_drv_get_micro_step_res_fake.custom_fake = + fake_stepper_drv_get_micro_step_res_delegate; + + return 0; +} + +static int fake_stepper_controller_init(const struct device *dev) +{ + fake_stepper_set_reference_position_fake.custom_fake = + fake_stepper_set_reference_position_delegate; + fake_stepper_get_actual_position_fake.custom_fake = + fake_stepper_get_actual_position_delegate; + + return 0; +} + +static DEVICE_API(stepper_drv, fake_stepper_driver_api) = { + .enable = fake_stepper_drv_enable, + .disable = fake_stepper_drv_disable, + .set_micro_step_res = fake_stepper_drv_set_micro_step_res, + .get_micro_step_res = fake_stepper_drv_get_micro_step_res, + .set_event_cb = fake_stepper_drv_set_event_cb, +}; + +static DEVICE_API(stepper, fake_stepper_controller_api) = { + .move_by = fake_stepper_move_by, + .is_moving = fake_stepper_is_moving, + .set_microstep_interval = fake_stepper_set_microstep_interval, + .set_reference_position = fake_stepper_set_reference_position, + .get_actual_position = fake_stepper_get_actual_position, + .move_to = fake_stepper_move_to, + .run = fake_stepper_run, + .stop = fake_stepper_stop, + .set_event_callback = fake_stepper_set_event_callback, +}; + +#define FAKE_STEPPER_DRIVER_INIT(inst) \ + \ + static struct fake_stepper_driver_data fake_stepper_driver_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, fake_stepper_driver_init, NULL, \ + &fake_stepper_driver_data_##inst, NULL, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &fake_stepper_driver_api); + +#define FAKE_STEPPER_CONTROLLER_INIT(inst) \ + \ + static struct fake_stepper_controller_data fake_stepper_controller_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, fake_stepper_controller_init, NULL, \ + &fake_stepper_controller_data_##inst, NULL, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &fake_stepper_controller_api); + +#define DT_DRV_COMPAT zephyr_fake_stepper_driver +DT_INST_FOREACH_STATUS_OKAY(FAKE_STEPPER_DRIVER_INIT) +#undef DT_DRV_COMPAT + +#define DT_DRV_COMPAT zephyr_fake_stepper_controller +DT_INST_FOREACH_STATUS_OKAY(FAKE_STEPPER_CONTROLLER_INIT) +#undef DT_DRV_COMPAT diff --git a/drivers/stepper/fake_stepper_controller.c b/drivers/stepper/fake_stepper_controller.c deleted file mode 100644 index 3abce9726c504..0000000000000 --- a/drivers/stepper/fake_stepper_controller.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include - -#ifdef CONFIG_ZTEST -#include -#endif /* CONFIG_ZTEST */ - -#define DT_DRV_COMPAT zephyr_fake_stepper - -struct fake_stepper_data { - enum stepper_micro_step_resolution micro_step_res; - int32_t actual_position; -}; - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_enable, const struct device *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_disable, const struct device *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_is_moving, const struct device *, bool *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_move_by, const struct device *, int32_t); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_microstep_interval, const struct device *, uint64_t); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_micro_step_res, const struct device *, - enum stepper_micro_step_resolution); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_get_micro_step_res, const struct device *, - enum stepper_micro_step_resolution *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_reference_position, const struct device *, int32_t); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_get_actual_position, const struct device *, int32_t *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_move_to, const struct device *, int32_t); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_run, const struct device *, enum stepper_direction); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_stop, const struct device *); - -DEFINE_FAKE_VALUE_FUNC(int, fake_stepper_set_event_callback, const struct device *, - stepper_event_callback_t, void *); - -static int fake_stepper_set_micro_step_res_delegate(const struct device *dev, - const enum stepper_micro_step_resolution res) -{ - struct fake_stepper_data *data = dev->data; - - data->micro_step_res = res; - - return 0; -} - -static int fake_stepper_get_micro_step_res_delegate(const struct device *dev, - enum stepper_micro_step_resolution *res) -{ - struct fake_stepper_data *data = dev->data; - - *res = data->micro_step_res; - - return 0; -} - -static int fake_stepper_set_reference_position_delegate(const struct device *dev, const int32_t pos) -{ - struct fake_stepper_data *data = dev->data; - - data->actual_position = pos; - - return 0; -} - -static int fake_stepper_get_actual_position_delegate(const struct device *dev, int32_t *pos) -{ - struct fake_stepper_data *data = dev->data; - - *pos = data->actual_position; - - return 0; -} - -#ifdef CONFIG_ZTEST -static void fake_stepper_reset_rule_before(const struct ztest_unit_test *test, void *fixture) -{ - ARG_UNUSED(test); - ARG_UNUSED(fixture); - - RESET_FAKE(fake_stepper_enable); - RESET_FAKE(fake_stepper_disable); - RESET_FAKE(fake_stepper_move_by); - RESET_FAKE(fake_stepper_is_moving); - RESET_FAKE(fake_stepper_set_microstep_interval); - RESET_FAKE(fake_stepper_set_micro_step_res); - RESET_FAKE(fake_stepper_get_micro_step_res); - RESET_FAKE(fake_stepper_set_reference_position); - RESET_FAKE(fake_stepper_get_actual_position); - RESET_FAKE(fake_stepper_move_to); - RESET_FAKE(fake_stepper_run); - RESET_FAKE(fake_stepper_stop); - - /* Install custom fakes for the setter and getter functions */ - fake_stepper_set_micro_step_res_fake.custom_fake = fake_stepper_set_micro_step_res_delegate; - fake_stepper_get_micro_step_res_fake.custom_fake = fake_stepper_get_micro_step_res_delegate; - fake_stepper_set_reference_position_fake.custom_fake = - fake_stepper_set_reference_position_delegate; - fake_stepper_get_actual_position_fake.custom_fake = - fake_stepper_get_actual_position_delegate; -} - -ZTEST_RULE(fake_stepper_reset_rule, fake_stepper_reset_rule_before, NULL); -#endif /* CONFIG_ZTEST */ - -static int fake_stepper_init(const struct device *dev) -{ - fake_stepper_set_micro_step_res_fake.custom_fake = fake_stepper_set_micro_step_res_delegate; - fake_stepper_get_micro_step_res_fake.custom_fake = fake_stepper_get_micro_step_res_delegate; - fake_stepper_set_reference_position_fake.custom_fake = - fake_stepper_set_reference_position_delegate; - fake_stepper_get_actual_position_fake.custom_fake = - fake_stepper_get_actual_position_delegate; - - return 0; -} - -static DEVICE_API(stepper, fake_stepper_driver_api) = { - .enable = fake_stepper_enable, - .disable = fake_stepper_disable, - .move_by = fake_stepper_move_by, - .is_moving = fake_stepper_is_moving, - .set_microstep_interval = fake_stepper_set_microstep_interval, - .set_micro_step_res = fake_stepper_set_micro_step_res, - .get_micro_step_res = fake_stepper_get_micro_step_res, - .set_reference_position = fake_stepper_set_reference_position, - .get_actual_position = fake_stepper_get_actual_position, - .move_to = fake_stepper_move_to, - .run = fake_stepper_run, - .stop = fake_stepper_stop, - .set_event_callback = fake_stepper_set_event_callback, -}; - -#define FAKE_STEPPER_INIT(inst) \ - \ - static struct fake_stepper_data fake_stepper_data_##inst; \ - \ - DEVICE_DT_INST_DEFINE(inst, fake_stepper_init, NULL, &fake_stepper_data_##inst, NULL, \ - POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ - &fake_stepper_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(FAKE_STEPPER_INIT) diff --git a/drivers/stepper/gpio_stepper/CMakeLists.txt b/drivers/stepper/gpio_stepper/CMakeLists.txt new file mode 100644 index 0000000000000..303dc16d649ed --- /dev/null +++ b/drivers/stepper/gpio_stepper/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_property(ALLOW_EMPTY TRUE) + +zephyr_library_sources_ifdef(CONFIG_H_BRIDGE_STEPPER h_bridge_stepper.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_STEP_DIR_CONTROLLER gpio_step_dir.c) + +add_subdirectory_ifdef(CONFIG_GPIO_STEPPER common) diff --git a/drivers/stepper/gpio_stepper/Kconfig b/drivers/stepper/gpio_stepper/Kconfig new file mode 100644 index 0000000000000..2fc5969929cf3 --- /dev/null +++ b/drivers/stepper/gpio_stepper/Kconfig @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Fabian Blatz +# SPDX-License-Identifier: Apache-2.0 + +comment "GPIO Stepper Controllers" + +rsource "common/Kconfig" +rsource "Kconfig.gpio_step_dir" +rsource "Kconfig.h_bridge" diff --git a/drivers/stepper/gpio_stepper/Kconfig.gpio_step_dir b/drivers/stepper/gpio_stepper/Kconfig.gpio_step_dir new file mode 100644 index 0000000000000..a0a31eed70cf1 --- /dev/null +++ b/drivers/stepper/gpio_stepper/Kconfig.gpio_step_dir @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_STEP_DIR_CONTROLLER + bool "Activate driver for gpio step dir stepper control" + select GPIO_STEPPER + depends on DT_HAS_ZEPHYR_GPIO_STEP_DIR_CONTROLLER_ENABLED + default y diff --git a/drivers/stepper/Kconfig.h_bridge b/drivers/stepper/gpio_stepper/Kconfig.h_bridge similarity index 76% rename from drivers/stepper/Kconfig.h_bridge rename to drivers/stepper/gpio_stepper/Kconfig.h_bridge index cff552908c511..51e17de09b914 100644 --- a/drivers/stepper/Kconfig.h_bridge +++ b/drivers/stepper/gpio_stepper/Kconfig.h_bridge @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 config H_BRIDGE_STEPPER - bool "Activate driver for gpio stepper control" + bool "Activate driver for gpio h-bridge stepper control" + select GPIO_STEPPER depends on DT_HAS_ZEPHYR_H_BRIDGE_STEPPER_ENABLED default y diff --git a/drivers/stepper/gpio_stepper/common/CMakeLists.txt b/drivers/stepper/gpio_stepper/common/CMakeLists.txt new file mode 100644 index 0000000000000..4adbd4748d58a --- /dev/null +++ b/drivers/stepper/gpio_stepper/common/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_include_directories(include) + +zephyr_library_sources(gpio_stepper_common.c) +zephyr_library_sources(stepper_work_timing.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER_COUNTER_TIMING stepper_counter_timing.c) diff --git a/drivers/stepper/gpio_stepper/common/Kconfig b/drivers/stepper/gpio_stepper/common/Kconfig new file mode 100644 index 0000000000000..ffc2ab226b934 --- /dev/null +++ b/drivers/stepper/gpio_stepper/common/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_STEPPER + bool + help + Enable library used for gpio based stepper controllers. + +if GPIO_STEPPER + +config GPIO_STEPPER_COUNTER_TIMING + bool "Counter use for stepping" + select COUNTER + default y + help + Enable usage of a counter device for accurate stepping. + +module = GPIO_STEPPER +module-str = gpio_stepper +source "drivers/stepper/Kconfig.stepper_event_template" + +endif # GPIO_STEPPER diff --git a/drivers/stepper/gpio_stepper/common/gpio_stepper_common.c b/drivers/stepper/gpio_stepper/common/gpio_stepper_common.c new file mode 100644 index 0000000000000..14d81974f0bd6 --- /dev/null +++ b/drivers/stepper/gpio_stepper/common/gpio_stepper_common.c @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "gpio_stepper_common.h" + +#include +LOG_MODULE_REGISTER(gpio_stepper, CONFIG_STEPPER_LOG_LEVEL); + +void gpio_stepper_trigger_callback(const struct device *dev, enum stepper_event event) +{ + struct gpio_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_GPIO_STEPPER_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_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS */ +} + +#ifdef CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS +static void gpio_stepper_work_event_handler(struct k_work *work) +{ + struct gpio_stepper_common_data *data = + CONTAINER_OF(work, struct gpio_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_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS */ + +int gpio_stepper_common_init(const struct device *dev) +{ + const struct gpio_stepper_common_config *config = dev->config; + int 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_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS + struct gpio_stepper_common_data *data = dev->data; + + k_msgq_init(&data->event_msgq, data->event_msgq_buffer, sizeof(enum stepper_event), + CONFIG_STEPPER_GPIO_STEPPER_EVENT_QUEUE_LEN); + k_work_init(&data->event_callback_work, gpio_stepper_work_event_handler); +#endif /* CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS */ + return 0; +} diff --git a/drivers/stepper/gpio_stepper/common/include/gpio_stepper_common.h b/drivers/stepper/gpio_stepper/common/include/gpio_stepper_common.h new file mode 100644 index 0000000000000..5d503a5b4c93d --- /dev/null +++ b/drivers/stepper/gpio_stepper/common/include/gpio_stepper_common.h @@ -0,0 +1,290 @@ +/* + * Copyright 2024 Fabian Blatz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_COMMON_H_ +#define ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_COMMON_H_ + +/** + * @brief Stepper Driver APIs + * @defgroup gpio_stepper Stepper Driver APIs + * @ingroup io_interfaces + * @{ + */ + +#include +#include +#include +#include + +#include "stepper_timing_source.h" + +/** + * @brief Common GPIO stepper config. + * + * This structure **must** be placed first in the driver's config structure. + */ +struct gpio_stepper_common_config { + const struct stepper_timing_source_api *timing_source; + const struct device *counter; + bool invert_direction; + void (*timing_source_cb)(const struct device *dev); +}; + +/** + * @brief Initialize common GPIO stepper config from devicetree instance. + * If the counter property is set, the timing source will be set to the counter timing + * source. + * + * @param node_id The devicetree node identifier. + */ +#define GPIO_STEPPER_DT_COMMON_CONFIG_INIT(node_id) \ + { \ + .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)), \ + } + +/** + * @brief Initialize common GPIO stepper config from devicetree instance. + * @param inst Instance. + */ +#define GPIO_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst) \ + GPIO_STEPPER_DT_COMMON_CONFIG_INIT(DT_DRV_INST(inst)) + +/** + * @brief Common GPIO stepper data. + * + * This structure **must** be placed first in the driver's data structure. + */ +struct gpio_stepper_common_data { + const struct device *dev; + struct k_spinlock lock; + enum stepper_direction direction; + enum stepper_run_mode run_mode; + uint64_t microstep_interval_ns; + atomic_t actual_position; + atomic_t step_count; + stepper_event_callback_t callback; + void *event_cb_user_data; + + struct k_work_delayable stepper_dwork; +#ifdef CONFIG_GPIO_STEPPER_COUNTER_TIMING + struct counter_top_cfg counter_top_cfg; + bool counter_running; +#endif /* CONFIG_GPIO_STEPPER_COUNTER_TIMING */ + +#ifdef CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS + struct k_work event_callback_work; + struct k_msgq event_msgq; + uint8_t event_msgq_buffer[CONFIG_STEPPER_GPIO_STEPPER_EVENT_QUEUE_LEN * + sizeof(enum stepper_event)]; +#endif /* CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS */ +}; + +/** + * @brief Initialize common GPIO stepper data from devicetree instance. + * + * @param node_id The devicetree node identifier. + */ +#define GPIO_STEPPER_DT_COMMON_DATA_INIT(node_id) \ + { \ + .dev = DEVICE_DT_GET(node_id), \ + } + +/** + * @brief Initialize common GPIO stepper data from devicetree instance. + * @param inst Instance. + */ +#define GPIO_STEPPER_DT_INST_COMMON_DATA_INIT(inst) \ + GPIO_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 GPIO_STEPPER_STRUCT_CHECK(config, data) \ + BUILD_ASSERT(offsetof(config, common) == 0, \ + "struct gpio_stepper_common_config must be placed first"); \ + BUILD_ASSERT(offsetof(data, common) == 0, \ + "struct gpio_stepper_common_data must be placed first"); + +/** + * @brief Common function to initialize a GPIO stepper device at init time. + * + * This function must be called at the end of the device init function. + * + * @param dev GPIO stepper device instance. + * + * @retval 0 If initialized successfully. + * @retval -errno Negative errno in case of failure. + */ +int gpio_stepper_common_init(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 trigger the callback for. + */ +void gpio_stepper_trigger_callback(const struct device *dev, enum stepper_event event); + +/** + * @brief Set the reference position of the stepper motor. + * + * @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. + */ +static inline int gpio_stepper_common_set_reference_position(const struct device *dev, + const int32_t value) +{ + struct gpio_stepper_common_data *data = dev->data; + + atomic_set(&data->actual_position, value); + + return 0; +} + +/** + * @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. + */ +static inline int gpio_stepper_common_get_actual_position(const struct device *dev, int32_t *value) +{ + struct gpio_stepper_common_data *data = dev->data; + + *value = atomic_get(&data->actual_position); + + return 0; +} + +/** + * @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. + */ +static inline int gpio_stepper_common_move_to(const struct device *dev, const int32_t value) +{ + struct gpio_stepper_common_data *data = dev->data; + int32_t steps_to_move; + + /* Calculate the relative movement required */ + steps_to_move = value - atomic_get(&data->actual_position); + + return stepper_move_by(dev, steps_to_move); +} + +/** + * @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. + */ +static inline int gpio_stepper_common_is_moving(const struct device *dev, bool *is_moving) +{ + const struct gpio_stepper_common_config *config = dev->config; + + *is_moving = config->timing_source->is_running(dev); + + return 0; +} + +/** + * @brief Set a callback function for stepper motor events. + * + * This function sets a user-defined callback that will be invoked when a stepper motor event + * occurs. + * + * @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. + */ +static inline int gpio_stepper_common_set_event_callback(const struct device *dev, + stepper_event_callback_t callback, + void *user_data) +{ + struct gpio_stepper_common_data *data = dev->data; + + data->callback = callback; + data->event_cb_user_data = user_data; + + return 0; +} + +/** + * @brief Update the direction of the stepper motor based on the step count. + * @param dev Pointer to the device structure. + */ +static inline void gpio_stepper_common_update_direction_from_step_count(const struct device *dev) +{ + struct gpio_stepper_common_data *data = dev->data; + + if (atomic_get(&data->step_count) > 0) { + data->direction = STEPPER_DIRECTION_POSITIVE; + } else if (atomic_get(&data->step_count) < 0) { + data->direction = STEPPER_DIRECTION_NEGATIVE; + } else { + /* Step count is zero, direction remains unchanged */ + } +} + +/** + * @brief Update the remaining steps to move for the stepper motor. + * @param dev Pointer to the device structure. + */ +static inline void gpio_stepper_common_update_remaining_steps(const struct device *dev) +{ + struct gpio_stepper_common_data *data = dev->data; + + if (atomic_get(&data->step_count) > 0) { + atomic_dec(&data->step_count); + } else if (atomic_get(&data->step_count) < 0) { + atomic_inc(&data->step_count); + } +} + +/** + * @brief Task for controlling the stepper motor in position mode. + * @param dev Pointer to the device structure. + */ +static inline void gpio_stepper_common_position_mode_task(const struct device *dev) +{ + struct gpio_stepper_common_data *data = dev->data; + const struct gpio_stepper_common_config *config = dev->config; + + if (config->timing_source->needs_reschedule(dev) && atomic_get(&data->step_count) != 0) { + (void)config->timing_source->start(dev); + } else if (atomic_get(&data->step_count) == 0) { + config->timing_source->stop(data->dev); + gpio_stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED); + } +} + +/** + * @brief Task for controlling the stepper motor in velocity mode. + * @param dev Pointer to the device structure. + */ +static inline void gpio_stepper_common_velocity_mode_task(const struct device *dev) +{ + const struct gpio_stepper_common_config *config = dev->config; + + if (config->timing_source->needs_reschedule(dev)) { + (void)config->timing_source->start(dev); + } +} +/** @} */ + +#endif /* ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_COMMON_H_ */ diff --git a/drivers/stepper/step_dir/step_dir_stepper_timing_source.h b/drivers/stepper/gpio_stepper/common/include/stepper_timing_source.h similarity index 88% rename from drivers/stepper/step_dir/step_dir_stepper_timing_source.h rename to drivers/stepper/gpio_stepper/common/include/stepper_timing_source.h index 4c77993614afc..ba222fb7ebf79 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_timing_source.h +++ b/drivers/stepper/gpio_stepper/common/include/stepper_timing_source.h @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ -#define ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ +#ifndef ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_STEPPER_TIMING_SOURCE_H_ +#define ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_STEPPER_TIMING_SOURCE_H_ #include @@ -72,8 +72,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_GPIO_STEPPER_COUNTER_TIMING extern const struct stepper_timing_source_api step_counter_timing_source_api; -#endif /* CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING */ +#endif /* CONFIG_GPIO_STEPPER_COUNTER_TIMING */ -#endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ */ +#endif /* ZEPHYR_DRIVER_STEPPER_GPIO_STEPPER_STEPPER_TIMING_SOURCE_H_ */ diff --git a/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c b/drivers/stepper/gpio_stepper/common/stepper_counter_timing.c similarity index 75% rename from drivers/stepper/step_dir/step_dir_stepper_counter_timing.c rename to drivers/stepper/gpio_stepper/common/stepper_counter_timing.c index bad216961e575..32f89679efd2d 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c +++ b/drivers/stepper/gpio_stepper/common/stepper_counter_timing.c @@ -4,24 +4,26 @@ */ #include -#include "step_dir_stepper_common.h" +#include "gpio_stepper_common.h" +#include "stepper_timing_source.h" #include -LOG_MODULE_DECLARE(step_dir_stepper); +LOG_MODULE_DECLARE(gpio_stepper); 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 gpio_stepper_common_data *data = user_data; + const struct gpio_stepper_common_config *config = data->dev->config; - stepper_handle_timing_signal(data->dev); + config->timing_source_cb(data->dev); } int step_counter_timing_source_update(const struct device *dev, 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; + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; int ret; if (microstep_interval_ns == 0) { @@ -48,8 +50,8 @@ int step_counter_timing_source_update(const struct device *dev, int step_counter_timing_source_start(const struct device *dev) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; int ret; ret = counter_start(config->counter); @@ -65,8 +67,8 @@ int step_counter_timing_source_start(const struct device *dev) int step_counter_timing_source_stop(const struct device *dev) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; int ret; ret = counter_stop(config->counter); @@ -88,15 +90,15 @@ bool step_counter_timing_source_needs_reschedule(const struct device *dev) bool step_counter_timing_source_is_running(const struct device *dev) { - struct step_dir_stepper_common_data *data = dev->data; + struct gpio_stepper_common_data *data = dev->data; return data->counter_running; } int step_counter_timing_source_init(const struct device *dev) { - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; if (!device_is_ready(config->counter)) { LOG_ERR("Counter device is not ready"); diff --git a/drivers/stepper/step_dir/step_dir_stepper_work_timing.c b/drivers/stepper/gpio_stepper/common/stepper_work_timing.c similarity index 74% rename from drivers/stepper/step_dir/step_dir_stepper_work_timing.c rename to drivers/stepper/gpio_stepper/common/stepper_work_timing.c index ac605f1f49727..0614c3291eadf 100644 --- a/drivers/stepper/step_dir/step_dir_stepper_work_timing.c +++ b/drivers/stepper/gpio_stepper/common/stepper_work_timing.c @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "step_dir_stepper_timing_source.h" -#include "step_dir_stepper_common.h" +#include "stepper_timing_source.h" +#include "gpio_stepper_common.h" static k_timeout_t stepper_movement_delay(const struct device *dev) { - const struct step_dir_stepper_common_data *data = dev->data; + const struct gpio_stepper_common_data *data = dev->data; if (data->microstep_interval_ns == 0) { return K_FOREVER; @@ -20,15 +20,16 @@ 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 gpio_stepper_common_data *data = + CONTAINER_OF(dwork, struct gpio_stepper_common_data, stepper_dwork); + const struct gpio_stepper_common_config *config = data->dev->config; - stepper_handle_timing_signal(data->dev); + config->timing_source_cb(data->dev); } int step_work_timing_source_init(const struct device *dev) { - struct step_dir_stepper_common_data *data = dev->data; + struct gpio_stepper_common_data *data = dev->data; k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); @@ -44,14 +45,14 @@ int step_work_timing_source_update(const struct device *dev, const uint64_t micr int step_work_timing_source_start(const struct device *dev) { - struct step_dir_stepper_common_data *data = dev->data; + struct gpio_stepper_common_data *data = dev->data; return k_work_reschedule(&data->stepper_dwork, stepper_movement_delay(dev)); } int step_work_timing_source_stop(const struct device *dev) { - struct step_dir_stepper_common_data *data = dev->data; + struct gpio_stepper_common_data *data = dev->data; return k_work_cancel_delayable(&data->stepper_dwork); } @@ -64,7 +65,7 @@ bool step_work_timing_source_needs_reschedule(const struct device *dev) bool step_work_timing_source_is_running(const struct device *dev) { - struct step_dir_stepper_common_data *data = dev->data; + struct gpio_stepper_common_data *data = dev->data; return k_work_delayable_is_pending(&data->stepper_dwork); } diff --git a/drivers/stepper/gpio_stepper/gpio_step_dir.c b/drivers/stepper/gpio_stepper/gpio_step_dir.c new file mode 100644 index 0000000000000..e0cf3ae6b7a35 --- /dev/null +++ b/drivers/stepper/gpio_stepper/gpio_step_dir.c @@ -0,0 +1,288 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_gpio_step_dir_controller + +#include +#include + +#include + +#include +LOG_MODULE_REGISTER(gpio_step_dir_controller, CONFIG_STEPPER_LOG_LEVEL); + +struct zephyr_gpio_step_dir_controller_config { + struct gpio_stepper_common_config common; + const struct gpio_dt_spec step_pin; + const struct gpio_dt_spec dir_pin; + uint32_t step_width_ns; + bool dual_edge; +}; + +struct zephyr_gpio_step_dir_controller_data { + struct gpio_stepper_common_data common; + atomic_t step_high; +}; + +GPIO_STEPPER_STRUCT_CHECK(struct zephyr_gpio_step_dir_controller_config, + struct zephyr_gpio_step_dir_controller_data); + +static int update_dir_pin(const struct device *dev) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + int ret; + + switch (data->common.direction) { + case STEPPER_DIRECTION_POSITIVE: + ret = gpio_pin_set_dt(&config->dir_pin, 1 ^ config->common.invert_direction); + break; + case STEPPER_DIRECTION_NEGATIVE: + ret = gpio_pin_set_dt(&config->dir_pin, 0 ^ config->common.invert_direction); + break; + default: + LOG_ERR("Unsupported direction: %d", data->common.direction); + return -ENOTSUP; + } + if (ret < 0) { + LOG_ERR("Failed to set direction: %d", ret); + return ret; + } + + return ret; +} + +static void stepper_handle_timing_signal(const struct device *dev) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + int ret; + + atomic_val_t step_pin_status = atomic_xor(&data->step_high, 1) ^ 1; + + ret = gpio_pin_set_dt(&config->step_pin, atomic_get(&step_pin_status)); + if (ret < 0) { + LOG_ERR("Failed to set step pin: %d", ret); + return; + } + + if (!atomic_get(&step_pin_status) || config->dual_edge) { + if (data->common.direction == STEPPER_DIRECTION_POSITIVE) { + atomic_inc(&data->common.actual_position); + } else { + atomic_dec(&data->common.actual_position); + } + } + + switch (data->common.run_mode) { + case STEPPER_RUN_MODE_POSITION: + if (!data->step_high || config->dual_edge) { + gpio_stepper_common_update_remaining_steps(dev); + } + gpio_stepper_common_position_mode_task(dev); + break; + case STEPPER_RUN_MODE_VELOCITY: + gpio_stepper_common_velocity_mode_task(dev); + break; + default: + LOG_WRN("Unsupported run mode: %d", data->common.run_mode); + break; + } +} + +static int start_stepping(const struct device *dev) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + int ret; + + ret = config->common.timing_source->update( + dev, config->dual_edge ? data->common.microstep_interval_ns + : data->common.microstep_interval_ns / 2); + if (ret < 0) { + LOG_ERR("Failed to update timing source: %d", ret); + return ret; + } + + ret = config->common.timing_source->start(dev); + if (ret < 0) { + LOG_ERR("Failed to start timing source: %d", ret); + return ret; + } + + stepper_handle_timing_signal(dev); + return 0; +} + +static int gpio_step_dir_move_by(const struct device *dev, const int32_t micro_steps) +{ + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + int ret; + + if (data->common.microstep_interval_ns == 0) { + LOG_ERR("Step interval not set or invalid step interval set"); + return -EINVAL; + } + + if (micro_steps == 0) { + gpio_stepper_trigger_callback(data->common.dev, STEPPER_EVENT_STEPS_COMPLETED); + config->common.timing_source->stop(dev); + return 0; + } + + K_SPINLOCK(&data->common.lock) { + data->common.run_mode = STEPPER_RUN_MODE_POSITION; + atomic_set(&data->common.step_count, micro_steps); + gpio_stepper_common_update_direction_from_step_count(dev); + ret = update_dir_pin(dev); + if (ret < 0) { + K_SPINLOCK_BREAK; + } + + ret = start_stepping(dev); + } + + return ret; +} + +static int gpio_step_dir_set_microstep_interval(const struct device *dev, + const uint64_t microstep_interval_ns) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + + if (microstep_interval_ns == 0) { + LOG_ERR("Step interval cannot be zero"); + return -EINVAL; + } + + if (config->dual_edge && (microstep_interval_ns < config->step_width_ns)) { + LOG_ERR("Step interval too small for configured step width"); + return -EINVAL; + } + + if (microstep_interval_ns < 2 * config->step_width_ns) { + LOG_ERR("Step interval too small for configured step width"); + return -EINVAL; + } + + K_SPINLOCK(&data->common.lock) { + data->common.microstep_interval_ns = microstep_interval_ns; + config->common.timing_source->update( + dev, config->dual_edge ? data->common.microstep_interval_ns + : data->common.microstep_interval_ns / 2); + } + + return 0; +} + +int gpio_step_dir_stepper_run(const struct device *dev, const enum stepper_direction direction) +{ + struct gpio_stepper_common_data *data = dev->data; + int ret; + + 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_VELOCITY; + data->direction = direction; + ret = update_dir_pin(dev); + if (ret < 0) { + K_SPINLOCK_BREAK; + } + + ret = start_stepping(dev); + } + + return ret; +} + +int gpio_step_dir_stepper_stop(const struct device *dev) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + struct zephyr_gpio_step_dir_controller_data *data = dev->data; + int ret; + + ret = config->common.timing_source->stop(dev); + if (ret != 0) { + LOG_ERR("Failed to stop timing source: %d", ret); + return ret; + } + + if (!config->dual_edge && atomic_cas(&data->step_high, 1, 0)) { + gpio_pin_set_dt(&config->step_pin, 0); + /* If we are in the high state, we need to account for that step */ + if (data->common.direction == STEPPER_DIRECTION_POSITIVE) { + atomic_inc(&data->common.actual_position); + } else { + atomic_dec(&data->common.actual_position); + } + } + + gpio_stepper_trigger_callback(dev, STEPPER_EVENT_STOPPED); + + return 0; +} + +static int gpio_step_dir_controller_init(const struct device *dev) +{ + const struct zephyr_gpio_step_dir_controller_config *config = dev->config; + int ret; + + if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) { + LOG_ERR("GPIO pins are not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&config->step_pin, GPIO_OUTPUT); + if (ret < 0) { + LOG_ERR("Failed to configure step pin: %d", ret); + return ret; + } + + ret = gpio_pin_configure_dt(&config->dir_pin, GPIO_OUTPUT); + if (ret < 0) { + LOG_ERR("Failed to configure dir pin: %d", ret); + return ret; + } + + return gpio_stepper_common_init(dev); +} + +static DEVICE_API(stepper, gpio_step_dir_stepper_api) = { + .move_by = gpio_step_dir_move_by, + .move_to = gpio_stepper_common_move_to, + .is_moving = gpio_stepper_common_is_moving, + .set_reference_position = gpio_stepper_common_set_reference_position, + .get_actual_position = gpio_stepper_common_get_actual_position, + .set_event_callback = gpio_stepper_common_set_event_callback, + .set_microstep_interval = gpio_step_dir_set_microstep_interval, + .run = gpio_step_dir_stepper_run, + .stop = gpio_step_dir_stepper_stop, +}; + +#define ZEPHYR_GPIO_STEP_DIR_CONTROLLER_DEFINE(inst) \ + static const struct zephyr_gpio_step_dir_controller_config gpio_step_dir_config_##inst = { \ + .common = GPIO_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ + .common.timing_source_cb = stepper_handle_timing_signal, \ + .step_pin = GPIO_DT_SPEC_INST_GET(inst, step_gpios), \ + .dir_pin = GPIO_DT_SPEC_INST_GET(inst, dir_gpios), \ + .step_width_ns = DT_INST_PROP(inst, step_width_ns), \ + .dual_edge = DT_INST_PROP_OR(inst, dual_edge_step, false), \ + }; \ + static struct zephyr_gpio_step_dir_controller_data gpio_step_dir_data_##inst = { \ + .common = GPIO_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \ + .step_high = ATOMIC_INIT(0), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, gpio_step_dir_controller_init, NULL, \ + &gpio_step_dir_data_##inst, &gpio_step_dir_config_##inst, \ + POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ + &gpio_step_dir_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(ZEPHYR_GPIO_STEP_DIR_CONTROLLER_DEFINE) diff --git a/drivers/stepper/gpio_stepper/h_bridge_stepper.c b/drivers/stepper/gpio_stepper/h_bridge_stepper.c new file mode 100644 index 0000000000000..3a85bb89af5ff --- /dev/null +++ b/drivers/stepper/gpio_stepper/h_bridge_stepper.c @@ -0,0 +1,274 @@ +/* + * 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_h_bridge_stepper + +#include +#include +#include +#include + +#include + +#include +LOG_MODULE_REGISTER(h_bridge_stepper, CONFIG_STEPPER_LOG_LEVEL); + +#define LUT_MAX_STEP_GAP 2 +#define NUM_CONTROL_PINS 4 + +static const uint8_t + half_step_lookup_table[NUM_CONTROL_PINS * LUT_MAX_STEP_GAP][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 h_bridge_stepper_config { + struct gpio_stepper_common_config common; + const struct gpio_dt_spec *control_pins; + uint8_t step_gap; +}; + +struct h_bridge_stepper_data { + struct gpio_stepper_common_data common; + uint8_t coil_charge; +}; + +GPIO_STEPPER_STRUCT_CHECK(struct h_bridge_stepper_config, struct h_bridge_stepper_data); + +static int stepper_motor_set_coil_charge(const struct device *dev) +{ + struct h_bridge_stepper_data *data = dev->data; + const struct h_bridge_stepper_config *config = dev->config; + int ret; + + for (int i = 0; i < NUM_CONTROL_PINS; i++) { + ret = gpio_pin_set_dt(&config->control_pins[i], + half_step_lookup_table[data->coil_charge][i]); + if (ret < 0) { + LOG_ERR("Failed to set control pin %d: %d", i, ret); + return ret; + } + } + + return 0; +} + +static void increment_coil_charge(const struct device *dev) +{ + const struct h_bridge_stepper_config *config = dev->config; + struct h_bridge_stepper_data *data = dev->data; + + if (data->coil_charge == NUM_CONTROL_PINS * LUT_MAX_STEP_GAP - config->step_gap) { + data->coil_charge = 0; + } else { + data->coil_charge = data->coil_charge + config->step_gap; + } +} + +static void decrement_coil_charge(const struct device *dev) +{ + const struct h_bridge_stepper_config *config = dev->config; + struct h_bridge_stepper_data *data = dev->data; + + if (data->coil_charge == 0) { + data->coil_charge = NUM_CONTROL_PINS * LUT_MAX_STEP_GAP - config->step_gap; + } else { + data->coil_charge = data->coil_charge - config->step_gap; + } +} + +static void update_coil_charge(const struct device *dev) +{ + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; + + if (data->direction == STEPPER_DIRECTION_POSITIVE) { + config->invert_direction ? decrement_coil_charge(dev) : increment_coil_charge(dev); + atomic_inc(&data->actual_position); + } else if (data->direction == STEPPER_DIRECTION_NEGATIVE) { + config->invert_direction ? increment_coil_charge(dev) : decrement_coil_charge(dev); + atomic_dec(&data->actual_position); + } +} + +static void stepper_work_step_handler(const struct device *dev) +{ + struct gpio_stepper_common_data *data = dev->data; + int ret; + + ret = stepper_motor_set_coil_charge(dev); + if (ret < 0) { + LOG_ERR("Failed to set coil charge: %d", ret); + return; + } + + update_coil_charge(dev); + K_SPINLOCK(&data->lock) { + switch (data->run_mode) { + case STEPPER_RUN_MODE_POSITION: + gpio_stepper_common_update_remaining_steps(dev); + gpio_stepper_common_position_mode_task(data->dev); + break; + case STEPPER_RUN_MODE_VELOCITY: + gpio_stepper_common_velocity_mode_task(data->dev); + break; + default: + LOG_WRN("Unsupported run mode %d", data->run_mode); + break; + } + } +} + +static int h_bridge_stepper_move_by(const struct device *dev, int32_t micro_steps) +{ + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; + int ret; + + if (data->microstep_interval_ns == 0) { + LOG_ERR("Step interval not set or invalid step interval set"); + return -EINVAL; + } + + if (micro_steps == 0) { + gpio_stepper_trigger_callback(dev, STEPPER_EVENT_STEPS_COMPLETED); + config->timing_source->stop(dev); + return 0; + } + + K_SPINLOCK(&data->lock) { + data->run_mode = STEPPER_RUN_MODE_POSITION; + atomic_set(&data->step_count, micro_steps); + gpio_stepper_common_update_direction_from_step_count(dev); + ret = config->timing_source->update(dev, data->microstep_interval_ns); + if (ret < 0) { + LOG_ERR("Failed to update timing source: %d", ret); + K_SPINLOCK_BREAK; + } + + ret = config->timing_source->start(dev); + if (ret < 0) { + LOG_ERR("Failed to start timing source: %d", ret); + } + } + + return 0; +} + +static int h_bridge_stepper_set_microstep_interval(const struct device *dev, + uint64_t microstep_interval_ns) +{ + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; + + if (microstep_interval_ns == 0) { + LOG_ERR("Step interval is invalid."); + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + data->microstep_interval_ns = microstep_interval_ns; + config->timing_source->update(dev, microstep_interval_ns); + } + LOG_DBG("Setting Motor step interval to %llu", microstep_interval_ns); + + return 0; +} + +static int h_bridge_stepper_run(const struct device *dev, const enum stepper_direction direction) +{ + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; + int ret; + + 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_VELOCITY; + data->direction = direction; + ret = config->timing_source->update(dev, data->microstep_interval_ns); + if (ret < 0) { + LOG_ERR("Failed to update timing source: %d", ret); + K_SPINLOCK_BREAK; + } + + ret = config->timing_source->start(dev); + if (ret < 0) { + LOG_ERR("Failed to start timing source: %d", ret); + } + } + + return 0; +} + +static int h_bridge_stepper_stop(const struct device *dev) +{ + const struct gpio_stepper_common_config *config = dev->config; + struct gpio_stepper_common_data *data = dev->data; + int err; + + K_SPINLOCK(&data->lock) { + err = config->timing_source->stop(dev); + gpio_stepper_trigger_callback(dev, STEPPER_EVENT_STOPPED); + } + + return err; +} + +static int h_bridge_stepper_init(const struct device *dev) +{ + struct gpio_stepper_common_data *data = dev->data; + const struct h_bridge_stepper_config *config = dev->config; + int err; + + data->dev = dev; + LOG_DBG("Initializing %s h_bridge_stepper with %d pin", dev->name, NUM_CONTROL_PINS); + for (uint8_t n_pin = 0; n_pin < NUM_CONTROL_PINS; n_pin++) { + if (!gpio_is_ready_dt(&config->control_pins[n_pin])) { + LOG_ERR("Control pin %d is not ready", n_pin); + return -ENODEV; + } + err = gpio_pin_configure_dt(&config->control_pins[n_pin], GPIO_OUTPUT_INACTIVE); + if (err < 0) { + LOG_ERR("Failed to configure control pin %d: %d", n_pin, err); + return -ENODEV; + } + } + + return gpio_stepper_common_init(dev); +} + +static DEVICE_API(stepper, h_bridge_stepper_api) = { + .set_reference_position = gpio_stepper_common_set_reference_position, + .get_actual_position = gpio_stepper_common_get_actual_position, + .set_event_callback = gpio_stepper_common_set_event_callback, + .set_microstep_interval = h_bridge_stepper_set_microstep_interval, + .move_by = h_bridge_stepper_move_by, + .move_to = gpio_stepper_common_move_to, + .run = h_bridge_stepper_run, + .stop = h_bridge_stepper_stop, + .is_moving = gpio_stepper_common_is_moving, +}; + +#define H_BRIDGE_STEPPER_DEFINE(inst) \ + static const struct gpio_dt_spec h_bridge_stepper_motor_control_pins_##inst[] = { \ + DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \ + }; \ + BUILD_ASSERT(ARRAY_SIZE(h_bridge_stepper_motor_control_pins_##inst) == 4, \ + "h_bridge stepper driver currently supports only 4 wire configuration"); \ + static const struct h_bridge_stepper_config h_bridge_stepper_config_##inst = { \ + .common = GPIO_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ + .common.timing_source_cb = stepper_work_step_handler, \ + .step_gap = DT_INST_PROP(inst, lut_step_gap), \ + .control_pins = h_bridge_stepper_motor_control_pins_##inst}; \ + static struct h_bridge_stepper_data h_bridge_stepper_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, h_bridge_stepper_init, NULL, &h_bridge_stepper_data_##inst, \ + &h_bridge_stepper_config_##inst, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &h_bridge_stepper_api); + +DT_INST_FOREACH_STATUS_OKAY(H_BRIDGE_STEPPER_DEFINE) diff --git a/drivers/stepper/h_bridge_stepper.c b/drivers/stepper/h_bridge_stepper.c deleted file mode 100644 index b6580a3c5876d..0000000000000 --- a/drivers/stepper/h_bridge_stepper.c +++ /dev/null @@ -1,438 +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_h_bridge_stepper - -#include -#include -#include -#include - -#include -LOG_MODULE_REGISTER(h_bridge_stepper, 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 h_bridge_stepper_config { - const struct gpio_dt_spec en_pin; - const struct gpio_dt_spec *control_pins; - bool invert_direction; -}; - -struct h_bridge_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; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; - -static int stepper_motor_set_coil_charge(const struct device *dev) -{ - struct h_bridge_stepper_data *data = dev->data; - const struct h_bridge_stepper_config *config = dev->config; - int ret; - - for (int i = 0; i < NUM_CONTROL_PINS; i++) { - ret = gpio_pin_set_dt(&config->control_pins[i], - half_step_lookup_table[data->coil_charge][i]); - if (ret < 0) { - LOG_ERR("Failed to set control pin %d: %d", i, ret); - return ret; - } - } - - return 0; -} - -static void increment_coil_charge(const struct device *dev) -{ - struct h_bridge_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 h_bridge_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 void update_coil_charge(const struct device *dev) -{ - const struct h_bridge_stepper_config *config = dev->config; - struct h_bridge_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 h_bridge_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 h_bridge_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 h_bridge_stepper_data *data = dev->data; - int ret; - - update_remaining_steps(dev); - ret = stepper_motor_set_coil_charge(dev); - if (ret < 0) { - LOG_ERR("Failed to set coil charge: %d", ret); - return; - } - - 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 h_bridge_stepper_data *data = dev->data; - int ret; - - ret = stepper_motor_set_coil_charge(dev); - if (ret < 0) { - LOG_ERR("Failed to set coil charge: %d", ret); - return; - } - - 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 h_bridge_stepper_data *data = - CONTAINER_OF(dwork, struct h_bridge_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 h_bridge_stepper_move_by(const struct device *dev, int32_t micro_steps) -{ - struct h_bridge_stepper_data *data = dev->data; - - if (data->delay_in_ns == 0) { - LOG_ERR("Step interval not set or invalid step interval set"); - return -EINVAL; - } - - if (micro_steps == 0) { - (void)k_work_cancel_delayable(&data->stepper_dwork); - if (data->callback) { - data->callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED, - data->event_cb_user_data); - } - return 0; - } - 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 h_bridge_stepper_set_reference_position(const struct device *dev, int32_t position) -{ - struct h_bridge_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - data->actual_position = position; - } - return 0; -} - -static int h_bridge_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - struct h_bridge_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - *position = data->actual_position; - } - return 0; -} - -static int h_bridge_stepper_move_to(const struct device *dev, int32_t micro_steps) -{ - struct h_bridge_stepper_data *data = dev->data; - int32_t steps_to_move; - - K_SPINLOCK(&data->lock) { - steps_to_move = micro_steps - data->actual_position; - } - return h_bridge_stepper_move_by(dev, steps_to_move); -} - -static int h_bridge_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - struct h_bridge_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 h_bridge_stepper_set_microstep_interval(const struct device *dev, - uint64_t microstep_interval_ns) -{ - struct h_bridge_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 h_bridge_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - struct h_bridge_stepper_data *data = dev->data; - - 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_VELOCITY; - data->direction = direction; - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); - } - return 0; -} - -static int h_bridge_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution micro_step_res) -{ - struct h_bridge_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 h_bridge_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *micro_step_res) -{ - struct h_bridge_stepper_data *data = dev->data; - *micro_step_res = MAX_MICRO_STEP_RES >> (data->step_gap - 1); - return 0; -} - -static int h_bridge_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct h_bridge_stepper_data *data = dev->data; - - K_SPINLOCK(&data->lock) { - data->callback = callback; - data->event_cb_user_data = user_data; - } - return 0; -} - -static int h_bridge_stepper_enable(const struct device *dev) -{ - const struct h_bridge_stepper_config *config = dev->config; - struct h_bridge_stepper_data *data = dev->data; - int err; - - K_SPINLOCK(&data->lock) { - if (config->en_pin.port != NULL) { - err = gpio_pin_set_dt(&config->en_pin, 1); - } else { - LOG_DBG("No en_pin detected"); - err = -ENOTSUP; - } - } - return err; -} - -static int h_bridge_stepper_disable(const struct device *dev) -{ - const struct h_bridge_stepper_config *config = dev->config; - struct h_bridge_stepper_data *data = dev->data; - int err; - - K_SPINLOCK(&data->lock) { - if (config->en_pin.port != NULL) { - err = gpio_pin_set_dt(&config->en_pin, 0); - } else { - LOG_DBG("No en_pin detected, power stages will not be turned off if " - "stepper is in motion"); - err = -ENOTSUP; - } - } - return err; -} - -static int h_bridge_stepper_stop(const struct device *dev) -{ - struct h_bridge_stepper_data *data = dev->data; - int err; - - K_SPINLOCK(&data->lock) { - err = k_work_cancel_delayable(&data->stepper_dwork); - - if (data->callback && !err) { - data->callback(data->dev, STEPPER_EVENT_STOPPED, data->event_cb_user_data); - } - } - return err; -} - -static int h_bridge_stepper_init(const struct device *dev) -{ - struct h_bridge_stepper_data *data = dev->data; - const struct h_bridge_stepper_config *config = dev->config; - int err; - - data->dev = dev; - LOG_DBG("Initializing %s h_bridge_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); - } - - if (config->en_pin.port != NULL) { - if (!gpio_is_ready_dt(&config->en_pin)) { - LOG_ERR("Enable Pin is not ready"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&config->en_pin, GPIO_OUTPUT_INACTIVE); - if (err != 0) { - LOG_ERR("%s: Failed to configure en_pin (error: %d)", dev->name, err); - return err; - } - } - - k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); - return 0; -} - -static DEVICE_API(stepper, h_bridge_stepper_api) = { - .enable = h_bridge_stepper_enable, - .disable = h_bridge_stepper_disable, - .set_micro_step_res = h_bridge_stepper_set_micro_step_res, - .get_micro_step_res = h_bridge_stepper_get_micro_step_res, - .set_reference_position = h_bridge_stepper_set_reference_position, - .get_actual_position = h_bridge_stepper_get_actual_position, - .set_event_callback = h_bridge_stepper_set_event_callback, - .set_microstep_interval = h_bridge_stepper_set_microstep_interval, - .move_by = h_bridge_stepper_move_by, - .move_to = h_bridge_stepper_move_to, - .run = h_bridge_stepper_run, - .stop = h_bridge_stepper_stop, - .is_moving = h_bridge_stepper_is_moving, -}; - -#define H_BRIDGE_STEPPER_DEFINE(inst) \ - static const struct gpio_dt_spec h_bridge_stepper_motor_control_pins_##inst[] = { \ - DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \ - }; \ - BUILD_ASSERT(ARRAY_SIZE(h_bridge_stepper_motor_control_pins_##inst) == 4, \ - "h_bridge stepper driver currently supports only 4 wire configuration"); \ - static const struct h_bridge_stepper_config h_bridge_stepper_config_##inst = { \ - .en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}), \ - .invert_direction = DT_INST_PROP(inst, invert_direction), \ - .control_pins = h_bridge_stepper_motor_control_pins_##inst}; \ - static struct h_bridge_stepper_data h_bridge_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, \ - "h_bridge stepper driver supports up to 2 micro steps"); \ - DEVICE_DT_INST_DEFINE(inst, h_bridge_stepper_init, NULL, &h_bridge_stepper_data_##inst, \ - &h_bridge_stepper_config_##inst, POST_KERNEL, \ - CONFIG_STEPPER_INIT_PRIORITY, &h_bridge_stepper_api); - -DT_INST_FOREACH_STATUS_OKAY(H_BRIDGE_STEPPER_DEFINE) diff --git a/drivers/stepper/step_dir/CMakeLists.txt b/drivers/stepper/step_dir/CMakeLists.txt deleted file mode 100644 index 7daa4cc04d5b2..0000000000000 --- a/drivers/stepper/step_dir/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz -# SPDX-License-Identifier: Apache-2.0 - -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 deleted file mode 100644 index d8629c649ffd2..0000000000000 --- a/drivers/stepper/step_dir/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2024 Fabian Blatz -# SPDX-License-Identifier: Apache-2.0 - -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 deleted file mode 100644 index 724360d6ad44a..0000000000000 --- a/drivers/stepper/step_dir/step_dir_stepper_common.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "step_dir_stepper_common.h" - -#include -LOG_MODULE_REGISTER(step_dir_stepper, CONFIG_STEPPER_LOG_LEVEL); - -static inline int update_dir_pin(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; - } - - return ret; -} - -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) -{ - 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 int start_stepping(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; - - ret = config->timing_source->update(dev, config->dual_edge - ? data->microstep_interval_ns - : data->microstep_interval_ns / 2); - if (ret < 0) { - LOG_ERR("Failed to update timing source: %d", ret); - return ret; - } - - ret = config->timing_source->start(dev); - if (ret < 0) { - LOG_ERR("Failed to start timing source: %d", ret); - return ret; - } - - stepper_handle_timing_signal(dev); - return 0; -} - -static void update_remaining_steps(const struct device *dev) -{ - const struct step_dir_stepper_common_config *config = dev->config; - struct step_dir_stepper_common_data *data = dev->data; - - if (data->step_high && !config->dual_edge) { - return; - } - if (atomic_get(&data->step_count) > 0) { - atomic_dec(&data->step_count); - } else if (atomic_get(&data->step_count) < 0) { - atomic_inc(&data->step_count); - } -} - -static void update_direction_from_step_count(const struct device *dev) -{ - struct step_dir_stepper_common_data *data = dev->data; - - if (atomic_get(&data->step_count) > 0) { - data->direction = STEPPER_DIRECTION_POSITIVE; - } else if (atomic_get(&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; - - update_remaining_steps(dev); - - if (config->timing_source->needs_reschedule(dev) && atomic_get(&data->step_count) != 0) { - (void)config->timing_source->start(dev); - } else if (atomic_get(&data->step_count) == 0) { - config->timing_source->stop(data->dev); - stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED); - } -} - -static void velocity_mode_task(const struct device *dev) -{ - const struct step_dir_stepper_common_config *config = dev->config; - - if (config->timing_source->needs_reschedule(dev)) { - (void)config->timing_source->start(dev); - } -} - -void stepper_handle_timing_signal(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; - - atomic_val_t step_pin_status = atomic_xor(&data->step_high, 1) ^ 1; - - ret = gpio_pin_set_dt(&config->step_pin, atomic_get(&step_pin_status)); - if (ret < 0) { - LOG_ERR("Failed to set step pin: %d", ret); - return; - } - - if (!atomic_get(&step_pin_status) || config->dual_edge) { - if (data->direction == STEPPER_DIRECTION_POSITIVE) { - atomic_inc(&data->actual_position); - } else { - atomic_dec(&data->actual_position); - } - } - - 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; - } -} - -int step_dir_stepper_common_init(const struct device *dev) -{ - const struct step_dir_stepper_common_config *config = dev->config; - int ret; - - if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) { - LOG_ERR("GPIO pins are not ready"); - return -ENODEV; - } - - ret = gpio_pin_configure_dt(&config->step_pin, GPIO_OUTPUT); - if (ret < 0) { - LOG_ERR("Failed to configure step pin: %d", ret); - return ret; - } - - ret = gpio_pin_configure_dt(&config->dir_pin, GPIO_OUTPUT); - if (ret < 0) { - LOG_ERR("Failed to configure dir pin: %d", ret); - 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; - int ret; - - if (data->microstep_interval_ns == 0) { - LOG_ERR("Step interval not set or invalid step interval set"); - return -EINVAL; - } - - if (micro_steps == 0) { - stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED); - config->timing_source->stop(dev); - return 0; - } - - K_SPINLOCK(&data->lock) { - data->run_mode = STEPPER_RUN_MODE_POSITION; - atomic_set(&data->step_count, micro_steps); - update_direction_from_step_count(dev); - ret = update_dir_pin(dev); - if (ret < 0) { - K_SPINLOCK_BREAK; - } - - ret = start_stepping(dev); - } - - return ret; -} - -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; - } - - if (config->dual_edge && (microstep_interval_ns < config->step_width_ns)) { - LOG_ERR("Step interval too small for configured step width"); - return -EINVAL; - } - - if (microstep_interval_ns < 2 * config->step_width_ns) { - LOG_ERR("Step interval too small for configured step width"); - return -EINVAL; - } - - K_SPINLOCK(&data->lock) { - data->microstep_interval_ns = microstep_interval_ns; - config->timing_source->update(dev, config->dual_edge - ? data->microstep_interval_ns - : data->microstep_interval_ns / 2); - } - - 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; - - *value = atomic_get(&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 */ - steps_to_move = value - atomic_get(&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; - int ret; - - 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_VELOCITY; - data->direction = direction; - ret = update_dir_pin(dev); - if (ret < 0) { - K_SPINLOCK_BREAK; - } - - ret = start_stepping(dev); - } - - return ret; -} - -int step_dir_stepper_common_stop(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; - - ret = config->timing_source->stop(dev); - if (ret != 0) { - LOG_ERR("Failed to stop timing source: %d", ret); - return ret; - } - - if (!config->dual_edge && atomic_cas(&data->step_high, 1, 0)) { - gpio_pin_set_dt(&config->step_pin, 0); - /* If we are in the high state, we need to account for that step */ - if (data->direction == STEPPER_DIRECTION_POSITIVE) { - atomic_inc(&data->actual_position); - } else { - atomic_dec(&data->actual_position); - } - } - - 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 deleted file mode 100644 index 0ccb96ddbc762..0000000000000 --- a/drivers/stepper/step_dir/step_dir_stepper_common.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2024 Fabian Blatz - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_COMMON_H_ -#define ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_COMMON_H_ - -/** - * @brief Stepper Driver APIs - * @defgroup step_dir_stepper Stepper Driver APIs - * @ingroup io_interfaces - * @{ - */ - -#include -#include -#include -#include - -#include "step_dir_stepper_timing_source.h" - -/** - * @brief Common step direction stepper config. - * - * This structure **must** be placed first in the driver's config structure. - */ -struct step_dir_stepper_common_config { - const struct gpio_dt_spec step_pin; - const struct gpio_dt_spec dir_pin; - uint32_t step_width_ns; - bool dual_edge; - const struct stepper_timing_source_api *timing_source; - const struct device *counter; - bool invert_direction; -}; - -/** - * @brief Initialize common step direction stepper config from devicetree instance. - * If the counter property is set, the timing source will be set to the counter timing - * source. - * - * @param node_id The devicetree node identifier. - */ -#define STEP_DIR_STEPPER_DT_COMMON_CONFIG_INIT(node_id) \ - { \ - .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), \ - .step_width_ns = DT_PROP(node_id, step_width_ns), \ - .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)), \ - } - -/** - * @brief Initialize common step direction stepper config from devicetree instance. - * @param inst Instance. - */ -#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; - uint64_t microstep_interval_ns; - atomic_t actual_position; - atomic_t step_count; - stepper_event_callback_t callback; - void *event_cb_user_data; - - struct k_work_delayable stepper_dwork; - atomic_t step_high; -#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) \ - 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"); - -/** - * @brief Common function to initialize a step direction stepper device at init time. - * - * This function must be called at the end of the device init function. - * - * @param dev Step direction stepper device instance. - * - * @retval 0 If initialized successfully. - * @retval -errno Negative errno in case of failure. - */ -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. - * - * @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. - * - * @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 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. - */ -int step_dir_stepper_common_run(const struct device *dev, const enum stepper_direction direction); - -/** - * @brief Stop the stepper motor. - * - * @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 sets a user-defined callback that will be invoked when a stepper motor event - * occurs. - * - * @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. - */ -void stepper_trigger_callback(const struct device *dev, enum stepper_event event); - -/** @} */ - -#endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_COMMON_H_ */ diff --git a/drivers/stepper/stepper_shell.c b/drivers/stepper/stepper_shell.c index 1e96fb20c5f11..8eede1677d0c6 100644 --- a/drivers/stepper/stepper_shell.c +++ b/drivers/stepper/stepper_shell.c @@ -20,7 +20,7 @@ enum { struct stepper_microstep_map { const char *name; - enum stepper_micro_step_resolution microstep; + enum stepper_drv_micro_step_resolution microstep; }; struct stepper_direction_map { @@ -28,6 +28,8 @@ struct stepper_direction_map { enum stepper_direction direction; }; +#define STEPPER_DRV_MICROSTEP_PARAM_IDX 2 + #define STEPPER_DIRECTION_MAP_ENTRY(_name, _dir) \ { \ .name = _name, \ @@ -40,10 +42,33 @@ struct stepper_direction_map { .microstep = _microstep, \ } +static void print_stepper_drv_event_cb(const struct device *dev, const enum stepper_drv_event event, + void *user_data) +{ + const struct shell *sh = user_data; + + if (!sh) { + return; + } + + switch (event) { + case STEPPER_DRV_EVENT_STALL_DETECTED: + shell_info(sh, "%s: Stall detected.", dev->name); + break; + case STEPPER_DRV_EVENT_FAULT_DETECTED: + shell_info(sh, "%s: Fault detected.", dev->name); + break; + default: + shell_info(sh, "%s: Unknown event.", dev->name); + break; + } +} + static void print_callback(const struct device *dev, const enum stepper_event event, void *user_data) { const struct shell *sh = user_data; + if (!sh) { return; } @@ -52,9 +77,6 @@ static void print_callback(const struct device *dev, const enum stepper_event ev case STEPPER_EVENT_STEPS_COMPLETED: shell_info(sh, "%s: Steps completed.", dev->name); break; - case STEPPER_EVENT_STALL_DETECTED: - shell_info(sh, "%s: Stall detected.", dev->name); - break; case STEPPER_EVENT_LEFT_END_STOP_DETECTED: shell_info(sh, "%s: Left limit switch pressed.", dev->name); break; @@ -64,16 +86,18 @@ static void print_callback(const struct device *dev, const enum stepper_event ev case STEPPER_EVENT_STOPPED: shell_info(sh, "%s: Stepper stopped.", dev->name); break; - case STEPPER_EVENT_FAULT_DETECTED: - shell_info(sh, "%s: Fault detected.", dev->name); - break; default: shell_info(sh, "%s: Unknown signal received.", dev->name); break; } } -static bool device_is_stepper(const struct device *dev) +static bool device_is_stepper_drv(const struct device *dev) +{ + return DEVICE_API_IS(stepper_drv, dev); +} + +static bool device_is_stepper_controller(const struct device *dev) { return DEVICE_API_IS(stepper, dev); } @@ -84,15 +108,15 @@ static const struct stepper_direction_map stepper_direction_map[] = { }; static const struct stepper_microstep_map stepper_microstep_map[] = { - STEPPER_MICROSTEP_MAP("1", STEPPER_MICRO_STEP_1), - STEPPER_MICROSTEP_MAP("2", STEPPER_MICRO_STEP_2), - STEPPER_MICROSTEP_MAP("4", STEPPER_MICRO_STEP_4), - STEPPER_MICROSTEP_MAP("8", STEPPER_MICRO_STEP_8), - STEPPER_MICROSTEP_MAP("16", STEPPER_MICRO_STEP_16), - STEPPER_MICROSTEP_MAP("32", STEPPER_MICRO_STEP_32), - STEPPER_MICROSTEP_MAP("64", STEPPER_MICRO_STEP_64), - STEPPER_MICROSTEP_MAP("128", STEPPER_MICRO_STEP_128), - STEPPER_MICROSTEP_MAP("256", STEPPER_MICRO_STEP_256), + STEPPER_MICROSTEP_MAP("1", STEPPER_DRV_MICRO_STEP_1), + STEPPER_MICROSTEP_MAP("2", STEPPER_DRV_MICRO_STEP_2), + STEPPER_MICROSTEP_MAP("4", STEPPER_DRV_MICRO_STEP_4), + STEPPER_MICROSTEP_MAP("8", STEPPER_DRV_MICRO_STEP_8), + STEPPER_MICROSTEP_MAP("16", STEPPER_DRV_MICRO_STEP_16), + STEPPER_MICROSTEP_MAP("32", STEPPER_DRV_MICRO_STEP_32), + STEPPER_MICROSTEP_MAP("64", STEPPER_DRV_MICRO_STEP_64), + STEPPER_MICROSTEP_MAP("128", STEPPER_DRV_MICRO_STEP_128), + STEPPER_MICROSTEP_MAP("256", STEPPER_DRV_MICRO_STEP_256), }; static void cmd_stepper_direction(size_t idx, struct shell_static_entry *entry) @@ -125,7 +149,7 @@ SHELL_DYNAMIC_CMD_CREATE(dsub_stepper_microstep, cmd_stepper_microstep); static void cmd_pos_stepper_motor_name(size_t idx, struct shell_static_entry *entry) { - const struct device *dev = shell_device_filter(idx, device_is_stepper); + const struct device *dev = shell_device_filter(idx, device_is_stepper_drv); entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; @@ -135,9 +159,21 @@ static void cmd_pos_stepper_motor_name(size_t idx, struct shell_static_entry *en SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_motor_name, cmd_pos_stepper_motor_name); -static void cmd_pos_stepper_motor_name_dir(size_t idx, struct shell_static_entry *entry) +static void cmd_pos_stepper_controller_name(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_filter(idx, device_is_stepper_controller); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = "List Devices"; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_controller_name, cmd_pos_stepper_controller_name); + +static void cmd_pos_stepper_controller_name_dir(size_t idx, struct shell_static_entry *entry) { - const struct device *dev = shell_device_filter(idx, device_is_stepper); + const struct device *dev = shell_device_filter(idx, device_is_stepper_controller); if (dev != NULL) { entry->syntax = dev->name; @@ -149,11 +185,11 @@ static void cmd_pos_stepper_motor_name_dir(size_t idx, struct shell_static_entry entry->subcmd = &dsub_stepper_direction; } -SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_motor_name_dir, cmd_pos_stepper_motor_name_dir); +SHELL_DYNAMIC_CMD_CREATE(dsub_pos_stepper_controller_name_dir, cmd_pos_stepper_controller_name_dir); static void cmd_pos_stepper_motor_name_microstep(size_t idx, struct shell_static_entry *entry) { - const struct device *dev = shell_device_filter(idx, device_is_stepper); + const struct device *dev = shell_device_filter(idx, device_is_stepper_drv); if (dev != NULL) { entry->syntax = dev->name; @@ -188,11 +224,15 @@ static int cmd_stepper_enable(const struct shell *sh, size_t argc, char **argv) return err; } - err = stepper_enable(dev); + err = stepper_drv_enable(dev); if (err) { shell_error(sh, "Error: %d", err); } + err = stepper_drv_set_event_cb(dev, print_stepper_drv_event_cb, (void *)sh); + if (err) { + shell_error(sh, "Failed to set stepper driver event callback: %d", err); + } return err; } @@ -206,7 +246,7 @@ static int cmd_stepper_disable(const struct shell *sh, size_t argc, char **argv) return err; } - err = stepper_disable(dev); + err = stepper_drv_disable(dev); if (err) { shell_error(sh, "Error: %d", err); } @@ -271,6 +311,7 @@ static int cmd_stepper_set_microstep_interval(const struct shell *sh, size_t arg { const struct device *dev; int err = 0; + uint64_t step_interval = shell_strtoull(argv[ARG_IDX_PARAM], 10, &err); if (err < 0) { @@ -293,18 +334,20 @@ static int cmd_stepper_set_microstep_interval(const struct shell *sh, size_t arg static int cmd_stepper_set_micro_step_res(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; - enum stepper_micro_step_resolution resolution; + enum stepper_drv_micro_step_resolution resolution; int err = -EINVAL; for (int i = 0; i < ARRAY_SIZE(stepper_microstep_map); i++) { - if (strcmp(argv[ARG_IDX_PARAM], stepper_microstep_map[i].name) == 0) { + if (strcmp(argv[STEPPER_DRV_MICROSTEP_PARAM_IDX], stepper_microstep_map[i].name) == + 0) { resolution = stepper_microstep_map[i].microstep; err = 0; break; } } if (err != 0) { - shell_error(sh, "Invalid microstep value %s", argv[ARG_IDX_PARAM]); + shell_error(sh, "Invalid microstep value %s", + argv[STEPPER_DRV_MICROSTEP_PARAM_IDX]); return err; } @@ -313,7 +356,7 @@ static int cmd_stepper_set_micro_step_res(const struct shell *sh, size_t argc, c return err; } - err = stepper_set_micro_step_res(dev, resolution); + err = stepper_drv_set_micro_step_res(dev, resolution); if (err) { shell_error(sh, "Error: %d", err); } @@ -325,14 +368,14 @@ static int cmd_stepper_get_micro_step_res(const struct shell *sh, size_t argc, c { const struct device *dev; int err; - enum stepper_micro_step_resolution micro_step_res; + enum stepper_drv_micro_step_resolution micro_step_res; err = parse_device_arg(sh, argv, &dev); if (err < 0) { return err; } - err = stepper_get_micro_step_res(dev, µ_step_res); + err = stepper_drv_get_micro_step_res(dev, µ_step_res); if (err < 0) { shell_warn(sh, "Failed to get micro-step resolution: %d", err); } else { @@ -346,6 +389,7 @@ static int cmd_stepper_set_reference_position(const struct shell *sh, size_t arg { const struct device *dev; int err = 0; + int32_t position = shell_strtol(argv[ARG_IDX_PARAM], 10, &err); if (err < 0) { @@ -369,6 +413,7 @@ static int cmd_stepper_get_actual_position(const struct shell *sh, size_t argc, { const struct device *dev; int err; + int32_t actual_position; err = parse_device_arg(sh, argv, &dev); @@ -390,6 +435,7 @@ static int cmd_stepper_move_to(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err = 0; + const int32_t position = shell_strtol(argv[ARG_IDX_PARAM], 10, &err); if (err < 0) { @@ -417,9 +463,11 @@ static int cmd_stepper_move_to(const struct shell *sh, size_t argc, char **argv) static int cmd_stepper_run(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; - int err = -EINVAL; + int err; + enum stepper_direction direction = STEPPER_DIRECTION_POSITIVE; + err = -EINVAL; for (int i = 0; i < ARRAY_SIZE(stepper_direction_map); i++) { if (strcmp(argv[ARG_IDX_PARAM], stepper_direction_map[i].name) == 0) { direction = stepper_direction_map[i].direction; @@ -451,13 +499,12 @@ static int cmd_stepper_run(const struct shell *sh, size_t argc, char **argv) return 0; } -static int cmd_stepper_info(const struct shell *sh, size_t argc, char **argv) +static int cmd_stepper_control_info(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; bool is_moving; int32_t actual_position; - enum stepper_micro_step_resolution micro_step_res; err = parse_device_arg(sh, argv, &dev); if (err < 0) { @@ -474,18 +521,35 @@ static int cmd_stepper_info(const struct shell *sh, size_t argc, char **argv) shell_print(sh, "Actual Position: %d", actual_position); } - err = stepper_get_micro_step_res(dev, µ_step_res); + err = stepper_is_moving(dev, &is_moving); if (err < 0) { - shell_warn(sh, "Failed to get micro-step resolution: %d", err); + shell_warn(sh, "Failed to check if the motor is moving: %d", err); } else { - shell_print(sh, "Micro-step Resolution: %d", micro_step_res); + shell_print(sh, "Is Moving: %s", is_moving ? "Yes" : "No"); } - err = stepper_is_moving(dev, &is_moving); + return 0; +} + +static int cmd_stepper_info(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + int err; + enum stepper_drv_micro_step_resolution micro_step_res; + + err = parse_device_arg(sh, argv, &dev); if (err < 0) { - shell_warn(sh, "Failed to check if the motor is moving: %d", err); + return err; + } + + shell_print(sh, "Stepper Info:"); + shell_print(sh, "Device: %s", dev->name); + + err = stepper_drv_get_micro_step_res(dev, µ_step_res); + if (err < 0) { + shell_warn(sh, "Failed to get micro-step resolution: %d", err); } else { - shell_print(sh, "Is Moving: %s", is_moving ? "Yes" : "No"); + shell_print(sh, "Micro-step Resolution: %d", micro_step_res); } return 0; @@ -499,19 +563,21 @@ SHELL_STATIC_SUBCMD_SET_CREATE( " ", cmd_stepper_set_micro_step_res, 3, 0), SHELL_CMD_ARG(get_micro_step_res, &dsub_pos_stepper_motor_name, "", cmd_stepper_get_micro_step_res, 2, 0), - SHELL_CMD_ARG(set_reference_position, &dsub_pos_stepper_motor_name, " ", - cmd_stepper_set_reference_position, 3, 0), - SHELL_CMD_ARG(get_actual_position, &dsub_pos_stepper_motor_name, "", + SHELL_CMD_ARG(set_reference_position, &dsub_pos_stepper_controller_name, + " ", cmd_stepper_set_reference_position, 3, 0), + SHELL_CMD_ARG(get_actual_position, &dsub_pos_stepper_controller_name, "", cmd_stepper_get_actual_position, 2, 0), - SHELL_CMD_ARG(set_microstep_interval, &dsub_pos_stepper_motor_name, + SHELL_CMD_ARG(set_microstep_interval, &dsub_pos_stepper_controller_name, " ", cmd_stepper_set_microstep_interval, 3, 0), - SHELL_CMD_ARG(move_by, &dsub_pos_stepper_motor_name, " ", + SHELL_CMD_ARG(move_by, &dsub_pos_stepper_controller_name, " ", cmd_stepper_move_by, 3, 0), - SHELL_CMD_ARG(move_to, &dsub_pos_stepper_motor_name, " ", + SHELL_CMD_ARG(move_to, &dsub_pos_stepper_controller_name, " ", cmd_stepper_move_to, 3, 0), - SHELL_CMD_ARG(run, &dsub_pos_stepper_motor_name_dir, " ", + SHELL_CMD_ARG(run, &dsub_pos_stepper_controller_name_dir, " ", cmd_stepper_run, 3, 0), - SHELL_CMD_ARG(stop, &dsub_pos_stepper_motor_name, "", cmd_stepper_stop, 2, 0), + SHELL_CMD_ARG(stop, &dsub_pos_stepper_controller_name, "", cmd_stepper_stop, 2, 0), + SHELL_CMD_ARG(control_info, &dsub_pos_stepper_controller_name, "", + cmd_stepper_control_info, 2, 0), SHELL_CMD_ARG(info, &dsub_pos_stepper_motor_name, "", cmd_stepper_info, 2, 0), SHELL_SUBCMD_SET_END); diff --git a/drivers/stepper/ti/Kconfig.drv84xx b/drivers/stepper/ti/Kconfig.drv84xx index 58ccc50bb7c52..b346ccc6a2ed7 100644 --- a/drivers/stepper/ti/Kconfig.drv84xx +++ b/drivers/stepper/ti/Kconfig.drv84xx @@ -5,7 +5,5 @@ config DRV84XX bool "TI DRV84XX stepper motor driver" default y depends on DT_HAS_TI_DRV84XX_ENABLED - 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 5e99964abea67..0f2a9246891b9 100644 --- a/drivers/stepper/ti/drv84xx.c +++ b/drivers/stepper/ti/drv84xx.c @@ -5,11 +5,9 @@ #define DT_DRV_COMPAT ti_drv84xx -#include #include #include #include -#include "../step_dir/step_dir_stepper_common.h" #include LOG_MODULE_REGISTER(drv84xx, CONFIG_STEPPER_LOG_LEVEL); @@ -23,11 +21,10 @@ LOG_MODULE_REGISTER(drv84xx, CONFIG_STEPPER_LOG_LEVEL); /** * @brief DRV84XX stepper driver configuration data. * - * This structure contains all of the devicetree specifications for the pins + * This structure contains all the devicetree specifications for the pins * needed by a given DRV84XX stepper driver. */ struct drv84xx_config { - struct step_dir_stepper_common_config common; struct gpio_dt_spec sleep_pin; struct gpio_dt_spec en_pin; struct gpio_dt_spec m0_pin; @@ -47,14 +44,14 @@ struct drv84xx_pin_states { * @brief DRV84XX stepper driver data. */ struct drv84xx_data { - const struct step_dir_stepper_common_data common; const struct device *dev; struct drv84xx_pin_states pin_states; - enum stepper_micro_step_resolution ustep_res; + enum stepper_drv_micro_step_resolution ustep_res; struct gpio_callback fault_cb_data; + stepper_drv_event_cb_t fault_cb; + void *fault_cb_user_data; }; -STEP_DIR_STEPPER_STRUCT_CHECK(struct drv84xx_config, struct drv84xx_data); static int drv84xx_set_microstep_pin(const struct device *dev, const struct gpio_dt_spec *pin, int value) @@ -255,8 +252,19 @@ static int drv84xx_disable(const struct device *dev) return ret; } +static int drv84xx_set_fault_cb(const struct device *dev, stepper_drv_event_cb_t fault_cb, + void *user_data) +{ + struct drv84xx_data *data = dev->data; + + data->fault_cb = fault_cb; + data->fault_cb_user_data = user_data; + + return 0; +} + static int drv84xx_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution micro_step_res) + enum stepper_drv_micro_step_resolution micro_step_res) { const struct drv84xx_config *config = dev->config; struct drv84xx_data *data = dev->data; @@ -279,39 +287,39 @@ static int drv84xx_set_micro_step_res(const struct device *dev, * 3: 330kΩ */ switch (micro_step_res) { - case STEPPER_MICRO_STEP_1: + case STEPPER_DRV_MICRO_STEP_1: m0_value = 0; m1_value = 0; break; - case STEPPER_MICRO_STEP_2: + case STEPPER_DRV_MICRO_STEP_2: m0_value = 2; m1_value = 0; break; - case STEPPER_MICRO_STEP_4: + case STEPPER_DRV_MICRO_STEP_4: m0_value = 0; m1_value = 1; break; - case STEPPER_MICRO_STEP_8: + case STEPPER_DRV_MICRO_STEP_8: m0_value = 1; m1_value = 1; break; - case STEPPER_MICRO_STEP_16: + case STEPPER_DRV_MICRO_STEP_16: m0_value = 2; m1_value = 1; break; - case STEPPER_MICRO_STEP_32: + case STEPPER_DRV_MICRO_STEP_32: m0_value = 0; m1_value = 2; break; - case STEPPER_MICRO_STEP_64: + case STEPPER_DRV_MICRO_STEP_64: m0_value = 2; m1_value = 3; break; - case STEPPER_MICRO_STEP_128: + case STEPPER_DRV_MICRO_STEP_128: m0_value = 2; m1_value = 2; break; - case STEPPER_MICRO_STEP_256: + case STEPPER_DRV_MICRO_STEP_256: m0_value = 1; m1_value = 2; break; @@ -337,7 +345,7 @@ static int drv84xx_set_micro_step_res(const struct device *dev, } static int drv84xx_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *micro_step_res) + enum stepper_drv_micro_step_resolution *micro_step_res) { struct drv84xx_data *data = dev->data; *micro_step_res = data->ustep_res; @@ -348,7 +356,12 @@ void fault_event(const struct device *dev, struct gpio_callback *cb, uint32_t pi { struct drv84xx_data *data = CONTAINER_OF(cb, struct drv84xx_data, fault_cb_data); - stepper_trigger_callback(data->dev, STEPPER_EVENT_FAULT_DETECTED); + if (data->fault_cb != NULL) { + data->fault_cb(data->dev, STEPPER_DRV_EVENT_FAULT_DETECTED, + data->fault_cb_user_data); + } else { + LOG_WRN_ONCE("%s: Fault pin triggered but no callback is set", dev->name); + } } static int drv84xx_init(const struct device *dev) @@ -404,12 +417,6 @@ static int drv84xx_init(const struct device *dev) } } - ret = step_dir_stepper_common_init(dev); - if (ret != 0) { - LOG_ERR("Failed to initialize common step direction stepper (error: %d)", ret); - return ret; - } - /* Configure fault pin if it is available */ if (config->fault_pin.port != NULL) { ret = gpio_pin_configure_dt(&config->fault_pin, GPIO_INPUT); @@ -432,26 +439,17 @@ static int drv84xx_init(const struct device *dev) return 0; } -static DEVICE_API(stepper, drv84xx_stepper_api) = { +static DEVICE_API(stepper_drv, drv84xx_stepper_api) = { .enable = drv84xx_enable, .disable = drv84xx_disable, - .move_by = step_dir_stepper_common_move_by, - .move_to = step_dir_stepper_common_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 = step_dir_stepper_common_run, - .stop = step_dir_stepper_common_stop, + .set_event_cb = drv84xx_set_fault_cb, .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, }; #define DRV84XX_DEVICE(inst) \ \ static const struct drv84xx_config drv84xx_config_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ .sleep_pin = GPIO_DT_SPEC_INST_GET_OR(inst, sleep_gpios, {0}), \ .en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}), \ .m0_pin = GPIO_DT_SPEC_INST_GET_OR(inst, m0_gpios, {0}), \ @@ -460,7 +458,6 @@ static DEVICE_API(stepper, drv84xx_stepper_api) = { }; \ \ static struct drv84xx_data drv84xx_data_##inst = { \ - .common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \ .ustep_res = DT_INST_PROP(inst, micro_step_res), \ .dev = DEVICE_DT_INST_GET(inst), \ }; \ diff --git a/dts/bindings/stepper/adi/adi,tmc2209.yaml b/dts/bindings/stepper/adi/adi,tmc2209.yaml index efc97ce26081c..e4246147aeed6 100644 --- a/dts/bindings/stepper/adi/adi,tmc2209.yaml +++ b/dts/bindings/stepper/adi/adi,tmc2209.yaml @@ -10,21 +10,14 @@ description: | enable-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; msx-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>, <&gpio0 2 GPIO_ACTIVE_HIGH>; - step-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; - dir-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; - dual-edge-step; } compatible: "adi,tmc2209" include: - - name: stepper-controller.yaml - - name: step-dir-timing.yaml + - name: stepper-driver.yaml properties: - step-width-ns: - default: 100 - msx-gpios: type: phandle-array description: | @@ -32,9 +25,3 @@ properties: The pins should be listed in the following order: - MS1 - MS2 - - dual-edge-step: - type: boolean - description: | - If present, the stepper motor controller supports dual edge step signals. - This means that the step signal can be toggled on both the rising and falling edge. diff --git a/dts/bindings/stepper/adi/adi,tmc50xx-motion-controller.yaml b/dts/bindings/stepper/adi/adi,tmc50xx-motion-controller.yaml new file mode 100644 index 0000000000000..560328272510c --- /dev/null +++ b/dts/bindings/stepper/adi/adi,tmc50xx-motion-controller.yaml @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +compatible: "adi,tmc50xx-motion-controller" + +include: + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - activate-stallguard2 + - stallguard-threshold-velocity + - stallguard-velocity-check-interval-ms + - name: adi,trinamic-ramp-generator.yaml + property-allowlist: + - vstart + - a1 + - v1 + - amax + - vmax + - dmax + - d1 + - vstop + - tzerowait + - vhigh + - vcoolthrs + - ihold + - irun + - iholddelay + +properties: + idx: + type: int + required: true diff --git a/dts/bindings/stepper/adi/adi,tmc50xx-stepper-driver.yaml b/dts/bindings/stepper/adi/adi,tmc50xx-stepper-driver.yaml new file mode 100644 index 0000000000000..6692c29de0af1 --- /dev/null +++ b/dts/bindings/stepper/adi/adi,tmc50xx-stepper-driver.yaml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +compatible: "adi,tmc50xx-stepper-driver" + +include: + - name: stepper-driver.yaml + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - stallguard2-threshold + +properties: + idx: + type: int + required: true diff --git a/dts/bindings/stepper/adi/adi,tmc50xx.yaml b/dts/bindings/stepper/adi/adi,tmc50xx.yaml index eba9cd3f664c2..bffcfd370b37a 100644 --- a/dts/bindings/stepper/adi/adi,tmc50xx.yaml +++ b/dts/bindings/stepper/adi/adi,tmc50xx.yaml @@ -15,40 +15,39 @@ description: | 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>; + /* common stepper controller settings */ + tmc50xx_0_stepper_driver: tmc50xx_0_stepper_driver { + idx = <0>; + compatible = "adi,tmc50xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC50XX */ + stallguard2-threshold=<30>; + }; - /* 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>; + /* ADI TMC ramp generator as well as current settings */ + tmc50xx_0_motion_controller: tmc50xx_0_motion_controller { + idx = <0>; + compatible = "adi,tmc50xx-motion-controller"; + vstart = <1000>; + vstop = <10>; + a1 = <10000>; + v1 = <50000>; + d1 = <14000>; + vmax = <900000>; + amax = <50000>; + dmax = <7000>; + tzerowait = <100>; + vhigh = <900000>; + vcoolthrs = <900000>; + ihold = <1>; + irun = <10>; + iholddelay = <1>; + activate-stallguard2; + stallguard-velocity-check-interval-ms=<1000>; + stallguard-threshold-velocity=<200000>; }; }; }; @@ -65,14 +64,6 @@ include: - lock-gconf properties: - "#address-cells": - default: 1 - const: 1 - - "#size-cells": - default: 0 - const: 0 - clock-frequency: type: int required: true @@ -83,31 +74,18 @@ properties: Hint: µstep velocity v[Hz] µsteps / s v[Hz] = v[50xx] * ( fCLK[Hz]/2 / 2^23 ) where v[50xx] is the value written to the TMC50XX. + shaft1: + type: boolean + description: | + Inverse motor 1 direction + + shaft2: + type: boolean + description: | + Inverse motor 2 direction + child-binding: include: - - name: stepper-controller.yaml - name: base.yaml property-allowlist: - reg - - name: adi,trinamic-ramp-generator.yaml - property-allowlist: - - vstart - - a1 - - v1 - - amax - - vmax - - dmax - - d1 - - vstop - - tzerowait - - vhigh - - vcoolthrs - - ihold - - irun - - iholddelay - - 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/adi/adi,tmc51xx-base.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml index dbdd40065c4a8..b311c5d120546 100644 --- a/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml +++ b/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml @@ -9,40 +9,12 @@ include: property-allowlist: - en-pwm-mode - test-mode - - name: stepper-controller.yaml - - name: adi,trinamic-ramp-generator.yaml - property-allowlist: - - vstart - - a1 - - v1 - - amax - - vmax - - dmax - - d1 - - vstop - - tzerowait - - thigh - - tcoolthrs - - tpwmthrs - - tpowerdown - - ihold - - irun - - iholddelay - - name: adi,trinamic-stallguard.yaml - property-allowlist: - - activate-stallguard2 - - stallguard2-threshold - - stallguard-threshold-velocity - - stallguard-velocity-check-interval-ms properties: - "#address-cells": - default: 1 - const: 1 - - "#size-cells": - default: 0 - const: 0 + shaft: + type: boolean + description: | + Inverse motor direction clock-frequency: type: int diff --git a/dts/bindings/stepper/adi/adi,tmc51xx-motion-controller.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-motion-controller.yaml new file mode 100644 index 0000000000000..2f0b92e08c4a5 --- /dev/null +++ b/dts/bindings/stepper/adi/adi,tmc51xx-motion-controller.yaml @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +compatible: "adi,tmc51xx-motion-controller" + +include: + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - activate-stallguard2 + - stallguard-threshold-velocity + - stallguard-velocity-check-interval-ms + - name: adi,trinamic-ramp-generator.yaml + property-allowlist: + - vstart + - a1 + - v1 + - amax + - vmax + - dmax + - d1 + - vstop + - tzerowait + - thigh + - tcoolthrs + - tpwmthrs + - tpowerdown + - ihold + - irun + - iholddelay diff --git a/dts/bindings/stepper/adi/adi,tmc51xx-spi.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-spi.yaml index f9213a95c11c7..88cb426576e31 100644 --- a/dts/bindings/stepper/adi/adi,tmc51xx-spi.yaml +++ b/dts/bindings/stepper/adi/adi,tmc51xx-spi.yaml @@ -13,28 +13,43 @@ description: | spi-max-frequency = ; diag0-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* Diag0 pin */ - #address-cells = <1>; - #size-cells = <0>; - /* Common settings from base binding */ clock-frequency = ; /* Internal/External Clock frequency */ - en-pwm-mode; - invert-direction; - micro-step-res = <256>; - - /* ADI TMC ramp generator as well as current settings */ - vstart = <0>; - vstop = <10>; - a1 = <1000>; - v1 = <50000>; - d1 = <1400>; - vmax = <200000>; - amax = <50000>; - dmax = <700>; - tzerowait = <100>; - ihold = <10>; - irun = <31>; - iholddelay = <6>; + en-pwm-mode, shaft; /* ADI TMC Global configuration flags */ + + tmc51xx_1_stepper_driver: tmc51xx_1_stepper_driver { + compatible = "adi,tmc51xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC51XX */ + stallguard2-threshold=<9>; + }; + + /* common stepper controller settings */ + tmc51xx_1_motion_controller: tmc51xx_1_motion_controller { + compatible = "adi,tmc51xx-motion-controller"; + + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard-threshold-velocity = <50000>; + + /* 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>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; }; diff --git a/dts/bindings/stepper/adi/adi,tmc51xx-stepper-driver.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-stepper-driver.yaml new file mode 100644 index 0000000000000..eda4b14487636 --- /dev/null +++ b/dts/bindings/stepper/adi/adi,tmc51xx-stepper-driver.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +compatible: "adi,tmc51xx-stepper-driver" + +include: + - name: stepper-driver.yaml + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - stallguard2-threshold diff --git a/dts/bindings/stepper/adi/adi,tmc51xx-uart.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-uart.yaml index a6691ff36b060..47ac1146dd2fb 100644 --- a/dts/bindings/stepper/adi/adi,tmc51xx-uart.yaml +++ b/dts/bindings/stepper/adi/adi,tmc51xx-uart.yaml @@ -23,21 +23,41 @@ description: | clock-frequency = ; en-pwm-mode; invert-direction; - micro-step-res = <256>; - - /* ADI TMC ramp generator as well as current settings */ - vstart = <0>; - vstop = <10>; - a1 = <1000>; - v1 = <50000>; - d1 = <1400>; - vmax = <200000>; - amax = <50000>; - dmax = <700>; - tzerowait = <100>; - ihold = <10>; - irun = <31>; - iholddelay = <6>; + + tmc51xx_stepper_driver: stepper_driver { + compatible = "adi,tmc51xx-stepper-driver"; + micro-step-res = <256>; + + /* ADI TMC stallguard settings specific to TMC51XX */ + stallguard2-threshold=<9>; + }; + + /* common stepper controller settings */ + tmc51xx_motion_controller: motion_controller { + compatible = "adi,tmc51xx-motion-controller"; + + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard-threshold-velocity = <50000>; + + /* 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>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; }; diff --git a/dts/bindings/stepper/allegro/allegro,a4979.yaml b/dts/bindings/stepper/allegro/allegro,a4979.yaml index d53832a83138c..958e01693f235 100644 --- a/dts/bindings/stepper/allegro/allegro,a4979.yaml +++ b/dts/bindings/stepper/allegro/allegro,a4979.yaml @@ -13,24 +13,17 @@ description: | compatible = "allegro,a4979"; micro-step-res = <2>; reset-gpios = <&gpiod 10 GPIO_ACTIVE_HIGH>; - dir-gpios = <&gpiod 14 GPIO_ACTIVE_HIGH>; - step-gpios = <&gpiod 15 GPIO_ACTIVE_HIGH>; en-gpios = <&gpiod 11 GPIO_ACTIVE_HIGH>; m0-gpios = <&gpiod 13 0>; m1-gpios = <&gpiod 12 0>; - counter = <&counter5>; }; compatible: "allegro,a4979" include: - - name: stepper-controller.yaml - - name: step-dir-timing.yaml + - name: stepper-driver.yaml properties: - step-width-ns: - default: 1000 - m0-gpios: required: true type: phandle-array diff --git a/dts/bindings/stepper/step-dir-timing.yaml b/dts/bindings/stepper/step-dir-timing.yaml deleted file mode 100644 index f67eeb1efa55a..0000000000000 --- a/dts/bindings/stepper/step-dir-timing.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya -# SPDX-License-Identifier: Apache-2.0 - -description: Step/Dir Control Signal Timing - -properties: - step-width-ns: - type: int - description: | - Minimum pulse width in nanoseconds for the step signal. diff --git a/dts/bindings/stepper/stepper-controller.yaml b/dts/bindings/stepper/stepper-controller.yaml deleted file mode 100644 index a73037bb6b975..0000000000000 --- a/dts/bindings/stepper/stepper-controller.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG -# SPDX-License-Identifier: Apache-2.0 - -description: Stepper Controller - -properties: - invert-direction: - type: boolean - description: | - Invert motor direction. - - micro-step-res: - type: int - default: 1 - enum: - - 1 - - 2 - - 4 - - 8 - - 16 - - 32 - - 64 - - 128 - - 256 - description: | - micro-step resolution to be set while initializing the device driver. - - en-gpios: - type: phandle-array - description: | - GPIO pins used to control the enable signal of the motor driver. - - step-gpios: - type: phandle-array - description: | - The GPIO pins used to send step signals to the stepper motor. - - dir-gpios: - type: phandle-array - 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/stepper-driver.yaml b/dts/bindings/stepper/stepper-driver.yaml new file mode 100644 index 0000000000000..a4ae99dc76e85 --- /dev/null +++ b/dts/bindings/stepper/stepper-driver.yaml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG +# SPDX-License-Identifier: Apache-2.0 + +description: collection of properties for stepper drivers. + +properties: + micro-step-res: + type: int + default: 1 + enum: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + description: | + micro-step resolution to be set while initializing the device driver. + + en-gpios: + type: phandle-array + description: | + GPIO pins used to control the enable signal of the motor driver. diff --git a/dts/bindings/stepper/ti/ti,drv84xx.yaml b/dts/bindings/stepper/ti/ti,drv84xx.yaml index d0efc11a3ebf6..82261257aa0a9 100644 --- a/dts/bindings/stepper/ti/ti,drv84xx.yaml +++ b/dts/bindings/stepper/ti/ti,drv84xx.yaml @@ -4,39 +4,26 @@ description: | TI DRV84XX stepper motor driver. Compatible with drv8424, drv8425, drv8426, drv8434 and drv8436. - SAFETY: - The counter needs to support both set_top_value functionalities: Setting a new top value and - attaching an ISR to the turnaround. - SAFETY: - The step gpio pin needs to be connected directly to the SOC GPIO controller, connecting the - pin to a controller connected via a bus such as i2c or others will lead to undefined behaviour. Example: drv8424: drv8424 { status = "okay"; compatible = "ti,drv84xx"; - dir-gpios = <&arduino_header 18 0>; - step-gpios = <&arduino_header 19 0>; fault-gpios = <&arduino_header 16 0>; sleep-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; en-gpios = <&arduino_header 14 0>; m0-gpios = <&mikroe_stepper_gpios 0 0>; m1-gpios = <&mikroe_stepper_gpios 1 0>; - counter = <&counter2>; }; compatible: "ti,drv84xx" include: - - name: stepper-controller.yaml - - name: step-dir-timing.yaml + - name: stepper-driver.yaml properties: - step-width-ns: - default: 970 - fault-gpios: type: phandle-array description: Fault pin. diff --git a/dts/bindings/stepper/zephyr,fake-stepper-controller.yaml b/dts/bindings/stepper/zephyr,fake-stepper-controller.yaml new file mode 100644 index 0000000000000..d7feab7961328 --- /dev/null +++ b/dts/bindings/stepper/zephyr,fake-stepper-controller.yaml @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +description: | + A fake stepper controller for use as either a stub or a mock in testing. + +compatible: "zephyr,fake-stepper-controller" diff --git a/dts/bindings/stepper/zephyr,fake-stepper-driver.yaml b/dts/bindings/stepper/zephyr,fake-stepper-driver.yaml new file mode 100644 index 0000000000000..3008315185483 --- /dev/null +++ b/dts/bindings/stepper/zephyr,fake-stepper-driver.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Fabian Blatz +# SPDX-License-Identifier: Apache-2.0 + +description: | + A fake stepper for use as either a stub or a mock in Zephyr testing. + +compatible: "zephyr,fake-stepper-driver" + +include: stepper-driver.yaml diff --git a/dts/bindings/stepper/zephyr,fake-stepper.yaml b/dts/bindings/stepper/zephyr,fake-stepper.yaml deleted file mode 100644 index d286dfbfeb4c5..0000000000000 --- a/dts/bindings/stepper/zephyr,fake-stepper.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2024 Fabian Blatz -# SPDX-License-Identifier: Apache-2.0 - -description: | - This binding provides a fake stepper controller for use as either a stub or a mock in - Zephyr testing. - -compatible: "zephyr,fake-stepper" - -include: stepper-controller.yaml diff --git a/dts/bindings/stepper/zephyr,gpio-step-dir-controller.yaml b/dts/bindings/stepper/zephyr,gpio-step-dir-controller.yaml new file mode 100644 index 0000000000000..6132ff482f805 --- /dev/null +++ b/dts/bindings/stepper/zephyr,gpio-step-dir-controller.yaml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya +# SPDX-License-Identifier: Apache-2.0 + +description: | + CPU based Stepper Motion Controller for controlling stepper motors using GPIO pins. + It is used to generate step and direction signals for a stepper motor driver. + Example: + step_dir_motion_control: step_dir_motion_control { + compatible = "zephyr,gpio-step-dir-controller"; + step-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; + dir-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + invert-direction; + counter = <&counter1>; + }; + +compatible: "zephyr,gpio-step-dir-controller" + +properties: + invert-direction: + type: boolean + description: | + Invert motor direction. + + step-gpios: + type: phandle-array + description: | + The GPIO pins used to send step signals to the stepper motor. + + dir-gpios: + type: phandle-array + 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. + This should be a reference to a counter node in the device tree. + When this property is present, the stepper motion controller will use + the counter as a timing source instead of the default work queue timing. + This enables more precise step timing control. + + step-width-ns: + type: int + required: true + description: | + Minimum pulse width in nanoseconds for the step signal. + + dual-edge-step: + type: boolean + description: | + If present, the stepper motor controller supports dual edge step signals. + This means that the step signal can be toggled on both the rising and falling edge. diff --git a/dts/bindings/stepper/zephyr,h-bridge-stepper.yaml b/dts/bindings/stepper/zephyr,h-bridge-stepper.yaml index b243a8d929385..9668487681d1b 100644 --- a/dts/bindings/stepper/zephyr,h-bridge-stepper.yaml +++ b/dts/bindings/stepper/zephyr,h-bridge-stepper.yaml @@ -7,9 +7,8 @@ description: | Example: /* Lead A is connected Lead C and Lead B is connected to Lead D*/ - stepper: stepper { + h_bridge_stepper: h_bridge_stepper { compatible = "zephyr,h-bridge-stepper"; - en-gpios = <&gpioa 6 GPIO_ACTIVE_HIGH>; gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>, /* Lead A1/A */ <&gpioc 7 GPIO_ACTIVE_HIGH>, /* Lead B1/B */ <&gpiob 0 GPIO_ACTIVE_HIGH>, /* Lead A2/C */ @@ -18,11 +17,34 @@ description: | compatible: "zephyr,h-bridge-stepper" -include: stepper-controller.yaml - properties: + invert-direction: + type: boolean + description: | + Invert motor direction. + gpios: type: phandle-array required: true description: | The gpio pin array on which the stepper inputs are to be connected + + lut-step-gap: + type: int + default: 1 + enum: + - 1 + - 2 + description: | + Step mode selection. + 1 - Half step mode + 2 - Full step mode + + counter: + type: phandle + description: | + Counter used for generating step-accurate pulse signals. + This should be a reference to a counter node in the device tree. + When this property is present, the stepper motion controller will use + the counter as a timing source instead of the default work queue timing. + This enables more precise step timing control. diff --git a/include/zephyr/drivers/stepper.h b/include/zephyr/drivers/stepper.h index 4ac168399a757..0e4828697c079 100644 --- a/include/zephyr/drivers/stepper.h +++ b/include/zephyr/drivers/stepper.h @@ -30,43 +30,6 @@ extern "C" { #endif -/** - * @brief Stepper Motor micro-step resolution options - */ -enum stepper_micro_step_resolution { - /** Full step resolution */ - STEPPER_MICRO_STEP_1 = 1, - /** 2 micro-steps per full step */ - STEPPER_MICRO_STEP_2 = 2, - /** 4 micro-steps per full step */ - STEPPER_MICRO_STEP_4 = 4, - /** 8 micro-steps per full step */ - STEPPER_MICRO_STEP_8 = 8, - /** 16 micro-steps per full step */ - STEPPER_MICRO_STEP_16 = 16, - /** 32 micro-steps per full step */ - STEPPER_MICRO_STEP_32 = 32, - /** 64 micro-steps per full step */ - STEPPER_MICRO_STEP_64 = 64, - /** 128 micro-steps per full step */ - STEPPER_MICRO_STEP_128 = 128, - /** 256 micro-steps per full step */ - STEPPER_MICRO_STEP_256 = 256, -}; - -/** - * @brief Macro to calculate the index of the microstep resolution - * @param res Microstep resolution - */ -#define MICRO_STEP_RES_INDEX(res) LOG2(res) - -#define VALID_MICRO_STEP_RES(res) \ - ((res) == STEPPER_MICRO_STEP_1 || (res) == STEPPER_MICRO_STEP_2 || \ - (res) == STEPPER_MICRO_STEP_4 || (res) == STEPPER_MICRO_STEP_8 || \ - (res) == STEPPER_MICRO_STEP_16 || (res) == STEPPER_MICRO_STEP_32 || \ - (res) == STEPPER_MICRO_STEP_64 || (res) == STEPPER_MICRO_STEP_128 || \ - (res) == STEPPER_MICRO_STEP_256) - /** * @brief Stepper Motor direction options */ @@ -83,7 +46,7 @@ enum stepper_direction { enum stepper_run_mode { /** Hold Mode */ STEPPER_RUN_MODE_HOLD = 0, - /** Position Mode*/ + /** Position Mode */ STEPPER_RUN_MODE_POSITION = 1, /** Velocity Mode */ STEPPER_RUN_MODE_VELOCITY = 2, @@ -95,16 +58,12 @@ enum stepper_run_mode { 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, + STEPPER_EVENT_LEFT_END_STOP_DETECTED = 1, /** Right end switch status changes to pressed */ - STEPPER_EVENT_RIGHT_END_STOP_DETECTED = 3, + STEPPER_EVENT_RIGHT_END_STOP_DETECTED = 2, /** Stepper has stopped */ - STEPPER_EVENT_STOPPED = 4, - /** Fault with the stepper controller detected */ - STEPPER_EVENT_FAULT_DETECTED = 5, + STEPPER_EVENT_STOPPED = 3, }; /** @@ -114,35 +73,6 @@ enum stepper_event { * */ -/** - * @brief Enable the stepper driver. - * - * @see stepper_enable() for details. - */ -typedef int (*stepper_enable_t)(const struct device *dev); - -/** - * @brief Disable the stepper driver. - * - * @see stepper_disable() for details. - */ -typedef int (*stepper_disable_t)(const struct device *dev); - -/** - * @brief Set the micro-step resolution - * - * @see stepper_set_micro_step_res() for details. - */ -typedef int (*stepper_set_micro_step_res_t)(const struct device *dev, - const enum stepper_micro_step_resolution resolution); - -/** - * @brief Get the micro-step resolution - * - * @see stepper_get_micro_step_res() for details. - */ -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 * @@ -170,6 +100,7 @@ typedef void (*stepper_event_callback_t)(const struct device *dev, const enum st */ 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. * @@ -216,10 +147,6 @@ typedef int (*stepper_is_moving_t)(const struct device *dev, bool *is_moving); * @brief Stepper Driver API */ __subsystem struct stepper_driver_api { - stepper_enable_t enable; - 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; @@ -235,105 +162,10 @@ __subsystem struct stepper_driver_api { * @endcond */ -/** - * @brief Enable stepper driver - * - * @details Enabling the driver shall switch on the power stage and energize the coils. - * - * @param dev pointer to the stepper driver instance - * - * @retval -EIO Error during Enabling - * @retval 0 Success - */ -__syscall int stepper_enable(const struct device *dev); - -static inline int z_impl_stepper_enable(const struct device *dev) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - return api->enable(dev); -} - -/** - * @brief Disable stepper driver - * - * @details Disabling the driver shall switch off the power stage and de-energize the coils. - * Disabling the stepper does not implicitly stop the stepper. If the motor shall not move after - * re-enabling the stepper than consider calling stepper_stop() before. - * - * @param dev pointer to the stepper driver instance - * - * @retval -ENOTSUP Disabling of driver is not supported. - * @retval -EIO Error during Disabling - * @retval 0 Success - */ -__syscall int stepper_disable(const struct device *dev); - -static inline int z_impl_stepper_disable(const struct device *dev) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - return api->disable(dev); -} - -/** - * @brief Set the micro-step resolution in stepper driver - * - * @param dev pointer to the stepper driver instance - * @param resolution micro-step resolution - * - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval -EINVAL If the requested resolution is invalid - * @retval -ENOTSUP If the requested resolution is not supported - * @retval 0 Success - */ -__syscall int stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution resolution); - -static inline int z_impl_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution resolution) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->set_micro_step_res == NULL) { - return -ENOSYS; - } - - if (!VALID_MICRO_STEP_RES(resolution)) { - return -EINVAL; - } - return api->set_micro_step_res(dev, resolution); -} - -/** - * @brief Get the micro-step resolution in stepper driver - * - * @param dev pointer to the stepper driver instance - * @param resolution micro-step resolution - * - * @retval -EIO General input / output error - * @retval -ENOSYS If not implemented by device driver - * @retval 0 Success - */ -__syscall int stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *resolution); - -static inline int z_impl_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *resolution) -{ - const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; - - if (api->get_micro_step_res == NULL) { - return -ENOSYS; - } - return api->get_micro_step_res(dev, resolution); -} - /** * @brief Set the reference position of the stepper * - * @param dev Pointer to the stepper driver instance. + * @param dev Pointer to the stepper controller instance. * @param value The reference position to set in micro-steps. * * @retval -EIO General input / output error @@ -345,6 +177,7 @@ __syscall int stepper_set_reference_position(const struct device *dev, int32_t v static inline int z_impl_stepper_set_reference_position(const struct device *dev, const int32_t value) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->set_reference_position == NULL) { @@ -358,7 +191,7 @@ static inline int z_impl_stepper_set_reference_position(const struct device *dev * @note This function does not guarantee that the returned position is the exact current * position. For precise positioning, encoders should be used in addition to the stepper driver. * - * @param dev pointer to the stepper driver instance + * @param dev pointer to the stepper controller instance * @param value The actual position to get in micro-steps * * @retval -EIO General input / output error @@ -369,6 +202,8 @@ __syscall int stepper_get_actual_position(const struct device *dev, int32_t *val static inline int z_impl_stepper_get_actual_position(const struct device *dev, int32_t *value) { + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(value != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->get_actual_position == NULL) { @@ -380,7 +215,7 @@ static inline int z_impl_stepper_get_actual_position(const struct device *dev, i /** * @brief Set the callback function to be called when a stepper event occurs * - * @param dev pointer to the stepper driver instance + * @param dev pointer to the stepper controller 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 @@ -395,6 +230,7 @@ static inline int z_impl_stepper_set_event_callback(const struct device *dev, stepper_event_callback_t callback, void *user_data) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->set_event_callback == NULL) { @@ -409,7 +245,7 @@ static inline int z_impl_stepper_set_event_callback(const struct device *dev, * @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 dev pointer to the stepper controller instance * @param microstep_interval_ns time interval between steps in nanoseconds * * @retval -EIO General input / output error @@ -423,6 +259,7 @@ __syscall int stepper_set_microstep_interval(const struct device *dev, static inline int z_impl_stepper_set_microstep_interval(const struct device *dev, const uint64_t microstep_interval_ns) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->set_microstep_interval == NULL) { @@ -437,7 +274,7 @@ static inline int z_impl_stepper_set_microstep_interval(const struct device *dev * @note 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 dev pointer to the stepper controller instance * @param micro_steps target micro-steps to be moved from the current position * * @retval -EIO General input / output error @@ -448,6 +285,7 @@ __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) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; return api->move_by(dev, micro_steps); @@ -459,7 +297,7 @@ static inline int z_impl_stepper_move_by(const struct device *dev, const int32_t * @note 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 dev pointer to the stepper controller instance * @param micro_steps target position to set in micro-steps * * @retval -EIO General input / output error @@ -470,11 +308,9 @@ __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) { + __ASSERT_NO_MSG(dev != NULL); 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); } @@ -485,7 +321,7 @@ static inline int z_impl_stepper_move_to(const struct device *dev, const int32_t * stalled or stopped using some other command, for instance, stepper_stop(). This * function is non-blocking. * - * @param dev pointer to the stepper driver instance + * @param dev pointer to the stepper controller instance * @param direction The direction to set * * @retval -EIO General input / output error @@ -498,6 +334,7 @@ __syscall int stepper_run(const struct device *dev, enum stepper_direction direc static inline int z_impl_stepper_run(const struct device *dev, const enum stepper_direction direction) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->run == NULL) { @@ -510,7 +347,7 @@ static inline int z_impl_stepper_run(const struct device *dev, * @brief Stop the stepper * @note Cancel all active movements. * - * @param dev pointer to the stepper driver instance + * @param dev pointer to the stepper controller instance * * @retval -EIO General input / output error * @retval -ENOSYS If not implemented by device driver @@ -520,6 +357,7 @@ __syscall int stepper_stop(const struct device *dev); static inline int z_impl_stepper_stop(const struct device *dev) { + __ASSERT_NO_MSG(dev != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->stop == NULL) { @@ -531,7 +369,7 @@ static inline int z_impl_stepper_stop(const struct device *dev) /** * @brief Check if the stepper is currently moving * - * @param dev pointer to the stepper driver instance + * @param dev pointer to the stepper controller instance * @param is_moving Pointer to a boolean to store the moving status of the stepper * * @retval -EIO General input / output error @@ -542,6 +380,8 @@ __syscall int stepper_is_moving(const struct device *dev, bool *is_moving); static inline int z_impl_stepper_is_moving(const struct device *dev, bool *is_moving) { + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(is_moving != NULL); const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api; if (api->is_moving == NULL) { @@ -550,6 +390,245 @@ static inline int z_impl_stepper_is_moving(const struct device *dev, bool *is_mo return api->is_moving(dev, is_moving); } +/** + * @} + */ + +/** + * @brief Stepper-Drv Driver Interface + * @defgroup stepper_drv_interface Stepper Drv Driver Interface + * @since 4.3 + * @version 0.1.0 + * @ingroup io_interfaces + * @{ + */ + +/** + * @brief Stepper Motor micro-step resolution options + */ +enum stepper_drv_micro_step_resolution { + /** Full step resolution */ + STEPPER_DRV_MICRO_STEP_1 = 1, + /** 2 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_2 = 2, + /** 4 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_4 = 4, + /** 8 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_8 = 8, + /** 16 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_16 = 16, + /** 32 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_32 = 32, + /** 64 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_64 = 64, + /** 128 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_128 = 128, + /** 256 micro-steps per full step */ + STEPPER_DRV_MICRO_STEP_256 = 256, +}; + +/** + * @brief Macro to calculate the index of the microstep resolution + * @param res Microstep resolution + */ +#define MICRO_STEP_RES_INDEX(res) LOG2(res) + +#define VALID_MICRO_STEP_RES(res) \ + ((res) == STEPPER_DRV_MICRO_STEP_1 || (res) == STEPPER_DRV_MICRO_STEP_2 || \ + (res) == STEPPER_DRV_MICRO_STEP_4 || (res) == STEPPER_DRV_MICRO_STEP_8 || \ + (res) == STEPPER_DRV_MICRO_STEP_16 || (res) == STEPPER_DRV_MICRO_STEP_32 || \ + (res) == STEPPER_DRV_MICRO_STEP_64 || (res) == STEPPER_DRV_MICRO_STEP_128 || \ + (res) == STEPPER_DRV_MICRO_STEP_256) + +enum stepper_drv_event { + /** Stepper driver stall detected */ + STEPPER_DRV_EVENT_STALL_DETECTED = 0, + /** Stepper driver fault detected */ + STEPPER_DRV_EVENT_FAULT_DETECTED = 1, +}; + +/** + * @cond INTERNAL_HIDDEN + * + * Stepper Drv driver API definition and system call entry points. + * + */ + +/** + * @brief Enable the stepper driver + * + * @see stepper_drv_enable() for details. + */ +typedef int (*stepper_drv_enable_t)(const struct device *dev); + +/** + * @brief Disable the stepper driver + * + * @see stepper_drv_disable() for details. + */ +typedef int (*stepper_drv_disable_t)(const struct device *dev); + +/** + * @brief Set the stepper micro-step resolution + * + * @see stepper_drv_set_micro_step_res() for details. + */ +typedef int (*stepper_drv_set_micro_step_res_t)( + const struct device *dev, const enum stepper_drv_micro_step_resolution resolution); + +/** + * @brief Get the stepper micro-step resolution + * + * @see stepper_drv_get_micro_step_res() for details. + */ +typedef int (*stepper_drv_get_micro_step_res_t)(const struct device *dev, + enum stepper_drv_micro_step_resolution *resolution); + +/** + * @brief Callback function for stepper driver events + */ +typedef void (*stepper_drv_event_cb_t)(const struct device *dev, const enum stepper_drv_event event, + void *user_data); + +/** + * @brief Set the callback function to be called when a stepper_drv_event occurs + * + * @see stepper_drv_set_event_callback() for details. + */ +typedef int (*stepper_drv_set_event_callback_t)(const struct device *dev, + stepper_drv_event_cb_t callback, void *user_data); + +/** + * @brief Stepper DRV Driver API + */ +__subsystem struct stepper_drv_driver_api { + stepper_drv_enable_t enable; + stepper_drv_disable_t disable; + stepper_drv_set_micro_step_res_t set_micro_step_res; + stepper_drv_get_micro_step_res_t get_micro_step_res; + stepper_drv_set_event_callback_t set_event_cb; +}; + +/** + * @endcond + */ + +/** + * @brief Enable stepper driver + * + * @details Enabling the driver shall switch on the power stage and energize the coils. + * + * @param dev pointer to the stepper_drv driver instance + * + * @retval -EIO Error during Enabling + * @retval 0 Success + */ +__syscall int stepper_drv_enable(const struct device *dev); + +static inline int z_impl_stepper_drv_enable(const struct device *dev) +{ + __ASSERT_NO_MSG(dev != NULL); + const struct stepper_drv_driver_api *api = (const struct stepper_drv_driver_api *)dev->api; + + return api->enable(dev); +} + +/** + * @brief Disable stepper driver + * + * @details Disabling the driver shall switch off the power stage and de-energize the coils. + * + * @param dev pointer to the stepper_drv driver instance + * + * @retval -ENOTSUP Disabling of driver is not supported. + * @retval -EIO Error during Disabling + * @retval 0 Success + */ +__syscall int stepper_drv_disable(const struct device *dev); + +static inline int z_impl_stepper_drv_disable(const struct device *dev) +{ + __ASSERT_NO_MSG(dev != NULL); + const struct stepper_drv_driver_api *api = (const struct stepper_drv_driver_api *)dev->api; + + return api->disable(dev); +} + +/** + * @brief Set the micro-step resolution in stepper driver + * + * @param dev pointer to the step dir driver instance + * @param res micro-step resolution + * + * @retval -EIO General input / output error + * @retval -EINVAL If the requested resolution is invalid + * @retval -ENOTSUP If the requested resolution is not supported + * @retval 0 Success + */ +__syscall int stepper_drv_set_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution res); + +static inline int z_impl_stepper_drv_set_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution res) +{ + __ASSERT_NO_MSG(dev != NULL); + const struct stepper_drv_driver_api *api = (const struct stepper_drv_driver_api *)dev->api; + + if (!VALID_MICRO_STEP_RES(res)) { + return -EINVAL; + } + return api->set_micro_step_res(dev, res); +} + +/** + * @brief Get the micro-step resolution in stepper driver + * + * @param dev pointer to the stepper_drv driver instance + * @param res micro-step resolution + * + * @retval -EIO General input / output error + * @retval 0 Success + */ +__syscall int stepper_drv_get_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution *res); + +static inline int z_impl_stepper_drv_get_micro_step_res(const struct device *dev, + enum stepper_drv_micro_step_resolution *res) +{ + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(res != NULL); + const struct stepper_drv_driver_api *api = (const struct stepper_drv_driver_api *)dev->api; + + return api->get_micro_step_res(dev, res); +} + +/** + * @brief Set the callback function to be called when a stepper_drv_event occurs + * + * @param dev pointer to the stepper_drv driver instance + * @param callback Callback function to be called when a stepper_drv_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_drv_set_event_cb(const struct device *dev, stepper_drv_event_cb_t callback, + void *user_data); + +static inline int z_impl_stepper_drv_set_event_cb(const struct device *dev, + stepper_drv_event_cb_t cb, void *user_data) +{ + __ASSERT_NO_MSG(dev != NULL); + const struct stepper_drv_driver_api *api = (const struct stepper_drv_driver_api *)dev->api; + + if (api->set_event_cb == NULL) { + return -ENOSYS; + } + + return api->set_event_cb(dev, cb, user_data); +} + /** * @} */ diff --git a/include/zephyr/drivers/stepper/stepper_fake.h b/include/zephyr/drivers/stepper/stepper_fake.h index 0dad709dc61ac..304066b75e733 100644 --- a/include/zephyr/drivers/stepper/stepper_fake.h +++ b/include/zephyr/drivers/stepper/stepper_fake.h @@ -14,19 +14,22 @@ extern "C" { #endif -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_enable, const struct device *); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_drv_enable, const struct device *); -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_disable, const struct device *); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_drv_disable, const struct device *); -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_move_by, const struct device *, int32_t); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_drv_set_micro_step_res, const struct device *, + enum stepper_drv_micro_step_resolution); -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_set_microstep_interval, const struct device *, uint64_t); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_drv_get_micro_step_res, const struct device *, + enum stepper_drv_micro_step_resolution *); -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_set_micro_step_res, const struct device *, - enum stepper_micro_step_resolution); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_drv_set_event_cb, const struct device *, + stepper_drv_event_cb_t, void *); -DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_get_micro_step_res, const struct device *, - enum stepper_micro_step_resolution *); +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_move_by, const struct device *, int32_t); + +DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_set_microstep_interval, const struct device *, uint64_t); DECLARE_FAKE_VALUE_FUNC(int, fake_stepper_set_reference_position, const struct device *, int32_t); diff --git a/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay index 9533bbb982e26..be55b93f422ad 100644 --- a/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay +++ b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay @@ -1,18 +1,30 @@ +/* + * Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + / { aliases { - stepper = &h_bridge_stepper; + stepper-drv = &tmc2209; + stepper = &step_dir_motion_control; }; -}; -/ { - h_bridge_stepper: h_bridge_stepper { - compatible = "zephyr,h-bridge-stepper"; - status = "okay"; - micro-step-res = <2>; + tmc2209: tmc2209 { + compatible = "adi,tmc2209"; en-gpios = <&gpioa 6 GPIO_ACTIVE_HIGH>; /* D12 */ - 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 */ + msx-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>, /* D10 */ + <&gpioa 7 GPIO_ACTIVE_HIGH>; /* D11 */ + micro-step-res = <32>; + }; + + step_dir_motion_control: step_dir_motion_control { + compatible = "zephyr,gpio-step-dir-controller"; + + step-gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>; /* D8 */ + dir-gpios = <&gpioc 7 GPIO_ACTIVE_HIGH>; /* D9 */ + invert-direction; + dual-edge-step; + step-width-ns = <1000>; }; }; diff --git a/samples/drivers/stepper/generic/src/main.c b/samples/drivers/stepper/generic/src/main.c index c044b88376c84..fea05d0190e96 100644 --- a/samples/drivers/stepper/generic/src/main.c +++ b/samples/drivers/stepper/generic/src/main.c @@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(stepper_generic, CONFIG_STEPPER_LOG_LEVEL); static const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); +static const struct device *stepper_drv = DEVICE_DT_GET(DT_ALIAS(stepper_drv)); enum stepper_mode { STEPPER_MODE_ENABLE, @@ -26,8 +27,8 @@ enum stepper_mode { static atomic_t stepper_mode = ATOMIC_INIT(STEPPER_MODE_DISABLE); -static int32_t ping_pong_target_position = - CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * DT_PROP(DT_ALIAS(stepper), micro_step_res); +static int32_t ping_pong_target_position = CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * + DT_PROP_OR(DT_ALIAS(stepper_drv), micro_step_res, 1); static K_SEM_DEFINE(stepper_generic_sem, 0, 1); @@ -79,7 +80,7 @@ int main(void) k_sem_take(&stepper_generic_sem, K_FOREVER); switch (atomic_get(&stepper_mode)) { case STEPPER_MODE_ENABLE: - stepper_enable(stepper); + stepper_drv_enable(stepper_drv); LOG_INF("mode: enable"); break; case STEPPER_MODE_PING_PONG_RELATIVE: @@ -105,7 +106,7 @@ int main(void) LOG_INF("mode: stop"); break; case STEPPER_MODE_DISABLE: - stepper_disable(stepper); + stepper_drv_disable(stepper_drv); LOG_INF("mode: disable"); break; default: diff --git a/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay b/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay index f30c659d44415..84c13aaff06ad 100644 --- a/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay +++ b/samples/drivers/stepper/tmc50xx/boards/nucleo_g071rb.overlay @@ -1,6 +1,13 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + / { aliases { - stepper = &tmc_stepper; + stepper-drv = &tmc_stepper_driver; + stepper = &tmc_motion_controller; }; }; @@ -14,25 +21,21 @@ reg = <0>; spi-max-frequency = ; /* Maximum SPI bus frequency */ - #address-cells = <1>; - #size-cells = <0>; - clock-frequency = ; /* Internal/External Clock frequency */ - tmc_stepper: tmc_stepper@0 { - status = "okay"; - reg = <0>; - - /* common stepper controller settings */ + /* common stepper controller settings */ + tmc_stepper_driver: stepper_driver { micro-step-res = <256>; - + idx = <0>; + compatible = "adi,tmc50xx-stepper-driver"; /* ADI TMC stallguard settings specific to TMC50XX */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <1000>; - stallguard2-threshold = <30>; - stallguard-threshold-velocity = <200000>; + stallguard2-threshold=<30>; + }; - /* ADI TMC ramp generator as well as current settings */ + /* ADI TMC ramp generator as well as current settings */ + tmc_motion_controller: motion_controller { + compatible = "adi,tmc50xx-motion-controller"; + idx = <0>; vstart = <1000>; vstop = <10>; a1 = <10000>; @@ -47,6 +50,9 @@ ihold = <1>; irun = <10>; iholddelay = <1>; + activate-stallguard2; + stallguard-velocity-check-interval-ms=<1000>; + stallguard-threshold-velocity=<200000>; }; }; }; diff --git a/samples/drivers/stepper/tmc50xx/src/main.c b/samples/drivers/stepper/tmc50xx/src/main.c index 996e96af9b739..aa8a01b32b2fe 100644 --- a/samples/drivers/stepper/tmc50xx/src/main.c +++ b/samples/drivers/stepper/tmc50xx/src/main.c @@ -12,15 +12,17 @@ #include LOG_MODULE_REGISTER(stepper_tmc50xx, CONFIG_STEPPER_LOG_LEVEL); -const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); +static const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); +static const struct device *stepper_drv = DEVICE_DT_GET(DT_ALIAS(stepper_drv)); int32_t ping_pong_target_position = CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * - DT_PROP(DT_ALIAS(stepper), micro_step_res); + DT_PROP(DT_ALIAS(stepper_drv), 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) { + ARG_UNUSED(user_data); switch (event) { case STEPPER_EVENT_STEPS_COMPLETED: k_sem_give(&steps_completed_sem); @@ -40,18 +42,36 @@ int main(void) LOG_DBG("stepper is %p, name is %s", stepper, stepper->name); stepper_set_event_callback(stepper, stepper_callback, NULL); - stepper_enable(stepper); + stepper_drv_enable(stepper); + + enum stepper_drv_micro_step_resolution micro_step_res; + + stepper_drv_get_micro_step_res(stepper_drv, µ_step_res); + LOG_DBG("Microstep resolution is %d", micro_step_res); + stepper_set_reference_position(stepper, 0); stepper_move_by(stepper, ping_pong_target_position); /* Change Max Velocity during runtime */ - int32_t tmc_velocity = DT_PROP(DT_ALIAS(stepper), vmax) * CONFIG_MAX_VELOCITY_MULTIPLIER; + int32_t tmc_velocity; + + tmc_velocity = DT_PROP(DT_ALIAS(stepper), vmax) * CONFIG_MAX_VELOCITY_MULTIPLIER; (void)tmc50xx_stepper_set_max_velocity(stepper, 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); + + int32_t actual_position; + int ret; + + ret = stepper_get_actual_position(stepper, &actual_position); + if (ret == 0) { + LOG_INF("Actual position: %d", actual_position); + } else { + LOG_ERR("Failed to get actual position"); + } } } return 0; diff --git a/tests/drivers/build_all/stepper/gpio.dtsi b/tests/drivers/build_all/stepper/gpio.dtsi index d0d7e79a9c686..5e32edf897665 100644 --- a/tests/drivers/build_all/stepper/gpio.dtsi +++ b/tests/drivers/build_all/stepper/gpio.dtsi @@ -15,9 +15,6 @@ adi_tmc2209: adi_tmc2209 { msx-gpios = <&test_gpio 0 0>, <&test_gpio 0 0>; en-gpios = <&test_gpio 0 0>; - step-gpios = <&test_gpio 0 0>; - dir-gpios = <&test_gpio 0 0>; - counter = <&counter0>; }; allegro_a4979: allegro_a4979 { @@ -25,34 +22,33 @@ allegro_a4979: allegro_a4979 { compatible = "allegro,a4979"; micro-step-res = <1>; reset-gpios = <&test_gpio 0 0>; - dir-gpios = <&test_gpio 0 0>; - step-gpios = <&test_gpio 0 0>; en-gpios = <&test_gpio 0 0>; m0-gpios = <&test_gpio 0 0>; m1-gpios = <&test_gpio 0 0>; - counter = <&counter0>; }; ti_drv84xx: ti_drv84xx { status = "okay"; compatible = "ti,drv84xx"; - - dir-gpios = <&test_gpio 0 0>; - step-gpios = <&test_gpio 0 0>; sleep-gpios = <&test_gpio 0 0>; en-gpios = <&test_gpio 0 0>; m0-gpios = <&test_gpio 0 0>; m1-gpios = <&test_gpio 0 0>; - counter = <&counter0>; +}; + +zephyr_gpio_step_dir_controller: zephyr_gpio_step_dir_controller { + compatible = "zephyr,gpio-step-dir-controller"; + step-gpios = <&test_gpio 0 0>; + dir-gpios = <&test_gpio 0 0>; + step-width-ns = <100>; }; zephyr_h_bridge_stepper: zephyr_h_bridge_stepper { compatible = "zephyr,h-bridge-stepper"; status = "okay"; - micro-step-res = <1>; - en-gpios = <&test_gpio 0 0>; + lut-step-gap = <1>; gpios = <&test_gpio 0 0>, - <&test_gpio 0 0>, - <&test_gpio 0 0>, - <&test_gpio 0 0>; + <&test_gpio 0 0>, + <&test_gpio 0 0>, + <&test_gpio 0 0>; }; diff --git a/tests/drivers/build_all/stepper/spi.dtsi b/tests/drivers/build_all/stepper/spi.dtsi index 400be1f84669f..0b4b47a92f6d0 100644 --- a/tests/drivers/build_all/stepper/spi.dtsi +++ b/tests/drivers/build_all/stepper/spi.dtsi @@ -4,8 +4,9 @@ */ /**************************************** * PLEASE KEEP REG ADDRESSES SEQUENTIAL * - ************************************** - */ + ***************************************/ + +#include adi_tmc50xx: adi_tmc50xx@0 { compatible = "adi,tmc50xx"; @@ -14,27 +15,57 @@ adi_tmc50xx: adi_tmc50xx@0 { spi-max-frequency = <8000000>; label = "tmc5041_0"; - #address-cells = <1>; - #size-cells = <0>; - - poscmp-enable; - test-mode; - lock-gconf; /* ADI TMC Global configuration flags */ + poscmp-enable; test-mode; lock-gconf; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ - tmc50xx_0: tmc50xx_0@0 { - status = "okay"; - reg = <0>; + /* common stepper controller settings */ + tmc50xx_0_stepper_driver: tmc50xx_0_stepper_driver { + idx = <0>; + compatible = "adi,tmc50xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC50XX */ + stallguard2-threshold=<30>; + }; + + /* ADI TMC ramp generator as well as current settings */ + tmc50xx_0_motion_controller: tmc50xx_0_motion_controller { + idx = <0>; + compatible = "adi,tmc50xx-motion-controller"; + vstart = <1000>; + vstop = <10>; + a1 = <10000>; + v1 = <50000>; + d1 = <14000>; + vmax = <900000>; + amax = <50000>; + dmax = <7000>; + tzerowait = <100>; + vhigh = <900000>; + vcoolthrs = <900000>; + ihold = <1>; + irun = <10>; + iholddelay = <1>; + activate-stallguard2; + stallguard-velocity-check-interval-ms=<1000>; + stallguard-threshold-velocity=<200000>; + }; - /* common stepper controller settings */ - invert-direction; + /* common stepper controller settings */ + tmc50xx_1_stepper_driver: tmc50xx_1_stepper_driver { + idx = <1>; + compatible = "adi,tmc50xx-stepper-driver"; micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC50XX */ + stallguard2-threshold=<9>; + }; + tmc50xx_1_motion_controller: tmc50xx_1_motion_controller { + idx = <1>; /* ADI TMC stallguard settings specific to TMC50XX */ + compatible = "adi,tmc50xx-motion-controller"; activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <500000>; + stallguard-velocity-check-interval-ms=<100>; + stallguard-threshold-velocity=<500000>; /* ADI TMC ramp generator as well as current settings */ vstart = <10>; @@ -51,20 +82,32 @@ adi_tmc50xx: adi_tmc50xx@0 { irun = <2>; iholddelay = <3>; }; +}; + +adi_tmc51xx_1: adi_tmc51xx@1 { + compatible = "adi,tmc51xx"; + status = "okay"; + reg = <0x01>; + spi-max-frequency = <8000000>; + label = "tmc5160_1"; - tmc50xx_1: tmc50xx_1@1 { - status = "okay"; - reg = <1>; + en-pwm-mode; test-mode; shaft; /* ADI TMC Global configuration flags */ + clock-frequency = <16000000>; /* Internal/External Clock frequency */ - /* common stepper controller settings */ - invert-direction; + tmc51xx_1_stepper_driver: tmc51xx_1_stepper_driver { + compatible = "adi,tmc51xx-stepper-driver"; micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC51XX */ + stallguard2-threshold=<9>; + }; + /* common stepper controller settings */ + tmc51xx_1_motion_controller: tmc51xx_1_motion_controller { + compatible = "adi,tmc51xx-motion-controller"; - /* ADI TMC stallguard settings specific to TMC50XX */ + /* ADI TMC stallguard settings specific to TMC5160 */ activate-stallguard2; stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <500000>; + stallguard-threshold-velocity = <50000>; /* ADI TMC ramp generator as well as current settings */ vstart = <10>; @@ -75,56 +118,16 @@ adi_tmc50xx: adi_tmc50xx@0 { amax = <60>; dmax = <70>; tzerowait = <80>; - vhigh = <90>; - vcoolthrs = <100>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; ihold = <1>; irun = <2>; iholddelay = <3>; }; }; -adi_tmc51xx_1: adi_tmc51xx@1 { - compatible = "adi,tmc51xx"; - status = "okay"; - reg = <0x01>; - spi-max-frequency = <8000000>; - label = "tmc5160_1"; - - #address-cells = <1>; - #size-cells = <0>; - - en-pwm-mode; - test-mode; /* ADI TMC Global configuration flags */ - clock-frequency = <16000000>; /* Internal/External Clock frequency */ - - /* common stepper controller settings */ - invert-direction; - micro-step-res = <256>; - - /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; - - /* 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>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; -}; - adi_tmc51xx_2: adi_tmc51xx@2 { compatible = "adi,tmc51xx"; status = "okay"; @@ -132,38 +135,41 @@ adi_tmc51xx_2: adi_tmc51xx@2 { spi-max-frequency = <8000000>; label = "tmc5160_2"; - #address-cells = <1>; - #size-cells = <0>; diag0-gpios = <&test_gpio 0x01 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* DIAG pin @0x01 */ - en-pwm-mode; - test-mode; /* ADI TMC Global configuration flags */ + en-pwm-mode; test-mode; shaft; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ + tmc51xx_2_stepper_driver: tmc51xx_2_stepper_driver { + compatible = "adi,tmc51xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC51XX */ + stallguard2-threshold=<9>; + }; /* common stepper controller settings */ - invert-direction; - micro-step-res = <256>; + tmc51xx_2_motion_controller: tmc51xx_2_motion_controller { + compatible = "adi,tmc51xx-motion-controller"; - /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard-threshold-velocity = <50000>; - /* 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>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; + /* 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>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; diff --git a/tests/drivers/build_all/stepper/testcase.yaml b/tests/drivers/build_all/stepper/testcase.yaml index c6be5f3bac8a6..2ae2cd9d33398 100644 --- a/tests/drivers/build_all/stepper/testcase.yaml +++ b/tests/drivers/build_all/stepper/testcase.yaml @@ -9,3 +9,4 @@ tests: build_only: true platform_allow: - native_sim + - native_sim/native/64 diff --git a/tests/drivers/build_all/stepper/uart.dtsi b/tests/drivers/build_all/stepper/uart.dtsi index 35480e08f61f7..ea8630a28405f 100644 --- a/tests/drivers/build_all/stepper/uart.dtsi +++ b/tests/drivers/build_all/stepper/uart.dtsi @@ -17,30 +17,37 @@ adi_tmc51xx_uart: adi_tmc51xx { test-mode; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ + + tmc51xx_stepper_driver: stepper_driver { + compatible = "adi,tmc51xx-stepper-driver"; + micro-step-res = <256>; + /* ADI TMC stallguard settings specific to TMC51XX */ + stallguard2-threshold=<9>; + }; /* common stepper controller settings */ - invert-direction; - micro-step-res = <256>; + tmc51xx_motion_controller: motion_controller { + compatible = "adi,tmc51xx-motion-controller"; - /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard-threshold-velocity = <50000>; - /* 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>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; + /* 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>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; diff --git a/tests/drivers/stepper/drv84xx/app.overlay b/tests/drivers/stepper/drv84xx/app.overlay index 2eb3f8e5b335d..29d49f13b9fa9 100644 --- a/tests/drivers/stepper/drv84xx/app.overlay +++ b/tests/drivers/stepper/drv84xx/app.overlay @@ -11,13 +11,10 @@ status = "okay"; compatible = "ti,drv84xx"; - dir-gpios = <&gpio1 0 0>; /* D3 */ - step-gpios = <&gpio1 1 0>; /* D4 */ sleep-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; /* D2 */ en-gpios = <&gpio2 1 0>; /* 5 */ m0-gpios = <&gpio3 0 0>; m1-gpios = <&gpio4 1 0>; - counter = <&counter0>; #address-cells = <1>; #size-cells = <0>; diff --git a/tests/drivers/stepper/drv84xx/src/main.c b/tests/drivers/stepper/drv84xx/src/main.c index f2ca77b0e2cf5..d199fc5dc6ea7 100644 --- a/tests/drivers/stepper/drv84xx/src/main.c +++ b/tests/drivers/stepper/drv84xx/src/main.c @@ -37,7 +37,7 @@ ZTEST_F(drv84xx_emul, test_enable_gpio_pins) int value = 0; int err; - err = stepper_enable(fixture->dev); + err = stepper_drv_enable(fixture->dev); if (err == -ENOTSUP) { ztest_test_skip(); } @@ -52,7 +52,7 @@ ZTEST_F(drv84xx_emul, test_enable_gpio_pins) } /* As enable is supported, disable must also be supported */ - zassert_ok(stepper_disable(fixture->dev)); + zassert_ok(stepper_drv_disable(fixture->dev)); if (en_pin.port != NULL) { value = gpio_emul_output_get(en_pin.port, en_pin.pin); @@ -66,10 +66,10 @@ ZTEST_F(drv84xx_emul, test_enable_gpio_pins) ZTEST_F(drv84xx_emul, test_micro_step_res_set) { - enum stepper_micro_step_resolution res; + enum stepper_drv_micro_step_resolution res; int value = 0; - zassert_ok(stepper_set_micro_step_res(fixture->dev, 4)); + zassert_ok(stepper_drv_set_micro_step_res(fixture->dev, 4)); if (m0_pin.port == NULL || m1_pin.port == NULL) { ztest_test_skip(); @@ -81,7 +81,7 @@ ZTEST_F(drv84xx_emul, test_micro_step_res_set) value = gpio_emul_output_get(m1_pin.port, m1_pin.pin); zassert_equal(value, 1, "M1 pin should be 1"); - zassert_ok(stepper_get_micro_step_res(fixture->dev, &res)); + zassert_ok(stepper_drv_get_micro_step_res(fixture->dev, &res)); zassert_equal(res, 4, "Micro step resolution not set correctly, should be %d but is %d", 4, res); } diff --git a/tests/drivers/stepper/shell/app.overlay b/tests/drivers/stepper/shell/app.overlay index f6c262f129eae..5ce6423ae233f 100644 --- a/tests/drivers/stepper/shell/app.overlay +++ b/tests/drivers/stepper/shell/app.overlay @@ -5,8 +5,13 @@ */ / { - fake_stepper: fake_stepper { - compatible = "zephyr,fake-stepper"; + fake_stepper_driver: fake_stepper_driver { + compatible = "zephyr,fake-stepper-driver"; + status = "okay"; + }; + + fake_stepper_controller: fake_stepper_controller { + compatible = "zephyr,fake-stepper-controller"; status = "okay"; }; }; diff --git a/tests/drivers/stepper/shell/src/main.c b/tests/drivers/stepper/shell/src/main.c index c4c9c5796272e..7aa966d276002 100644 --- a/tests/drivers/stepper/shell/src/main.c +++ b/tests/drivers/stepper/shell/src/main.c @@ -14,18 +14,22 @@ #include #include -#define FAKE_STEPPER_NAME DEVICE_DT_NAME(DT_NODELABEL(fake_stepper)) +#define FAKE_STEPPER_CONTROLLER DEVICE_DT_NAME(DT_NODELABEL(fake_stepper_controller)) +#define FAKE_STEPPER_DRIVER DEVICE_DT_NAME(DT_NODELABEL(fake_stepper_driver)) /* Global variables */ -static const struct device *const fake_stepper_dev = DEVICE_DT_GET(DT_NODELABEL(fake_stepper)); +static const struct device *const fake_stepper_driver_dev = + DEVICE_DT_GET(DT_NODELABEL(fake_stepper_driver)); +static const struct device *const fake_stepper_controller_dev = + DEVICE_DT_GET(DT_NODELABEL(fake_stepper_controller)); DEFINE_FFF_GLOBALS; -#define ASSERT_STEPPER_FUNC_CALLED(stepper_fake_func, retval) \ +#define ASSERT_STEPPER_FUNC_CALLED(stepper_fake_func, stepper_fake_dev, retval) \ zassert_ok(retval, "failed to execute shell command (err %d)", retval); \ zassert_equal(stepper_fake_func.call_count, 1, \ STRINGIFY(stepper_fake_func) " function not called"); \ - zassert_equal(stepper_fake_func.arg0_val, fake_stepper_dev, "wrong device pointer") + zassert_equal(stepper_fake_func.arg0_val, stepper_fake_dev, "wrong device pointer") static void *stepper_shell_setup(void) { @@ -40,75 +44,81 @@ static void *stepper_shell_setup(void) ZTEST_SUITE(stepper_shell, NULL, stepper_shell_setup, NULL, NULL, NULL); -ZTEST(stepper_shell, test_stepper_enable) +ZTEST(stepper_shell, test_stepper_drv_enable) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper enable " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper enable " FAKE_STEPPER_DRIVER); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_enable_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_drv_enable_fake, fake_stepper_driver_dev, err); zassert_equal(err, 0, "stepper enable could not be executed"); } -ZTEST(stepper_shell, test_stepper_disable) +ZTEST(stepper_shell, test_stepper_drv_disable) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper disable " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper disable " FAKE_STEPPER_DRIVER); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_disable_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_drv_disable_fake, fake_stepper_driver_dev, err); zassert_equal(err, 0, "stepper disable could not be executed"); } ZTEST(stepper_shell, test_stepper_move_by) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper move_by " FAKE_STEPPER_NAME " 1000"); + int err = shell_execute_cmd(sh, "stepper move_by " FAKE_STEPPER_CONTROLLER " 1000"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_move_by_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_move_by_fake, fake_stepper_controller_dev, err); zassert_equal(fake_stepper_move_by_fake.arg1_val, 1000, "wrong microsteps value"); } ZTEST(stepper_shell, test_stepper_set_microstep_interval) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper set_microstep_interval " FAKE_STEPPER_NAME " 200"); + int err = shell_execute_cmd(sh, "stepper set_microstep_interval " FAKE_STEPPER_CONTROLLER + " 200"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_set_microstep_interval_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_set_microstep_interval_fake, + fake_stepper_controller_dev, err); zassert_equal(fake_stepper_set_microstep_interval_fake.arg1_val, 200, "wrong step_interval value"); } -ZTEST(stepper_shell, test_stepper_set_micro_step_res) +ZTEST(stepper_shell, test_stepper_drv_set_micro_step_res) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper set_micro_step_res " FAKE_STEPPER_NAME " 64"); + int err = shell_execute_cmd(sh, "stepper set_micro_step_res " FAKE_STEPPER_DRIVER " 64"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_set_micro_step_res_fake, err); - zassert_equal(fake_stepper_set_micro_step_res_fake.arg1_val, 64, + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_drv_set_micro_step_res_fake, + fake_stepper_driver_dev, err); + zassert_equal(fake_stepper_drv_set_micro_step_res_fake.arg1_val, 64, "wrong micro steps resolution value"); } -ZTEST(stepper_shell, test_stepper_set_micro_step_res_invalid_value) +ZTEST(stepper_shell, test_stepper_drv_set_micro_step_res_invalid_value) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper set_micro_step_res " FAKE_STEPPER_NAME " 111"); + int err = shell_execute_cmd(sh, "stepper set_micro_step_res " FAKE_STEPPER_DRIVER " 111"); zassert_not_equal(err, 0, " executed set_micro_step_res with invalid micro steps value"); } -ZTEST(stepper_shell, test_stepper_get_micro_step_res) +ZTEST(stepper_shell, test_stepper_drv_get_micro_step_res) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper get_micro_step_res " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper get_micro_step_res " FAKE_STEPPER_DRIVER); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_get_micro_step_res_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_drv_get_micro_step_res_fake, + fake_stepper_driver_dev, err); } ZTEST(stepper_shell, test_stepper_set_reference_position) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper set_reference_position " FAKE_STEPPER_NAME " 100"); + int err = shell_execute_cmd(sh, "stepper set_reference_position " FAKE_STEPPER_CONTROLLER + " 100"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_set_reference_position_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_set_reference_position_fake, + fake_stepper_controller_dev, err); zassert_equal(fake_stepper_set_reference_position_fake.arg1_val, 100, "wrong actual position value"); } @@ -116,26 +126,27 @@ ZTEST(stepper_shell, test_stepper_set_reference_position) ZTEST(stepper_shell, test_stepper_get_actual_position) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper get_actual_position " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper get_actual_position " FAKE_STEPPER_CONTROLLER); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_get_actual_position_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_get_actual_position_fake, + fake_stepper_controller_dev, err); } ZTEST(stepper_shell, test_stepper_move_to) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper move_to " FAKE_STEPPER_NAME " 200"); + int err = shell_execute_cmd(sh, "stepper move_to " FAKE_STEPPER_CONTROLLER " 200"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_move_to_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_move_to_fake, fake_stepper_controller_dev, err); zassert_equal(fake_stepper_move_to_fake.arg1_val, 200, "wrong target position value"); } ZTEST(stepper_shell, test_stepper_run) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper run " FAKE_STEPPER_NAME " positive"); + int err = shell_execute_cmd(sh, "stepper run " FAKE_STEPPER_CONTROLLER " positive"); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_run_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_run_fake, fake_stepper_controller_dev, err); zassert_equal(fake_stepper_run_fake.arg1_val, STEPPER_DIRECTION_POSITIVE, "wrong direction value"); } @@ -143,7 +154,7 @@ ZTEST(stepper_shell, test_stepper_run) ZTEST(stepper_shell, test_stepper_run_invalid_direction) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper run " FAKE_STEPPER_NAME " foo"); + int err = shell_execute_cmd(sh, "stepper run " FAKE_STEPPER_CONTROLLER " foo"); zassert_not_equal(err, 0, " executed run with invalid direction value"); } @@ -151,22 +162,31 @@ ZTEST(stepper_shell, test_stepper_run_invalid_direction) ZTEST(stepper_shell, test_stepper_stop) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper stop " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper stop " FAKE_STEPPER_CONTROLLER); - ASSERT_STEPPER_FUNC_CALLED(fake_stepper_stop_fake, err); + ASSERT_STEPPER_FUNC_CALLED(fake_stepper_stop_fake, fake_stepper_controller_dev, err); zassert_equal(err, 0, "stepper stop could not be executed"); } -ZTEST(stepper_shell, test_stepper_info) +ZTEST(stepper_shell, test_stepper_controller_info) { const struct shell *sh = shell_backend_dummy_get_ptr(); - int err = shell_execute_cmd(sh, "stepper info " FAKE_STEPPER_NAME); + int err = shell_execute_cmd(sh, "stepper control_info " FAKE_STEPPER_CONTROLLER); zassert_ok(err, "failed to execute shell command (err %d)", err); zassert_equal(fake_stepper_is_moving_fake.call_count, 1, "is_moving function not called"); zassert_equal(fake_stepper_get_actual_position_fake.call_count, 1, "get_actual_position function not called"); - zassert_equal(fake_stepper_get_micro_step_res_fake.call_count, 1, +} + +ZTEST(stepper_shell, test_stepper_info) +{ + const struct shell *sh = shell_backend_dummy_get_ptr(); + int err = shell_execute_cmd(sh, "stepper info " FAKE_STEPPER_DRIVER); + + zassert_ok(err, "failed to execute shell command (err %d)", err); + + zassert_equal(fake_stepper_drv_get_micro_step_res_fake.call_count, 1, "get_micro_step_res function not called"); } diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209.overlay deleted file mode 100644 index 1aa221c3897d1..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209.overlay +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &adi_tmc2209; - }; -}; - -/ { - adi_tmc2209: adi_tmc2209 { - status = "okay"; - compatible = "adi,tmc2209"; - micro-step-res = <32>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - en-gpios = <&gpio2 1 0>; - msx-gpios = <&gpio3 0 0>, <&gpio4 1 0>; - counter = <&counter0>; - dual-edge-step; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209_work_q.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209_work_q.overlay deleted file mode 100644 index 39ba159a400e4..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_adi_tmc2209_work_q.overlay +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2025 Josselin Bunt - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &adi_tmc2209; - }; -}; - -/ { - adi_tmc2209: adi_tmc2209 { - status = "okay"; - compatible = "adi,tmc2209"; - micro-step-res = <32>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - en-gpios = <&gpio2 1 0>; - msx-gpios = <&gpio3 0 0>, <&gpio4 1 0>; - dual-edge-step; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979.overlay deleted file mode 100644 index 897d95933c8fb..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979.overlay +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &allegro_a4979; - }; -}; - -/ { - allegro_a4979: allegro_a4979 { - status = "okay"; - compatible = "allegro,a4979"; - micro-step-res = <1>; - reset-gpios = <&gpio4 0 0>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - en-gpios = <&gpio2 1 0>; - m0-gpios = <&gpio3 0 0>; - m1-gpios = <&gpio3 1 0>; - counter = <&counter0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979_work_q.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979_work_q.overlay deleted file mode 100644 index 85404efc18ace..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_allegro_a4979_work_q.overlay +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2025 Josselin Bunt - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &allegro_a4979; - }; -}; - -/ { - allegro_a4979: allegro_a4979 { - status = "okay"; - compatible = "allegro,a4979"; - micro-step-res = <1>; - reset-gpios = <&gpio4 0 0>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - en-gpios = <&gpio2 1 0>; - m0-gpios = <&gpio3 0 0>; - m1-gpios = <&gpio3 1 0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir.overlay new file mode 100644 index 0000000000000..65238d299b066 --- /dev/null +++ b/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir.overlay @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "native_sim.overlay" + +/ { + aliases { + stepper = &gpio_step_dir_controller; + }; + + gpio_step_dir_controller: gpio_step_dir_controller { + compatible = "zephyr,gpio-step-dir-controller"; + step-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; + dir-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>; + step-width-ns = <100>; + counter = <&counter0>; + }; +}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir_workq.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir_workq.overlay new file mode 100644 index 0000000000000..0c4bbae922b96 --- /dev/null +++ b/tests/drivers/stepper/stepper_api/boards/native_sim_gpio_step_dir_workq.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "native_sim.overlay" + +/ { + aliases { + stepper = &gpio_step_dir_controller; + }; + + gpio_step_dir_controller: gpio_step_dir_controller { + compatible = "zephyr,gpio-step-dir-controller"; + step-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; + dir-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>; + step-width-ns = <100>; + }; +}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper.overlay new file mode 100644 index 0000000000000..3f756e58312f9 --- /dev/null +++ b/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper.overlay @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 Jilay Sandeep Pandya + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "native_sim.overlay" + +/ { + aliases { + stepper = &h_bridge_stepper; + }; + + h_bridge_stepper: h_bridge_stepper { + compatible = "zephyr,h-bridge-stepper"; + status = "okay"; + lut-step-gap = <1>; + gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>, + <&gpio2 0 GPIO_ACTIVE_HIGH>, + <&gpio3 0 GPIO_ACTIVE_HIGH>, + <&gpio4 0 GPIO_ACTIVE_HIGH>; + counter = <&counter0>; + }; +}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper_workq.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper_workq.overlay new file mode 100644 index 0000000000000..9c589039dba69 --- /dev/null +++ b/tests/drivers/stepper/stepper_api/boards/native_sim_h_bridge_stepper_workq.overlay @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "native_sim.overlay" + +/ { + aliases { + stepper = &h_bridge_stepper; + }; + + h_bridge_stepper: h_bridge_stepper { + compatible = "zephyr,h-bridge-stepper"; + status = "okay"; + lut-step-gap = <1>; + gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>, + <&gpio2 0 GPIO_ACTIVE_HIGH>, + <&gpio3 0 GPIO_ACTIVE_HIGH>, + <&gpio4 0 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx.overlay deleted file mode 100644 index 22b09bc1edf53..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx.overlay +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &ti_drv84xx; - }; -}; - -/ { - ti_drv84xx: ti_drv84xx { - status = "okay"; - compatible = "ti,drv84xx"; - micro-step-res = <8>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - sleep-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; - en-gpios = <&gpio2 1 0>; - m0-gpios = <&gpio3 0 0>; - m1-gpios = <&gpio3 1 0>; - counter = <&counter0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx_work_q.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx_work_q.overlay deleted file mode 100644 index b9545a12739a9..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_ti_drv84xx_work_q.overlay +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2025 Josselin Bunt - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &ti_drv84xx; - }; -}; - -/ { - ti_drv84xx: ti_drv84xx { - status = "okay"; - compatible = "ti,drv84xx"; - micro-step-res = <8>; - dir-gpios = <&gpio1 0 0>; - step-gpios = <&gpio1 1 0>; - sleep-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; - en-gpios = <&gpio2 1 0>; - m0-gpios = <&gpio3 0 0>; - m1-gpios = <&gpio3 1 0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/native_sim_zephyr_h_bridge_stepper.overlay b/tests/drivers/stepper/stepper_api/boards/native_sim_zephyr_h_bridge_stepper.overlay deleted file mode 100644 index 11bfb3d560244..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/native_sim_zephyr_h_bridge_stepper.overlay +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "native_sim.overlay" - -/ { - aliases { - stepper = &zephyr_h_bridge_stepper; - }; -}; - -/ { - zephyr_h_bridge_stepper: zephyr_h_bridge_stepper { - compatible = "zephyr,h-bridge-stepper"; - status = "okay"; - micro-step-res = <1>; - en-gpios = <&gpio1 0 0>; - gpios = <&gpio1 0 0>, - <&gpio2 0 0>, - <&gpio3 0 0>, - <&gpio4 0 0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/boards/qemu_x86_64_zephyr_h_bridge_stepper.overlay b/tests/drivers/stepper/stepper_api/boards/qemu_x86_64_zephyr_h_bridge_stepper.overlay deleted file mode 100644 index e11ea8dddc302..0000000000000 --- a/tests/drivers/stepper/stepper_api/boards/qemu_x86_64_zephyr_h_bridge_stepper.overlay +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -/ { - aliases { - stepper = &zephyr_h_bridge_stepper; - }; -}; - -/ { - gpio1: gpio1 { - compatible = "zephyr,gpio-emul"; - #gpio-cells = <0x2>; - status = "okay"; - gpio-controller; - }; - - gpio2: gpio2 { - compatible = "zephyr,gpio-emul"; - #gpio-cells = <0x2>; - status = "okay"; - gpio-controller; - }; - - gpio3: gpio3 { - compatible = "zephyr,gpio-emul"; - #gpio-cells = <0x2>; - status = "okay"; - gpio-controller; - }; - - gpio4: gpio4 { - compatible = "zephyr,gpio-emul"; - #gpio-cells = <0x2>; - status = "okay"; - gpio-controller; - }; -}; - -/ { - zephyr_h_bridge_stepper: zephyr_h_bridge_stepper { - compatible = "zephyr,h-bridge-stepper"; - status = "okay"; - micro-step-res = <2>; - gpios = <&gpio1 0 0>, - <&gpio2 0 0>, - <&gpio3 0 0>, - <&gpio4 0 0>; - }; -}; diff --git a/tests/drivers/stepper/stepper_api/src/main.c b/tests/drivers/stepper/stepper_api/src/main.c index 826f0876ef538..55edbb18feec2 100644 --- a/tests/drivers/stepper/stepper_api/src/main.c +++ b/tests/drivers/stepper/stepper_api/src/main.c @@ -46,9 +46,6 @@ static void stepper_print_event_callback(const struct device *dev, enum stepper_ case STEPPER_EVENT_RIGHT_END_STOP_DETECTED: k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_RIGHT_END_STOP_DETECTED); break; - case STEPPER_EVENT_STALL_DETECTED: - k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STALL_DETECTED); - break; case STEPPER_EVENT_STOPPED: k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STOPPED); break; @@ -75,7 +72,6 @@ static void *stepper_setup(void) zassert_equal( stepper_set_event_callback(fixture.dev, fixture.callback, (void *)fixture.dev), 0, "Failed to set event callback"); - (void)stepper_enable(fixture.dev); return &fixture; } @@ -91,21 +87,6 @@ static void stepper_before(void *f) ZTEST_SUITE(stepper, NULL, stepper_setup, stepper_before, NULL, NULL); -ZTEST_F(stepper, test_set_micro_step_res_invalid) -{ - int ret = stepper_set_micro_step_res(fixture->dev, 127); - - zassert_equal(ret, -EINVAL, "Invalid micro step resolution should return -EINVAL"); -} - -ZTEST_F(stepper, test_get_micro_step_res) -{ - enum stepper_micro_step_resolution res; - (void)stepper_get_micro_step_res(fixture->dev, &res); - zassert_equal(res, DT_PROP(DT_ALIAS(stepper), micro_step_res), - "Micro step resolution not set correctly"); -} - ZTEST_F(stepper, test_set_micro_step_interval_invalid_zero) { int err = stepper_set_microstep_interval(fixture->dev, 0); diff --git a/tests/drivers/stepper/stepper_api/testcase.yaml b/tests/drivers/stepper/stepper_api/testcase.yaml index ceae1261d50e7..fe6e7ade434b8 100644 --- a/tests/drivers/stepper/stepper_api/testcase.yaml +++ b/tests/drivers/stepper/stepper_api/testcase.yaml @@ -7,66 +7,37 @@ common: - stepper - api tests: - drivers.stepper.stepper_api.adi_tmc2209: + drivers.stepper.stepper_api.gpio_step_dir: extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_adi_tmc2209.overlay" + - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_gpio_step_dir.overlay" extra_configs: - CONFIG_GPIO=y - CONFIG_COUNTER=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y + - CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS=y platform_allow: - native_sim/native/64 - drivers.stepper.stepper_api.adi_tmc2209_work_q: + drivers.stepper.stepper_api.gpio_step_dir_work_q: extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_adi_tmc2209_work_q.overlay" + - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_gpio_step_dir_workq.overlay" extra_configs: - CONFIG_GPIO=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y - CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT=130 platform_allow: - native_sim/native/64 - drivers.stepper.stepper_api.allegro_a4979: + drivers.stepper.stepper_api.h_bridge_stepper: extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_allegro_a4979.overlay" + - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_h_bridge_stepper.overlay" extra_configs: - CONFIG_GPIO=y - CONFIG_COUNTER=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y + - CONFIG_STEPPER_GPIO_STEPPER_GENERATE_ISR_SAFE_EVENTS=y platform_allow: - native_sim/native/64 - drivers.stepper.stepper_api.allegro_a4979_work_q: + drivers.stepper.stepper_api.h_bridge_stepper_work_q: extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_allegro_a4979_work_q.overlay" + - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_h_bridge_stepper_workq.overlay" extra_configs: - CONFIG_GPIO=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y - CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT=130 platform_allow: - native_sim/native/64 - drivers.stepper.stepper_api.ti_drv84xx: - extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_ti_drv84xx.overlay" - extra_configs: - - CONFIG_GPIO=y - - CONFIG_COUNTER=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y - platform_allow: - - native_sim/native/64 - drivers.stepper.stepper_api.ti_drv84xx_work_q: - extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_ti_drv84xx_work_q.overlay" - extra_configs: - - CONFIG_GPIO=y - - CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS=y - - CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT=130 - platform_allow: - - native_sim/native/64 - drivers.stepper.stepper_api.zephyr_gpio_stepper: - extra_args: - - platform:native_sim/native/64:DTC_OVERLAY_FILE="boards/native_sim_zephyr_h_bridge_stepper.overlay" - - platform:qemu_x86_64/atom:DTC_OVERLAY_FILE="boards/qemu_x86_64_zephyr_h_bridge_stepper.overlay" - extra_configs: - - CONFIG_GPIO=y - platform_allow: - - native_sim/native/64 - - qemu_x86_64/atom