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:
- 2 PPI channels per instance + 2 PPI channels per PWM channel.
- 1 PPI group per instance.
- 1 GPIOTE channel per PWM channel.
For example, a PWM instance with two channels will consume 2+4 PPI channels, 1 PPI group, and 2 GPIOTE channels.
The PPI and GPIOTE channels are allocated by the PPI and GPIOTE drivers.
Initialization and configuration
To use the PWM library, you must 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:
Configuration
After creating an instance, you must initialize it by providing an app_pwm_config_t structure with the default or custom configuration. See the APP_PWM_DEFAULT_CONFIG_1CH and APP_PWM_DEFAULT_CONFIG_2CH macros for default configurations for one or two channels, respectively.
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).
Event handler
You should 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.
Procedure
Complete the following steps to generate a PWM signal:
- Create and initialize a app_pwm_config_t structure as described in the Configuration section.
- Initialize an 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 the instance.
- Use app_pwm_channel_duty_set to set the duty cycle.
A PWM signal should now appear on the output pin. No CPU time is used during operation.
Limitations
- A duty cycle change takes up to two full PWM cycles (two short interrupt routine calls at the end of each cycle). 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 complete, the user callback function is called.
- PWM frequency is limited to 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)
{
}
pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_HIGH;
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);
}
}
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 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 two PWM timer cycles.