aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32f7/src/bin/pwm_ringbuffer.rs
blob: 4d191ac13591df924aec29aa37a44e621037ac23 (plain)
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"),
            }
        }
    }
}