| nRF5 SDK v12.0.0 |

Choose documentation: | nRF5 SDK | S130 SoftDevice API | S132 SoftDevice API | S212 SoftDevice API | S332 SoftDevice API

All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
PWM
This information applies to the nRF52 Series only.

The Pulse Width Modulation (PWM) module driver includes two layers: the hardware access layer (HAL) and the driver layer (DRV).

The hardware access layer provides basic APIs for accessing the registers of the PWM peripheral. See the API documentation for the PWM HAL for details.

The driver layer provides APIs on a higher level than the HAL. See the API documentation for the PWM driver for details.

Key features include:

- Multi-instance support
- Two kinds of playback:
  - Simple: a single sequence of duty cycle values
  - Complex: two concatenated sequences
- Sequences are played back a specified number of times
- Optionally, the whole playback can be repeated in a loop
- A user-defined event handler can be used to perform additional actions when a sequence or the whole playback is finished
- If no event handler is used, playbacks can be carried out without involving the CPU

The PWM Driver Example provides sample code that you can use to quickly get started.

# Driver configuration

The configurable parameters include:

- Pins to be used as outputs for PWM channels (up to 4 per instance)
- Base clock frequency and top counter value (these parameters jointly determine the PWM period and resolution)
- Mode of loading the sequence values from RAM and distributing them to compare registers:
  - Common: One value is used in all channels.
  - Grouped: One value is used in channels 0 and 1, and another one in channels 2 and 3.
  - Individual: Separate values are used in each channel.
  - Waveform: Similar to Individual mode, but only three channels are used. The fourth value is loaded into the pulse generator counter as its top value.
- Mode of advancing the active sequence (automatic or triggered via a task)

The default configuration for the PWM driver is located in `sdk_config.h`. This configuration is used when nrf_drv_pwm_init is called with a NULL pointer to the configuration structure.

Before it can be used for a particular instance of the peripheral, the driver must be explicitly enabled for this instance in `sdk_config.h`.

# Using the PWM driver

First, call nrf_drv_pwm_init to initialize and configure the driver for a given instance of the peripheral. See the nrf_drv_pwm_config_t structure documentation for possible configuration options.

For example:

uint32_t err_code;
nrf_drv_pwm_config_t const config0 =
{
.output_pins =
{
BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
},
.irq_priority = APP_IRQ_PRIORITY_LOW,
.base_clock = NRF_PWM_CLK_1MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = 1000,
.load_mode = NRF_PWM_LOAD_COMMON,
.step_mode = NRF_PWM_STEP_AUTO
};
err_code = nrf_drv_pwm_init(&m_pwm0, &config0, NULL);
if (err_code != NRF_SUCCESS)
{
// Initialization failed. Take recovery action.
}

Next, define a sequence of duty cycle values using the nrf_pwm_sequence_t structure. For example:

static nrf_pwm_values_common_t seq_values[] =
{
0, 200, 400, 600, 800, 1000
};
nrf_pwm_sequence_t const seq =
{
.values.p_common = seq_values,
.length = NRF_PWM_VALUES_LENGTH(seq_values),
.repeats = 0,
.end_delay = 0
};

Start the playback of the sequence by calling nrf_drv_pwm_simple_playback.

You can play the sequence several times (for example, 3 times):

nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 3, 0);

Additionally, the playback can be repeated in a loop:

nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 3, NRF_DRV_PWM_FLAG_LOOP);

If a more complicated pattern of duty cycles is required that involves two sequence definitions, use nrf_drv_pwm_complex_playback.

If a sequence must be updated during the playback, define a proper event handler and pass it as parameter in the call to nrf_drv_pwm_init. By default, the handler is called at the end of the playback (or right before the next iteration in a loop), but it can be configured to be called when a sequence is completely loaded.

For example:

static void event_handler(nrf_drv_pwm_evt_type_t event_type)
{
if (event_type == NRF_DRV_PWM_EVT_END_SEQ0)
{
// Update sequence 0.
/* ... */
}
else if (event_type == NRF_DRV_PWM_EVT_END_SEQ1)
{
// Update sequence 1.
/* ... */
}
}
/* ... */
err_code = nrf_drv_pwm_init(&m_pwm0, &config0, event_handler);
/* ... */
nrf_drv_pwm_complex_playback(&m_pwm0, &seq0, &seq1, 1,
NRF_DRV_PWM_FLAG_LOOP |
NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0 |
NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1 |
NRF_DRV_PWM_FLAG_NO_EVT_FINISHED);

Entries in the array of duty cycle values can be modified directly, since the peripheral loads the values as the sequence is played back. If the whole sequence definition must be changed, use nrf_drv_pwm_sequence_update. If only some part of the definition must be modified, you can use one of the other updating functions.

At any time, the playback can be stopped by calling nrf_drv_pwm_stop. The pulse generation will stop at the end of the current PWM period.

When the PWM driver is no longer needed or its configuration must be changed, call nrf_drv_pwm_uninit.