1
1
/*
2
2
* 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
4
4
* SPDX-License-Identifier: Apache-2.0
5
5
*/
6
6
10
10
#include <zephyr/kernel.h>
11
11
#include <zephyr/sys_clock.h>
12
12
#include <zephyr/drivers/stepper.h>
13
+ #include "stepper_common.h"
13
14
#include <zephyr/sys/__assert.h>
14
15
16
+ #ifdef CONFIG_STEPPER_RAMP
17
+ #include <stdlib.h>
18
+ #include "ramp/ramp.h"
19
+ #endif
20
+
15
21
#include <zephyr/logging/log.h>
16
22
LOG_MODULE_REGISTER (gpio_stepper_motor_controller , CONFIG_STEPPER_LOG_LEVEL );
17
23
@@ -26,6 +32,10 @@ static const uint8_t
26
32
struct gpio_stepper_config {
27
33
const struct gpio_dt_spec * control_pins ;
28
34
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
29
39
};
30
40
31
41
struct gpio_stepper_data {
@@ -42,6 +52,9 @@ struct gpio_stepper_data {
42
52
bool is_enabled ;
43
53
stepper_event_callback_t callback ;
44
54
void * event_cb_user_data ;
55
+ #ifdef CONFIG_STEPPER_RAMP
56
+ struct stepper_ramp_common ramp_common ;
57
+ #endif
45
58
};
46
59
47
60
static int stepper_motor_set_coil_charge (const struct device * dev )
@@ -98,6 +111,24 @@ static void update_coil_charge(const struct device *dev)
98
111
const struct gpio_stepper_config * config = dev -> config ;
99
112
struct gpio_stepper_data * data = dev -> data ;
100
113
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
+
101
132
if (data -> direction == STEPPER_DIRECTION_POSITIVE ) {
102
133
config -> invert_direction ? decrement_coil_charge (dev ) : increment_coil_charge (dev );
103
134
data -> actual_position ++ ;
@@ -111,16 +142,30 @@ static void update_remaining_steps(const struct device *dev)
111
142
{
112
143
struct gpio_stepper_data * data = dev -> data ;
113
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
114
158
if (data -> step_count > 0 ) {
115
159
data -> step_count -- ;
116
160
} else if (data -> step_count < 0 ) {
117
161
data -> step_count ++ ;
118
162
}
119
163
}
120
164
121
- static void update_direction_from_step_count (const struct device * dev )
165
+ static bool update_direction_from_step_count (const struct device * dev )
122
166
{
123
167
struct gpio_stepper_data * data = dev -> data ;
168
+ enum stepper_direction direction = data -> direction ;
124
169
125
170
if (data -> step_count > 0 ) {
126
171
data -> direction = STEPPER_DIRECTION_POSITIVE ;
@@ -129,6 +174,10 @@ static void update_direction_from_step_count(const struct device *dev)
129
174
} else {
130
175
LOG_ERR ("Step count is zero" );
131
176
}
177
+ if (data -> direction != direction ) {
178
+ return true;
179
+ }
180
+ return false;
132
181
}
133
182
134
183
static void position_mode_task (const struct device * dev )
@@ -139,6 +188,12 @@ static void position_mode_task(const struct device *dev)
139
188
(void )stepper_motor_set_coil_charge (dev );
140
189
update_coil_charge (dev );
141
190
if (data -> step_count ) {
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
142
197
(void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
143
198
} else {
144
199
if (data -> callback ) {
@@ -155,6 +210,14 @@ static void velocity_mode_task(const struct device *dev)
155
210
156
211
(void )stepper_motor_set_coil_charge (dev );
157
212
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
+
158
221
(void )k_work_reschedule (& data -> stepper_dwork , K_NSEC (data -> delay_in_ns ));
159
222
}
160
223
@@ -179,6 +242,23 @@ static void stepper_work_step_handler(struct k_work *work)
179
242
}
180
243
}
181
244
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
+
182
262
static int gpio_stepper_move_by (const struct device * dev , int32_t micro_steps )
183
263
{
184
264
struct gpio_stepper_data * data = dev -> data ;
@@ -188,14 +268,36 @@ static int gpio_stepper_move_by(const struct device *dev, int32_t micro_steps)
188
268
return - ECANCELED ;
189
269
}
190
270
191
- if (data -> delay_in_ns == 0 ) {
192
- LOG_ERR ("Step interval not set or invalid step interval set" );
271
+ if (!is_step_timing_valid (dev )) {
193
272
return - EINVAL ;
194
273
}
274
+
275
+ if (micro_steps == 0 ) {
276
+ LOG_WRN ("Step count is zero" );
277
+ return 0 ;
278
+ }
279
+
195
280
K_SPINLOCK (& data -> lock ) {
196
281
data -> run_mode = STEPPER_RUN_MODE_POSITION ;
197
282
data -> step_count = micro_steps ;
198
- 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
+ data -> ramp_common .ramp_runtime_data .is_stepper_moving = is_stepper_moving ;
290
+ data -> ramp_common .ramp_runtime_data .is_stepper_dir_changed = is_dir_changed ;
291
+ config -> ramp_api -> reset_ramp_runtime_data (& config -> ramp_config , & data -> ramp_common ,
292
+ steps_to_move );
293
+ if (!is_stepper_moving ) {
294
+ data -> delay_in_ns = config -> ramp_api -> calculate_start_interval (
295
+ data -> ramp_common .ramp_profile .acceleration );
296
+ }
297
+ config -> ramp_api -> recalculate_ramp (& data -> ramp_common , steps_to_move );
298
+ #else
299
+ ARG_UNUSED (is_dir_changed );
300
+ #endif
199
301
(void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
200
302
}
201
303
return 0 ;
@@ -258,6 +360,23 @@ static int gpio_stepper_set_microstep_interval(const struct device *dev,
258
360
return 0 ;
259
361
}
260
362
363
+ #ifdef CONFIG_STEPPER_RAMP
364
+
365
+ static int gpio_stepper_set_ramp_profile (const struct device * dev ,
366
+ const struct stepper_ramp_profile * const ramp_profile )
367
+ {
368
+ struct gpio_stepper_data * data = dev -> data ;
369
+
370
+ K_SPINLOCK (& data -> lock ) {
371
+ data -> ramp_common .ramp_profile .acceleration = ramp_profile -> acceleration ;
372
+ data -> ramp_common .ramp_profile .max_velocity = ramp_profile -> max_velocity ;
373
+ data -> ramp_common .ramp_profile .deceleration = ramp_profile -> deceleration ;
374
+ }
375
+ return 0 ;
376
+ }
377
+
378
+ #endif
379
+
261
380
static int gpio_stepper_run (const struct device * dev , const enum stepper_direction direction )
262
381
{
263
382
struct gpio_stepper_data * data = dev -> data ;
@@ -269,7 +388,23 @@ static int gpio_stepper_run(const struct device *dev, const enum stepper_directi
269
388
270
389
K_SPINLOCK (& data -> lock ) {
271
390
data -> run_mode = STEPPER_RUN_MODE_VELOCITY ;
391
+ bool is_dir_changed = direction != data -> direction ;
272
392
data -> direction = direction ;
393
+ #ifdef CONFIG_STEPPER_RAMP
394
+ const struct gpio_stepper_config * config = dev -> config ;
395
+ const bool is_stepper_moving = k_work_delayable_is_pending (& data -> stepper_dwork );
396
+
397
+ data -> ramp_common .ramp_runtime_data .is_stepper_moving = is_stepper_moving ;
398
+ data -> ramp_common .ramp_runtime_data .is_stepper_dir_changed = is_dir_changed ;
399
+ config -> ramp_api -> reset_ramp_runtime_data (& config -> ramp_config , & data -> ramp_common ,
400
+ UINT32_MAX );
401
+ if (!is_stepper_moving ) {
402
+ data -> delay_in_ns = config -> ramp_api -> calculate_start_interval (
403
+ data -> ramp_common .ramp_profile .acceleration );
404
+ }
405
+ #else
406
+ ARG_UNUSED (is_dir_changed );
407
+ #endif
273
408
(void )k_work_reschedule (& data -> stepper_dwork , K_NO_WAIT );
274
409
}
275
410
return 0 ;
@@ -393,20 +528,27 @@ static DEVICE_API(stepper, gpio_stepper_api) = {
393
528
.run = gpio_stepper_run ,
394
529
.stop = gpio_stepper_stop ,
395
530
.is_moving = gpio_stepper_is_moving ,
531
+ IF_ENABLED (CONFIG_STEPPER_RAMP , (.set_ramp_profile = gpio_stepper_set_ramp_profile ,))
396
532
};
397
533
398
534
#define GPIO_STEPPER_DEFINE (inst ) \
399
535
static const struct gpio_dt_spec gpio_stepper_motor_control_pins_##inst[] = { \
400
536
DT_INST_FOREACH_PROP_ELEM_SEP(inst, gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)), \
401
537
}; \
402
538
BUILD_ASSERT(ARRAY_SIZE(gpio_stepper_motor_control_pins_##inst) == 4, \
403
- "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
539
+ "gpio_stepper_controller driver currently supports only 4 wire configuration"); \
404
540
static const struct gpio_stepper_config gpio_stepper_config_##inst = { \
405
541
.invert_direction = DT_INST_PROP(inst, invert_direction), \
406
- .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))) }; \
407
546
static struct gpio_stepper_data gpio_stepper_data_##inst = { \
408
547
.step_gap = MAX_MICRO_STEP_RES >> (DT_INST_PROP(inst, micro_step_res) - 1), \
409
- }; \
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),)) }; \
410
552
BUILD_ASSERT(DT_INST_PROP(inst, micro_step_res) <= STEPPER_MICRO_STEP_2, \
411
553
"gpio_stepper_controller driver supports up to 2 micro steps"); \
412
554
DEVICE_DT_INST_DEFINE(inst, gpio_stepper_init, NULL, &gpio_stepper_data_##inst, \
0 commit comments