-
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?
Conversation
#define IHOLDDELAY_MAX_VALUE 15 | ||
|
||
/* Initial register values derived from the "Getting Started" Chapter of the TMC2130 datasheet*/ | ||
#define TMC2130_CHOPCONF_INIT \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you would probably want to offer a dedicated function for configuring CHOPCONF/PWMCONF and so on. I think this is coming from the initialization example from tmc datasheet.
Better would be to allow the end user to do it from application code by providing a function.
tmc2130_controller_read/write(dev,reg,value)
, you could put all the register definitions in a file and then the user could set this up
For tmc50xx i know that settting up the ramp is bare minimum to get the steppers running. Hence in the default driver instantiation, i only introduced the bare minimum stuff to run the stepper.
#define TMC2130_CHOPCONF_INIT \ | |
#define TMC2130_CHOPCONF_INIT 0x000100C3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about about also offering the the register function to the user, through we might want to think about making it a general function for all tmc drivers, sort of like tmc_spi_write_register
is already. But that specific idea would be beyond the scope of this PR.
That said, I dont see an issue with the default value. I need to set something, as zeroing the register with exception of ms-resolution, will disable the driver. So I choose values that are recommended by ADI as reasonable starting values. Everybody who needs finer control, for this or other registers would need to use their own spi calls at the moment - the same is true with the tmc50xx and tmc51xx drivers as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah in terms of tmc50xx driver the bare minimum required to run the driver in ramp_gen register which i added to the dt-bindings.
In the example in dt-bindings you can document the default init values as mentioned in the datasheet. But hardcoding a INIT value over here is not a good practice. An end user has no control as of now on these parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did some tests and managed to strip each of the two down to one parameter each, which is the minimum to safely run the driver.
struct tmc2130_spi_sd_data *data = dev->data; | ||
int ret; | ||
|
||
K_SPINLOCK((struct k_spinlock *)&data->common.lock) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can also enable/disable via spi, or? Not a must for this PR, though, would be nice if you could give a look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I am aware of it, but there is a little bit more to it and it is not a priority for me during this PR.
K_SPINLOCK_BREAK; | ||
} | ||
if (!config->common.dual_edge) { | ||
ret = gpio_pin_set_dt(&config->common.step_pin, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this step required? Disabling the driver should turn of all the coils, or?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I remember this came up in other driver discussions. At the moment this is not needed, but as soon as the step signal has 50% duty cycle, the driver might stop with step signal high, this is to fix that, through there is an argument to be made, that the step-dir implementation should ensure that it does not stop on a step signal high with dual_edge disabled.
struct tmc2130_spi_sd_data *data = dev->data; | ||
|
||
if (!data->enabled) { | ||
LOG_ERR("Failed to move by delta, device is not enabled"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG_ERR("Failed to move by delta, device is not enabled"); | |
LOG_ERR("Failed to move by %d, device is not enabled", steps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drv84xx and a4979 also use this message + the step amount is irrelevant as it is an enable/disable question. Also see the message for move_to.
boards/shields/mikroe_silent_step_2_click/mikroe_silent_step_2_click.overlay
Outdated
Show resolved
Hide resolved
736deba
to
64e0536
Compare
That should be the majority of things outside of large scale renaming and splitting the reg.h file. That + plus some other changes to file locations will require a rebase if I am not mistaken. |
64e0536
to
5be76a7
Compare
drivers/stepper/adi_tmc/tmc2130.c
Outdated
|
||
/* Check availability of the enable pin, as it might be hardwired. */ | ||
if (config->en_pin.port == NULL) { | ||
LOG_ERR("%s: Failed to disable device, enable pin is not " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you shorten this message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer not to, as everything in the message is relevant. Also, drv84xx uses the same message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These messages get stored in Flash and probably get used only during development, you can either shorten them or change this LOG_DBG.
Reference:
#86620 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, I see that you have changed to LOG_WRN_ONCE, but the reasoning behind using LOG_DBG or shorter messages has been correctly pointed out in the comment that i have referenced earlier. If you want to keep the entire log message, then switch is LOG_DBG and then release builds atleast in my experience, set the loggging level to LOG_INFO and hence this message won't take up flash anymore
drivers/stepper/adi_tmc/tmc2130.c
Outdated
{ | ||
struct tmc2130_data *data = dev->data; | ||
|
||
if (!data->enabled) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use a spinlock when setting the enabled flag, whereas when reading, theres no lock. Any reason to not use a spinlock in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would, I think, require putting the entire function in a spinlock, which is definitely an option, as otherwise you could change the enable status after the check but while the initial movement function is still running (I hope that made sense).
That raises the interesting question, what the intended behavior of of the api is in that instance.
drivers/stepper/adi_tmc/tmc2130.c
Outdated
uint32_t reg_combined[][2] = { | ||
{TMC2130_CHOPCONF, TMC2130_CHOPCONF_INIT + (ustep_res_reg_value << 24)}, | ||
{TMC2130_IHOLD_IRUN, | ||
0x00000000 + (config->iholddelay << 16) + (config->irun << 8) + config->ihold}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use magic numbers being used for the shifts here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using defined constants is definitely an option. The downside is that these lines become even longer and thus potentially less readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here you go, we are already doing it in tmc5xxx drivers, probably you can copy paste the same pattern in tmc2130_reg.h
#define TMC5XXX_IHOLDDELAY(n) (((n) << TMC5XXX_IHOLDDELAY_SHIFT) & TMC5XXX_IHOLDDELAY_MASK) |
#define TMC5XXX_IHOLD_MASK GENMASK(4, 0)
#define TMC5XXX_IHOLD_SHIFT 0
#define TMC5XXX_IHOLD(n) (((n) << TMC5XXX_IHOLD_SHIFT) & TMC5XXX_IHOLD_MASK)
#define TMC5XXX_IRUN_MASK GENMASK(12, 8)
#define TMC5XXX_IRUN_SHIFT 8
#define TMC5XXX_IRUN(n) (((n) << TMC5XXX_IRUN_SHIFT) & TMC5XXX_IRUN_MASK)
#define TMC5XXX_IHOLDDELAY_MASK GENMASK(19, 16)
#define TMC5XXX_IHOLDDELAY_SHIFT 16
#define TMC5XXX_IHOLDDELAY(n) (((n) << TMC5XXX_IHOLDDELAY_SHIFT) & TMC5XXX_IHOLDDELAY_MASK)
.iholdrun = (TMC5XXX_IRUN(DT_PROP(node, irun)) | \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The downside is that these lines become even longer and thus potentially less readable.
{TMC2130_CHOPCONF, TMC2130_CHOPCONF_INIT + (ustep_res_reg_value << 24)}, {TMC2130_IHOLD_IRUN, 0x00000000 + (config->iholddelay << 16) + (config->irun << 8) + config->ihold}
How is this readable
5be76a7
to
d9b2575
Compare
I tried to rebase onto latest main locally to fix the compliance issues, but that didn't help. As they all occur in files I didn't touch, I will wait on this. |
drivers/stepper/adi_tmc/tmc2130.c
Outdated
uint32_t reg_combined[][2] = { | ||
{TMC2130_CHOPCONF, TMC2130_CHOPCONF_INIT + (ustep_res_reg_value << 24)}, | ||
{TMC2130_IHOLD_IRUN, | ||
0x00000000 + (config->iholddelay << 16) + (config->irun << 8) + config->ihold}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here you go, we are already doing it in tmc5xxx drivers, probably you can copy paste the same pattern in tmc2130_reg.h
#define TMC5XXX_IHOLDDELAY(n) (((n) << TMC5XXX_IHOLDDELAY_SHIFT) & TMC5XXX_IHOLDDELAY_MASK) |
#define TMC5XXX_IHOLD_MASK GENMASK(4, 0)
#define TMC5XXX_IHOLD_SHIFT 0
#define TMC5XXX_IHOLD(n) (((n) << TMC5XXX_IHOLD_SHIFT) & TMC5XXX_IHOLD_MASK)
#define TMC5XXX_IRUN_MASK GENMASK(12, 8)
#define TMC5XXX_IRUN_SHIFT 8
#define TMC5XXX_IRUN(n) (((n) << TMC5XXX_IRUN_SHIFT) & TMC5XXX_IRUN_MASK)
#define TMC5XXX_IHOLDDELAY_MASK GENMASK(19, 16)
#define TMC5XXX_IHOLDDELAY_SHIFT 16
#define TMC5XXX_IHOLDDELAY(n) (((n) << TMC5XXX_IHOLDDELAY_SHIFT) & TMC5XXX_IHOLDDELAY_MASK)
.iholdrun = (TMC5XXX_IRUN(DT_PROP(node, irun)) | \ |
#define IHOLDDELAY_MAX_VALUE 15 | ||
|
||
/* Initial register values derived from the "Getting Started" Chapter of the TMC2130 datasheet*/ | ||
#define TMC2130_CHOPCONF_INIT \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah in terms of tmc50xx driver the bare minimum required to run the driver in ramp_gen register which i added to the dt-bindings.
In the example in dt-bindings you can document the default init values as mentioned in the datasheet. But hardcoding a INIT value over here is not a good practice. An end user has no control as of now on these parameters.
drivers/stepper/adi_tmc/tmc2130.c
Outdated
|
||
/* Check availability of the enable pin, as it might be hardwired. */ | ||
if (config->en_pin.port == NULL) { | ||
LOG_ERR("%s: Failed to disable device, enable pin is not " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, I see that you have changed to LOG_WRN_ONCE, but the reasoning behind using LOG_DBG or shorter messages has been correctly pointed out in the comment that i have referenced earlier. If you want to keep the entire log message, then switch is LOG_DBG and then release builds atleast in my experience, set the loggging level to LOG_INFO and hence this message won't take up flash anymore
1808170
to
c130d61
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, overall I think it's going In a good direction.
drivers/stepper/adi_tmc/tmc2130.c
Outdated
K_SPINLOCK_BREAK; | ||
} | ||
|
||
data->ustep_res = micro_step_res; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3 questions:
- Why are you saving this when you are saving this value in the IC register
- The whole spinlock is necessary to lock your step-dir data and you seem to use the same lock over the spi transactions
- If you still intend to use this information, why aren't you using it when querying the microsteps?
Could you please follow the patterns being followed in the tmc5xxx series.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will switch, in the next update, to the 5xxx approach of storing that in config as the default ustep_res and no longer update it in set_microstep_resolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed data->ustep_res
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again, could you please follow the pattern being followed in the tmc5xxx series. Why are using a spinlock on IO operations? You are writing 2 SPI registers with the spinlock held on. Are you certain you want to disable all interrupts until your SPI transactions are complete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am un-resolving this comment here, since this does not follow the tmc5xxx pattern here.
Please do not resolve comments which are not resolved.
Moved some dt properties for tmc drivers from ramp-generator to common as they are also used by non-ramp drivers. Signed-off-by: Jan Behrens <jan.behrens@navimatix.de>
c130d61
to
a7a0b7d
Compare
boards/shields/mikroe_silent_step_2_click/mikroe_silent_step_2_click.overlay
Outdated
Show resolved
Hide resolved
a7a0b7d
to
5db2f2c
Compare
Added a driver for the tmc2130 stepper driver. The driver is only for the tmc2130s step-dir interface in combination with spi-based configuration. The other control interfaces of the tmc2130 are not supported. Signed-off-by: Jan Behrens <jan.behrens@navimatix.de>
Adds the tmc2130 stepper driver to the build_all test. Signed-off-by: Jan Behrens <jan.behrens@navimatix.de>
The Silent Step 2 Click shield offers a ADI TMC3130 stepper driver. Some pins are accessed via a NXP PCA9538A gpio expander. Signed-off-by: Jan Behrens <jan.behrens@navimatix.de>
5db2f2c
to
c41b3de
Compare
|
struct step_dir_stepper_common_config common; | ||
struct gpio_dt_spec en_pin; | ||
struct spi_dt_spec spi; | ||
bool stealth_chop_enabled; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
} | ||
|
||
set_micro_step_res_end: | ||
k_sem_give(&data->sem); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a wrapper for bus communication as done in other trinamic drivers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropping some comments about how the Inital Register Values
are getting configured.
#define TMC2130_CHOPCONF_MRES_SHIFT 24 | ||
#define TMC2130_CHOPCONF_DOUBLE_EDGE_SHIFT 29 | ||
|
||
#define TMC2130_TPWMTHRS_MAX_VALUE ((1 << 20) - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#define TMC2130_TPWMTHRS_MAX_VALUE ((1 << 20) - 1) | |
#define TMC2130_TPWMTHRS_MAX_VALUE BITMASK(20) |
/* Initial register values */ | ||
#define TMC2130_GCONF_INIT(stealth_chop) \ | ||
(0x00000000 + (stealth_chop << TMC2130_GCONF_STEALTH_CHOP_SHIFT)) | ||
#define TMC2130_IHOLD_IRUN_INIT(iholddelay, irun, ihold) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can pass the node in the macro and compose the entire register from DT_PROPs as we are doing in tmc5xxx drivers. I think down the line we could even generalize some of the these macros for all trinamic drivers, reducing code duplication :)
#define TMC2130_IHOLD_IRUN_INIT(iholddelay, irun, ihold) \ | |
#define TMC2130_IHOLD_IRUN(node) \ |
#define TMC_RAMP_DT_SPEC_GET_COMMON(node) \ |
.iholdrun = (TMC5XXX_IRUN(DT_PROP(node, irun)) | \ |
#define TMC2130_IHOLD_IRUN_INIT(iholddelay, irun, ihold) \ | ||
(0x00000000 + (iholddelay << TMC2130_IHOLDDELAY_SHIFT) + \ | ||
((irun << TMC2130_IRUN_SHIFT) & TMC2130_IRUN_MASK) + (ihold & TMC2130_IHOLD_MASK)) | ||
#define TMC2130_TPOWERDOWN_INIT(tpowerdown) (0x00000000 + tpowerdown) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this macro really required?
Ain't (0x00000000 + tpowerdown)
effectively the same tpowerdown
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tpowerdown
on its own should be enough, but I used a macro to keep the array in the init function consistent.
At least, that was the idea I think, but looking at the code again, some of the other registers also don't use a macro, so I will drop the macro and just use the value, especially, as this register doesn't store multiple properties.
((irun << TMC2130_IRUN_SHIFT) & TMC2130_IRUN_MASK) + (ihold & TMC2130_IHOLD_MASK)) | ||
#define TMC2130_TPOWERDOWN_INIT(tpowerdown) (0x00000000 + tpowerdown) | ||
#define TMC2130_TPWMTHRS_INIT(tpwmthrs) (0x00000000 + tpwmthrs) | ||
#define TMC2130_CHOPCONF_INIT(ustep_res, double_edge) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#define TMC2130_CHOPCONF_INIT(ustep_res, double_edge) \ | |
#define TMC_CHOPCONF_DT_SPEC_GET_TMC2130(node) \ |
#define TMC_RAMP_DT_SPEC_GET_TMC51XX(node) \ |
#define TMC2130_CHOPCONF_INIT(ustep_res, double_edge) \ | ||
(0x00000002 + (double_edge << TMC2130_CHOPCONF_DOUBLE_EDGE_SHIFT) + \ | ||
((ustep_res << TMC2130_CHOPCONF_MRES_SHIFT) & TMC2130_CHOPCONF_MRES_MASK)) /* TOFF=3 */ | ||
#define TMC2130_PWMCONF_INIT 0x00040000 /* pwm_autoscale=1 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not hardcode registers like this.
What i would do is basically compose this register using appropriaate properties as mentioned in the datasheet. We follow this pattern #define TMC_RAMP_DT_SPEC_GET_TMC50XX(node)
for configuring ramps. You could do something similiar here as well, like #define TMC_PWMCONF_DT_SPEC_GET_TMC2130(node)
or even TMC2130_PWMCONF_DT_SPEC_GET(node)
, so that we have a coherent way of configuring these trinamic register :)
For reference:
https://github.com/analogdevicesinc/TMC-API/blob/master/tmc/ic/TMC2130/TMC2130_HW_Abstraction.h
#define TMC2130_TPOWERDOWN_INIT(tpowerdown) (0x00000000 + tpowerdown) | ||
#define TMC2130_TPWMTHRS_INIT(tpwmthrs) (0x00000000 + tpwmthrs) | ||
#define TMC2130_CHOPCONF_INIT(ustep_res, double_edge) \ | ||
(0x00000002 + (double_edge << TMC2130_CHOPCONF_DOUBLE_EDGE_SHIFT) + \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is 0x00000002
for? Are you configuring toff1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TOFF=2
as that is the smallest valid toff value for running the driver. The comment for toff=3 at the line end is outdated.
ngpios = <8>; | ||
#gpio-cells = <2>; | ||
|
||
gpio-reserved-ranges = <3 1>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow the tmc5xxx bus wrapper logic.
This PR adds a driver for the ADI TMC2130 stepper driver. The driver only supports the step-dir-spi interface (simply called step-dir in official documentation) of the TMC2130. The pure gpio (called standalone, also a step-dir interface) and pure spi interfaces are not supported.
I moved some devicetree bindings from
adi,trinamic-ramp-generator.yaml
toadi,trinamic-common.yaml
as they are unrelated to ramp generation and used by the TMC2130 driver.The driver tested using the shell as well as the drv84xx/api test suite.