Merge pull request #2410 from eZioPan/waveform-on-CHx
impl waveform with TIM OC Channel DMA
This commit is contained in:
commit
7e02389995
5 changed files with 133 additions and 26 deletions
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue