-
Notifications
You must be signed in to change notification settings - Fork 7.7k
drivers: stepper: ADI TMC2130 Stepper Driver #90677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c9ecb8c
6efc973
3779214
c41b3de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Copyright (c) 2025 Navimatix GmbH | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config SHIELD_MIKROE_SILENT_STEP_2_CLICK | ||
def_bool $(shields_list_contains,mikroe_silent_step_2_click) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
.. _mikroe_silent_step_2_click: | ||
|
||
MikroElektronika Silent Step 2 Click | ||
#################################### | ||
|
||
Overview | ||
******** | ||
|
||
The MikroElektronika `Silent Step 2 Click`_ features the `ADI TMC2130`_ stepper driver with step-dir | ||
control via gpio and configuration via spi. A `NXP PCA9538A`_ gpio expander accessed via i2c offers | ||
access to additional pins of th stepper driver. | ||
|
||
.. figure:: silent_step_2_click.webp | ||
:align: center | ||
:alt: MikroElektronika Silent Step 2 Click | ||
|
||
jbehrensnx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MikroElektronika Silent Step 2 Click (Credit: MikroElektronika) | ||
|
||
Requirements | ||
************ | ||
|
||
This shield can only be used with a board that provides a mikroBUS | ||
socket and defines a ``mikrobus_i2c`` node label for the mikroBUS I2C | ||
interface, a ``mikrobus_spi`` node label for the mikroBUS SPI | ||
interface and a ``mikrobus_header`` node label (see :ref:`shields` for more details). | ||
|
||
Programming | ||
*********** | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/drivers/stepper/generic/ | ||
:board: <board> | ||
:shield: mikroe_silent_step_2_click | ||
:goals: build | ||
|
||
.. _Silent Step 2 Click: | ||
https://www.mikroe.com/silent-step-2-click | ||
|
||
.. _ADI TMC2130: | ||
https://www.analog.com/en/products/tmc2130.html | ||
|
||
.. _NXP PCA9538A: | ||
https://www.nxp.com/products/interfaces/ic-spi-i3c-interface-devices/general-purpose-i-o-gpio/low-voltage-8-bit-ic-bus-i-o-port-with-interrupt-and-reset:PCA9538A |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright (c) 2025 Navimatix GmbH | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
&mikrobus_i2c { | ||
status = "okay"; | ||
pca9538a_mikroe_silent_step_2_click: pca9538a@70 { | ||
status = "okay"; | ||
compatible = "nxp,pca9538"; | ||
|
||
reg = <0x70>; | ||
|
||
gpio-controller; | ||
ngpios = <8>; | ||
#gpio-cells = <2>; | ||
|
||
gpio-reserved-ranges = <3 1>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting gpio-reserved-ranges on nxp,pca9538 has no effect - the driver does not handle this. |
||
|
||
gpio-line-names = | ||
"EN", | ||
"FT1", | ||
"FT2"; | ||
}; | ||
}; | ||
|
||
&mikrobus_spi { | ||
cs-gpios = <&mikrobus_header 2 GPIO_ACTIVE_LOW>; | ||
status = "okay"; | ||
tmc2130_mikroe_silent_step_2_click: tmc2130@0 { | ||
compatible = "adi,tmc2130"; | ||
reg = <0>; | ||
spi-max-frequency = <DT_FREQ_M(1)>; | ||
|
||
dir-gpios = <&mikrobus_header 0 0>; | ||
step-gpios = <&mikrobus_header 6 0>; | ||
en-gpios = <&pca9538a_mikroe_silent_step_2_click 0 GPIO_ACTIVE_LOW>; | ||
irun = <20>; | ||
ihold = <20>; | ||
en-pwm-mode; | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
shield: | ||
name: mikroe_silent_step_2_click | ||
full_name: Silent Step 2 Click | ||
vendor: mikroe | ||
supported_features: | ||
- stepper |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# SPDX-FileCopyrightText: Copyright (c) 2025 Navimatix GmbH | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config STEPPER_ADI_TMC2130 | ||
bool "Activate trinamic tmc2209 stepper driver" | ||
depends on DT_HAS_ADI_TMC2130_ENABLED | ||
select STEP_DIR_STEPPER | ||
select STEPPER_ADI_TMC_SPI | ||
default y | ||
help | ||
Stepper driver for TMC2130, using the step-dir interface with spi configuration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
/* | ||
* SPDX-FileCopyrightText: Copyright (c) 2025 Navimatix GmbH | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define DT_DRV_COMPAT adi_tmc2130 | ||
|
||
#include <zephyr/kernel.h> | ||
jilaypandya marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include <zephyr/drivers/gpio.h> | ||
#include <zephyr/drivers/spi.h> | ||
#include <zephyr/drivers/stepper.h> | ||
#include "../step_dir/step_dir_stepper_common.h" | ||
#include "tmc2130_reg.h" | ||
#include "adi_tmc_spi.h" | ||
|
||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(tmc2130, CONFIG_STEPPER_LOG_LEVEL); | ||
|
||
struct tmc2130_config { | ||
struct step_dir_stepper_common_config common; | ||
struct gpio_dt_spec en_pin; | ||
struct spi_dt_spec spi; | ||
bool stealth_chop_enabled; | ||
jilaypandya marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a gconf property. Please use the appropriate register name i.e. gconf. Follow the pattern as done in other trinamic drivers where this register gets configured during startup. Thanks. |
||
uint32_t tpwmthrs; | ||
uint8_t tpowerdown; | ||
uint32_t ihold_irun; | ||
enum stepper_micro_step_resolution default_ustep_res; | ||
jilaypandya marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
struct tmc2130_data { | ||
struct step_dir_stepper_common_data common; | ||
struct k_sem sem; | ||
}; | ||
|
||
STEP_DIR_STEPPER_STRUCT_CHECK(struct tmc2130_config, struct tmc2130_data); | ||
|
||
static int tmc2130_stepper_enable(const struct device *dev) | ||
{ | ||
const struct tmc2130_config *config = dev->config; | ||
int ret; | ||
|
||
/* Check availability of the enable pin, as it might be hardwired. */ | ||
if (config->en_pin.port == NULL) { | ||
LOG_WRN_ONCE("%s: Enable pin undefined.", dev->name); | ||
return 0; | ||
} | ||
|
||
ret = gpio_pin_set_dt(&config->en_pin, 1); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to set en_pin (error: %d)", dev->name, ret); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static int tmc2130_stepper_disable(const struct device *dev) | ||
{ | ||
const struct tmc2130_config *config = dev->config; | ||
int ret; | ||
|
||
/* Check availability of the enable pin, as it might be hardwired. */ | ||
if (config->en_pin.port == NULL) { | ||
LOG_WRN_ONCE("%s: Enable pin undefined.", dev->name); | ||
return -ENOTSUP; | ||
} | ||
|
||
ret = gpio_pin_set_dt(&config->en_pin, 0); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to set en_pin (error: %d)", dev->name, ret); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static int tmc2130_stepper_set_micro_step_res(const struct device *dev, | ||
enum stepper_micro_step_resolution micro_step_res) | ||
{ | ||
const struct tmc2130_config *config = dev->config; | ||
struct tmc2130_data *data = dev->data; | ||
int ret; | ||
uint32_t reg_value; | ||
|
||
k_sem_take(&data->sem, K_FOREVER); | ||
|
||
ret = tmc_spi_read_register(&config->spi, TMC2130_ADDRESS_MASK, TMC2130_CHOPCONF, | ||
®_value); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to read register 0x%x (error code: %d)", dev->name, | ||
TMC2130_CHOPCONF, ret); | ||
goto set_micro_step_res_end; | ||
} | ||
reg_value &= ~TMC2130_CHOPCONF_MRES_MASK; | ||
reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(micro_step_res)) | ||
<< TMC2130_CHOPCONF_MRES_SHIFT); | ||
|
||
ret = tmc_spi_write_register(&config->spi, TMC2130_WRITE_BIT, TMC2130_CHOPCONF, reg_value); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to write register 0x%x (error code: %d)", dev->name, | ||
TMC2130_CHOPCONF, ret); | ||
} | ||
|
||
set_micro_step_res_end: | ||
k_sem_give(&data->sem); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a wrapper for bus communication as done in other trinamic drivers. |
||
|
||
return ret; | ||
} | ||
|
||
static int tmc2130_stepper_get_micro_step_res(const struct device *dev, | ||
enum stepper_micro_step_resolution *micro_step_res) | ||
{ | ||
const struct tmc2130_config *config = dev->config; | ||
struct tmc2130_data *data = dev->data; | ||
uint32_t reg_value; | ||
int err; | ||
|
||
k_sem_take(&data->sem, K_FOREVER); | ||
|
||
err = tmc_spi_read_register(&config->spi, TMC2130_ADDRESS_MASK, TMC2130_CHOPCONF, | ||
®_value); | ||
if (err != 0) { | ||
return -EIO; | ||
} | ||
|
||
k_sem_give(&data->sem); | ||
|
||
reg_value &= TMC2130_CHOPCONF_MRES_MASK; | ||
reg_value >>= TMC2130_CHOPCONF_MRES_SHIFT; | ||
*micro_step_res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); | ||
|
||
return 0; | ||
} | ||
|
||
static int tmc2130_stepper_init(const struct device *dev) | ||
{ | ||
const struct tmc2130_config *config = dev->config; | ||
struct tmc2130_data *data = dev->data; | ||
uint32_t gstat_data; | ||
int ret; | ||
|
||
uint8_t ustep_res_reg_value = | ||
(MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(config->default_ustep_res)); | ||
|
||
/* Read GSTAT register to clear any errors. */ | ||
if (!spi_is_ready_dt(&config->spi)) { | ||
LOG_ERR("SPI bus is not ready"); | ||
return -ENODEV; | ||
} | ||
tmc_spi_read_register(&config->spi, TMC2130_ADDRESS_MASK, TMC2130_GSTAT, &gstat_data); | ||
LOG_DBG("GSTAT: %x", gstat_data); | ||
|
||
/* Configuration registers and their intended values. */ | ||
uint32_t reg_combined[][2] = { | ||
{TMC2130_CHOPCONF, | ||
TMC2130_CHOPCONF_INIT(ustep_res_reg_value, config->common.dual_edge)}, | ||
{TMC2130_IHOLD_IRUN, config->ihold_irun}, | ||
{TMC2130_TPOWERDOWN, TMC2130_TPOWERDOWN_INIT(config->tpowerdown)}, | ||
{TMC2130_GCONF, TMC2130_GCONF_INIT(config->stealth_chop_enabled)}, | ||
{TMC2130_TPWMTHRS, TMC2130_TPWMTHRS_INIT(config->tpwmthrs)}, | ||
{TMC2130_PWMCONF, TMC2130_PWMCONF_INIT}}; | ||
|
||
/*Write configuration. */ | ||
for (int i = 0; i < ARRAY_SIZE(reg_combined); i++) { | ||
ret = tmc_spi_write_register(&config->spi, TMC2130_WRITE_BIT, reg_combined[i][0], | ||
reg_combined[i][1]); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to write register 0x%x (error code: %d)", dev->name, | ||
reg_combined[i][0], ret); | ||
return ret; | ||
} | ||
} | ||
|
||
/* Configure enable pin if it is available */ | ||
if (config->en_pin.port != NULL) { | ||
ret = gpio_pin_configure_dt(&config->en_pin, GPIO_OUTPUT_INACTIVE); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to configure en_pin (error: %d)", dev->name, ret); | ||
return ret; | ||
} | ||
} | ||
|
||
k_sem_init(&data->sem, 1, 1); | ||
|
||
ret = step_dir_stepper_common_init(dev); | ||
if (ret != 0) { | ||
LOG_ERR("%s: Failed to initialize common step direction stepper (error: %d)", | ||
dev->name, ret); | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static DEVICE_API(stepper, tmc2130_stepper_api) = { | ||
.enable = tmc2130_stepper_enable, | ||
.disable = tmc2130_stepper_disable, | ||
.move_by = step_dir_stepper_common_move_by, | ||
.is_moving = step_dir_stepper_common_is_moving, | ||
.set_reference_position = step_dir_stepper_common_set_reference_position, | ||
.get_actual_position = step_dir_stepper_common_get_actual_position, | ||
.move_to = step_dir_stepper_common_move_to, | ||
.set_microstep_interval = step_dir_stepper_common_set_microstep_interval, | ||
.run = step_dir_stepper_common_run, | ||
.stop = step_dir_stepper_common_stop, | ||
.set_event_callback = step_dir_stepper_common_set_event_callback, | ||
.set_micro_step_res = tmc2130_stepper_set_micro_step_res, | ||
.get_micro_step_res = tmc2130_stepper_get_micro_step_res, | ||
}; | ||
|
||
#define TMC2130_CHECK_CONFIGURATION(inst) \ | ||
BUILD_ASSERT(DT_INST_PROP(inst, tpwmthrs) <= TMC2130_TPWMTHRS_MAX_VALUE, \ | ||
"tpwthrs is too large"); \ | ||
BUILD_ASSERT(DT_INST_PROP(inst, iholddelay) <= TMC2130_IHOLDDELAY_MAX_VALUE, \ | ||
"iholddelay is too large"); \ | ||
BUILD_ASSERT(DT_INST_PROP(inst, tpowerdown) <= TMC2130_TPOWERDOWN_MAX_VALUE, \ | ||
"tpowerdown is too large"); \ | ||
BUILD_ASSERT(DT_INST_PROP(inst, ihold) <= TMC2130_IHOLD_MAX_VALUE, "ihold is too large"); \ | ||
BUILD_ASSERT(DT_INST_PROP(inst, irun) <= TMC2130_IRUN_MAX_VALUE, "irun is too large"); | ||
|
||
#define TMC2130_STEPPER_DEVICE(inst) \ | ||
TMC2130_CHECK_CONFIGURATION(inst); \ | ||
static const struct tmc2130_config tmc2130_config_##inst = { \ | ||
.common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \ | ||
.en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}), \ | ||
.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), \ | ||
.tpwmthrs = DT_INST_PROP(inst, tpwmthrs), \ | ||
.tpowerdown = DT_INST_PROP(inst, tpowerdown), \ | ||
.stealth_chop_enabled = DT_INST_PROP(inst, en_pwm_mode), \ | ||
.default_ustep_res = DT_INST_PROP(inst, micro_step_res), \ | ||
.ihold_irun = TMC2130_IHOLD_IRUN_INIT(DT_INST_PROP(inst, iholddelay), \ | ||
DT_INST_PROP(inst, irun), \ | ||
DT_INST_PROP(inst, ihold)), \ | ||
}; \ | ||
static struct tmc2130_data tmc2130_data_##inst = { \ | ||
.common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \ | ||
}; \ | ||
DEVICE_DT_INST_DEFINE(inst, tmc2130_stepper_init, NULL, &tmc2130_data_##inst, \ | ||
&tmc2130_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ | ||
&tmc2130_stepper_api); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(TMC2130_STEPPER_DEVICE) |
Uh oh!
There was an error while loading. Please reload this page.