1 /*
2  * Copyright 2024 Navimatix GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/gpio.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/ztest_assert.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <zephyr/ztest.h>
13 #include <zephyr/drivers/stepper.h>
14 
15 struct drv8424_api_fixture {
16 	const struct device *dev;
17 	stepper_event_callback_t callback;
18 };
19 
20 struct k_poll_signal stepper_signal;
21 struct k_poll_event stepper_event;
22 
drv8424_api_print_event_callback(const struct device * dev,enum stepper_event event,void * dummy)23 static void drv8424_api_print_event_callback(const struct device *dev, enum stepper_event event,
24 					     void *dummy)
25 {
26 	switch (event) {
27 	case STEPPER_EVENT_STEPS_COMPLETED:
28 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STEPS_COMPLETED);
29 		break;
30 	case STEPPER_EVENT_LEFT_END_STOP_DETECTED:
31 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_LEFT_END_STOP_DETECTED);
32 		break;
33 	case STEPPER_EVENT_RIGHT_END_STOP_DETECTED:
34 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_RIGHT_END_STOP_DETECTED);
35 		break;
36 	case STEPPER_EVENT_STALL_DETECTED:
37 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STALL_DETECTED);
38 		break;
39 	default:
40 		break;
41 	}
42 }
43 
drv8424_api_setup(void)44 static void *drv8424_api_setup(void)
45 {
46 	static struct drv8424_api_fixture fixture = {
47 		.dev = DEVICE_DT_GET(DT_NODELABEL(drv8424)),
48 		.callback = drv8424_api_print_event_callback,
49 	};
50 
51 	k_poll_signal_init(&stepper_signal);
52 	k_poll_event_init(&stepper_event, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
53 			  &stepper_signal);
54 
55 	zassert_not_null(fixture.dev);
56 	return &fixture;
57 }
58 
drv8424_api_before(void * f)59 static void drv8424_api_before(void *f)
60 {
61 	struct drv8424_api_fixture *fixture = f;
62 	(void)stepper_set_reference_position(fixture->dev, 0);
63 	(void)stepper_set_micro_step_res(fixture->dev, 1);
64 	k_poll_signal_reset(&stepper_signal);
65 }
66 
drv8424_api_after(void * f)67 static void drv8424_api_after(void *f)
68 {
69 	struct drv8424_api_fixture *fixture = f;
70 	(void)stepper_enable(fixture->dev, false);
71 }
72 
ZTEST_F(drv8424_api,test_micro_step_res_set)73 ZTEST_F(drv8424_api, test_micro_step_res_set)
74 {
75 	(void)stepper_set_micro_step_res(fixture->dev, 4);
76 	enum stepper_micro_step_resolution res;
77 	(void)stepper_get_micro_step_res(fixture->dev, &res);
78 	zassert_equal(res, 4, "Micro step resolution not set correctly, should be %d but is %d", 4,
79 		      res);
80 }
81 
ZTEST_F(drv8424_api,test_actual_position_set)82 ZTEST_F(drv8424_api, test_actual_position_set)
83 {
84 	int32_t pos = 100u;
85 	(void)stepper_set_reference_position(fixture->dev, pos);
86 	(void)stepper_get_actual_position(fixture->dev, &pos);
87 	zassert_equal(pos, 100u, "Actual position should be %u but is %u", 100u, pos);
88 }
89 
ZTEST_F(drv8424_api,test_is_not_moving_when_disabled)90 ZTEST_F(drv8424_api, test_is_not_moving_when_disabled)
91 {
92 	int32_t steps = 100;
93 	bool moving = true;
94 
95 	(void)stepper_enable(fixture->dev, true);
96 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
97 	(void)stepper_move_by(fixture->dev, steps);
98 	(void)stepper_enable(fixture->dev, false);
99 	(void)stepper_is_moving(fixture->dev, &moving);
100 	zassert_false(moving, "Driver should not be in state is_moving after being disabled");
101 }
102 
ZTEST_F(drv8424_api,test_position_not_updating_when_disabled)103 ZTEST_F(drv8424_api, test_position_not_updating_when_disabled)
104 {
105 	int32_t steps = 1000;
106 	int32_t position_1 = 0;
107 	int32_t position_2 = 0;
108 
109 	(void)stepper_enable(fixture->dev, true);
110 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
111 	(void)stepper_move_by(fixture->dev, steps);
112 	(void)stepper_enable(fixture->dev, false);
113 	(void)stepper_get_actual_position(fixture->dev, &position_1);
114 	k_msleep(100);
115 	(void)stepper_get_actual_position(fixture->dev, &position_2);
116 	zassert_equal(position_2, position_1,
117 		      "Actual position should not have changed from %d but is %d", position_1,
118 		      position_2);
119 }
120 
ZTEST_F(drv8424_api,test_is_not_moving_when_reenabled_after_movement)121 ZTEST_F(drv8424_api, test_is_not_moving_when_reenabled_after_movement)
122 {
123 	int32_t steps = 1000;
124 	bool moving = true;
125 
126 	(void)stepper_enable(fixture->dev, true);
127 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
128 	(void)stepper_move_by(fixture->dev, steps);
129 	(void)stepper_enable(fixture->dev, false);
130 	(void)k_msleep(100);
131 	(void)stepper_enable(fixture->dev, true);
132 	(void)k_msleep(100);
133 	(void)stepper_is_moving(fixture->dev, &moving);
134 	zassert_false(moving, "Driver should not be in state is_moving after being reenabled");
135 }
ZTEST_F(drv8424_api,test_position_not_updating_when_reenabled_after_movement)136 ZTEST_F(drv8424_api, test_position_not_updating_when_reenabled_after_movement)
137 {
138 	int32_t steps = 1000;
139 	int32_t position_1 = 0;
140 	int32_t position_2 = 0;
141 
142 	(void)stepper_enable(fixture->dev, true);
143 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
144 	(void)stepper_move_by(fixture->dev, steps);
145 	(void)stepper_enable(fixture->dev, false);
146 	(void)stepper_get_actual_position(fixture->dev, &position_1);
147 	(void)k_msleep(100);
148 	(void)stepper_enable(fixture->dev, true);
149 	(void)k_msleep(100);
150 	(void)stepper_get_actual_position(fixture->dev, &position_2);
151 	zassert_equal(position_2, position_1,
152 		      "Actual position should not have changed from %d but is %d", position_1,
153 		      position_2);
154 }
155 
ZTEST_F(drv8424_api,test_move_to_positive_direction_movement)156 ZTEST_F(drv8424_api, test_move_to_positive_direction_movement)
157 {
158 	int32_t pos = 50;
159 
160 	(void)stepper_enable(fixture->dev, true);
161 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
162 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
163 	(void)stepper_move_to(fixture->dev, pos);
164 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
165 	unsigned int signaled;
166 	int result;
167 
168 	k_poll_signal_check(&stepper_signal, &signaled, &result);
169 	zassert_equal(signaled, 1, "No event detected");
170 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
171 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
172 	(void)stepper_get_actual_position(fixture->dev, &pos);
173 	zassert_equal(pos, 50u, "Target position should be %d but is %d", 50u, pos);
174 }
175 
ZTEST_F(drv8424_api,test_move_to_negative_direction_movement)176 ZTEST_F(drv8424_api, test_move_to_negative_direction_movement)
177 {
178 	int32_t pos = -50;
179 
180 	(void)stepper_enable(fixture->dev, true);
181 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
182 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
183 	(void)stepper_move_to(fixture->dev, pos);
184 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
185 	unsigned int signaled;
186 	int result;
187 
188 	k_poll_signal_check(&stepper_signal, &signaled, &result);
189 	zassert_equal(signaled, 1, "No event detected");
190 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
191 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
192 	(void)stepper_get_actual_position(fixture->dev, &pos);
193 	zassert_equal(pos, -50, "Target position should be %d but is %d", -50, pos);
194 }
195 
ZTEST_F(drv8424_api,test_move_to_identical_current_and_target_position)196 ZTEST_F(drv8424_api, test_move_to_identical_current_and_target_position)
197 {
198 	int32_t pos = 0;
199 
200 	(void)stepper_enable(fixture->dev, true);
201 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
202 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
203 	(void)stepper_move_to(fixture->dev, pos);
204 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
205 	unsigned int signaled;
206 	int result;
207 
208 	k_poll_signal_check(&stepper_signal, &signaled, &result);
209 	zassert_equal(signaled, 1, "No event detected");
210 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
211 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
212 	(void)stepper_get_actual_position(fixture->dev, &pos);
213 	zassert_equal(pos, 0, "Target position should not have changed from %d but is %d", 0, pos);
214 }
215 
ZTEST_F(drv8424_api,test_move_to_is_moving_true_while_moving)216 ZTEST_F(drv8424_api, test_move_to_is_moving_true_while_moving)
217 {
218 	int32_t pos = 50;
219 	bool moving = false;
220 
221 	(void)stepper_enable(fixture->dev, true);
222 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
223 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
224 	(void)stepper_move_to(fixture->dev, pos);
225 	(void)stepper_is_moving(fixture->dev, &moving);
226 	zassert_true(moving, "Driver should be in state is_moving while moving");
227 }
228 
ZTEST_F(drv8424_api,test_move_to_is_moving_false_when_completed)229 ZTEST_F(drv8424_api, test_move_to_is_moving_false_when_completed)
230 {
231 	int32_t pos = 50;
232 	bool moving = false;
233 
234 	(void)stepper_enable(fixture->dev, true);
235 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
236 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
237 	(void)stepper_move_to(fixture->dev, pos);
238 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
239 	unsigned int signaled;
240 	int result;
241 
242 	k_poll_signal_check(&stepper_signal, &signaled, &result);
243 	zassert_equal(signaled, 1, "No event detected");
244 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
245 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
246 	(void)stepper_is_moving(fixture->dev, &moving);
247 	zassert_false(moving, "Driver should not be in state is_moving after finishing");
248 }
249 
ZTEST_F(drv8424_api,test_move_to_no_movement_when_disabled)250 ZTEST_F(drv8424_api, test_move_to_no_movement_when_disabled)
251 {
252 	int32_t pos = 50;
253 	int32_t curr_pos = 50;
254 	int32_t ret = 0;
255 
256 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
257 	(void)stepper_enable(fixture->dev, false);
258 
259 	ret = stepper_move_to(fixture->dev, pos);
260 	zassert_equal(ret, -ECANCELED, "Move_to should fail with error code %d but returned %d",
261 		      -ECANCELED, ret);
262 	(void)stepper_get_actual_position(fixture->dev, &curr_pos);
263 	zassert_equal(curr_pos, 0, "Current position should not have changed from %d but is %d", 0,
264 		      curr_pos);
265 }
266 
ZTEST_F(drv8424_api,test_move_by_positive_step_count)267 ZTEST_F(drv8424_api, test_move_by_positive_step_count)
268 {
269 	int32_t steps = 50;
270 
271 	(void)stepper_enable(fixture->dev, true);
272 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
273 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
274 	(void)stepper_move_by(fixture->dev, steps);
275 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
276 	unsigned int signaled;
277 	int result;
278 
279 	k_poll_signal_check(&stepper_signal, &signaled, &result);
280 	zassert_equal(signaled, 1, "No event detected");
281 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
282 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
283 	(void)stepper_get_actual_position(fixture->dev, &steps);
284 	zassert_equal(steps, 50u, "Target position should be %d but is %d", 50u, steps);
285 }
286 
ZTEST_F(drv8424_api,test_move_by_negative_step_count)287 ZTEST_F(drv8424_api, test_move_by_negative_step_count)
288 {
289 	int32_t steps = -50;
290 
291 	(void)stepper_enable(fixture->dev, true);
292 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
293 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
294 	(void)stepper_move_by(fixture->dev, steps);
295 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
296 	unsigned int signaled;
297 	int result;
298 
299 	k_poll_signal_check(&stepper_signal, &signaled, &result);
300 	zassert_equal(signaled, 1, "No event detected");
301 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
302 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
303 	(void)stepper_get_actual_position(fixture->dev, &steps);
304 	zassert_equal(steps, -50, "Target position should be %d but is %d", -50, steps);
305 }
306 
ZTEST_F(drv8424_api,test_move_by_zero_steps_no_movement)307 ZTEST_F(drv8424_api, test_move_by_zero_steps_no_movement)
308 {
309 	int32_t steps = 0;
310 
311 	(void)stepper_enable(fixture->dev, true);
312 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
313 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
314 	(void)stepper_move_by(fixture->dev, steps);
315 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
316 	unsigned int signaled;
317 	int result;
318 
319 	k_poll_signal_check(&stepper_signal, &signaled, &result);
320 	zassert_equal(signaled, 1, "No event detected");
321 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
322 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
323 	(void)stepper_get_actual_position(fixture->dev, &steps);
324 	zassert_equal(steps, 0, "Target position should be %d but is %d", 0, steps);
325 }
326 
ZTEST_F(drv8424_api,test_move_by_zero_step_interval)327 ZTEST_F(drv8424_api, test_move_by_zero_step_interval)
328 {
329 	int32_t steps = 100;
330 	int32_t ret = 0;
331 	int32_t pos = 100;
332 
333 	(void)stepper_enable(fixture->dev, true);
334 	(void)stepper_enable(fixture->dev, false);
335 	ret = stepper_move_by(fixture->dev, steps);
336 
337 	zassert_not_equal(ret, 0, "Command should fail with an error code, but returned 0");
338 	k_msleep(100);
339 	(void)stepper_get_actual_position(fixture->dev, &pos);
340 	zassert_equal(pos, 0, "Target position should not have changed from %d but is %d", 0, pos);
341 }
342 
ZTEST_F(drv8424_api,test_move_by_is_moving_true_while_moving)343 ZTEST_F(drv8424_api, test_move_by_is_moving_true_while_moving)
344 {
345 	int32_t steps = 50;
346 	bool moving = false;
347 
348 	(void)stepper_enable(fixture->dev, true);
349 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
350 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
351 	(void)stepper_move_by(fixture->dev, steps);
352 	(void)stepper_is_moving(fixture->dev, &moving);
353 	zassert_true(moving, "Driver should be in state is_moving");
354 }
355 
ZTEST_F(drv8424_api,test_move_by_is_moving_false_when_completed)356 ZTEST_F(drv8424_api, test_move_by_is_moving_false_when_completed)
357 {
358 	int32_t steps = 50;
359 	bool moving = true;
360 
361 	(void)stepper_enable(fixture->dev, true);
362 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
363 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, NULL);
364 	(void)stepper_move_by(fixture->dev, steps);
365 	(void)k_poll(&stepper_event, 1, K_SECONDS(5));
366 	unsigned int signaled;
367 	int result;
368 
369 	k_poll_signal_check(&stepper_signal, &signaled, &result);
370 	zassert_equal(signaled, 1, "No event detected");
371 	zassert_equal(result, STEPPER_EVENT_STEPS_COMPLETED,
372 		      "Event was not STEPPER_EVENT_STEPS_COMPLETED event");
373 	(void)stepper_is_moving(fixture->dev, &moving);
374 	zassert_false(moving, "Driver should not be in state is_moving after completion");
375 }
376 
ZTEST_F(drv8424_api,test_move_by_no_movement_when_disabled)377 ZTEST_F(drv8424_api, test_move_by_no_movement_when_disabled)
378 {
379 	int32_t steps = 100;
380 	int32_t curr_pos = 100;
381 	int32_t ret = 0;
382 
383 	(void)stepper_set_microstep_interval(fixture->dev, 20000000);
384 	(void)stepper_enable(fixture->dev, false);
385 
386 	ret = stepper_move_by(fixture->dev, steps);
387 	zassert_equal(ret, -ECANCELED, "Move_by should fail with error code %d but returned %d",
388 		      -ECANCELED, ret);
389 	(void)stepper_get_actual_position(fixture->dev, &curr_pos);
390 	zassert_equal(curr_pos, 0, "Current position should not have changed from %d but is %d", 0,
391 		      curr_pos);
392 }
393 
ZTEST_F(drv8424_api,test_run_positive_direction_correct_position)394 ZTEST_F(drv8424_api, test_run_positive_direction_correct_position)
395 {
396 	uint64_t step_interval = 20000000;
397 	int32_t steps = 0;
398 
399 	(void)stepper_enable(fixture->dev, true);
400 	(void)stepper_set_microstep_interval(fixture->dev, step_interval);
401 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
402 	k_busy_wait(110000);
403 
404 	(void)stepper_get_actual_position(fixture->dev, &steps);
405 	zassert_true(IN_RANGE(steps, 4, 6), "Current position should be between 4 and 6 but is %d",
406 		     steps);
407 }
408 
ZTEST_F(drv8424_api,test_run_negative_direction_correct_position)409 ZTEST_F(drv8424_api, test_run_negative_direction_correct_position)
410 {
411 	uint64_t step_interval = 20000000;
412 	int32_t steps = 0;
413 
414 	(void)stepper_enable(fixture->dev, true);
415 	(void)stepper_set_microstep_interval(fixture->dev, step_interval);
416 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_NEGATIVE);
417 	k_busy_wait(110000);
418 
419 	(void)stepper_get_actual_position(fixture->dev, &steps);
420 	zassert_true(IN_RANGE(steps, -6, 4),
421 		     "Current position should be between -6 and -4 but is %d", steps);
422 }
423 
ZTEST_F(drv8424_api,test_run_zero_step_interval_correct_position)424 ZTEST_F(drv8424_api, test_run_zero_step_interval_correct_position)
425 {
426 	uint64_t step_interval = 0;
427 	int32_t steps = 0;
428 
429 	(void)stepper_enable(fixture->dev, true);
430 	(void)stepper_set_microstep_interval(fixture->dev, step_interval);
431 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
432 	k_msleep(100);
433 
434 	zassert_equal(steps, 0, "Current position should not have changed from %d but is %d", 0,
435 		      steps);
436 }
437 
ZTEST_F(drv8424_api,test_run_is_moving_true_when_step_interval_greater_zero)438 ZTEST_F(drv8424_api, test_run_is_moving_true_when_step_interval_greater_zero)
439 {
440 	uint64_t step_interval = 20000000;
441 	bool moving = false;
442 
443 	(void)stepper_enable(fixture->dev, true);
444 	(void)stepper_set_microstep_interval(fixture->dev, step_interval);
445 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
446 	(void)stepper_is_moving(fixture->dev, &moving);
447 	zassert_true(moving, "Driver should be in state is_moving");
448 	(void)stepper_enable(fixture->dev, false);
449 }
450 
ZTEST_F(drv8424_api,test_run_no_movement_when_disabled)451 ZTEST_F(drv8424_api, test_run_no_movement_when_disabled)
452 {
453 	uint64_t step_interval = 20000000;
454 	int32_t steps = 50;
455 	int32_t ret = 0;
456 
457 	(void)stepper_enable(fixture->dev, false);
458 	(void)stepper_set_microstep_interval(fixture->dev, step_interval);
459 
460 	ret = stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
461 	zassert_equal(ret, -ECANCELED, "Run should fail with error code %d but returned %d",
462 		      -ECANCELED, ret);
463 	(void)stepper_get_actual_position(fixture->dev, &steps);
464 	zassert_equal(steps, 0, "Current position should not have changed from %d but is %d", 0,
465 		      steps);
466 }
467 
468 ZTEST_SUITE(drv8424_api, NULL, drv8424_api_setup, drv8424_api_before, drv8424_api_after, NULL);
469