1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_PWM_H
8 #define _HARDWARE_PWM_H
9 
10 #include "pico.h"
11 #include "hardware/structs/pwm.h"
12 #include "hardware/regs/dreq.h"
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
18 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PWM, Enable/disable assertions in the PWM module, type=bool, default=0, group=hardware_pwm
19 #ifndef PARAM_ASSERTIONS_ENABLED_PWM
20 #define PARAM_ASSERTIONS_ENABLED_PWM 0
21 #endif
22 
23 /** \file hardware/pwm.h
24  *  \defgroup hardware_pwm hardware_pwm
25  *
26  * Hardware Pulse Width Modulation (PWM) API
27  *
28  * The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
29  * measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
30  * PWM outputs. All 30 GPIOs can be driven by the PWM block.
31  *
32  * The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
33  * toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
34  * time spent at the high signal level is known as the duty cycle of the signal.
35  *
36  * The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
37  * immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
38  * reaching TOP, until it reaches 0 again.
39  *
40  * \subsection pwm_example Example
41  * \addtogroup hardware_pwm
42  * \include hello_pwm.c
43  */
44 
45 /** \brief PWM Divider mode settings
46  *   \ingroup hardware_pwm
47  *
48  */
49 enum pwm_clkdiv_mode
50 {
51     PWM_DIV_FREE_RUNNING = 0, ///< Free-running counting at rate dictated by fractional divider
52     PWM_DIV_B_HIGH = 1,       ///< Fractional divider is gated by the PWM B pin
53     PWM_DIV_B_RISING = 2,     ///< Fractional divider advances with each rising edge of the PWM B pin
54     PWM_DIV_B_FALLING = 3    ///< Fractional divider advances with each falling edge of the PWM B pin
55 };
56 
57 enum pwm_chan
58 {
59     PWM_CHAN_A = 0,
60     PWM_CHAN_B = 1
61 };
62 
63 typedef struct {
64     uint32_t csr;
65     uint32_t div;
66     uint32_t top;
67 } pwm_config;
68 
check_slice_num_param(__unused uint slice_num)69 static inline void check_slice_num_param(__unused uint slice_num) {
70     valid_params_if(PWM, slice_num < NUM_PWM_SLICES);
71 }
72 
73 /** \brief Determine the PWM slice that is attached to the specified GPIO
74  *  \ingroup hardware_pwm
75  *
76  * \return The PWM slice number that controls the specified GPIO.
77  */
pwm_gpio_to_slice_num(uint gpio)78 static inline uint pwm_gpio_to_slice_num(uint gpio) {
79     valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
80     return (gpio >> 1u) & 7u;
81 }
82 
83 /** \brief Determine the PWM channel that is attached to the specified GPIO.
84  *  \ingroup hardware_pwm
85  *
86  * Each slice 0 to 7 has two channels, A and B.
87  *
88  * \return The PWM channel that controls the specified GPIO.
89  */
pwm_gpio_to_channel(uint gpio)90 static inline uint pwm_gpio_to_channel(uint gpio) {
91     valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
92     return gpio & 1u;
93 }
94 
95 /** \brief Set phase correction in a PWM configuration
96  *  \ingroup hardware_pwm
97  *
98  * \param c PWM configuration struct to modify
99  * \param phase_correct true to set phase correct modulation, false to set trailing edge
100  *
101  * Setting phase control to true means that instead of wrapping back to zero when the wrap point is reached,
102  * the PWM starts counting back down. The output frequency is halved when phase-correct mode is enabled.
103  */
pwm_config_set_phase_correct(pwm_config * c,bool phase_correct)104 static inline void pwm_config_set_phase_correct(pwm_config *c, bool phase_correct) {
105     c->csr = (c->csr & ~PWM_CH0_CSR_PH_CORRECT_BITS)
106         | (bool_to_bit(phase_correct) << PWM_CH0_CSR_PH_CORRECT_LSB);
107 }
108 
109 /** \brief Set PWM clock divider in a PWM configuration
110  *  \ingroup hardware_pwm
111  *
112  * \param c PWM configuration struct to modify
113  * \param div Value to divide counting rate by. Must be greater than or equal to 1.
114  *
115  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
116  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
117  * before passing them on to the PWM counter.
118  */
pwm_config_set_clkdiv(pwm_config * c,float div)119 static inline void pwm_config_set_clkdiv(pwm_config *c, float div) {
120     valid_params_if(PWM, div >= 1.f && div < 256.f);
121     c->div = (uint32_t)(div * (float)(1u << PWM_CH0_DIV_INT_LSB));
122 }
123 
124 /** \brief Set PWM clock divider in a PWM configuration using an 8:4 fractional value
125  *  \ingroup hardware_pwm
126  *
127  * \param c PWM configuration struct to modify
128  * \param integer 8 bit integer part of the clock divider. Must be greater than or equal to 1.
129  * \param fract 4 bit fractional part of the clock divider
130  *
131  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
132  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
133  * before passing them on to the PWM counter.
134  */
pwm_config_set_clkdiv_int_frac(pwm_config * c,uint8_t integer,uint8_t fract)135 static inline void pwm_config_set_clkdiv_int_frac(pwm_config *c, uint8_t integer, uint8_t fract) {
136     valid_params_if(PWM, integer >= 1);
137     valid_params_if(PWM, fract < 16);
138     c->div = (((uint)integer) << PWM_CH0_DIV_INT_LSB) | (((uint)fract) << PWM_CH0_DIV_FRAC_LSB);
139 }
140 
141 /** \brief Set PWM clock divider in a PWM configuration
142  *  \ingroup hardware_pwm
143  *
144  * \param c PWM configuration struct to modify
145  * \param div Integer value to reduce counting rate by. Must be greater than or equal to 1.
146  *
147  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
148  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
149  * before passing them on to the PWM counter.
150  */
pwm_config_set_clkdiv_int(pwm_config * c,uint div)151 static inline void pwm_config_set_clkdiv_int(pwm_config *c, uint div) {
152     valid_params_if(PWM, div >= 1 && div < 256);
153     pwm_config_set_clkdiv_int_frac(c, (uint8_t)div, 0);
154 }
155 
156 /** \brief Set PWM counting mode in a PWM configuration
157  *  \ingroup hardware_pwm
158  *
159  * \param c PWM configuration struct to modify
160  * \param mode PWM divide/count mode
161  *
162  * Configure which event gates the operation of the fractional divider.
163  * The default is always-on (free-running PWM). Can also be configured to count on
164  * high level, rising edge or falling edge of the B pin input.
165  */
pwm_config_set_clkdiv_mode(pwm_config * c,enum pwm_clkdiv_mode mode)166 static inline void pwm_config_set_clkdiv_mode(pwm_config *c, enum pwm_clkdiv_mode mode) {
167     valid_params_if(PWM, mode == PWM_DIV_FREE_RUNNING ||
168             mode == PWM_DIV_B_RISING ||
169             mode == PWM_DIV_B_HIGH ||
170             mode == PWM_DIV_B_FALLING);
171     c->csr = (c->csr & ~PWM_CH0_CSR_DIVMODE_BITS)
172         | (((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB);
173 }
174 
175 /** \brief Set output polarity in a PWM configuration
176  *  \ingroup hardware_pwm
177  *
178  * \param c PWM configuration struct to modify
179  * \param a true to invert output A
180  * \param b true to invert output B
181  */
pwm_config_set_output_polarity(pwm_config * c,bool a,bool b)182 static inline void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b) {
183     c->csr = (c->csr & ~(PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS))
184         | ((bool_to_bit(a) << PWM_CH0_CSR_A_INV_LSB) | (bool_to_bit(b) << PWM_CH0_CSR_B_INV_LSB));
185 }
186 
187 /** \brief Set PWM counter wrap value in a PWM configuration
188  *  \ingroup hardware_pwm
189  *
190  * Set the highest value the counter will reach before returning to 0. Also known as TOP.
191  *
192  * \param c PWM configuration struct to modify
193  * \param wrap Value to set wrap to
194  */
pwm_config_set_wrap(pwm_config * c,uint16_t wrap)195 static inline void pwm_config_set_wrap(pwm_config *c, uint16_t wrap) {
196     c->top = wrap;
197 }
198 
199 /** \brief Initialise a PWM with settings from a configuration object
200  *  \ingroup hardware_pwm
201  *
202  * Use the \ref pwm_get_default_config() function to initialise a config structure, make changes as
203  * needed using the pwm_config_* functions, then call this function to set up the PWM.
204  *
205  * \param slice_num PWM slice number
206  * \param c The configuration to use
207  * \param start If true the PWM will be started running once configured. If false you will need to start
208  *  manually using \ref pwm_set_enabled() or \ref pwm_set_mask_enabled()
209  */
pwm_init(uint slice_num,pwm_config * c,bool start)210 static inline void pwm_init(uint slice_num, pwm_config *c, bool start) {
211     check_slice_num_param(slice_num);
212     pwm_hw->slice[slice_num].csr = 0;
213 
214     pwm_hw->slice[slice_num].ctr = PWM_CH0_CTR_RESET;
215     pwm_hw->slice[slice_num].cc = PWM_CH0_CC_RESET;
216     pwm_hw->slice[slice_num].top = c->top;
217     pwm_hw->slice[slice_num].div = c->div;
218     pwm_hw->slice[slice_num].csr = c->csr | (bool_to_bit(start) << PWM_CH0_CSR_EN_LSB);
219 }
220 
221 /** \brief Get a set of default values for PWM configuration
222  *  \ingroup hardware_pwm
223  *
224  * PWM config is free-running at system clock speed, no phase correction, wrapping at 0xffff,
225  * with standard polarities for channels A and B.
226  *
227  * \return Set of default values.
228  */
pwm_get_default_config(void)229 static inline pwm_config pwm_get_default_config(void) {
230     pwm_config c = {0, 0, 0};
231     pwm_config_set_phase_correct(&c, false);
232     pwm_config_set_clkdiv_int(&c, 1);
233     pwm_config_set_clkdiv_mode(&c, PWM_DIV_FREE_RUNNING);
234     pwm_config_set_output_polarity(&c, false, false);
235     pwm_config_set_wrap(&c, 0xffffu);
236     return c;
237 }
238 
239 /** \brief Set the current PWM counter wrap value
240  *  \ingroup hardware_pwm
241  *
242  * Set the highest value the counter will reach before returning to 0. Also
243  * known as TOP.
244  *
245  * The counter wrap value is double-buffered in hardware. This means that,
246  * when the PWM is running, a write to the counter wrap value does not take
247  * effect until after the next time the PWM slice wraps (or, in phase-correct
248  * mode, the next time the slice reaches 0). If the PWM is not running, the
249  * write is latched in immediately.
250  *
251  * \param slice_num PWM slice number
252  * \param wrap Value to set wrap to
253  */
pwm_set_wrap(uint slice_num,uint16_t wrap)254 static inline void pwm_set_wrap(uint slice_num, uint16_t wrap) {
255     check_slice_num_param(slice_num);
256     pwm_hw->slice[slice_num].top = wrap;
257 }
258 
259 /** \brief Set the current PWM counter compare value for one channel
260  *  \ingroup hardware_pwm
261  *
262  * Set the value of the PWM counter compare value, for either channel A or channel B.
263  *
264  * The counter compare register is double-buffered in hardware. This means
265  * that, when the PWM is running, a write to the counter compare values does
266  * not take effect until the next time the PWM slice wraps (or, in
267  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
268  * running, the write is latched in immediately.
269  *
270  * \param slice_num PWM slice number
271  * \param chan Which channel to update. 0 for A, 1 for B.
272  * \param level new level for the selected output
273  */
pwm_set_chan_level(uint slice_num,uint chan,uint16_t level)274 static inline void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level) {
275     check_slice_num_param(slice_num);
276     hw_write_masked(
277         &pwm_hw->slice[slice_num].cc,
278         ((uint)level) << (chan ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB),
279         chan ? PWM_CH0_CC_B_BITS : PWM_CH0_CC_A_BITS
280     );
281 }
282 
283 /** \brief Set PWM counter compare values
284  *  \ingroup hardware_pwm
285  *
286  * Set the value of the PWM counter compare values, A and B.
287  *
288  * The counter compare register is double-buffered in hardware. This means
289  * that, when the PWM is running, a write to the counter compare values does
290  * not take effect until the next time the PWM slice wraps (or, in
291  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
292  * running, the write is latched in immediately.
293  *
294  * \param slice_num PWM slice number
295  * \param level_a Value to set compare A to. When the counter reaches this value the A output is deasserted
296  * \param level_b Value to set compare B to. When the counter reaches this value the B output is deasserted
297  */
pwm_set_both_levels(uint slice_num,uint16_t level_a,uint16_t level_b)298 static inline void pwm_set_both_levels(uint slice_num, uint16_t level_a, uint16_t level_b) {
299     check_slice_num_param(slice_num);
300     pwm_hw->slice[slice_num].cc = (((uint)level_b) << PWM_CH0_CC_B_LSB) | (((uint)level_a) << PWM_CH0_CC_A_LSB);
301 }
302 
303 /** \brief Helper function to set the PWM level for the slice and channel associated with a GPIO.
304  *  \ingroup hardware_pwm
305  *
306  * Look up the correct slice (0 to 7) and channel (A or B) for a given GPIO, and update the corresponding
307  * counter compare field.
308  *
309  * This PWM slice should already have been configured and set running. Also be careful of multiple GPIOs
310  * mapping to the same slice and channel (if GPIOs have a difference of 16).
311  *
312  * The counter compare register is double-buffered in hardware. This means
313  * that, when the PWM is running, a write to the counter compare values does
314  * not take effect until the next time the PWM slice wraps (or, in
315  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
316  * running, the write is latched in immediately.
317  *
318  * \param gpio GPIO to set level of
319  * \param level PWM level for this GPIO
320  */
pwm_set_gpio_level(uint gpio,uint16_t level)321 static inline void pwm_set_gpio_level(uint gpio, uint16_t level) {
322     valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
323     pwm_set_chan_level(pwm_gpio_to_slice_num(gpio), pwm_gpio_to_channel(gpio), level);
324 }
325 
326 /** \brief Get PWM counter
327  *  \ingroup hardware_pwm
328  *
329  * Get current value of PWM counter
330  *
331  * \param slice_num PWM slice number
332  * \return Current value of the PWM counter
333  */
pwm_get_counter(uint slice_num)334 static inline uint16_t pwm_get_counter(uint slice_num) {
335     check_slice_num_param(slice_num);
336     return (uint16_t)(pwm_hw->slice[slice_num].ctr);
337 }
338 
339 /** \brief Set PWM counter
340  *  \ingroup hardware_pwm
341  *
342  * Set the value of the PWM counter
343  *
344  * \param slice_num PWM slice number
345  * \param c Value to set the PWM counter to
346  *
347  */
pwm_set_counter(uint slice_num,uint16_t c)348 static inline void pwm_set_counter(uint slice_num, uint16_t c) {
349     check_slice_num_param(slice_num);
350     pwm_hw->slice[slice_num].ctr = c;
351 }
352 
353 /** \brief Advance PWM count
354  *  \ingroup hardware_pwm
355  *
356  * Advance the phase of a running the counter by 1 count.
357  *
358  * This function will return once the increment is complete.
359  *
360  * \param slice_num PWM slice number
361  */
pwm_advance_count(uint slice_num)362 static inline void pwm_advance_count(uint slice_num) {
363     check_slice_num_param(slice_num);
364     hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_ADV_BITS);
365     while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_ADV_BITS) {
366         tight_loop_contents();
367     }
368 }
369 
370 /** \brief Retard PWM count
371  *  \ingroup hardware_pwm
372  *
373  * Retard the phase of a running counter by 1 count
374  *
375  * This function will return once the retardation is complete.
376  *
377  * \param slice_num PWM slice number
378  */
pwm_retard_count(uint slice_num)379 static inline void pwm_retard_count(uint slice_num) {
380     check_slice_num_param(slice_num);
381     hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_RET_BITS);
382     while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_RET_BITS) {
383         tight_loop_contents();
384     }
385 }
386 
387 /** \brief Set PWM clock divider using an 8:4 fractional value
388  *  \ingroup hardware_pwm
389  *
390  * Set the clock divider. Counter increment will be on sysclock divided by this value, taking into account the gating.
391  *
392  * \param slice_num PWM slice number
393  * \param integer  8 bit integer part of the clock divider
394  * \param fract 4 bit fractional part of the clock divider
395  */
pwm_set_clkdiv_int_frac(uint slice_num,uint8_t integer,uint8_t fract)396 static inline void pwm_set_clkdiv_int_frac(uint slice_num, uint8_t integer, uint8_t fract) {
397     check_slice_num_param(slice_num);
398     valid_params_if(PWM, integer >= 1);
399     valid_params_if(PWM, fract < 16);
400     pwm_hw->slice[slice_num].div = (((uint)integer) << PWM_CH0_DIV_INT_LSB) | (((uint)fract) << PWM_CH0_DIV_FRAC_LSB);
401 }
402 
403 /** \brief Set PWM clock divider
404  *  \ingroup hardware_pwm
405  *
406  * Set the clock divider. Counter increment will be on sysclock divided by this value, taking into account the gating.
407  *
408  * \param slice_num PWM slice number
409  * \param divider Floating point clock divider,  1.f <= value < 256.f
410  */
pwm_set_clkdiv(uint slice_num,float divider)411 static inline void pwm_set_clkdiv(uint slice_num, float divider) {
412     check_slice_num_param(slice_num);
413     valid_params_if(PWM, divider >= 1.f && divider < 256.f);
414     uint8_t i = (uint8_t)divider;
415     uint8_t f = (uint8_t)((divider - i) * (0x01 << 4));
416     pwm_set_clkdiv_int_frac(slice_num, i, f);
417 }
418 
419 /** \brief Set PWM output polarity
420  *  \ingroup hardware_pwm
421  *
422  * \param slice_num PWM slice number
423  * \param a true to invert output A
424  * \param b true to invert output B
425  */
pwm_set_output_polarity(uint slice_num,bool a,bool b)426 static inline void pwm_set_output_polarity(uint slice_num, bool a, bool b) {
427     check_slice_num_param(slice_num);
428     hw_write_masked(&pwm_hw->slice[slice_num].csr, bool_to_bit(a) << PWM_CH0_CSR_A_INV_LSB | bool_to_bit(b) << PWM_CH0_CSR_B_INV_LSB,
429                      PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS);
430 }
431 
432 
433 /** \brief Set PWM divider mode
434  *  \ingroup hardware_pwm
435  *
436  * \param slice_num PWM slice number
437  * \param mode Required divider mode
438  */
pwm_set_clkdiv_mode(uint slice_num,enum pwm_clkdiv_mode mode)439 static inline void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode) {
440     check_slice_num_param(slice_num);
441     valid_params_if(PWM, mode == PWM_DIV_FREE_RUNNING ||
442                          mode == PWM_DIV_B_RISING ||
443                          mode == PWM_DIV_B_HIGH ||
444                          mode == PWM_DIV_B_FALLING);
445     hw_write_masked(&pwm_hw->slice[slice_num].csr, ((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB, PWM_CH0_CSR_DIVMODE_BITS);
446 }
447 
448 /** \brief Set PWM phase correct on/off
449  *  \ingroup hardware_pwm
450  *
451  * \param slice_num PWM slice number
452  * \param phase_correct true to set phase correct modulation, false to set trailing edge
453  *
454  * Setting phase control to true means that instead of wrapping back to zero when the wrap point is reached,
455  * the PWM starts counting back down. The output frequency is halved when phase-correct mode is enabled.
456  */
pwm_set_phase_correct(uint slice_num,bool phase_correct)457 static inline void pwm_set_phase_correct(uint slice_num, bool phase_correct) {
458     check_slice_num_param(slice_num);
459     hw_write_masked(&pwm_hw->slice[slice_num].csr, bool_to_bit(phase_correct) << PWM_CH0_CSR_PH_CORRECT_LSB, PWM_CH0_CSR_PH_CORRECT_BITS);
460 }
461 
462 /** \brief Enable/Disable PWM
463  *  \ingroup hardware_pwm
464  *
465  * When a PWM is disabled, it halts its counter, and the output pins are left
466  * high or low depending on exactly when the counter is halted. When
467  * re-enabled the PWM resumes immediately from where it left off.
468  *
469  * If the PWM's output pins need to be low when halted:
470  *
471  * - The counter compare can be set to zero whilst the PWM is enabled, and
472  *   then the PWM disabled once both pins are seen to be low
473  *
474  * - The GPIO output overrides can be used to force the actual pins low
475  *
476  * - The PWM can be run for one cycle (i.e. enabled then immediately disabled)
477  *   with a TOP of 0, count of 0 and counter compare of 0, to force the pins
478  *   low when the PWM has already been halted. The same method can be used
479  *   with a counter compare value of 1 to force a pin high.
480  *
481  * Note that, when disabled, the PWM can still be advanced one count at a time
482  * by pulsing the PH_ADV bit in its CSR. The output pins transition as though
483  * the PWM were enabled.
484  *
485  * \param slice_num PWM slice number
486  * \param enabled true to enable the specified PWM, false to disable.
487  */
pwm_set_enabled(uint slice_num,bool enabled)488 static inline void pwm_set_enabled(uint slice_num, bool enabled) {
489     check_slice_num_param(slice_num);
490     hw_write_masked(&pwm_hw->slice[slice_num].csr, bool_to_bit(enabled) << PWM_CH0_CSR_EN_LSB, PWM_CH0_CSR_EN_BITS);
491 }
492 
493 /** \brief Enable/Disable multiple PWM slices simultaneously
494  *  \ingroup hardware_pwm
495  *
496  * \param mask Bitmap of PWMs to enable/disable. Bits 0 to 7 enable slices 0-7 respectively
497  */
pwm_set_mask_enabled(uint32_t mask)498 static inline void pwm_set_mask_enabled(uint32_t mask) {
499     pwm_hw->en = mask;
500 }
501 
502 /*! \brief  Enable PWM instance interrupt
503  *  \ingroup hardware_pwm
504  *
505  * Used to enable a single PWM instance interrupt.
506  *
507  * \param slice_num PWM block to enable/disable
508  * \param enabled true to enable, false to disable
509  */
pwm_set_irq_enabled(uint slice_num,bool enabled)510 static inline void pwm_set_irq_enabled(uint slice_num, bool enabled) {
511     check_slice_num_param(slice_num);
512     if (enabled) {
513         hw_set_bits(&pwm_hw->inte, 1u << slice_num);
514     } else {
515         hw_clear_bits(&pwm_hw->inte, 1u << slice_num);
516     }
517 }
518 
519 /*! \brief  Enable multiple PWM instance interrupts
520  *  \ingroup hardware_pwm
521  *
522  * Use this to enable multiple PWM interrupts at once.
523  *
524  * \param slice_mask Bitmask of all the blocks to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
525  * \param enabled true to enable, false to disable
526  */
pwm_set_irq_mask_enabled(uint32_t slice_mask,bool enabled)527 static inline void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled) {
528     valid_params_if(PWM, slice_mask < 256);
529     if (enabled) {
530         hw_set_bits(&pwm_hw->inte, slice_mask);
531     } else {
532         hw_clear_bits(&pwm_hw->inte, slice_mask);
533     }
534 }
535 
536 /*! \brief  Clear a single PWM channel interrupt
537  *  \ingroup hardware_pwm
538  *
539  * \param slice_num PWM slice number
540  */
pwm_clear_irq(uint slice_num)541 static inline void pwm_clear_irq(uint slice_num) {
542     pwm_hw->intr = 1u << slice_num;
543 }
544 
545 /*! \brief  Get PWM interrupt status, raw
546  *  \ingroup hardware_pwm
547  *
548  * \return Bitmask of all PWM interrupts currently set
549  */
pwm_get_irq_status_mask(void)550 static inline uint32_t pwm_get_irq_status_mask(void) {
551     return pwm_hw->ints;
552 }
553 
554 /*! \brief  Force PWM interrupt
555  *  \ingroup hardware_pwm
556  *
557  * \param slice_num PWM slice number
558  */
pwm_force_irq(uint slice_num)559 static inline void pwm_force_irq(uint slice_num) {
560     pwm_hw->intf = 1u << slice_num;
561 }
562 
563 /*! \brief Return the DREQ to use for pacing transfers to a particular PWM slice
564  *  \ingroup hardware_pwm
565  *
566  * \param slice_num PWM slice number
567  */
pwm_get_dreq(uint slice_num)568 static inline uint pwm_get_dreq(uint slice_num) {
569     static_assert(DREQ_PWM_WRAP1 == DREQ_PWM_WRAP0 + 1, "");
570     static_assert(DREQ_PWM_WRAP7 == DREQ_PWM_WRAP0 + 7, "");
571     check_slice_num_param(slice_num);
572     return DREQ_PWM_WRAP0 + slice_num;
573 }
574 
575 #ifdef __cplusplus
576 }
577 #endif
578 
579 #endif
580