Skip to content
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

RFC: Change Stepper API to use step interval #83726

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

faxe1008
Copy link
Collaborator

@faxe1008 faxe1008 commented Jan 9, 2025

As discussed in the original RFC issue: Change the stepper API to accept the interval to wait at a minimum in between executing consequtive steps. This allows for a more granular control then the previous interface.

Resolves #83591

@faxe1008
Copy link
Collaborator Author

faxe1008 commented Jan 9, 2025

Did not include it yet, but the stepper.h could be a macro STEP_INTERVAL_US_FROM_VELOCITY to ease the transition for people already using the old API.

@faxe1008 faxe1008 force-pushed the stepper_velocity branch 7 times, most recently from 5090c7c to d09f9cb Compare January 9, 2025 08:19
@jilaypandya
Copy link
Contributor

The API Change and then relevant changes in shell, drivers, tests should be squashed in one commit.


static inline int z_impl_stepper_run(const struct device *dev,
const enum stepper_direction direction,
const uint32_t velocity)
const uint64_t step_interval_us)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now that passing step interval again over here is just redundant.
User would set_interval->enable->run

Suggested change
const uint64_t step_interval_us)
static inline int z_impl_stepper_run(const struct device *dev,
const enum stepper_direction direction)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah thats also true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But with this you can stop the motor - maybe thats worth keeping?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the stopping of the motor to the set_step_interval function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to handle this in a different manner. Setting step_interval to zero in theory means that the motor spins at inf speed, hence setting step_interval to zero should not in theory stop the motor. If anything 0 is -EINVAL.

setting velocity to zero in drivers as of now does not power down the coils and is in hence some sort of hold mode. This could be done using stepper_hold(device, HOLD_MODE) or stepper_stop(device) where HOLD_MODE could even include stuff like passive_braking when it is supported by the driver.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I reverted that part back setting interval 0 returns -EINVAL again. For the holding though I suggest maybe making another PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep it has to be another PR. Was just thinking out Loud :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some libraries out there use an extra setVelocity function to set the maximal possible velocity where the api is supporting acceleration/deceleration. Since acceleration ramp is not part of zephyr (yet?), setVelocity as such is redundant to the run parameter at the moment. Also it is not instantly clear to the user what would happen if the stepper motor is already moving. would it then change the velocity? or does it require another call to run?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the idea is to decouple setting velocity from run. Run shall only do running in a given direction.

fabiobaltieri
fabiobaltieri previously approved these changes Jan 9, 2025
@faxe1008
Copy link
Collaborator Author

faxe1008 commented Jan 9, 2025

About the squashing - don't know whats the proper ettiquette for it. Did not do that many treewide changes as of now.

@jilaypandya
Copy link
Contributor

About the squashing - don't know whats the proper ettiquette for it. Did not do that many treewide changes as of now.

when we recently shifted to DEVICE_API, we introduced that change in all the drivers in a single commit, I believe its easier to track. The way it's done right now is like this that we introduce a change and then do the correction in drivers, tests, shell successively in respective commits.

I would have had one commit for stepper_set_step_interval, one commit for stepper_runand then one migration-guide commit.

@fabiobaltieri
Copy link
Member

About the squashing - don't know whats the proper ettiquette for it. Did not do that many treewide changes as of now.

You should really squash the API change with the code update to use the new API, basically no commit should break the build. Doc update can be on its own. In this case it does make it harder to review but that's how it should be done.

