The pulse-width modulation (PWM) library provides functions to generate a pulse-width modulated output signal.
Key features include:
- Up to three independent PWM instances.
- Two PWM output channels per instance.
- Minimal CPU usage.
- Configurable per-channel polarity.
Resource usage
Users can create multiple instances of the library. The PWM library allocates a timer instance (see Timer) that is not available to the user. Therefore, the number of independent PWM instances depends on the number of hardware timers.
Resource usage:
nRF51 Series only:
- Two PPI channels per PWM instance and two PPI channels per PWM channel.
- One PPI group per instance.
- One GPIOTE channel per PWM channel.
nRF52 Series only:
- One PPI channel per PWM instance and two PPI channels per PWM channel.
- One GPIOTE channel per PWM channel.
nRF51 Series only: For example, a PWM instance with two channels will consume 2+4 PPI channels, 1 PPI group, and 2 GPIOTE channels. nRF52 Series only: For example, a PWM instance with two channels will consume 1+4 PPI channels and 2 GPIOTE channels.
The PPI and GPIOTE channels are allocated by the PPI and GPIOTE drivers.
Generating a low-power PWM signal
Complete the following steps to generate a PWM signal:
- Create one or more PWM instances by calling the APP_PWM_INSTANCE macro. Note that each instance will utilize a hardware timer, which will be unavailable for other uses. Changing the timer parameters during PWM operation will result in undefined behavior.
The following code shows how to create a new PWM instance:
- For each instance, create an app_pwm_config_t structure with the default or custom configuration. If you want to use the default configuration, call the APP_PWM_DEFAULT_CONFIG_1CH or APP_PWM_DEFAULT_CONFIG_2CH macro to fill the configuration structure.
The following parameters must be specified:
- pins: Array of two unsigned integers that indicate which physical pins will be used for the PWM output. In one-channel mode, the second element is ignored.
- pin_polarity: 2-element array of app_pwm_polarity_t that indicates the output signal polarity. In one-channel mode, the second element is ignored.
- num_of_channels: Number of PWM channels (1 or 2).
- period_us: Signal period (in microseconds).
- Provide a callback function (see app_pwm_callback_t) that will be executed when the PWM state changes from BUSY to READY. (Also see the Limitations section.)
- Initialize each instance using app_pwm_init, with pointers to the instance, the configuration structure, and the callback function as parameters. The function will return NRF_SUCCESS on success or an error code on failure. Initialization will fail if the instance was already initialized, if an invalid configuration was provided, or if there were not enough free resources.
- Call app_pwm_enable to enable a given instance.
- Call app_pwm_channel_duty_set to set the duty cycle for a given instance.
A PWM signal should now appear on the output pin. No CPU time is used during operation.
Limitations
nRF51 Series only:
- A duty cycle change takes up to two full PWM cycles. Therefore, during the duty change process, no changes can be performed on the second channel, and app_pwm_channel_duty_set returns NRF_ERROR_BUSY. After the change is completed, the user callback function is called, if provided.
- PWM frequency is limited to a maximum of 200 kHz (5 microseconds period). Exceeding this value might introduce glitches in the output signal.
nRF52 Series only:
- When setting a new duty, a duty cycle change takes up to one full PWM cycle if both of these conditions are met:
- The current duty is neither at zero, nor at the maximum value.
- The new duty is smaller than the current one.
In such case, during the duty change process, no changes can be performed on the second channel, and app_pwm_channel_duty_set returns NRF_ERROR_BUSY. After the change is completed, the user callback function is called, if provided.
- In all other cases, the duty cycle change will happen immediately.
- PWM frequency is limited to a maximum of 200 kHz (5 microseconds period). Exceeding this value might introduce glitches in the output signal.
Example
See the following code for a usage example. Note that this example is not power optimized.
void pwm_ready_callback(uint32_t pwm_id)
{
}
err_code =
app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
uint32_t value;
while(true)
{
for (uint8_t i = 0; i < 40; ++i)
{
value = (i < 20) ? (i * 5) : (100 - (i - 20) * 5);
nrf_delay_ms(25);
}
}
See the PWM Library Example for a full application that uses the PWM library.
Usage with a SoftDevice
The PWM library can be used with a SoftDevice. However, there are some limitations:
- TIMER0 is reserved for the SoftDevice and not available to the user. Attempting to initialize an instance using TIMER0 will result in a hard fault due to memory access violation.
- The SoftDevice can interrupt the application at any time for a certain amount of time. In this case, the duty cycle change might take more than one or two PWM timer cycles.