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