1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/ztest.h>
7 #include <zephyr/drivers/stepper.h>
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(stepper_api, CONFIG_STEPPER_LOG_LEVEL);
11 
12 struct stepper_fixture {
13 	const struct device *dev;
14 	stepper_event_callback_t callback;
15 };
16 
17 struct k_poll_signal stepper_signal;
18 struct k_poll_event stepper_event;
19 void *user_data_received;
20 
21 #define POLL_AND_CHECK_SIGNAL(signal, event, expected_event, timeout)                              \
22 	({                                                                                         \
23 		do {                                                                               \
24 			(void)k_poll(&(event), 1, timeout);                                        \
25 			unsigned int signaled;                                                     \
26 			int result;                                                                \
27 			k_poll_signal_check(&(signal), &signaled, &result);                        \
28 			zassert_equal(signaled, 1, "Signal not set");                              \
29 			zassert_equal(result, (expected_event), "Signal not set");                 \
30 		} while (0);                                                                       \
31 	})
32 
stepper_print_event_callback(const struct device * dev,enum stepper_event event,void * user_data)33 static void stepper_print_event_callback(const struct device *dev, enum stepper_event event,
34 					 void *user_data)
35 {
36 	const struct device *dev_callback = user_data;
37 	user_data_received = user_data;
38 
39 	switch (event) {
40 	case STEPPER_EVENT_STEPS_COMPLETED:
41 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STEPS_COMPLETED);
42 		break;
43 	case STEPPER_EVENT_LEFT_END_STOP_DETECTED:
44 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_LEFT_END_STOP_DETECTED);
45 		break;
46 	case STEPPER_EVENT_RIGHT_END_STOP_DETECTED:
47 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_RIGHT_END_STOP_DETECTED);
48 		break;
49 	case STEPPER_EVENT_STALL_DETECTED:
50 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STALL_DETECTED);
51 		break;
52 	case STEPPER_EVENT_STOPPED:
53 		k_poll_signal_raise(&stepper_signal, STEPPER_EVENT_STOPPED);
54 		break;
55 	default:
56 		break;
57 	}
58 
59 	LOG_DBG("Event %d, %s called for %s, expected for %s\n", event, __func__,
60 		dev_callback->name, dev->name);
61 }
62 
stepper_setup(void)63 static void *stepper_setup(void)
64 {
65 	static struct stepper_fixture fixture = {
66 		.dev = DEVICE_DT_GET(DT_ALIAS(stepper)),
67 		.callback = stepper_print_event_callback,
68 	};
69 
70 	k_poll_signal_init(&stepper_signal);
71 	k_poll_event_init(&stepper_event, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
72 			  &stepper_signal);
73 
74 	zassert_not_null(fixture.dev);
75 	(void)stepper_enable(fixture.dev, true);
76 	return &fixture;
77 }
78 
stepper_before(void * f)79 static void stepper_before(void *f)
80 {
81 	struct stepper_fixture *fixture = f;
82 	(void)stepper_set_reference_position(fixture->dev, 0);
83 
84 	k_poll_signal_reset(&stepper_signal);
85 
86 	user_data_received = NULL;
87 }
88 
89 ZTEST_SUITE(stepper, NULL, stepper_setup, stepper_before, NULL, NULL);
90 
ZTEST_F(stepper,test_set_micro_step_res_incorrect)91 ZTEST_F(stepper, test_set_micro_step_res_incorrect)
92 {
93 	int ret = stepper_set_micro_step_res(fixture->dev, 127);
94 
95 	zassert_equal(ret, -ENOTSUP, "Incorrect micro step resolution should return -ENOTSUP");
96 }
97 
ZTEST_F(stepper,test_get_micro_step_res)98 ZTEST_F(stepper, test_get_micro_step_res)
99 {
100 	enum stepper_micro_step_resolution res;
101 	(void)stepper_get_micro_step_res(fixture->dev, &res);
102 	zassert_equal(res, DT_PROP(DT_ALIAS(stepper), micro_step_res),
103 		      "Micro step resolution not set correctly");
104 }
105 
ZTEST_F(stepper,test_set_micro_step_interval_invalid_zero)106 ZTEST_F(stepper, test_set_micro_step_interval_invalid_zero)
107 {
108 	int err = stepper_set_microstep_interval(fixture->dev, 0);
109 
110 	zassert_equal(err, -EINVAL, "ustep interval cannot be zero");
111 }
112 
ZTEST_F(stepper,test_actual_position)113 ZTEST_F(stepper, test_actual_position)
114 {
115 	int32_t pos = 100u;
116 
117 	(void)stepper_set_reference_position(fixture->dev, pos);
118 	(void)stepper_get_actual_position(fixture->dev, &pos);
119 	zassert_equal(pos, 100u, "Actual position not set correctly");
120 }
121 
ZTEST_F(stepper,test_target_position_w_fixed_step_interval)122 ZTEST_F(stepper, test_target_position_w_fixed_step_interval)
123 {
124 	int32_t pos = 10u;
125 
126 	(void)stepper_set_microstep_interval(fixture->dev, 100 * USEC_PER_SEC);
127 
128 	/* Pass the function name as user data */
129 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, (void *)fixture->dev);
130 
131 	(void)stepper_move_to(fixture->dev, pos);
132 
133 	/* timeout is set with 20% tolerance */
134 	POLL_AND_CHECK_SIGNAL(stepper_signal, stepper_event, STEPPER_EVENT_STEPS_COMPLETED,
135 			      K_MSEC(pos * 120));
136 
137 	(void)stepper_get_actual_position(fixture->dev, &pos);
138 	zassert_equal(pos, 10u, "Target position should be %d but is %d", 10u, pos);
139 	zassert_equal(user_data_received, fixture->dev, "User data not received");
140 }
141 
ZTEST_F(stepper,test_stop)142 ZTEST_F(stepper, test_stop)
143 {
144 	(void)stepper_set_event_callback(fixture->dev, fixture->callback, (void *)fixture->dev);
145 
146 	/* Run the stepper in positive direction */
147 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
148 
149 	/* Stop the stepper */
150 	int ret = stepper_stop(fixture->dev);
151 	bool is_moving;
152 
153 	if (ret == 0) {
154 		POLL_AND_CHECK_SIGNAL(stepper_signal, stepper_event, STEPPER_EVENT_STOPPED,
155 				      K_NO_WAIT);
156 		zassert_equal(user_data_received, fixture->dev, "User data not received");
157 
158 		/* Check if the stepper is stopped */
159 		stepper_is_moving(fixture->dev, &is_moving);
160 		zassert_equal(is_moving, false, "Stepper is still moving");
161 	} else if (ret == -ENOSYS) {
162 		stepper_is_moving(fixture->dev, &is_moving);
163 		zassert_equal(is_moving, true,
164 			      "Stepper should be moving since stop is not implemented");
165 	} else {
166 		zassert_unreachable("Stepper stop failed");
167 	}
168 }
169