pwm.h
Go to the documentation of this file.
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 
50 {
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 
69 static inline void check_slice_num_param(__unused uint slice_num) {
70  valid_params_if(PWM, slice_num < NUM_PWM_SLICES);
71 }
72 
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 
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 
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 
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 
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 
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 
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 
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 
195 static inline void pwm_config_set_wrap(pwm_config *c, uint16_t wrap) {
196  c->top = wrap;
197 }
198 
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 
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);
234  pwm_config_set_output_polarity(&c, false, false);
235  pwm_config_set_wrap(&c, 0xffffu);
236  return c;
237 }
238 
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 
274 static inline void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level) {
275  check_slice_num_param(slice_num);
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 
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 
321 static inline void pwm_set_gpio_level(uint gpio, uint16_t level) {
322  valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
324 }
325 
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 
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 
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) {
367  }
368 }
369 
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) {
384  }
385 }
386 
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 
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 
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 
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 
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 
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 
498 static inline void pwm_set_mask_enabled(uint32_t mask) {
499  pwm_hw->en = mask;
500 }
501 
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 
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 
541 static inline void pwm_clear_irq(uint slice_num) {
542  pwm_hw->intr = 1u << slice_num;
543 }
544 
550 static inline uint32_t pwm_get_irq_status_mask(void) {
551  return pwm_hw->ints;
552 }
553 
559 static inline void pwm_force_irq(uint slice_num) {
560  pwm_hw->intf = 1u << slice_num;
561 }
562 
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
pwm_set_irq_mask_enabled
static void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled)
Enable multiple PWM instance interrupts.
Definition: pwm.h:527
hw_clear_bits
static __force_inline void hw_clear_bits(io_rw_32 *addr, uint32_t mask)
Atomically clear the specified bits to 0 in a HW register.
Definition: address_mapped.h:131
pwm_set_wrap
static void pwm_set_wrap(uint slice_num, uint16_t wrap)
Set the current PWM counter wrap value.
Definition: pwm.h:254
pwm_set_chan_level
static void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level)
Set the current PWM counter compare value for one channel.
Definition: pwm.h:274
pwm_set_mask_enabled
static void pwm_set_mask_enabled(uint32_t mask)
Enable/Disable multiple PWM slices simultaneously.
Definition: pwm.h:498
pwm_set_output_polarity
static void pwm_set_output_polarity(uint slice_num, bool a, bool b)
Set PWM output polarity.
Definition: pwm.h:426
pwm_retard_count
static void pwm_retard_count(uint slice_num)
Retard PWM count.
Definition: pwm.h:379
PWM_DIV_B_RISING
@ PWM_DIV_B_RISING
Fractional divider advances with each rising edge of the PWM B pin.
Definition: pwm.h:53
pwm_config_set_clkdiv_int
static void pwm_config_set_clkdiv_int(pwm_config *c, uint div)
Set PWM clock divider in a PWM configuration.
Definition: pwm.h:151
pwm_advance_count
static void pwm_advance_count(uint slice_num)
Advance PWM count.
Definition: pwm.h:362
pwm_clkdiv_mode
pwm_clkdiv_mode
PWM Divider mode settings.
Definition: pwm.h:49
pwm_config_set_clkdiv_int_frac
static void pwm_config_set_clkdiv_int_frac(pwm_config *c, uint8_t integer, uint8_t fract)
Set PWM clock divider in a PWM configuration using an 8:4 fractional value.
Definition: pwm.h:135
pwm_set_clkdiv_int_frac
static void pwm_set_clkdiv_int_frac(uint slice_num, uint8_t integer, uint8_t fract)
Set PWM clock divider using an 8:4 fractional value.
Definition: pwm.h:396
PWM_DIV_B_HIGH
@ PWM_DIV_B_HIGH
Fractional divider is gated by the PWM B pin.
Definition: pwm.h:52
pwm_get_dreq
static uint pwm_get_dreq(uint slice_num)
Return the DREQ to use for pacing transfers to a particular PWM slice.
Definition: pwm.h:568
pwm_config_set_clkdiv
static void pwm_config_set_clkdiv(pwm_config *c, float div)
Set PWM clock divider in a PWM configuration.
Definition: pwm.h:119
pwm_set_irq_enabled
static void pwm_set_irq_enabled(uint slice_num, bool enabled)
Enable PWM instance interrupt.
Definition: pwm.h:510
pwm_set_gpio_level
static void pwm_set_gpio_level(uint gpio, uint16_t level)
Helper function to set the PWM level for the slice and channel associated with a GPIO.
Definition: pwm.h:321
pwm_config_set_phase_correct
static void pwm_config_set_phase_correct(pwm_config *c, bool phase_correct)
Set phase correction in a PWM configuration.
Definition: pwm.h:104
PWM_DIV_FREE_RUNNING
@ PWM_DIV_FREE_RUNNING
Free-running counting at rate dictated by fractional divider.
Definition: pwm.h:51
pwm_force_irq
static void pwm_force_irq(uint slice_num)
Force PWM interrupt.
Definition: pwm.h:559
pwm_config
Definition: pwm.h:63
PWM_DIV_B_FALLING
@ PWM_DIV_B_FALLING
Fractional divider advances with each falling edge of the PWM B pin.
Definition: pwm.h:54
hw_write_masked
static __force_inline void hw_write_masked(io_rw_32 *addr, uint32_t values, uint32_t write_mask)
Set new values for a sub-set of the bits in a HW register.
Definition: address_mapped.h:157
pwm_set_clkdiv_mode
static void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode)
Set PWM divider mode.
Definition: pwm.h:439
hw_set_bits
static __force_inline void hw_set_bits(io_rw_32 *addr, uint32_t mask)
Atomically set the specified bits to 1 in a HW register.
Definition: address_mapped.h:121
pwm_set_phase_correct
static void pwm_set_phase_correct(uint slice_num, bool phase_correct)
Set PWM phase correct on/off.
Definition: pwm.h:457
pwm_init
static void pwm_init(uint slice_num, pwm_config *c, bool start)
Initialise a PWM with settings from a configuration object.
Definition: pwm.h:210
pwm_set_both_levels
static void pwm_set_both_levels(uint slice_num, uint16_t level_a, uint16_t level_b)
Set PWM counter compare values.
Definition: pwm.h:298
pwm_gpio_to_slice_num
static uint pwm_gpio_to_slice_num(uint gpio)
Determine the PWM slice that is attached to the specified GPIO.
Definition: pwm.h:78
tight_loop_contents
static __always_inline void tight_loop_contents(void)
No-op function for the body of tight loops.
Definition: platform.h:358
pwm_config_set_clkdiv_mode
static void pwm_config_set_clkdiv_mode(pwm_config *c, enum pwm_clkdiv_mode mode)
Set PWM counting mode in a PWM configuration.
Definition: pwm.h:166
pico.h
pwm_gpio_to_channel
static uint pwm_gpio_to_channel(uint gpio)
Determine the PWM channel that is attached to the specified GPIO.
Definition: pwm.h:90
pwm_set_counter
static void pwm_set_counter(uint slice_num, uint16_t c)
Set PWM counter.
Definition: pwm.h:348
pwm_get_default_config
static pwm_config pwm_get_default_config(void)
Get a set of default values for PWM configuration.
Definition: pwm.h:229
pwm_set_enabled
static void pwm_set_enabled(uint slice_num, bool enabled)
Enable/Disable PWM.
Definition: pwm.h:488
pwm_set_clkdiv
static void pwm_set_clkdiv(uint slice_num, float divider)
Set PWM clock divider.
Definition: pwm.h:411
pwm_config_set_output_polarity
static void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b)
Set output polarity in a PWM configuration.
Definition: pwm.h:182
pwm_config_set_wrap
static void pwm_config_set_wrap(pwm_config *c, uint16_t wrap)
Set PWM counter wrap value in a PWM configuration.
Definition: pwm.h:195
pwm_clear_irq
static void pwm_clear_irq(uint slice_num)
Clear a single PWM channel interrupt.
Definition: pwm.h:541
pwm_get_counter
static uint16_t pwm_get_counter(uint slice_num)
Get PWM counter.
Definition: pwm.h:334
pwm_get_irq_status_mask
static uint32_t pwm_get_irq_status_mask(void)
Get PWM interrupt status, raw.
Definition: pwm.h:550