diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 1cf0ad72..04aaf108 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -1,6 +1,8 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::signal::Signal; use super::*; #[allow(unused_imports)] @@ -8,6 +10,28 @@ use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; +use crate::_generated::interrupt::typelevel::Interrupt; + +// Declare a signal to awake user code for signaling the update interrupt id happen +static SIGNAL_UPDATE: Signal = Signal::new(); + +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + let sr = regs.sr().read(); + if sr.uif() { + SIGNAL_UPDATE.signal(0); + // clear the flag + critical_section::with(|_| { + regs.sr().modify(|w| w.set_uif(false)); + }) + } + } +} pub struct Ch1; pub struct Ch2; @@ -82,6 +106,9 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); this.inner .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; this } @@ -106,6 +133,14 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { self.inner.get_max_compare_value() + 1 } + pub fn enable_update_interrupt(&mut self, enable: bool) { + self.inner.enable_update_interrupt(enable); + } + + pub async fn wait_update_interrupt(&self) { + _ = SIGNAL_UPDATE.wait().await; + } + pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) diff --git a/examples/stm32g0/Embed.toml b/examples/stm32g0/Embed.toml new file mode 100644 index 00000000..84775124 --- /dev/null +++ b/examples/stm32g0/Embed.toml @@ -0,0 +1,43 @@ +[default.probe] +protocol = "Swd" +# USB vendor ID +# usb_vid = "6790" +# USB product ID +# usb_pid = "29987" + +[default.flashing] +enabled = true + +[default.reset] +# Whether or not the target should be reset. +# When flashing is enabled as well, the target will be reset after flashing. +enabled = true +# Whether or not the target should be halted after reset. +halt_afterwards = false + +[default.general] +# The chip name of the chip to be debugged. +#chip = "STM32G030J6Mx" +chip = "STM32G070CBTx" +# A list of chip descriptions to be loaded during runtime. +chip_descriptions = [] +# The default log level to be used. +log_level = "Warn" + +[default.rtt] +enabled = false +# A list of channel associations to be displayed. If left empty, all channels are displayed. +channels = [ + # { up = 0, down = 0, name = "name" } +] +# The duration in ms for which the logger should retry to attach to RTT. +timeout = 3000 +# Whether timestamps in the RTTUI are enabled +show_timestamps = true + +[default.gdb] +# Whether or not a GDB server should be opened after flashing. +# This is exclusive and cannot be used with RTT at the moment. +enabled = false +# The connection string in host:port format wher the GDB server will open a socket. +# gdb_connection_string \ No newline at end of file diff --git a/examples/stm32g0/src/bin/pwm_interrupt_blinky.rs b/examples/stm32g0/src/bin/pwm_interrupt_blinky.rs new file mode 100644 index 00000000..9eaaa1d6 --- /dev/null +++ b/examples/stm32g0/src/bin/pwm_interrupt_blinky.rs @@ -0,0 +1,95 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, OutputType, Speed}; +use embassy_stm32::peripherals::PA5; +use embassy_stm32::time::Hertz; +use embassy_stm32::timer::simple_pwm::{InterruptHandler, PwmPin, SimplePwm}; +use embassy_stm32::timer::{self, Channel}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// This test is meant for the target nucleo G070 RB +// On arduino pin d4 (pb5) a pwm signal of about 0.3 hz can me measured. +// Attach a led and a resistor of 330 ohm in series to watch the pwm +// The user led arduino pin d13 (pa5) will flash with exactly 1 hrz. + +bind_interrupts!( + struct Irqs { + TIM3 => InterruptHandler; + } +); + +#[embassy_executor::task] +async fn pwm_task(mut pwm_test: PwmTest) { + pwm_test.task().await; +} + +pub struct PwmTest { + pwm3: SimplePwm<'static, peripherals::TIM3>, + led: Output<'static, PA5>, + max3: u16, + duty: u16, + counter: usize, +} + +impl PwmTest { + fn new(mut pwm3: SimplePwm<'static, peripherals::TIM3>, led: Output<'static, PA5>) -> Self { + let max3 = pwm3.get_max_duty(); + pwm3.enable(timer::Channel::Ch2); + pwm3.enable_update_interrupt(true); + PwmTest { + pwm3, + max3, + duty: 0, + counter: 0, + led, + } + } + async fn task(&mut self) { + loop { + self.duty = (self.duty + 200) % self.max3; + self.pwm3.set_duty(Channel::Ch2, self.duty); + // note that the update interrupt will be call exact 100 times per second! + self.pwm3.wait_update_interrupt().await; + self.counter = (self.counter + 1) % 100; + match self.counter { + 10 => self.led.set_high(), + 30 => self.led.set_low(), + _ => (), + } + } + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + let d4_pb5 = PwmPin::new_ch2(p.PB5, OutputType::PushPull); + let pwm3 = SimplePwm::<'static>::new( + p.TIM3, + None, + Some(d4_pb5), + None, + None, + Hertz(100), + embassy_stm32::timer::CountingMode::EdgeAlignedUp, + ); + let led_g = Output::new(p.PA5, Level::High, Speed::Low); + let pwm_test = PwmTest::new(pwm3, led_g); + // note that at the end the pwmTest task is the owner of pwmTest. + // PwmTest is the owner of the pwm and the led. + spawner.spawn(pwm_task(pwm_test)).unwrap(); + + loop { + info!("high"); + Timer::after_millis(300).await; + + info!("low"); + Timer::after_millis(300).await; + } +}