Browse Source

drivers: stepper: Add timing source for step dir stepper

Adds a timing source api which is used by the step-dir stepper common code.
This allows the reusable common code to configure different timing sources,
since the initial delayable work implementation was inacurate for higher
maximum velocities.

Signed-off-by: Fabian Blatz <fabianblatz@gmail.com>
pull/83429/head
Fabian Blatz 7 months ago committed by Benjamin Cabé
parent
commit
0b124a2ff6
  1. 2
      drivers/stepper/CMakeLists.txt
  2. 7
      drivers/stepper/Kconfig
  3. 18
      drivers/stepper/Kconfig.stepper_event_template
  4. 2
      drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c
  5. 8
      drivers/stepper/step_dir/CMakeLists.txt
  6. 22
      drivers/stepper/step_dir/Kconfig
  7. 132
      drivers/stepper/step_dir/step_dir_stepper_common.c
  8. 38
      drivers/stepper/step_dir/step_dir_stepper_common.h
  9. 120
      drivers/stepper/step_dir/step_dir_stepper_counter_timing.c
  10. 78
      drivers/stepper/step_dir/step_dir_stepper_timing_source.h
  11. 79
      drivers/stepper/step_dir/step_dir_stepper_work_timing.c
  12. 4
      dts/bindings/stepper/stepper-controller.yaml

2
drivers/stepper/CMakeLists.txt

@ -6,6 +6,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h) @@ -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) @@ -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)

7
drivers/stepper/Kconfig

@ -24,10 +24,9 @@ config STEPPER_SHELL @@ -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"

18
drivers/stepper/Kconfig.stepper_event_template

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
# Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
# 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.

2
drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c

@ -3,7 +3,7 @@ @@ -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 <zephyr/logging/log.h>
LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL);

8
drivers/stepper/step_dir/CMakeLists.txt

@ -0,0 +1,8 @@ @@ -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)

22
drivers/stepper/step_dir/Kconfig

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
# Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
# 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

132
drivers/stepper/step_dir_stepper_common.c → 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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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 @@ -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 @@ -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_ @@ -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 @@ -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 @@ -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 @@ -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);
}
}

38
drivers/stepper/step_dir_stepper_common.h → drivers/stepper/step_dir/step_dir_stepper_common.h

@ -17,6 +17,9 @@ @@ -17,6 +17,9 @@
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/stepper.h>
#include <zephyr/drivers/counter.h>
#include "step_dir_stepper_timing_source.h"
/**
* @brief Common step direction stepper config.
@ -27,18 +30,26 @@ struct step_dir_stepper_common_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 { @@ -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 @@ -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_ */

120
drivers/stepper/step_dir/step_dir_stepper_counter_timing.c

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/counter.h>
#include "step_dir_stepper_common.h"
#include <zephyr/logging/log.h>
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,
};

78
drivers/stepper/step_dir/step_dir_stepper_timing_source.h

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
* 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 <zephyr/device.h>
/**
* @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_ */

79
drivers/stepper/step_dir/step_dir_stepper_work_timing.c

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
* 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,
};

4
dts/bindings/stepper/stepper-controller.yaml

@ -40,3 +40,7 @@ properties: @@ -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.

Loading…
Cancel
Save