Merge pull request #4232 from RaulIQ/main

[embassy-stm32] add PWM multi channel waveform generation using DMA burst mode
This commit is contained in:
Ulf Lilleengen 2025-05-27 06:25:33 +00:00 committed by GitHub
commit 5f3204f9c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -381,6 +381,89 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
self.inner.enable_update_dma(false); self.inner.enable_update_dma(false);
} }
} }
/// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
///
/// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
/// in sequence on each update event (UEV). The data is written via the DMAR register using the
/// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
///
/// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
/// represents a single update event and each column corresponds to a specific timer channel (starting
/// from `starting_channel` up to and including `ending_channel`).
///
/// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
///
/// let dma_buf: [u16; 16] = [
/// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
/// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
/// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
/// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
/// ];
///
/// Each group of N values (where N = number of channels) is transferred on one update event,
/// updating the duty cycles of all selected channels simultaneously.
///
/// Note:
/// you will need to provide corresponding TIMx_UP DMA channel to use this method.
pub async fn waveform_up_multi_channel(
&mut self,
dma: Peri<'_, impl super::UpDma<T>>,
starting_channel: Channel,
ending_channel: Channel,
duty: &[u16],
) {
let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32;
let start_ch_index = starting_channel.index();
let end_ch_index = ending_channel.index();
assert!(start_ch_index <= end_ch_index);
let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
self.inner
.regs_gp16()
.dcr()
.modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
self.inner
.regs_gp16()
.dcr()
.modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();
let original_update_dma_state = self.inner.get_update_dma_state();
if !original_update_dma_state {
self.inner.enable_update_dma(true);
}
unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};
let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Incr4,
..Default::default()
};
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
dma_transfer_option,
)
.await
};
if !original_update_dma_state {
self.inner.enable_update_dma(false);
}
}
} }
macro_rules! impl_waveform_chx { macro_rules! impl_waveform_chx {