drivers: stepper: adi: trinamic tmc5041
This commit introduces initial structure for trinamic drivers TMC5041 is implemented with following features: - StallGuard - RAMPSTAT_POLL - RAMP_GEN Signed-off-by: Dipak Shetty <dipak.shetty@zeiss.com> Signed-off-by: Jilay Pandya <jilay.pandya@zeiss.com>
This commit is contained in:
parent
42c43b9f9c
commit
52c6a289f1
15 changed files with 1652 additions and 1 deletions
|
|
@ -3,9 +3,13 @@
|
|||
|
||||
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h)
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC adi_tmc)
|
||||
# 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_GPIO_STEPPER gpio_stepper_controller.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ config STEPPER_SHELL_THREAD_PRIORITY
|
|||
|
||||
comment "Stepper Drivers"
|
||||
|
||||
rsource "adi_tmc/Kconfig"
|
||||
rsource "Kconfig.fake"
|
||||
rsource "Kconfig.gpio"
|
||||
|
||||
|
|
|
|||
6
drivers/stepper/adi_tmc/CMakeLists.txt
Normal file
6
drivers/stepper/adi_tmc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC5041 adi_tmc5041_stepper_controller.c)
|
||||
56
drivers/stepper/adi_tmc/Kconfig
Normal file
56
drivers/stepper/adi_tmc/Kconfig
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig STEPPER_ADI_TMC
|
||||
bool "Trinamic Stepper Controller"
|
||||
depends on STEPPER
|
||||
default y
|
||||
help
|
||||
Enable trinamic stepper controller
|
||||
|
||||
if STEPPER_ADI_TMC
|
||||
|
||||
config STEPPER_ADI_TMC_RAMP_GEN
|
||||
bool "Use Trinamic Stepper Controller with Ramp Generator"
|
||||
depends on STEPPER_ADI_TMC
|
||||
default y
|
||||
help
|
||||
Enable ramp generator for trinamic stepper controller
|
||||
|
||||
config STEPPER_ADI_TMC_SPI
|
||||
bool "Use Trinamic Stepper Controller with SPI"
|
||||
depends on STEPPER_ADI_TMC
|
||||
select SPI
|
||||
help
|
||||
A Trinamic Stepper Controller with SPI is enabled
|
||||
|
||||
comment "Trinamic Stepper Drivers"
|
||||
|
||||
config STEPPER_ADI_TMC5041
|
||||
bool "Activate trinamic tmc5041 stepper driver"
|
||||
depends on DT_HAS_ADI_TMC5041_ENABLED && STEPPER_ADI_TMC
|
||||
select STEPPER_ADI_TMC_SPI
|
||||
default y
|
||||
help
|
||||
Stepper driver for TMC5041.
|
||||
|
||||
config STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
bool "TMC5041 poll ramp status"
|
||||
depends on STEPPER_ADI_TMC5041
|
||||
select POLL
|
||||
default y
|
||||
help
|
||||
When enabled, the ramp status will be polled on TMC5041, to check for events:
|
||||
- TMC5041_POS_REACHED_EVENT
|
||||
- TMC5041_STOP_SG_EVENT
|
||||
- TMC5041_STOP_LEFT_EVENT
|
||||
- TMC5041_STOP_RIGHT_EVENT
|
||||
|
||||
config STEPPER_ADI_TMC5041_RAMPSTAT_POLL_INTERVAL_IN_MSEC
|
||||
int "TMC5041 poll ramp status interval in ms"
|
||||
depends on STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
default 100
|
||||
help
|
||||
The interval in ms to poll the ramp status on TMC5041.
|
||||
|
||||
endif # STEPPER_ADI_TMC
|
||||
762
drivers/stepper/adi_tmc/adi_tmc5041_stepper_controller.c
Normal file
762
drivers/stepper/adi_tmc/adi_tmc5041_stepper_controller.c
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT adi_tmc5041
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
#include <zephyr/drivers/stepper.h>
|
||||
#include <zephyr/drivers/stepper/stepper_trinamic.h>
|
||||
|
||||
#include "adi_tmc_reg.h"
|
||||
#include "adi_tmc_spi.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(tmc5041, CONFIG_STEPPER_LOG_LEVEL);
|
||||
|
||||
struct tmc5041_data {
|
||||
struct k_sem sem;
|
||||
};
|
||||
|
||||
struct tmc5041_config {
|
||||
const uint32_t gconf;
|
||||
struct spi_dt_spec spi;
|
||||
const uint32_t clock_frequency;
|
||||
};
|
||||
|
||||
struct tmc5041_stepper_data {
|
||||
struct k_work_delayable stallguard_dwork;
|
||||
/* Work item to run the callback in a thread context. */
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
struct k_work_delayable rampstat_callback_dwork;
|
||||
#endif
|
||||
/* device pointer required to access config in k_work */
|
||||
const struct device *stepper;
|
||||
struct k_poll_signal *async_signal;
|
||||
};
|
||||
|
||||
struct tmc5041_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_TMC_RAMP_GEN
|
||||
const struct tmc_ramp_generator_data default_ramp_config;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int tmc5041_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val)
|
||||
{
|
||||
const struct tmc5041_config *config = dev->config;
|
||||
struct tmc5041_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, TMC5041_WRITE_BIT, reg_addr, reg_val);
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val)
|
||||
{
|
||||
const struct tmc5041_config *config = dev->config;
|
||||
struct tmc5041_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, TMC5041_ADDRESS_MASK, reg_addr, reg_val);
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to read register 0x%x", reg_addr);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calculate_velocity_from_hz_to_fclk(const struct device *dev, const uint32_t velocity_hz,
|
||||
uint32_t *const velocity_fclk)
|
||||
{
|
||||
const struct tmc5041_config *config = dev->config;
|
||||
|
||||
*velocity_fclk =
|
||||
((uint64_t)(velocity_hz) << TMC5041_CLOCK_FREQ_SHIFT) / config->clock_frequency;
|
||||
LOG_DBG("Stepper motor controller %s velocity: %d Hz, velocity_fclk: %d", dev->name,
|
||||
velocity_hz, *velocity_fclk);
|
||||
}
|
||||
|
||||
static void set_async_signal(const struct device *dev, struct k_poll_signal *async)
|
||||
{
|
||||
struct tmc5041_stepper_data *data = dev->data;
|
||||
|
||||
if (!async) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->async_signal) {
|
||||
k_poll_signal_reset(data->async_signal);
|
||||
}
|
||||
data->async_signal = async;
|
||||
}
|
||||
|
||||
static int stallguard_enable(const struct device *dev, const bool enable)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_SWMODE(config->index), ®_value);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to read SWMODE register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
reg_value |= TMC5041_SW_MODE_SG_STOP_ENABLE;
|
||||
|
||||
int32_t actual_velocity;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_VACTUAL(config->index),
|
||||
&actual_velocity);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to read VACTUAL register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
actual_velocity = (actual_velocity << (31 - TMC_RAMP_VACTUAL_SHIFT)) >>
|
||||
(31 - TMC_RAMP_VACTUAL_SHIFT);
|
||||
LOG_DBG("actual velocity: %d", actual_velocity);
|
||||
|
||||
if (abs(actual_velocity) < config->sg_threshold_velocity) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
reg_value &= ~TMC5041_SW_MODE_SG_STOP_ENABLE;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_SWMODE(config->index), reg_value);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write SWMODE register");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stallguard_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
struct tmc5041_stepper_data *stepper_data =
|
||||
CONTAINER_OF(dwork, struct tmc5041_stepper_data, stallguard_dwork);
|
||||
int err;
|
||||
const struct tmc5041_stepper_config *stepper_config = stepper_data->stepper->config;
|
||||
|
||||
err = stallguard_enable(stepper_data->stepper, true);
|
||||
if (err == -EAGAIN) {
|
||||
LOG_ERR("retrying stallguard activation");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
|
||||
static void emit_signal(struct k_poll_signal *async_signal, const enum stepper_signal_result signal)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!async_signal) {
|
||||
LOG_WRN("Async signal is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
err = k_poll_signal_raise(async_signal, signal);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to raise signal %d error %d", signal, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void rampstat_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
|
||||
struct tmc5041_stepper_data *stepper_data =
|
||||
CONTAINER_OF(dwork, struct tmc5041_stepper_data, rampstat_callback_dwork);
|
||||
const struct tmc5041_stepper_config *stepper_config = stepper_data->stepper->config;
|
||||
|
||||
__ASSERT_NO_MSG(stepper_config->controller != NULL);
|
||||
|
||||
uint32_t drv_status;
|
||||
int err;
|
||||
|
||||
tmc5041_read(stepper_config->controller, TMC5041_DRVSTATUS(stepper_config->index),
|
||||
&drv_status);
|
||||
if (FIELD_GET(TMC5041_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) {
|
||||
LOG_INF("%s: Stall detected", stepper_data->stepper->name);
|
||||
err = tmc5041_write(stepper_config->controller,
|
||||
TMC5041_RAMPMODE(stepper_config->index),
|
||||
TMC5041_RAMPMODE_HOLD_MODE);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to stop motor", stepper_data->stepper->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rampstat_value;
|
||||
|
||||
err = tmc5041_read(stepper_config->controller, TMC5041_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(TMC5041_RAMPSTAT_INT_MASK, rampstat_value);
|
||||
|
||||
if (ramp_stat_values > 0) {
|
||||
switch (ramp_stat_values) {
|
||||
|
||||
case TMC5041_STOP_LEFT_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Left end-stop detected", stepper_data->stepper->name);
|
||||
emit_signal(stepper_data->async_signal,
|
||||
STEPPER_SIGNAL_LEFT_END_STOP_DETECTED);
|
||||
break;
|
||||
|
||||
case TMC5041_STOP_RIGHT_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Right end-stop detected", stepper_data->stepper->name);
|
||||
emit_signal(stepper_data->async_signal,
|
||||
STEPPER_SIGNAL_RIGHT_END_STOP_DETECTED);
|
||||
break;
|
||||
|
||||
case TMC5041_POS_REACHED_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Position reached", stepper_data->stepper->name);
|
||||
emit_signal(stepper_data->async_signal, STEPPER_SIGNAL_STEPS_COMPLETED);
|
||||
break;
|
||||
|
||||
case TMC5041_STOP_SG_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Stall detected", stepper_data->stepper->name);
|
||||
stallguard_enable(stepper_data->stepper, false);
|
||||
emit_signal(stepper_data->async_signal,
|
||||
STEPPER_SIGNAL_SENSORLESS_STALL_DETECTED);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Illegal ramp stat bit field");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
k_work_reschedule(
|
||||
&stepper_data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int tmc5041_stepper_enable(const struct device *dev, const bool enable)
|
||||
{
|
||||
LOG_DBG("Stepper motor controller %s %s", dev->name, enable ? "enabled" : "disabled");
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_CHOPCONF(config->index), ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
reg_value |= TMC5041_CHOPCONF_DRV_ENABLE_MASK;
|
||||
} else {
|
||||
reg_value &= ~TMC5041_CHOPCONF_DRV_ENABLE_MASK;
|
||||
}
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_CHOPCONF(config->index), reg_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_is_moving(const struct device *dev, bool *is_moving)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_DRVSTATUS(config->index), ®_value);
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*is_moving = (FIELD_GET(TMC5041_DRV_STATUS_STST_BIT, reg_value) != 1U);
|
||||
LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_move(const struct device *dev, const int32_t steps,
|
||||
struct k_poll_signal *async)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
struct tmc5041_stepper_data *data = dev->data;
|
||||
int err;
|
||||
|
||||
set_async_signal(dev, async);
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
err = stallguard_enable(dev, false);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t position;
|
||||
|
||||
err = stepper_get_actual_position(dev, &position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
int32_t target_position = position + steps;
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_RAMPMODE(config->index),
|
||||
TMC5041_RAMPMODE_POSITIONING_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
LOG_DBG("Stepper motor controller %s moved to %d by steps: %d", dev->name, target_position,
|
||||
steps);
|
||||
err = tmc5041_write(config->controller, TMC5041_XTARGET(config->index), target_position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
k_work_reschedule(&data->stallguard_dwork,
|
||||
K_MSEC(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
k_work_reschedule(&data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_set_max_velocity(const struct device *dev, uint32_t velocity)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t velocity_fclk;
|
||||
int err;
|
||||
|
||||
calculate_velocity_from_hz_to_fclk(config->controller, velocity, &velocity_fclk);
|
||||
err = tmc5041_write(config->controller, TMC5041_VMAX(config->index), velocity_fclk);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to set max velocity", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_set_micro_step_res(const struct device *dev,
|
||||
enum micro_step_resolution res)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_CHOPCONF(config->index), ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
reg_value &= ~TMC5041_CHOPCONF_MRES_MASK;
|
||||
reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res))
|
||||
<< TMC5041_CHOPCONF_MRES_SHIFT);
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_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 tmc5041_stepper_get_micro_step_res(const struct device *dev,
|
||||
enum micro_step_resolution *res)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_CHOPCONF(config->index), ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
reg_value &= TMC5041_CHOPCONF_MRES_MASK;
|
||||
reg_value >>= TMC5041_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 tmc5041_stepper_set_actual_position(const struct device *dev, const int32_t position)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_RAMPMODE(config->index),
|
||||
TMC5041_RAMPMODE_HOLD_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_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 tmc5041_stepper_get_actual_position(const struct device *dev, int32_t *position)
|
||||
{
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
err = tmc5041_read(config->controller, TMC5041_XACTUAL(config->index), position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
LOG_DBG("%s actual position: %d", dev->name, *position);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_set_target_position(const struct device *dev, const int32_t position,
|
||||
struct k_poll_signal *async)
|
||||
{
|
||||
LOG_DBG("Stepper motor controller %s set target position to %d", dev->name, position);
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
struct tmc5041_stepper_data *data = dev->data;
|
||||
int err;
|
||||
|
||||
set_async_signal(dev, async);
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
stallguard_enable(dev, false);
|
||||
}
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_RAMPMODE(config->index),
|
||||
TMC5041_RAMPMODE_POSITIONING_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
tmc5041_write(config->controller, TMC5041_XTARGET(config->index), position);
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
k_work_reschedule(&data->stallguard_dwork,
|
||||
K_MSEC(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
k_work_reschedule(&data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_enable_constant_velocity_mode(const struct device *dev,
|
||||
const enum stepper_direction direction,
|
||||
const uint32_t velocity)
|
||||
{
|
||||
LOG_DBG("Stepper motor controller %s enable constant velocity mode", dev->name);
|
||||
const struct tmc5041_stepper_config *config = dev->config;
|
||||
struct tmc5041_stepper_data *data = dev->data;
|
||||
uint32_t velocity_fclk;
|
||||
int err;
|
||||
|
||||
calculate_velocity_from_hz_to_fclk(config->controller, velocity, &velocity_fclk);
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
err = stallguard_enable(dev, false);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case STEPPER_DIRECTION_POSITIVE:
|
||||
err = tmc5041_write(config->controller, TMC5041_RAMPMODE(config->index),
|
||||
TMC5041_RAMPMODE_POSITIVE_VELOCITY_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VMAX(config->index), velocity_fclk);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case STEPPER_DIRECTION_NEGATIVE:
|
||||
err = tmc5041_write(config->controller, TMC5041_RAMPMODE(config->index),
|
||||
TMC5041_RAMPMODE_NEGATIVE_VELOCITY_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VMAX(config->index), velocity_fclk);
|
||||
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));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC_RAMP_GEN
|
||||
|
||||
int tmc5041_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 tmc5041_stepper_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
err = tmc5041_write(config->controller, TMC5041_VSTART(config->index), ramp_data->vstart);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_A1(config->index), ramp_data->a1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_AMAX(config->index), ramp_data->amax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_D1(config->index), ramp_data->d1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_DMAX(config->index), ramp_data->dmax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_V1(config->index), ramp_data->v1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VMAX(config->index), ramp_data->vmax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VSTOP(config->index), ramp_data->vstop);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_TZEROWAIT(config->index),
|
||||
ramp_data->tzerowait);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VHIGH(config->index), ramp_data->vhigh);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_VCOOLTHRS(config->index),
|
||||
ramp_data->vcoolthrs);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc5041_write(config->controller, TMC5041_IHOLD_IRUN(config->index),
|
||||
ramp_data->iholdrun);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int tmc5041_init(const struct device *dev)
|
||||
{
|
||||
LOG_DBG("TMC5041 stepper motor controller %s initialized", dev->name);
|
||||
struct tmc5041_data *data = dev->data;
|
||||
const struct tmc5041_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 = tmc5041_write(dev, TMC5041_GCONF, config->gconf);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read GSTAT register values to clear any errors SPI Datagram. */
|
||||
uint32_t gstat_value;
|
||||
|
||||
err = tmc5041_read(dev, TMC5041_GSTAT, &gstat_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_DBG("Device %s initialized", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc5041_stepper_init(const struct device *dev)
|
||||
{
|
||||
const struct tmc5041_stepper_config *stepper_config = dev->config;
|
||||
struct tmc5041_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 = tmc5041_write(stepper_config->controller,
|
||||
TMC5041_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, TMC5041_SG_MIN_VALUE,
|
||||
TMC5041_SG_MAX_VALUE)) {
|
||||
LOG_ERR("Stallguard threshold out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32_t stall_guard_threshold = (int32_t)stepper_config->sg_threshold;
|
||||
|
||||
err = tmc5041_write(
|
||||
stepper_config->controller, TMC5041_COOLCONF(stepper_config->index),
|
||||
stall_guard_threshold << TMC5041_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = stallguard_enable(dev, true);
|
||||
if (err == -EAGAIN) {
|
||||
LOG_ERR("retrying stallguard activation");
|
||||
k_work_reschedule(&data->stallguard_dwork,
|
||||
K_MSEC(stepper_config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC_RAMP_GEN
|
||||
err = tmc5041_stepper_set_ramp(dev, &stepper_config->default_ramp_config);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL
|
||||
k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler);
|
||||
k_work_reschedule(&data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC5041_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
#endif
|
||||
err = tmc5041_stepper_set_micro_step_res(dev, stepper_config->default_micro_step_res);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TMC5041_SHAFT_CONFIG(child) \
|
||||
(DT_PROP(child, invert_direction) << TMC5041_GCONF_SHAFT_SHIFT(DT_REG_ADDR(child))) |
|
||||
|
||||
#define TMC5041_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_TMC_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \
|
||||
static const struct tmc5041_stepper_config tmc5041_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_TMC_RAMP_GEN, \
|
||||
(.default_ramp_config = TMC_RAMP_DT_SPEC_GET(child))) };
|
||||
|
||||
#define TMC5041_STEPPER_DATA_DEFINE(child) \
|
||||
static struct tmc5041_stepper_data tmc5041_stepper_data_##child = { \
|
||||
.stepper = DEVICE_DT_GET(child),};
|
||||
|
||||
#define TMC5041_STEPPER_API_DEFINE(child) \
|
||||
static const struct stepper_driver_api tmc5041_stepper_api_##child = { \
|
||||
.enable = tmc5041_stepper_enable, \
|
||||
.is_moving = tmc5041_stepper_is_moving, \
|
||||
.move = tmc5041_stepper_move, \
|
||||
.set_max_velocity = tmc5041_stepper_set_max_velocity, \
|
||||
.set_micro_step_res = tmc5041_stepper_set_micro_step_res, \
|
||||
.get_micro_step_res = tmc5041_stepper_get_micro_step_res, \
|
||||
.set_actual_position = tmc5041_stepper_set_actual_position, \
|
||||
.get_actual_position = tmc5041_stepper_get_actual_position, \
|
||||
.set_target_position = tmc5041_stepper_set_target_position, \
|
||||
.enable_constant_velocity_mode = tmc5041_stepper_enable_constant_velocity_mode, \
|
||||
};
|
||||
|
||||
#define TMC5041_STEPPER_DEFINE(child) \
|
||||
DEVICE_DT_DEFINE(child, tmc5041_stepper_init, NULL, &tmc5041_stepper_data_##child, \
|
||||
&tmc5041_stepper_config_##child, POST_KERNEL, \
|
||||
CONFIG_STEPPER_INIT_PRIORITY, &tmc5041_stepper_api_##child);
|
||||
|
||||
#define TMC5041_DEFINE(inst) \
|
||||
BUILD_ASSERT(DT_INST_CHILD_NUM(inst) <= 2, "tmc5041 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 tmc5041_data tmc5041_data_##inst; \
|
||||
static const struct tmc5041_config tmc5041_config_##inst = { \
|
||||
.gconf = ( \
|
||||
(DT_INST_PROP(inst, poscmp_enable) << TMC5041_GCONF_POSCMP_ENABLE_SHIFT) |\
|
||||
(DT_INST_PROP(inst, test_mode) << TMC5041_GCONF_TEST_MODE_SHIFT) | \
|
||||
DT_INST_FOREACH_CHILD(inst, TMC5041_SHAFT_CONFIG) \
|
||||
(DT_INST_PROP(inst, lock_gconf) << TMC5041_LOCK_GCONF_SHIFT)), \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \
|
||||
SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8)), 0), \
|
||||
.clock_frequency = DT_INST_PROP(inst, clock_frequency),}; \
|
||||
DT_INST_FOREACH_CHILD(inst, TMC5041_STEPPER_CONFIG_DEFINE); \
|
||||
DT_INST_FOREACH_CHILD(inst, TMC5041_STEPPER_DATA_DEFINE); \
|
||||
DT_INST_FOREACH_CHILD(inst, TMC5041_STEPPER_API_DEFINE); \
|
||||
DT_INST_FOREACH_CHILD(inst, TMC5041_STEPPER_DEFINE); \
|
||||
DEVICE_DT_INST_DEFINE(inst, tmc5041_init, NULL, &tmc5041_data_##inst, \
|
||||
&tmc5041_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,\
|
||||
NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TMC5041_DEFINE)
|
||||
148
drivers/stepper/adi_tmc/adi_tmc_reg.h
Normal file
148
drivers/stepper/adi_tmc/adi_tmc_reg.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* @file drivers/stepper/adi/tmc_reg.h
|
||||
*
|
||||
* @brief TMC Registers
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC5041
|
||||
|
||||
#define TMC5041_MOTOR_ADDR(m) (0x20 << (m))
|
||||
#define TMC5041_MOTOR_ADDR_DRV(m) ((m) << 4)
|
||||
#define TMC5041_MOTOR_ADDR_PWM(m) ((m) << 3)
|
||||
|
||||
/**
|
||||
* @name TMC5041 module registers
|
||||
* @anchor TMC5041_REGISTERS
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define TMC5041_WRITE_BIT 0x80U
|
||||
#define TMC5041_ADDRESS_MASK 0x7FU
|
||||
|
||||
#define TMC5041_GCONF_POSCMP_ENABLE_SHIFT 3
|
||||
#define TMC5041_GCONF_TEST_MODE_SHIFT 7
|
||||
#define TMC5041_GCONF_SHAFT_SHIFT(n) ((n) ? 8 : 9)
|
||||
#define TMC5041_LOCK_GCONF_SHIFT 10
|
||||
|
||||
#define TMC5041_GCONF 0x00
|
||||
#define TMC5041_GSTAT 0x01
|
||||
#define TMC5041_INPUT 0x04
|
||||
#define TMC5041_X_COMPARE 0x05
|
||||
|
||||
#define TMC5041_PWMCONF(motor) (0x10 | TMC5041_MOTOR_ADDR_PWM(motor))
|
||||
#define TMC5041_PWM_STATUS(motor) (0x11 | TMC5041_MOTOR_ADDR_PWM(motor))
|
||||
|
||||
#define TMC5041_RAMPMODE(motor) (0x00 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_XACTUAL(motor) (0x01 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VACTUAL(motor) (0x02 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VSTART(motor) (0x03 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_A1(motor) (0x04 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_V1(motor) (0x05 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_AMAX(motor) (0x06 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VMAX(motor) (0x07 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_DMAX(motor) (0x08 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_D1(motor) (0x0A | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VSTOP(motor) (0x0B | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_TZEROWAIT(motor) (0x0C | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_XTARGET(motor) (0x0D | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_IHOLD_IRUN(motor) (0x10 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VCOOLTHRS(motor) (0x11 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_VHIGH(motor) (0x12 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_SWMODE(motor) (0x14 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_RAMPSTAT(motor) (0x15 | TMC5041_MOTOR_ADDR(motor))
|
||||
#define TMC5041_XLATCH(motor) (0x16 | TMC5041_MOTOR_ADDR(motor))
|
||||
|
||||
#define TMC5041_MSLUT0(motor) (0x60 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT1(motor) (0x61 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT2(motor) (0x62 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT3(motor) (0x63 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT4(motor) (0x64 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT5(motor) (0x65 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT6(motor) (0x66 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUT7(motor) (0x67 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUTSEL(motor) (0x68 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSLUTSTART(motor) (0x69 | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSCNT(motor) (0x6A | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_MSCURACT(motor) (0x6B | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_CHOPCONF(motor) (0x6C | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_COOLCONF(motor) (0x6D | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
#define TMC5041_DRVSTATUS(motor) (0x6F | TMC5041_MOTOR_ADDR_DRV(motor))
|
||||
|
||||
#define TMC5041_RAMPMODE_POSITIONING_MODE 0
|
||||
#define TMC5041_RAMPMODE_POSITIVE_VELOCITY_MODE 1
|
||||
#define TMC5041_RAMPMODE_NEGATIVE_VELOCITY_MODE 2
|
||||
#define TMC5041_RAMPMODE_HOLD_MODE 3
|
||||
|
||||
#define TMC5041_SW_MODE_SG_STOP_ENABLE BIT(10)
|
||||
|
||||
#define TMC5041_RAMPSTAT_INT_MASK GENMASK(7, 4)
|
||||
#define TMC5041_RAMPSTAT_INT_SHIFT 4
|
||||
|
||||
#define TMC5041_RAMPSTAT_POS_REACHED_EVENT_MASK BIT(7)
|
||||
#define TMC5041_POS_REACHED_EVENT \
|
||||
(TMC5041_RAMPSTAT_POS_REACHED_EVENT_MASK >> TMC5041_RAMPSTAT_INT_SHIFT)
|
||||
|
||||
#define TMC5041_RAMPSTAT_STOP_SG_EVENT_MASK BIT(6)
|
||||
#define TMC5041_STOP_SG_EVENT (TMC5041_RAMPSTAT_STOP_SG_EVENT_MASK >> TMC5041_RAMPSTAT_INT_SHIFT)
|
||||
|
||||
#define TMC5041_RAMPSTAT_STOP_RIGHT_EVENT_MASK BIT(5)
|
||||
#define TMC5041_STOP_RIGHT_EVENT \
|
||||
(TMC5041_RAMPSTAT_STOP_RIGHT_EVENT_MASK >> TMC5041_RAMPSTAT_INT_SHIFT)
|
||||
|
||||
#define TMC5041_RAMPSTAT_STOP_LEFT_EVENT_MASK BIT(4)
|
||||
#define TMC5041_STOP_LEFT_EVENT \
|
||||
(TMC5041_RAMPSTAT_STOP_LEFT_EVENT_MASK >> TMC5041_RAMPSTAT_INT_SHIFT)
|
||||
|
||||
#define TMC5041_DRV_STATUS_STST_BIT BIT(31)
|
||||
#define TMC5041_DRV_STATUS_SG_RESULT_MASK GENMASK(9, 0)
|
||||
#define TMC5041_DRV_STATUS_SG_STATUS_MASK BIT(24)
|
||||
#define TMC5041_DRV_STATUS_SG_STATUS_SHIFT 24
|
||||
|
||||
#define TMC5041_SG_MIN_VALUE -64
|
||||
#define TMC5041_SG_MAX_VALUE 63
|
||||
|
||||
#define TMC5041_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT 16
|
||||
|
||||
#define TMC5041_IHOLD_MASK GENMASK(4, 0)
|
||||
#define TMC5041_IHOLD_SHIFT 0
|
||||
#define TMC5041_IHOLD(n) (((n) << TMC5041_IHOLD_SHIFT) & TMC5041_IHOLD_MASK)
|
||||
|
||||
#define TMC5041_IRUN_MASK GENMASK(12, 8)
|
||||
#define TMC5041_IRUN_SHIFT 8
|
||||
#define TMC5041_IRUN(n) (((n) << TMC5041_IRUN_SHIFT) & TMC5041_IRUN_MASK)
|
||||
|
||||
#define TMC5041_IHOLDDELAY_MASK GENMASK(19, 16)
|
||||
#define TMC5041_IHOLDDELAY_SHIFT 16
|
||||
#define TMC5041_IHOLDDELAY(n) (((n) << TMC5041_IHOLDDELAY_SHIFT) & TMC5041_IHOLDDELAY_MASK)
|
||||
|
||||
#define TMC5041_CHOPCONF_DRV_ENABLE_MASK GENMASK(3, 0)
|
||||
#define TMC5041_CHOPCONF_MRES_MASK GENMASK(27, 24)
|
||||
#define TMC5041_CHOPCONF_MRES_SHIFT 24
|
||||
|
||||
#define TMC5041_CLOCK_FREQ_SHIFT 24
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_REG_H_ */
|
||||
118
drivers/stepper/adi_tmc/adi_tmc_spi.c
Normal file
118
drivers/stepper/adi_tmc/adi_tmc_spi.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include "adi_tmc_spi.h"
|
||||
|
||||
#define BUFFER_SIZE 5U
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(tmc_spi, CONFIG_SPI_LOG_LEVEL);
|
||||
|
||||
static void parse_tmc_spi_status(const uint8_t status_byte)
|
||||
{
|
||||
if ((status_byte & BIT_MASK(0)) != 0) {
|
||||
LOG_WRN("spi dataframe: reset_flag detected");
|
||||
}
|
||||
if ((status_byte & BIT_MASK(1)) != 0) {
|
||||
LOG_WRN("spi dataframe: driver_error(1) detected");
|
||||
}
|
||||
if ((status_byte & BIT_MASK(2)) != 0) {
|
||||
LOG_WRN("spi dataframe: driver_error(2) detected");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_tx_rx_buffer(const uint8_t *const tx_buffer, const uint8_t *const rx_buffer)
|
||||
{
|
||||
LOG_HEXDUMP_DBG(tx_buffer, BUFFER_SIZE, "TX: ");
|
||||
LOG_HEXDUMP_DBG(rx_buffer, BUFFER_SIZE, "RX: ");
|
||||
}
|
||||
|
||||
int tmc_spi_read_register(const struct spi_dt_spec *bus, const uint8_t read_address_mask,
|
||||
const uint8_t register_address, uint32_t *data)
|
||||
{
|
||||
uint8_t tx_buffer[BUFFER_SIZE] = {read_address_mask & register_address, 0U, 0U, 0U, 0U};
|
||||
uint8_t rx_buffer[BUFFER_SIZE];
|
||||
int status;
|
||||
|
||||
const struct spi_buf spi_buffer_tx = {
|
||||
.buf = &tx_buffer,
|
||||
.len = sizeof(tx_buffer),
|
||||
};
|
||||
struct spi_buf_set spi_buffer_array_tx = {
|
||||
.buffers = &spi_buffer_tx,
|
||||
.count = 1U,
|
||||
};
|
||||
|
||||
struct spi_buf spi_buffer_rx = {
|
||||
.buf = &rx_buffer,
|
||||
.len = sizeof(rx_buffer),
|
||||
};
|
||||
struct spi_buf_set spi_buffer_array_rx = {
|
||||
.buffers = &spi_buffer_rx,
|
||||
.count = 1U,
|
||||
};
|
||||
|
||||
/** send read with the address byte */
|
||||
status = spi_transceive_dt(bus, &spi_buffer_array_tx, &spi_buffer_array_rx);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
print_tx_rx_buffer(tx_buffer, rx_buffer);
|
||||
parse_tmc_spi_status(rx_buffer[0]);
|
||||
|
||||
/** read the value from the address */
|
||||
status = spi_transceive_dt(bus, &spi_buffer_array_tx, &spi_buffer_array_rx);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
*data = ((uint32_t)rx_buffer[1] << 24) + ((uint32_t)rx_buffer[2] << 16) +
|
||||
((uint32_t)rx_buffer[3] << 8) + (uint32_t)rx_buffer[4];
|
||||
|
||||
print_tx_rx_buffer(tx_buffer, rx_buffer);
|
||||
parse_tmc_spi_status(rx_buffer[0]);
|
||||
return status;
|
||||
}
|
||||
|
||||
int tmc_spi_write_register(const struct spi_dt_spec *bus, const uint8_t write_bit,
|
||||
const uint8_t register_address, const uint32_t data)
|
||||
{
|
||||
uint8_t tx_buffer[BUFFER_SIZE] = {write_bit | register_address, data >> 24, data >> 16,
|
||||
data >> 8, data};
|
||||
uint8_t rx_buffer[BUFFER_SIZE];
|
||||
int status;
|
||||
|
||||
const struct spi_buf spi_buffer_tx = {
|
||||
.buf = &tx_buffer,
|
||||
.len = sizeof(tx_buffer),
|
||||
};
|
||||
struct spi_buf_set spi_buffer_array_tx = {
|
||||
.buffers = &spi_buffer_tx,
|
||||
.count = 1U,
|
||||
};
|
||||
|
||||
struct spi_buf spi_buffer_rx = {
|
||||
.buf = &rx_buffer,
|
||||
.len = sizeof(rx_buffer),
|
||||
};
|
||||
struct spi_buf_set spi_buffer_array_rx = {
|
||||
.buffers = &spi_buffer_rx,
|
||||
.count = 1U,
|
||||
};
|
||||
|
||||
status = spi_transceive_dt(bus, &spi_buffer_array_tx, &spi_buffer_array_rx);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
print_tx_rx_buffer(tx_buffer, rx_buffer);
|
||||
parse_tmc_spi_status(rx_buffer[0]);
|
||||
|
||||
return status;
|
||||
}
|
||||
63
drivers/stepper/adi_tmc/adi_tmc_spi.h
Normal file
63
drivers/stepper/adi_tmc/adi_tmc_spi.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file drivers/stepper/adi/stepper.h
|
||||
*
|
||||
* @brief Private API for Trinamic SPI bus
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TMC SPI INTERFACE
|
||||
* @ingroup io_priv_interfaces
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/spi.h>
|
||||
|
||||
/**
|
||||
* @brief Read a register from the TMC module using the SPI Bus.
|
||||
*
|
||||
* @param bus SPI DT information of the bus.
|
||||
* @param read_address_mask Address Mask for read operation.
|
||||
* @param register_address Register.
|
||||
* @param data Pointer to read value.
|
||||
*
|
||||
* @return a value from spi_transceive().
|
||||
*/
|
||||
int tmc_spi_read_register(const struct spi_dt_spec *bus, const uint8_t read_address_mask,
|
||||
const uint8_t register_address, uint32_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write into a register in the TMC module using the SPI Bus.
|
||||
*
|
||||
* @param bus SPI DT information of the bus.
|
||||
* @param write_bit Write bit for write operation.
|
||||
* @param register_address Register.
|
||||
* @param data Value to be written in the register.
|
||||
*
|
||||
* @return a value from spi_transceive().
|
||||
*/
|
||||
int tmc_spi_write_register(const struct spi_dt_spec *bus, const uint8_t write_bit,
|
||||
const uint8_t register_address, const uint32_t data);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_ */
|
||||
67
dts/bindings/stepper/adi/adi,tmc5041.yaml
Normal file
67
dts/bindings/stepper/adi/adi,tmc5041.yaml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Analog Devices TMC5041 Stepper Motor Controller
|
||||
|
||||
compatible: "adi,tmc5041"
|
||||
|
||||
include:
|
||||
- name: spi-device.yaml
|
||||
- name: adi,trinamic-gconf.yaml
|
||||
property-allowlist:
|
||||
- poscmp_enable
|
||||
- shaft1
|
||||
- shaft2
|
||||
- test_mode
|
||||
- lock_gconf
|
||||
|
||||
properties:
|
||||
"#address-cells":
|
||||
default: 1
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
default: 0
|
||||
const: 0
|
||||
|
||||
clock-frequency:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The frequency of the clock signal provided to the TMC5041.
|
||||
This is used for real world conversion.
|
||||
|
||||
Hint: µstep velocity v[Hz] µsteps / s v[Hz] = v[5041] * ( fCLK[Hz]/2 / 2^23 )
|
||||
where v[5041] is the value written to the TMC5041.
|
||||
|
||||
child-binding:
|
||||
include:
|
||||
- name: base.yaml
|
||||
property-allowlist:
|
||||
- reg
|
||||
- name: stepper-controller.yaml
|
||||
property-allowlist:
|
||||
- invert-direction
|
||||
- micro-step-res
|
||||
- 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
|
||||
74
dts/bindings/stepper/adi/adi,trinamic-gconf.yaml
Normal file
74
dts/bindings/stepper/adi/adi,trinamic-gconf.yaml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Global configuration flags for Trinamic stepper controller.
|
||||
|
||||
properties:
|
||||
en_spreadcycle:
|
||||
type: boolean
|
||||
description: |
|
||||
A high level on the pin SPREAD inverts this flag to switch between both chopper modes.
|
||||
0: StealthChop mode
|
||||
1: SpreadCycle mode enabled
|
||||
|
||||
i_scale_analog:
|
||||
type: boolean
|
||||
description: |
|
||||
0: Use internal reference derived from 5VOUT
|
||||
1: Use voltage supplied to VREF as current reference
|
||||
|
||||
internal_rsense:
|
||||
type: boolean
|
||||
description: |
|
||||
0: Operation with external sense resistors
|
||||
1: Internal sense resistors. Use current supplied into VREF as reference for internal
|
||||
sense resistor. VREF pin internally is driven to GND in this mode.
|
||||
|
||||
index_otpw:
|
||||
type: boolean
|
||||
description: |
|
||||
0: INDEX shows the first microstep position of sequencer
|
||||
1: INDEX output shows step pulses from internal pulse generator (toggle upon each step)
|
||||
|
||||
index_step:
|
||||
type: boolean
|
||||
description: |
|
||||
0: INDEX output as selected by index_otpw
|
||||
1: INDEX pin shows the current step position of sequencer
|
||||
|
||||
pdn_disable:
|
||||
type: boolean
|
||||
description: |
|
||||
0: Normal operation
|
||||
1: Power down mode
|
||||
|
||||
mstep_reg_select:
|
||||
type: boolean
|
||||
description: |
|
||||
0: Microstep resolution selected by pins MS1, MS2
|
||||
1: Microstep resolution selected by MRES register
|
||||
|
||||
poscmp_enable:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable position compare feature
|
||||
0: Outputs INT and PP are tristated.
|
||||
1: Position compare pulse (PP) and interrupt output (INT) are available
|
||||
|
||||
Attention – do not leave the outputs floating in tristate condition, provide an external
|
||||
pull-up or set poscmp_enable=1
|
||||
|
||||
test_mode:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable test mode
|
||||
0: Normal operation
|
||||
1: Enable analog test output on pin REFR2
|
||||
TEST_SEL selects the function of REFR2: 0…4: T120, DAC1, VDDH1, DAC2, VDDH2
|
||||
|
||||
Attention: Not for user, set to 0 for normal operation!
|
||||
|
||||
lock_gconf:
|
||||
type: boolean
|
||||
description: |
|
||||
1: GCONF is locked against further write access.
|
||||
133
dts/bindings/stepper/adi/adi,trinamic-ramp-generator.yaml
Normal file
133
dts/bindings/stepper/adi/adi,trinamic-ramp-generator.yaml
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Ramp Generator Motion Control Register-Set for Trinamic stepper controller.
|
||||
|
||||
properties:
|
||||
vstart:
|
||||
type: int
|
||||
default: 1
|
||||
description: |
|
||||
Motor start velocity in [µsteps/t](unsigned)
|
||||
|
||||
Normally, set VSTOP ≥ VSTART! VSTART may be
|
||||
set to a higher value, when motion distance is
|
||||
sufficient to allow deceleration to VSTOP.
|
||||
|
||||
a1:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
First acceleration between VSTART and V1 in [µsteps/ta²](unsigned)
|
||||
|
||||
v1:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
First acceleration / deceleration phase threshold velocity in [µsteps/t] (unsigned)
|
||||
|
||||
0: Disables A1 and D1 phase, use AMAX, DMAX only
|
||||
|
||||
amax:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Second acceleration between V1 and VMAX in [µsteps/ta²](unsigned)
|
||||
This is the acceleration and deceleration value
|
||||
for velocity mode.
|
||||
|
||||
vmax:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Motion ramp target velocity in [µsteps/t] (for positioning ensure VMAX ≥ VSTART) (unsigned)
|
||||
This is the target velocity in velocity mode. It can be changed any time during a motion.
|
||||
|
||||
dmax:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Deceleration between VMAX and V1 in [µsteps/ta²](unsigned)
|
||||
|
||||
d1:
|
||||
type: int
|
||||
default: 1
|
||||
description: |
|
||||
Deceleration between V1 and VSTOP in [µsteps/ta²](unsigned)
|
||||
|
||||
Attention: Do not set 0 in positioning mode,
|
||||
even if V1=0!
|
||||
|
||||
vstop:
|
||||
type: int
|
||||
default: 10
|
||||
description: |
|
||||
Motor stop velocity in [µsteps/t] (unsigned)
|
||||
|
||||
Attention: Set VSTOP ≥ VSTART!
|
||||
|
||||
Attention: Do not set 0 in positioning mode,
|
||||
minimum 10 recommended!
|
||||
|
||||
tzerowait:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Waiting time after ramping down to zero velocity before next movement or direction
|
||||
inversion can start and before motor power down starts. Time range is about 0 to 2
|
||||
seconds. This setting avoids excess acceleration e.g. from VSTOP to -VSTART.
|
||||
|
||||
ihold:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Hold current in % of run current (0-100)
|
||||
Standstill current (0=1/32…31=32/32)
|
||||
In combination with StealthChop mode, setting IHOLD=0 allows to choose freewheeling or coil
|
||||
short circuit for motor stand still
|
||||
|
||||
irun:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Motor run current (0=1/32…31=32/32)
|
||||
Hint: Choose sense resistors in a way, that normal
|
||||
IRUN is 16 to 31 for best microstep performance.
|
||||
|
||||
iholddelay:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Controls the number of clock cycles for motor power down after a motion as soon as TZEROWAIT
|
||||
has expired. The smooth transition avoids a motor jerk upon power down.
|
||||
0: instant power down
|
||||
1..15: Delay per current reduction step in multiple of 2^18 clocks
|
||||
|
||||
vcoolthrs:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This is the lower threshold velocity for switching on smart energy CoolStep and StallGuard
|
||||
feature. Further it is the upper operation velocity for StealthChop. (unsigned)
|
||||
|
||||
Set this parameter to disable CoolStep at low speeds, where it cannot work reliably.
|
||||
The stop on stall function (enable with sg_stop when using internal motion controller)
|
||||
becomes enabled when exceeding this velocity. It becomes disabled again once the velocity
|
||||
falls below this threshold. This allows for homing procedures with StallGuard by blanking out
|
||||
the StallGuard signal at low velocities (will not work in combination with StealthChop).
|
||||
VHIGH ≥ |VACT| ≥ VCOOLTHRS:
|
||||
- CoolStep and stop on stall are enabled, if configured
|
||||
- Voltage PWM mode StealthChop is switched off, if configured
|
||||
|
||||
vhigh:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This velocity setting allows velocity dependent switching into a different chopper mode and
|
||||
fullstepping to maximize torque.(unsigned)
|
||||
|VACT| ≥ VHIGH:
|
||||
- CoolStep is disabled (motor runs with normal current scale)
|
||||
- If vhighchm is set, the chopper switches to chm=1 with TFD=0
|
||||
(constant off time with slow decay, only).
|
||||
- If vhighfs is set, the motor operates in fullstep mode.
|
||||
- Voltage PWM mode StealthChop is switched off, if configured
|
||||
36
dts/bindings/stepper/adi/adi,trinamic-stallguard.yaml
Normal file
36
dts/bindings/stepper/adi/adi,trinamic-stallguard.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Stallguard bindings for Trinamic stepper controller.
|
||||
|
||||
properties:
|
||||
activate-stallguard2:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable StallGuard2 feature, if the driver supports it.
|
||||
|
||||
stallguard2-threshold:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This signed value controls StallGuard2 level for stall output and sets the
|
||||
optimum measurement range for readout. A lower value gives a higher sensitivity.
|
||||
Zero is the starting value working with most motors.
|
||||
|
||||
-64 to +63: A higher value makes StallGuard2 less sensitive and requires more torque
|
||||
to indicate a stall.
|
||||
|
||||
stallguard-threshold-velocity:
|
||||
type: int
|
||||
default: 1
|
||||
description: |
|
||||
Threshold velocity for StallGuard2 to detect a stall event.
|
||||
This value should be greater than zero.
|
||||
|
||||
stallguard-velocity-check-interval-ms:
|
||||
type: int
|
||||
default: 100
|
||||
description: |
|
||||
Stallguard should not be enabled during motor spin-up.
|
||||
This delay is used to check if the actual stepper velocity is greater than
|
||||
stallguard-threshold-velocity before enabling stallguard.
|
||||
|
|
@ -6,8 +6,14 @@ description: Stepper Controller
|
|||
include: base.yaml
|
||||
|
||||
properties:
|
||||
invert-direction:
|
||||
type: boolean
|
||||
description: |
|
||||
Invert motor direction.
|
||||
|
||||
micro-step-res:
|
||||
type: int
|
||||
default: 1
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MICRO_STEP_RES_INDEX(res) LOG2(res)
|
||||
|
||||
/**
|
||||
* @brief Stepper Motor micro step resolution options
|
||||
*/
|
||||
|
|
@ -80,6 +82,9 @@ enum stepper_run_mode {
|
|||
enum stepper_signal_result {
|
||||
/** Steps set using move or set_target_position have been executed */
|
||||
STEPPER_SIGNAL_STEPS_COMPLETED = 0,
|
||||
STEPPER_SIGNAL_SENSORLESS_STALL_DETECTED = 1,
|
||||
STEPPER_SIGNAL_LEFT_END_STOP_DETECTED = 2,
|
||||
STEPPER_SIGNAL_RIGHT_END_STOP_DETECTED = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
172
include/zephyr/drivers/stepper/stepper_trinamic.h
Normal file
172
include/zephyr/drivers/stepper/stepper_trinamic.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* @file drivers/stepper/stepper_trinamic.h
|
||||
*
|
||||
* @brief Public API for Trinamic Stepper Controller Specific Functions
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_STEPPER_STEPPER_TRINAMIC_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_STEPPER_STEPPER_TRINAMIC_H_
|
||||
|
||||
/**
|
||||
* @brief Trinamic Stepper Controller Interface
|
||||
* @defgroup trinamic_stepper_interface Trinamic Stepper Controller Interface
|
||||
* @ingroup stepper_interface
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/drivers/stepper.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Trinamic stepper controller ramp generator data limits
|
||||
*/
|
||||
#define TMC_RAMP_VSTART_MAX GENMASK(17, 0)
|
||||
#define TMC_RAMP_VSTART_MIN 0
|
||||
#define TMC_RAMP_V1_MAX GENMASK(19, 0)
|
||||
#define TMC_RAMP_V1_MIN 0
|
||||
#define TMC_RAMP_VMAX_MAX (GENMASK(22, 0) - 512)
|
||||
#define TMC_RAMP_VMAX_MIN 0
|
||||
#define TMC_RAMP_A1_MAX GENMASK(15, 0)
|
||||
#define TMC_RAMP_A1_MIN 0
|
||||
#define TMC_RAMP_AMAX_MAX GENMASK(15, 0)
|
||||
#define TMC_RAMP_AMAX_MIN 0
|
||||
#define TMC_RAMP_D1_MAX GENMASK(15, 0)
|
||||
#define TMC_RAMP_D1_MIN 1
|
||||
#define TMC_RAMP_DMAX_MAX GENMASK(15, 0)
|
||||
#define TMC_RAMP_DMAX_MIN 0
|
||||
#define TMC_RAMP_VSTOP_MAX GENMASK(17, 0)
|
||||
#define TMC_RAMP_VSTOP_MIN 1
|
||||
#define TMC_RAMP_TZEROWAIT_MAX (GENMASK(15, 0) - 512)
|
||||
#define TMC_RAMP_TZEROWAIT_MIN 0
|
||||
#define TMC_RAMP_VCOOLTHRS_MAX GENMASK(22, 0)
|
||||
#define TMC_RAMP_VCOOLTHRS_MIN 0
|
||||
#define TMC_RAMP_VHIGH_MAX GENMASK(22, 0)
|
||||
#define TMC_RAMP_VHIGH_MIN 0
|
||||
#define TMC_RAMP_IHOLD_IRUN_MAX GENMASK(4, 0)
|
||||
#define TMC_RAMP_IHOLD_IRUN_MIN 0
|
||||
#define TMC_RAMP_IHOLDDELAY_MAX GENMASK(3, 0)
|
||||
#define TMC_RAMP_IHOLDDELAY_MIN 0
|
||||
#define TMC_RAMP_VACTUAL_SHIFT 22
|
||||
/**
|
||||
* @brief Trinamic Stepper Ramp Generator data
|
||||
*/
|
||||
struct tmc_ramp_generator_data {
|
||||
uint32_t vstart;
|
||||
uint32_t v1;
|
||||
uint32_t vmax;
|
||||
uint16_t a1;
|
||||
uint16_t amax;
|
||||
uint16_t d1;
|
||||
uint16_t dmax;
|
||||
uint32_t vstop;
|
||||
uint16_t tzerowait;
|
||||
uint32_t vcoolthrs;
|
||||
uint32_t vhigh;
|
||||
uint32_t iholdrun;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check if Ramp DT data is within limits
|
||||
*/
|
||||
#define CHECK_RAMP_DT_DATA(node) \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, vstart), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, vstart), TMC_RAMP_VSTART_MIN, \
|
||||
TMC_RAMP_VSTART_MAX), "vstart out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, v1), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, v1), TMC_RAMP_V1_MIN, \
|
||||
TMC_RAMP_V1_MAX), "v1 out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, vmax), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, vmax), TMC_RAMP_VMAX_MIN, \
|
||||
TMC_RAMP_VMAX_MAX), "vmax out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, a1), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, a1), TMC_RAMP_A1_MIN, \
|
||||
TMC_RAMP_A1_MAX), "a1 out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, amax), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, amax), TMC_RAMP_AMAX_MIN, \
|
||||
TMC_RAMP_AMAX_MAX), "amax out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, d1), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, d1), TMC_RAMP_D1_MIN, \
|
||||
TMC_RAMP_D1_MAX), "d1 out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, dmax), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, dmax), TMC_RAMP_DMAX_MIN, \
|
||||
TMC_RAMP_DMAX_MAX), "dmax out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, vstop), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, vstop), TMC_RAMP_VSTOP_MIN, \
|
||||
TMC_RAMP_VSTOP_MAX), "vstop out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, tzerowait), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, tzerowait), TMC_RAMP_TZEROWAIT_MIN, \
|
||||
TMC_RAMP_TZEROWAIT_MAX), "tzerowait out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, vcoolthrs), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, vcoolthrs), TMC_RAMP_VCOOLTHRS_MIN, \
|
||||
TMC_RAMP_VCOOLTHRS_MAX), "vcoolthrs out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, vhigh), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, vhigh), TMC_RAMP_VHIGH_MIN, \
|
||||
TMC_RAMP_VHIGH_MAX), "vhigh out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, ihold), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, ihold), TMC_RAMP_IHOLD_IRUN_MIN, \
|
||||
TMC_RAMP_IHOLD_IRUN_MAX), "ihold out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, irun), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, irun), TMC_RAMP_IHOLD_IRUN_MIN, \
|
||||
TMC_RAMP_IHOLD_IRUN_MAX), "irun out of range"), ()); \
|
||||
COND_CODE_1(DT_PROP_EXISTS(node, iholddelay), \
|
||||
BUILD_ASSERT(IN_RANGE(DT_PROP(node, iholddelay), TMC_RAMP_IHOLDDELAY_MIN, \
|
||||
TMC_RAMP_IHOLDDELAY_MAX), "iholddelay out of range"), ());
|
||||
|
||||
/**
|
||||
* @brief Get Trinamic Stepper Ramp Generator data from DT
|
||||
*
|
||||
* @param node DT node identifier
|
||||
*
|
||||
* @return struct tmc_ramp_generator_data
|
||||
*/
|
||||
#define TMC_RAMP_DT_SPEC_GET(node) \
|
||||
{ \
|
||||
.vstart = DT_PROP(node, vstart), \
|
||||
.v1 = DT_PROP(node, v1), \
|
||||
.vmax = DT_PROP(node, vmax), \
|
||||
.a1 = DT_PROP(node, a1), \
|
||||
.amax = DT_PROP(node, amax), \
|
||||
.d1 = DT_PROP(node, d1), \
|
||||
.dmax = DT_PROP(node, dmax), \
|
||||
.vstop = DT_PROP(node, vstop), \
|
||||
.tzerowait = DT_PROP(node, tzerowait), \
|
||||
.vcoolthrs = DT_PROP(node, vcoolthrs), \
|
||||
.vhigh = DT_PROP(node, vhigh), \
|
||||
.iholdrun = (TMC5041_IRUN(DT_PROP(node, irun)) | \
|
||||
TMC5041_IHOLD(DT_PROP(node, ihold)) | \
|
||||
TMC5041_IHOLDDELAY(DT_PROP(node, iholddelay))), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure Trinamic Stepper Ramp Generator
|
||||
*
|
||||
* @param dev Pointer to the stepper motor controller instance
|
||||
* @param ramp_data Pointer to a struct containing the required ramp parameters
|
||||
*
|
||||
* @retval -EIO General input / output error
|
||||
* @retval -ENOSYS If not implemented by device driver
|
||||
* @retval 0 Success
|
||||
*/
|
||||
int tmc5041_stepper_set_ramp(const struct device *dev,
|
||||
const struct tmc_ramp_generator_data *ramp_data);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_STEPPER_STEPPER_TRINAMIC_H_ */
|
||||
Loading…
Reference in a new issue