impl waveform with TIM Channel

This commit is contained in:
eZio Pan 2024-01-06 22:22:38 +08:00
parent 294046cddb
commit 424ddaf3d9
4 changed files with 119 additions and 12 deletions
embassy-stm32
examples/stm32f4/src/bin

View file

@ -1009,6 +1009,10 @@ fn main() {
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
(("timer", "UP"), quote!(crate::timer::UpDma)),
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
(("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
]
.into();
@ -1024,16 +1028,6 @@ fn main() {
}
if let Some(tr) = signals.get(&(regs.kind, ch.signal)) {
// TIM6 of stm32f334 is special, DMA channel for TIM6 depending on SYSCFG state
if chip_name.starts_with("stm32f334") && p.name == "TIM6" {
continue;
}
// TIM6 of stm32f378 is special, DMA channel for TIM6 depending on SYSCFG state
if chip_name.starts_with("stm32f378") && p.name == "TIM6" {
continue;
}
let peri = format_ident!("{}", p.name);
let channel = if let Some(channel) = &ch.channel {

View file

@ -311,6 +311,26 @@ pub(crate) mod sealed {
.ccmr_output(channel_index / 2)
.modify(|w| w.set_ocpe(channel_index % 2, preload));
}
/// Get capture compare DMA selection
fn get_cc_dma_selection(&self) -> super::vals::Ccds {
Self::regs_gp16().cr2().read().ccds()
}
/// Set capture compare DMA selection
fn set_cc_dma_selection(&mut self, ccds: super::vals::Ccds) {
Self::regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
}
/// Get capture compare DMA enable state
fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {
Self::regs_gp16().dier().read().ccde(channel.index())
}
/// Set capture compare DMA enable state
fn set_cc_dma_enable_state(&mut self, channel: Channel, ccde: bool) {
Self::regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
}
}
/// Capture/Compare 16-bit timer instance with complementary pin support.
@ -705,3 +725,8 @@ foreach_interrupt! {
// Update Event trigger DMA for every timer
dma_trait!(UpDma, Basic16bitInstance);
dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
dma_trait!(Ch3Dma, CaptureCompare16bitInstance);
dma_trait!(Ch4Dma, CaptureCompare16bitInstance);

View file

@ -155,7 +155,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
///
/// Note:
/// you will need to provide corresponding TIMx_UP DMA channel to use this method.
pub async fn gen_waveform(
pub async fn waveform_up(
&mut self,
dma: impl Peripheral<P = impl super::UpDma<T>>,
channel: Channel,
@ -221,6 +221,94 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
}
}
macro_rules! impl_waveform_chx {
($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
/// Generate a sequence of PWM waveform
///
/// Note:
/// you will need to provide corresponding TIMx_CHy DMA channel to use this method.
pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
use super::vals::Ccds;
assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
into_ref!(dma);
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();
let cc_channel = super::Channel::$cc_ch;
let original_duty_state = self.get_duty(cc_channel);
let original_enable_state = self.is_enabled(cc_channel);
let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
if original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ONUPDATE)
}
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, true);
}
if !original_enable_state {
self.enable(cc_channel);
}
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::Incr8,
..Default::default()
};
Transfer::new_write(
&mut dma,
req,
duty,
T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
dma_transfer_option,
)
.await
};
// restore output compare state
if !original_enable_state {
self.disable(cc_channel);
}
self.set_duty(cc_channel, original_duty_state);
// Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
//
// optional TODO:
// clean FEIF after disable UDE
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, false);
}
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ONCOMPARE)
}
}
}
};
}
impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
type Channel = Channel;
type Time = Hertz;

View file

@ -91,7 +91,7 @@ async fn main(_spawner: Spawner) {
loop {
for &color in color_list {
// with &mut, we can easily reuse same DMA channel multiple times
ws2812_pwm.gen_waveform(&mut dp.DMA1_CH2, pwm_channel, color).await;
ws2812_pwm.waveform_up(&mut dp.DMA1_CH2, pwm_channel, color).await;
// ws2812 need at least 50 us low level input to confirm the input data and change it's state
Timer::after_micros(50).await;
// wait until ticker tick