1 /*
2 * Copyright (c) 2025 Renesas Electronics Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <inttypes.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11
12 #include <zephyr/device.h>
13 #include <zephyr/devicetree.h>
14 #include <zephyr/drivers/pwm.h>
15 #include <zephyr/drivers/misc/interconn/renesas_elc/renesas_elc.h>
16 #include <zephyr/kernel.h>
17 #include <zephyr/sys/printk.h>
18 #include <zephyr/sys/util.h>
19
20 /* Define DT aliases for PWM and ELC devices */
21 #define PWM_GEN_NODE DT_ALIAS(pwm_gen)
22 #define PWM_CAP_NODE DT_ALIAS(pwm_cap)
23 #define ELC_LINK_NODE DT_ALIAS(elc_link)
24
25 /* PWM channel definitions for generator and capture */
26 #define PWM_GEN_CHANNEL 0
27 #define PWM_CAP_CHANNEL 0
28
29 /* PWM generator software events:
30 * - Software Event 0 starts the PWM generator.
31 * - Software Event 1 stops the PWM generator.
32 */
33 #define PWM_GEN_EVENT_START FSP_SIGNAL_ELC_SOFTWARE_EVENT_0
34 #define PWM_GEN_EVENT_STOP FSP_SIGNAL_ELC_SOFTWARE_EVENT_1
35
36 /* Set the PWM period to 1,000,000 nanoseconds (1 kHz) */
37 #define PWM_PERIOD_NSEC 1000000U
38
39 #define WITHIN(a, b, d) ((a) >= ((b) - (d))) && ((a) <= ((b) + (d)))
40
41 /**
42 * @brief Sample Renesas ELC device functionality.
43 *
44 * This sample verifies the functionality of the Renesas ELC device.
45 * It obtains the required devices from the device tree, configures a PWM
46 * generator, captures its signal, stops and restarts the generator via
47 * the ELC device, and verifies PWM operation via PWM capture.
48 *
49 * @return 0 on success.
50 */
main(void)51 int main(void)
52 {
53 int ret;
54 const struct device *pwm_gen_dev;
55 const struct device *pwm_cap_dev;
56 const struct device *elc_dev;
57
58 /*
59 * Obtain device pointers using DEVICE_DT_GET():
60 * - pwm_gen_dev: PWM generator from PWM_GEN_NODE.
61 * - pwm_cap_dev: PWM capture from PWM_CAP_NODE.
62 * - elc_dev: ELC device from ELC_LINK_NODE.
63 *
64 * Check that each device is ready with device_is_ready().
65 * If a device is not ready, print an error and return.
66 * Otherwise, print device names for confirmation.
67 */
68 pwm_gen_dev = DEVICE_DT_GET(PWM_GEN_NODE);
69 pwm_cap_dev = DEVICE_DT_GET(PWM_CAP_NODE);
70 elc_dev = DEVICE_DT_GET(ELC_LINK_NODE);
71
72 if (!device_is_ready(pwm_gen_dev)) {
73 printk("PWM generator device is not ready");
74 return 0;
75 }
76 if (!device_is_ready(pwm_cap_dev)) {
77 printk("PWM capture device is not ready");
78 return 0;
79 }
80 if (!device_is_ready(elc_dev)) {
81 printk("ELC device is not ready");
82 return 0;
83 }
84 printk("PWM generator device: %s\n", pwm_gen_dev->name);
85 printk("PWM capture device: %s\n", pwm_cap_dev->name);
86 printk("ELC device: %s\n\n", elc_dev->name);
87
88 /*
89 * Enable the ELC device.
90 * Software Event 0 is linked to starting the PWM generator,
91 * Software Event 1 is linked to stopping the PWM generator.
92 */
93 ret = renesas_elc_enable(elc_dev);
94 if (ret) {
95 printk("Error: Failed to enable the ELC device. Err=%d\n", ret);
96 return 0;
97 }
98
99 /*
100 * Configure the PWM generator device.
101 * - PWM_PERIOD_NSEC defines the period (1,000,000 ns).
102 * - 'period' sets the full cycle time.
103 * - 'pulse' is set to half of 'period', yielding a 50%% duty cycle.
104 *
105 * After configuration, the PWM generator starts automatically.
106 */
107 uint32_t period = PWM_PERIOD_NSEC;
108 uint32_t pulse = PWM_PERIOD_NSEC / 2;
109
110 ret = pwm_set(pwm_gen_dev, PWM_GEN_CHANNEL, period, pulse, PWM_POLARITY_NORMAL);
111 if (ret) {
112 printk("Error: Failed to set the PWM generator. Err=%d\n", ret);
113 return 0;
114 }
115 printk("PWM generator configured: period=%u ns, pulse=%u ns\n", period, pulse);
116
117 /*
118 * Capture the PWM period to verify PWM generator device operation.
119 * The error between the captured period and the configured period should
120 * be less than 1%.
121 */
122 uint64_t period_capture;
123 uint64_t pulse_capture;
124
125 ret = pwm_capture_nsec(pwm_cap_dev, PWM_CAP_CHANNEL, PWM_CAPTURE_TYPE_PERIOD,
126 &period_capture, &pulse_capture, K_NSEC(period * 10));
127 if (ret) {
128 printk("Error: Failed to capture the period value of the PWM generator output. "
129 "Err=%d\n",
130 ret);
131 return 0;
132 }
133 ret = pwm_disable_capture(pwm_cap_dev, PWM_CAP_CHANNEL);
134 if (ret) {
135 printk("Error: Failed to disable the PWM capture device. Err=%d\n", ret);
136 return 0;
137 }
138 if (!WITHIN(period_capture, period, period / 100)) {
139 printk("Error: Period capture off by more than 1%%\n");
140 return 0;
141 }
142 printk("PWM captured: period=%llu ns\n", period_capture);
143
144 /*
145 * Stop the PWM generator using ELC software event 1.
146 * Then, attempt to capture the PWM generator's period.
147 * The capture API is expected to return a timeout (-EAGAIN).
148 */
149 printk("\nGenerate ELC software event to stop PWM generator.\n");
150 ret = renesas_elc_software_event_generate(elc_dev, PWM_GEN_EVENT_STOP);
151 if (ret) {
152 printk("Error: Failed to generate ELC software event. Err=%d\n", ret);
153 return 0;
154 }
155 ret = pwm_capture_nsec(pwm_cap_dev, PWM_CAP_CHANNEL, PWM_CAPTURE_TYPE_PERIOD,
156 &period_capture, &pulse_capture, K_NSEC(period * 10));
157 if (ret != -EAGAIN) {
158 printk("Error: PWM generator cannot be stopped by the ELC link as expected\n");
159 return 0;
160 }
161 ret = pwm_disable_capture(pwm_cap_dev, PWM_CAP_CHANNEL);
162 if (ret) {
163 printk("Error: Failed to disable the PWM capture device. Err=%d\n", ret);
164 return 0;
165 }
166 printk("PWM generator stopped by the ELC link as expected.\n");
167
168 /*
169 * Restart the PWM generator using ELC software event 0.
170 * Then, capture the PWM generator's period to verify correct operation.
171 * The error between the captured period and the configured period should
172 * be less than 1%.
173 */
174 printk("\nGenerate ELC software event to start PWM generator.\n");
175 ret = renesas_elc_software_event_generate(elc_dev, PWM_GEN_EVENT_START);
176 if (ret) {
177 printk("Error: Failed to generate ELC software event. Err=%d\n", ret);
178 return 0;
179 }
180 ret = pwm_capture_nsec(pwm_cap_dev, PWM_CAP_CHANNEL, PWM_CAPTURE_TYPE_PERIOD,
181 &period_capture, &pulse_capture, K_NSEC(period * 10));
182 if (ret) {
183 printk("Error: PWM generator cannot be started by the ELC link as expected\n");
184 return 0;
185 }
186 ret = pwm_disable_capture(pwm_cap_dev, PWM_CAP_CHANNEL);
187 if (ret) {
188 printk("Error: Failed to disable the PWM capture device. Err=%d\n", ret);
189 return 0;
190 }
191 if (!WITHIN(period_capture, period, period / 100)) {
192 printk("Error: Period capture off by more than 1%%\n");
193 return 0;
194 }
195 printk("PWM captured: period=%llu ns\n", period_capture);
196 printk("PWM generator started by the ELC link as expected.\n");
197
198 while (1) {
199 k_sleep(K_FOREVER);
200 }
201 return 0;
202 }
203