1212#include <zephyr/drivers/stepper.h>
1313#include <zephyr/sys/__assert.h>
1414
15+ #ifdef CONFIG_STEPPER_RAMP
16+ #include <stdlib.h>
17+ #include "ramp/ramp.h"
18+ #endif
19+
1520#include <zephyr/logging/log.h>
1621LOG_MODULE_REGISTER (gpio_stepper_motor_controller , CONFIG_STEPPER_LOG_LEVEL );
1722
@@ -26,6 +31,9 @@ static const uint8_t
2631struct gpio_stepper_config {
2732 const struct gpio_dt_spec * control_pins ;
2833 bool invert_direction ;
34+ #ifdef CONFIG_STEPPER_RAMP
35+ const struct stepper_ramp_config ramp_config ;
36+ #endif
2937};
3038
3139struct gpio_stepper_data {
@@ -42,6 +50,9 @@ struct gpio_stepper_data {
4250 bool is_enabled ;
4351 stepper_event_callback_t callback ;
4452 void * event_cb_user_data ;
53+ #ifdef CONFIG_STEPPER_RAMP
54+ struct stepper_ramp_common ramp_common ;
55+ #endif
4556};
4657
4758static int stepper_motor_set_coil_charge (const struct device * dev )
@@ -98,6 +109,23 @@ static void update_coil_charge(const struct device *dev)
98109 const struct gpio_stepper_config * config = dev -> config ;
99110 struct gpio_stepper_data * data = dev -> data ;
100111
112+ #ifdef CONFIG_STEPPER_RAMP
113+ if (data -> ramp_common .ramp_data .current_ramp_state == STEPPER_RAMP_STATE_PRE_DECELERATION ) {
114+ if (data -> direction == STEPPER_DIRECTION_NEGATIVE ) {
115+ config -> invert_direction ? decrement_coil_charge (dev )
116+ : increment_coil_charge (dev );
117+ data -> actual_position ++ ;
118+ return ;
119+ }
120+ if (data -> direction == STEPPER_DIRECTION_POSITIVE ) {
121+ config -> invert_direction ? increment_coil_charge (dev )
122+ : decrement_coil_charge (dev );
123+ data -> actual_position -- ;
124+ return ;
125+ }
126+ }
127+ #endif
128+
101129 if (data -> direction == STEPPER_DIRECTION_POSITIVE ) {
102130 config -> invert_direction ? decrement_coil_charge (dev ) : increment_coil_charge (dev );
103131 data -> actual_position ++ ;
@@ -109,6 +137,24 @@ static void update_coil_charge(const struct device *dev)
109137
110138static void update_remaining_steps (struct gpio_stepper_data * data )
111139{
140+ #ifdef CONFIG_STEPPER_RAMP
141+ if (data -> step_count != 0 ) {
142+ data -> delay_in_ns = trapezoidal_ramp_api .get_next_step_interval (& data -> ramp_common ,
143+ data -> delay_in_ns );
144+
145+ if (data -> ramp_common .ramp_data .current_ramp_state ==
146+ STEPPER_RAMP_STATE_PRE_DECELERATION ) {
147+ if (data -> step_count > 0 ) {
148+ data -> step_count ++ ;
149+ } else {
150+ data -> step_count -- ;
151+ }
152+ (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
153+ }
154+ } else {
155+ data -> ramp_common .ramp_data .current_ramp_state = STEPPER_RAMP_STATE_NOT_MOVING ;
156+ }
157+ #endif
112158 if (data -> step_count > 0 ) {
113159 data -> step_count -- ;
114160 (void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
@@ -124,9 +170,10 @@ static void update_remaining_steps(struct gpio_stepper_data *data)
124170 }
125171}
126172
127- static void update_direction_from_step_count (const struct device * dev )
173+ static bool update_direction_from_step_count (const struct device * dev )
128174{
129175 struct gpio_stepper_data * data = dev -> data ;
176+ enum stepper_direction direction = data -> direction ;
130177
131178 if (data -> step_count > 0 ) {
132179 data -> direction = STEPPER_DIRECTION_POSITIVE ;
@@ -135,6 +182,10 @@ static void update_direction_from_step_count(const struct device *dev)
135182 } else {
136183 LOG_ERR ("Step count is zero" );
137184 }
185+ if (data -> direction != direction ) {
186+ return true;
187+ }
188+ return false;
138189}
139190
140191static void position_mode_task (const struct device * dev )
@@ -178,6 +229,23 @@ static void stepper_work_step_handler(struct k_work *work)
178229 }
179230}
180231
232+ static bool is_step_timing_valid (const struct device * dev )
233+ {
234+ struct gpio_stepper_data * data = dev -> data ;
235+ #ifdef CONFIG_STEPPER_RAMP
236+
237+ if (data -> ramp_common .ramp_profile .max_velocity > 0 ) {
238+ return true;
239+ }
240+ return false;
241+ #else
242+ if (data -> delay_in_ns > 0 ) {
243+ return true;
244+ }
245+ return false;
246+ #endif
247+ }
248+
181249static int gpio_stepper_move_by (const struct device * dev , int32_t micro_steps )
182250{
183251 struct gpio_stepper_data * data = dev -> data ;
@@ -187,14 +255,29 @@ static int gpio_stepper_move_by(const struct device *dev, int32_t micro_steps)
187255 return - ECANCELED ;
188256 }
189257
190- if (data -> delay_in_ns == 0 ) {
191- LOG_ERR ("Step interval not set or invalid step interval set" );
258+ if (!is_step_timing_valid (dev )) {
192259 return - EINVAL ;
193260 }
261+
262+ if (micro_steps == 0 ) {
263+ LOG_WRN ("Step count is zero" );
264+ return 0 ;
265+ }
266+
194267 K_SPINLOCK (& data -> lock ) {
195268 data -> run_mode = STEPPER_RUN_MODE_POSITION ;
196269 data -> step_count = micro_steps ;
197- update_direction_from_step_count (dev );
270+ bool is_dir_changed = update_direction_from_step_count (dev );
271+ #ifdef CONFIG_STEPPER_RAMP
272+ const struct gpio_stepper_config * config = dev -> config ;
273+ uint32_t steps_to_move = abs (micro_steps );
274+
275+ trapezoidal_ramp_api .reset_ramp_data (& config -> ramp_config , & data -> ramp_common ,
276+ & data -> delay_in_ns , is_dir_changed ,
277+ steps_to_move );
278+ #else
279+ ARG_UNUSED (is_dir_changed );
280+ #endif
198281 (void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
199282 }
200283 return 0 ;
@@ -229,14 +312,24 @@ static int gpio_stepper_move_to(const struct device *dev, int32_t micro_steps)
229312 return - ECANCELED ;
230313 }
231314
232- if (data -> delay_in_ns == 0 ) {
233- LOG_ERR ("Step interval not set or invalid step interval set" );
315+ if (!is_step_timing_valid (dev )) {
234316 return - EINVAL ;
235317 }
318+
236319 K_SPINLOCK (& data -> lock ) {
237320 data -> run_mode = STEPPER_RUN_MODE_POSITION ;
238321 data -> step_count = micro_steps - data -> actual_position ;
239- update_direction_from_step_count (dev );
322+ bool is_dir_changed = update_direction_from_step_count (dev );
323+ #ifdef CONFIG_STEPPER_RAMP
324+ const struct gpio_stepper_config * config = dev -> config ;
325+ uint32_t steps_to_move = abs (micro_steps - data -> actual_position );
326+
327+ trapezoidal_ramp_api .reset_ramp_data (& config -> ramp_config , & data -> ramp_common ,
328+ & data -> delay_in_ns , is_dir_changed ,
329+ steps_to_move );
330+ #else
331+ ARG_UNUSED (is_dir_changed );
332+ #endif
240333 (void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
241334 }
242335 return 0 ;
@@ -268,6 +361,23 @@ static int gpio_stepper_set_microstep_interval(const struct device *dev,
268361 return 0 ;
269362}
270363
364+ #ifdef CONFIG_STEPPER_RAMP
365+
366+ static int gpio_stepper_set_ramp_profile (const struct device * dev ,
367+ const struct stepper_ramp_profile * const ramp_profile )
368+ {
369+ struct gpio_stepper_data * data = dev -> data ;
370+
371+ K_SPINLOCK (& data -> lock ) {
372+ data -> ramp_common .ramp_profile .acceleration = ramp_profile -> acceleration ;
373+ data -> ramp_common .ramp_profile .max_velocity = ramp_profile -> max_velocity ;
374+ data -> ramp_common .ramp_profile .deceleration = ramp_profile -> deceleration ;
375+ }
376+ return 0 ;
377+ }
378+
379+ #endif
380+
271381static int gpio_stepper_run (const struct device * dev , const enum stepper_direction direction )
272382{
273383 struct gpio_stepper_data * data = dev -> data ;
@@ -350,6 +460,9 @@ static int gpio_stepper_disable(const struct device *dev)
350460 int err ;
351461
352462 K_SPINLOCK (& data -> lock ) {
463+ #ifdef CONFIG_STEPPER_RAMP
464+ data -> ramp_common .ramp_data .current_ramp_state = STEPPER_RAMP_STATE_NOT_MOVING ;
465+ #endif
353466 (void )k_work_cancel_delayable (& data -> stepper_dwork );
354467 err = energize_coils (dev , false);
355468 if (err == 0 ) {
@@ -385,6 +498,7 @@ static int gpio_stepper_init(const struct device *dev)
385498 for (uint8_t n_pin = 0 ; n_pin < NUM_CONTROL_PINS ; n_pin ++ ) {
386499 (void )gpio_pin_configure_dt (& config -> control_pins [n_pin ], GPIO_OUTPUT_INACTIVE );
387500 }
501+
388502 k_work_init_delayable (& data -> stepper_dwork , stepper_work_step_handler );
389503 return 0 ;
390504}
@@ -403,20 +517,29 @@ static DEVICE_API(stepper, gpio_stepper_api) = {
403517 .run = gpio_stepper_run ,
404518 .stop = gpio_stepper_stop ,
405519 .is_moving = gpio_stepper_is_moving ,
520+ IF_ENABLED (CONFIG_STEPPER_RAMP , (.set_ramp_profile = gpio_stepper_set_ramp_profile ,))
521+
406522};
407523
408524#define GPIO_STEPPER_DEFINE (inst ) \
409525 static const struct gpio_dt_spec gpio_stepper_motor_control_pins_##inst[] = { \
410526 DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \
411527 }; \
412528 BUILD_ASSERT(ARRAY_SIZE(gpio_stepper_motor_control_pins_##inst) == 4, \
413- "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
529+ "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
414530 static const struct gpio_stepper_config gpio_stepper_config_##inst = { \
415531 .invert_direction = DT_INST_PROP(inst, invert_direction), \
416- .control_pins = gpio_stepper_motor_control_pins_##inst}; \
532+ .control_pins = gpio_stepper_motor_control_pins_##inst, \
533+ IF_ENABLED(CONFIG_STEPPER_RAMP, \
534+ (.ramp_config.pre_deceleration_steps = DT_INST_PROP(inst, pre_decel_steps))) }; \
417535 static struct gpio_stepper_data gpio_stepper_data_##inst = { \
418536 .step_gap = MAX_MICRO_STEP_RES >> (DT_INST_PROP(inst, micro_step_res) - 1), \
419- }; \
537+ IF_ENABLED(CONFIG_STEPPER_RAMP, \
538+ (.ramp_common.ramp_data.ramp_stop_step_interval_threshold_in_ns = \
539+ DT_INST_PROP(inst, ramp_stop_step_interval), \
540+ .ramp_common.ramp_profile.acceleration = DT_INST_PROP(inst, acceleration), \
541+ .ramp_common.ramp_profile.deceleration = DT_INST_PROP(inst, deceleration), \
542+ .ramp_common.ramp_profile.max_velocity = DT_INST_PROP(inst, max_speed),)) }; \
420543 BUILD_ASSERT(DT_INST_PROP(inst, micro_step_res) <= STEPPER_MICRO_STEP_2, \
421544 "gpio_stepper_controller driver supports up to 2 micro steps"); \
422545 DEVICE_DT_INST_DEFINE(inst, gpio_stepper_init, NULL, &gpio_stepper_data_##inst, \
0 commit comments