aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32f7/src
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-25 10:14:55 -0600
committerxoviat <[email protected]>2025-11-25 10:14:55 -0600
commit2e70c376c884a64fc931406350fecb6c0314dcf0 (patch)
treea8469b7356bff1ca74bf46827d7d8024c0085625 /examples/stm32f7/src
parent1f9c436afe6b0bcb306803d916e28df9e910479c (diff)
timer: add writable ring buffer
Diffstat (limited to 'examples/stm32f7/src')
-rw-r--r--examples/stm32f7/src/bin/pwm.rs61
-rw-r--r--examples/stm32f7/src/bin/pwm_ringbuffer.rs153
2 files changed, 214 insertions, 0 deletions
diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs
new file mode 100644
index 000000000..b071eb597
--- /dev/null
+++ b/examples/stm32f7/src/bin/pwm.rs
@@ -0,0 +1,61 @@
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::{Hertz, 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!("Hello World!");
21
22 let mut config = Config::default();
23 {
24 use embassy_stm32::rcc::*;
25 config.rcc.hse = Some(Hse {
26 freq: Hertz(8_000_000),
27 mode: HseMode::Bypass,
28 });
29 config.rcc.pll_src = PllSource::HSE;
30 config.rcc.pll = Some(Pll {
31 prediv: PllPreDiv::DIV4,
32 mul: PllMul::MUL200,
33 divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz
34 divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz
35 divr: None,
36 });
37 config.rcc.ahb_pre = AHBPrescaler::DIV1;
38 config.rcc.apb1_pre = APBPrescaler::DIV4;
39 config.rcc.apb2_pre = APBPrescaler::DIV2;
40 config.rcc.sys = Sysclk::PLL1_P;
41 }
42 let p = embassy_stm32::init(config);
43 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
44 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default());
45 let mut ch1 = pwm.ch1();
46 ch1.enable();
47
48 info!("PWM initialized");
49 info!("PWM max duty {}", ch1.max_duty_cycle());
50
51 loop {
52 ch1.set_duty_cycle_fully_off();
53 Timer::after_millis(300).await;
54 ch1.set_duty_cycle_fraction(1, 4);
55 Timer::after_millis(300).await;
56 ch1.set_duty_cycle_fraction(1, 2);
57 Timer::after_millis(300).await;
58 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
59 Timer::after_millis(300).await;
60 }
61}
diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs
new file mode 100644
index 000000000..4d191ac13
--- /dev/null
+++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs
@@ -0,0 +1,153 @@
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.ch1().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer);
87
88 info!("Ring buffer capacity: {}", ring_pwm.capacity());
89
90 // Pre-write some initial data to the buffer before starting
91 info!("Pre-writing initial waveform data...");
92
93 ring_pwm.write(&[0; BUFFER_SIZE]).unwrap();
94
95 // Enable the PWM channel output
96 ring_pwm.enable();
97
98 // Start the DMA ring buffer
99 ring_pwm.start();
100 info!("Channel 2 (PE11/D5): Ring buffered sine wave started");
101
102 // Give DMA time to start consuming
103 Timer::after_millis(10).await;
104
105 // Continuously update the waveform
106 let mut phase: f32 = 0.0;
107 let mut amplitude: f32 = 1.0;
108 let mut amplitude_direction = -0.05;
109
110 loop {
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_exact(&new_data).await {
127 Ok(_remaining) => {}
128 Err(e) => {
129 info!("Write error: {:?}", e);
130 }
131 }
132
133 // Update phase for animation effect
134 phase += 2.0;
135 if phase >= 64.0 {
136 phase = 0.0;
137 }
138
139 // Vary amplitude for breathing effect
140 amplitude += amplitude_direction;
141 if amplitude <= 0.2 || amplitude >= 1.0 {
142 amplitude_direction = -amplitude_direction;
143 }
144
145 // Log buffer status periodically
146 if (phase as u32) % 10 == 0 {
147 match ring_pwm.len() {
148 Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()),
149 Err(_) => info!("Error reading buffer length"),
150 }
151 }
152 }
153}