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