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