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
15extern "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
57enum pwm_chan
58{
59 PWM_CHAN_A = 0,
60 PWM_CHAN_B = 1
61};
62
63typedef struct {
64 uint32_t csr;
65 uint32_t div;
66 uint32_t top;
68
69static inline void check_slice_num_param(__unused uint slice_num) {
70 valid_params_if(PWM, slice_num < NUM_PWM_SLICES);
71}
72
78static 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
90static inline uint pwm_gpio_to_channel(uint gpio) {
91 valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
92 return gpio & 1u;
93}
94
104static 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
119static inline void pwm_config_set_clkdiv(pwm_config *c, float div) {
120 c->div = (uint32_t)(div * (float)(1u << PWM_CH0_DIV_INT_LSB));
121}
122
133static inline void pwm_config_set_clkdiv_int(pwm_config *c, uint div) {
134 c->div = div << PWM_CH0_DIV_INT_LSB;
135}
136
147static inline void pwm_config_set_clkdiv_mode(pwm_config *c, enum pwm_clkdiv_mode mode) {
148 valid_params_if(PWM, mode == PWM_DIV_FREE_RUNNING ||
149 mode == PWM_DIV_B_RISING ||
150 mode == PWM_DIV_B_HIGH ||
151 mode == PWM_DIV_B_FALLING);
152 c->csr = (c->csr & ~PWM_CH0_CSR_DIVMODE_BITS)
153 | (((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB);
154}
155
163static inline void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b) {
164 c->csr = (c->csr & ~(PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS))
165 | ((bool_to_bit(a) << PWM_CH0_CSR_A_INV_LSB) | (bool_to_bit(b) << PWM_CH0_CSR_B_INV_LSB));
166}
167
176static inline void pwm_config_set_wrap(pwm_config *c, uint16_t wrap) {
177 c->top = wrap;
178}
179
191static inline void pwm_init(uint slice_num, pwm_config *c, bool start) {
192 check_slice_num_param(slice_num);
193 pwm_hw->slice[slice_num].csr = 0;
194
195 pwm_hw->slice[slice_num].ctr = PWM_CH0_CTR_RESET;
196 pwm_hw->slice[slice_num].cc = PWM_CH0_CC_RESET;
197 pwm_hw->slice[slice_num].top = c->top;
198 pwm_hw->slice[slice_num].div = c->div;
199 pwm_hw->slice[slice_num].csr = c->csr | (bool_to_bit(start) << PWM_CH0_CSR_EN_LSB);
200}
201
211 pwm_config c = {0, 0, 0};
215 pwm_config_set_output_polarity(&c, false, false);
216 pwm_config_set_wrap(&c, 0xffffu);
217 return c;
218}
219
235static inline void pwm_set_wrap(uint slice_num, uint16_t wrap) {
236 check_slice_num_param(slice_num);
237 pwm_hw->slice[slice_num].top = wrap;
238}
239
255static inline void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level) {
256 check_slice_num_param(slice_num);
258 &pwm_hw->slice[slice_num].cc,
259 ((uint)level) << (chan ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB),
260 chan ? PWM_CH0_CC_B_BITS : PWM_CH0_CC_A_BITS
261 );
262}
263
279static inline void pwm_set_both_levels(uint slice_num, uint16_t level_a, uint16_t level_b) {
280 check_slice_num_param(slice_num);
281 pwm_hw->slice[slice_num].cc = (((uint)level_b) << PWM_CH0_CC_B_LSB) | (((uint)level_a) << PWM_CH0_CC_A_LSB);
282}
283
302static inline void pwm_set_gpio_level(uint gpio, uint16_t level) {
303 valid_params_if(PWM, gpio < NUM_BANK0_GPIOS);
305}
306
315static inline uint16_t pwm_get_counter(uint slice_num) {
316 check_slice_num_param(slice_num);
317 return (uint16_t)(pwm_hw->slice[slice_num].ctr);
318}
319
329static inline void pwm_set_counter(uint slice_num, uint16_t c) {
330 check_slice_num_param(slice_num);
331 pwm_hw->slice[slice_num].ctr = c;
332}
333
343static inline void pwm_advance_count(uint slice_num) {
344 check_slice_num_param(slice_num);
345 hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_ADV_BITS);
346 while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_ADV_BITS) {
348 }
349}
350
360static inline void pwm_retard_count(uint slice_num) {
361 check_slice_num_param(slice_num);
362 hw_set_bits(&pwm_hw->slice[slice_num].csr, PWM_CH0_CSR_PH_RET_BITS);
363 while (pwm_hw->slice[slice_num].csr & PWM_CH0_CSR_PH_RET_BITS) {
365 }
366}
367
377static inline void pwm_set_clkdiv_int_frac(uint slice_num, uint8_t integer, uint8_t fract) {
378 check_slice_num_param(slice_num);
379 valid_params_if(PWM, fract < 16);
380 pwm_hw->slice[slice_num].div = (((uint)integer) << PWM_CH0_DIV_INT_LSB) | (((uint)fract) << PWM_CH0_DIV_FRAC_LSB);
381}
382
391static inline void pwm_set_clkdiv(uint slice_num, float divider) {
392 check_slice_num_param(slice_num);
393 valid_params_if(PWM, divider >= 1.f && divider < 256.f);
394 uint8_t i = (uint8_t)divider;
395 uint8_t f = (uint8_t)((divider - i) * (0x01 << 4));
396 pwm_set_clkdiv_int_frac(slice_num, i, f);
397}
398
406static inline void pwm_set_output_polarity(uint slice_num, bool a, bool b) {
407 check_slice_num_param(slice_num);
408 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,
409 PWM_CH0_CSR_A_INV_BITS | PWM_CH0_CSR_B_INV_BITS);
410}
411
412
419static inline void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode) {
420 check_slice_num_param(slice_num);
421 valid_params_if(PWM, mode == PWM_DIV_FREE_RUNNING ||
422 mode == PWM_DIV_B_RISING ||
423 mode == PWM_DIV_B_HIGH ||
424 mode == PWM_DIV_B_FALLING);
425 hw_write_masked(&pwm_hw->slice[slice_num].csr, ((uint)mode) << PWM_CH0_CSR_DIVMODE_LSB, PWM_CH0_CSR_DIVMODE_BITS);
426}
427
437static inline void pwm_set_phase_correct(uint slice_num, bool phase_correct) {
438 check_slice_num_param(slice_num);
439 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);
440}
441
468static inline void pwm_set_enabled(uint slice_num, bool enabled) {
469 check_slice_num_param(slice_num);
470 hw_write_masked(&pwm_hw->slice[slice_num].csr, bool_to_bit(enabled) << PWM_CH0_CSR_EN_LSB, PWM_CH0_CSR_EN_BITS);
471}
472
478static inline void pwm_set_mask_enabled(uint32_t mask) {
479 pwm_hw->en = mask;
480}
481
490static inline void pwm_set_irq_enabled(uint slice_num, bool enabled) {
491 check_slice_num_param(slice_num);
492 if (enabled) {
493 hw_set_bits(&pwm_hw->inte, 1u << slice_num);
494 } else {
495 hw_clear_bits(&pwm_hw->inte, 1u << slice_num);
496 }
497}
498
507static inline void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled) {
508 valid_params_if(PWM, slice_mask < 256);
509 if (enabled) {
510 hw_set_bits(&pwm_hw->inte, slice_mask);
511 } else {
512 hw_clear_bits(&pwm_hw->inte, slice_mask);
513 }
514}
515
521static inline void pwm_clear_irq(uint slice_num) {
522 pwm_hw->intr = 1u << slice_num;
523}
524
530static inline uint32_t pwm_get_irq_status_mask(void) {
531 return pwm_hw->ints;
532}
533
539static inline void pwm_force_irq(uint slice_num) {
540 pwm_hw->intf = 1u << slice_num;
541}
542
548static inline uint pwm_get_dreq(uint slice_num) {
549 static_assert(DREQ_PWM_WRAP1 == DREQ_PWM_WRAP0 + 1, "");
550 static_assert(DREQ_PWM_WRAP7 == DREQ_PWM_WRAP0 + 7, "");
551 check_slice_num_param(slice_num);
552 return DREQ_PWM_WRAP0 + slice_num;
553}
554
555#ifdef __cplusplus
556}
557#endif
558
559#endif
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
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
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
static void pwm_init(uint slice_num, pwm_config *c, bool start)
Initialise a PWM with settings from a configuration object.
Definition: pwm.h:191
static void pwm_config_set_phase_correct(pwm_config *c, bool phase_correct)
Set phase correction in a PWM configuration.
Definition: pwm.h:104
static void pwm_set_irq_enabled(uint slice_num, bool enabled)
Enable PWM instance interrupt.
Definition: pwm.h:490
pwm_clkdiv_mode
PWM Divider mode settings.
Definition: pwm.h:50
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:377
static void pwm_retard_count(uint slice_num)
Retard PWM count.
Definition: pwm.h:360
static uint16_t pwm_get_counter(uint slice_num)
Get PWM counter.
Definition: pwm.h:315
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:255
static void pwm_config_set_clkdiv_int(pwm_config *c, uint div)
Set PWM clock divider in a PWM configuration.
Definition: pwm.h:133
static void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode)
Set PWM divider mode.
Definition: pwm.h:419
static void pwm_set_enabled(uint slice_num, bool enabled)
Enable/Disable PWM.
Definition: pwm.h:468
static void pwm_set_mask_enabled(uint32_t mask)
Enable/Disable multiple PWM slices simultaneously.
Definition: pwm.h:478
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:302
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:147
static uint pwm_get_dreq(uint slice_num)
Return the DREQ to use for pacing transfers to a particular PWM slice.
Definition: pwm.h:548
static void pwm_force_irq(uint slice_num)
Force PWM interrupt.
Definition: pwm.h:539
static void pwm_set_counter(uint slice_num, uint16_t c)
Set PWM counter.
Definition: pwm.h:329
static uint32_t pwm_get_irq_status_mask(void)
Get PWM interrupt status, raw.
Definition: pwm.h:530
static void pwm_config_set_clkdiv(pwm_config *c, float div)
Set clock divider in a PWM configuration.
Definition: pwm.h:119
static void pwm_set_wrap(uint slice_num, uint16_t wrap)
Set the current PWM counter wrap value.
Definition: pwm.h:235
static uint pwm_gpio_to_slice_num(uint gpio)
Determine the PWM slice that is attached to the specified GPIO.
Definition: pwm.h:78
static void pwm_clear_irq(uint slice_num)
Clear a single PWM channel interrupt.
Definition: pwm.h:521
static void pwm_set_clkdiv(uint slice_num, float divider)
Set PWM clock divider.
Definition: pwm.h:391
static uint pwm_gpio_to_channel(uint gpio)
Determine the PWM channel that is attached to the specified GPIO.
Definition: pwm.h:90
static void pwm_advance_count(uint slice_num)
Advance PWM count.
Definition: pwm.h:343
static void pwm_set_output_polarity(uint slice_num, bool a, bool b)
Set PWM output polarity.
Definition: pwm.h:406
static void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b)
Set output polarity in a PWM configuration.
Definition: pwm.h:163
static void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled)
Enable multiple PWM instance interrupts.
Definition: pwm.h:507
static void pwm_set_phase_correct(uint slice_num, bool phase_correct)
Set PWM phase correct on/off.
Definition: pwm.h:437
static void pwm_config_set_wrap(pwm_config *c, uint16_t wrap)
Set PWM counter wrap value in a PWM configuration.
Definition: pwm.h:176
static pwm_config pwm_get_default_config(void)
Get a set of default values for PWM configuration.
Definition: pwm.h:210
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:279
@ PWM_DIV_B_FALLING
Fractional divider advances with each falling edge of the PWM B pin.
Definition: pwm.h:54
@ PWM_DIV_B_HIGH
Fractional divider is gated by the PWM B pin.
Definition: pwm.h:52
@ PWM_DIV_FREE_RUNNING
Free-running counting at rate dictated by fractional divider.
Definition: pwm.h:51
@ PWM_DIV_B_RISING
Fractional divider advances with each rising edge of the PWM B pin.
Definition: pwm.h:53
static __always_inline void tight_loop_contents(void)
No-op function for the body of tight loops.
Definition: platform.h:347
Definition: pwm.h:63