1 /*
2 * Copyright (c) 2020-2021 Vestas Wind Systems A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/pwm.h>
9 #include <zephyr/ztest.h>
10
11 #include "test_pwm_loopback.h"
12
13 #define TEST_PWM_PERIOD_NSEC 100000000
14 #define TEST_PWM_PULSE_NSEC 15000000
15 #define TEST_PWM_PERIOD_USEC 100000
16 #define TEST_PWM_PULSE_USEC 75000
17
18 enum test_pwm_unit {
19 TEST_PWM_UNIT_NSEC,
20 TEST_PWM_UNIT_USEC,
21 };
22
get_test_pwms(struct test_pwm * out,struct test_pwm * in)23 void get_test_pwms(struct test_pwm *out, struct test_pwm *in)
24 {
25 /* PWM generator device */
26 out->dev = DEVICE_DT_GET(PWM_LOOPBACK_OUT_CTLR);
27 out->pwm = PWM_LOOPBACK_OUT_CHANNEL;
28 out->flags = PWM_LOOPBACK_OUT_FLAGS;
29 zassert_true(device_is_ready(out->dev), "pwm loopback output device is not ready");
30
31 /* PWM capture device */
32 in->dev = DEVICE_DT_GET(PWM_LOOPBACK_IN_CTLR);
33 in->pwm = PWM_LOOPBACK_IN_CHANNEL;
34 in->flags = PWM_LOOPBACK_IN_FLAGS;
35 zassert_true(device_is_ready(in->dev), "pwm loopback input device is not ready");
36 }
37
test_capture(uint32_t period,uint32_t pulse,enum test_pwm_unit unit,pwm_flags_t flags)38 static void test_capture(uint32_t period, uint32_t pulse, enum test_pwm_unit unit,
39 pwm_flags_t flags)
40 {
41 struct test_pwm in;
42 struct test_pwm out;
43 uint64_t period_capture = 0;
44 uint64_t pulse_capture = 0;
45 int err = 0;
46
47 get_test_pwms(&out, &in);
48
49 switch (unit) {
50 case TEST_PWM_UNIT_NSEC:
51 TC_PRINT("Testing PWM capture @ %u/%u nsec\n",
52 pulse, period);
53 err = pwm_set(out.dev, out.pwm, period, pulse, out.flags ^=
54 (flags & PWM_POLARITY_MASK));
55 break;
56
57 case TEST_PWM_UNIT_USEC:
58 TC_PRINT("Testing PWM capture @ %u/%u usec\n",
59 pulse, period);
60 err = pwm_set(out.dev, out.pwm, PWM_USEC(period),
61 PWM_USEC(pulse), out.flags ^=
62 (flags & PWM_POLARITY_MASK));
63 break;
64
65 default:
66 TC_PRINT("Unsupported test unit");
67 ztest_test_fail();
68 }
69
70 zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
71
72 switch (unit) {
73 case TEST_PWM_UNIT_NSEC:
74 err = pwm_capture_nsec(in.dev, in.pwm, flags, &period_capture,
75 &pulse_capture, K_NSEC(period * 10));
76 break;
77
78 case TEST_PWM_UNIT_USEC:
79 err = pwm_capture_usec(in.dev, in.pwm, flags, &period_capture,
80 &pulse_capture, K_USEC(period * 10));
81 break;
82
83 default:
84 TC_PRINT("Unsupported test unit");
85 ztest_test_fail();
86 }
87
88 pwm_disable_capture(in.dev, in.pwm);
89
90 if (err == -ENOTSUP) {
91 TC_PRINT("capture type not supported\n");
92 ztest_test_skip();
93 }
94
95 zassert_equal(err, 0, "failed to capture pwm (err %d)", err);
96
97 if (flags & PWM_CAPTURE_TYPE_PERIOD) {
98 zassert_within(period_capture, period, period / 100,
99 "period capture off by more than 1%");
100 }
101
102 if (flags & PWM_CAPTURE_TYPE_PULSE) {
103 zassert_within(pulse_capture, pulse, pulse / 100,
104 "pulse capture off by more than 1%");
105 }
106 }
107
ZTEST_USER(pwm_loopback,test_pulse_capture)108 ZTEST_USER(pwm_loopback, test_pulse_capture)
109 {
110 test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
111 TEST_PWM_UNIT_NSEC,
112 PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_NORMAL);
113 test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
114 TEST_PWM_UNIT_USEC,
115 PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_NORMAL);
116 }
117
ZTEST_USER(pwm_loopback,test_pulse_capture_inverted)118 ZTEST_USER(pwm_loopback, test_pulse_capture_inverted)
119 {
120 test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
121 TEST_PWM_UNIT_NSEC,
122 PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_INVERTED);
123 test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
124 TEST_PWM_UNIT_USEC,
125 PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_INVERTED);
126 }
127
ZTEST_USER(pwm_loopback,test_period_capture)128 ZTEST_USER(pwm_loopback, test_period_capture)
129 {
130 test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
131 TEST_PWM_UNIT_NSEC,
132 PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_NORMAL);
133 test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
134 TEST_PWM_UNIT_USEC,
135 PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_NORMAL);
136 }
137
ZTEST_USER(pwm_loopback,test_period_capture_inverted)138 ZTEST_USER(pwm_loopback, test_period_capture_inverted)
139 {
140 test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
141 TEST_PWM_UNIT_NSEC,
142 PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_INVERTED);
143 test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
144 TEST_PWM_UNIT_USEC,
145 PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_INVERTED);
146 }
147
ZTEST_USER(pwm_loopback,test_pulse_and_period_capture)148 ZTEST_USER(pwm_loopback, test_pulse_and_period_capture)
149 {
150 test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
151 TEST_PWM_UNIT_NSEC,
152 PWM_CAPTURE_TYPE_BOTH | PWM_POLARITY_NORMAL);
153 test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
154 TEST_PWM_UNIT_USEC,
155 PWM_CAPTURE_TYPE_BOTH | PWM_POLARITY_NORMAL);
156 }
157
ZTEST_USER(pwm_loopback,test_capture_timeout)158 ZTEST_USER(pwm_loopback, test_capture_timeout)
159 {
160 struct test_pwm in;
161 struct test_pwm out;
162 uint32_t period;
163 uint32_t pulse;
164 int err;
165
166 get_test_pwms(&out, &in);
167
168 err = pwm_set_cycles(out.dev, out.pwm, 100, 0, out.flags);
169 zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
170
171 err = pwm_capture_cycles(in.dev, in.pwm, PWM_CAPTURE_TYPE_PULSE,
172 &period, &pulse, K_MSEC(1000));
173 if (err == -ENOTSUP) {
174 TC_PRINT("Pulse capture not supported, "
175 "trying period capture\n");
176 err = pwm_capture_cycles(in.dev, in.pwm,
177 PWM_CAPTURE_TYPE_PERIOD, &period,
178 &pulse, K_MSEC(1000));
179 }
180
181 zassert_equal(err, -EAGAIN, "pwm capture did not timeout (err %d)",
182 err);
183 }
184
continuous_capture_callback(const struct device * dev,uint32_t pwm,uint32_t period_cycles,uint32_t pulse_cycles,int status,void * user_data)185 static void continuous_capture_callback(const struct device *dev,
186 uint32_t pwm,
187 uint32_t period_cycles,
188 uint32_t pulse_cycles,
189 int status,
190 void *user_data)
191 {
192 struct test_pwm_callback_data *data = user_data;
193
194 if (data->count > data->buffer_len) {
195 /* Safe guard in case capture is not disabled */
196 return;
197 }
198
199 if (status != 0) {
200 /* Error occurred */
201 data->status = status;
202 k_sem_give(&data->sem);
203 }
204
205 if (data->pulse_capture) {
206 data->buffer[data->count++] = pulse_cycles;
207 } else {
208 data->buffer[data->count++] = period_cycles;
209 }
210
211 if (data->count > data->buffer_len) {
212 data->status = 0;
213 k_sem_give(&data->sem);
214 }
215 }
216
ZTEST(pwm_loopback,test_continuous_capture)217 ZTEST(pwm_loopback, test_continuous_capture)
218 {
219 struct test_pwm in;
220 struct test_pwm out;
221 uint32_t buffer[10];
222 struct test_pwm_callback_data data = {
223 .buffer = buffer,
224 .buffer_len = ARRAY_SIZE(buffer),
225 .count = 0,
226 .pulse_capture = true,
227 };
228 uint64_t usec = 0;
229 int err;
230 int i;
231
232 get_test_pwms(&out, &in);
233
234 memset(buffer, 0, sizeof(buffer));
235 k_sem_init(&data.sem, 0, 1);
236
237 err = pwm_set(out.dev, out.pwm, PWM_USEC(TEST_PWM_PERIOD_USEC),
238 PWM_USEC(TEST_PWM_PULSE_USEC), out.flags);
239 zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
240
241 err = pwm_configure_capture(in.dev, in.pwm,
242 in.flags |
243 PWM_CAPTURE_MODE_CONTINUOUS |
244 PWM_CAPTURE_TYPE_PULSE,
245 continuous_capture_callback, &data);
246 if (err == -ENOTSUP) {
247 TC_PRINT("Pulse capture not supported, "
248 "trying period capture\n");
249 err = pwm_configure_capture(in.dev, in.pwm,
250 in.flags |
251 PWM_CAPTURE_MODE_CONTINUOUS |
252 PWM_CAPTURE_TYPE_PERIOD,
253 continuous_capture_callback, &data);
254 zassert_equal(err, 0, "failed to configure pwm input (err %d)",
255 err);
256 data.pulse_capture = false;
257 }
258
259 err = pwm_enable_capture(in.dev, in.pwm);
260 zassert_equal(err, 0, "failed to enable pwm capture (err %d)", err);
261
262 err = k_sem_take(&data.sem, K_USEC(TEST_PWM_PERIOD_USEC * data.buffer_len * 10));
263 zassert_equal(err, 0, "pwm capture timed out (err %d)", err);
264 zassert_equal(data.status, 0, "pwm capture failed (err %d)", err);
265
266 err = pwm_disable_capture(in.dev, in.pwm);
267 zassert_equal(err, 0, "failed to disable pwm capture (err %d)", err);
268
269 for (i = 0; i < data.buffer_len; i++) {
270 err = pwm_cycles_to_usec(in.dev, in.pwm, buffer[i], &usec);
271 zassert_equal(err, 0, "failed to calculate usec (err %d)", err);
272
273 if (data.pulse_capture) {
274 zassert_within(usec, TEST_PWM_PULSE_USEC, TEST_PWM_PULSE_USEC / 100,
275 "pulse capture off by more than 1%");
276 } else {
277 zassert_within(usec, TEST_PWM_PERIOD_USEC, TEST_PWM_PERIOD_USEC / 100,
278 "period capture off by more than 1%");
279 }
280 }
281 }
282
ZTEST(pwm_loopback,test_capture_busy)283 ZTEST(pwm_loopback, test_capture_busy)
284 {
285 struct test_pwm in;
286 struct test_pwm out;
287 uint32_t buffer[10];
288 struct test_pwm_callback_data data = {
289 .buffer = buffer,
290 .buffer_len = ARRAY_SIZE(buffer),
291 .count = 0,
292 .pulse_capture = true,
293 };
294 pwm_flags_t flags = PWM_CAPTURE_MODE_SINGLE |
295 PWM_CAPTURE_TYPE_PULSE;
296 int err;
297
298 get_test_pwms(&out, &in);
299
300 memset(buffer, 0, sizeof(buffer));
301 k_sem_init(&data.sem, 0, 1);
302
303 err = pwm_set_cycles(out.dev, out.pwm, 100, 0, out.flags);
304 zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
305
306 err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
307 continuous_capture_callback, &data);
308 if (err == -ENOTSUP) {
309 TC_PRINT("Pulse capture not supported, "
310 "trying period capture\n");
311 flags = PWM_CAPTURE_MODE_SINGLE | PWM_CAPTURE_TYPE_PERIOD;
312 err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
313 continuous_capture_callback, &data);
314 zassert_equal(err, 0, "failed to configure pwm input (err %d)",
315 err);
316 data.pulse_capture = false;
317 }
318
319 err = pwm_enable_capture(in.dev, in.pwm);
320 zassert_equal(err, 0, "failed to enable pwm capture (err %d)", err);
321
322 err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
323 continuous_capture_callback, &data);
324 zassert_equal(err, -EBUSY, "pwm capture not busy (err %d)", err);
325
326 err = pwm_enable_capture(in.dev, in.pwm);
327 zassert_equal(err, -EBUSY, "pwm capture not busy (err %d)", err);
328
329 err = pwm_disable_capture(in.dev, in.pwm);
330 zassert_equal(err, 0, "failed to disable pwm capture (err %d)", err);
331 }
332