2121 * Always on -> Period : Pulse (1 : 1) -> 3.3V
2222 * Half on -> Period : Pulse (2 : 1) -> 1.65V
2323 * Always off -> Period : Pulse (1 : 0) -> 0V
24+ *
25+ * Note: It is possible to enable timing checks through the symbol
26+ * CONFIG_ENABLE_TIMING_CHECK. This setting will use GPIO channel
27+ * pin interrupts and the systimer - assuming it is validated - to
28+ * do automated measurements.
2429 */
2530
2631#include <zephyr/device.h>
2732#include <inttypes.h>
2833#include <zephyr/drivers/pwm.h>
2934#include <zephyr/kernel.h>
3035#include <zephyr/ztest.h>
36+ #if CONFIG_ENABLE_TIMING_CHECK
37+ #include <zephyr/drivers/gpio.h>
38+ #include <stdlib.h>
39+ #include <math.h>
40+ #endif
3141
3242#if DT_NODE_HAS_STATUS_OKAY (DT_ALIAS (pwm_0 ))
3343#define PWM_DEV_NODE DT_ALIAS(pwm_0)
6070#error "Define a PWM device"
6171#endif
6272
73+ #if CONFIG_ENABLE_TIMING_CHECK
74+
75+ #define DT_RESOURCES DT_INST(0, test_pwm_api)
76+
77+ #if DT_NODE_HAS_STATUS_OKAY (DT_RESOURCES )
78+ #define GPIO_HDL DT_GPIO_CTLR(DT_RESOURCES, in_gpios)
79+ #define GPIO_DEV DEVICE_DT_GET(GPIO_HDL)
80+ #define GPIO_PIN DT_GPIO_PIN(DT_RESOURCES, in_gpios)
81+ #define GPIO_FLG DT_GPIO_FLAGS(DT_RESOURCES, in_gpios)
82+ #else
83+ #error Invalid device tree config for GPIO input pin
84+ #endif
85+
86+ /* Variables for timing measurement */
87+ static struct test_context {
88+ uint32_t last_edge_time ;
89+ uint32_t high_time ;
90+ uint32_t low_time ;
91+ bool gpio_cfg_done ;
92+ bool sampling_done ;
93+ uint8_t skip_cnt ;
94+ } ctx ;
95+
96+ /* Skipping a couple of edges greatly improves measurement precision
97+ * due to interrupt latency present on the first edge (ref ZEP-868)
98+ */
99+ #define SKIP_EDGE_NUM 2
100+
101+ #endif /* CONFIG_ENABLE_TIMING_CHECK */
102+
63103#if defined(CONFIG_BOARD_COLIBRI_IMX7D_MCIMX7D_M4 ) || defined(CONFIG_SOC_MK64F12 ) || \
64- defined(CONFIG_SOC_MKW41Z4 ) || defined(CONFIG_SOC_SERIES_ESP32S2 ) || \
65- defined(CONFIG_SOC_SERIES_ESP32S3 ) || defined(CONFIG_SOC_SERIES_ESP32C3 )
104+ defined(CONFIG_SOC_MKW41Z4 )
66105#define DEFAULT_PERIOD_CYCLE 1024
67- #define DEFAULT_PULSE_CYCLE 512
68- #define DEFAULT_PERIOD_NSEC 2000000
69- #define DEFAULT_PULSE_NSEC 500000
106+ #define DEFAULT_PULSE_CYCLE 512
107+ #define DEFAULT_PERIOD_NSEC 2000000
108+ #define DEFAULT_PULSE_NSEC 500000
109+ #elif defined(CONFIG_SOC_SERIES_ESP32S2 ) || defined(CONFIG_SOC_SERIES_ESP32S3 ) || \
110+ defined(CONFIG_SOC_SERIES_ESP32C3 )
111+ #define DEFAULT_PERIOD_CYCLE 16200
112+ #define DEFAULT_PULSE_CYCLE 8100
113+ #define DEFAULT_PERIOD_NSEC 160000
114+ #define DEFAULT_PULSE_NSEC 40000
70115#elif DT_HAS_COMPAT_STATUS_OKAY (intel_blinky_pwm )
71116#define DEFAULT_PERIOD_CYCLE 32768
72- #define DEFAULT_PULSE_CYCLE 16384
73- #define DEFAULT_PERIOD_NSEC 2000000
74- #define DEFAULT_PULSE_NSEC 500000
117+ #define DEFAULT_PULSE_CYCLE 16384
118+ #define DEFAULT_PERIOD_NSEC 2000000
119+ #define DEFAULT_PULSE_NSEC 500000
75120#else
76121#define DEFAULT_PERIOD_CYCLE 64000
77- #define DEFAULT_PULSE_CYCLE 32000
78- #define DEFAULT_PERIOD_NSEC 2000000
79- #define DEFAULT_PULSE_NSEC 1000000
122+ #define DEFAULT_PULSE_CYCLE 32000
123+ #define DEFAULT_PERIOD_NSEC 2000000
124+ #define DEFAULT_PULSE_NSEC 1000000
80125#endif
81126
82127#if defined CONFIG_BOARD_SAM_E70_XPLAINED
106151#define DEFAULT_PWM_PORT 0
107152#endif
108153
109- #define UNIT_CYCLES 0
110- #define UNIT_NSECS 1
154+ #define UNIT_CYCLES 0
155+ #define UNIT_NSECS 1
111156
112157const struct device * get_pwm_device (void )
113158{
114159 return DEVICE_DT_GET (PWM_DEV_NODE );
115160}
116161
162+ #if CONFIG_ENABLE_TIMING_CHECK
163+
164+ /* Interrupt handler for edge detection */
165+ void edge_detect_handler (const struct device * dev , struct gpio_callback * cb , uint32_t pins )
166+ {
167+ uint32_t current_time = k_cycle_get_32 ();
168+
169+ if (ctx .sampling_done || ++ ctx .skip_cnt < SKIP_EDGE_NUM ) {
170+ return ;
171+ }
172+
173+ if (!ctx .last_edge_time ) {
174+ /* init last_edge_time for first delta*/
175+ ctx .last_edge_time = current_time ;
176+ return ;
177+ }
178+
179+ uint32_t elapsed_time = current_time - ctx .last_edge_time ;
180+
181+ int pin_state = gpio_pin_get_raw (GPIO_DEV , GPIO_PIN );
182+
183+ if (pin_state ) {
184+ ctx .low_time = elapsed_time ;
185+ } else {
186+ ctx .high_time = elapsed_time ;
187+ }
188+
189+ /* sampling is done when both high and low times were stored */
190+ if (ctx .high_time && ctx .low_time ) {
191+ ctx .sampling_done = true;
192+ }
193+
194+ ctx .last_edge_time = current_time ;
195+ }
196+
197+ void setup_edge_detection (const struct device * gpio_dev , uint32_t pin )
198+ {
199+ static struct gpio_callback gpio_cb ;
200+
201+ /* Configure GPIO pin for edge detection */
202+ if (!ctx .gpio_cfg_done ) {
203+ gpio_pin_configure (gpio_dev , pin , (GPIO_INPUT | GPIO_FLG ));
204+ gpio_init_callback (& gpio_cb , edge_detect_handler , BIT (pin ));
205+ gpio_add_callback (gpio_dev , & gpio_cb );
206+ gpio_pin_interrupt_configure (gpio_dev , pin , GPIO_INT_EDGE_BOTH );
207+
208+ ctx .gpio_cfg_done = true;
209+ }
210+
211+ ctx .last_edge_time = 0 ;
212+ ctx .high_time = 0 ;
213+ ctx .low_time = 0 ;
214+ ctx .sampling_done = false;
215+ ctx .skip_cnt = 0 ;
216+ }
217+
218+ bool check_range (float refval , float measval )
219+ {
220+ float delta = fabsf (refval - measval );
221+ float allowed_deviation = (refval * (float )CONFIG_ALLOWED_DEVIATION ) / 100 ;
222+
223+ return delta <= allowed_deviation ;
224+ }
225+
226+ static int timing_check (const struct device * pwm_dev , uint32_t port , uint32_t period ,
227+ uint32_t pulse , uint8_t unit )
228+ {
229+ uint64_t cycles_s_sys , cycles_s_pwm ;
230+ uint32_t expected_period_ns ;
231+ uint32_t expected_pulse_ns ;
232+ uint32_t expected_duty_cycle ;
233+ int pin_state ;
234+
235+ /* wait for stable signal */
236+ k_sleep (K_MSEC (100 ));
237+
238+ /* set up GPIO input pin for edge detection */
239+ setup_edge_detection (GPIO_DEV , GPIO_PIN );
240+
241+ /* wait for sampling */
242+ k_sleep (K_MSEC (100 ));
243+
244+ /* store pin state for duty == 100% or 0% checks */
245+ pin_state = gpio_pin_get_raw (GPIO_DEV , GPIO_PIN );
246+
247+ cycles_s_sys = (uint64_t )sys_clock_hw_cycles_per_sec ();
248+ pwm_get_cycles_per_sec (pwm_dev , port , & cycles_s_pwm );
249+
250+ if (unit == UNIT_CYCLES ) {
251+ /* convert cycles to ns using PWM clock period */
252+ expected_period_ns = (period * 1e9 ) / cycles_s_pwm ;
253+ expected_pulse_ns = (pulse * 1e9 ) / cycles_s_pwm ;
254+ } else if (unit == UNIT_NSECS ) {
255+ /* already in nanoseconds */
256+ expected_period_ns = period ;
257+ expected_pulse_ns = pulse ;
258+ } else {
259+ TC_PRINT ("Unexpected unit" );
260+ return TC_FAIL ;
261+ }
262+
263+ /* sampling_done should be false for 0 and 100% duty (no switching) */
264+ TC_PRINT ("Sampling done: %s\n" , ctx .sampling_done ? "true" : "false" );
265+
266+ expected_duty_cycle = (expected_pulse_ns * 100 ) / expected_period_ns ;
267+
268+ if (expected_duty_cycle == 100 ) {
269+ if ((pin_state == 1 ) && !ctx .sampling_done ) {
270+ return TC_PASS ;
271+ } else {
272+ return TC_FAIL ;
273+ }
274+ } else if (expected_duty_cycle == 0 ) {
275+ if ((pin_state == 0 ) && !ctx .sampling_done ) {
276+ return TC_PASS ;
277+ } else {
278+ return TC_FAIL ;
279+ }
280+ } else {
281+ uint32_t measured_period = ctx .high_time + ctx .low_time ;
282+ uint32_t measured_period_ns = (measured_period * 1e9 ) / cycles_s_sys ;
283+ float measured_duty_cycle = (ctx .high_time * 100.0f ) / measured_period ;
284+ uint32_t measured_duty_cycle_2p = (uint32_t )(measured_duty_cycle * 100 );
285+ uint32_t period_deviation_2p = (uint64_t )10000 *
286+ abs (measured_period_ns - expected_period_ns ) /
287+ expected_period_ns ;
288+ uint32_t duty_deviation_2p =
289+ (uint32_t )10000 * fabs (measured_duty_cycle - (float )expected_duty_cycle ) /
290+ expected_duty_cycle ;
291+
292+ TC_PRINT ("Expected period: %u ns, pulse: %u ns duty cycle: %u%%\n" ,
293+ expected_period_ns , expected_pulse_ns , expected_duty_cycle );
294+ TC_PRINT ("Measured period: %u cycles, high: %u, low: %u [unit: systimer ticks]\n" ,
295+ measured_period , ctx .high_time , ctx .low_time );
296+ TC_PRINT ("Measured period: %u ns, deviation: %d.%d%%\n" , measured_period_ns ,
297+ period_deviation_2p / 100 , period_deviation_2p % 100 );
298+ TC_PRINT ("Measured duty: %d.%d%%, deviation: %d.%d%%\n" ,
299+ measured_duty_cycle_2p / 100 , measured_duty_cycle_2p % 100 ,
300+ duty_deviation_2p / 100 , duty_deviation_2p % 100 );
301+
302+ /* Compare measured values with expected ones */
303+ if (check_range (measured_period_ns , expected_period_ns ) &&
304+ check_range (measured_duty_cycle , expected_duty_cycle )) {
305+ TC_PRINT ("PWM output matches the programmed values\n" );
306+ return TC_PASS ;
307+ }
308+
309+ TC_PRINT ("PWM output does NOT match the programmed values\n" );
310+ return TC_FAIL ;
311+ }
312+ }
313+
314+ #endif
315+
117316static int test_task (uint32_t port , uint32_t period , uint32_t pulse , uint8_t unit )
118317{
119- TC_PRINT ("[PWM]: %" PRIu8 ", [period]: %" PRIu32 ", [pulse]: %" PRIu32 "\n" ,
120- port , period , pulse );
318+ TC_PRINT ("[PWM]: %" PRIu8 ", [period]: %" PRIu32 ", [pulse]: %" PRIu32 "\n" , port , period ,
319+ pulse );
121320
122321 const struct device * pwm_dev = get_pwm_device ();
123322
@@ -140,43 +339,47 @@ static int test_task(uint32_t port, uint32_t period, uint32_t pulse, uint8_t uni
140339 }
141340 }
142341
342+ #if CONFIG_ENABLE_TIMING_CHECK
343+ return timing_check (pwm_dev , port , period , pulse , unit );
344+ #else
143345 return TC_PASS ;
346+ #endif
144347}
145348
146349ZTEST_USER (pwm_basic , test_pwm_nsec )
147350{
148351 /* Period : Pulse (2000000 : 1000000), unit (nsec). Voltage : 1.65V */
149352 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_NSEC ,
150353 DEFAULT_PULSE_NSEC , UNIT_NSECS ) == TC_PASS , NULL );
151- k_sleep (K_MSEC (1000 ));
354+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
152355
153356 /* Period : Pulse (2000000 : 2000000), unit (nsec). Voltage : 3.3V */
154357 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_NSEC ,
155358 DEFAULT_PERIOD_NSEC , UNIT_NSECS ) == TC_PASS , NULL );
156- k_sleep (K_MSEC (1000 ));
359+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
157360
158361 /* Period : Pulse (2000000 : 0), unit (nsec). Voltage : 0V */
159362 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_NSEC ,
160363 0 , UNIT_NSECS ) == TC_PASS , NULL );
161- k_sleep (K_MSEC (1000 ));
364+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
162365}
163366
164367ZTEST_USER (pwm_basic , test_pwm_cycle )
165368{
166369 /* Period : Pulse (64000 : 32000), unit (cycle). Voltage : 1.65V */
167370 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_CYCLE ,
168371 DEFAULT_PULSE_CYCLE , UNIT_CYCLES ) == TC_PASS , NULL );
169- k_sleep (K_MSEC (1000 ));
372+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
170373
171374 /* Period : Pulse (64000 : 64000), unit (cycle). Voltage : 3.3V */
172375 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_CYCLE ,
173376 DEFAULT_PERIOD_CYCLE , UNIT_CYCLES ) == TC_PASS , NULL );
174- k_sleep (K_MSEC (1000 ));
377+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
175378
176379 /* Period : Pulse (64000 : 0), unit (cycle). Voltage : 0V */
177380 zassert_true (test_task (DEFAULT_PWM_PORT , DEFAULT_PERIOD_CYCLE ,
178381 0 , UNIT_CYCLES ) == TC_PASS , NULL );
179- k_sleep (K_MSEC (1000 ));
382+ k_sleep (K_MSEC (CONFIG_TEST_DELAY ));
180383}
181384
182385#if defined INVALID_PWM_PORT
@@ -185,13 +388,12 @@ ZTEST_USER(pwm_basic, test_pwm_invalid_port)
185388 const struct device * pwm_dev = get_pwm_device ();
186389
187390 TC_PRINT ("[PWM]: %" PRIu8 ", [period]: %" PRIu32 ", [pulse]: %" PRIu32 "\n" ,
188- INVALID_PWM_PORT , DEFAULT_PERIOD_CYCLE , DEFAULT_PULSE_CYCLE );
391+ INVALID_PWM_PORT , DEFAULT_PERIOD_CYCLE , DEFAULT_PULSE_CYCLE );
189392
190393 zassert_true (device_is_ready (pwm_dev ), "PWM device is not ready" );
191394
192395 zassert_equal (pwm_set_cycles (pwm_dev , INVALID_PWM_PORT , DEFAULT_PERIOD_CYCLE ,
193396 DEFAULT_PULSE_CYCLE , 0 ),
194397 - EINVAL , "Invalid PWM port\n" );
195-
196398}
197399#endif
0 commit comments