Skip to content

Commit 6a97b67

Browse files
wip: Add a working example for the stm32f767zi nucleo. Currently no PWM signal visible...
1 parent 05417e9 commit 6a97b67

File tree

3 files changed

+171
-19
lines changed

3 files changed

+171
-19
lines changed

embassy-stm32/src/timer/simple_pwm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
412412
};
413413

414414
self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);
415+
self.inner.set_cc_dma_enable_state(channel, true);
416+
415417
let ring_buf = unsafe {
416418
WritableRingBuffer::new(
417419
tx_dma,

examples/stm32f7/src/bin/pwm.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use embassy_stm32::gpio::OutputType;
88
use embassy_stm32::time::Hertz;
99
use embassy_stm32::time::mhz;
1010
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
11+
use embassy_time::Timer;
1112
use {defmt_rtt as _, panic_probe as _};
1213

1314
// If you are trying this and your USB device doesn't connect, the most
@@ -41,29 +42,21 @@ async fn main(_spawner: Spawner) {
4142
}
4243
let p = embassy_stm32::init(config);
4344
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(
46-
p.TIM1,
47-
Some(ch1_pin),
48-
Some(ch2_pin),
49-
None,
50-
None,
51-
mhz(1),
52-
Default::default(),
53-
);
45+
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default());
5446
let mut ch1 = pwm.ch1();
5547
ch1.enable();
48+
5649
info!("PWM initialized");
5750
info!("PWM max duty {}", ch1.max_duty_cycle());
5851

59-
info!("PWM duty on channel 1 (D6) 50%");
60-
ch1.set_duty_cycle_fraction(1, 2);
61-
info!("PWM waveform on channel 2 (D5)");
62-
const MAX_DUTY: usize = 200;
63-
let mut duty = [0u16; MAX_DUTY];
64-
for i in 0..MAX_DUTY {
65-
duty[i] = i as u16;
52+
loop {
53+
ch1.set_duty_cycle_fully_off();
54+
Timer::after_millis(300).await;
55+
ch1.set_duty_cycle_fraction(1, 4);
56+
Timer::after_millis(300).await;
57+
ch1.set_duty_cycle_fraction(1, 2);
58+
Timer::after_millis(300).await;
59+
ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
60+
Timer::after_millis(300).await;
6661
}
67-
pwm.waveform_continuous::<embassy_stm32::timer::Ch2>(p.DMA2_CH6, &duty)
68-
.await;
6962
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use defmt::*;
5+
use embassy_executor::Spawner;
6+
use embassy_stm32::Config;
7+
use embassy_stm32::gpio::OutputType;
8+
use embassy_stm32::time::mhz;
9+
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10+
use embassy_time::Timer;
11+
use {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]
19+
async 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+
}

0 commit comments

Comments
 (0)