mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
ask a DMA Channel only when use .gen_waveform()
This commit is contained in:
parent
f5a218a018
commit
c276da5fcb
@ -55,19 +55,12 @@ channel_impl!(new_ch3, Ch3, Channel3Pin);
|
|||||||
channel_impl!(new_ch4, Ch4, Channel4Pin);
|
channel_impl!(new_ch4, Ch4, Channel4Pin);
|
||||||
|
|
||||||
/// Simple PWM driver.
|
/// Simple PWM driver.
|
||||||
pub struct SimplePwm<'d, T, Dma> {
|
pub struct SimplePwm<'d, T> {
|
||||||
inner: PeripheralRef<'d, T>,
|
inner: PeripheralRef<'d, T>,
|
||||||
dma: PeripheralRef<'d, Dma>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> {
|
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||||
/// Create a new simple PWM driver.
|
/// Create a new simple PWM driver.
|
||||||
///
|
|
||||||
/// Note:
|
|
||||||
/// If you want to use [`Self::gen_waveform()`], you need to provide corresponding TIMx_UP DMA channel.
|
|
||||||
/// Otherwise you can just put a [`dma::NoDma`](crate::dma::NoDma)
|
|
||||||
/// Currently, you can only use one channel at a time to generate waveform with [`Self::gen_waveform()`].
|
|
||||||
/// But you can always use multiple TIM to generate multiple waveform simultaneously.
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tim: impl Peripheral<P = T> + 'd,
|
tim: impl Peripheral<P = T> + 'd,
|
||||||
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
||||||
@ -76,22 +69,16 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> {
|
|||||||
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
counting_mode: CountingMode,
|
counting_mode: CountingMode,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_inner(tim, freq, counting_mode, dma)
|
Self::new_inner(tim, freq, counting_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_inner(
|
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
|
||||||
tim: impl Peripheral<P = T> + 'd,
|
into_ref!(tim);
|
||||||
freq: Hertz,
|
|
||||||
counting_mode: CountingMode,
|
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
|
||||||
) -> Self {
|
|
||||||
into_ref!(tim, dma);
|
|
||||||
|
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
|
|
||||||
let mut this = Self { inner: tim, dma };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.inner.set_counting_mode(counting_mode);
|
this.inner.set_counting_mode(counting_mode);
|
||||||
this.set_frequency(freq);
|
this.set_frequency(freq);
|
||||||
@ -165,32 +152,23 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance, Dma> SimplePwm<'d, T, Dma>
|
impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance> SimplePwm<'d, T> {
|
||||||
where
|
|
||||||
Dma: super::UpDma<T>,
|
|
||||||
{
|
|
||||||
/// Generate a sequence of PWM waveform
|
/// Generate a sequence of PWM waveform
|
||||||
pub async fn gen_waveform(&mut self, channel: Channel, duty: &[u16]) {
|
///
|
||||||
|
/// Note:
|
||||||
|
/// you will need to provide corresponding TIMx_UP DMA channel to use this method.
|
||||||
|
pub async fn gen_waveform(
|
||||||
|
&mut self,
|
||||||
|
dma: impl Peripheral<P = impl super::UpDma<T>>,
|
||||||
|
channel: Channel,
|
||||||
|
duty: &[u16],
|
||||||
|
) {
|
||||||
assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
|
assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
|
||||||
|
|
||||||
#[cfg_attr(any(stm32f334, stm32f378), allow(clippy::let_unit_value))]
|
into_ref!(dma);
|
||||||
let req = self.dma.request();
|
|
||||||
|
|
||||||
#[cfg(not(any(bdma, gpdma)))]
|
#[allow(clippy::let_unit_value)] // eg. stm32f334
|
||||||
let (isr_bit, isr_reg, ifcr_reg) = {
|
let req = dma.request();
|
||||||
let dma_regs = self.dma.regs();
|
|
||||||
let isr_num = self.dma.num() / 4;
|
|
||||||
let isr_bit = self.dma.num() % 4;
|
|
||||||
let isr_reg = dma_regs.isr(isr_num);
|
|
||||||
let ifcr_reg = dma_regs.ifcr(isr_num);
|
|
||||||
(isr_bit, isr_reg, ifcr_reg)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(any(bdma, gpdma)))]
|
|
||||||
// clean DMA FIFO error before a transfer
|
|
||||||
if isr_reg.read().feif(isr_bit) {
|
|
||||||
ifcr_reg.write(|v| v.set_feif(isr_bit, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
let original_duty_state = self.get_duty(channel);
|
let original_duty_state = self.get_duty(channel);
|
||||||
let original_enable_state = self.is_enabled(channel);
|
let original_enable_state = self.is_enabled(channel);
|
||||||
@ -218,7 +196,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Transfer::new_write(
|
Transfer::new_write(
|
||||||
&mut self.dma,
|
&mut dma,
|
||||||
req,
|
req,
|
||||||
duty,
|
duty,
|
||||||
T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _,
|
T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _,
|
||||||
@ -231,21 +209,21 @@ where
|
|||||||
if !original_enable_state {
|
if !original_enable_state {
|
||||||
self.disable(channel);
|
self.disable(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_duty(channel, original_duty_state);
|
self.set_duty(channel, original_duty_state);
|
||||||
|
|
||||||
|
// Since DMA is closed before timer update event trigger DMA is turn off,
|
||||||
|
// this can almost always trigger a DMA FIFO error.
|
||||||
|
//
|
||||||
|
// optional TODO:
|
||||||
|
// clean FEIF after disable UDE
|
||||||
if !original_update_dma_state {
|
if !original_update_dma_state {
|
||||||
self.inner.enable_update_dma(false);
|
self.inner.enable_update_dma(false);
|
||||||
|
|
||||||
#[cfg(not(any(bdma, gpdma)))]
|
|
||||||
// Since DMA could be closed before timer update event trigger DMA is turn off, this can almost always trigger a DMA FIFO error.
|
|
||||||
// Thus, we will try clean DMA FEIF after each transfer
|
|
||||||
if isr_reg.read().feif(isr_bit) {
|
|
||||||
ifcr_reg.write(|v| v.set_feif(isr_bit, true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: CaptureCompare16bitInstance, Dma> embedded_hal_02::Pwm for SimplePwm<'d, T, Dma> {
|
impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
|
||||||
type Channel = Channel;
|
type Channel = Channel;
|
||||||
type Time = Hertz;
|
type Time = Hertz;
|
||||||
type Duty = u16;
|
type Duty = u16;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dma;
|
|
||||||
use embassy_stm32::gpio::OutputType;
|
use embassy_stm32::gpio::OutputType;
|
||||||
use embassy_stm32::time::khz;
|
use embassy_stm32::time::khz;
|
||||||
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||||
@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
|
let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
|
||||||
let mut pwm = SimplePwm::new(
|
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default());
|
||||||
p.TIM1,
|
|
||||||
Some(ch1),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
khz(10),
|
|
||||||
Default::default(),
|
|
||||||
dma::NoDma,
|
|
||||||
);
|
|
||||||
let max = pwm.get_max_duty();
|
let max = pwm.get_max_duty();
|
||||||
pwm.enable(Channel::Ch1);
|
pwm.enable(Channel::Ch1);
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
device_config.rcc.sys = Sysclk::PLL1_P;
|
device_config.rcc.sys = Sysclk::PLL1_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dp = embassy_stm32::init(device_config);
|
let mut dp = embassy_stm32::init(device_config);
|
||||||
|
|
||||||
let mut ws2812_pwm = SimplePwm::new(
|
let mut ws2812_pwm = SimplePwm::new(
|
||||||
dp.TIM3,
|
dp.TIM3,
|
||||||
@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
None,
|
None,
|
||||||
khz(800), // data rate of ws2812
|
khz(800), // data rate of ws2812
|
||||||
CountingMode::EdgeAlignedUp,
|
CountingMode::EdgeAlignedUp,
|
||||||
dp.DMA1_CH2,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// construct ws2812 non-return-to-zero (NRZ) code bit by bit
|
// construct ws2812 non-return-to-zero (NRZ) code bit by bit
|
||||||
@ -91,7 +90,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
for &color in color_list {
|
for &color in color_list {
|
||||||
ws2812_pwm.gen_waveform(pwm_channel, color).await;
|
// with &mut, we can easily reuse same DMA channel multiple times
|
||||||
|
ws2812_pwm.gen_waveform(&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
|
// ws2812 need at least 50 us low level input to confirm the input data and change it's state
|
||||||
Timer::after_micros(50).await;
|
Timer::after_micros(50).await;
|
||||||
// wait until ticker tick
|
// wait until ticker tick
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dma;
|
|
||||||
use embassy_stm32::gpio::OutputType;
|
use embassy_stm32::gpio::OutputType;
|
||||||
use embassy_stm32::time::khz;
|
use embassy_stm32::time::khz;
|
||||||
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||||
@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull);
|
let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull);
|
||||||
let mut pwm = SimplePwm::new(
|
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default());
|
||||||
p.TIM1,
|
|
||||||
Some(ch1),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
khz(10),
|
|
||||||
Default::default(),
|
|
||||||
dma::NoDma,
|
|
||||||
);
|
|
||||||
let max = pwm.get_max_duty();
|
let max = pwm.get_max_duty();
|
||||||
pwm.enable(Channel::Ch1);
|
pwm.enable(Channel::Ch1);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use embassy_stm32::gpio::OutputType;
|
|||||||
use embassy_stm32::time::khz;
|
use embassy_stm32::time::khz;
|
||||||
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||||
use embassy_stm32::timer::Channel;
|
use embassy_stm32::timer::Channel;
|
||||||
use embassy_stm32::{dma, Config};
|
use embassy_stm32::Config;
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -38,16 +38,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull);
|
let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull);
|
||||||
let mut pwm = SimplePwm::new(
|
let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default());
|
||||||
p.TIM3,
|
|
||||||
Some(ch1),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
khz(10),
|
|
||||||
Default::default(),
|
|
||||||
dma::NoDma,
|
|
||||||
);
|
|
||||||
let max = pwm.get_max_duty();
|
let max = pwm.get_max_duty();
|
||||||
pwm.enable(Channel::Ch1);
|
pwm.enable(Channel::Ch1);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user