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