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 	zassert_equal(
76 		stepper_set_event_callback(fixture.dev, fixture.callback, (void *)fixture.dev), 0,
77 		"Failed to set event callback");
78 	(void)stepper_enable(fixture.dev);
79 	return &fixture;
80 }
81 
stepper_before(void * f)82 static void stepper_before(void *f)
83 {
84 	struct stepper_fixture *fixture = f;
85 	(void)stepper_set_reference_position(fixture->dev, 0);
86 
87 	k_poll_signal_reset(&stepper_signal);
88 
89 	user_data_received = NULL;
90 }
91 
92 ZTEST_SUITE(stepper, NULL, stepper_setup, stepper_before, NULL, NULL);
93 
ZTEST_F(stepper,test_set_micro_step_res_invalid)94 ZTEST_F(stepper, test_set_micro_step_res_invalid)
95 {
96 	int ret = stepper_set_micro_step_res(fixture->dev, 127);
97 
98 	zassert_equal(ret, -EINVAL, "Invalid micro step resolution should return -EINVAL");
99 }
100 
ZTEST_F(stepper,test_get_micro_step_res)101 ZTEST_F(stepper, test_get_micro_step_res)
102 {
103 	enum stepper_micro_step_resolution res;
104 	(void)stepper_get_micro_step_res(fixture->dev, &res);
105 	zassert_equal(res, DT_PROP(DT_ALIAS(stepper), micro_step_res),
106 		      "Micro step resolution not set correctly");
107 }
108 
ZTEST_F(stepper,test_set_micro_step_interval_invalid_zero)109 ZTEST_F(stepper, test_set_micro_step_interval_invalid_zero)
110 {
111 	int err = stepper_set_microstep_interval(fixture->dev, 0);
112 	if (err == -ENOSYS) {
113 		ztest_test_skip();
114 	}
115 	zassert_equal(err, -EINVAL, "ustep interval cannot be zero");
116 }
117 
ZTEST_F(stepper,test_actual_position)118 ZTEST_F(stepper, test_actual_position)
119 {
120 	int32_t pos = 100u;
121 	int ret;
122 
123 	ret = stepper_set_reference_position(fixture->dev, pos);
124 	zassert_equal(ret, 0, "Failed to set reference position");
125 
126 	ret = stepper_get_actual_position(fixture->dev, &pos);
127 	zassert_equal(ret, 0, "Failed to get actual position");
128 	zassert_equal(pos, 100u, "Actual position not set correctly");
129 }
130 
ZTEST_F(stepper,test_target_position_w_fixed_step_interval)131 ZTEST_F(stepper, test_target_position_w_fixed_step_interval)
132 {
133 	int32_t pos = 10u;
134 	int ret;
135 
136 	ret = stepper_set_microstep_interval(fixture->dev, 100 * USEC_PER_SEC);
137 	if (ret == -ENOSYS) {
138 		ztest_test_skip();
139 	}
140 
141 	(void)stepper_move_to(fixture->dev, pos);
142 
143 	POLL_AND_CHECK_SIGNAL(
144 		stepper_signal, stepper_event, STEPPER_EVENT_STEPS_COMPLETED,
145 		K_MSEC(pos * (100 + CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT)));
146 
147 	(void)stepper_get_actual_position(fixture->dev, &pos);
148 	zassert_equal(pos, 10u, "Target position should be %d but is %d", 10u, pos);
149 	zassert_equal(user_data_received, fixture->dev, "User data not received");
150 }
151 
ZTEST_F(stepper,test_move_by_positive_step_count)152 ZTEST_F(stepper, test_move_by_positive_step_count)
153 {
154 	int32_t steps = 20;
155 
156 	(void)stepper_set_microstep_interval(fixture->dev, 100 * USEC_PER_SEC);
157 	(void)stepper_move_by(fixture->dev, steps);
158 
159 	POLL_AND_CHECK_SIGNAL(
160 		stepper_signal, stepper_event, STEPPER_EVENT_STEPS_COMPLETED,
161 		K_MSEC(steps * (100 + CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT)));
162 	(void)stepper_get_actual_position(fixture->dev, &steps);
163 	zassert_equal(steps, 20u, "Target position should be %d but is %d", 20u, steps);
164 }
165 
ZTEST_F(stepper,test_move_by_negative_step_count)166 ZTEST_F(stepper, test_move_by_negative_step_count)
167 {
168 	int32_t steps = -20;
169 
170 	(void)stepper_set_microstep_interval(fixture->dev, 100 * USEC_PER_SEC);
171 	(void)stepper_move_by(fixture->dev, steps);
172 
173 	POLL_AND_CHECK_SIGNAL(
174 		stepper_signal, stepper_event, STEPPER_EVENT_STEPS_COMPLETED,
175 		K_MSEC(-steps * (100 + CONFIG_STEPPER_TEST_TIMING_TIMEOUT_TOLERANCE_PCT)));
176 	(void)stepper_get_actual_position(fixture->dev, &steps);
177 	zassert_equal(steps, -20u, "Target position should be %d but is %d", -20u, steps);
178 }
179 
ZTEST_F(stepper,test_stop)180 ZTEST_F(stepper, test_stop)
181 {
182 	/* Run the stepper in positive direction */
183 	(void)stepper_run(fixture->dev, STEPPER_DIRECTION_POSITIVE);
184 
185 	/* Stop the stepper */
186 	int ret = stepper_stop(fixture->dev);
187 	bool is_moving;
188 
189 	if (ret == 0) {
190 		POLL_AND_CHECK_SIGNAL(stepper_signal, stepper_event, STEPPER_EVENT_STOPPED,
191 				      K_NO_WAIT);
192 		zassert_equal(user_data_received, fixture->dev, "User data not received");
193 
194 		/* Check if the stepper is stopped */
195 		stepper_is_moving(fixture->dev, &is_moving);
196 		zassert_equal(is_moving, false, "Stepper is still moving");
197 	} else if (ret == -ENOSYS) {
198 		stepper_is_moving(fixture->dev, &is_moving);
199 		zassert_equal(is_moving, true,
200 			      "Stepper should be moving since stop is not implemented");
201 	} else {
202 		zassert_unreachable("Stepper stop failed");
203 	}
204 }
205 
ZTEST_F(stepper,test_move_by_zero_steps)206 ZTEST_F(stepper, test_move_by_zero_steps)
207 {
208 	bool is_moving;
209 	int err;
210 
211 	err = stepper_set_microstep_interval(fixture->dev, 100 * USEC_PER_SEC);
212 	if (err == -ENOSYS) {
213 		ztest_test_skip();
214 	}
215 	zassert_equal(err, 0, "Failed to set microstep interval");
216 
217 	zassert_equal(stepper_move_by(fixture->dev, 0), 0, "Failed to move by zero steps");
218 	POLL_AND_CHECK_SIGNAL(stepper_signal, stepper_event, STEPPER_EVENT_STEPS_COMPLETED,
219 			      K_NO_WAIT);
220 	zassert_equal(stepper_is_moving(fixture->dev, &is_moving), 0,
221 		      "Failed to check if stepper is moving");
222 	zassert_equal(is_moving, false, "Stepper is still moving");
223 }
224