aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-12-15 23:05:32 +0000
committerGitHub <[email protected]>2023-12-15 23:05:32 +0000
commit858987263b03b1641df56de1856dc713ba499e52 (patch)
tree4ba3fa72b215389ce5e6a3f964d5267a6e7de518 /examples
parentb966f55883d8b6879b220b12c3449a25b4530667 (diff)
parentea1e1973eb88a3a57e7f4e2ad97d32e5fcd8b8d1 (diff)
Merge pull request #2290 from eZioPan/stm32f4-example-ws2812
add ws2812 example for stm32f4 with PWM and DMA
Diffstat (limited to 'examples')
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm_dma.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs b/examples/stm32f4/src/bin/ws2812_pwm_dma.rs
new file mode 100644
index 000000000..52cc665c7
--- /dev/null
+++ b/examples/stm32f4/src/bin/ws2812_pwm_dma.rs
@@ -0,0 +1,131 @@
1// Configure TIM3 in PWM mode, and start DMA Transfer(s) to send color data into ws2812.
2// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered.
3//
4// This demo is a combination of HAL, PAC, and manually invoke `dma::Transfer`
5//
6// Warning:
7// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.
8
9#![no_std]
10#![no_main]
11#![feature(type_alias_impl_trait)]
12
13use embassy_executor::Spawner;
14use embassy_stm32::gpio::OutputType;
15use embassy_stm32::pac;
16use embassy_stm32::time::khz;
17use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
18use embassy_stm32::timer::{Channel, CountingMode};
19use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _};
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let mut device_config = embassy_stm32::Config::default();
25
26 // set SYSCLK/HCLK/PCLK2 to 20 MHz, thus each tick is 0.05 us,
27 // and ws2812 timings are integer multiples of 0.05 us
28 {
29 use embassy_stm32::rcc::*;
30 use embassy_stm32::time::*;
31 device_config.enable_debug_during_sleep = true;
32 device_config.rcc.hse = Some(Hse {
33 freq: mhz(12),
34 mode: HseMode::Oscillator,
35 });
36 device_config.rcc.sys = Sysclk::PLL1_P;
37 device_config.rcc.pll_src = PllSource::HSE;
38 device_config.rcc.pll = Some(Pll {
39 prediv: PllPreDiv::DIV6,
40 mul: PllMul::MUL80,
41 divp: Some(PllPDiv::DIV8),
42 divq: None,
43 divr: None,
44 });
45 }
46
47 let mut dp = embassy_stm32::init(device_config);
48
49 let mut ws2812_pwm = SimplePwm::new(
50 dp.TIM3,
51 Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)),
52 None,
53 None,
54 None,
55 khz(800), // data rate of ws2812
56 CountingMode::EdgeAlignedUp,
57 );
58
59 // PAC level hacking,
60 // enable auto-reload preload, and enable timer-update-event trigger DMA
61 {
62 pac::TIM3.cr1().modify(|v| v.set_arpe(true));
63 pac::TIM3.dier().modify(|v| v.set_ude(true));
64 }
65
66 // construct ws2812 non-return-to-zero (NRZ) code bit by bit
67
68 let max_duty = ws2812_pwm.get_max_duty();
69 let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
70 let n1 = 2 * n0; // ws2812 Bit 1 high level timing
71
72 let turn_off = [
73 n0, n0, n0, n0, n0, n0, n0, n0, // Green
74 n0, n0, n0, n0, n0, n0, n0, n0, // Red
75 n0, n0, n0, n0, n0, n0, n0, n0, // Blue
76 0, // keep PWM output low after a transfer
77 ];
78
79 let dim_white = [
80 n0, n0, n0, n0, n0, n0, n1, n0, // Green
81 n0, n0, n0, n0, n0, n0, n1, n0, // Red
82 n0, n0, n0, n0, n0, n0, n1, n0, // Blue
83 0, // keep PWM output low after a transfer
84 ];
85
86 let color_list = [&turn_off, &dim_white];
87
88 let pwm_channel = Channel::Ch1;
89
90 // make sure PWM output keep low on first start
91 ws2812_pwm.set_duty(pwm_channel, 0);
92
93 {
94 use embassy_stm32::dma::{Burst, FifoThreshold, Transfer, TransferOptions};
95
96 // configure FIFO and MBURST of DMA, to minimize DMA occupation on AHB/APB
97 let mut dma_transfer_option = TransferOptions::default();
98 dma_transfer_option.fifo_threshold = Some(FifoThreshold::Full);
99 dma_transfer_option.mburst = Burst::Incr8;
100
101 let mut color_list_index = 0;
102
103 loop {
104 // start PWM output
105 ws2812_pwm.enable(pwm_channel);
106
107 unsafe {
108 Transfer::new_write(
109 // with &mut, we can easily reuse same DMA channel multiple times
110 &mut dp.DMA1_CH2,
111 5,
112 color_list[color_list_index],
113 pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _,
114 dma_transfer_option,
115 )
116 .await;
117 // ws2812 need at least 50 us low level input to confirm the input data and change it's state
118 Timer::after_micros(50).await;
119 }
120
121 // stop PWM output for saving some energy
122 ws2812_pwm.disable(pwm_channel);
123
124 // wait another half second, so that we can see color change
125 Timer::after_millis(500).await;
126
127 // flip the index bit so that next round DMA transfer the other color data
128 color_list_index ^= 1;
129 }
130 }
131}