From 6a97b6718e1389ce9e5dd3fb989b3b9f3fcfbd09 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 22:31:04 +0100 Subject: wip: Add a working example for the stm32f767zi nucleo. Currently no PWM signal visible... --- embassy-stm32/src/timer/simple_pwm.rs | 2 + examples/stm32f7/src/bin/pwm.rs | 31 +++--- examples/stm32f7/src/bin/pwm_ringbuffer.rs | 157 +++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 examples/stm32f7/src/bin/pwm_ringbuffer.rs diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f4656b7bd..8b738741a 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -412,6 +412,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { }; self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); + self.inner.set_cc_dma_enable_state(channel, true); + let ring_buf = unsafe { WritableRingBuffer::new( tx_dma, diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs index 53efc2d8a..c1c7ec6ce 100644 --- a/examples/stm32f7/src/bin/pwm.rs +++ b/examples/stm32f7/src/bin/pwm.rs @@ -8,6 +8,7 @@ use embassy_stm32::gpio::OutputType; use embassy_stm32::time::Hertz; use embassy_stm32::time::mhz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; // If you are trying this and your USB device doesn't connect, the most @@ -41,29 +42,21 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); - let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); - let mut pwm = SimplePwm::new( - p.TIM1, - Some(ch1_pin), - Some(ch2_pin), - None, - None, - mhz(1), - Default::default(), - ); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default()); let mut ch1 = pwm.ch1(); ch1.enable(); + info!("PWM initialized"); info!("PWM max duty {}", ch1.max_duty_cycle()); - info!("PWM duty on channel 1 (D6) 50%"); - ch1.set_duty_cycle_fraction(1, 2); - info!("PWM waveform on channel 2 (D5)"); - const MAX_DUTY: usize = 200; - let mut duty = [0u16; MAX_DUTY]; - for i in 0..MAX_DUTY { - duty[i] = i as u16; + loop { + ch1.set_duty_cycle_fully_off(); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 4); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 2); + Timer::after_millis(300).await; + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); + Timer::after_millis(300).await; } - pwm.waveform_continuous::(p.DMA2_CH6, &duty) - .await; } diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs new file mode 100644 index 000000000..a8744f190 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs @@ -0,0 +1,157 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::mhz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("PWM Ring Buffer Example"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + use embassy_stm32::time::Hertz; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz + divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + // Initialize PWM on TIM1 + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); + let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); + let mut pwm = SimplePwm::new( + p.TIM1, + Some(ch1_pin), + Some(ch2_pin), + None, + None, + mhz(1), + Default::default(), + ); + + // Use channel 1 for static PWM at 50% + let mut ch1 = pwm.ch1(); + ch1.enable(); + ch1.set_duty_cycle_fraction(1, 2); + info!("Channel 1 (PE9/D6): Static 50% duty cycle"); + + // Get max duty from channel 1 before converting channel 2 + let max_duty = ch1.max_duty_cycle(); + info!("PWM max duty: {}", max_duty); + + // Create a DMA ring buffer for channel 2 + const BUFFER_SIZE: usize = 128; + static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE]; + let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) }; + + // Pre-fill buffer with initial sine wave using lookup table approach + for i in 0..BUFFER_SIZE { + // Simple sine approximation using triangle wave + let phase = (i * 256) / BUFFER_SIZE; + let sine_approx = if phase < 128 { + phase as u16 * 2 + } else { + (255 - phase) as u16 * 2 + }; + dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16; + } + + // Convert channel 2 to ring-buffered PWM + let mut ring_pwm = pwm.into_ring_buffered_channel::(p.DMA2_CH6, dma_buffer); + + info!("Ring buffer capacity: {}", ring_pwm.capacity()); + + // Enable the PWM channel output + ring_pwm.enable(); + + // Pre-write some initial data to the buffer before starting + info!("Pre-writing initial waveform data..."); + + // Start the DMA ring buffer + ring_pwm.start(); + info!("Channel 2 (PE11/D5): Ring buffered sine wave started"); + + // Give DMA time to start consuming + Timer::after_millis(10).await; + + // Continuously update the waveform + let mut phase: f32 = 0.0; + let mut amplitude: f32 = 1.0; + let mut amplitude_direction = -0.05; + + loop { + Timer::after_millis(50).await; + + // Generate new waveform data with varying amplitude + let mut new_data = [0u16; 32]; + for i in 0..new_data.len() { + // Triangle wave approximation for sine + let pos = ((i as u32 + phase as u32) * 4) % 256; + let sine_approx = if pos < 128 { + pos as u16 * 2 + } else { + (255 - pos) as u16 * 2 + }; + let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256); + new_data[i] = ((scaled * max_duty as u32) / 256) as u16; + } + + // Write new data to the ring buffer + match ring_pwm.write(&new_data) { + Ok((written, _remaining)) => { + if written < new_data.len() { + info!("Ring buffer getting full, wrote {} of {}", written, new_data.len()); + } + } + Err(e) => { + info!("Write error: {:?}", e); + } + } + + // Update phase for animation effect + phase += 2.0; + if phase >= 64.0 { + phase = 0.0; + } + + // Vary amplitude for breathing effect + amplitude += amplitude_direction; + if amplitude <= 0.2 || amplitude >= 1.0 { + amplitude_direction = -amplitude_direction; + } + + // Log buffer status periodically + if (phase as u32) % 10 == 0 { + match ring_pwm.len() { + Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()), + Err(_) => info!("Error reading buffer length"), + } + } + } +} -- cgit