aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-26 08:44:03 -0600
committerGitHub <[email protected]>2025-11-26 08:44:03 -0600
commit1045738fa3e2f2f6b2968d4b35a4b618e6235d2e (patch)
tree6cf0abaca5f946cac1598f34d8f98a922f6c10e6 /embassy-stm32
parent3ba8bb866a19a09f25e0b21419a068fd765a9033 (diff)
parent9fa4f7309895bab81eb0e398d8f457ee528aad69 (diff)
Merge branch 'main' into time
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/CHANGELOG.md6
-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/hspi/mod.rs93
-rw-r--r--embassy-stm32/src/i2c/v2.rs11
-rw-r--r--embassy-stm32/src/low_power.rs22
-rw-r--r--embassy-stm32/src/ospi/mod.rs60
-rw-r--r--embassy-stm32/src/rcc/mod.rs111
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs17
-rw-r--r--embassy-stm32/src/timer/low_level.rs207
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs169
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs45
-rw-r--r--embassy-stm32/src/usart/mod.rs16
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs3
-rw-r--r--embassy-stm32/src/xspi/mod.rs98
18 files changed, 593 insertions, 352 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 0f19b14b3..4c38b0add 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- fix: fix incorrect handling of split interrupts in timer driver 10- fix: fix incorrect handling of split interrupts in timer driver
11- feat: allow granular stop for regular usart
12- feat: Add continuous waveform method to SimplePWM
13- change: remove waveform timer method
11- change: low power: store stop mode for dma channels 14- change: low power: store stop mode for dma channels
12- fix: Fixed ADC4 enable() for WBA 15- fix: Fixed ADC4 enable() for WBA
13- feat: allow use of anyadcchannel for adc4 16- feat: allow use of anyadcchannel for adc4
@@ -41,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41- feat: Configurable gpio speed for QSPI 44- feat: Configurable gpio speed for QSPI
42- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs 45- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs
43- fix: handle address and data-length errors in OSPI 46- fix: handle address and data-length errors in OSPI
44- feat: Allow OSPI DMA writes larger than 64kB using chunking 47- feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking
45- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times 48- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times
46- feat: Add USB CRS sync support for STM32C071 49- feat: Add USB CRS sync support for STM32C071
47- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. 50- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map.
@@ -52,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) 55- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
53- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options 56- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
54- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer 57- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
58- fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits.
55- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached 59- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached
56- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) 60- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
57- change: timer: added output compare values 61- change: timer: added output compare values
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/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs
index 69baa708e..1d3560678 100644
--- a/embassy-stm32/src/hspi/mod.rs
+++ b/embassy-stm32/src/hspi/mod.rs
@@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
391 while T::REGS.sr().read().busy() {} 391 while T::REGS.sr().read().busy() {}
392 392
393 T::REGS.cr().modify(|w| { 393 T::REGS.cr().modify(|w| {
394 w.set_fmode(0.into()); 394 w.set_fmode(FunctionalMode::IndirectWrite.into());
395 }); 395 });
396 396
397 // Configure alternate bytes 397 // Configure alternate bytes
@@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
498 w.set_dmaen(false); 498 w.set_dmaen(false);
499 }); 499 });
500 500
501 self.configure_command(&transaction, Some(buf.len()))?; 501 let transfer_size_bytes = buf.len() * W::size().bytes();
502 self.configure_command(&transaction, Some(transfer_size_bytes))?;
502 503
503 let current_address = T::REGS.ar().read().address(); 504 let current_address = T::REGS.ar().read().address();
504 let current_instruction = T::REGS.ir().read().instruction(); 505 let current_instruction = T::REGS.ir().read().instruction();
@@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
537 w.set_dmaen(false); 538 w.set_dmaen(false);
538 }); 539 });
539 540
540 self.configure_command(&transaction, Some(buf.len()))?; 541 let transfer_size_bytes = buf.len() * W::size().bytes();
542 self.configure_command(&transaction, Some(transfer_size_bytes))?;
541 543
542 T::REGS 544 T::REGS
543 .cr() 545 .cr()
@@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
767 // Wait for peripheral to be free 769 // Wait for peripheral to be free
768 while T::REGS.sr().read().busy() {} 770 while T::REGS.sr().read().busy() {}
769 771
770 self.configure_command(&transaction, Some(buf.len()))?; 772 let transfer_size_bytes = buf.len() * W::size().bytes();
773 self.configure_command(&transaction, Some(transfer_size_bytes))?;
771 774
772 let current_address = T::REGS.ar().read().address(); 775 let current_address = T::REGS.ar().read().address();
773 let current_instruction = T::REGS.ir().read().instruction(); 776 let current_instruction = T::REGS.ir().read().instruction();
@@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
782 T::REGS.ar().write(|v| v.set_address(current_address)); 785 T::REGS.ar().write(|v| v.set_address(current_address));
783 } 786 }
784 787
785 let transfer = unsafe { 788 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
786 self.dma 789 let transfer = unsafe {
787 .as_mut() 790 self.dma
788 .unwrap() 791 .as_mut()
789 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 792 .unwrap()
790 }; 793 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
794 };
791 795
792 T::REGS.cr().modify(|w| w.set_dmaen(true)); 796 T::REGS.cr().modify(|w| w.set_dmaen(true));
793 797
794 transfer.blocking_wait(); 798 transfer.blocking_wait();
799 }
795 800
796 finish_dma(T::REGS); 801 finish_dma(T::REGS);
797 802
@@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
807 // Wait for peripheral to be free 812 // Wait for peripheral to be free
808 while T::REGS.sr().read().busy() {} 813 while T::REGS.sr().read().busy() {}
809 814
810 self.configure_command(&transaction, Some(buf.len()))?; 815 let transfer_size_bytes = buf.len() * W::size().bytes();
816 self.configure_command(&transaction, Some(transfer_size_bytes))?;
811 T::REGS 817 T::REGS
812 .cr() 818 .cr()
813 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); 819 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
814 820
815 let transfer = unsafe { 821 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
816 self.dma 822 let transfer = unsafe {
817 .as_mut() 823 self.dma
818 .unwrap() 824 .as_mut()
819 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 825 .unwrap()
820 }; 826 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
827 };
821 828
822 T::REGS.cr().modify(|w| w.set_dmaen(true)); 829 T::REGS.cr().modify(|w| w.set_dmaen(true));
823 830
824 transfer.blocking_wait(); 831 transfer.blocking_wait();
832 }
825 833
826 finish_dma(T::REGS); 834 finish_dma(T::REGS);
827 835
@@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
837 // Wait for peripheral to be free 845 // Wait for peripheral to be free
838 while T::REGS.sr().read().busy() {} 846 while T::REGS.sr().read().busy() {}
839 847
840 self.configure_command(&transaction, Some(buf.len()))?; 848 let transfer_size_bytes = buf.len() * W::size().bytes();
849 self.configure_command(&transaction, Some(transfer_size_bytes))?;
841 850
842 let current_address = T::REGS.ar().read().address(); 851 let current_address = T::REGS.ar().read().address();
843 let current_instruction = T::REGS.ir().read().instruction(); 852 let current_instruction = T::REGS.ir().read().instruction();
@@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
852 T::REGS.ar().write(|v| v.set_address(current_address)); 861 T::REGS.ar().write(|v| v.set_address(current_address));
853 } 862 }
854 863
855 let transfer = unsafe { 864 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
856 self.dma 865 let transfer = unsafe {
857 .as_mut() 866 self.dma
858 .unwrap() 867 .as_mut()
859 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 868 .unwrap()
860 }; 869 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
870 };
861 871
862 T::REGS.cr().modify(|w| w.set_dmaen(true)); 872 T::REGS.cr().modify(|w| w.set_dmaen(true));
863 873
864 transfer.await; 874 transfer.await;
875 }
865 876
866 finish_dma(T::REGS); 877 finish_dma(T::REGS);
867 878
@@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
877 // Wait for peripheral to be free 888 // Wait for peripheral to be free
878 while T::REGS.sr().read().busy() {} 889 while T::REGS.sr().read().busy() {}
879 890
880 self.configure_command(&transaction, Some(buf.len()))?; 891 let transfer_size_bytes = buf.len() * W::size().bytes();
892 self.configure_command(&transaction, Some(transfer_size_bytes))?;
881 T::REGS 893 T::REGS
882 .cr() 894 .cr()
883 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); 895 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
884 896
885 let transfer = unsafe { 897 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
886 self.dma 898 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
887 .as_mut() 899 let transfer = unsafe {
888 .unwrap() 900 self.dma
889 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 901 .as_mut()
890 }; 902 .unwrap()
903 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
904 };
891 905
892 T::REGS.cr().modify(|w| w.set_dmaen(true)); 906 T::REGS.cr().modify(|w| w.set_dmaen(true));
893 907
894 transfer.await; 908 transfer.await;
909 }
895 910
896 finish_dma(T::REGS); 911 finish_dma(T::REGS);
897 912
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 61e550ad4..b2ba94e21 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1075,8 +1075,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1075 1075
1076 /// Write. 1076 /// Write.
1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
1078 #[cfg(all(feature = "low-power", stm32wlex))]
1079 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1080 let timeout = self.timeout(); 1078 let timeout = self.timeout();
1081 if write.is_empty() { 1079 if write.is_empty() {
1082 self.write_internal(address.into(), write, true, timeout) 1080 self.write_internal(address.into(), write, true, timeout)
@@ -1091,8 +1089,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1091 /// 1089 ///
1092 /// The buffers are concatenated in a single write transaction. 1090 /// The buffers are concatenated in a single write transaction.
1093 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { 1091 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
1094 #[cfg(all(feature = "low-power", stm32wlex))]
1095 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1096 let timeout = self.timeout(); 1092 let timeout = self.timeout();
1097 1093
1098 if write.is_empty() { 1094 if write.is_empty() {
@@ -1124,8 +1120,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1124 1120
1125 /// Read. 1121 /// Read.
1126 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 1122 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
1127 #[cfg(all(feature = "low-power", stm32wlex))]
1128 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1129 let timeout = self.timeout(); 1123 let timeout = self.timeout();
1130 1124
1131 if buffer.is_empty() { 1125 if buffer.is_empty() {
@@ -1138,8 +1132,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1138 1132
1139 /// Write, restart, read. 1133 /// Write, restart, read.
1140 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 1134 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
1141 #[cfg(all(feature = "low-power", stm32wlex))]
1142 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1143 let timeout = self.timeout(); 1135 let timeout = self.timeout();
1144 1136
1145 if write.is_empty() { 1137 if write.is_empty() {
@@ -1165,9 +1157,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1165 /// 1157 ///
1166 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 1158 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
1167 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 1159 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
1168 #[cfg(all(feature = "low-power", stm32wlex))]
1169 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1170
1171 if operations.is_empty() { 1160 if operations.is_empty() {
1172 return Err(Error::ZeroLengthTransfer); 1161 return Err(Error::ZeroLengthTransfer);
1173 } 1162 }
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/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 592a8594a..2d5dbd95a 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
451 } 451 }
452 452
453 T::REGS.cr().modify(|w| { 453 T::REGS.cr().modify(|w| {
454 w.set_fmode(0.into()); 454 w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE);
455 }); 455 });
456 456
457 // Configure alternate bytes 457 // Configure alternate bytes
@@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
577 w.set_dmaen(false); 577 w.set_dmaen(false);
578 }); 578 });
579 579
580 self.configure_command(&transaction, Some(buf.len()))?; 580 let transfer_size_bytes = buf.len() * W::size().bytes();
581 self.configure_command(&transaction, Some(transfer_size_bytes))?;
581 582
582 let current_address = T::REGS.ar().read().address(); 583 let current_address = T::REGS.ar().read().address();
583 let current_instruction = T::REGS.ir().read().instruction(); 584 let current_instruction = T::REGS.ir().read().instruction();
@@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
616 w.set_dmaen(false); 617 w.set_dmaen(false);
617 }); 618 });
618 619
619 self.configure_command(&transaction, Some(buf.len()))?; 620 let transfer_size_bytes = buf.len() * W::size().bytes();
621 self.configure_command(&transaction, Some(transfer_size_bytes))?;
620 622
621 T::REGS 623 T::REGS
622 .cr() 624 .cr()
@@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1153 // Wait for peripheral to be free 1155 // Wait for peripheral to be free
1154 while T::REGS.sr().read().busy() {} 1156 while T::REGS.sr().read().busy() {}
1155 1157
1156 self.configure_command(&transaction, Some(buf.len()))?; 1158 let transfer_size_bytes = buf.len() * W::size().bytes();
1159 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1157 1160
1158 let current_address = T::REGS.ar().read().address(); 1161 let current_address = T::REGS.ar().read().address();
1159 let current_instruction = T::REGS.ir().read().instruction(); 1162 let current_instruction = T::REGS.ir().read().instruction();
@@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1168 T::REGS.ar().write(|v| v.set_address(current_address)); 1171 T::REGS.ar().write(|v| v.set_address(current_address));
1169 } 1172 }
1170 1173
1171 let transfer = unsafe { 1174 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1172 self.dma 1175 let transfer = unsafe {
1173 .as_mut() 1176 self.dma
1174 .unwrap() 1177 .as_mut()
1175 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1178 .unwrap()
1176 }; 1179 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1180 };
1177 1181
1178 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1182 T::REGS.cr().modify(|w| w.set_dmaen(true));
1179 1183
1180 transfer.blocking_wait(); 1184 transfer.blocking_wait();
1185 }
1181 1186
1182 finish_dma(T::REGS); 1187 finish_dma(T::REGS);
1183 1188
@@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1193 // Wait for peripheral to be free 1198 // Wait for peripheral to be free
1194 while T::REGS.sr().read().busy() {} 1199 while T::REGS.sr().read().busy() {}
1195 1200
1196 self.configure_command(&transaction, Some(buf.len()))?; 1201 let transfer_size_bytes = buf.len() * W::size().bytes();
1202 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1197 T::REGS 1203 T::REGS
1198 .cr() 1204 .cr()
1199 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1205 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1200 1206
1201 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. 1207 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1202 for chunk in buf.chunks(0xFFFF) { 1208 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1203 let transfer = unsafe { 1209 let transfer = unsafe {
1204 self.dma 1210 self.dma
1205 .as_mut() 1211 .as_mut()
@@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1226 // Wait for peripheral to be free 1232 // Wait for peripheral to be free
1227 while T::REGS.sr().read().busy() {} 1233 while T::REGS.sr().read().busy() {}
1228 1234
1229 self.configure_command(&transaction, Some(buf.len()))?; 1235 let transfer_size_bytes = buf.len() * W::size().bytes();
1236 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1230 1237
1231 let current_address = T::REGS.ar().read().address(); 1238 let current_address = T::REGS.ar().read().address();
1232 let current_instruction = T::REGS.ir().read().instruction(); 1239 let current_instruction = T::REGS.ir().read().instruction();
@@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1241 T::REGS.ar().write(|v| v.set_address(current_address)); 1248 T::REGS.ar().write(|v| v.set_address(current_address));
1242 } 1249 }
1243 1250
1244 let transfer = unsafe { 1251 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1245 self.dma 1252 let transfer = unsafe {
1246 .as_mut() 1253 self.dma
1247 .unwrap() 1254 .as_mut()
1248 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1255 .unwrap()
1249 }; 1256 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1257 };
1250 1258
1251 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1259 T::REGS.cr().modify(|w| w.set_dmaen(true));
1252 1260
1253 transfer.await; 1261 transfer.await;
1262 }
1254 1263
1255 finish_dma(T::REGS); 1264 finish_dma(T::REGS);
1256 1265
@@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1266 // Wait for peripheral to be free 1275 // Wait for peripheral to be free
1267 while T::REGS.sr().read().busy() {} 1276 while T::REGS.sr().read().busy() {}
1268 1277
1269 self.configure_command(&transaction, Some(buf.len()))?; 1278 let transfer_size_bytes = buf.len() * W::size().bytes();
1279 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1270 T::REGS 1280 T::REGS
1271 .cr() 1281 .cr()
1272 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1282 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1273 1283
1274 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. 1284 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1275 for chunk in buf.chunks(0xFFFF) { 1285 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1276 let transfer = unsafe { 1286 let transfer = unsafe {
1277 self.dma 1287 self.dma
1278 .as_mut() 1288 .as_mut()
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/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 6d4c70dff..77f19a37b 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -220,9 +220,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
220 /// 220 ///
221 /// Note: 221 /// Note:
222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
223 #[inline(always)]
224 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 223 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
225 self.inner.waveform_up(dma, channel, duty).await 224 self.inner.enable_channel(channel, true);
225 self.inner.enable_update_dma(true);
226 self.inner.setup_update_dma(dma, channel, duty).await;
227 self.inner.enable_update_dma(false);
226 } 228 }
227 229
228 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 230 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -254,7 +256,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
254 /// Also be aware that embassy timers use one of timers internally. It is possible to 256 /// Also be aware that embassy timers use one of timers internally. It is possible to
255 /// switch this timer by using `time-driver-timX` feature. 257 /// switch this timer by using `time-driver-timX` feature.
256 /// 258 ///
257 #[inline(always)]
258 pub async fn waveform_up_multi_channel( 259 pub async fn waveform_up_multi_channel(
259 &mut self, 260 &mut self,
260 dma: Peri<'_, impl super::UpDma<T>>, 261 dma: Peri<'_, impl super::UpDma<T>>,
@@ -262,15 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
262 ending_channel: Channel, 263 ending_channel: Channel,
263 duty: &[u16], 264 duty: &[u16],
264 ) { 265 ) {
266 self.inner.enable_update_dma(true);
265 self.inner 267 self.inner
266 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 268 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
267 .await; 269 .await;
268 } 270 self.inner.enable_update_dma(false);
269
270 /// Generate a sequence of PWM waveform
271 #[inline(always)]
272 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
273 self.inner.waveform(dma, duty).await;
274 } 271 }
275} 272}
276 273
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index f0105ece8..aba08081f 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -13,9 +13,10 @@ use embassy_hal_internal::Peri;
13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; 13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};
14 14
15use super::*; 15use super::*;
16use crate::dma::{Transfer, WritableRingBuffer};
16use crate::pac::timer::vals; 17use crate::pac::timer::vals;
18use crate::rcc;
17use crate::time::Hertz; 19use crate::time::Hertz;
18use crate::{dma, rcc};
19 20
20/// Input capture mode. 21/// Input capture mode.
21#[derive(Clone, Copy)] 22#[derive(Clone, Copy)]
@@ -659,29 +660,88 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
659 } 660 }
660 } 661 }
661 662
663 /// Setup a ring buffer for the channel
664 pub fn setup_ring_buffer<'a>(
665 &mut self,
666 dma: Peri<'a, impl super::UpDma<T>>,
667 channel: Channel,
668 dma_buf: &'a mut [u16],
669 ) -> WritableRingBuffer<'a, u16> {
670 #[allow(clippy::let_unit_value)] // eg. stm32f334
671 let req = dma.request();
672
673 unsafe {
674 use crate::dma::TransferOptions;
675 #[cfg(not(any(bdma, gpdma)))]
676 use crate::dma::{Burst, FifoThreshold};
677
678 let dma_transfer_option = TransferOptions {
679 #[cfg(not(any(bdma, gpdma)))]
680 fifo_threshold: Some(FifoThreshold::Full),
681 #[cfg(not(any(bdma, gpdma)))]
682 mburst: Burst::Incr8,
683 ..Default::default()
684 };
685
686 WritableRingBuffer::new(
687 dma,
688 req,
689 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
690 dma_buf,
691 dma_transfer_option,
692 )
693 }
694 }
695
662 /// Generate a sequence of PWM waveform 696 /// Generate a sequence of PWM waveform
663 /// 697 ///
664 /// Note: 698 /// Note:
665 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 699 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
666 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 700 pub fn setup_update_dma<'a>(
701 &mut self,
702 dma: Peri<'a, impl super::UpDma<T>>,
703 channel: Channel,
704 duty: &'a [u16],
705 ) -> Transfer<'a> {
667 #[allow(clippy::let_unit_value)] // eg. stm32f334 706 #[allow(clippy::let_unit_value)] // eg. stm32f334
668 let req = dma.request(); 707 let req = dma.request();
669 708
670 let original_update_dma_state = self.get_update_dma_state(); 709 unsafe {
710 #[cfg(not(any(bdma, gpdma)))]
711 use crate::dma::{Burst, FifoThreshold};
712 use crate::dma::{Transfer, TransferOptions};
671 713
672 if !original_update_dma_state { 714 let dma_transfer_option = TransferOptions {
673 self.enable_update_dma(true); 715 #[cfg(not(any(bdma, gpdma)))]
674 } 716 fifo_threshold: Some(FifoThreshold::Full),
717 #[cfg(not(any(bdma, gpdma)))]
718 mburst: Burst::Incr8,
719 ..Default::default()
720 };
675 721
676 self.waveform_helper(dma, req, channel, duty).await; 722 match self.bits() {
723 TimerBits::Bits16 => Transfer::new_write(
724 dma,
725 req,
726 duty,
727 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
728 dma_transfer_option,
729 ),
730 #[cfg(not(any(stm32l0)))]
731 TimerBits::Bits32 => {
732 #[cfg(not(any(bdma, gpdma)))]
733 panic!("unsupported timer bits");
677 734
678 // Since DMA is closed before timer update event trigger DMA is turn off, 735 #[cfg(any(bdma, gpdma))]
679 // this can almost always trigger a DMA FIFO error. 736 Transfer::new_write(
680 // 737 dma,
681 // optional TODO: 738 req,
682 // clean FEIF after disable UDE 739 duty,
683 if !original_update_dma_state { 740 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
684 self.enable_update_dma(false); 741 dma_transfer_option,
742 )
743 }
744 }
685 } 745 }
686 } 746 }
687 747
@@ -714,13 +774,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
714 /// Also be aware that embassy timers use one of timers internally. It is possible to 774 /// Also be aware that embassy timers use one of timers internally. It is possible to
715 /// switch this timer by using `time-driver-timX` feature. 775 /// switch this timer by using `time-driver-timX` feature.
716 /// 776 ///
717 pub async fn waveform_up_multi_channel( 777 pub fn setup_update_dma_burst<'a>(
718 &mut self, 778 &mut self,
719 dma: Peri<'_, impl super::UpDma<T>>, 779 dma: Peri<'a, impl super::UpDma<T>>,
720 starting_channel: Channel, 780 starting_channel: Channel,
721 ending_channel: Channel, 781 ending_channel: Channel,
722 duty: &[u16], 782 duty: &'a [u16],
723 ) { 783 ) -> Transfer<'a> {
724 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; 784 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
725 let start_ch_index = starting_channel.index(); 785 let start_ch_index = starting_channel.index();
726 let end_ch_index = ending_channel.index(); 786 let end_ch_index = ending_channel.index();
@@ -738,11 +798,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
738 #[allow(clippy::let_unit_value)] // eg. stm32f334 798 #[allow(clippy::let_unit_value)] // eg. stm32f334
739 let req = dma.request(); 799 let req = dma.request();
740 800
741 let original_update_dma_state = self.get_update_dma_state();
742 if !original_update_dma_state {
743 self.enable_update_dma(true);
744 }
745
746 unsafe { 801 unsafe {
747 #[cfg(not(any(bdma, gpdma)))] 802 #[cfg(not(any(bdma, gpdma)))]
748 use crate::dma::{Burst, FifoThreshold}; 803 use crate::dma::{Burst, FifoThreshold};
@@ -763,115 +818,9 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
763 self.regs_gp16().dmar().as_ptr() as *mut u16, 818 self.regs_gp16().dmar().as_ptr() as *mut u16,
764 dma_transfer_option, 819 dma_transfer_option,
765 ) 820 )
766 .await
767 };
768
769 if !original_update_dma_state {
770 self.enable_update_dma(false);
771 } 821 }
772 } 822 }
773 823
774 /// Generate a sequence of PWM waveform
775 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
776 use crate::pac::timer::vals::Ccds;
777
778 #[allow(clippy::let_unit_value)] // eg. stm32f334
779 let req = dma.request();
780
781 let cc_channel = C::CHANNEL;
782
783 let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE;
784 let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel);
785
786 // redirect CC DMA request onto Update Event
787 if !original_cc_dma_on_update {
788 self.set_cc_dma_selection(Ccds::ON_UPDATE)
789 }
790
791 if !original_cc_dma_enabled {
792 self.set_cc_dma_enable_state(cc_channel, true);
793 }
794
795 self.waveform_helper(dma, req, cc_channel, duty).await;
796
797 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
798 // this can almost always trigger a DMA FIFO error.
799 //
800 // optional TODO:
801 // clean FEIF after disable UDE
802 if !original_cc_dma_enabled {
803 self.set_cc_dma_enable_state(cc_channel, false);
804 }
805
806 if !original_cc_dma_on_update {
807 self.set_cc_dma_selection(Ccds::ON_COMPARE)
808 }
809 }
810
811 async fn waveform_helper(
812 &mut self,
813 dma: Peri<'_, impl dma::Channel>,
814 req: dma::Request,
815 channel: Channel,
816 duty: &[u16],
817 ) {
818 let original_duty_state = self.get_compare_value(channel);
819 let original_enable_state = self.get_channel_enable_state(channel);
820
821 if !original_enable_state {
822 self.enable_channel(channel, true);
823 }
824
825 unsafe {
826 #[cfg(not(any(bdma, gpdma)))]
827 use crate::dma::{Burst, FifoThreshold};
828 use crate::dma::{Transfer, TransferOptions};
829
830 let dma_transfer_option = TransferOptions {
831 #[cfg(not(any(bdma, gpdma)))]
832 fifo_threshold: Some(FifoThreshold::Full),
833 #[cfg(not(any(bdma, gpdma)))]
834 mburst: Burst::Incr8,
835 ..Default::default()
836 };
837
838 match self.bits() {
839 TimerBits::Bits16 => {
840 Transfer::new_write(
841 dma,
842 req,
843 duty,
844 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
845 dma_transfer_option,
846 )
847 .await
848 }
849 #[cfg(not(any(stm32l0)))]
850 TimerBits::Bits32 => {
851 #[cfg(not(any(bdma, gpdma)))]
852 panic!("unsupported timer bits");
853
854 #[cfg(any(bdma, gpdma))]
855 Transfer::new_write(
856 dma,
857 req,
858 duty,
859 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
860 dma_transfer_option,
861 )
862 .await
863 }
864 };
865 };
866
867 // restore output compare state
868 if !original_enable_state {
869 self.enable_channel(channel, false);
870 }
871
872 self.set_compare_value(channel, original_duty_state);
873 }
874
875 /// Get capture value for a channel. 824 /// Get capture value for a channel.
876 pub fn get_capture_value(&self, channel: Channel) -> u32 { 825 pub fn get_capture_value(&self, channel: Channel) -> u32 {
877 self.get_compare_value(channel) 826 self.get_compare_value(channel)
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 804d1ef37..3fa363881 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -12,6 +12,7 @@ pub mod low_level;
12pub mod one_pulse; 12pub mod one_pulse;
13pub mod pwm_input; 13pub mod pwm_input;
14pub mod qei; 14pub mod qei;
15pub mod ringbuffered;
15pub mod simple_pwm; 16pub mod simple_pwm;
16 17
17use crate::interrupt; 18use crate::interrupt;
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs
new file mode 100644
index 000000000..e8f97bf59
--- /dev/null
+++ b/embassy-stm32/src/timer/ringbuffered.rs
@@ -0,0 +1,169 @@
1//! RingBuffered PWM driver.
2
3use core::mem::ManuallyDrop;
4use core::task::Waker;
5
6use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer;
9use crate::dma::ringbuffer::Error;
10
11/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.
12///
13/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU.
14/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling
15/// through duty cycle values stored in memory.
16///
17/// You can write new duty cycle values to the ring buffer while it's running, enabling
18/// dynamic waveform generation for applications like motor control, LED dimming, or audio output.
19///
20/// # Example
21/// ```ignore
22/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer);
23/// channel.start(); // Start DMA transfer
24/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles
25/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> {
27 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, u16>,
29 channel: Channel,
30}
31
32impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
33 pub(crate) fn new(
34 timer: ManuallyDrop<Timer<'d, T>>,
35 channel: Channel,
36 ring_buf: WritableRingBuffer<'d, u16>,
37 ) -> Self {
38 Self {
39 timer,
40 ring_buf,
41 channel,
42 }
43 }
44
45 /// Start the ring buffer operation.
46 ///
47 /// You must call this after creating it for it to work.
48 pub fn start(&mut self) {
49 self.ring_buf.start()
50 }
51
52 /// Clear all data in the ring buffer.
53 pub fn clear(&mut self) {
54 self.ring_buf.clear()
55 }
56
57 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer.
58 pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> {
59 self.ring_buf.write_immediate(buf)
60 }
61
62 /// Write elements from the ring buffer
63 /// Return a tuple of the length written and the length remaining in the buffer
64 pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> {
65 self.ring_buf.write(buf)
66 }
67
68 /// Write an exact number of elements to the ringbuffer.
69 pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> {
70 self.ring_buf.write_exact(buffer).await
71 }
72
73 /// Wait for any ring buffer write error.
74 pub async fn wait_write_error(&mut self) -> Result<usize, Error> {
75 self.ring_buf.wait_write_error().await
76 }
77
78 /// The current length of the ringbuffer
79 pub fn len(&mut self) -> Result<usize, Error> {
80 self.ring_buf.len()
81 }
82
83 /// The capacity of the ringbuffer
84 pub const fn capacity(&self) -> usize {
85 self.ring_buf.capacity()
86 }
87
88 /// Set a waker to be woken when at least one byte is send.
89 pub fn set_waker(&mut self, waker: &Waker) {
90 self.ring_buf.set_waker(waker)
91 }
92
93 /// Request the DMA to reset. The configuration for this channel will not be preserved.
94 ///
95 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
96 pub fn request_reset(&mut self) {
97 self.ring_buf.request_reset()
98 }
99
100 /// Request the transfer to pause, keeping the existing configuration for this channel.
101 /// To restart the transfer, call [`start`](Self::start) again.
102 ///
103 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
104 pub fn request_pause(&mut self) {
105 self.ring_buf.request_pause()
106 }
107
108 /// Return whether DMA is still running.
109 ///
110 /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop.
111 pub fn is_running(&mut self) -> bool {
112 self.ring_buf.is_running()
113 }
114
115 /// Stop the DMA transfer and await until the buffer is empty.
116 ///
117 /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written.
118 ///
119 /// This is designed to be used with streaming output data such as the I2S/SAI or DAC.
120 pub async fn stop(&mut self) {
121 self.ring_buf.stop().await
122 }
123
124 /// Enable the given channel.
125 pub fn enable(&mut self) {
126 self.timer.enable_channel(self.channel, true);
127 }
128
129 /// Disable the given channel.
130 pub fn disable(&mut self) {
131 self.timer.enable_channel(self.channel, false);
132 }
133
134 /// Check whether given channel is enabled
135 pub fn is_enabled(&self) -> bool {
136 self.timer.get_channel_enable_state(self.channel)
137 }
138
139 /// Get max duty value.
140 ///
141 /// This value depends on the configured frequency and the timer's clock rate from RCC.
142 pub fn max_duty_cycle(&self) -> u16 {
143 let max = self.timer.get_max_compare_value();
144 assert!(max < u16::MAX as u32);
145 max as u16 + 1
146 }
147
148 /// Set the output polarity for a given channel.
149 pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) {
150 self.timer.set_output_polarity(self.channel, polarity);
151 }
152
153 /// Set the output compare mode for a given channel.
154 pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) {
155 self.timer.set_output_compare_mode(self.channel, mode);
156 }
157}
158
159/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
160pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> {
161 /// Channel 1
162 pub ch1: RingBufferedPwmChannel<'d, T>,
163 /// Channel 2
164 pub ch2: RingBufferedPwmChannel<'d, T>,
165 /// Channel 3
166 pub ch3: RingBufferedPwmChannel<'d, T>,
167 /// Channel 4
168 pub ch4: RingBufferedPwmChannel<'d, T>,
169}
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 6c9ef17e0..484e9fd81 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,6 +4,7 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::ringbuffered::RingBufferedPwmChannel;
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; 8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 9use crate::Peri;
9#[cfg(gpio_v2)] 10#[cfg(gpio_v2)]
@@ -158,6 +159,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
158 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { 159 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
159 self.timer.set_output_compare_mode(self.channel, mode); 160 self.timer.set_output_compare_mode(self.channel, mode);
160 } 161 }
162
163 /// Convert this PWM channel into a ring-buffered PWM channel.
164 ///
165 /// This allows continuous PWM waveform generation using a DMA ring buffer.
166 /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking.
167 ///
168 /// # Arguments
169 /// * `tx_dma` - The DMA channel to use for transferring duty cycle values
170 /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements)
171 ///
172 /// # Panics
173 /// Panics if `dma_buf` is empty or longer than 65535 elements.
174 pub fn into_ring_buffered_channel(
175 mut self,
176 tx_dma: Peri<'d, impl super::UpDma<T>>,
177 dma_buf: &'d mut [u16],
178 ) -> RingBufferedPwmChannel<'d, T> {
179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
180
181 self.timer.enable_update_dma(true);
182
183 RingBufferedPwmChannel::new(
184 unsafe { self.timer.clone_unchecked() },
185 self.channel,
186 self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf),
187 )
188 }
161} 189}
162 190
163/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. 191/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
@@ -316,9 +344,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
316 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. 344 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
317 /// Also be aware that embassy timers use one of timers internally. It is possible to 345 /// Also be aware that embassy timers use one of timers internally. It is possible to
318 /// switch this timer by using `time-driver-timX` feature. 346 /// switch this timer by using `time-driver-timX` feature.
319 #[inline(always)]
320 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 347 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
321 self.inner.waveform_up(dma, channel, duty).await; 348 self.inner.enable_channel(channel, true);
349 self.inner.enable_update_dma(true);
350 self.inner.setup_update_dma(dma, channel, duty).await;
351 self.inner.enable_update_dma(false);
322 } 352 }
323 353
324 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 354 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -350,7 +380,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
350 /// Also be aware that embassy timers use one of timers internally. It is possible to 380 /// Also be aware that embassy timers use one of timers internally. It is possible to
351 /// switch this timer by using `time-driver-timX` feature. 381 /// switch this timer by using `time-driver-timX` feature.
352 /// 382 ///
353 #[inline(always)]
354 pub async fn waveform_up_multi_channel( 383 pub async fn waveform_up_multi_channel(
355 &mut self, 384 &mut self,
356 dma: Peri<'_, impl super::UpDma<T>>, 385 dma: Peri<'_, impl super::UpDma<T>>,
@@ -358,15 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
358 ending_channel: Channel, 387 ending_channel: Channel,
359 duty: &[u16], 388 duty: &[u16],
360 ) { 389 ) {
390 self.inner.enable_update_dma(true);
361 self.inner 391 self.inner
362 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 392 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
363 .await; 393 .await;
364 } 394 self.inner.enable_update_dma(false);
365
366 /// Generate a sequence of PWM waveform
367 #[inline(always)]
368 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
369 self.inner.waveform(dma, duty).await;
370 } 395 }
371} 396}
372 397
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());
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs
index a80a2692b..466e1a9b4 100644
--- a/embassy-stm32/src/xspi/mod.rs
+++ b/embassy-stm32/src/xspi/mod.rs
@@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
420 return Err(XspiError::InvalidCommand); 420 return Err(XspiError::InvalidCommand);
421 } 421 }
422 422
423 T::REGS.cr().modify(|w| { 423 T::REGS
424 w.set_fmode(0.into()); 424 .cr()
425 }); 425 .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
426 426
427 // Configure alternate bytes 427 // Configure alternate bytes
428 if let Some(ab) = command.alternate_bytes { 428 if let Some(ab) = command.alternate_bytes {
@@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
538 w.set_dmaen(false); 538 w.set_dmaen(false);
539 }); 539 });
540 540
541 // self.configure_command(&transaction, Some(buf.len()))?; 541 let transfer_size_bytes = buf.len() * W::size().bytes();
542 self.configure_command(&transaction, Some(buf.len())).unwrap(); 542 self.configure_command(&transaction, Some(transfer_size_bytes))?;
543 543
544 let current_address = T::REGS.ar().read().address(); 544 let current_address = T::REGS.ar().read().address();
545 let current_instruction = T::REGS.ir().read().instruction(); 545 let current_instruction = T::REGS.ir().read().instruction();
@@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
578 w.set_dmaen(false); 578 w.set_dmaen(false);
579 }); 579 });
580 580
581 self.configure_command(&transaction, Some(buf.len()))?; 581 let transfer_size_bytes = buf.len() * W::size().bytes();
582 self.configure_command(&transaction, Some(transfer_size_bytes))?;
582 583
583 T::REGS 584 T::REGS
584 .cr() 585 .cr()
@@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1145 // Wait for peripheral to be free 1146 // Wait for peripheral to be free
1146 while T::REGS.sr().read().busy() {} 1147 while T::REGS.sr().read().busy() {}
1147 1148
1148 self.configure_command(&transaction, Some(buf.len()))?; 1149 let transfer_size_bytes = buf.len() * W::size().bytes();
1150 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1149 1151
1150 let current_address = T::REGS.ar().read().address(); 1152 let current_address = T::REGS.ar().read().address();
1151 let current_instruction = T::REGS.ir().read().instruction(); 1153 let current_instruction = T::REGS.ir().read().instruction();
@@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1160 T::REGS.ar().write(|v| v.set_address(current_address)); 1162 T::REGS.ar().write(|v| v.set_address(current_address));
1161 } 1163 }
1162 1164
1163 let transfer = unsafe { 1165 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1164 self.dma 1166 let transfer = unsafe {
1165 .as_mut() 1167 self.dma
1166 .unwrap() 1168 .as_mut()
1167 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1169 .unwrap()
1168 }; 1170 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1171 };
1169 1172
1170 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1173 T::REGS.cr().modify(|w| w.set_dmaen(true));
1171 1174
1172 transfer.blocking_wait(); 1175 transfer.blocking_wait();
1176 }
1173 1177
1174 finish_dma(T::REGS); 1178 finish_dma(T::REGS);
1175 1179
@@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1185 // Wait for peripheral to be free 1189 // Wait for peripheral to be free
1186 while T::REGS.sr().read().busy() {} 1190 while T::REGS.sr().read().busy() {}
1187 1191
1188 self.configure_command(&transaction, Some(buf.len()))?; 1192 let transfer_size_bytes = buf.len() * W::size().bytes();
1193 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1189 T::REGS 1194 T::REGS
1190 .cr() 1195 .cr()
1191 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); 1196 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1192 1197
1193 let transfer = unsafe { 1198 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1194 self.dma 1199 let transfer = unsafe {
1195 .as_mut() 1200 self.dma
1196 .unwrap() 1201 .as_mut()
1197 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1202 .unwrap()
1198 }; 1203 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1204 };
1199 1205
1200 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1206 T::REGS.cr().modify(|w| w.set_dmaen(true));
1201 1207
1202 transfer.blocking_wait(); 1208 transfer.blocking_wait();
1209 }
1203 1210
1204 finish_dma(T::REGS); 1211 finish_dma(T::REGS);
1205 1212
@@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1215 // Wait for peripheral to be free 1222 // Wait for peripheral to be free
1216 while T::REGS.sr().read().busy() {} 1223 while T::REGS.sr().read().busy() {}
1217 1224
1218 self.configure_command(&transaction, Some(buf.len()))?; 1225 let transfer_size_bytes = buf.len() * W::size().bytes();
1226 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1219 1227
1220 let current_address = T::REGS.ar().read().address(); 1228 let current_address = T::REGS.ar().read().address();
1221 let current_instruction = T::REGS.ir().read().instruction(); 1229 let current_instruction = T::REGS.ir().read().instruction();
@@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1230 T::REGS.ar().write(|v| v.set_address(current_address)); 1238 T::REGS.ar().write(|v| v.set_address(current_address));
1231 } 1239 }
1232 1240
1233 let transfer = unsafe { 1241 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1234 self.dma 1242 let transfer = unsafe {
1235 .as_mut() 1243 self.dma
1236 .unwrap() 1244 .as_mut()
1237 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1245 .unwrap()
1238 }; 1246 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1247 };
1239 1248
1240 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1249 T::REGS.cr().modify(|w| w.set_dmaen(true));
1241 1250
1242 transfer.await; 1251 transfer.await;
1252 }
1243 1253
1244 finish_dma(T::REGS); 1254 finish_dma(T::REGS);
1245 1255
@@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1255 // Wait for peripheral to be free 1265 // Wait for peripheral to be free
1256 while T::REGS.sr().read().busy() {} 1266 while T::REGS.sr().read().busy() {}
1257 1267
1258 self.configure_command(&transaction, Some(buf.len()))?; 1268 let transfer_size_bytes = buf.len() * W::size().bytes();
1269 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1259 T::REGS 1270 T::REGS
1260 .cr() 1271 .cr()
1261 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); 1272 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1262 1273
1263 let transfer = unsafe { 1274 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1264 self.dma 1275 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1265 .as_mut() 1276 let transfer = unsafe {
1266 .unwrap() 1277 self.dma
1267 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1278 .as_mut()
1268 }; 1279 .unwrap()
1280 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1281 };
1269 1282
1270 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1283 T::REGS.cr().modify(|w| w.set_dmaen(true));
1271 1284
1272 transfer.await; 1285 transfer.await;
1286 }
1273 1287
1274 finish_dma(T::REGS); 1288 finish_dma(T::REGS);
1275 1289