1 /**
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include "pico/stdlib.h"
9 #include "pico/test.h"
10 #include "pico/time.h"
11 #include "hardware/irq.h"
12 #include "hardware/resets.h"
13 #include "hardware/pwm.h"
14 
15 PICOTEST_MODULE_NAME("PWM", "PWM SDK Test harness");
16 
17 /* In a struct for future expansion of the interrupt testv */
18 struct interrupt_state {
19     int count;
20 } interrupt_states[NUM_PWM_SLICES] = {0};
21 
on_pwm_wrap()22 void on_pwm_wrap() {
23     for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) {
24         // See if this pwm, is the one that fired.
25         if (pwm_get_irq_status_mask() & (1 << pwm)) {
26             // Clear the interrupt flag that brought us here
27             pwm_clear_irq(pwm);
28 
29             interrupt_states[pwm].count++;
30         }
31     }
32 }
33 
main()34 int main() {
35     reset_block(RESETS_RESET_PWM_BITS);
36     unreset_block_wait(RESETS_RESET_PWM_BITS);
37 
38     setup_default_uart();
39 
40     PICOTEST_START();
41 
42     pwm_config config = pwm_get_default_config();
43 
44 
45     // Test that config sets works on all PWMs by comparing what we pass in
46     // via the API with what the registers contains afterwards
47 
48     pwm_config_set_phase_correct(&config, true);
49     pwm_config_set_clkdiv(&config, 42.5);
50     pwm_config_set_clkdiv_mode(&config, PWM_DIV_B_HIGH);
51     pwm_config_set_output_polarity(&config, false, true);
52     pwm_config_set_wrap(&config, 0x1234);
53 
54     PICOTEST_START_SECTION("PWM config init tests");
55         for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) {
56             pwm_slice_hw_t *slice = &pwm_hw->slice[pwm];
57 
58             pwm_init(pwm, &config, false);
59 
60             uint div = (uint)(42.5f * (float)(1 << PWM_CH0_DIV_INT_LSB));
61 
62             PICOTEST_CHECK_CHANNEL(pwm, slice->top == config.top, "HW top does not match requested config");
63             //PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == 0x1234, "HW counter does not match config");
64             PICOTEST_CHECK_CHANNEL(pwm, slice->cc == PWM_CH0_CC_RESET, "HW compares does not match config");
65             PICOTEST_CHECK_CHANNEL(pwm, slice->div == div, "HW divider  does not match config");
66             PICOTEST_CHECK_CHANNEL(pwm, slice->csr ==
67                                         (1 << PWM_CH0_CSR_PH_CORRECT_LSB | 0 << PWM_CH0_CSR_A_INV_LSB | 1 << PWM_CH0_CSR_B_INV_LSB |
68                                          PWM_CH0_CSR_DIVMODE_VALUE_LEVEL << PWM_CH0_CSR_DIVMODE_LSB), "HW CSR does not match config");
69         }
70     PICOTEST_END_SECTION();
71 
72 
73     // Need to test the SDK APIs do the right thing
74 
75     PICOTEST_START_SECTION("PWM SDK API tests");
76         for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) {
77             pwm_slice_hw_t *slice = &pwm_hw->slice[pwm];
78             int v = 100 + pwm * 10;
79 
80             pwm_set_wrap(pwm, v);
81             PICOTEST_CHECK_CHANNEL(pwm, slice->top == v, "pwm_set_wrap() failed to set register");
82 
83             pwm_set_both_levels(pwm, v + 1, v);
84             PICOTEST_CHECK_CHANNEL(pwm, slice->cc == (((v) << PWM_CH0_CC_B_LSB) | ((v + 1) << PWM_CH0_CC_A_LSB)),
85                                    "pwm_set_both_levels() failed to set register");
86 
87             float divider = 100.5;
88             int i = (int16_t) divider;
89             int f = (int8_t) ((divider - i) * 16);
90             pwm_set_clkdiv(pwm, divider);
91             PICOTEST_CHECK_CHANNEL(pwm, slice->div == (i << 4 | f), "pwm_set_clkdiv() failed to set register");
92 
93             i++;
94             pwm_set_clkdiv_int_frac(pwm, i, f);
95             PICOTEST_CHECK_CHANNEL(pwm, slice->div == (i << 4 | f),
96                                    "pwm_set_clkdiv_int_frac() failed to set register");
97 
98             int c = 1234;
99             pwm_set_counter(pwm, c);
100             PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == c, "pwm_set_counter() failed to set register");
101 
102             int cc = pwm_get_counter(pwm);
103             PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == cc && cc == c, "pwm_get_counter() failed to get register");
104 
105             pwm_set_output_polarity(pwm, false, false);
106             PICOTEST_CHECK_CHANNEL(pwm,
107                                    !(slice->csr & PWM_CH0_CSR_A_INV_BITS) && !(slice->csr & PWM_CH0_CSR_B_INV_BITS),
108                                    "pwm_set_output_polarity() (F/F)");
109 
110             pwm_set_output_polarity(pwm, true, false);
111             PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_A_INV_BITS) && !(slice->csr & PWM_CH0_CSR_B_INV_BITS),
112                                    "pwm_set_output_polarity() (T/F)");
113 
114             pwm_set_output_polarity(pwm, false, true);
115             PICOTEST_CHECK_CHANNEL(pwm, !(slice->csr & PWM_CH0_CSR_A_INV_BITS) && (slice->csr & PWM_CH0_CSR_B_INV_BITS),
116                                    "pwm_set_output_polarity() (F/T)");
117 
118             pwm_set_output_polarity(pwm, true, true);
119             PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_A_INV_BITS) && (slice->csr & PWM_CH0_CSR_B_INV_BITS),
120                                    "pwm_set_output_polarity() (T/T)");
121 
122             pwm_set_phase_correct(pwm, true);
123             PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_PH_CORRECT_BITS), "pwm_set_phase_correct(T)");
124 
125             pwm_set_phase_correct(pwm, false);
126             PICOTEST_CHECK_CHANNEL(pwm, !(slice->csr & PWM_CH0_CSR_PH_CORRECT_BITS), "pwm_set_phase_correct(F)");
127 
128             for (int m = PWM_DIV_FREE_RUNNING; m <= PWM_DIV_B_FALLING; m++) {
129                 pwm_set_clkdiv_mode(pwm, m);
130                 PICOTEST_CHECK_CHANNEL(pwm, ((slice->csr & PWM_CH0_CSR_DIVMODE_BITS) >> PWM_CH0_CSR_DIVMODE_LSB) == m,
131                                        "pwm_set_clkdiv_mode");
132             }
133         }
134     PICOTEST_END_SECTION();
135 
136     PICOTEST_START_SECTION("PWM IRQ tests");
137 
138         irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
139         irq_set_enabled(PWM_IRQ_WRAP, true);
140 
141         config = pwm_get_default_config();
142 
143         // Slow down the interrupt rate a load, don't need it high.
144         // This give about 40 per second on Picoboard
145         pwm_config_set_clkdiv(&config, 50);
146 
147         for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) {
148             pwm_init(pwm, &config, false);
149             pwm_clear_irq(pwm);
150             pwm_set_irq_enabled(pwm, true);
151         }
152 
153         // Now enable all the PWM at the same time.
154         pwm_set_mask_enabled(0xff);
155 
156         sleep_ms(1000);
157 
158         int err = 0;
159 
160         for (int p = 0; p < NUM_PWM_SLICES; p++) {
161             PICOTEST_CHECK_CHANNEL(p, interrupt_states[p].count != 0, "No interrupts detected from PWM %d\n");
162         }
163 
164     PICOTEST_END_SECTION();
165 
166     PICOTEST_END_TEST();
167 }
168 
169