Merge pull request #2410 from eZioPan/waveform-on-CHx

impl waveform with TIM OC Channel DMA
This commit is contained in:
Dario Nieuwenhuis 2024-02-01 01:02:01 +00:00 committed by GitHub
commit 7e02389995
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 133 additions and 26 deletions

View file

@ -949,9 +949,9 @@ fn main() {
} else if pin.signal.starts_with("INN") {
// TODO handle in the future when embassy supports differential measurements
None
} else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") {
} else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') {
// we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap();
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap();
Some(32u8 + signal.parse::<u8>().unwrap())
} else if pin.signal.starts_with("IN") {
Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
@ -1022,6 +1022,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();
@ -1037,16 +1041,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 {
@ -1205,7 +1199,7 @@ fn main() {
ADC3 and higher are assigned to the adc34 clock in the table
The adc3_common cfg directive is added if ADC3_COMMON exists
*/
let has_adc3 = METADATA.peripherals.iter().find(|p| p.name == "ADC3_COMMON").is_some();
let has_adc3 = METADATA.peripherals.iter().any(|p| p.name == "ADC3_COMMON");
let set_adc345 = HashSet::from(["ADC3", "ADC4", "ADC5"]);
for m in METADATA
@ -1389,6 +1383,7 @@ fn main() {
// =======
// ADC3_COMMON is present
#[allow(clippy::print_literal)]
if has_adc3 {
println!("cargo:rustc-cfg={}", "adc3_common");
}

View file

@ -54,6 +54,7 @@ pub struct ComplementaryPwm<'d, T> {
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
/// Create a new complementary PWM driver.
#[allow(clippy::too_many_arguments)]
pub fn new(
tim: impl Peripheral<P = T> + 'd,
_ch1: Option<PwmPin<'d, T, Ch1>>,
@ -165,7 +166,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for C
}
fn get_period(&self) -> Self::Time {
self.inner.get_frequency().into()
self.inner.get_frequency()
}
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {

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.
@ -450,20 +470,17 @@ pub enum CountingMode {
impl CountingMode {
/// Return whether this mode is edge-aligned (up or down).
pub fn is_edge_aligned(&self) -> bool {
match self {
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
_ => false,
}
matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)
}
/// Return whether this mode is center-aligned.
pub fn is_center_aligned(&self) -> bool {
match self {
matches!(
self,
CountingMode::CenterAlignedDownInterrupts
| CountingMode::CenterAlignedUpInterrupts
| CountingMode::CenterAlignedBothInterrupts => true,
_ => false,
}
| CountingMode::CenterAlignedUpInterrupts
| CountingMode::CenterAlignedBothInterrupts
)
}
}
@ -705,3 +722,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

@ -160,7 +160,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,
@ -226,6 +226,95 @@ 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);
// redirect CC DMA request onto Update Event
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;
@ -240,7 +329,7 @@ impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d,
}
fn get_period(&self) -> Self::Time {
self.inner.get_frequency().into()
self.inner.get_frequency()
}
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {

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