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