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 #include "hardware/regs/intctrl.h"
14 
15 #ifdef __cplusplus
16 extern "C" {
17 #endif
18 
19 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_PWM, Enable/disable assertions in the hardware_pwm module, type=bool, default=0, group=hardware_pwm
20 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_PWM
21 #ifdef PARAM_ASSERTIONS_ENABLED_PWM // backwards compatibility with SDK < 2.0.0
22 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_PWM PARAM_ASSERTIONS_ENABLED_PWM
23 #else
24 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_PWM 0
25 #endif
26 #endif
27 
28 /** \file hardware/pwm.h
29  *  \defgroup hardware_pwm hardware_pwm
30  *
31  * \brief Hardware Pulse Width Modulation (PWM) API
32  *
33  * The RP2040 PWM block has 8 identical slices, the RP2350 has 12.  Each slice can drive two PWM output signals, or
34  * measure the frequency or duty cycle of an input signal. This gives a total of up to 16/24 controllable
35  * PWM outputs. All 30 GPIOs can be driven by the PWM block.
36  *
37  * The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
38  * toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
39  * time spent at the high signal level is known as the duty cycle of the signal.
40  *
41  * The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
42  * immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
43  * reaching TOP, until it reaches 0 again.
44  *
45  * \subsection pwm_example Example
46  * \addtogroup hardware_pwm
47  * \include hello_pwm.c
48  */
49 
50 /** \brief PWM Divider mode settings
51  *   \ingroup hardware_pwm
52  *
53  */
54 enum pwm_clkdiv_mode
55 {
56     PWM_DIV_FREE_RUNNING = 0, ///< Free-running counting at rate dictated by fractional divider
57     PWM_DIV_B_HIGH = 1,       ///< Fractional divider is gated by the PWM B pin
58     PWM_DIV_B_RISING = 2,     ///< Fractional divider advances with each rising edge of the PWM B pin
59     PWM_DIV_B_FALLING = 3     ///< Fractional divider advances with each falling edge of the PWM B pin
60 };
61 
62 enum pwm_chan
63 {
64     PWM_CHAN_A = 0,
65     PWM_CHAN_B = 1
66 };
67 
68 typedef struct {
69     uint32_t csr;
70     uint32_t div;
71     uint32_t top;
72 } pwm_config;
73 
74 /**
75  * \def PWM_DREQ_NUM(slice_num)
76  * \ingroup hardware_pwm
77  * \hideinitializer
78  * \brief Returns the \ref dreq_num_t used for pacing DMA transfers for a given PWM slice
79  *
80  * Note this macro is intended to resolve at compile time, and does no parameter checking
81  */
82 #ifndef PWM_DREQ_NUM
83 static_assert(DREQ_PWM_WRAP1 == DREQ_PWM_WRAP0 + 1, "");
84 static_assert(DREQ_PWM_WRAP7 == DREQ_PWM_WRAP0 + 7, "");
85 #define PWM_DREQ_NUM(slice_num) (DREQ_PWM_WRAP0 + (slice_num))
86 #endif
87 
88 /**
89  * \def PWM_GPIO_SLICE_NUM(gpio)
90  * \ingroup hardware_pwm
91  * \hideinitializer
92  * \brief Returns the PWM slice number for a given GPIO number
93  */
94 #ifndef PWM_GPIO_SLICE_NUM
95 #define PWM_GPIO_SLICE_NUM(gpio) ({             \
96     uint slice_num;                             \
97     if ((gpio) < 32) {                          \
98         slice_num = ((gpio) >> 1u) & 7u;        \
99     } else {                                    \
100         slice_num = 8u + (((gpio) >> 1u) & 3u); \
101     }                                           \
102     slice_num;                                  \
103 })
104 #endif
105 
106 // PICO_CONFIG: PICO_PWM_CLKDIV_ROUND_NEAREST, True if floating point PWM clock divisors should be rounded to the nearest possible clock divisor rather than rounding down, type=bool, default=PICO_CLKDIV_ROUND_NEAREST, group=hardware_pwm
107 #ifndef PICO_PWM_CLKDIV_ROUND_NEAREST
108 #define PICO_PWM_CLKDIV_ROUND_NEAREST PICO_CLKDIV_ROUND_NEAREST
109 #endif
110 
check_slice_num_param(__unused uint slice_num)111 static inline void check_slice_num_param(__unused uint slice_num) {
112     valid_params_if(HARDWARE_PWM, slice_num < NUM_PWM_SLICES);
113 }
114 
115 /** \brief Determine the PWM slice that is attached to the specified GPIO
116  *  \ingroup hardware_pwm
117  *
118  * \return The PWM slice number that controls the specified GPIO.
119  */
pwm_gpio_to_slice_num(uint gpio)120 static inline uint pwm_gpio_to_slice_num(uint gpio) {
121     valid_params_if(HARDWARE_PWM, gpio < NUM_BANK0_GPIOS);
122     return PWM_GPIO_SLICE_NUM(gpio);
123 }
124 
125 /** \brief Determine the PWM channel that is attached to the specified GPIO.
126  *  \ingroup hardware_pwm
127  *
128  * Each slice 0 to 7 has two channels, A and B.
129  *
130  * \return The PWM channel that controls the specified GPIO.
131  */
pwm_gpio_to_channel(uint gpio)132 static inline uint pwm_gpio_to_channel(uint gpio) {
133     valid_params_if(HARDWARE_PWM, gpio < NUM_BANK0_GPIOS);
134     return gpio & 1u;
135 }
136 
137 /** \brief Set phase correction in a PWM configuration
138  *  \ingroup hardware_pwm
139  *
140  * \param c PWM configuration struct to modify
141  * \param phase_correct true to set phase correct modulation, false to set trailing edge
142  *
143  * Setting phase control to true means that instead of wrapping back to zero when the wrap point is reached,
144  * the PWM starts counting back down. The output frequency is halved when phase-correct mode is enabled.
145  */
pwm_config_set_phase_correct(pwm_config * c,bool phase_correct)146 static inline void pwm_config_set_phase_correct(pwm_config *c, bool phase_correct) {
147     c->csr = (c->csr & ~PWM_CH0_CSR_PH_CORRECT_BITS)
148         | (bool_to_bit(phase_correct) << PWM_CH0_CSR_PH_CORRECT_LSB);
149 }
150 
151 /** \brief Set PWM clock divider in a PWM configuration
152  *  \ingroup hardware_pwm
153  *
154  * \param c PWM configuration struct to modify
155  * \param div Value to divide counting rate by. Must be greater than or equal to 1.
156  *
157  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
158  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
159  * before passing them on to the PWM counter.
160  */
pwm_config_set_clkdiv(pwm_config * c,float div)161 static inline void pwm_config_set_clkdiv(pwm_config *c, float div) {
162     valid_params_if(HARDWARE_PWM, div >= 1.f && div < 256.f);
163     const int frac_bit_count = REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC);
164 #if PICO_PWM_CLKDIV_ROUND_NEAREST
165     div += 0.5f / (1 << frac_bit_count); // round to the nearest fraction
166 #endif
167     c->div = (uint32_t)(div * (float)(1u << frac_bit_count));
168 }
169 
170 /** \brief Set PWM clock divider in a PWM configuration using an 8:4 fractional value
171  *  \ingroup hardware_pwm
172  *
173  * \param c PWM configuration struct to modify
174  * \param div_int 8 bit integer part of the clock divider. Must be greater than or equal to 1.
175  * \param div_frac4 4 bit fractional part of the clock divider
176  *
177  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
178  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
179  * before passing them on to the PWM counter.
180  */
pwm_config_set_clkdiv_int_frac4(pwm_config * c,uint32_t div_int,uint8_t div_frac4)181 static inline void pwm_config_set_clkdiv_int_frac4(pwm_config *c, uint32_t div_int, uint8_t div_frac4) {
182     static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_INT) == 8, "");
183     valid_params_if(HARDWARE_PWM, div_int >= 1 && div_int < 256);
184     static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC) == 4, "");
185     valid_params_if(HARDWARE_PWM, div_frac4 < 16);
186     c->div = (((uint)div_int) << PWM_CH0_DIV_INT_LSB) | (((uint)div_frac4) << PWM_CH0_DIV_FRAC_LSB);
187 }
188 
189 // backwards compatibility
pwm_config_set_clkdiv_int_frac(pwm_config * c,uint8_t div_int,uint8_t div_frac4)190 static inline void pwm_config_set_clkdiv_int_frac(pwm_config *c, uint8_t div_int, uint8_t div_frac4) {
191     pwm_config_set_clkdiv_int_frac4(c, div_int, div_frac4);
192 }
193 
194 /** \brief Set PWM clock divider in a PWM configuration
195  *  \ingroup hardware_pwm
196  *
197  * \param c PWM configuration struct to modify
198  * \param div_int Integer value to reduce counting rate by. Must be greater than or equal to 1 and less than 256.
199  *
200  * If the divide mode is free-running, the PWM counter runs at clk_sys / div.
201  * Otherwise, the divider reduces the rate of events seen on the B pin input (level or edge)
202  * before passing them on to the PWM counter.
203  */
pwm_config_set_clkdiv_int(pwm_config * c,uint32_t div_int)204 static inline void pwm_config_set_clkdiv_int(pwm_config *c, uint32_t div_int) {
205     pwm_config_set_clkdiv_int_frac4(c, div_int, 0);
206 }
207 
208 /** \brief Set PWM counting mode in a PWM configuration
209  *  \ingroup hardware_pwm
210  *
211  * \param c PWM configuration struct to modify
212  * \param mode PWM divide/count mode
213  *
214  * Configure which event gates the operation of the fractional divider.
215  * The default is always-on (free-running PWM). Can also be configured to count on
216  * high level, rising edge or falling edge of the B pin input.
217  */
pwm_config_set_clkdiv_mode(pwm_config * c,enum pwm_clkdiv_mode mode)218 static inline void pwm_config_set_clkdiv_mode(pwm_config *c, enum pwm_clkdiv_mode mode) {
219     valid_params_if(HARDWARE_PWM, mode == PWM_DIV_FREE_RUNNING ||
220             mode == PWM_DIV_B_RISING ||
221             mode == PWM_DIV_B_HIGH ||
222             mode == PWM_DIV_B_FALLING);
223     c->csr = (c->csr & ~PWM_CH0_CSR_DIVMODE_BITS)
224         | (((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB);
225 }
226 
227 /** \brief Set output polarity in a PWM configuration
228  *  \ingroup hardware_pwm
229  *
230  * \param c PWM configuration struct to modify
231  * \param a true to invert output A
232  * \param b true to invert output B
233  */
pwm_config_set_output_polarity(pwm_config * c,bool a,bool b)234 static inline void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b) {
235     c->csr = (c->csr & ~(PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS))
236         | ((bool_to_bit(a) << PWM_CH0_CSR_A_INV_LSB) | (bool_to_bit(b) << PWM_CH0_CSR_B_INV_LSB));
237 }
238 
239 /** \brief Set PWM counter wrap value in a PWM configuration
240  *  \ingroup hardware_pwm
241  *
242  * Set the highest value the counter will reach before returning to 0. Also known as TOP.
243  *
244  * \param c PWM configuration struct to modify
245  * \param wrap Value to set wrap to
246  */
pwm_config_set_wrap(pwm_config * c,uint16_t wrap)247 static inline void pwm_config_set_wrap(pwm_config *c, uint16_t wrap) {
248     c->top = wrap;
249 }
250 
251 /** \brief Initialise a PWM with settings from a configuration object
252  *  \ingroup hardware_pwm
253  *
254  * Use the \ref pwm_get_default_config() function to initialise a config structure, make changes as
255  * needed using the pwm_config_* functions, then call this function to set up the PWM.
256  *
257  * \param slice_num PWM slice number
258  * \param c The configuration to use
259  * \param start If true the PWM will be started running once configured. If false you will need to start
260  *  manually using \ref pwm_set_enabled() or \ref pwm_set_mask_enabled()
261  */
pwm_init(uint slice_num,pwm_config * c,bool start)262 static inline void pwm_init(uint slice_num, pwm_config *c, bool start) {
263     check_slice_num_param(slice_num);
264     pwm_hw->slice[slice_num].csr = 0;
265 
266     pwm_hw->slice[slice_num].ctr = PWM_CH0_CTR_RESET;
267     pwm_hw->slice[slice_num].cc = PWM_CH0_CC_RESET;
268     pwm_hw->slice[slice_num].top = c->top;
269     pwm_hw->slice[slice_num].div = c->div;
270     pwm_hw->slice[slice_num].csr = c->csr | (bool_to_bit(start) << PWM_CH0_CSR_EN_LSB);
271 }
272 
273 /** \brief Get a set of default values for PWM configuration
274  *  \ingroup hardware_pwm
275  *
276  * PWM config is free-running at system clock speed, no phase correction, wrapping at 0xffff,
277  * with standard polarities for channels A and B.
278  *
279  * \return Set of default values.
280  */
pwm_get_default_config(void)281 static inline pwm_config pwm_get_default_config(void) {
282     pwm_config c = {0, 0, 0};
283     pwm_config_set_phase_correct(&c, false);
284     pwm_config_set_clkdiv_int(&c, 1);
285     pwm_config_set_clkdiv_mode(&c, PWM_DIV_FREE_RUNNING);
286     pwm_config_set_output_polarity(&c, false, false);
287     pwm_config_set_wrap(&c, 0xffffu);
288     return c;
289 }
290 
291 /** \brief Set the current PWM counter wrap value
292  *  \ingroup hardware_pwm
293  *
294  * Set the highest value the counter will reach before returning to 0. Also
295  * known as TOP.
296  *
297  * The counter wrap value is double-buffered in hardware. This means that,
298  * when the PWM is running, a write to the counter wrap value does not take
299  * effect until after the next time the PWM slice wraps (or, in phase-correct
300  * mode, the next time the slice reaches 0). If the PWM is not running, the
301  * write is latched in immediately.
302  *
303  * \param slice_num PWM slice number
304  * \param wrap Value to set wrap to
305  */
pwm_set_wrap(uint slice_num,uint16_t wrap)306 static inline void pwm_set_wrap(uint slice_num, uint16_t wrap) {
307     check_slice_num_param(slice_num);
308     pwm_hw->slice[slice_num].top = wrap;
309 }
310 
311 /** \brief Set the current PWM counter compare value for one channel
312  *  \ingroup hardware_pwm
313  *
314  * Set the value of the PWM counter compare value, for either channel A or channel B.
315  *
316  * The counter compare register is double-buffered in hardware. This means
317  * that, when the PWM is running, a write to the counter compare values does
318  * not take effect until the next time the PWM slice wraps (or, in
319  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
320  * running, the write is latched in immediately.
321  *
322  * \param slice_num PWM slice number
323  * \param chan Which channel to update. 0 for A, 1 for B.
324  * \param level new level for the selected output
325  */
pwm_set_chan_level(uint slice_num,uint chan,uint16_t level)326 static inline void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level) {
327     check_slice_num_param(slice_num);
328     hw_write_masked(
329         &pwm_hw->slice[slice_num].cc,
330         ((uint)level) << (chan ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB),
331         chan ? PWM_CH0_CC_B_BITS : PWM_CH0_CC_A_BITS
332     );
333 }
334 
335 /** \brief Set PWM counter compare values
336  *  \ingroup hardware_pwm
337  *
338  * Set the value of the PWM counter compare values, A and B.
339  *
340  * The counter compare register is double-buffered in hardware. This means
341  * that, when the PWM is running, a write to the counter compare values does
342  * not take effect until the next time the PWM slice wraps (or, in
343  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
344  * running, the write is latched in immediately.
345  *
346  * \param slice_num PWM slice number
347  * \param level_a Value to set compare A to. When the counter reaches this value the A output is deasserted
348  * \param level_b Value to set compare B to. When the counter reaches this value the B output is deasserted
349  */
pwm_set_both_levels(uint slice_num,uint16_t level_a,uint16_t level_b)350 static inline void pwm_set_both_levels(uint slice_num, uint16_t level_a, uint16_t level_b) {
351     check_slice_num_param(slice_num);
352     pwm_hw->slice[slice_num].cc = (((uint)level_b) << PWM_CH0_CC_B_LSB) | (((uint)level_a) << PWM_CH0_CC_A_LSB);
353 }
354 
355 /** \brief Helper function to set the PWM level for the slice and channel associated with a GPIO.
356  *  \ingroup hardware_pwm
357  *
358  * Look up the correct slice (0 to 7) and channel (A or B) for a given GPIO, and update the corresponding
359  * counter compare field.
360  *
361  * This PWM slice should already have been configured and set running. Also be careful of multiple GPIOs
362  * mapping to the same slice and channel (if GPIOs have a difference of 16).
363  *
364  * The counter compare register is double-buffered in hardware. This means
365  * that, when the PWM is running, a write to the counter compare values does
366  * not take effect until the next time the PWM slice wraps (or, in
367  * phase-correct mode, the next time the slice reaches 0). If the PWM is not
368  * running, the write is latched in immediately.
369  *
370  * \param gpio GPIO to set level of
371  * \param level PWM level for this GPIO
372  */
pwm_set_gpio_level(uint gpio,uint16_t level)373 static inline void pwm_set_gpio_level(uint gpio, uint16_t level) {
374     valid_params_if(HARDWARE_PWM, gpio < NUM_BANK0_GPIOS);
375     pwm_set_chan_level(pwm_gpio_to_slice_num(gpio), pwm_gpio_to_channel(gpio), level);
376 }
377 
378 /** \brief Get PWM counter
379  *  \ingroup hardware_pwm
380  *
381  * Get current value of PWM counter
382  *
383  * \param slice_num PWM slice number
384  * \return Current value of the PWM counter
385  */
pwm_get_counter(uint slice_num)386 static inline uint16_t pwm_get_counter(uint slice_num) {
387     check_slice_num_param(slice_num);
388     return (uint16_t)(pwm_hw->slice[slice_num].ctr);
389 }
390 
391 /** \brief Set PWM counter
392  *  \ingroup hardware_pwm
393  *
394  * Set the value of the PWM counter
395  *
396  * \param slice_num PWM slice number
397  * \param c Value to set the PWM counter to
398  *
399  */
pwm_set_counter(uint slice_num,uint16_t c)400 static inline void pwm_set_counter(uint slice_num, uint16_t c) {
401     check_slice_num_param(slice_num);
402     pwm_hw->slice[slice_num].ctr = c;
403 }
404 
405 /** \brief Advance PWM count
406  *  \ingroup hardware_pwm
407  *
408  * Advance the phase of a running the counter by 1 count.
409  *
410  * This function will return once the increment is complete.
411  *
412  * \param slice_num PWM slice number
413  */
pwm_advance_count(uint slice_num)414 static inline void pwm_advance_count(uint slice_num) {
415     check_slice_num_param(slice_num);
416     hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_ADV_BITS);
417     while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_ADV_BITS) {
418         tight_loop_contents();
419     }
420 }
421 
422 /** \brief Retard PWM count
423  *  \ingroup hardware_pwm
424  *
425  * Retard the phase of a running counter by 1 count
426  *
427  * This function will return once the retardation is complete.
428  *
429  * \param slice_num PWM slice number
430  */
pwm_retard_count(uint slice_num)431 static inline void pwm_retard_count(uint slice_num) {
432     check_slice_num_param(slice_num);
433     hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_RET_BITS);
434     while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_RET_BITS) {
435         tight_loop_contents();
436     }
437 }
438 
439 /** \brief Set PWM clock divider using an 8:4 fractional value
440  *  \ingroup hardware_pwm
441  *
442  * Set the clock divider. Counter increment will be on sysclock divided by this value, taking into account the gating.
443  *
444  * \param slice_num PWM slice number
445  * \param div_int  8 bit integer part of the clock divider
446  * \param div_frac4 4 bit fractional part of the clock divider
447  */
pwm_set_clkdiv_int_frac4(uint slice_num,uint8_t div_int,uint8_t div_frac4)448 static inline void pwm_set_clkdiv_int_frac4(uint slice_num, uint8_t div_int, uint8_t div_frac4) {
449     check_slice_num_param(slice_num);
450     valid_params_if(HARDWARE_PWM, div_int >= 1);
451     static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC) == 4, "");
452     valid_params_if(HARDWARE_PWM, div_frac4 < 16);
453     pwm_hw->slice[slice_num].div = (((uint)div_int) << PWM_CH0_DIV_INT_LSB) | (((uint)div_frac4) << PWM_CH0_DIV_FRAC_LSB);
454 }
455 
456 // backwards compatibility
pwm_set_clkdiv_int_frac(uint slice_num,uint8_t div_int,uint8_t div_frac4)457 static inline void pwm_set_clkdiv_int_frac(uint slice_num, uint8_t div_int, uint8_t div_frac4) {
458     pwm_set_clkdiv_int_frac4(slice_num, div_int, div_frac4);
459 }
460 
461 /** \brief Set PWM clock divider
462  *  \ingroup hardware_pwm
463  *
464  * Set the clock divider. Counter increment will be on sysclock divided by this value, taking into account the gating.
465  *
466  * \param slice_num PWM slice number
467  * \param divider Floating point clock divider,  1.f <= value < 256.f
468  */
pwm_set_clkdiv(uint slice_num,float divider)469 static inline void pwm_set_clkdiv(uint slice_num, float divider) {
470     check_slice_num_param(slice_num);
471     valid_params_if(HARDWARE_PWM, divider >= 1.f && divider < 256.f);
472     uint8_t i = (uint8_t)divider;
473     uint8_t f = (uint8_t)((divider - i) * (0x01 << 4));
474     pwm_set_clkdiv_int_frac4(slice_num, i, f);
475 }
476 
477 /** \brief Set PWM output polarity
478  *  \ingroup hardware_pwm
479  *
480  * \param slice_num PWM slice number
481  * \param a true to invert output A
482  * \param b true to invert output B
483  */
pwm_set_output_polarity(uint slice_num,bool a,bool b)484 static inline void pwm_set_output_polarity(uint slice_num, bool a, bool b) {
485     check_slice_num_param(slice_num);
486     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,
487                      PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS);
488 }
489 
490 
491 /** \brief Set PWM divider mode
492  *  \ingroup hardware_pwm
493  *
494  * \param slice_num PWM slice number
495  * \param mode Required divider mode
496  */
pwm_set_clkdiv_mode(uint slice_num,enum pwm_clkdiv_mode mode)497 static inline void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode) {
498     check_slice_num_param(slice_num);
499     valid_params_if(HARDWARE_PWM, mode == PWM_DIV_FREE_RUNNING ||
500                          mode == PWM_DIV_B_RISING ||
501                          mode == PWM_DIV_B_HIGH ||
502                          mode == PWM_DIV_B_FALLING);
503     hw_write_masked(&pwm_hw->slice[slice_num].csr, ((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB, PWM_CH0_CSR_DIVMODE_BITS);
504 }
505 
506 /** \brief Set PWM phase correct on/off
507  *  \ingroup hardware_pwm
508  *
509  * \param slice_num PWM slice number
510  * \param phase_correct true to set phase correct modulation, false to set trailing edge
511  *
512  * Setting phase control to true means that instead of wrapping back to zero when the wrap point is reached,
513  * the PWM starts counting back down. The output frequency is halved when phase-correct mode is enabled.
514  */
pwm_set_phase_correct(uint slice_num,bool phase_correct)515 static inline void pwm_set_phase_correct(uint slice_num, bool phase_correct) {
516     check_slice_num_param(slice_num);
517     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);
518 }
519 
520 /** \brief Enable/Disable PWM
521  *  \ingroup hardware_pwm
522  *
523  * When a PWM is disabled, it halts its counter, and the output pins are left
524  * high or low depending on exactly when the counter is halted. When
525  * re-enabled the PWM resumes immediately from where it left off.
526  *
527  * If the PWM's output pins need to be low when halted:
528  *
529  * - The counter compare can be set to zero whilst the PWM is enabled, and
530  *   then the PWM disabled once both pins are seen to be low
531  *
532  * - The GPIO output overrides can be used to force the actual pins low
533  *
534  * - The PWM can be run for one cycle (i.e. enabled then immediately disabled)
535  *   with a TOP of 0, count of 0 and counter compare of 0, to force the pins
536  *   low when the PWM has already been halted. The same method can be used
537  *   with a counter compare value of 1 to force a pin high.
538  *
539  * Note that, when disabled, the PWM can still be advanced one count at a time
540  * by pulsing the PH_ADV bit in its CSR. The output pins transition as though
541  * the PWM were enabled.
542  *
543  * \param slice_num PWM slice number
544  * \param enabled true to enable the specified PWM, false to disable.
545  */
pwm_set_enabled(uint slice_num,bool enabled)546 static inline void pwm_set_enabled(uint slice_num, bool enabled) {
547     check_slice_num_param(slice_num);
548     hw_write_masked(&pwm_hw->slice[slice_num].csr, bool_to_bit(enabled) << PWM_CH0_CSR_EN_LSB, PWM_CH0_CSR_EN_BITS);
549 }
550 
551 /** \brief Enable/Disable multiple PWM slices simultaneously
552  *  \ingroup hardware_pwm
553  *
554  * \param mask Bitmap of PWMs to enable/disable. Bits 0 to 7 enable slices 0-7 respectively
555  */
pwm_set_mask_enabled(uint32_t mask)556 static inline void pwm_set_mask_enabled(uint32_t mask) {
557     pwm_hw->en = mask;
558 }
559 
560 /**
561  * \def PWM_DEFAULT_IRQ_NUM()
562  * \ingroup hardware_pwm
563  * \hideinitializer
564  * \brief Returns the \ref irq_num_t for the default PWM IRQ.
565  *
566  * \if rp2040_specific
567  * On RP2040, there is only one PWM irq: PWM_IRQ_WRAP
568  * \endif
569  *
570  * \if rp2350_specific
571  * On RP2350 this returns to PWM_IRQ_WRAP0
572  * \endif
573  *
574  * Note this macro is intended to resolve at compile time, and does no parameter checking
575  */
576 #ifndef PWM_DEFAULT_IRQ_NUM
577 #if PICO_RP2040
578 #define PWM_DEFAULT_IRQ_NUM() PWM_IRQ_WRAP
579 #else
580 #define PWM_DEFAULT_IRQ_NUM() PWM_IRQ_WRAP_0
581 // backwards compatibility with RP2040
582 #define PWM_IRQ_WRAP          PWM_IRQ_WRAP_0
583 #define isr_pwm_wrap          isr_pwm_wrap_0
584 #endif
585 #endif
586 
587 /*! \brief  Enable PWM instance interrupt via the default PWM IRQ (PWM_IRQ_WRAP_0 on RP2350)
588  *  \ingroup hardware_pwm
589  *
590  * Used to enable a single PWM instance interrupt.
591  *
592  * Note there is only one PWM_IRQ_WRAP on RP2040.
593  *
594  * \param slice_num PWM block to enable/disable
595  * \param enabled true to enable, false to disable
596  */
pwm_set_irq_enabled(uint slice_num,bool enabled)597 static inline void pwm_set_irq_enabled(uint slice_num, bool enabled) {
598     check_slice_num_param(slice_num);
599     if (enabled) {
600         hw_set_bits(&pwm_hw->inte, 1u << slice_num);
601     } else {
602         hw_clear_bits(&pwm_hw->inte, 1u << slice_num);
603     }
604 }
605 
606 /*! \brief  Enable PWM instance interrupt via PWM_IRQ_WRAP_0
607  *  \ingroup hardware_pwm
608  *
609  * Used to enable a single PWM instance interrupt.
610  *
611  * \param slice_num PWM block to enable/disable
612  * \param enabled true to enable, false to disable
613  */
pwm_set_irq0_enabled(uint slice_num,bool enabled)614 static inline void pwm_set_irq0_enabled(uint slice_num, bool enabled) {
615     // irq0 always corresponds to the default IRQ
616     pwm_set_irq_enabled(slice_num, enabled);
617 }
618 
619 #if NUM_PWM_IRQS > 1
620 /*! \brief  Enable PWM instance interrupt via PWM_IRQ_WRAP_1
621  *  \ingroup hardware_pwm
622  *
623  * Used to enable a single PWM instance interrupt.
624  *
625  * \param slice_num PWM block to enable/disable
626  * \param enabled true to enable, false to disable
627  */
pwm_set_irq1_enabled(uint slice_num,bool enabled)628 static inline void pwm_set_irq1_enabled(uint slice_num, bool enabled) {
629     check_slice_num_param(slice_num);
630     if (enabled) {
631         hw_set_bits(&pwm_hw->inte1, 1u << slice_num);
632     } else {
633         hw_clear_bits(&pwm_hw->inte1, 1u << slice_num);
634     }
635 }
636 #endif
637 
638 /*! \brief  Enable PWM instance interrupt via either PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
639  *  \ingroup hardware_pwm
640  *
641  * Used to enable a single PWM instance interrupt.
642  *
643  * Note there is only one PWM_IRQ_WRAP on RP2040.
644  *
645  * \param irq_index the IRQ index; either 0 or 1 for PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
646  * \param slice_num PWM block to enable/disable
647  * \param enabled true to enable, false to disable
648  */
pwm_irqn_set_slice_enabled(uint irq_index,uint slice_num,bool enabled)649 static inline void pwm_irqn_set_slice_enabled(uint irq_index, uint slice_num, bool enabled) {
650     check_slice_num_param(slice_num);
651     invalid_params_if(HARDWARE_PWM, irq_index >= NUM_PWM_IRQS);
652     check_slice_num_param(slice_num);
653     if (enabled) {
654         hw_set_bits(&pwm_hw->irq_ctrl[irq_index].inte, 1u << slice_num);
655     } else {
656         hw_clear_bits(&pwm_hw->irq_ctrl[irq_index].inte, 1u << slice_num);
657     }
658 }
659 
660 /*! \brief  Enable multiple PWM instance interrupts via the default PWM IRQ (PWM_IRQ_WRAP_0 on RP2350)
661  *  \ingroup hardware_pwm
662  *
663  * Use this to enable multiple PWM interrupts at once.
664  *
665  * Note there is only one PWM_IRQ_WRAP on RP2040.
666  *
667  * \param slice_mask Bitmask of all the blocks to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
668  * \param enabled true to enable, false to disable
669  */
pwm_set_irq_mask_enabled(uint32_t slice_mask,bool enabled)670 static inline void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled) {
671     valid_params_if(HARDWARE_PWM, slice_mask < 256);
672 #if PICO_RP2040
673     if (enabled) {
674         hw_set_bits(&pwm_hw->inte, slice_mask);
675     } else {
676         hw_clear_bits(&pwm_hw->inte, slice_mask);
677     }
678 #else
679     static_assert(PWM_IRQ_WRAP_1 == PWM_IRQ_WRAP_0 + 1, "");
680     uint irq_index = PWM_DEFAULT_IRQ_NUM() - PWM_IRQ_WRAP_0;
681     if (enabled) {
682         hw_set_bits(&pwm_hw->irq_ctrl[irq_index].inte, slice_mask);
683     } else {
684         hw_clear_bits(&pwm_hw->irq_ctrl[irq_index].inte, slice_mask);
685     }
686 #endif
687 }
688 
689 /*! \brief  Enable multiple PWM instance interrupts via PWM_IRQ_WRAP_0
690  *  \ingroup hardware_pwm
691  *
692  * Use this to enable multiple PWM interrupts at once.
693  *
694  * \param slice_mask Bitmask of all the blocks to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
695  * \param enabled true to enable, false to disable
696  */
pwm_set_irq0_mask_enabled(uint32_t slice_mask,bool enabled)697 static inline void pwm_set_irq0_mask_enabled(uint32_t slice_mask, bool enabled) {
698     // default irq is irq0
699     pwm_set_irq_mask_enabled(slice_mask, enabled);
700 }
701 
702 #if NUM_PWM_IRQS > 1
703 /*! \brief  Enable multiple PWM instance interrupts via PWM_IRQ_WRAP_1
704  *  \ingroup hardware_pwm
705  *
706  * Use this to enable multiple PWM interrupts at once.
707  *
708  * \param slice_mask Bitmask of all the blocks to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
709  * \param enabled true to enable, false to disable
710  */
pwm_set_irq1_mask_enabled(uint32_t slice_mask,bool enabled)711 static inline void pwm_set_irq1_mask_enabled(uint32_t slice_mask, bool enabled) {
712     if (enabled) {
713         hw_set_bits(&pwm_hw->inte1, slice_mask);
714     } else {
715         hw_clear_bits(&pwm_hw->inte1, slice_mask);
716     }
717 }
718 #endif
719 
720 /*! \brief  Enable PWM instance interrupts via either PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
721 *  \ingroup hardware_pwm
722 *
723 * Used to enable a single PWM instance interrupt.
724 *
725 * Note there is only one PWM_IRQ_WRAP on RP2040.
726 *
727 * \param irq_index the IRQ index; either 0 or 1 for PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
728 * \param slice_mask Bitmask of all the blocks to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
729 * \param enabled true to enable, false to disable
730 */
pwm_irqn_set_slice_mask_enabled(uint irq_index,uint slice_mask,bool enabled)731 static inline void pwm_irqn_set_slice_mask_enabled(uint irq_index, uint slice_mask, bool enabled) {
732     invalid_params_if(HARDWARE_PWM, irq_index >= NUM_PWM_IRQS);
733     if (enabled) {
734         hw_set_bits(&pwm_hw->irq_ctrl[irq_index].inte, slice_mask);
735     } else {
736         hw_clear_bits(&pwm_hw->irq_ctrl[irq_index].inte, slice_mask);
737     }
738 }
739 
740 /*! \brief  Clear a single PWM channel interrupt
741  *  \ingroup hardware_pwm
742  *
743  * \param slice_num PWM slice number
744  */
pwm_clear_irq(uint slice_num)745 static inline void pwm_clear_irq(uint slice_num) {
746     pwm_hw->intr = 1u << slice_num;
747 }
748 
749 /*! \brief  Get PWM interrupt status, raw for the default PWM IRQ (PWM_IRQ_WRAP_0 on RP2350)
750  *  \ingroup hardware_pwm
751  *
752  * \return Bitmask of all PWM interrupts currently set
753  */
pwm_get_irq_status_mask(void)754 static inline uint32_t pwm_get_irq_status_mask(void) {
755     return pwm_hw->ints;
756 }
757 
758 /*! \brief  Get PWM interrupt status, raw for the PWM_IRQ_WRAP_0
759  *  \ingroup hardware_pwm
760  *
761  * \return Bitmask of all PWM interrupts currently set
762  */
pwm_get_irq0_status_mask(void)763 static inline uint32_t pwm_get_irq0_status_mask(void) {
764     return pwm_get_irq_status_mask();
765 }
766 
767 #if NUM_PWM_IRQS > 1
768 /*! \brief  Get PWM interrupt status, raw for the PWM_IRQ_WRAP_1
769  *  \ingroup hardware_pwm
770  *
771  * \return Bitmask of all PWM interrupts currently set
772  */
pwm_get_irq1_status_mask(void)773 static inline uint32_t pwm_get_irq1_status_mask(void) {
774     return pwm_hw->ints1;
775 }
776 #endif
777 
778 /*! \brief  Get PWM interrupt status, raw for either PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
779  *  \ingroup hardware_pwm
780  *
781 * \param irq_index the IRQ index; either 0 or 1 for PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
782  * \return Bitmask of all PWM interrupts currently set
783  */
pwm_irqn_get_status_mask(uint irq_index)784 static inline uint32_t pwm_irqn_get_status_mask(uint irq_index) {
785     invalid_params_if(HARDWARE_PWM, irq_index >= NUM_PWM_IRQS);
786     return pwm_hw->irq_ctrl[irq_index].ints;
787 }
788 
789 /*! \brief  Force PWM interrupt for the default PWM IRQ (PWM_IRQ_WRAP_0 on RP2350)
790  *  \ingroup hardware_pwm
791  *
792  * \param slice_num PWM slice number
793  */
pwm_force_irq(uint slice_num)794 static inline void pwm_force_irq(uint slice_num) {
795     pwm_hw->intf = 1u << slice_num;
796 }
797 
798 /*! \brief  Force PWM interrupt via PWM_IRQ_WRAP_0
799  *  \ingroup hardware_pwm
800  *
801  * \param slice_num PWM slice number
802  */
pwm_force_irq0(uint slice_num)803 static inline void pwm_force_irq0(uint slice_num) {
804     pwm_force_irq(slice_num);
805 }
806 
807 #if NUM_PWM_IRQS > 1
808 /*! \brief  Force PWM interrupt via PWM_IRQ_WRAP_0
809  *  \ingroup hardware_pwm
810  *
811  * \param slice_num PWM slice number
812  */
pwm_force_irq1(uint slice_num)813 static inline void pwm_force_irq1(uint slice_num) {
814     pwm_hw->intf1 = 1u << slice_num;
815 }
816 #endif
817 
818 /*! \brief  Force PWM interrupt via PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
819  *  \ingroup hardware_pwm
820  *
821  * \param irq_index the IRQ index; either 0 or 1 for PWM_IRQ_WRAP_0 or PWM_IRQ_WRAP_1
822  * \param slice_num PWM slice number
823  */
pwm_irqn_force(uint irq_index,uint slice_num)824 static inline void pwm_irqn_force(uint irq_index, uint slice_num) {
825     invalid_params_if(HARDWARE_PWM, irq_index >= NUM_PWM_IRQS);
826     pwm_hw->irq_ctrl[irq_index].intf = 1u << slice_num;
827 }
828 
829 /*! \brief Return the DREQ to use for pacing transfers to a particular PWM slice
830  *  \ingroup hardware_pwm
831  *
832  * \param slice_num PWM slice number
833  */
pwm_get_dreq(uint slice_num)834 static inline uint pwm_get_dreq(uint slice_num) {
835     check_slice_num_param(slice_num);
836     return PWM_DREQ_NUM(slice_num);
837 }
838 
839 #ifdef __cplusplus
840 }
841 #endif
842 
843 #endif
844