11/*
22 * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG
3- * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya
3+ * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya
44 * SPDX-License-Identifier: Apache-2.0
55 */
66
1010#include <zephyr/kernel.h>
1111#include <zephyr/sys_clock.h>
1212#include <zephyr/drivers/stepper.h>
13+ #include "stepper_common.h"
1314#include <zephyr/sys/__assert.h>
1415
16+ #ifdef CONFIG_STEPPER_RAMP
17+ #include <stdlib.h>
18+ #include "ramp/ramp.h"
19+ #endif
20+
1521#include <zephyr/logging/log.h>
1622LOG_MODULE_REGISTER (gpio_stepper_motor_controller , CONFIG_STEPPER_LOG_LEVEL );
1723
@@ -26,6 +32,10 @@ static const uint8_t
2632struct gpio_stepper_config {
2733 const struct gpio_dt_spec * control_pins ;
2834 bool invert_direction ;
35+ #ifdef CONFIG_STEPPER_RAMP
36+ const struct stepper_ramp_api * ramp_api ;
37+ const struct stepper_ramp_config ramp_config ;
38+ #endif
2939};
3040
3141struct gpio_stepper_data {
@@ -42,6 +52,9 @@ struct gpio_stepper_data {
4252 bool is_enabled ;
4353 stepper_event_callback_t callback ;
4454 void * event_cb_user_data ;
55+ #ifdef CONFIG_STEPPER_RAMP
56+ struct stepper_ramp_common ramp_common ;
57+ #endif
4558};
4659
4760static int stepper_motor_set_coil_charge (const struct device * dev )
@@ -98,6 +111,24 @@ static void update_coil_charge(const struct device *dev)
98111 const struct gpio_stepper_config * config = dev -> config ;
99112 struct gpio_stepper_data * data = dev -> data ;
100113
114+ #ifdef CONFIG_STEPPER_RAMP
115+ if (data -> ramp_common .ramp_runtime_data .current_ramp_state ==
116+ STEPPER_RAMP_STATE_PRE_DECELERATION ) {
117+ if (data -> direction == STEPPER_DIRECTION_NEGATIVE ) {
118+ config -> invert_direction ? decrement_coil_charge (dev )
119+ : increment_coil_charge (dev );
120+ data -> actual_position ++ ;
121+ return ;
122+ }
123+ if (data -> direction == STEPPER_DIRECTION_POSITIVE ) {
124+ config -> invert_direction ? increment_coil_charge (dev )
125+ : decrement_coil_charge (dev );
126+ data -> actual_position -- ;
127+ return ;
128+ }
129+ }
130+ #endif
131+
101132 if (data -> direction == STEPPER_DIRECTION_POSITIVE ) {
102133 config -> invert_direction ? decrement_coil_charge (dev ) : increment_coil_charge (dev );
103134 data -> actual_position ++ ;
@@ -107,26 +138,34 @@ static void update_coil_charge(const struct device *dev)
107138 }
108139}
109140
110- static void update_remaining_steps (struct gpio_stepper_data * data )
141+ static void update_remaining_steps (const struct device * dev )
111142{
143+ struct gpio_stepper_data * data = dev -> data ;
144+
145+ #ifdef CONFIG_STEPPER_RAMP
146+
147+ if (data -> ramp_common .ramp_runtime_data .current_ramp_state ==
148+ STEPPER_RAMP_STATE_PRE_DECELERATION ) {
149+ if (data -> step_count > 0 ) {
150+ data -> step_count ++ ;
151+ } else {
152+ data -> step_count -- ;
153+ }
154+ return ;
155+ }
156+
157+ #endif
112158 if (data -> step_count > 0 ) {
113159 data -> step_count -- ;
114- (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
115160 } else if (data -> step_count < 0 ) {
116161 data -> step_count ++ ;
117- (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
118- } else {
119- if (!data -> callback ) {
120- LOG_WRN_ONCE ("No callback set" );
121- return ;
122- }
123- data -> callback (data -> dev , STEPPER_EVENT_STEPS_COMPLETED , data -> event_cb_user_data );
124162 }
125163}
126164
127- static void update_direction_from_step_count (const struct device * dev )
165+ static bool update_direction_from_step_count (const struct device * dev )
128166{
129167 struct gpio_stepper_data * data = dev -> data ;
168+ enum stepper_direction direction = data -> direction ;
130169
131170 if (data -> step_count > 0 ) {
132171 data -> direction = STEPPER_DIRECTION_POSITIVE ;
@@ -135,17 +174,34 @@ static void update_direction_from_step_count(const struct device *dev)
135174 } else {
136175 LOG_ERR ("Step count is zero" );
137176 }
177+ if (data -> direction != direction ) {
178+ return true;
179+ }
180+ return false;
138181}
139182
140183static void position_mode_task (const struct device * dev )
141184{
142185 struct gpio_stepper_data * data = dev -> data ;
143186
187+ update_remaining_steps (dev );
188+ (void )stepper_motor_set_coil_charge (dev );
189+ update_coil_charge (dev );
144190 if (data -> step_count ) {
145- (void )stepper_motor_set_coil_charge (dev );
146- update_coil_charge (dev );
191+ #ifdef CONFIG_STEPPER_RAMP
192+ const struct gpio_stepper_config * config = dev -> config ;
193+
194+ data -> delay_in_ns = config -> ramp_api -> get_next_step_interval (
195+ & data -> ramp_common , data -> delay_in_ns , STEPPER_RUN_MODE_POSITION );
196+ #endif
197+ (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
198+ } else {
199+ if (data -> callback ) {
200+ data -> callback (data -> dev , STEPPER_EVENT_STEPS_COMPLETED ,
201+ data -> event_cb_user_data );
202+ }
203+ (void )k_work_cancel_delayable (& data -> stepper_dwork );
147204 }
148- update_remaining_steps (dev -> data );
149205}
150206
151207static void velocity_mode_task (const struct device * dev )
@@ -154,6 +210,14 @@ static void velocity_mode_task(const struct device *dev)
154210
155211 (void )stepper_motor_set_coil_charge (dev );
156212 update_coil_charge (dev );
213+
214+ #ifdef CONFIG_STEPPER_RAMP
215+ const struct gpio_stepper_config * config = dev -> config ;
216+
217+ data -> delay_in_ns = config -> ramp_api -> get_next_step_interval (
218+ & data -> ramp_common , data -> delay_in_ns , STEPPER_RUN_MODE_VELOCITY );
219+ #endif
220+
157221 (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
158222}
159223
@@ -178,6 +242,23 @@ static void stepper_work_step_handler(struct k_work *work)
178242 }
179243}
180244
245+ static bool is_step_timing_valid (const struct device * dev )
246+ {
247+ struct gpio_stepper_data * data = dev -> data ;
248+ #ifdef CONFIG_STEPPER_RAMP
249+
250+ if (data -> ramp_common .ramp_profile .max_velocity > 0 ) {
251+ return true;
252+ }
253+ return false;
254+ #else
255+ if (data -> delay_in_ns > 0 ) {
256+ return true;
257+ }
258+ return false;
259+ #endif
260+ }
261+
181262static int gpio_stepper_move_by (const struct device * dev , int32_t micro_steps )
182263{
183264 struct gpio_stepper_data * data = dev -> data ;
@@ -187,14 +268,35 @@ static int gpio_stepper_move_by(const struct device *dev, int32_t micro_steps)
187268 return - ECANCELED ;
188269 }
189270
190- if (data -> delay_in_ns == 0 ) {
191- LOG_ERR ("Step interval not set or invalid step interval set" );
271+ if (!is_step_timing_valid (dev )) {
192272 return - EINVAL ;
193273 }
274+
275+ if (micro_steps == 0 ) {
276+ LOG_WRN ("Step count is zero" );
277+ return 0 ;
278+ }
279+
194280 K_SPINLOCK (& data -> lock ) {
195281 data -> run_mode = STEPPER_RUN_MODE_POSITION ;
196282 data -> step_count = micro_steps ;
197- update_direction_from_step_count (dev );
283+ bool is_dir_changed = update_direction_from_step_count (dev );
284+ #ifdef CONFIG_STEPPER_RAMP
285+ const struct gpio_stepper_config * config = dev -> config ;
286+ uint32_t steps_to_move = abs (micro_steps );
287+ bool is_stepper_moving = k_work_delayable_is_pending (& data -> stepper_dwork );
288+
289+ config -> ramp_api -> reset_ramp_runtime_data (& config -> ramp_config , & data -> ramp_common ,
290+ is_dir_changed , is_stepper_moving ,
291+ steps_to_move );
292+ if (!is_stepper_moving ) {
293+ data -> delay_in_ns = config -> ramp_api -> calculate_start_interval (
294+ data -> ramp_common .ramp_profile .acceleration );
295+ }
296+ config -> ramp_api -> recalculate_ramp (& data -> ramp_common , steps_to_move );
297+ #else
298+ ARG_UNUSED (is_dir_changed );
299+ #endif
198300 (void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
199301 }
200302 return 0 ;
@@ -257,6 +359,23 @@ static int gpio_stepper_set_microstep_interval(const struct device *dev,
257359 return 0 ;
258360}
259361
362+ #ifdef CONFIG_STEPPER_RAMP
363+
364+ static int gpio_stepper_set_ramp_profile (const struct device * dev ,
365+ const struct stepper_ramp_profile * const ramp_profile )
366+ {
367+ struct gpio_stepper_data * data = dev -> data ;
368+
369+ K_SPINLOCK (& data -> lock ) {
370+ data -> ramp_common .ramp_profile .acceleration = ramp_profile -> acceleration ;
371+ data -> ramp_common .ramp_profile .max_velocity = ramp_profile -> max_velocity ;
372+ data -> ramp_common .ramp_profile .deceleration = ramp_profile -> deceleration ;
373+ }
374+ return 0 ;
375+ }
376+
377+ #endif
378+
260379static int gpio_stepper_run (const struct device * dev , const enum stepper_direction direction )
261380{
262381 struct gpio_stepper_data * data = dev -> data ;
@@ -268,7 +387,22 @@ static int gpio_stepper_run(const struct device *dev, const enum stepper_directi
268387
269388 K_SPINLOCK (& data -> lock ) {
270389 data -> run_mode = STEPPER_RUN_MODE_VELOCITY ;
390+ bool is_dir_changed = direction != data -> direction ;
271391 data -> direction = direction ;
392+ #ifdef CONFIG_STEPPER_RAMP
393+ const struct gpio_stepper_config * config = dev -> config ;
394+ const bool is_stepper_moving = k_work_delayable_is_pending (& data -> stepper_dwork );
395+
396+ config -> ramp_api -> reset_ramp_runtime_data (& config -> ramp_config , & data -> ramp_common ,
397+ is_dir_changed , is_stepper_moving ,
398+ UINT32_MAX );
399+ if (!is_stepper_moving ) {
400+ data -> delay_in_ns = config -> ramp_api -> calculate_start_interval (
401+ data -> ramp_common .ramp_profile .acceleration );
402+ }
403+ #else
404+ ARG_UNUSED (is_dir_changed );
405+ #endif
272406 (void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
273407 }
274408 return 0 ;
@@ -374,6 +508,7 @@ static int gpio_stepper_init(const struct device *dev)
374508 for (uint8_t n_pin = 0 ; n_pin < NUM_CONTROL_PINS ; n_pin ++ ) {
375509 (void )gpio_pin_configure_dt (& config -> control_pins [n_pin ], GPIO_OUTPUT_INACTIVE );
376510 }
511+
377512 k_work_init_delayable (& data -> stepper_dwork , stepper_work_step_handler );
378513 return 0 ;
379514}
@@ -392,20 +527,28 @@ static DEVICE_API(stepper, gpio_stepper_api) = {
392527 .run = gpio_stepper_run ,
393528 .stop = gpio_stepper_stop ,
394529 .is_moving = gpio_stepper_is_moving ,
530+ IF_ENABLED (CONFIG_STEPPER_RAMP , (.set_ramp_profile = gpio_stepper_set_ramp_profile ,))
531+
395532};
396533
397534#define GPIO_STEPPER_DEFINE (inst ) \
398535 static const struct gpio_dt_spec gpio_stepper_motor_control_pins_##inst[] = { \
399536 DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \
400537 }; \
401538 BUILD_ASSERT(ARRAY_SIZE(gpio_stepper_motor_control_pins_##inst) == 4, \
402- "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
539+ "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
403540 static const struct gpio_stepper_config gpio_stepper_config_##inst = { \
404541 .invert_direction = DT_INST_PROP(inst, invert_direction), \
405- .control_pins = gpio_stepper_motor_control_pins_##inst}; \
542+ .control_pins = gpio_stepper_motor_control_pins_##inst, \
543+ IF_ENABLED(CONFIG_STEPPER_RAMP, \
544+ (.ramp_config.pre_deceleration_steps = DT_INST_PROP(inst, pre_decel_steps), \
545+ .ramp_api = RAMP_DT_SPEC_GET_API(inst))) }; \
406546 static struct gpio_stepper_data gpio_stepper_data_##inst = { \
407547 .step_gap = MAX_MICRO_STEP_RES >> (DT_INST_PROP(inst, micro_step_res) - 1), \
408- }; \
548+ IF_ENABLED(CONFIG_STEPPER_RAMP, ( \
549+ .ramp_common.ramp_profile.acceleration = DT_INST_PROP(inst, acceleration), \
550+ .ramp_common.ramp_profile.deceleration = DT_INST_PROP(inst, deceleration), \
551+ .ramp_common.ramp_profile.max_velocity = DT_INST_PROP(inst, max_speed),)) }; \
409552 BUILD_ASSERT(DT_INST_PROP(inst, micro_step_res) <= STEPPER_MICRO_STEP_2, \
410553 "gpio_stepper_controller driver supports up to 2 micro steps"); \
411554 DEVICE_DT_INST_DEFINE(inst, gpio_stepper_init, NULL, &gpio_stepper_data_##inst, \
0 commit comments