aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEicke Hecht <[email protected]>2025-11-24 22:31:04 +0100
committerEicke Hecht <[email protected]>2025-11-24 22:40:34 +0100
commit6a97b6718e1389ce9e5dd3fb989b3b9f3fcfbd09 (patch)
treeec7e3ee95580b700141e0bc7981684fc0e6fd05a
parent05417e91093dfd7e150083738988259d66ee4e37 (diff)
wip: Add a working example for the stm32f767zi nucleo. Currently no PWM signal visible...
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs2
-rw-r--r--examples/stm32f7/src/bin/pwm.rs31
-rw-r--r--examples/stm32f7/src/bin/pwm_ringbuffer.rs157
3 files changed, 171 insertions, 19 deletions
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> {
412 }; 412 };
413 413
414 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); 414 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);
415 self.inner.set_cc_dma_enable_state(channel, true);
416
415 let ring_buf = unsafe { 417 let ring_buf = unsafe {
416 WritableRingBuffer::new( 418 WritableRingBuffer::new(
417 tx_dma, 419 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;
8use embassy_stm32::time::Hertz; 8use embassy_stm32::time::Hertz;
9use embassy_stm32::time::mhz; 9use embassy_stm32::time::mhz;
10use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 10use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
11use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
12 13
13// If you are trying this and your USB device doesn't connect, the most 14// If you are trying this and your USB device doesn't connect, the most
@@ -41,29 +42,21 @@ async fn main(_spawner: Spawner) {
41 } 42 }
42 let p = embassy_stm32::init(config); 43 let p = embassy_stm32::init(config);
43 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); 44 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
44 let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); 45 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default());
45 let mut pwm = SimplePwm::new(
46 p.TIM1,
47 Some(ch1_pin),
48 Some(ch2_pin),
49 None,
50 None,
51 mhz(1),
52 Default::default(),
53 );
54 let mut ch1 = pwm.ch1(); 46 let mut ch1 = pwm.ch1();
55 ch1.enable(); 47 ch1.enable();
48
56 info!("PWM initialized"); 49 info!("PWM initialized");
57 info!("PWM max duty {}", ch1.max_duty_cycle()); 50 info!("PWM max duty {}", ch1.max_duty_cycle());
58 51
59 info!("PWM duty on channel 1 (D6) 50%"); 52 loop {
60 ch1.set_duty_cycle_fraction(1, 2); 53 ch1.set_duty_cycle_fully_off();
61 info!("PWM waveform on channel 2 (D5)"); 54 Timer::after_millis(300).await;
62 const MAX_DUTY: usize = 200; 55 ch1.set_duty_cycle_fraction(1, 4);
63 let mut duty = [0u16; MAX_DUTY]; 56 Timer::after_millis(300).await;
64 for i in 0..MAX_DUTY { 57 ch1.set_duty_cycle_fraction(1, 2);
65 duty[i] = i as u16; 58 Timer::after_millis(300).await;
59 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
60 Timer::after_millis(300).await;
66 } 61 }
67 pwm.waveform_continuous::<embassy_stm32::timer::Ch2>(p.DMA2_CH6, &duty)
68 .await;
69} 62}
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 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::time::mhz;
9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13// If you are trying this and your USB device doesn't connect, the most
14// common issues are the RCC config and vbus_detection
15//
16// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
17// for more information.
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 info!("PWM Ring Buffer Example");
21
22 let mut config = Config::default();
23 {
24 use embassy_stm32::rcc::*;
25 use embassy_stm32::time::Hertz;
26 config.rcc.hse = Some(Hse {
27 freq: Hertz(8_000_000),
28 mode: HseMode::Bypass,
29 });
30 config.rcc.pll_src = PllSource::HSE;
31 config.rcc.pll = Some(Pll {
32 prediv: PllPreDiv::DIV4,
33 mul: PllMul::MUL200,
34 divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz
35 divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz
36 divr: None,
37 });
38 config.rcc.ahb_pre = AHBPrescaler::DIV1;
39 config.rcc.apb1_pre = APBPrescaler::DIV4;
40 config.rcc.apb2_pre = APBPrescaler::DIV2;
41 config.rcc.sys = Sysclk::PLL1_P;
42 }
43 let p = embassy_stm32::init(config);
44
45 // Initialize PWM on TIM1
46 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
47 let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull);
48 let mut pwm = SimplePwm::new(
49 p.TIM1,
50 Some(ch1_pin),
51 Some(ch2_pin),
52 None,
53 None,
54 mhz(1),
55 Default::default(),
56 );
57
58 // Use channel 1 for static PWM at 50%
59 let mut ch1 = pwm.ch1();
60 ch1.enable();
61 ch1.set_duty_cycle_fraction(1, 2);
62 info!("Channel 1 (PE9/D6): Static 50% duty cycle");
63
64 // Get max duty from channel 1 before converting channel 2
65 let max_duty = ch1.max_duty_cycle();
66 info!("PWM max duty: {}", max_duty);
67
68 // Create a DMA ring buffer for channel 2
69 const BUFFER_SIZE: usize = 128;
70 static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE];
71 let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) };
72
73 // Pre-fill buffer with initial sine wave using lookup table approach
74 for i in 0..BUFFER_SIZE {
75 // Simple sine approximation using triangle wave
76 let phase = (i * 256) / BUFFER_SIZE;
77 let sine_approx = if phase < 128 {
78 phase as u16 * 2
79 } else {
80 (255 - phase) as u16 * 2
81 };
82 dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16;
83 }
84
85 // Convert channel 2 to ring-buffered PWM
86 let mut ring_pwm = pwm.into_ring_buffered_channel::<embassy_stm32::timer::Ch2>(p.DMA2_CH6, dma_buffer);
87
88 info!("Ring buffer capacity: {}", ring_pwm.capacity());
89
90 // Enable the PWM channel output
91 ring_pwm.enable();
92
93 // Pre-write some initial data to the buffer before starting
94 info!("Pre-writing initial waveform data...");
95
96 // Start the DMA ring buffer
97 ring_pwm.start();
98 info!("Channel 2 (PE11/D5): Ring buffered sine wave started");
99
100 // Give DMA time to start consuming
101 Timer::after_millis(10).await;
102
103 // Continuously update the waveform
104 let mut phase: f32 = 0.0;
105 let mut amplitude: f32 = 1.0;
106 let mut amplitude_direction = -0.05;
107
108 loop {
109 Timer::after_millis(50).await;
110
111 // Generate new waveform data with varying amplitude
112 let mut new_data = [0u16; 32];
113 for i in 0..new_data.len() {
114 // Triangle wave approximation for sine
115 let pos = ((i as u32 + phase as u32) * 4) % 256;
116 let sine_approx = if pos < 128 {
117 pos as u16 * 2
118 } else {
119 (255 - pos) as u16 * 2
120 };
121 let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256);
122 new_data[i] = ((scaled * max_duty as u32) / 256) as u16;
123 }
124
125 // Write new data to the ring buffer
126 match ring_pwm.write(&new_data) {
127 Ok((written, _remaining)) => {
128 if written < new_data.len() {
129 info!("Ring buffer getting full, wrote {} of {}", written, new_data.len());
130 }
131 }
132 Err(e) => {
133 info!("Write error: {:?}", e);
134 }
135 }
136
137 // Update phase for animation effect
138 phase += 2.0;
139 if phase >= 64.0 {
140 phase = 0.0;
141 }
142
143 // Vary amplitude for breathing effect
144 amplitude += amplitude_direction;
145 if amplitude <= 0.2 || amplitude >= 1.0 {
146 amplitude_direction = -amplitude_direction;
147 }
148
149 // Log buffer status periodically
150 if (phase as u32) % 10 == 0 {
151 match ring_pwm.len() {
152 Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()),
153 Err(_) => info!("Error reading buffer length"),
154 }
155 }
156 }
157}