Introduction
When using PWM to generate signals (e.g. sine waves or sound), it is useful to use DMA to update the duty cycle on the fly without occupying the CPU (example). For this, it is required to enable DMA request generation on the timer/PWM peripheral.
Problem description
The PWM peripheral can be used to generate complex signals such as sine waves or sounds from .wav files. To achieve this, the peripheral needs to be in PWM output mode and the duty cycle needs to change once per period, also depending on the signal. The best way to achieve this without overloading the CPU is to use DMA requests triggered by the end of the PWM cycle.
On STM32 microcontrollers, this cannot be done with the current PWM and DMA APIs. STM32 microcontrollers have a TIMx_DIER with CCyDE fields which need to be set to enable the triggering of DMA requests. Other microcontrollers such as the nrf54L, Microchip PIC32CM, and TI CC13x2, CC26x2 also need some kind of subscription/enable to allow these DMA requests to happen.
Proposed change
Add optional functions to the PWM API which allow to enable and disable DMA requests triggered by a given PWM channel.
Detailed RFC
Proposed change (Detailed)
I propose to add a Kconfig parameter PWM_WITH_DMA which extends the Zephyr PWM API with the ability to trigger DMA requests from PWM channels. When enabled, this provides two functions: pwm_enable_dma(const struct device *dev, uint32_t channel) and pwm_disable_dma(const struct device *dev, uint32_t channel).
For STM32, the pwm_enable_dma function sets the appropriate CCyDE field of the TIMx_DIER register where y is the given channel and x can be derived from the given device. The pwm_disable_dma function clears this field. On STM32 timers, these fields can be set for channels that support Input Capture/Output Compare. However, the DMA requests can be used for other purposes as mentioned above. Therefore, I propose the functions to support channels 1, 2, 3 and 4, as is the default case for the pwm_stm32_enable/disable_capture functions. I also believe the functions need to be independent of CONFIG_PWM_CAPTURE.
For other architectures/vendors, the functions should call the publish/subscribe API (nordic) or set the appropriate registers (Microchip, Texas Instruments). However, for some of them (e.g. Microchip), these registers are located on the DMA itself, not on the timer.
Dependencies
This change affects the PWM API. For some vendors, it might affect the DMA API.
Concerns and Unresolved Questions
On STM32, for the DMA request to be actually triggered by PWM cycles, the TIMx_DIER_CCyDE field needs to be set. For other vendors/architectures, I have only read documentation. I have not confirmed how it works there.
On STM32, there's a correlation between the Input Capture/Output Compare functionality and DMA for PWM, despite that DMA can be used for other purposes (as confirmed by their own TIM_DMA example).
Alternatives
The use case I have in mind is specifically focused on using DMA to change the duty cycle (TIMx_CCRy register). The alternative to adding enable and disable DMA functions, is to add a function which does both the DMA and PWM setup. For that, that function needs to take care of starting PWM in output mode on a given channel, configure a DMA request with given in-memory array of data (possibly using a struct like dma_config), enable DMA request triggering by the given PWM channel and starting the DMA.
Introduction
When using PWM to generate signals (e.g. sine waves or sound), it is useful to use DMA to update the duty cycle on the fly without occupying the CPU (example). For this, it is required to enable DMA request generation on the timer/PWM peripheral.
Problem description
The PWM peripheral can be used to generate complex signals such as sine waves or sounds from .wav files. To achieve this, the peripheral needs to be in PWM output mode and the duty cycle needs to change once per period, also depending on the signal. The best way to achieve this without overloading the CPU is to use DMA requests triggered by the end of the PWM cycle.
On STM32 microcontrollers, this cannot be done with the current PWM and DMA APIs. STM32 microcontrollers have a TIMx_DIER with CCyDE fields which need to be set to enable the triggering of DMA requests. Other microcontrollers such as the nrf54L, Microchip PIC32CM, and TI CC13x2, CC26x2 also need some kind of subscription/enable to allow these DMA requests to happen.
Proposed change
Add optional functions to the PWM API which allow to enable and disable DMA requests triggered by a given PWM channel.
Detailed RFC
Proposed change (Detailed)
I propose to add a Kconfig parameter
PWM_WITH_DMAwhich extends the Zephyr PWM API with the ability to trigger DMA requests from PWM channels. When enabled, this provides two functions:pwm_enable_dma(const struct device *dev, uint32_t channel)andpwm_disable_dma(const struct device *dev, uint32_t channel).For STM32, the
pwm_enable_dmafunction sets the appropriate CCyDE field of the TIMx_DIER register where y is the given channel and x can be derived from the given device. Thepwm_disable_dmafunction clears this field. On STM32 timers, these fields can be set for channels that support Input Capture/Output Compare. However, the DMA requests can be used for other purposes as mentioned above. Therefore, I propose the functions to support channels 1, 2, 3 and 4, as is the default case for thepwm_stm32_enable/disable_capturefunctions. I also believe the functions need to be independent ofCONFIG_PWM_CAPTURE.For other architectures/vendors, the functions should call the publish/subscribe API (nordic) or set the appropriate registers (Microchip, Texas Instruments). However, for some of them (e.g. Microchip), these registers are located on the DMA itself, not on the timer.
Dependencies
This change affects the PWM API. For some vendors, it might affect the DMA API.
Concerns and Unresolved Questions
On STM32, for the DMA request to be actually triggered by PWM cycles, the TIMx_DIER_CCyDE field needs to be set. For other vendors/architectures, I have only read documentation. I have not confirmed how it works there.
On STM32, there's a correlation between the Input Capture/Output Compare functionality and DMA for PWM, despite that DMA can be used for other purposes (as confirmed by their own TIM_DMA example).
Alternatives
The use case I have in mind is specifically focused on using DMA to change the duty cycle (TIMx_CCRy register). The alternative to adding enable and disable DMA functions, is to add a function which does both the DMA and PWM setup. For that, that function needs to take care of starting PWM in output mode on a given channel, configure a DMA request with given in-memory array of data (possibly using a struct like
dma_config), enable DMA request triggering by the given PWM channel and starting the DMA.