Skip to content

Using PWM

Pico 2 PWM overview can be found in the datasheet https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf on page 1077.

PWM overview Illustration from the rp2350 datasheet

All GPIO can be configured to connect to PWM. The table on page 1078 in the datasheet has the details. PWM GPIO Mapping Table from the rp2350 datasheet

There are 12 'slices', which each drive 2 outputs: A and B. The PWM period is controlled by the wrap value for a slice. This means that output A and B for a slice will have the same wrap value. The duty cycle is controlled by the level value for the outputs, which are separate for A and B. This is illustrated in the following figure from the datasheet, where 'TOP' is the wrap value and 'TOP/3' is the level value:
PWM counter example
Illustration from the rp2350 datasheet

The PWM frequency will be the system clock divided by the value of the Fractional Clock Divider divided by the PWM period. The RP2350 system clock is 150 MHz.

First, add hardware_pwm library to the CMakeList.txt for PWM support.

target_link_libraries(my_project pico_stdlib hardware_pwm)

Now the steps are: - Configure GPIO pin(s) for PWM. - Set the period and duty cycle. - Configure the clock divider. - Start and stop PWM using the pwm_set_enabled function.

    gpio_set_function(8, GPIO_FUNC_PWM); // set GPIO 8 to PWM - this is connected to slice 4A
    gpio_set_function(9, GPIO_FUNC_PWM); // set GPIO 9 to PWM - this is connected to slice 4B
    uint8_t slice_num = pwm_gpio_to_slice_num(9); // find the 'slice' to configure

    // Set period of 99 cycles (0 to 99 inclusive)
    pwm_set_wrap(slice_num, 99);

    // Set channel A output high for 10 cycles before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_A, 9);

    // Set initial B output high for 50 cycles before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_B, 49);

    // divide the system clock (150MhZ) with 150
    pwm_set_clkdiv(slice_num, 150.f);

    // Set the PWM running
    pwm_set_enabled(slice_num, true);

The level controlling the duty cycle can be set by the pwm_set_gpio_level(uint gpio, uint16_t level)function. If set to 0, the output is always low, if set to wrap + 1, the output is always high.

Exercise

  • Add functions to increase and decrease the duty cycle.
  • Make sure to limit the level to be between 0 and wrap + 1.
  • Call the increase and decrease functions from a UART interrupt, to make it possible to control the duty cycle from a terminal.
  • Measure with oscilloscope.