1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
#![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.ch1().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer);
info!("Ring buffer capacity: {}", ring_pwm.capacity());
// Pre-write some initial data to the buffer before starting
info!("Pre-writing initial waveform data...");
ring_pwm.write(&[0; BUFFER_SIZE]).unwrap();
// Enable the PWM channel output
ring_pwm.enable();
// 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 {
// 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_exact(&new_data).await {
Ok(_remaining) => {}
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"),
}
}
}
}
|