diff --git a/drivers/stepper/CMakeLists.txt b/drivers/stepper/CMakeLists.txt index 55a9b28c345..7f135001497 100644 --- a/drivers/stepper/CMakeLists.txt +++ b/drivers/stepper/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h) # zephyr-keep-sorted-start add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC adi_tmc) add_subdirectory_ifdef(CONFIG_STEPPER_TI ti) +add_subdirectory_ifdef(CONFIG_STEP_DIR_STEPPER step_dir) # zephyr-keep-sorted-stop zephyr_library() @@ -13,5 +14,4 @@ zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_FAKE_STEPPER fake_stepper_controller.c) zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_controller.c) -zephyr_library_sources_ifdef(CONFIG_STEP_DIR_STEPPER step_dir_stepper_common.c) zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c) diff --git a/drivers/stepper/Kconfig b/drivers/stepper/Kconfig index 387517a249a..f8dd4a43b1b 100644 --- a/drivers/stepper/Kconfig +++ b/drivers/stepper/Kconfig @@ -24,10 +24,9 @@ config STEPPER_SHELL help Enable stepper shell for testing. -config STEP_DIR_STEPPER - bool - help - Enable library used for step direction stepper drivers. +comment "Stepper Driver Common" + +rsource "step_dir/Kconfig" comment "Stepper Drivers" diff --git a/drivers/stepper/Kconfig.stepper_event_template b/drivers/stepper/Kconfig.stepper_event_template new file mode 100644 index 00000000000..fb29a1e91e0 --- /dev/null +++ b/drivers/stepper/Kconfig.stepper_event_template @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Fabian Blatz +# SPDX-License-Identifier: Apache-2.0 + +config STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS + bool "$(module-str) guarantee non ISR callbacks upon stepper events" + help + Enable the dispatch of stepper generated events via + a message queue to guarantee that the event handler + code is not run inside of an ISR. Can be disabled, but + then registered stepper event callback must be ISR safe. + +config STEPPER_$(module)_EVENT_QUEUE_LEN + int "$(module-str) maximum number of pending stepper events" + default 4 + depends on STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS + help + The maximum number of stepper events that can be pending before new events + are dropped. diff --git a/drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c b/drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c index 15e4119b66c..f93cb6efd18 100644 --- a/drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c +++ b/drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "../step_dir_stepper_common.h" +#include "../step_dir/step_dir_stepper_common.h" #include LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL); diff --git a/drivers/stepper/step_dir/CMakeLists.txt b/drivers/stepper/step_dir/CMakeLists.txt new file mode 100644 index 00000000000..7daa4cc04d5 --- /dev/null +++ b/drivers/stepper/step_dir/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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 new file mode 100644 index 00000000000..d8629c649ff --- /dev/null +++ b/drivers/stepper/step_dir/Kconfig @@ -0,0 +1,22 @@ +# 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_stepper_common.c b/drivers/stepper/step_dir/step_dir_stepper_common.c similarity index 61% rename from drivers/stepper/step_dir_stepper_common.c rename to drivers/stepper/step_dir/step_dir_stepper_common.c index 22ef6eef6f4..9955b140867 100644 --- a/drivers/stepper/step_dir_stepper_common.c +++ b/drivers/stepper/step_dir/step_dir_stepper_common.c @@ -47,20 +47,70 @@ static inline int step_dir_stepper_perform_step(const struct device *dev) return 0; } +static 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 void update_remaining_steps(struct step_dir_stepper_common_data *data) { if (data->step_count > 0) { data->step_count--; - (void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us)); } else if (data->step_count < 0) { data->step_count++; - (void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us)); } else { - if (!data->callback) { - LOG_WRN_ONCE("No callback set"); - return; - } - data->callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED, data->event_cb_user_data); + stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED); } } @@ -80,34 +130,41 @@ static void update_direction_from_step_count(const struct device *dev) 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; if (data->step_count) { (void)step_dir_stepper_perform_step(dev); } + update_remaining_steps(dev->data); + + if (config->timing_source->needs_reschedule(dev) && data->step_count != 0) { + (void)config->timing_source->start(dev); + } } static void velocity_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; (void)step_dir_stepper_perform_step(dev); - (void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us)); + + if (config->timing_source->needs_reschedule(dev)) { + (void)config->timing_source->start(dev); + } } -static void stepper_work_step_handler(struct k_work *work) +void stepper_handle_timing_signal(const struct device *dev) { - 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 step_dir_stepper_common_data *data = dev->data; K_SPINLOCK(&data->lock) { switch (data->run_mode) { case STEPPER_RUN_MODE_POSITION: - position_mode_task(data->dev); + position_mode_task(dev); break; case STEPPER_RUN_MODE_VELOCITY: - velocity_mode_task(data->dev); + velocity_mode_task(dev); break; default: LOG_WRN("Unsupported run mode: %d", data->run_mode); @@ -119,7 +176,6 @@ static void stepper_work_step_handler(struct k_work *work) int step_dir_stepper_common_init(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; if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) { @@ -139,7 +195,21 @@ int step_dir_stepper_common_init(const struct device *dev) return ret; } - k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); + 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; } @@ -147,8 +217,9 @@ int step_dir_stepper_common_init(const struct device *dev) int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micro_steps) { struct step_dir_stepper_common_data *data = dev->data; + const struct step_dir_stepper_common_config *config = dev->config; - if (data->delay_in_us == 0) { + if (data->max_velocity == 0) { LOG_ERR("Velocity not set or invalid velocity set"); return -EINVAL; } @@ -156,8 +227,9 @@ int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micr K_SPINLOCK(&data->lock) { data->run_mode = STEPPER_RUN_MODE_POSITION; data->step_count = micro_steps; + config->timing_source->update(dev, data->max_velocity); update_direction_from_step_count(dev); - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); + config->timing_source->start(dev); } return 0; @@ -166,6 +238,7 @@ int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micr int step_dir_stepper_common_set_max_velocity(const struct device *dev, const uint32_t velocity) { struct step_dir_stepper_common_data *data = dev->data; + const struct step_dir_stepper_common_config *config = dev->config; if (velocity == 0) { LOG_ERR("Velocity cannot be zero"); @@ -178,7 +251,8 @@ int step_dir_stepper_common_set_max_velocity(const struct device *dev, const uin } K_SPINLOCK(&data->lock) { - data->delay_in_us = USEC_PER_SEC / velocity; + data->max_velocity = velocity; + config->timing_source->update(dev, velocity); } return 0; @@ -209,8 +283,9 @@ int step_dir_stepper_common_get_actual_position(const struct device *dev, int32_ int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value) { struct step_dir_stepper_common_data *data = dev->data; + const struct step_dir_stepper_common_config *config = dev->config; - if (data->delay_in_us == 0) { + if (data->max_velocity == 0) { LOG_ERR("Velocity not set or invalid velocity set"); return -EINVAL; } @@ -218,8 +293,9 @@ int step_dir_stepper_common_move_to(const struct device *dev, const int32_t valu K_SPINLOCK(&data->lock) { data->run_mode = STEPPER_RUN_MODE_POSITION; data->step_count = value - data->actual_position; + config->timing_source->update(dev, data->max_velocity); update_direction_from_step_count(dev); - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); + config->timing_source->start(dev); } return 0; @@ -227,9 +303,9 @@ int step_dir_stepper_common_move_to(const struct device *dev, const int32_t valu int step_dir_stepper_common_is_moving(const struct device *dev, bool *is_moving) { - struct step_dir_stepper_common_data *data = dev->data; + const struct step_dir_stepper_common_config *config = dev->config; - *is_moving = k_work_delayable_is_pending(&data->stepper_dwork); + *is_moving = config->timing_source->is_running(dev); return 0; } @@ -237,15 +313,17 @@ int step_dir_stepper_common_run(const struct device *dev, const enum stepper_dir const uint32_t velocity) { struct step_dir_stepper_common_data *data = dev->data; + const struct step_dir_stepper_common_config *config = dev->config; K_SPINLOCK(&data->lock) { data->run_mode = STEPPER_RUN_MODE_VELOCITY; data->direction = direction; + data->max_velocity = velocity; + config->timing_source->update(dev, velocity); if (velocity != 0) { - data->delay_in_us = USEC_PER_SEC / velocity; - (void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT); + config->timing_source->start(dev); } else { - (void)k_work_cancel_delayable(&data->stepper_dwork); + config->timing_source->stop(dev); } } diff --git a/drivers/stepper/step_dir_stepper_common.h b/drivers/stepper/step_dir/step_dir_stepper_common.h similarity index 82% rename from drivers/stepper/step_dir_stepper_common.h rename to drivers/stepper/step_dir/step_dir_stepper_common.h index e1cfa773c9d..007d646fb0f 100644 --- a/drivers/stepper/step_dir_stepper_common.h +++ b/drivers/stepper/step_dir/step_dir_stepper_common.h @@ -17,6 +17,9 @@ #include #include #include +#include + +#include "step_dir_stepper_timing_source.h" /** * @brief Common step direction stepper config. @@ -27,18 +30,26 @@ struct step_dir_stepper_common_config { const struct gpio_dt_spec step_pin; const struct gpio_dt_spec dir_pin; bool dual_edge; + const struct stepper_timing_source_api *timing_source; + const struct device *counter; }; /** * @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), \ + .dir_pin = GPIO_DT_SPEC_GET(node_id, dir_gpios), \ + .dual_edge = DT_PROP(node_id, dual_edge_step), \ + .counter = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, counter)), \ + .timing_source = COND_CODE_1(DT_NODE_HAS_PROP(node_id, counter), \ + (&step_counter_timing_source_api), \ + (&step_work_timing_source_api)), \ } /** @@ -58,12 +69,25 @@ struct step_dir_stepper_common_data { struct k_spinlock lock; enum stepper_direction direction; enum stepper_run_mode run_mode; - struct k_work_delayable stepper_dwork; int32_t actual_position; - uint32_t delay_in_us; + uint32_t max_velocity; int32_t step_count; stepper_event_callback_t callback; void *event_cb_user_data; + + struct k_work_delayable stepper_dwork; + +#ifdef CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING + struct counter_top_cfg counter_top_cfg; + bool counter_running; +#endif /* CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING */ + +#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS + struct k_work event_callback_work; + struct k_msgq event_msgq; + uint8_t event_msgq_buffer[CONFIG_STEPPER_STEP_DIR_EVENT_QUEUE_LEN * + sizeof(enum stepper_event)]; +#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */ }; /** @@ -186,6 +210,12 @@ int step_dir_stepper_common_run(const struct device *dev, const enum stepper_dir 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); + /** @} */ #endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_COMMON_H_ */ diff --git a/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c b/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c new file mode 100644 index 00000000000..6a99b434632 --- /dev/null +++ b/drivers/stepper/step_dir/step_dir_stepper_counter_timing.c @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "step_dir_stepper_common.h" + +#include +LOG_MODULE_DECLARE(step_dir_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; + + stepper_handle_timing_signal(data->dev); +} + +int step_counter_timing_source_update(const struct device *dev, const uint32_t velocity) +{ + const struct step_dir_stepper_common_config *config = dev->config; + struct step_dir_stepper_common_data *data = dev->data; + int ret; + + if (velocity == 0) { + return -EINVAL; + } + + data->counter_top_cfg.ticks = + DIV_ROUND_UP(counter_us_to_ticks(config->counter, USEC_PER_SEC), velocity); + + /* Lock interrupts while modifying counter settings */ + int key = irq_lock(); + + ret = counter_set_top_value(config->counter, &data->counter_top_cfg); + + irq_unlock(key); + + if (ret != 0) { + LOG_ERR("%s: Failed to set counter top value (error: %d)", dev->name, ret); + return ret; + } + + return 0; +} + +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; + int ret; + + ret = counter_start(config->counter); + if (ret < 0 && ret != -EALREADY) { + LOG_ERR("Failed to start counter: %d", ret); + return ret; + } + + data->counter_running = true; + + return 0; +} + +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; + int ret; + + ret = counter_stop(config->counter); + if (ret < 0 && ret != -EALREADY) { + LOG_ERR("Failed to stop counter: %d", ret); + return ret; + } + + data->counter_running = false; + + return 0; +} + +bool step_counter_timing_source_needs_reschedule(const struct device *dev) +{ + ARG_UNUSED(dev); + return false; +} + +bool step_counter_timing_source_is_running(const struct device *dev) +{ + struct step_dir_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; + + if (!device_is_ready(config->counter)) { + LOG_ERR("Counter device is not ready"); + return -ENODEV; + } + + data->counter_top_cfg.callback = step_counter_top_interrupt; + data->counter_top_cfg.user_data = data; + data->counter_top_cfg.flags = 0; + data->counter_top_cfg.ticks = counter_us_to_ticks(config->counter, 1000000); + + return 0; +} + +const struct stepper_timing_source_api step_counter_timing_source_api = { + .init = step_counter_timing_source_init, + .update = step_counter_timing_source_update, + .start = step_counter_timing_source_start, + .needs_reschedule = step_counter_timing_source_needs_reschedule, + .stop = step_counter_timing_source_stop, + .is_running = step_counter_timing_source_is_running, +}; diff --git a/drivers/stepper/step_dir/step_dir_stepper_timing_source.h b/drivers/stepper/step_dir/step_dir_stepper_timing_source.h new file mode 100644 index 00000000000..b2d369f968c --- /dev/null +++ b/drivers/stepper/step_dir/step_dir_stepper_timing_source.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz + * 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_ + +#include + +/** + * @brief Initialize the stepper timing source. + * + * @param dev Pointer to the device structure. + * @return 0 on success, or a negative error code on failure. + */ +typedef int (*stepper_timing_source_init)(const struct device *dev); + +/** + * @brief Update the stepper timing source. + * + * @param dev Pointer to the device structure. + * @param velocity Velocity in microsteps per second. + * @return 0 on success, or a negative error code on failure. + */ +typedef int (*stepper_timing_source_update)(const struct device *dev, uint32_t velocity); + +/** + * @brief Start the stepper timing source. + * + * @param dev Pointer to the device structure. + * @return 0 on success, or a negative error code on failure. + */ +typedef int (*stepper_timing_source_start)(const struct device *dev); + +/** + * @brief Whether the stepper timing source requires rescheduling (keeps running + * after the initial start). + * + * @param dev Pointer to the device structure. + * @return true if the timing source requires rescheduling, false otherwise. + */ +typedef bool (*stepper_timing_sources_requires_reschedule)(const struct device *dev); + +/** + * @brief Stop the stepper timing source. + * + * @param dev Pointer to the device structure. + * @return 0 on success, or a negative error code on failure. + */ +typedef int (*stepper_timing_source_stop)(const struct device *dev); + +/** + * @brief Check if the stepper timing source is running. + * + * @param dev Pointer to the device structure. + * @return true if the timing source is running, false otherwise. + */ +typedef bool (*stepper_timing_source_is_running)(const struct device *dev); + +/** + * @brief Stepper timing source API. + */ +struct stepper_timing_source_api { + stepper_timing_source_init init; + stepper_timing_source_update update; + stepper_timing_source_start start; + stepper_timing_sources_requires_reschedule needs_reschedule; + stepper_timing_source_stop stop; + stepper_timing_source_is_running is_running; +}; + +extern const struct stepper_timing_source_api step_work_timing_source_api; +#ifdef CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING +extern const struct stepper_timing_source_api step_counter_timing_source_api; +#endif /* CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING */ + +#endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_TIMING_SOURCE_H_ */ diff --git a/drivers/stepper/step_dir/step_dir_stepper_work_timing.c b/drivers/stepper/step_dir/step_dir_stepper_work_timing.c new file mode 100644 index 00000000000..8d3bc861414 --- /dev/null +++ b/drivers/stepper/step_dir/step_dir_stepper_work_timing.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "step_dir_stepper_timing_source.h" +#include "step_dir_stepper_common.h" + +static k_timeout_t stepper_movement_delay(const struct device *dev) +{ + const struct step_dir_stepper_common_data *data = dev->data; + + if (data->max_velocity == 0) { + return K_FOREVER; + } + + return K_USEC(USEC_PER_SEC / data->max_velocity); +} + +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); + + stepper_handle_timing_signal(data->dev); +} + +int step_work_timing_source_init(const struct device *dev) +{ + struct step_dir_stepper_common_data *data = dev->data; + + k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler); + + return 0; +} + +int step_work_timing_source_update(const struct device *dev, const uint32_t velocity) +{ + ARG_UNUSED(dev); + ARG_UNUSED(velocity); + return 0; +} + +int step_work_timing_source_start(const struct device *dev) +{ + struct step_dir_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; + + return k_work_cancel_delayable(&data->stepper_dwork); +} + +bool step_work_timing_source_needs_reschedule(const struct device *dev) +{ + ARG_UNUSED(dev); + return true; +} + +bool step_work_timing_source_is_running(const struct device *dev) +{ + struct step_dir_stepper_common_data *data = dev->data; + + return k_work_delayable_is_pending(&data->stepper_dwork); +} + +const struct stepper_timing_source_api step_work_timing_source_api = { + .init = step_work_timing_source_init, + .update = step_work_timing_source_update, + .start = step_work_timing_source_start, + .needs_reschedule = step_work_timing_source_needs_reschedule, + .stop = step_work_timing_source_stop, + .is_running = step_work_timing_source_is_running, +}; diff --git a/dts/bindings/stepper/stepper-controller.yaml b/dts/bindings/stepper/stepper-controller.yaml index eae57a18a25..a73037bb6b9 100644 --- a/dts/bindings/stepper/stepper-controller.yaml +++ b/dts/bindings/stepper/stepper-controller.yaml @@ -40,3 +40,7 @@ properties: description: | The GPIO pins used to send direction signals to the stepper motor. Pin will be driven high for forward direction and low for reverse direction. + + counter: + type: phandle + description: Counter used for generating step-accurate pulse signals.