aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src
diff options
context:
space:
mode:
authorElias Hanelt <[email protected]>2025-12-01 14:43:51 -0800
committerElias Hanelt <[email protected]>2025-12-01 14:43:51 -0800
commit7772ab265c9776b3df3f7d4aeb397b6076e1f5a8 (patch)
tree40af598203172aa76fc66bdefdd767d93952e0d0 /embassy-stm32/src
parent88ce8314c5cd653ac245cc5dbf367f6814f24be9 (diff)
parent217b683427687e8f3a27f02852e6f5bd2405ace3 (diff)
Merge remote-tracking branch 'origin/main' into feature/spi-bidi
Diffstat (limited to 'embassy-stm32/src')
-rw-r--r--embassy-stm32/src/can/enums.rs37
-rw-r--r--embassy-stm32/src/can/util.rs33
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs15
-rw-r--r--embassy-stm32/src/dma/gpdma/mod.rs10
-rw-r--r--embassy-stm32/src/dma/gpdma/ringbuffered.rs11
-rw-r--r--embassy-stm32/src/dma/mod.rs50
-rw-r--r--embassy-stm32/src/low_power.rs22
-rw-r--r--embassy-stm32/src/rcc/mod.rs111
-rw-r--r--embassy-stm32/src/time_driver.rs126
-rw-r--r--embassy-stm32/src/usart/mod.rs16
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs3
11 files changed, 218 insertions, 216 deletions
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs
index 6d91020fc..c5900cadc 100644
--- a/embassy-stm32/src/can/enums.rs
+++ b/embassy-stm32/src/can/enums.rs
@@ -82,3 +82,40 @@ pub enum RefCountOp {
82 /// Notify sender destroyed 82 /// Notify sender destroyed
83 NotifySenderDestroyed, 83 NotifySenderDestroyed,
84} 84}
85
86/// Error returned when calculating the can timing fails
87#[derive(Debug)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub enum TimingCalcError {
90 /// Bitrate is lower than 1000
91 BitrateTooLow {
92 /// The set bitrate
93 bitrate: u32,
94 },
95 /// No solution possible
96 NoSolution {
97 /// The sum of BS1 and BS2
98 bs1_bs2_sum: u8,
99 },
100 /// Prescaler is not 1 < prescaler < 1024
101 InvalidPrescaler {
102 /// The calculated prescaler value
103 prescaler: u32,
104 },
105 /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX
106 BSNotInRange {
107 /// The value of BS1
108 bs1: u8,
109 /// The value of BS2
110 bs2: u8,
111 },
112 /// Final bitrate doesn't match the requested bitrate
113 NoMatch {
114 /// The requested bitrate
115 requested: u32,
116 /// The calculated bitrate
117 final_calculated: u32,
118 },
119 /// core::num::NonZeroUxx::new error
120 CoreNumNew,
121}
diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs
index 6d7f0c16a..beca4c34e 100644
--- a/embassy-stm32/src/can/util.rs
+++ b/embassy-stm32/src/can/util.rs
@@ -2,6 +2,8 @@
2 2
3use core::num::{NonZeroU8, NonZeroU16}; 3use core::num::{NonZeroU8, NonZeroU16};
4 4
5use crate::can::enums::TimingCalcError;
6
5/// Shared struct to represent bit timings used by calc_can_timings. 7/// Shared struct to represent bit timings used by calc_can_timings.
6#[derive(Clone, Copy, Debug)] 8#[derive(Clone, Copy, Debug)]
7pub struct NominalBitTiming { 9pub struct NominalBitTiming {
@@ -17,7 +19,10 @@ pub struct NominalBitTiming {
17} 19}
18 20
19/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency 21/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency
20pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option<NominalBitTiming> { 22pub fn calc_can_timings(
23 periph_clock: crate::time::Hertz,
24 can_bitrate: u32,
25) -> Result<NominalBitTiming, TimingCalcError> {
21 const BS1_MAX: u8 = 16; 26 const BS1_MAX: u8 = 16;
22 const BS2_MAX: u8 = 8; 27 const BS2_MAX: u8 = 8;
23 const MAX_SAMPLE_POINT_PERMILL: u16 = 900; 28 const MAX_SAMPLE_POINT_PERMILL: u16 = 900;
@@ -25,7 +30,7 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
25 let periph_clock = periph_clock.0; 30 let periph_clock = periph_clock.0;
26 31
27 if can_bitrate < 1000 { 32 if can_bitrate < 1000 {
28 return None; 33 return Err(TimingCalcError::BitrateTooLow { bitrate: can_bitrate });
29 } 34 }
30 35
31 // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG 36 // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
@@ -53,14 +58,14 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
53 let mut bs1_bs2_sum = max_quanta_per_bit - 1; 58 let mut bs1_bs2_sum = max_quanta_per_bit - 1;
54 while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { 59 while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 {
55 if bs1_bs2_sum <= 2 { 60 if bs1_bs2_sum <= 2 {
56 return None; // No solution 61 return Err(TimingCalcError::NoSolution { bs1_bs2_sum }); // No solution
57 } 62 }
58 bs1_bs2_sum -= 1; 63 bs1_bs2_sum -= 1;
59 } 64 }
60 65
61 let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; 66 let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32;
62 if (prescaler < 1) || (prescaler > 1024) { 67 if (prescaler < 1) || (prescaler > 1024) {
63 return None; // No solution 68 return Err(TimingCalcError::InvalidPrescaler { prescaler }); // No solution
64 } 69 }
65 70
66 // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. 71 // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
@@ -93,22 +98,26 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
93 98
94 // Check is BS1 and BS2 are in range 99 // Check is BS1 and BS2 are in range
95 if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { 100 if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) {
96 return None; 101 return Err(TimingCalcError::BSNotInRange { bs1, bs2 });
97 } 102 }
98 103
104 let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32);
99 // Check if final bitrate matches the requested 105 // Check if final bitrate matches the requested
100 if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { 106 if can_bitrate != calculated {
101 return None; 107 return Err(TimingCalcError::NoMatch {
108 requested: can_bitrate,
109 final_calculated: calculated,
110 });
102 } 111 }
103 112
104 // One is recommended by DS-015, CANOpen, and DeviceNet 113 // One is recommended by DS-015, CANOpen, and DeviceNet
105 let sync_jump_width = core::num::NonZeroU8::new(1)?; 114 let sync_jump_width = core::num::NonZeroU8::new(1).ok_or(TimingCalcError::CoreNumNew)?;
106 115
107 let seg1 = core::num::NonZeroU8::new(bs1)?; 116 let seg1 = core::num::NonZeroU8::new(bs1).ok_or(TimingCalcError::CoreNumNew)?;
108 let seg2 = core::num::NonZeroU8::new(bs2)?; 117 let seg2 = core::num::NonZeroU8::new(bs2).ok_or(TimingCalcError::CoreNumNew)?;
109 let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; 118 let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16).ok_or(TimingCalcError::CoreNumNew)?;
110 119
111 Some(NominalBitTiming { 120 Ok(NominalBitTiming {
112 sync_jump_width, 121 sync_jump_width,
113 prescaler: nz_prescaler, 122 prescaler: nz_prescaler,
114 seg1, 123 seg1,
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index b46ae2813..adc084474 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -8,8 +8,9 @@ use embassy_sync::waitqueue::AtomicWaker;
8 8
9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
10use super::word::{Word, WordSize}; 10use super::word::{Word, WordSize};
11use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; 11use super::{AnyChannel, Channel, Dir, Request, STATE};
12use crate::interrupt::typelevel::Interrupt; 12use crate::interrupt::typelevel::Interrupt;
13use crate::rcc::BusyPeripheral;
13use crate::{interrupt, pac}; 14use crate::{interrupt, pac};
14 15
15pub(crate) struct ChannelInfo { 16pub(crate) struct ChannelInfo {
@@ -602,7 +603,7 @@ impl AnyChannel {
602/// DMA transfer. 603/// DMA transfer.
603#[must_use = "futures do nothing unless you `.await` or poll them"] 604#[must_use = "futures do nothing unless you `.await` or poll them"]
604pub struct Transfer<'a> { 605pub struct Transfer<'a> {
605 channel: BusyChannel<'a>, 606 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
606} 607}
607 608
608impl<'a> Transfer<'a> { 609impl<'a> Transfer<'a> {
@@ -714,7 +715,7 @@ impl<'a> Transfer<'a> {
714 ); 715 );
715 channel.start(); 716 channel.start();
716 Self { 717 Self {
717 channel: BusyChannel::new(channel), 718 channel: BusyPeripheral::new(channel),
718 } 719 }
719 } 720 }
720 721
@@ -818,7 +819,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> {
818 819
819/// Ringbuffer for receiving data using DMA circular mode. 820/// Ringbuffer for receiving data using DMA circular mode.
820pub struct ReadableRingBuffer<'a, W: Word> { 821pub struct ReadableRingBuffer<'a, W: Word> {
821 channel: BusyChannel<'a>, 822 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
822 ringbuf: ReadableDmaRingBuffer<'a, W>, 823 ringbuf: ReadableDmaRingBuffer<'a, W>,
823} 824}
824 825
@@ -855,7 +856,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
855 ); 856 );
856 857
857 Self { 858 Self {
858 channel: BusyChannel::new(channel), 859 channel: BusyPeripheral::new(channel),
859 ringbuf: ReadableDmaRingBuffer::new(buffer), 860 ringbuf: ReadableDmaRingBuffer::new(buffer),
860 } 861 }
861 } 862 }
@@ -974,7 +975,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
974 975
975/// Ringbuffer for writing data using DMA circular mode. 976/// Ringbuffer for writing data using DMA circular mode.
976pub struct WritableRingBuffer<'a, W: Word> { 977pub struct WritableRingBuffer<'a, W: Word> {
977 channel: BusyChannel<'a>, 978 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
978 ringbuf: WritableDmaRingBuffer<'a, W>, 979 ringbuf: WritableDmaRingBuffer<'a, W>,
979} 980}
980 981
@@ -1011,7 +1012,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
1011 ); 1012 );
1012 1013
1013 Self { 1014 Self {
1014 channel: BusyChannel::new(channel), 1015 channel: BusyPeripheral::new(channel),
1015 ringbuf: WritableDmaRingBuffer::new(buffer), 1016 ringbuf: WritableDmaRingBuffer::new(buffer),
1016 } 1017 }
1017 } 1018 }
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs
index 383c74a78..bfd0570f8 100644
--- a/embassy-stm32/src/dma/gpdma/mod.rs
+++ b/embassy-stm32/src/dma/gpdma/mod.rs
@@ -11,10 +11,10 @@ use linked_list::Table;
11 11
12use super::word::{Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::{AnyChannel, Channel, Dir, Request, STATE}; 13use super::{AnyChannel, Channel, Dir, Request, STATE};
14use crate::dma::BusyChannel;
15use crate::interrupt::typelevel::Interrupt; 14use crate::interrupt::typelevel::Interrupt;
16use crate::pac; 15use crate::pac;
17use crate::pac::gpdma::vals; 16use crate::pac::gpdma::vals;
17use crate::rcc::BusyPeripheral;
18 18
19pub mod linked_list; 19pub mod linked_list;
20pub mod ringbuffered; 20pub mod ringbuffered;
@@ -409,7 +409,7 @@ impl AnyChannel {
409/// Linked-list DMA transfer. 409/// Linked-list DMA transfer.
410#[must_use = "futures do nothing unless you `.await` or poll them"] 410#[must_use = "futures do nothing unless you `.await` or poll them"]
411pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { 411pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> {
412 channel: BusyChannel<'a>, 412 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
413} 413}
414 414
415impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { 415impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {
@@ -431,7 +431,7 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {
431 channel.start(); 431 channel.start();
432 432
433 Self { 433 Self {
434 channel: BusyChannel::new(channel), 434 channel: BusyPeripheral::new(channel),
435 } 435 }
436 } 436 }
437 437
@@ -508,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT>
508/// DMA transfer. 508/// DMA transfer.
509#[must_use = "futures do nothing unless you `.await` or poll them"] 509#[must_use = "futures do nothing unless you `.await` or poll them"]
510pub struct Transfer<'a> { 510pub struct Transfer<'a> {
511 channel: BusyChannel<'a>, 511 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
512} 512}
513 513
514impl<'a> Transfer<'a> { 514impl<'a> Transfer<'a> {
@@ -629,7 +629,7 @@ impl<'a> Transfer<'a> {
629 channel.start(); 629 channel.start();
630 630
631 Self { 631 Self {
632 channel: BusyChannel::new(channel), 632 channel: BusyPeripheral::new(channel),
633 } 633 }
634 } 634 }
635 635
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
index 54e4d5f71..c150d0b95 100644
--- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs
+++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
@@ -12,7 +12,8 @@ use super::{AnyChannel, STATE, TransferOptions};
12use crate::dma::gpdma::linked_list::{RunMode, Table}; 12use crate::dma::gpdma::linked_list::{RunMode, Table};
13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
14use crate::dma::word::Word; 14use crate::dma::word::Word;
15use crate::dma::{BusyChannel, Channel, Dir, Request}; 15use crate::dma::{Channel, Dir, Request};
16use crate::rcc::BusyPeripheral;
16 17
17struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); 18struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>);
18 19
@@ -49,7 +50,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> {
49 50
50/// Ringbuffer for receiving data using GPDMA linked-list mode. 51/// Ringbuffer for receiving data using GPDMA linked-list mode.
51pub struct ReadableRingBuffer<'a, W: Word> { 52pub struct ReadableRingBuffer<'a, W: Word> {
52 channel: BusyChannel<'a>, 53 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
53 ringbuf: ReadableDmaRingBuffer<'a, W>, 54 ringbuf: ReadableDmaRingBuffer<'a, W>,
54 table: Table<2>, 55 table: Table<2>,
55 options: TransferOptions, 56 options: TransferOptions,
@@ -70,7 +71,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
70 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); 71 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory);
71 72
72 Self { 73 Self {
73 channel: BusyChannel::new(channel), 74 channel: BusyPeripheral::new(channel),
74 ringbuf: ReadableDmaRingBuffer::new(buffer), 75 ringbuf: ReadableDmaRingBuffer::new(buffer),
75 table, 76 table,
76 options, 77 options,
@@ -189,7 +190,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
189 190
190/// Ringbuffer for writing data using GPDMA linked-list mode. 191/// Ringbuffer for writing data using GPDMA linked-list mode.
191pub struct WritableRingBuffer<'a, W: Word> { 192pub struct WritableRingBuffer<'a, W: Word> {
192 channel: BusyChannel<'a>, 193 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
193 ringbuf: WritableDmaRingBuffer<'a, W>, 194 ringbuf: WritableDmaRingBuffer<'a, W>,
194 table: Table<2>, 195 table: Table<2>,
195 options: TransferOptions, 196 options: TransferOptions,
@@ -210,7 +211,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
210 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); 211 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral);
211 212
212 Self { 213 Self {
213 channel: BusyChannel::new(channel), 214 channel: BusyPeripheral::new(channel),
214 ringbuf: WritableDmaRingBuffer::new(buffer), 215 ringbuf: WritableDmaRingBuffer::new(buffer),
215 table, 216 table,
216 options, 217 options,
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index 4becc2d87..efb324fa6 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -3,14 +3,12 @@
3 3
4#[cfg(any(bdma, dma))] 4#[cfg(any(bdma, dma))]
5mod dma_bdma; 5mod dma_bdma;
6use core::ops;
7 6
8#[cfg(any(bdma, dma))] 7#[cfg(any(bdma, dma))]
9pub use dma_bdma::*; 8pub use dma_bdma::*;
10 9
11#[cfg(gpdma)] 10#[cfg(gpdma)]
12pub(crate) mod gpdma; 11pub(crate) mod gpdma;
13use embassy_hal_internal::Peri;
14#[cfg(gpdma)] 12#[cfg(gpdma)]
15pub use gpdma::ringbuffered::*; 13pub use gpdma::ringbuffered::*;
16#[cfg(gpdma)] 14#[cfg(gpdma)]
@@ -27,9 +25,10 @@ pub(crate) use util::*;
27pub(crate) mod ringbuffer; 25pub(crate) mod ringbuffer;
28pub mod word; 26pub mod word;
29 27
30use embassy_hal_internal::{PeripheralType, impl_peripheral}; 28use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};
31 29
32use crate::interrupt; 30use crate::interrupt;
31use crate::rcc::StoppablePeripheral;
33 32
34/// The direction of a DMA transfer. 33/// The direction of a DMA transfer.
35#[derive(Debug, Copy, Clone, PartialEq, Eq)] 34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -48,6 +47,13 @@ pub type Request = u8;
48#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] 47#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]
49pub type Request = (); 48pub type Request = ();
50 49
50impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> {
51 #[cfg(feature = "low-power")]
52 fn stop_mode(&self) -> crate::rcc::StopMode {
53 self.stop_mode
54 }
55}
56
51pub(crate) trait SealedChannel { 57pub(crate) trait SealedChannel {
52 #[cfg(not(stm32n6))] 58 #[cfg(not(stm32n6))]
53 fn id(&self) -> u8; 59 fn id(&self) -> u8;
@@ -103,44 +109,6 @@ macro_rules! dma_channel_impl {
103 }; 109 };
104} 110}
105 111
106pub(crate) struct BusyChannel<'a> {
107 channel: Peri<'a, AnyChannel>,
108}
109
110impl<'a> BusyChannel<'a> {
111 pub fn new(channel: Peri<'a, AnyChannel>) -> Self {
112 #[cfg(feature = "low-power")]
113 critical_section::with(|cs| {
114 crate::rcc::increment_stop_refcount(cs, channel.stop_mode);
115 });
116
117 Self { channel }
118 }
119}
120
121impl<'a> Drop for BusyChannel<'a> {
122 fn drop(&mut self) {
123 #[cfg(feature = "low-power")]
124 critical_section::with(|cs| {
125 crate::rcc::decrement_stop_refcount(cs, self.stop_mode);
126 });
127 }
128}
129
130impl<'a> ops::Deref for BusyChannel<'a> {
131 type Target = Peri<'a, AnyChannel>;
132
133 fn deref(&self) -> &Self::Target {
134 &self.channel
135 }
136}
137
138impl<'a> ops::DerefMut for BusyChannel<'a> {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.channel
141 }
142}
143
144/// Type-erased DMA channel. 112/// Type-erased DMA channel.
145pub struct AnyChannel { 113pub struct AnyChannel {
146 pub(crate) id: u8, 114 pub(crate) id: u8,
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index bd8290da0..cdf3323fb 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -51,7 +51,7 @@ use embassy_executor::*;
51 51
52use crate::interrupt; 52use crate::interrupt;
53pub use crate::rcc::StopMode; 53pub use crate::rcc::StopMode;
54use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; 54use crate::rcc::{BusyPeripheral, RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2};
55use crate::time_driver::get_driver; 55use crate::time_driver::get_driver;
56 56
57const THREAD_PENDER: usize = usize::MAX; 57const THREAD_PENDER: usize = usize::MAX;
@@ -59,7 +59,9 @@ const THREAD_PENDER: usize = usize::MAX;
59static mut EXECUTOR_TAKEN: bool = false; 59static mut EXECUTOR_TAKEN: bool = false;
60 60
61/// Prevent the device from going into the stop mode if held 61/// Prevent the device from going into the stop mode if held
62pub struct DeviceBusy(StopMode); 62pub struct DeviceBusy {
63 _stop_mode: BusyPeripheral<StopMode>,
64}
63 65
64impl DeviceBusy { 66impl DeviceBusy {
65 /// Create a new DeviceBusy with stop1. 67 /// Create a new DeviceBusy with stop1.
@@ -74,19 +76,9 @@ impl DeviceBusy {
74 76
75 /// Create a new DeviceBusy. 77 /// Create a new DeviceBusy.
76 pub fn new(stop_mode: StopMode) -> Self { 78 pub fn new(stop_mode: StopMode) -> Self {
77 critical_section::with(|cs| { 79 Self {
78 increment_stop_refcount(cs, stop_mode); 80 _stop_mode: BusyPeripheral::new(stop_mode),
79 }); 81 }
80
81 Self(stop_mode)
82 }
83}
84
85impl Drop for DeviceBusy {
86 fn drop(&mut self) {
87 critical_section::with(|cs| {
88 decrement_stop_refcount(cs, self.0);
89 });
90 } 82 }
91} 83}
92 84
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 85434fa83..1dd634cfe 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -4,6 +4,7 @@
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5 5
6use core::mem::MaybeUninit; 6use core::mem::MaybeUninit;
7use core::ops;
7 8
8mod bd; 9mod bd;
9pub use bd::*; 10pub use bd::*;
@@ -112,7 +113,7 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo
112} 113}
113 114
114#[cfg(feature = "low-power")] 115#[cfg(feature = "low-power")]
115pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { 116fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
116 match stop_mode { 117 match stop_mode {
117 StopMode::Standby => {} 118 StopMode::Standby => {}
118 StopMode::Stop2 => unsafe { 119 StopMode::Stop2 => unsafe {
@@ -125,7 +126,7 @@ pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode)
125} 126}
126 127
127#[cfg(feature = "low-power")] 128#[cfg(feature = "low-power")]
128pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { 129fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
129 match stop_mode { 130 match stop_mode {
130 StopMode::Standby => {} 131 StopMode::Standby => {}
131 StopMode::Stop2 => unsafe { 132 StopMode::Stop2 => unsafe {
@@ -182,6 +183,12 @@ pub enum StopMode {
182 Standby, 183 Standby,
183} 184}
184 185
186#[cfg(feature = "low-power")]
187type BusyRccPeripheral = BusyPeripheral<StopMode>;
188
189#[cfg(not(feature = "low-power"))]
190type BusyRccPeripheral = ();
191
185impl RccInfo { 192impl RccInfo {
186 /// Safety: 193 /// Safety:
187 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit 194 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit
@@ -234,9 +241,6 @@ impl RccInfo {
234 } 241 }
235 } 242 }
236 243
237 #[cfg(feature = "low-power")]
238 increment_stop_refcount(_cs, self.stop_mode);
239
240 // set the xxxRST bit 244 // set the xxxRST bit
241 let reset_ptr = self.reset_ptr(); 245 let reset_ptr = self.reset_ptr();
242 if let Some(reset_ptr) = reset_ptr { 246 if let Some(reset_ptr) = reset_ptr {
@@ -292,9 +296,6 @@ impl RccInfo {
292 } 296 }
293 } 297 }
294 298
295 #[cfg(feature = "low-power")]
296 decrement_stop_refcount(_cs, self.stop_mode);
297
298 // clear the xxxEN bit 299 // clear the xxxEN bit
299 let enable_ptr = self.enable_ptr(); 300 let enable_ptr = self.enable_ptr();
300 unsafe { 301 unsafe {
@@ -303,16 +304,63 @@ impl RccInfo {
303 } 304 }
304 } 305 }
305 306
307 #[allow(dead_code)]
308 pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) {
309 #[cfg(feature = "low-power")]
310 increment_stop_refcount(_cs, self.stop_mode);
311 }
312
313 #[allow(dead_code)]
314 pub(crate) fn increment_stop_refcount(&self) {
315 #[cfg(feature = "low-power")]
316 critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs))
317 }
318
319 #[allow(dead_code)]
320 pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) {
321 #[cfg(feature = "low-power")]
322 decrement_stop_refcount(_cs, self.stop_mode);
323 }
324
325 #[allow(dead_code)]
326 pub(crate) fn decrement_stop_refcount(&self) {
327 #[cfg(feature = "low-power")]
328 critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs))
329 }
330
306 // TODO: should this be `unsafe`? 331 // TODO: should this be `unsafe`?
307 pub(crate) fn enable_and_reset(&self) { 332 pub(crate) fn enable_and_reset(&self) {
333 critical_section::with(|cs| {
334 self.enable_and_reset_with_cs(cs);
335 self.increment_stop_refcount_with_cs(cs);
336 })
337 }
338
339 #[allow(dead_code)]
340 pub(crate) fn enable_and_reset_without_stop(&self) {
308 critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) 341 critical_section::with(|cs| self.enable_and_reset_with_cs(cs))
309 } 342 }
310 343
311 // TODO: should this be `unsafe`? 344 // TODO: should this be `unsafe`?
312 pub(crate) fn disable(&self) { 345 pub(crate) fn disable(&self) {
346 critical_section::with(|cs| {
347 self.disable_with_cs(cs);
348 self.decrement_stop_refcount_with_cs(cs);
349 })
350 }
351
352 // TODO: should this be `unsafe`?
353 #[allow(dead_code)]
354 pub(crate) fn disable_without_stop(&self) {
313 critical_section::with(|cs| self.disable_with_cs(cs)) 355 critical_section::with(|cs| self.disable_with_cs(cs))
314 } 356 }
315 357
358 #[allow(dead_code)]
359 pub(crate) fn block_stop(&self) -> BusyRccPeripheral {
360 #[cfg(feature = "low-power")]
361 BusyPeripheral::new(self.stop_mode)
362 }
363
316 fn reset_ptr(&self) -> Option<*mut u32> { 364 fn reset_ptr(&self) -> Option<*mut u32> {
317 if self.reset_offset_or_0xff != 0xff { 365 if self.reset_offset_or_0xff != 0xff {
318 Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) }) 366 Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) })
@@ -326,6 +374,53 @@ impl RccInfo {
326 } 374 }
327} 375}
328 376
377pub(crate) trait StoppablePeripheral {
378 #[cfg(feature = "low-power")]
379 #[allow(dead_code)]
380 fn stop_mode(&self) -> StopMode;
381}
382
383#[cfg(feature = "low-power")]
384impl<'a> StoppablePeripheral for StopMode {
385 fn stop_mode(&self) -> StopMode {
386 *self
387 }
388}
389
390pub(crate) struct BusyPeripheral<T: StoppablePeripheral> {
391 peripheral: T,
392}
393
394impl<T: StoppablePeripheral> BusyPeripheral<T> {
395 pub fn new(peripheral: T) -> Self {
396 #[cfg(feature = "low-power")]
397 critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode()));
398
399 Self { peripheral }
400 }
401}
402
403impl<T: StoppablePeripheral> Drop for BusyPeripheral<T> {
404 fn drop(&mut self) {
405 #[cfg(feature = "low-power")]
406 critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode()));
407 }
408}
409
410impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> {
411 type Target = T;
412
413 fn deref(&self) -> &Self::Target {
414 &self.peripheral
415 }
416}
417
418impl<T: StoppablePeripheral> ops::DerefMut for BusyPeripheral<T> {
419 fn deref_mut(&mut self) -> &mut Self::Target {
420 &mut self.peripheral
421 }
422}
423
329#[allow(unused)] 424#[allow(unused)]
330mod util { 425mod util {
331 use crate::time::Hertz; 426 use crate::time::Hertz;
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 0b75aef92..cfcf5f3fd 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -14,11 +14,11 @@ use stm32_metapac::timer::{TimGp16, regs};
14 14
15use crate::interrupt::typelevel::Interrupt; 15use crate::interrupt::typelevel::Interrupt;
16use crate::pac::timer::vals; 16use crate::pac::timer::vals;
17use crate::peripherals;
17use crate::rcc::{self, SealedRccPeripheral}; 18use crate::rcc::{self, SealedRccPeripheral};
18#[cfg(feature = "low-power")] 19#[cfg(feature = "low-power")]
19use crate::rtc::Rtc; 20use crate::rtc::Rtc;
20use crate::timer::{CoreInstance, GeneralInstance1Channel}; 21use crate::timer::{CoreInstance, GeneralInstance1Channel};
21use crate::{interrupt, peripherals};
22 22
23// NOTE regarding ALARM_COUNT: 23// NOTE regarding ALARM_COUNT:
24// 24//
@@ -56,121 +56,6 @@ type T = peripherals::TIM23;
56#[cfg(time_driver_tim24)] 56#[cfg(time_driver_tim24)]
57type T = peripherals::TIM24; 57type T = peripherals::TIM24;
58 58
59foreach_interrupt! {
60 (TIM1, timer, $block:ident, CC, $irq:ident) => {
61 #[cfg(time_driver_tim1)]
62 #[cfg(feature = "rt")]
63 #[interrupt]
64 fn $irq() {
65 DRIVER.on_interrupt()
66 }
67 };
68 (TIM2, timer, $block:ident, CC, $irq:ident) => {
69 #[cfg(time_driver_tim2)]
70 #[cfg(feature = "rt")]
71 #[interrupt]
72 fn $irq() {
73 DRIVER.on_interrupt()
74 }
75 };
76 (TIM3, timer, $block:ident, CC, $irq:ident) => {
77 #[cfg(time_driver_tim3)]
78 #[cfg(feature = "rt")]
79 #[interrupt]
80 fn $irq() {
81 DRIVER.on_interrupt()
82 }
83 };
84 (TIM4, timer, $block:ident, CC, $irq:ident) => {
85 #[cfg(time_driver_tim4)]
86 #[cfg(feature = "rt")]
87 #[interrupt]
88 fn $irq() {
89 DRIVER.on_interrupt()
90 }
91 };
92 (TIM5, timer, $block:ident, CC, $irq:ident) => {
93 #[cfg(time_driver_tim5)]
94 #[cfg(feature = "rt")]
95 #[interrupt]
96 fn $irq() {
97 DRIVER.on_interrupt()
98 }
99 };
100 (TIM8, timer, $block:ident, CC, $irq:ident) => {
101 #[cfg(time_driver_tim8)]
102 #[cfg(feature = "rt")]
103 #[interrupt]
104 fn $irq() {
105 DRIVER.on_interrupt()
106 }
107 };
108 (TIM9, timer, $block:ident, CC, $irq:ident) => {
109 #[cfg(time_driver_tim9)]
110 #[cfg(feature = "rt")]
111 #[interrupt]
112 fn $irq() {
113 DRIVER.on_interrupt()
114 }
115 };
116 (TIM12, timer, $block:ident, CC, $irq:ident) => {
117 #[cfg(time_driver_tim12)]
118 #[cfg(feature = "rt")]
119 #[interrupt]
120 fn $irq() {
121 DRIVER.on_interrupt()
122 }
123 };
124 (TIM15, timer, $block:ident, CC, $irq:ident) => {
125 #[cfg(time_driver_tim15)]
126 #[cfg(feature = "rt")]
127 #[interrupt]
128 fn $irq() {
129 DRIVER.on_interrupt()
130 }
131 };
132 (TIM20, timer, $block:ident, CC, $irq:ident) => {
133 #[cfg(time_driver_tim20)]
134 #[cfg(feature = "rt")]
135 #[interrupt]
136 fn $irq() {
137 DRIVER.on_interrupt()
138 }
139 };
140 (TIM21, timer, $block:ident, CC, $irq:ident) => {
141 #[cfg(time_driver_tim21)]
142 #[cfg(feature = "rt")]
143 #[interrupt]
144 fn $irq() {
145 DRIVER.on_interrupt()
146 }
147 };
148 (TIM22, timer, $block:ident, CC, $irq:ident) => {
149 #[cfg(time_driver_tim22)]
150 #[cfg(feature = "rt")]
151 #[interrupt]
152 fn $irq() {
153 DRIVER.on_interrupt()
154 }
155 };
156 (TIM23, timer, $block:ident, CC, $irq:ident) => {
157 #[cfg(time_driver_tim23)]
158 #[cfg(feature = "rt")]
159 #[interrupt]
160 fn $irq() {
161 DRIVER.on_interrupt()
162 }
163 };
164 (TIM24, timer, $block:ident, CC, $irq:ident) => {
165 #[cfg(time_driver_tim24)]
166 #[cfg(feature = "rt")]
167 #[interrupt]
168 fn $irq() {
169 DRIVER.on_interrupt()
170 }
171 };
172}
173
174fn regs_gp16() -> TimGp16 { 59fn regs_gp16() -> TimGp16 {
175 unsafe { TimGp16::from_ptr(T::regs()) } 60 unsafe { TimGp16::from_ptr(T::regs()) }
176} 61}
@@ -282,7 +167,11 @@ impl RtcDriver {
282 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); 167 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst)));
283 168
284 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); 169 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
285 unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; 170 <T as CoreInstance>::UpdateInterrupt::unpend();
171 unsafe {
172 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable();
173 <T as CoreInstance>::UpdateInterrupt::enable();
174 }
286 } 175 }
287 176
288 fn init(&'static self, cs: CriticalSection) { 177 fn init(&'static self, cs: CriticalSection) {
@@ -290,7 +179,7 @@ impl RtcDriver {
290 regs_gp16().cr1().modify(|w| w.set_cen(true)); 179 regs_gp16().cr1().modify(|w| w.set_cen(true));
291 } 180 }
292 181
293 fn on_interrupt(&self) { 182 pub(crate) fn on_interrupt(&self) {
294 let r = regs_gp16(); 183 let r = regs_gp16();
295 184
296 critical_section::with(|cs| { 185 critical_section::with(|cs| {
@@ -508,7 +397,6 @@ impl Driver for RtcDriver {
508 } 397 }
509} 398}
510 399
511#[cfg(feature = "low-power")]
512pub(crate) const fn get_driver() -> &'static RtcDriver { 400pub(crate) const fn get_driver() -> &'static RtcDriver {
513 &DRIVER 401 &DRIVER
514} 402}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 0e7da634d..8047d6005 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -491,6 +491,8 @@ impl<'d> UartTx<'d, Async> {
491 491
492 /// Initiate an asynchronous UART write 492 /// Initiate an asynchronous UART write
493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { 493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
494 let _ = self.info.rcc.block_stop();
495
494 let r = self.info.regs; 496 let r = self.info.regs;
495 497
496 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); 498 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));
@@ -508,6 +510,8 @@ impl<'d> UartTx<'d, Async> {
508 510
509 /// Wait until transmission complete 511 /// Wait until transmission complete
510 pub async fn flush(&mut self) -> Result<(), Error> { 512 pub async fn flush(&mut self) -> Result<(), Error> {
513 let _ = self.info.rcc.block_stop();
514
511 flush(&self.info, &self.state).await 515 flush(&self.info, &self.state).await
512 } 516 }
513} 517}
@@ -569,7 +573,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
569 let state = self.state; 573 let state = self.state;
570 state.tx_rx_refcount.store(1, Ordering::Relaxed); 574 state.tx_rx_refcount.store(1, Ordering::Relaxed);
571 575
572 info.rcc.enable_and_reset(); 576 info.rcc.enable_and_reset_without_stop();
573 577
574 info.regs.cr3().modify(|w| { 578 info.regs.cr3().modify(|w| {
575 w.set_ctse(self.cts.is_some()); 579 w.set_ctse(self.cts.is_some());
@@ -726,6 +730,8 @@ impl<'d> UartRx<'d, Async> {
726 730
727 /// Initiate an asynchronous UART read 731 /// Initiate an asynchronous UART read
728 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 732 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
733 let _ = self.info.rcc.block_stop();
734
729 self.inner_read(buffer, false).await?; 735 self.inner_read(buffer, false).await?;
730 736
731 Ok(()) 737 Ok(())
@@ -733,6 +739,8 @@ impl<'d> UartRx<'d, Async> {
733 739
734 /// Initiate an asynchronous read with idle line detection enabled 740 /// Initiate an asynchronous read with idle line detection enabled
735 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 741 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
742 let _ = self.info.rcc.block_stop();
743
736 self.inner_read(buffer, true).await 744 self.inner_read(buffer, true).await
737 } 745 }
738 746
@@ -1004,7 +1012,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
1004 .eager_reads 1012 .eager_reads
1005 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); 1013 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
1006 1014
1007 info.rcc.enable_and_reset(); 1015 info.rcc.enable_and_reset_without_stop();
1008 1016
1009 info.regs.cr3().write(|w| { 1017 info.regs.cr3().write(|w| {
1010 w.set_rtse(self.rts.is_some()); 1018 w.set_rtse(self.rts.is_some());
@@ -1143,7 +1151,7 @@ fn drop_tx_rx(info: &Info, state: &State) {
1143 refcount == 1 1151 refcount == 1
1144 }); 1152 });
1145 if is_last_drop { 1153 if is_last_drop {
1146 info.rcc.disable(); 1154 info.rcc.disable_without_stop();
1147 } 1155 }
1148} 1156}
1149 1157
@@ -1506,7 +1514,7 @@ impl<'d, M: Mode> Uart<'d, M> {
1506 .eager_reads 1514 .eager_reads
1507 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); 1515 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
1508 1516
1509 info.rcc.enable_and_reset(); 1517 info.rcc.enable_and_reset_without_stop();
1510 1518
1511 info.regs.cr3().write(|w| { 1519 info.regs.cr3().write(|w| {
1512 w.set_rtse(self.rx.rts.is_some()); 1520 w.set_rtse(self.rx.rts.is_some());
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs
index bac570d27..cc5224b69 100644
--- a/embassy-stm32/src/usart/ringbuffered.rs
+++ b/embassy-stm32/src/usart/ringbuffered.rs
@@ -117,6 +117,8 @@ impl<'d> UartRx<'d, Async> {
117 let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; 117 let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) };
118 let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; 118 let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) };
119 119
120 info.rcc.increment_stop_refcount();
121
120 // Don't disable the clock 122 // Don't disable the clock
121 mem::forget(self); 123 mem::forget(self);
122 124
@@ -324,6 +326,7 @@ impl<'d> RingBufferedUartRx<'d> {
324 326
325impl Drop for RingBufferedUartRx<'_> { 327impl Drop for RingBufferedUartRx<'_> {
326 fn drop(&mut self) { 328 fn drop(&mut self) {
329 self.info.rcc.decrement_stop_refcount();
327 self.stop_uart(); 330 self.stop_uart();
328 self.rx.as_ref().map(|x| x.set_as_disconnected()); 331 self.rx.as_ref().map(|x| x.set_as_disconnected());
329 self.rts.as_ref().map(|x| x.set_as_disconnected()); 332 self.rts.as_ref().map(|x| x.set_as_disconnected());