fabiobaltieri
fabiobaltieri previously approved these changes Jan 9, 2025
static inline uint32_t tmc5xxx_calculate_velocity_from_hz_to_fclk(uint64_t velocity_hz,
uint32_t clock_frequency)
static inline uint32_t
tmc5xxx_calculate_velocity_from_step_interval_to_fclk(uint64_t step_interval_us,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can the argument name be shortened in order to atleast have the function name and return type in a single line

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to change the function name for that to work, done.

@@ -20,10 +20,10 @@ typedef int (*stepper_timing_source_init)(const struct device *dev);
* @brief Update the stepper timing source.
*
* @param dev Pointer to the device structure.
* @param velocity Velocity in microsteps per second.
* @param step_interval_us The new step interval in microseconds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step interval in microseconds

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* @details If velocity > 0, motor shall be set into motion and run incessantly until and unless
* stalled or stopped using some other command, for instance, motor_enable(false).
* This function is non-blocking.
* @details The motor shall be set into motion and run incessantly until and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continuously instead of incessantly

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@faxe1008 faxe1008 force-pushed the stepper_velocity branch 2 times, most recently from 8cadbdf to facdd26 Compare January 10, 2025 08:35
{
const struct tmc5041_stepper_config *config = dev->config;
const struct tmc5041_config *tmc5041_config = config->controller->config;
const uint32_t clock_frequency = tmc5041_config->clock_frequency;
uint32_t velocity_fclk;
int err;

velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency);
if (step_interval_us == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return -EINVAL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah missed that one. done :^)

{
__ASSERT_NO_MSG(clock_frequency);
__ASSERT_NO_MSG(step_interval_us);

uint32_t velocity_hz = 1e6 / step_interval_us;
Copy link
Contributor

@jilaypandya jilaypandya Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Max possible speed here is then limited 1MHz, which is not much since 256 usteps * 200 steps/rev = 51200 usteps/rev, so over here we will be limiting the speed to somewhere around 2rps. At this point i can only see that we need to allow both the interfaces.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use nanoseconds instead of microseconds?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also 1e6/51200 is about 20rps, or am I missing something?

Copy link
Contributor

@jilaypandya jilaypandya Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No that's correct, although 20 rps is still i would say on the slow speed spectrum.

Let's do it this way since step_interval serves majority of the drivers, let's keep it, i think microseconds timebase is good enough.

I think going in nanoseconds timebase would just be to accomodate drivers like tmc5041, or?

For drivers like tmc5041, this function shall return -ENOTSUP, as there is also a way to set speed via ramp as can be seen over here

int tmc5041_stepper_set_ramp(const struct device *dev,
.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also 0.9° steppers which are also very common especially for projects requiring precise positions. This would half the RPM additionally. But on the other hand, the use case where you step at 1/256 uSteps and want to achieve very fast speeds at the same time are very rare. In this case dynamic microstepping can be used, where you change the uStep resolution for slow and fast velocities can be used (this would be responsibility of the application code, since it would introduce to much complexity into the stepper driver in zephyr). This is what we are doing in OpenAstroTech. We switch to something like 1/32 for fast slew movements and then back to e.g. 1/128 for slow and precise tracking rotations.

In general tmc2209 needs a STEP pin input timing of at least 100ns. Disregarding other factors this would result in maximal frequency of 10MHz.

Copy link
Contributor

@jilaypandya jilaypandya Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so i suppose we need to go to nanoseconds timebase.

For controllers like tmc5041 with internal ramp control we cannot ascertain step_interval due to acceleration/deceleration, we can more or less only set_max_velocity, also the reason why this API was designed like this.

stepper_set_step_interval(const struct device*, uint64_t step_interval_nsec) it is :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For drivers like tmc5041, this function shall return -ENOTSUP, as there is also a way to set speed via ramp as can be seen over here

How would that condition look like? The issue is if you want to be faster then 1 step_interval_us but since its a uint64_t you can not attempt to go faster, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry it is -ENOSYS, i mean that the tmc5041 driver shall not implement stepper_set_step_interval, ofcourse with uint64_t tmc5041 could go faster, what i was trying to say that when i set a step interval, i expect that every step shall be exactly that interval apart, which cannot be ascertained with drivers like tmc5041 which have internal ramp motion controller which is reponsible for stepping intervals.

Hence the stepper_set_step_interval shall not be supported by the tmc5041 driver.

Change the stepper API to instead of changing the stepper speed based on
the velocity in microsteps per second to use the delay in usec between
successive steps. Also remove the velocity from the `stepper_run` function
as typical API usage is enable -> set step interval -> run.

Signed-off-by: Fabian Blatz <[email protected]>
Migrates the peripherals documentation to the new step interval based APi
instead of the velocity one.

Signed-off-by: Fabian Blatz <[email protected]>
Adds a two bullet points explaining the change of the velocity based API
towards an step interval one.

Signed-off-by: Fabian Blatz <[email protected]>
static inline int z_impl_stepper_set_max_velocity(const struct device *dev,
const uint32_t micro_steps_per_second)
static inline int z_impl_stepper_set_step_interval(const struct device *dev,
const uint64_t step_interval_us)
{
const struct stepper_driver_api *api = (const struct stepper_driver_api *)dev->api;

Copy link
Contributor

@jilaypandya jilaypandya Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In conjuction to #83726 (comment)

Suggested change
if (api->set_step_interval == NULL) {
return -ENOSYS;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Stepper Release Notes To be mentioned in the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

drivers: stepper: API: Do not limit velocity to integer
5 participants