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