diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/stm32f4/src/bin/pwm.rs | 12 | ||||
| -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.rs | 12 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/pwm.rs | 13 |
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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dma; | ||
| 6 | use embassy_stm32::gpio::OutputType; | 7 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 8 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 9 | use 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 | ||
| 21 | use embassy_executor::Spawner; | 15 | use embassy_executor::Spawner; |
| 22 | use embassy_stm32::gpio::OutputType; | 16 | use embassy_stm32::gpio::OutputType; |
| 23 | use embassy_stm32::pac; | ||
| 24 | use embassy_stm32::time::khz; | 17 | use embassy_stm32::time::khz; |
| 25 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 18 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 26 | use embassy_stm32::timer::{Channel, CountingMode}; | 19 | use 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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dma; | ||
| 6 | use embassy_stm32::gpio::OutputType; | 7 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 8 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 9 | use 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; | |||
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | 9 | use embassy_stm32::timer::Channel; |
| 10 | use embassy_stm32::Config; | 10 | use embassy_stm32::{dma, Config}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {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 | ||
