aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-01-02 16:32:06 +0000
committerGitHub <[email protected]>2024-01-02 16:32:06 +0000
commit79ce34931d5b80dc52485c91c86a85c2f1ed170e (patch)
tree7fa7a1b5762d4e5efa5c3ec8bbcee482ca5f1ac4 /examples
parent7d037df1261715e216659d8b0efe29e296ae4e23 (diff)
parent638aa313d4e5e80649f7f6201fef3154e5b2bbd5 (diff)
Merge pull request #2367 from eZioPan/simplepwm-dma
implement PWM waveform generating with DMA
Diffstat (limited to 'examples')
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs (renamed from examples/stm32f4/src/bin/ws2812_pwm_dma.rs)75
1 files changed, 13 insertions, 62 deletions
diff --git a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index 4458b643f..239709253 100644
--- a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs
+++ b/examples/stm32f4/src/bin/ws2812_pwm.rs
@@ -2,15 +2,9 @@
2// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered. 2// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered.
3// 3//
4// The idea is that the data rate of ws2812 is 800 kHz, and it use different duty ratio to represent bit 0 and bit 1. 4// The idea is that the data rate of ws2812 is 800 kHz, and it use different duty ratio to represent bit 0 and bit 1.
5// Thus we can set TIM overflow at 800 kHz, and let TIM Update Event trigger a DMA transfer, then let DMA change CCR value, 5// Thus we can set TIM overflow at 800 kHz, and change duty ratio of TIM to meet the bit representation of ws2812.
6// such that pwm duty ratio meet the bit representation of ws2812.
7// 6//
8// You may want to modify TIM CCR with Cortex core directly, 7// you may also want to take a look at `ws2812_spi.rs` file, which make use of SPI instead.
9// but according to my test, Cortex core will need to run far more than 100 MHz to catch up with TIM.
10// Thus we need to use a DMA.
11//
12// This demo is a combination of HAL, PAC, and manually invoke `dma::Transfer`.
13// If you need a simpler way to control ws2812, you may want to take a look at `ws2812_spi.rs` file, which make use of SPI.
14// 8//
15// Warning: 9// Warning:
16// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn. 10// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.
@@ -20,7 +14,6 @@
20 14
21use embassy_executor::Spawner; 15use embassy_executor::Spawner;
22use embassy_stm32::gpio::OutputType; 16use embassy_stm32::gpio::OutputType;
23use embassy_stm32::pac;
24use embassy_stm32::time::khz; 17use embassy_stm32::time::khz;
25use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 18use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
26use embassy_stm32::timer::{Channel, CountingMode}; 19use embassy_stm32::timer::{Channel, CountingMode};
@@ -89,62 +82,20 @@ async fn main(_spawner: Spawner) {
89 82
90 let pwm_channel = Channel::Ch1; 83 let pwm_channel = Channel::Ch1;
91 84
92 // PAC level hacking, enable output compare preload
93 // keep output waveform integrity
94 pac::TIM3
95 .ccmr_output(pwm_channel.index())
96 .modify(|v| v.set_ocpe(0, true));
97
98 // make sure PWM output keep low on first start 85 // make sure PWM output keep low on first start
99 ws2812_pwm.set_duty(pwm_channel, 0); 86 ws2812_pwm.set_duty(pwm_channel, 0);
100 87
101 { 88 // flip color at 2 Hz
102 use embassy_stm32::dma::{Burst, FifoThreshold, Transfer, TransferOptions}; 89 let mut ticker = Ticker::every(Duration::from_millis(500));
103 90
104 // configure FIFO and MBURST of DMA, to minimize DMA occupation on AHB/APB 91 loop {
105 let mut dma_transfer_option = TransferOptions::default(); 92 for &color in color_list {
106 dma_transfer_option.fifo_threshold = Some(FifoThreshold::Full); 93 // with &mut, we can easily reuse same DMA channel multiple times
107 dma_transfer_option.mburst = Burst::Incr8; 94 ws2812_pwm.gen_waveform(&mut dp.DMA1_CH2, pwm_channel, color).await;
108 95 // ws2812 need at least 50 us low level input to confirm the input data and change it's state
109 // flip color at 2 Hz 96 Timer::after_micros(50).await;
110 let mut ticker = Ticker::every(Duration::from_millis(500)); 97 // wait until ticker tick
111 98 ticker.next().await;
112 loop {
113 for &color in color_list {
114 // start PWM output
115 ws2812_pwm.enable(pwm_channel);
116
117 // PAC level hacking, enable timer-update-event trigger DMA
118 pac::TIM3.dier().modify(|v| v.set_ude(true));
119
120 unsafe {
121 Transfer::new_write(
122 // with &mut, we can easily reuse same DMA channel multiple times
123 &mut dp.DMA1_CH2,
124 5,
125 color,
126 pac::TIM3.ccr(pwm_channel.index()).as_ptr() as *mut _,
127 dma_transfer_option,
128 )
129 .await;
130
131 // Turn off timer-update-event trigger DMA as soon as possible.
132 // Then clean the FIFO Error Flag if set.
133 pac::TIM3.dier().modify(|v| v.set_ude(false));
134 if pac::DMA1.isr(0).read().feif(2) {
135 pac::DMA1.ifcr(0).write(|v| v.set_feif(2, true));
136 }
137
138 // ws2812 need at least 50 us low level input to confirm the input data and change it's state
139 Timer::after_micros(50).await;
140 }
141
142 // stop PWM output for saving some energy
143 ws2812_pwm.disable(pwm_channel);
144
145 // wait until ticker tick
146 ticker.next().await;
147 }
148 } 99 }
149 } 100 }
150} 101}