drivers: stepper: adi_tmc: Add tmc51xx support
Add tmc51xx support based on tmc50xx implementation. Signed-off-by: Anders Nielsen <anders.nielsen@prevas.dk>
This commit is contained in:
parent
242e6ea12a
commit
b98bd7c145
6 changed files with 967 additions and 2 deletions
|
@ -7,3 +7,4 @@ zephyr_library_property(ALLOW_EMPTY TRUE)
|
|||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC2209 adi_tmc22xx_stepper_controller.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX adi_tmc50xx_stepper_controller.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX adi_tmc51xx_stepper_controller.c)
|
||||
|
|
690
drivers/stepper/adi_tmc/adi_tmc51xx_stepper_controller.c
Normal file
690
drivers/stepper/adi_tmc/adi_tmc51xx_stepper_controller.c
Normal file
|
@ -0,0 +1,690 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT adi_tmc51xx
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/drivers/stepper.h>
|
||||
#include <zephyr/drivers/stepper/stepper_trinamic.h>
|
||||
|
||||
#include "adi_tmc_spi.h"
|
||||
#include "adi_tmc5xxx_common.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(tmc51xx, CONFIG_STEPPER_LOG_LEVEL);
|
||||
|
||||
struct tmc51xx_data {
|
||||
struct k_sem sem;
|
||||
struct k_work_delayable stallguard_dwork;
|
||||
/* Work item to run the callback in a thread context. */
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL
|
||||
struct k_work_delayable rampstat_callback_dwork;
|
||||
#endif
|
||||
/* device pointer required to access config in k_work */
|
||||
const struct device *stepper;
|
||||
stepper_event_callback_t callback;
|
||||
void *event_cb_user_data;
|
||||
};
|
||||
|
||||
struct tmc51xx_config {
|
||||
const uint32_t gconf;
|
||||
struct spi_dt_spec spi;
|
||||
const uint32_t clock_frequency;
|
||||
const uint16_t default_micro_step_res;
|
||||
const int8_t sg_threshold;
|
||||
const bool is_sg_enabled;
|
||||
const uint32_t sg_velocity_check_interval_ms;
|
||||
const uint32_t sg_threshold_velocity;
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN
|
||||
const struct tmc_ramp_generator_data default_ramp_config;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int tmc51xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val)
|
||||
{
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
const struct spi_dt_spec bus = config->spi;
|
||||
int err;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
err = tmc_spi_write_register(&bus, TMC5XXX_WRITE_BIT, reg_addr, reg_val);
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val)
|
||||
{
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
const struct spi_dt_spec bus = config->spi;
|
||||
int err;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
err = tmc_spi_read_register(&bus, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val);
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to read register 0x%x", reg_addr);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_set_event_callback(const struct device *dev,
|
||||
stepper_event_callback_t callback, void *user_data)
|
||||
{
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
|
||||
data->callback = callback;
|
||||
data->event_cb_user_data = user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stallguard_enable(const struct device *dev, const bool enable)
|
||||
{
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_SWMODE, ®_value);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to read SWMODE register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE;
|
||||
|
||||
int32_t actual_velocity;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_VACTUAL,
|
||||
&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 &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_SWMODE, 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 tmc51xx_data *stepper_data =
|
||||
CONTAINER_OF(dwork, struct tmc51xx_data, stallguard_dwork);
|
||||
const struct device *dev = stepper_data->stepper;
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
err = stallguard_enable(dev, true);
|
||||
if (err == -EAGAIN) {
|
||||
LOG_ERR("retrying stallguard activation");
|
||||
k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
if (err == -EIO) {
|
||||
LOG_ERR("Failed to enable stallguard because of I/O error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL
|
||||
|
||||
static void execute_callback(const struct device *dev, const enum stepper_event event)
|
||||
{
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
|
||||
if (!data->callback) {
|
||||
LOG_WRN_ONCE("No callback registered");
|
||||
return;
|
||||
}
|
||||
data->callback(dev, event, data->event_cb_user_data);
|
||||
}
|
||||
|
||||
static void rampstat_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
|
||||
struct tmc51xx_data *stepper_data =
|
||||
CONTAINER_OF(dwork, struct tmc51xx_data, rampstat_callback_dwork);
|
||||
const struct device *dev = stepper_data->stepper;
|
||||
|
||||
__ASSERT_NO_MSG(dev != NULL);
|
||||
|
||||
uint32_t drv_status;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_DRVSTATUS,
|
||||
&drv_status);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) {
|
||||
LOG_INF("%s: Stall detected", dev->name);
|
||||
err = tmc51xx_write(dev,
|
||||
TMC51XX_RAMPMODE,
|
||||
TMC5XXX_RAMPMODE_HOLD_MODE);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to stop motor", dev->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rampstat_value;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_RAMPSTAT,
|
||||
&rampstat_value);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to read RAMPSTAT register", dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value);
|
||||
|
||||
if (ramp_stat_values > 0) {
|
||||
switch (ramp_stat_values) {
|
||||
|
||||
case TMC5XXX_STOP_LEFT_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Left end-stop detected", dev->name);
|
||||
execute_callback(dev,
|
||||
STEPPER_EVENT_LEFT_END_STOP_DETECTED);
|
||||
break;
|
||||
|
||||
case TMC5XXX_STOP_RIGHT_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Right end-stop detected", dev->name);
|
||||
execute_callback(dev,
|
||||
STEPPER_EVENT_RIGHT_END_STOP_DETECTED);
|
||||
break;
|
||||
|
||||
case TMC5XXX_POS_REACHED_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Position reached", dev->name);
|
||||
execute_callback(dev, STEPPER_EVENT_STEPS_COMPLETED);
|
||||
break;
|
||||
|
||||
case TMC5XXX_STOP_SG_EVENT:
|
||||
LOG_DBG("RAMPSTAT %s:Stall detected", dev->name);
|
||||
stallguard_enable(dev, false);
|
||||
execute_callback(dev, STEPPER_EVENT_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_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int tmc51xx_stepper_enable(const struct device *dev)
|
||||
{
|
||||
LOG_DBG("Enabling Stepper motor controller %s", dev->name);
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK;
|
||||
|
||||
return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value);
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_disable(const struct device *dev)
|
||||
{
|
||||
LOG_DBG("Disabling Stepper motor controller %s", dev->name);
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK;
|
||||
|
||||
return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value);
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_is_moving(const struct device *dev, bool *is_moving)
|
||||
{
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_DRVSTATUS, ®_value);
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) == 1U);
|
||||
LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tmc51xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity)
|
||||
{
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
const uint32_t clock_frequency = config->clock_frequency;
|
||||
uint32_t velocity_fclk;
|
||||
int err;
|
||||
|
||||
velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency);
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_VMAX, velocity_fclk);
|
||||
if (err != 0) {
|
||||
LOG_ERR("%s: Failed to set max velocity", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_set_micro_step_res(const struct device *dev,
|
||||
enum stepper_micro_step_resolution res)
|
||||
{
|
||||
if (!VALID_MICRO_STEP_RES(res)) {
|
||||
LOG_ERR("Invalid micro step resolution %d", res);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK;
|
||||
reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res))
|
||||
<< TMC5XXX_CHOPCONF_MRES_SHIFT);
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_DBG("Stepper motor controller %s set micro step resolution to 0x%x", dev->name,
|
||||
reg_value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_get_micro_step_res(const struct device *dev,
|
||||
enum stepper_micro_step_resolution *res)
|
||||
{
|
||||
uint32_t reg_value;
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
reg_value &= TMC5XXX_CHOPCONF_MRES_MASK;
|
||||
reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT;
|
||||
*res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value));
|
||||
LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_set_reference_position(const struct device *dev, const int32_t position)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_RAMPMODE,
|
||||
TMC5XXX_RAMPMODE_HOLD_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_XACTUAL, position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_get_actual_position(const struct device *dev, int32_t *position)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tmc51xx_read(dev, TMC51XX_XACTUAL, position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
LOG_DBG("%s actual position: %d", dev->name, *position);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_move_to(const struct device *dev, const int32_t micro_steps)
|
||||
{
|
||||
LOG_DBG("%s set target position to %d", dev->name, micro_steps);
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
int err;
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
stallguard_enable(dev, false);
|
||||
}
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_RAMPMODE,
|
||||
TMC5XXX_RAMPMODE_POSITIONING_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_XTARGET, micro_steps);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
k_work_reschedule(&data->stallguard_dwork,
|
||||
K_MSEC(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL
|
||||
if (data->callback) {
|
||||
k_work_reschedule(
|
||||
&data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_move_by(const struct device *dev, const int32_t micro_steps)
|
||||
{
|
||||
int err;
|
||||
int32_t position;
|
||||
|
||||
err = stepper_get_actual_position(dev, &position);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
int32_t target_position = position + micro_steps;
|
||||
|
||||
LOG_DBG("%s moved to %d by steps: %d", dev->name, target_position, micro_steps);
|
||||
|
||||
return tmc51xx_stepper_move_to(dev, target_position);
|
||||
}
|
||||
|
||||
static int tmc51xx_stepper_run(const struct device *dev, const enum stepper_direction direction)
|
||||
{
|
||||
LOG_DBG("Stepper motor controller %s run", dev->name);
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
int err;
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
err = stallguard_enable(dev, false);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case STEPPER_DIRECTION_POSITIVE:
|
||||
err = tmc51xx_write(dev, TMC51XX_RAMPMODE,
|
||||
TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case STEPPER_DIRECTION_NEGATIVE:
|
||||
err = tmc51xx_write(dev, TMC51XX_RAMPMODE,
|
||||
TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
k_work_reschedule(&data->stallguard_dwork,
|
||||
K_MSEC(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL
|
||||
if (data->callback) {
|
||||
k_work_reschedule(
|
||||
&data->rampstat_callback_dwork,
|
||||
K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN
|
||||
|
||||
int tmc51xx_stepper_set_ramp(const struct device *dev,
|
||||
const struct tmc_ramp_generator_data *ramp_data)
|
||||
{
|
||||
LOG_DBG("Stepper motor controller %s set ramp", dev->name);
|
||||
int err;
|
||||
|
||||
err = tmc51xx_write(dev, TMC51XX_VSTART, ramp_data->vstart);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_A1, ramp_data->a1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_AMAX, ramp_data->amax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_D1, ramp_data->d1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_DMAX, ramp_data->dmax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_V1, ramp_data->v1);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_VMAX, ramp_data->vmax);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_VSTOP, ramp_data->vstop);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_TZEROWAIT,
|
||||
ramp_data->tzerowait);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_THIGH, ramp_data->thigh);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_TCOOLTHRS, ramp_data->tcoolthrs);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_TPWMTHRS, ramp_data->tpwmthrs);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_TPOWER_DOWN, ramp_data->tpowerdown);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
err = tmc51xx_write(dev, TMC51XX_IHOLD_IRUN, ramp_data->iholdrun);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int tmc51xx_init(const struct device *dev)
|
||||
{
|
||||
LOG_DBG("TMC51XX stepper motor controller %s initialized", dev->name);
|
||||
struct tmc51xx_data *data = dev->data;
|
||||
const struct tmc51xx_config *config = dev->config;
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_DBG("GCONF: %d", config->gconf);
|
||||
err = tmc51xx_write(dev, TMC5XXX_GCONF, config->gconf);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read and write GSTAT register to clear any SPI Datagram errors. */
|
||||
uint32_t gstat_value;
|
||||
|
||||
err = tmc51xx_read(dev, TMC5XXX_GSTAT, &gstat_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = tmc51xx_write(dev, TMC5XXX_GSTAT, gstat_value);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (config->is_sg_enabled) {
|
||||
k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler);
|
||||
|
||||
err = tmc51xx_write(dev,
|
||||
TMC51XX_SWMODE, BIT(10));
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_DBG("Setting stall guard to %d with delay %d ms", config->sg_threshold,
|
||||
config->sg_velocity_check_interval_ms);
|
||||
if (!IN_RANGE(config->sg_threshold, TMC5XXX_SG_MIN_VALUE,
|
||||
TMC5XXX_SG_MAX_VALUE)) {
|
||||
LOG_ERR("Stallguard threshold out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32_t stall_guard_threshold = (int32_t)config->sg_threshold;
|
||||
|
||||
err = tmc51xx_write(
|
||||
dev, TMC51XX_COOLCONF,
|
||||
stall_guard_threshold << TMC5XXX_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(config->sg_velocity_check_interval_ms));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN
|
||||
err = tmc51xx_stepper_set_ramp(dev, &config->default_ramp_config);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_STEPPER_ADI_TMC51XX_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_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC));
|
||||
#endif
|
||||
err = tmc51xx_stepper_set_micro_step_res(dev, config->default_micro_step_res);
|
||||
if (err != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_API(stepper, tmc51xx_api) = {
|
||||
.enable = tmc51xx_stepper_enable,
|
||||
.disable = tmc51xx_stepper_disable,
|
||||
.is_moving = tmc51xx_stepper_is_moving,
|
||||
.move_by = tmc51xx_stepper_move_by,
|
||||
.set_micro_step_res = tmc51xx_stepper_set_micro_step_res,
|
||||
.get_micro_step_res = tmc51xx_stepper_get_micro_step_res,
|
||||
.set_reference_position = tmc51xx_stepper_set_reference_position,
|
||||
.get_actual_position = tmc51xx_stepper_get_actual_position,
|
||||
.move_to = tmc51xx_stepper_move_to,
|
||||
.run = tmc51xx_stepper_run,
|
||||
.set_event_callback = tmc51xx_stepper_set_event_callback,
|
||||
};
|
||||
|
||||
#define TMC51XX_DEFINE(inst) \
|
||||
BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \
|
||||
"clock frequency must be non-zero positive value"); \
|
||||
static struct tmc51xx_data tmc51xx_data_##inst = { \
|
||||
.stepper = DEVICE_DT_GET(DT_DRV_INST(inst))}; \
|
||||
COND_CODE_1(DT_PROP_EXISTS(inst, stallguard_threshold_velocity), \
|
||||
BUILD_ASSERT(DT_PROP(inst, stallguard_threshold_velocity), \
|
||||
"stallguard threshold velocity must be a positive value"), ()); \
|
||||
IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(inst))); \
|
||||
static const struct tmc51xx_config tmc51xx_config_##inst = { \
|
||||
.gconf = ( \
|
||||
(DT_INST_PROP(inst, en_pwm_mode) << TMC51XX_GCONF_EN_PWM_MODE_SHIFT) | \
|
||||
(DT_INST_PROP(inst, test_mode) << TMC51XX_GCONF_TEST_MODE_SHIFT) | \
|
||||
(DT_INST_PROP(inst, invert_direction) << TMC51XX_GCONF_SHAFT_SHIFT)), \
|
||||
.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), \
|
||||
.default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \
|
||||
.sg_threshold = DT_INST_PROP(inst, stallguard2_threshold), \
|
||||
.sg_threshold_velocity = DT_INST_PROP(inst, stallguard_threshold_velocity), \
|
||||
.sg_velocity_check_interval_ms = DT_INST_PROP(inst, \
|
||||
stallguard_velocity_check_interval_ms), \
|
||||
.is_sg_enabled = DT_INST_PROP(inst, activate_stallguard2), \
|
||||
IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, \
|
||||
(.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC51XX(inst)))}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, tmc51xx_init, NULL, &tmc51xx_data_##inst, \
|
||||
&tmc51xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,\
|
||||
&tmc51xx_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TMC51XX_DEFINE)
|
108
dts/bindings/stepper/adi/adi,tmc51xx.yaml
Normal file
108
dts/bindings/stepper/adi/adi,tmc51xx.yaml
Normal file
|
@ -0,0 +1,108 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Analog Devices TMC51XX Stepper Motor Controller
|
||||
|
||||
Example:
|
||||
|
||||
&spi0 {
|
||||
/* SPI bus options here, not shown */
|
||||
|
||||
/* Controller/driver for one 2-phase bipolar stepper motor */
|
||||
tmc51xx: tmc51xx@0 {
|
||||
compatible = "adi,tmc51xx";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <DT_FREQ_M(8)>; /* Maximum SPI bus frequency */
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
en-pwm-mode; test-mode; /* ADI TMC Global configuration flags */
|
||||
clock-frequency = <DT_FREQ_M(16)>; /* Internal/External Clock frequency */
|
||||
|
||||
/* common stepper controller settings */
|
||||
invert-direction;
|
||||
micro-step-res = <256>;
|
||||
|
||||
/* ADI TMC stallguard settings specific to TMC51XX */
|
||||
activate-stallguard2;
|
||||
stallguard-velocity-check-interval-ms=<100>;
|
||||
stallguard2-threshold=<9>;
|
||||
stallguard-threshold-velocity=<500000>;
|
||||
|
||||
/* ADI TMC ramp generator as well as current settings */
|
||||
vstart = <10>;
|
||||
a1 = <20>;
|
||||
v1 = <30>;
|
||||
d1 = <40>;
|
||||
vmax = <50>;
|
||||
amax = <60>;
|
||||
dmax = <70>;
|
||||
tzerowait = <80>;
|
||||
thigh = <90>;
|
||||
tcoolthrs = <100>;
|
||||
tpwmthrs = <110>;
|
||||
tpowerdown = <120>;
|
||||
ihold = <1>;
|
||||
irun = <2>;
|
||||
iholddelay = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
compatible: "adi,tmc51xx"
|
||||
|
||||
include:
|
||||
- name: spi-device.yaml
|
||||
- name: adi,trinamic-gconf.yaml
|
||||
property-allowlist:
|
||||
- en-pwm-mode
|
||||
- test-mode
|
||||
- name: stepper-controller.yaml
|
||||
- name: base.yaml
|
||||
property-allowlist:
|
||||
- reg
|
||||
- name: adi,trinamic-ramp-generator.yaml
|
||||
property-allowlist:
|
||||
- vstart
|
||||
- a1
|
||||
- v1
|
||||
- amax
|
||||
- vmax
|
||||
- dmax
|
||||
- d1
|
||||
- vstop
|
||||
- tzerowait
|
||||
- thigh
|
||||
- tcoolthrs
|
||||
- tpwmthrs
|
||||
- tpowerdown
|
||||
- ihold
|
||||
- irun
|
||||
- iholddelay
|
||||
- name: adi,trinamic-stallguard.yaml
|
||||
property-allowlist:
|
||||
- activate-stallguard2
|
||||
- stallguard2-threshold
|
||||
- stallguard-threshold-velocity
|
||||
- stallguard-velocity-check-interval-ms
|
||||
|
||||
properties:
|
||||
"#address-cells":
|
||||
default: 1
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
default: 0
|
||||
const: 0
|
||||
|
||||
clock-frequency:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The frequency of the clock signal provided to the TMC51XX.
|
||||
This is used for real world conversion.
|
||||
|
||||
Hint: µstep velocity v[Hz] µsteps / s v[Hz] = v[51xx] * ( fCLK[Hz]/2 / 2^23 )
|
||||
where v[51xx] is the value written to the TMC51XX.
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Global configuration flags for Trinamic stepper controller.
|
||||
|
@ -72,3 +73,11 @@ properties:
|
|||
type: boolean
|
||||
description: |
|
||||
1: GCONF is locked against further write access.
|
||||
|
||||
en-pwm-mode:
|
||||
type: boolean
|
||||
description: |
|
||||
1: StealthChop voltage PWM mode enabled
|
||||
(depending on velocity thresholds). Switch from
|
||||
off to on state while in stand-still and at IHOLD=
|
||||
nominal IRUN current, only.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Ramp Generator Motion Control Register-Set for Trinamic stepper controller.
|
||||
|
@ -131,3 +132,63 @@ properties:
|
|||
(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
|
||||
|
||||
tcoolthrs:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This is the lower threshold velocity for switching on smart
|
||||
energy CoolStep and StallGuard feature. (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) and the stall
|
||||
output signal become enabled when exceeding this velocity. In
|
||||
non-DcStep mode, it becomes disabled again once the velocity
|
||||
falls below this threshold.
|
||||
TCOOLTHRS ≥ TSTEP ≥ THIGH:
|
||||
- CoolStep is enabled, if configured
|
||||
- StealthChop voltage PWM mode is disabled
|
||||
TCOOLTHRS ≥ TSTEP
|
||||
- Stop on stall is enabled, if configured
|
||||
- Stall output signal (DIAG0/1) is enabled, if configured
|
||||
|
||||
thigh:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This velocity setting allows velocity dependent switching into
|
||||
a different chopper mode and fullstepping to maximize torque.
|
||||
(unsigned)
|
||||
The stall detection feature becomes switched off for 2-3
|
||||
electrical periods whenever passing THIGH threshold to
|
||||
compensate for the effect of switching modes.
|
||||
TSTEP ≤ THIGH:
|
||||
- CoolStep is disabled (motor runs with normal current
|
||||
scale)
|
||||
- StealthChop voltage PWM mode is disabled
|
||||
- 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,
|
||||
and the stall detection becomes switched over to
|
||||
DcStep stall detection.
|
||||
|
||||
tpwmthrs:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
This is the upper velocity for StealthChop voltage PWM mode.
|
||||
TSTEP ≥ TPWMTHRS
|
||||
- StealthChop PWM mode is enabled, if configured
|
||||
- DcStep is disabled
|
||||
|
||||
tpowerdown:
|
||||
type: int
|
||||
default: 10
|
||||
description: |
|
||||
TPOWERDOWN sets the delay time after stand still (stst) of the
|
||||
motor to motor current power down. Time range is about 0 to
|
||||
4 seconds.
|
||||
Attention: A minimum setting of 2 is required to allow
|
||||
automatic tuning of StealthChop PWM_OFS_AUTO.
|
||||
Reset Default = 10
|
||||
0…((2^8)-1) * 2^18 tCLK
|
||||
|
|
|
@ -8,19 +8,115 @@
|
|||
|
||||
adi_tmc50xx: adi_tmc50xx@0 {
|
||||
compatible = "adi,tmc50xx";
|
||||
status = "okay";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
label = "tmc5041_0";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <1>;
|
||||
|
||||
poscmp-enable; test-mode; lock-gconf; /* ADI TMC Global configuration flags */
|
||||
clock-frequency = <16000000>; /* Internal/External Clock frequency */
|
||||
|
||||
tmc50xx_0: tmc50xx_0@0 {
|
||||
status = "okay";
|
||||
reg = <0>;
|
||||
|
||||
/* common stepper controller settings */
|
||||
invert-direction;
|
||||
micro-step-res = <256>;
|
||||
|
||||
/* ADI TMC stallguard settings specific to TMC50XX */
|
||||
activate-stallguard2;
|
||||
stallguard-velocity-check-interval-ms=<100>;
|
||||
stallguard2-threshold=<9>;
|
||||
stallguard-threshold-velocity=<500000>;
|
||||
|
||||
/* ADI TMC ramp generator as well as current settings */
|
||||
vstart = <10>;
|
||||
a1 = <20>;
|
||||
v1 = <30>;
|
||||
d1 = <40>;
|
||||
vmax = <50>;
|
||||
amax = <60>;
|
||||
dmax = <70>;
|
||||
tzerowait = <80>;
|
||||
vhigh = <90>;
|
||||
vcoolthrs = <100>;
|
||||
ihold = <1>;
|
||||
irun = <2>;
|
||||
iholddelay = <3>;
|
||||
};
|
||||
|
||||
tmc50xx_1: tmc50xx_1@1 {
|
||||
status = "okay";
|
||||
reg = <1>;
|
||||
|
||||
/* common stepper controller settings */
|
||||
invert-direction;
|
||||
micro-step-res = <256>;
|
||||
|
||||
/* ADI TMC stallguard settings specific to TMC50XX */
|
||||
activate-stallguard2;
|
||||
stallguard-velocity-check-interval-ms=<100>;
|
||||
stallguard2-threshold=<9>;
|
||||
stallguard-threshold-velocity=<500000>;
|
||||
|
||||
/* ADI TMC ramp generator as well as current settings */
|
||||
vstart = <10>;
|
||||
a1 = <20>;
|
||||
v1 = <30>;
|
||||
d1 = <40>;
|
||||
vmax = <50>;
|
||||
amax = <60>;
|
||||
dmax = <70>;
|
||||
tzerowait = <80>;
|
||||
vhigh = <90>;
|
||||
vcoolthrs = <100>;
|
||||
ihold = <1>;
|
||||
irun = <2>;
|
||||
iholddelay = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
adi_tmc51xx: adi_tmc51xx@1 {
|
||||
compatible = "adi,tmc51xx";
|
||||
status = "okay";
|
||||
reg = <0x01>;
|
||||
spi-max-frequency = <8000000>;
|
||||
label = "tmc5160_1";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
en-pwm-mode; test-mode; /* ADI TMC Global configuration flags */
|
||||
clock-frequency = <16000000>; /* Internal/External Clock frequency */
|
||||
|
||||
/* common stepper controller settings */
|
||||
invert-direction;
|
||||
micro-step-res = <256>;
|
||||
|
||||
/* ADI TMC stallguard settings specific to TMC5160 */
|
||||
activate-stallguard2;
|
||||
stallguard-velocity-check-interval-ms=<100>;
|
||||
stallguard2-threshold=<9>;
|
||||
stallguard-threshold-velocity=<50000>;
|
||||
|
||||
/* ADI TMC ramp generator as well as current settings */
|
||||
vstart = <10>;
|
||||
a1 = <20>;
|
||||
v1 = <30>;
|
||||
d1 = <40>;
|
||||
vmax = <50>;
|
||||
amax = <60>;
|
||||
dmax = <70>;
|
||||
tzerowait = <80>;
|
||||
thigh = <90>;
|
||||
tcoolthrs = <100>;
|
||||
tpwmthrs = <110>;
|
||||
tpowerdown = <120>;
|
||||
ihold = <1>;
|
||||
irun = <2>;
|
||||
iholddelay = <3>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue