1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "step_dir_stepper_common.h"
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(step_dir_stepper, CONFIG_STEPPER_LOG_LEVEL);
10
step_dir_stepper_perform_step(const struct device * dev)11 static inline int step_dir_stepper_perform_step(const struct device *dev)
12 {
13 const struct step_dir_stepper_common_config *config = dev->config;
14 struct step_dir_stepper_common_data *data = dev->data;
15 int ret;
16
17 switch (data->direction) {
18 case STEPPER_DIRECTION_POSITIVE:
19 ret = gpio_pin_set_dt(&config->dir_pin, 1);
20 break;
21 case STEPPER_DIRECTION_NEGATIVE:
22 ret = gpio_pin_set_dt(&config->dir_pin, 0);
23 break;
24 default:
25 LOG_ERR("Unsupported direction: %d", data->direction);
26 return -ENOTSUP;
27 }
28 if (ret < 0) {
29 LOG_ERR("Failed to set direction: %d", ret);
30 return ret;
31 }
32
33 ret = gpio_pin_toggle_dt(&config->step_pin);
34 if (ret < 0) {
35 LOG_ERR("Failed to toggle step pin: %d", ret);
36 return ret;
37 }
38
39 if (!config->dual_edge) {
40 ret = gpio_pin_toggle_dt(&config->step_pin);
41 if (ret < 0) {
42 LOG_ERR("Failed to toggle step pin: %d", ret);
43 return ret;
44 }
45 }
46
47 if (data->direction == STEPPER_DIRECTION_POSITIVE) {
48 data->actual_position++;
49 } else {
50 data->actual_position--;
51 }
52
53 return 0;
54 }
55
stepper_trigger_callback(const struct device * dev,enum stepper_event event)56 static void stepper_trigger_callback(const struct device *dev, enum stepper_event event)
57 {
58 struct step_dir_stepper_common_data *data = dev->data;
59
60 if (!data->callback) {
61 LOG_WRN_ONCE("No callback set");
62 return;
63 }
64
65 if (!k_is_in_isr()) {
66 data->callback(dev, event, data->event_cb_user_data);
67 return;
68 }
69
70 #ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
71 /* Dispatch to msgq instead of raising directly */
72 int ret = k_msgq_put(&data->event_msgq, &event, K_NO_WAIT);
73
74 if (ret != 0) {
75 LOG_WRN("Failed to put event in msgq: %d", ret);
76 }
77
78 ret = k_work_submit(&data->event_callback_work);
79 if (ret < 0) {
80 LOG_ERR("Failed to submit work item: %d", ret);
81 }
82 #else
83 LOG_WRN_ONCE("Event callback called from ISR context without ISR safe events enabled");
84 #endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
85 }
86
87 #ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
stepper_work_event_handler(struct k_work * work)88 static void stepper_work_event_handler(struct k_work *work)
89 {
90 struct step_dir_stepper_common_data *data =
91 CONTAINER_OF(work, struct step_dir_stepper_common_data, event_callback_work);
92 enum stepper_event event;
93 int ret;
94
95 ret = k_msgq_get(&data->event_msgq, &event, K_NO_WAIT);
96 if (ret != 0) {
97 return;
98 }
99
100 /* Run the callback */
101 if (data->callback != NULL) {
102 data->callback(data->dev, event, data->event_cb_user_data);
103 }
104
105 /* If there are more pending events, resubmit this work item to handle them */
106 if (k_msgq_num_used_get(&data->event_msgq) > 0) {
107 k_work_submit(work);
108 }
109 }
110 #endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
111
update_remaining_steps(struct step_dir_stepper_common_data * data)112 static void update_remaining_steps(struct step_dir_stepper_common_data *data)
113 {
114 const struct step_dir_stepper_common_config *config = data->dev->config;
115
116 if (data->step_count > 0) {
117 data->step_count--;
118 } else if (data->step_count < 0) {
119 data->step_count++;
120 } else {
121 stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED);
122 config->timing_source->stop(data->dev);
123 }
124 }
125
update_direction_from_step_count(const struct device * dev)126 static void update_direction_from_step_count(const struct device *dev)
127 {
128 struct step_dir_stepper_common_data *data = dev->data;
129
130 if (data->step_count > 0) {
131 data->direction = STEPPER_DIRECTION_POSITIVE;
132 } else if (data->step_count < 0) {
133 data->direction = STEPPER_DIRECTION_NEGATIVE;
134 } else {
135 LOG_ERR("Step count is zero");
136 }
137 }
138
position_mode_task(const struct device * dev)139 static void position_mode_task(const struct device *dev)
140 {
141 struct step_dir_stepper_common_data *data = dev->data;
142 const struct step_dir_stepper_common_config *config = dev->config;
143
144 if (data->step_count) {
145 (void)step_dir_stepper_perform_step(dev);
146 }
147
148 update_remaining_steps(dev->data);
149
150 if (config->timing_source->needs_reschedule(dev) && data->step_count != 0) {
151 (void)config->timing_source->start(dev);
152 }
153 }
154
velocity_mode_task(const struct device * dev)155 static void velocity_mode_task(const struct device *dev)
156 {
157 const struct step_dir_stepper_common_config *config = dev->config;
158
159 (void)step_dir_stepper_perform_step(dev);
160
161 if (config->timing_source->needs_reschedule(dev)) {
162 (void)config->timing_source->start(dev);
163 }
164 }
165
stepper_handle_timing_signal(const struct device * dev)166 void stepper_handle_timing_signal(const struct device *dev)
167 {
168 struct step_dir_stepper_common_data *data = dev->data;
169
170 K_SPINLOCK(&data->lock) {
171 switch (data->run_mode) {
172 case STEPPER_RUN_MODE_POSITION:
173 position_mode_task(dev);
174 break;
175 case STEPPER_RUN_MODE_VELOCITY:
176 velocity_mode_task(dev);
177 break;
178 default:
179 LOG_WRN("Unsupported run mode: %d", data->run_mode);
180 break;
181 }
182 }
183 }
184
step_dir_stepper_common_init(const struct device * dev)185 int step_dir_stepper_common_init(const struct device *dev)
186 {
187 const struct step_dir_stepper_common_config *config = dev->config;
188 int ret;
189
190 if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) {
191 LOG_ERR("GPIO pins are not ready");
192 return -ENODEV;
193 }
194
195 ret = gpio_pin_configure_dt(&config->step_pin, GPIO_OUTPUT);
196 if (ret < 0) {
197 LOG_ERR("Failed to configure step pin: %d", ret);
198 return ret;
199 }
200
201 ret = gpio_pin_configure_dt(&config->dir_pin, GPIO_OUTPUT);
202 if (ret < 0) {
203 LOG_ERR("Failed to configure dir pin: %d", ret);
204 return ret;
205 }
206
207 if (config->timing_source->init) {
208 ret = config->timing_source->init(dev);
209 if (ret < 0) {
210 LOG_ERR("Failed to initialize timing source: %d", ret);
211 return ret;
212 }
213 }
214
215 #ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
216 struct step_dir_stepper_common_data *data = dev->data;
217
218 k_msgq_init(&data->event_msgq, data->event_msgq_buffer, sizeof(enum stepper_event),
219 CONFIG_STEPPER_STEP_DIR_EVENT_QUEUE_LEN);
220 k_work_init(&data->event_callback_work, stepper_work_event_handler);
221 #endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
222
223 return 0;
224 }
225
step_dir_stepper_common_move_by(const struct device * dev,const int32_t micro_steps)226 int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micro_steps)
227 {
228 struct step_dir_stepper_common_data *data = dev->data;
229 const struct step_dir_stepper_common_config *config = dev->config;
230
231 if (data->max_velocity == 0) {
232 LOG_ERR("Velocity not set or invalid velocity set");
233 return -EINVAL;
234 }
235
236 K_SPINLOCK(&data->lock) {
237 data->run_mode = STEPPER_RUN_MODE_POSITION;
238 data->step_count = micro_steps;
239 config->timing_source->update(dev, data->max_velocity);
240 update_direction_from_step_count(dev);
241 config->timing_source->start(dev);
242 }
243
244 return 0;
245 }
246
step_dir_stepper_common_set_max_velocity(const struct device * dev,const uint32_t velocity)247 int step_dir_stepper_common_set_max_velocity(const struct device *dev, const uint32_t velocity)
248 {
249 struct step_dir_stepper_common_data *data = dev->data;
250 const struct step_dir_stepper_common_config *config = dev->config;
251
252 if (velocity == 0) {
253 LOG_ERR("Velocity cannot be zero");
254 return -EINVAL;
255 }
256
257 if (velocity > USEC_PER_SEC) {
258 LOG_ERR("Velocity cannot be greater than %d micro steps per second", USEC_PER_SEC);
259 return -EINVAL;
260 }
261
262 K_SPINLOCK(&data->lock) {
263 data->max_velocity = velocity;
264 config->timing_source->update(dev, velocity);
265 }
266
267 return 0;
268 }
269
step_dir_stepper_common_set_reference_position(const struct device * dev,const int32_t value)270 int step_dir_stepper_common_set_reference_position(const struct device *dev, const int32_t value)
271 {
272 struct step_dir_stepper_common_data *data = dev->data;
273
274 K_SPINLOCK(&data->lock) {
275 data->actual_position = value;
276 }
277
278 return 0;
279 }
280
step_dir_stepper_common_get_actual_position(const struct device * dev,int32_t * value)281 int step_dir_stepper_common_get_actual_position(const struct device *dev, int32_t *value)
282 {
283 struct step_dir_stepper_common_data *data = dev->data;
284
285 K_SPINLOCK(&data->lock) {
286 *value = data->actual_position;
287 }
288
289 return 0;
290 }
291
step_dir_stepper_common_move_to(const struct device * dev,const int32_t value)292 int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value)
293 {
294 struct step_dir_stepper_common_data *data = dev->data;
295 const struct step_dir_stepper_common_config *config = dev->config;
296
297 if (data->max_velocity == 0) {
298 LOG_ERR("Velocity not set or invalid velocity set");
299 return -EINVAL;
300 }
301
302 K_SPINLOCK(&data->lock) {
303 data->run_mode = STEPPER_RUN_MODE_POSITION;
304 data->step_count = value - data->actual_position;
305 config->timing_source->update(dev, data->max_velocity);
306 update_direction_from_step_count(dev);
307 config->timing_source->start(dev);
308 }
309
310 return 0;
311 }
312
step_dir_stepper_common_is_moving(const struct device * dev,bool * is_moving)313 int step_dir_stepper_common_is_moving(const struct device *dev, bool *is_moving)
314 {
315 const struct step_dir_stepper_common_config *config = dev->config;
316
317 *is_moving = config->timing_source->is_running(dev);
318 return 0;
319 }
320
step_dir_stepper_common_run(const struct device * dev,const enum stepper_direction direction,const uint32_t velocity)321 int step_dir_stepper_common_run(const struct device *dev, const enum stepper_direction direction,
322 const uint32_t velocity)
323 {
324 struct step_dir_stepper_common_data *data = dev->data;
325 const struct step_dir_stepper_common_config *config = dev->config;
326
327 K_SPINLOCK(&data->lock) {
328 data->run_mode = STEPPER_RUN_MODE_VELOCITY;
329 data->direction = direction;
330 data->max_velocity = velocity;
331 config->timing_source->update(dev, velocity);
332 if (velocity != 0) {
333 config->timing_source->start(dev);
334 } else {
335 config->timing_source->stop(dev);
336 }
337 }
338
339 return 0;
340 }
341
step_dir_stepper_common_set_event_callback(const struct device * dev,stepper_event_callback_t callback,void * user_data)342 int step_dir_stepper_common_set_event_callback(const struct device *dev,
343 stepper_event_callback_t callback, void *user_data)
344 {
345 struct step_dir_stepper_common_data *data = dev->data;
346
347 data->callback = callback;
348 data->event_cb_user_data = user_data;
349 return 0;
350 }
351