aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/timer/input_capture.rs39
-rw-r--r--embassy-stm32/src/timer/low_level.rs28
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs114
-rw-r--r--examples/stm32f1/src/bin/input_capture.rs52
-rw-r--r--examples/stm32f1/src/bin/pwm_input.rs54
-rw-r--r--examples/stm32f4/src/bin/pwm_input.rs54
7 files changed, 314 insertions, 28 deletions
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index a1c1486f9..0258d4077 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -7,7 +7,7 @@ use core::task::{Context, Poll};
7 7
8use embassy_hal_internal::{into_ref, PeripheralRef}; 8use embassy_hal_internal::{into_ref, PeripheralRef};
9 9
10use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; 10use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};
11use super::{ 11use super::{
12 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, 12 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
13 GeneralInstance4Channel, 13 GeneralInstance4Channel,
@@ -40,11 +40,9 @@ macro_rules! channel_impl {
40 #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] 40 #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")]
41 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull_type: Pull) -> Self { 41 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull_type: Pull) -> Self {
42 into_ref!(pin); 42 into_ref!(pin);
43 critical_section::with(|_| { 43
44 pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); 44 pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
45 #[cfg(gpio_v2)] 45
46 pin.set_speed(crate::gpio::Speed::VeryHigh);
47 });
48 CapturePin { 46 CapturePin {
49 _pin: pin.map_into(), 47 _pin: pin.map_into(),
50 phantom: PhantomData, 48 phantom: PhantomData,
@@ -83,7 +81,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
83 let mut this = Self { inner: Timer::new(tim) }; 81 let mut this = Self { inner: Timer::new(tim) };
84 82
85 this.inner.set_counting_mode(counting_mode); 83 this.inner.set_counting_mode(counting_mode);
86 this.set_tick_freq(freq); 84 this.inner.set_tick_freq(freq);
87 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 85 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
88 this.inner.start(); 86 this.inner.start();
89 87
@@ -109,24 +107,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
109 self.inner.get_channel_enable_state(channel) 107 self.inner.get_channel_enable_state(channel)
110 } 108 }
111 109
112 /// Set tick frequency.
113 ///
114 /// Note: when you call this, the max period value changes
115 pub fn set_tick_freq(&mut self, freq: Hertz) {
116 let f = freq;
117 assert!(f.0 > 0);
118 let timer_f = self.inner.get_clock_frequency();
119
120 let pclk_ticks_per_timer_period = timer_f / f;
121 let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into());
122
123 let regs = self.inner.regs_core();
124 regs.psc().write_value(psc);
125
126 // Generate an Update Request
127 regs.egr().write(|r| r.set_ug(true));
128 }
129
130 /// Set the input capture mode for a given channel. 110 /// Set the input capture mode for a given channel.
131 pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { 111 pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {
132 self.inner.set_input_capture_mode(channel, mode); 112 self.inner.set_input_capture_mode(channel, mode);
@@ -148,10 +128,13 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
148 } 128 }
149 129
150 fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> { 130 fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> {
151 self.inner.enable_channel(channel, true); 131 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5
152 self.inner.set_input_capture_mode(channel, mode); 132 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode
153 self.inner.set_input_ti_selection(channel, tisel); 133 self.inner.set_input_ti_selection(channel, tisel);
154 self.inner.clear_input_interrupt(channel); 134 self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER);
135 self.inner.set_input_capture_mode(channel, mode);
136 self.inner.set_input_capture_prescaler(channel, 0);
137 self.inner.enable_channel(channel, true);
155 self.inner.enable_input_interrupt(channel, true); 138 self.inner.enable_input_interrupt(channel, true);
156 139
157 InputCaptureFuture { 140 InputCaptureFuture {
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 9932c04cd..e643722aa 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -7,6 +7,8 @@
7//! The available functionality depends on the timer type. 7//! The available functionality depends on the timer type.
8 8
9use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; 9use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
10// Re-export useful enums
11pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource};
10 12
11use super::*; 13use super::*;
12use crate::pac::timer::vals; 14use crate::pac::timer::vals;
@@ -274,6 +276,22 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
274 } 276 }
275 } 277 }
276 278
279 /// Set tick frequency.
280 pub fn set_tick_freq(&mut self, freq: Hertz) {
281 let f = freq;
282 assert!(f.0 > 0);
283 let timer_f = self.get_clock_frequency();
284
285 let pclk_ticks_per_timer_period = timer_f / f;
286 let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into());
287
288 let regs = self.regs_core();
289 regs.psc().write_value(psc);
290
291 // Generate an Update Request
292 regs.egr().write(|r| r.set_ug(true));
293 }
294
277 /// Clear update interrupt. 295 /// Clear update interrupt.
278 /// 296 ///
279 /// Returns whether the update interrupt flag was set. 297 /// Returns whether the update interrupt flag was set.
@@ -573,6 +591,16 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
573 pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { 591 pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
574 self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) 592 self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
575 } 593 }
594
595 /// Set Timer Slave Mode
596 pub fn set_slave_mode(&self, sms: SlaveMode) {
597 self.regs_gp16().smcr().modify(|r| r.set_sms(sms));
598 }
599
600 /// Set Timer Trigger Source
601 pub fn set_trigger_source(&self, ts: TriggerSource) {
602 self.regs_gp16().smcr().modify(|r| r.set_ts(ts));
603 }
576} 604}
577 605
578#[cfg(not(stm32l0))] 606#[cfg(not(stm32l0))]
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 314b6006b..25782ee13 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -8,6 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker;
8pub mod complementary_pwm; 8pub mod complementary_pwm;
9pub mod input_capture; 9pub mod input_capture;
10pub mod low_level; 10pub mod low_level;
11pub mod pwm_input;
11pub mod qei; 12pub mod qei;
12pub mod simple_pwm; 13pub mod simple_pwm;
13 14
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
new file mode 100644
index 000000000..dcf098a78
--- /dev/null
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -0,0 +1,114 @@
1//! PWM Input driver.
2
3use embassy_hal_internal::into_ref;
4
5use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
6use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel};
7use crate::gpio::{AFType, Pull};
8use crate::time::Hertz;
9use crate::Peripheral;
10
11/// PWM Input driver.
12pub struct PwmInput<'d, T: GeneralInstance4Channel> {
13 channel: Channel,
14 inner: Timer<'d, T>,
15}
16
17impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
18 /// Create a new PWM input driver.
19 pub fn new(
20 tim: impl Peripheral<P = T> + 'd,
21 pin: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
22 pull_type: Pull,
23 freq: Hertz,
24 ) -> Self {
25 into_ref!(pin);
26
27 pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
28
29 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
30 }
31
32 /// Create a new PWM input driver.
33 pub fn new_alt(
34 tim: impl Peripheral<P = T> + 'd,
35 pin: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
36 pull_type: Pull,
37 freq: Hertz,
38 ) -> Self {
39 into_ref!(pin);
40
41 pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
42
43 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
44 }
45
46 fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self {
47 let mut inner = Timer::new(tim);
48
49 inner.set_counting_mode(CountingMode::EdgeAlignedUp);
50 inner.set_tick_freq(freq);
51 inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
52 inner.start();
53
54 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
55 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode
56 inner.set_input_ti_selection(ch1, InputTISelection::Normal);
57 inner.set_input_capture_mode(ch1, InputCaptureMode::Rising);
58
59 inner.set_input_ti_selection(ch2, InputTISelection::Alternate);
60 inner.set_input_capture_mode(ch2, InputCaptureMode::Falling);
61
62 inner.set_trigger_source(match ch1 {
63 Channel::Ch1 => TriggerSource::TI1FP1,
64 Channel::Ch2 => TriggerSource::TI2FP2,
65 _ => panic!("Invalid channel for PWM input"),
66 });
67
68 inner.set_slave_mode(SlaveMode::RESET_MODE);
69
70 // Must call the `enable` function after
71
72 Self { channel: ch1, inner }
73 }
74
75 /// Enable the given channel.
76 pub fn enable(&mut self) {
77 self.inner.enable_channel(Channel::Ch1, true);
78 self.inner.enable_channel(Channel::Ch2, true);
79 }
80
81 /// Disable the given channel.
82 pub fn disable(&mut self) {
83 self.inner.enable_channel(Channel::Ch1, false);
84 self.inner.enable_channel(Channel::Ch2, false);
85 }
86
87 /// Check whether given channel is enabled
88 pub fn is_enabled(&self) -> bool {
89 self.inner.get_channel_enable_state(Channel::Ch1)
90 }
91
92 /// Get the period tick count
93 pub fn get_period_ticks(&self) -> u32 {
94 self.inner.get_capture_value(self.channel)
95 }
96
97 /// Get the pulse width tick count
98 pub fn get_width_ticks(&self) -> u32 {
99 self.inner.get_capture_value(match self.channel {
100 Channel::Ch1 => Channel::Ch2,
101 Channel::Ch2 => Channel::Ch1,
102 _ => panic!("Invalid channel for PWM input"),
103 })
104 }
105
106 /// Get the duty cycle in 100%
107 pub fn get_duty_cycle(&self) -> f32 {
108 let period = self.get_period_ticks();
109 if period == 0 {
110 return 0.;
111 }
112 100. * (self.get_width_ticks() as f32) / (period as f32)
113 }
114}
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs
new file mode 100644
index 000000000..5e2dab9e6
--- /dev/null
+++ b/examples/stm32f1/src/bin/input_capture.rs
@@ -0,0 +1,52 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{Level, Output, Pull, Speed};
7use embassy_stm32::time::khz;
8use embassy_stm32::timer::input_capture::{CapturePin, InputCapture};
9use embassy_stm32::timer::{self, Channel};
10use embassy_stm32::{bind_interrupts, peripherals};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14/// Connect PA2 and PC13 with a 1k Ohm resistor
15
16#[embassy_executor::task]
17async fn blinky(led: peripherals::PC13) {
18 let mut led = Output::new(led, Level::High, Speed::Low);
19
20 loop {
21 info!("high");
22 led.set_high();
23 Timer::after_millis(300).await;
24
25 info!("low");
26 led.set_low();
27 Timer::after_millis(300).await;
28 }
29}
30
31bind_interrupts!(struct Irqs {
32 TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;
33});
34
35#[embassy_executor::main]
36async fn main(spawner: Spawner) {
37 let p = embassy_stm32::init(Default::default());
38 info!("Hello World!");
39
40 unwrap!(spawner.spawn(blinky(p.PC13)));
41
42 let ch3 = CapturePin::new_ch3(p.PA2, Pull::None);
43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
44
45 loop {
46 info!("wait for rising edge");
47 ic.wait_for_rising_edge(Channel::Ch3).await;
48
49 let capture_value = ic.get_capture_value(Channel::Ch3);
50 info!("new capture! {}", capture_value);
51 }
52}
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs
new file mode 100644
index 000000000..f74853d4e
--- /dev/null
+++ b/examples/stm32f1/src/bin/pwm_input.rs
@@ -0,0 +1,54 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{Level, Output, Pull, Speed};
7use embassy_stm32::time::khz;
8use embassy_stm32::timer::pwm_input::PwmInput;
9use embassy_stm32::{bind_interrupts, peripherals, timer};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13/// Connect PA0 and PC13 with a 1k Ohm resistor
14
15#[embassy_executor::task]
16async fn blinky(led: peripherals::PC13) {
17 let mut led = Output::new(led, Level::High, Speed::Low);
18
19 loop {
20 info!("high");
21 led.set_high();
22 Timer::after_millis(300).await;
23
24 info!("low");
25 led.set_low();
26 Timer::after_millis(300).await;
27 }
28}
29
30bind_interrupts!(struct Irqs {
31 TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;
32});
33
34#[embassy_executor::main]
35async fn main(spawner: Spawner) {
36 let p = embassy_stm32::init(Default::default());
37 info!("Hello World!");
38
39 unwrap!(spawner.spawn(blinky(p.PC13)));
40
41 let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10));
42 pwm_input.enable();
43
44 loop {
45 Timer::after_millis(500).await;
46 let period = pwm_input.get_period_ticks();
47 let width = pwm_input.get_width_ticks();
48 let duty_cycle = pwm_input.get_duty_cycle();
49 info!(
50 "period ticks: {} width ticks: {} duty cycle: {}",
51 period, width, duty_cycle
52 );
53 }
54}
diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs
new file mode 100644
index 000000000..ce200549d
--- /dev/null
+++ b/examples/stm32f4/src/bin/pwm_input.rs
@@ -0,0 +1,54 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{Level, Output, Pull, Speed};
7use embassy_stm32::time::khz;
8use embassy_stm32::timer::pwm_input::PwmInput;
9use embassy_stm32::{bind_interrupts, peripherals, timer};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13/// Connect PB2 and PA6 with a 1k Ohm resistor
14
15#[embassy_executor::task]
16async fn blinky(led: peripherals::PB2) {
17 let mut led = Output::new(led, Level::High, Speed::Low);
18
19 loop {
20 info!("high");
21 led.set_high();
22 Timer::after_millis(300).await;
23
24 info!("low");
25 led.set_low();
26 Timer::after_millis(300).await;
27 }
28}
29
30bind_interrupts!(struct Irqs {
31 TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;
32});
33
34#[embassy_executor::main]
35async fn main(spawner: Spawner) {
36 let p = embassy_stm32::init(Default::default());
37 info!("Hello World!");
38
39 unwrap!(spawner.spawn(blinky(p.PB2)));
40
41 let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10));
42 pwm_input.enable();
43
44 loop {
45 Timer::after_millis(500).await;
46 let period = pwm_input.get_period_ticks();
47 let width = pwm_input.get_width_ticks();
48 let duty_cycle = pwm_input.get_duty_cycle();
49 info!(
50 "period ticks: {} width ticks: {} duty cycle: {}",
51 period, width, duty_cycle
52 );
53 }
54}