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