aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authoreZio Pan <[email protected]>2023-12-28 16:23:47 +0800
committereZio Pan <[email protected]>2023-12-28 20:09:12 +0800
commit8c2a6df03b852233ef6c774896cbb00c2a15040f (patch)
treec8bd878540fafbf589055a91993b8e3b01661a09 /examples
parenteebfee189a592427423d3a3ad22132d59926a0e8 (diff)
implement PWM waveform generating with DMA
Diffstat (limited to 'examples')
-rw-r--r--examples/stm32f4/src/bin/pwm.rs12
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs (renamed from examples/stm32f4/src/bin/ws2812_pwm_dma.rs)75
-rw-r--r--examples/stm32g4/src/bin/pwm.rs12
-rw-r--r--examples/stm32h7/src/bin/pwm.rs13
4 files changed, 46 insertions, 66 deletions
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs
index 8844a9f0e..92bc42ec8 100644
--- a/examples/stm32f4/src/bin/pwm.rs
+++ b/examples/stm32f4/src/bin/pwm.rs
@@ -3,6 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::dma;
6use embassy_stm32::gpio::OutputType; 7use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 8use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
@@ -16,7 +17,16 @@ async fn main(_spawner: Spawner) {
16 info!("Hello World!"); 17 info!("Hello World!");
17 18
18 let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); 19 let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
19 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); 20 let mut pwm = SimplePwm::new(
21 p.TIM1,
22 Some(ch1),
23 None,
24 None,
25 None,
26 khz(10),
27 Default::default(),
28 dma::NoDma,
29 );
20 let max = pwm.get_max_duty(); 30 let max = pwm.get_max_duty();
21 pwm.enable(Channel::Ch1); 31 pwm.enable(Channel::Ch1);
22 32
diff --git a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index 4458b643f..973743e49 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};
@@ -52,7 +45,7 @@ async fn main(_spawner: Spawner) {
52 device_config.rcc.sys = Sysclk::PLL1_P; 45 device_config.rcc.sys = Sysclk::PLL1_P;
53 } 46 }
54 47
55 let mut dp = embassy_stm32::init(device_config); 48 let dp = embassy_stm32::init(device_config);
56 49
57 let mut ws2812_pwm = SimplePwm::new( 50 let mut ws2812_pwm = SimplePwm::new(
58 dp.TIM3, 51 dp.TIM3,
@@ -62,6 +55,7 @@ async fn main(_spawner: Spawner) {
62 None, 55 None,
63 khz(800), // data rate of ws2812 56 khz(800), // data rate of ws2812
64 CountingMode::EdgeAlignedUp, 57 CountingMode::EdgeAlignedUp,
58 dp.DMA1_CH2,
65 ); 59 );
66 60
67 // construct ws2812 non-return-to-zero (NRZ) code bit by bit 61 // construct ws2812 non-return-to-zero (NRZ) code bit by bit
@@ -89,62 +83,19 @@ async fn main(_spawner: Spawner) {
89 83
90 let pwm_channel = Channel::Ch1; 84 let pwm_channel = Channel::Ch1;
91 85
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 86 // make sure PWM output keep low on first start
99 ws2812_pwm.set_duty(pwm_channel, 0); 87 ws2812_pwm.set_duty(pwm_channel, 0);
100 88
101 { 89 // flip color at 2 Hz
102 use embassy_stm32::dma::{Burst, FifoThreshold, Transfer, TransferOptions}; 90 let mut ticker = Ticker::every(Duration::from_millis(500));
103
104 // configure FIFO and MBURST of DMA, to minimize DMA occupation on AHB/APB
105 let mut dma_transfer_option = TransferOptions::default();
106 dma_transfer_option.fifo_threshold = Some(FifoThreshold::Full);
107 dma_transfer_option.mburst = Burst::Incr8;
108
109 // flip color at 2 Hz
110 let mut ticker = Ticker::every(Duration::from_millis(500));
111
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 91
145 // wait until ticker tick 92 loop {
146 ticker.next().await; 93 for &color in color_list {
147 } 94 ws2812_pwm.gen_waveform(Channel::Ch1, color).await;
95 // ws2812 need at least 50 us low level input to confirm the input data and change it's state
96 Timer::after_micros(50).await;
97 // wait until ticker tick
98 ticker.next().await;
148 } 99 }
149 } 100 }
150} 101}
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index d4809a481..9fa004c3e 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -3,6 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::dma;
6use embassy_stm32::gpio::OutputType; 7use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 8use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
@@ -16,7 +17,16 @@ async fn main(_spawner: Spawner) {
16 info!("Hello World!"); 17 info!("Hello World!");
17 18
18 let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); 19 let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull);
19 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); 20 let mut pwm = SimplePwm::new(
21 p.TIM1,
22 Some(ch1),
23 None,
24 None,
25 None,
26 khz(10),
27 Default::default(),
28 dma::NoDma,
29 );
20 let max = pwm.get_max_duty(); 30 let max = pwm.get_max_duty();
21 pwm.enable(Channel::Ch1); 31 pwm.enable(Channel::Ch1);
22 32
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs
index 1e48ba67b..de155fc94 100644
--- a/examples/stm32h7/src/bin/pwm.rs
+++ b/examples/stm32h7/src/bin/pwm.rs
@@ -7,7 +7,7 @@ use embassy_stm32::gpio::OutputType;
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; 8use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
9use embassy_stm32::timer::Channel; 9use embassy_stm32::timer::Channel;
10use embassy_stm32::Config; 10use embassy_stm32::{dma, Config};
11use embassy_time::Timer; 11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
13 13
@@ -38,7 +38,16 @@ async fn main(_spawner: Spawner) {
38 info!("Hello World!"); 38 info!("Hello World!");
39 39
40 let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); 40 let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull);
41 let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); 41 let mut pwm = SimplePwm::new(
42 p.TIM3,
43 Some(ch1),
44 None,
45 None,
46 None,
47 khz(10),
48 Default::default(),
49 dma::NoDma,
50 );
42 let max = pwm.get_max_duty(); 51 let max = pwm.get_max_duty();
43 pwm.enable(Channel::Ch1); 52 pwm.enable(Channel::Ch1);
44 53