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>
This commit is contained in:
parent
fc2567939b
commit
0b124a2ff6
12 changed files with 473 additions and 37 deletions
|
|
@ -6,6 +6,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h)
|
||||||
# zephyr-keep-sorted-start
|
# zephyr-keep-sorted-start
|
||||||
add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC adi_tmc)
|
add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC adi_tmc)
|
||||||
add_subdirectory_ifdef(CONFIG_STEPPER_TI ti)
|
add_subdirectory_ifdef(CONFIG_STEPPER_TI ti)
|
||||||
|
add_subdirectory_ifdef(CONFIG_STEP_DIR_STEPPER step_dir)
|
||||||
# zephyr-keep-sorted-stop
|
# zephyr-keep-sorted-stop
|
||||||
|
|
||||||
zephyr_library()
|
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_FAKE_STEPPER fake_stepper_controller.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_controller.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_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)
|
zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c)
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,9 @@ config STEPPER_SHELL
|
||||||
help
|
help
|
||||||
Enable stepper shell for testing.
|
Enable stepper shell for testing.
|
||||||
|
|
||||||
config STEP_DIR_STEPPER
|
comment "Stepper Driver Common"
|
||||||
bool
|
|
||||||
help
|
rsource "step_dir/Kconfig"
|
||||||
Enable library used for step direction stepper drivers.
|
|
||||||
|
|
||||||
comment "Stepper Drivers"
|
comment "Stepper Drivers"
|
||||||
|
|
||||||
|
|
|
||||||
18
drivers/stepper/Kconfig.stepper_event_template
Normal file
18
drivers/stepper/Kconfig.stepper_event_template
Normal file
|
|
@ -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.
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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>
|
#include <zephyr/logging/log.h>
|
||||||
LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL);
|
LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL);
|
||||||
|
|
|
||||||
8
drivers/stepper/step_dir/CMakeLists.txt
Normal file
8
drivers/stepper/step_dir/CMakeLists.txt
Normal file
|
|
@ -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
Normal file
22
drivers/stepper/step_dir/Kconfig
Normal file
|
|
@ -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
|
||||||
|
|
@ -47,20 +47,70 @@ static inline int step_dir_stepper_perform_step(const struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_remaining_steps(struct step_dir_stepper_common_data *data)
|
static void stepper_trigger_callback(const struct device *dev, enum stepper_event event)
|
||||||
{
|
{
|
||||||
if (data->step_count > 0) {
|
struct step_dir_stepper_common_data *data = dev->data;
|
||||||
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) {
|
if (!data->callback) {
|
||||||
LOG_WRN_ONCE("No callback set");
|
LOG_WRN_ONCE("No callback set");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data->callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED, data->event_cb_user_data);
|
|
||||||
|
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--;
|
||||||
|
} else if (data->step_count < 0) {
|
||||||
|
data->step_count++;
|
||||||
|
} else {
|
||||||
|
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)
|
static void position_mode_task(const struct device *dev)
|
||||||
{
|
{
|
||||||
struct step_dir_stepper_common_data *data = dev->data;
|
struct step_dir_stepper_common_data *data = dev->data;
|
||||||
|
const struct step_dir_stepper_common_config *config = dev->config;
|
||||||
|
|
||||||
if (data->step_count) {
|
if (data->step_count) {
|
||||||
(void)step_dir_stepper_perform_step(dev);
|
(void)step_dir_stepper_perform_step(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_remaining_steps(dev->data);
|
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)
|
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)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 = dev->data;
|
||||||
struct step_dir_stepper_common_data *data =
|
|
||||||
CONTAINER_OF(dwork, struct step_dir_stepper_common_data, stepper_dwork);
|
|
||||||
|
|
||||||
K_SPINLOCK(&data->lock) {
|
K_SPINLOCK(&data->lock) {
|
||||||
switch (data->run_mode) {
|
switch (data->run_mode) {
|
||||||
case STEPPER_RUN_MODE_POSITION:
|
case STEPPER_RUN_MODE_POSITION:
|
||||||
position_mode_task(data->dev);
|
position_mode_task(dev);
|
||||||
break;
|
break;
|
||||||
case STEPPER_RUN_MODE_VELOCITY:
|
case STEPPER_RUN_MODE_VELOCITY:
|
||||||
velocity_mode_task(data->dev);
|
velocity_mode_task(dev);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_WRN("Unsupported run mode: %d", data->run_mode);
|
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)
|
int step_dir_stepper_common_init(const struct device *dev)
|
||||||
{
|
{
|
||||||
const struct step_dir_stepper_common_config *config = dev->config;
|
const struct step_dir_stepper_common_config *config = dev->config;
|
||||||
struct step_dir_stepper_common_data *data = dev->data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) {
|
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;
|
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;
|
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)
|
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;
|
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");
|
LOG_ERR("Velocity not set or invalid velocity set");
|
||||||
return -EINVAL;
|
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) {
|
K_SPINLOCK(&data->lock) {
|
||||||
data->run_mode = STEPPER_RUN_MODE_POSITION;
|
data->run_mode = STEPPER_RUN_MODE_POSITION;
|
||||||
data->step_count = micro_steps;
|
data->step_count = micro_steps;
|
||||||
|
config->timing_source->update(dev, data->max_velocity);
|
||||||
update_direction_from_step_count(dev);
|
update_direction_from_step_count(dev);
|
||||||
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
|
config->timing_source->start(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
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;
|
struct step_dir_stepper_common_data *data = dev->data;
|
||||||
|
const struct step_dir_stepper_common_config *config = dev->config;
|
||||||
|
|
||||||
if (velocity == 0) {
|
if (velocity == 0) {
|
||||||
LOG_ERR("Velocity cannot be zero");
|
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) {
|
K_SPINLOCK(&data->lock) {
|
||||||
data->delay_in_us = USEC_PER_SEC / velocity;
|
data->max_velocity = velocity;
|
||||||
|
config->timing_source->update(dev, velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value)
|
||||||
{
|
{
|
||||||
struct step_dir_stepper_common_data *data = dev->data;
|
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");
|
LOG_ERR("Velocity not set or invalid velocity set");
|
||||||
return -EINVAL;
|
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) {
|
K_SPINLOCK(&data->lock) {
|
||||||
data->run_mode = STEPPER_RUN_MODE_POSITION;
|
data->run_mode = STEPPER_RUN_MODE_POSITION;
|
||||||
data->step_count = value - data->actual_position;
|
data->step_count = value - data->actual_position;
|
||||||
|
config->timing_source->update(dev, data->max_velocity);
|
||||||
update_direction_from_step_count(dev);
|
update_direction_from_step_count(dev);
|
||||||
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
|
config->timing_source->start(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,15 +313,17 @@ int step_dir_stepper_common_run(const struct device *dev, const enum stepper_dir
|
||||||
const uint32_t velocity)
|
const uint32_t velocity)
|
||||||
{
|
{
|
||||||
struct step_dir_stepper_common_data *data = dev->data;
|
struct step_dir_stepper_common_data *data = dev->data;
|
||||||
|
const struct step_dir_stepper_common_config *config = dev->config;
|
||||||
|
|
||||||
K_SPINLOCK(&data->lock) {
|
K_SPINLOCK(&data->lock) {
|
||||||
data->run_mode = STEPPER_RUN_MODE_VELOCITY;
|
data->run_mode = STEPPER_RUN_MODE_VELOCITY;
|
||||||
data->direction = direction;
|
data->direction = direction;
|
||||||
|
data->max_velocity = velocity;
|
||||||
|
config->timing_source->update(dev, velocity);
|
||||||
if (velocity != 0) {
|
if (velocity != 0) {
|
||||||
data->delay_in_us = USEC_PER_SEC / velocity;
|
config->timing_source->start(dev);
|
||||||
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
|
|
||||||
} else {
|
} else {
|
||||||
(void)k_work_cancel_delayable(&data->stepper_dwork);
|
config->timing_source->stop(dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
#include <zephyr/drivers/stepper.h>
|
#include <zephyr/drivers/stepper.h>
|
||||||
|
#include <zephyr/drivers/counter.h>
|
||||||
|
|
||||||
|
#include "step_dir_stepper_timing_source.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Common step direction stepper config.
|
* @brief Common step direction stepper config.
|
||||||
|
|
@ -27,10 +30,14 @@ struct step_dir_stepper_common_config {
|
||||||
const struct gpio_dt_spec step_pin;
|
const struct gpio_dt_spec step_pin;
|
||||||
const struct gpio_dt_spec dir_pin;
|
const struct gpio_dt_spec dir_pin;
|
||||||
bool dual_edge;
|
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.
|
* @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.
|
* @param node_id The devicetree node identifier.
|
||||||
*/
|
*/
|
||||||
|
|
@ -38,7 +45,11 @@ struct step_dir_stepper_common_config {
|
||||||
{ \
|
{ \
|
||||||
.step_pin = GPIO_DT_SPEC_GET(node_id, step_gpios), \
|
.step_pin = GPIO_DT_SPEC_GET(node_id, step_gpios), \
|
||||||
.dir_pin = GPIO_DT_SPEC_GET(node_id, dir_gpios), \
|
.dir_pin = GPIO_DT_SPEC_GET(node_id, dir_gpios), \
|
||||||
.dual_edge = DT_PROP_OR(node_id, dual_edge_step, false), \
|
.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;
|
struct k_spinlock lock;
|
||||||
enum stepper_direction direction;
|
enum stepper_direction direction;
|
||||||
enum stepper_run_mode run_mode;
|
enum stepper_run_mode run_mode;
|
||||||
struct k_work_delayable stepper_dwork;
|
|
||||||
int32_t actual_position;
|
int32_t actual_position;
|
||||||
uint32_t delay_in_us;
|
uint32_t max_velocity;
|
||||||
int32_t step_count;
|
int32_t step_count;
|
||||||
stepper_event_callback_t callback;
|
stepper_event_callback_t callback;
|
||||||
void *event_cb_user_data;
|
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,
|
int step_dir_stepper_common_set_event_callback(const struct device *dev,
|
||||||
stepper_event_callback_t callback, void *user_data);
|
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_ */
|
#endif /* ZEPHYR_DRIVER_STEPPER_STEP_DIR_STEPPER_COMMON_H_ */
|
||||||
120
drivers/stepper/step_dir/step_dir_stepper_counter_timing.c
Normal file
120
drivers/stepper/step_dir/step_dir_stepper_counter_timing.c
Normal file
|
|
@ -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
Normal file
78
drivers/stepper/step_dir/step_dir_stepper_timing_source.h
Normal file
|
|
@ -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
Normal file
79
drivers/stepper/step_dir/step_dir_stepper_work_timing.c
Normal file
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
@ -40,3 +40,7 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
The GPIO pins used to send direction signals to the stepper motor.
|
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.
|
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…
Reference in a new issue