aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYannick Steiner <[email protected]>2025-11-06 17:18:43 +0100
committerGitHub <[email protected]>2025-11-06 17:18:43 +0100
commitfbdaade0279658e8e480133b5d7909c490b6a49c (patch)
treec903b3f161399d4776df232c3ef03aaf7809c483
parent1f2d953b5662fadecad1e63e9e17efdd881e7d0d (diff)
parent11302ba0db44edcb20680376f847ae14e997ea33 (diff)
Merge branch 'main' into stm32-dsi-cmd
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/i2c/v2.rs4
-rw-r--r--embassy-stm32/src/i2s.rs3
-rw-r--r--embassy-stm32/src/spi/mod.rs147
-rw-r--r--embassy-usb-logger/CHANGELOG.md2
-rw-r--r--embassy-usb-logger/src/lib.rs20
-rw-r--r--examples/stm32f4/src/bin/eth_w5500.rs3
-rw-r--r--examples/stm32h7/src/bin/spi.rs2
-rw-r--r--examples/stm32h7/src/bin/spi_bdma.rs2
-rw-r--r--examples/stm32h7/src/bin/spi_dma.rs2
-rw-r--r--examples/stm32h7rs/src/bin/spi.rs2
-rw-r--r--examples/stm32h7rs/src/bin/spi_dma.rs2
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs3
-rw-r--r--tests/stm32/src/bin/spi.rs7
-rw-r--r--tests/stm32/src/bin/spi_dma.rs7
15 files changed, 164 insertions, 43 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 35dd79d2a..df1d909b7 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
45- fix: Correct STM32WBA VREFBUFTRIM values 45- fix: Correct STM32WBA VREFBUFTRIM values
46- low_power: remove stop_with rtc and initialize in init if low-power feature enabled. 46- low_power: remove stop_with rtc and initialize in init if low-power feature enabled.
47- feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847)) 47- feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847))
48- feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388))
48 49
49## 0.4.0 - 2025-08-26 50## 0.4.0 - 2025-08-26
50 51
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index ba9590013..57a7acee7 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1196,7 +1196,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1196 1196
1197 let regs = self.info.regs; 1197 let regs = self.info.regs;
1198 1198
1199 let dma_transfer = unsafe { 1199 let mut dma_transfer = unsafe {
1200 regs.cr1().modify(|w| { 1200 regs.cr1().modify(|w| {
1201 w.set_rxdmaen(true); 1201 w.set_rxdmaen(true);
1202 w.set_stopie(true); 1202 w.set_stopie(true);
@@ -1244,6 +1244,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1244 }) 1244 })
1245 .await?; 1245 .await?;
1246 1246
1247 dma_transfer.request_pause();
1247 dma_transfer.await; 1248 dma_transfer.await;
1248 1249
1249 drop(on_drop); 1250 drop(on_drop);
@@ -1309,6 +1310,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1309 }) 1310 })
1310 .await?; 1311 .await?;
1311 1312
1313 dma_transfer.request_pause();
1312 dma_transfer.await; 1314 dma_transfer.await;
1313 1315
1314 drop(on_drop); 1316 drop(on_drop);
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index db22cfa11..df077a3ae 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -7,6 +7,7 @@ use crate::Peri;
7use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; 7use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer};
8use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; 8use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
9use crate::mode::Async; 9use crate::mode::Async;
10use crate::spi::mode::Master;
10use crate::spi::{Config as SpiConfig, RegsExt as _, *}; 11use crate::spi::{Config as SpiConfig, RegsExt as _, *};
11use crate::time::Hertz; 12use crate::time::Hertz;
12 13
@@ -225,7 +226,7 @@ impl<'s, 'd, W: Word> Reader<'s, 'd, W> {
225pub struct I2S<'d, W: Word> { 226pub struct I2S<'d, W: Word> {
226 #[allow(dead_code)] 227 #[allow(dead_code)]
227 mode: Mode, 228 mode: Mode,
228 spi: Spi<'d, Async>, 229 spi: Spi<'d, Async, Master>,
229 txsd: Option<Peri<'d, AnyPin>>, 230 txsd: Option<Peri<'d, AnyPin>>,
230 rxsd: Option<Peri<'d, AnyPin>>, 231 rxsd: Option<Peri<'d, AnyPin>>,
231 ws: Option<Peri<'d, AnyPin>>, 232 ws: Option<Peri<'d, AnyPin>>,
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index c27d09ea7..abb80ed26 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -125,26 +125,69 @@ impl Config {
125 ) 125 )
126 } 126 }
127} 127}
128
129/// SPI communication mode
130pub mod mode {
131 use stm32_metapac::spi::vals;
132
133 trait SealedMode {}
134
135 /// Trait for SPI communication mode operations.
136 #[allow(private_bounds)]
137 pub trait CommunicationMode: SealedMode {
138 /// Spi communication mode
139 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
140 const MASTER: vals::Mstr;
141 /// Spi communication mode
142 #[cfg(any(spi_v4, spi_v5, spi_v6))]
143 const MASTER: vals::Master;
144 }
145
146 /// Mode allowing for SPI master operations.
147 pub struct Master;
148 /// Mode allowing for SPI slave operations.
149 pub struct Slave;
150
151 impl SealedMode for Master {}
152 impl CommunicationMode for Master {
153 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
154 const MASTER: vals::Mstr = vals::Mstr::MASTER;
155 #[cfg(any(spi_v4, spi_v5, spi_v6))]
156 const MASTER: vals::Master = vals::Master::MASTER;
157 }
158
159 impl SealedMode for Slave {}
160 impl CommunicationMode for Slave {
161 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
162 const MASTER: vals::Mstr = vals::Mstr::SLAVE;
163 #[cfg(any(spi_v4, spi_v5, spi_v6))]
164 const MASTER: vals::Master = vals::Master::SLAVE;
165 }
166}
167use mode::{CommunicationMode, Master, Slave};
168
128/// SPI driver. 169/// SPI driver.
129pub struct Spi<'d, M: PeriMode> { 170pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> {
130 pub(crate) info: &'static Info, 171 pub(crate) info: &'static Info,
131 kernel_clock: Hertz, 172 kernel_clock: Hertz,
132 sck: Option<Peri<'d, AnyPin>>, 173 sck: Option<Peri<'d, AnyPin>>,
133 mosi: Option<Peri<'d, AnyPin>>, 174 mosi: Option<Peri<'d, AnyPin>>,
134 miso: Option<Peri<'d, AnyPin>>, 175 miso: Option<Peri<'d, AnyPin>>,
176 nss: Option<Peri<'d, AnyPin>>,
135 tx_dma: Option<ChannelAndRequest<'d>>, 177 tx_dma: Option<ChannelAndRequest<'d>>,
136 rx_dma: Option<ChannelAndRequest<'d>>, 178 rx_dma: Option<ChannelAndRequest<'d>>,
137 _phantom: PhantomData<M>, 179 _phantom: PhantomData<(M, CM)>,
138 current_word_size: word_impl::Config, 180 current_word_size: word_impl::Config,
139 gpio_speed: Speed, 181 gpio_speed: Speed,
140} 182}
141 183
142impl<'d, M: PeriMode> Spi<'d, M> { 184impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
143 fn new_inner<T: Instance>( 185 fn new_inner<T: Instance>(
144 _peri: Peri<'d, T>, 186 _peri: Peri<'d, T>,
145 sck: Option<Peri<'d, AnyPin>>, 187 sck: Option<Peri<'d, AnyPin>>,
146 mosi: Option<Peri<'d, AnyPin>>, 188 mosi: Option<Peri<'d, AnyPin>>,
147 miso: Option<Peri<'d, AnyPin>>, 189 miso: Option<Peri<'d, AnyPin>>,
190 nss: Option<Peri<'d, AnyPin>>,
148 tx_dma: Option<ChannelAndRequest<'d>>, 191 tx_dma: Option<ChannelAndRequest<'d>>,
149 rx_dma: Option<ChannelAndRequest<'d>>, 192 rx_dma: Option<ChannelAndRequest<'d>>,
150 config: Config, 193 config: Config,
@@ -155,6 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
155 sck, 198 sck,
156 mosi, 199 mosi,
157 miso, 200 miso,
201 nss,
158 tx_dma, 202 tx_dma,
159 rx_dma, 203 rx_dma,
160 current_word_size: <u8 as SealedWord>::CONFIG, 204 current_word_size: <u8 as SealedWord>::CONFIG,
@@ -183,12 +227,12 @@ impl<'d, M: PeriMode> Spi<'d, M> {
183 w.set_cpha(cpha); 227 w.set_cpha(cpha);
184 w.set_cpol(cpol); 228 w.set_cpol(cpol);
185 229
186 w.set_mstr(vals::Mstr::MASTER); 230 w.set_mstr(CM::MASTER);
187 w.set_br(br); 231 w.set_br(br);
188 w.set_spe(true); 232 w.set_spe(true);
189 w.set_lsbfirst(lsbfirst); 233 w.set_lsbfirst(lsbfirst);
190 w.set_ssi(true); 234 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
191 w.set_ssm(true); 235 w.set_ssm(CM::MASTER == vals::Mstr::MASTER);
192 w.set_crcen(false); 236 w.set_crcen(false);
193 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 237 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
194 // we're doing "fake rxonly", by actually writing one 238 // we're doing "fake rxonly", by actually writing one
@@ -210,11 +254,11 @@ impl<'d, M: PeriMode> Spi<'d, M> {
210 w.set_cpha(cpha); 254 w.set_cpha(cpha);
211 w.set_cpol(cpol); 255 w.set_cpol(cpol);
212 256
213 w.set_mstr(vals::Mstr::MASTER); 257 w.set_mstr(CM::MASTER);
214 w.set_br(br); 258 w.set_br(br);
215 w.set_lsbfirst(lsbfirst); 259 w.set_lsbfirst(lsbfirst);
216 w.set_ssi(true); 260 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
217 w.set_ssm(true); 261 w.set_ssm(CM::MASTER == vals::Mstr::MASTER);
218 w.set_crcen(false); 262 w.set_crcen(false);
219 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 263 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
220 w.set_spe(true); 264 w.set_spe(true);
@@ -229,8 +273,8 @@ impl<'d, M: PeriMode> Spi<'d, M> {
229 w.set_cpha(cpha); 273 w.set_cpha(cpha);
230 w.set_cpol(cpol); 274 w.set_cpol(cpol);
231 w.set_lsbfirst(lsbfirst); 275 w.set_lsbfirst(lsbfirst);
232 w.set_ssm(true); 276 w.set_ssm(CM::MASTER == vals::Master::MASTER);
233 w.set_master(vals::Master::MASTER); 277 w.set_master(CM::MASTER);
234 w.set_comm(vals::Comm::FULL_DUPLEX); 278 w.set_comm(vals::Comm::FULL_DUPLEX);
235 w.set_ssom(vals::Ssom::ASSERTED); 279 w.set_ssom(vals::Ssom::ASSERTED);
236 w.set_midi(0); 280 w.set_midi(0);
@@ -469,7 +513,30 @@ impl<'d, M: PeriMode> Spi<'d, M> {
469 } 513 }
470} 514}
471 515
472impl<'d> Spi<'d, Blocking> { 516impl<'d> Spi<'d, Blocking, Slave> {
517 /// Create a new blocking SPI slave driver.
518 pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>(
519 peri: Peri<'d, T>,
520 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
521 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
522 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
523 cs: Peri<'d, if_afio!(impl CsPin<T, A>)>,
524 config: Config,
525 ) -> Self {
526 Self::new_inner(
527 peri,
528 new_pin!(sck, config.sck_af()),
529 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),
530 new_pin!(miso, AfType::input(config.miso_pull)),
531 new_pin!(cs, AfType::input(Pull::None)),
532 None,
533 None,
534 config,
535 )
536 }
537}
538
539impl<'d> Spi<'d, Blocking, Master> {
473 /// Create a new blocking SPI driver. 540 /// Create a new blocking SPI driver.
474 pub fn new_blocking<T: Instance, #[cfg(afio)] A>( 541 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
475 peri: Peri<'d, T>, 542 peri: Peri<'d, T>,
@@ -485,6 +552,7 @@ impl<'d> Spi<'d, Blocking> {
485 new_pin!(miso, AfType::input(config.miso_pull)), 552 new_pin!(miso, AfType::input(config.miso_pull)),
486 None, 553 None,
487 None, 554 None,
555 None,
488 config, 556 config,
489 ) 557 )
490 } 558 }
@@ -503,6 +571,7 @@ impl<'d> Spi<'d, Blocking> {
503 new_pin!(miso, AfType::input(config.miso_pull)), 571 new_pin!(miso, AfType::input(config.miso_pull)),
504 None, 572 None,
505 None, 573 None,
574 None,
506 config, 575 config,
507 ) 576 )
508 } 577 }
@@ -521,6 +590,7 @@ impl<'d> Spi<'d, Blocking> {
521 None, 590 None,
522 None, 591 None,
523 None, 592 None,
593 None,
524 config, 594 config,
525 ) 595 )
526 } 596 }
@@ -540,12 +610,38 @@ impl<'d> Spi<'d, Blocking> {
540 None, 610 None,
541 None, 611 None,
542 None, 612 None,
613 None,
614 config,
615 )
616 }
617}
618
619impl<'d> Spi<'d, Async, Slave> {
620 /// Create a new SPI slave driver.
621 pub fn new_slave<T: Instance, #[cfg(afio)] A>(
622 peri: Peri<'d, T>,
623 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
624 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
625 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
626 cs: Peri<'d, if_afio!(impl CsPin<T, A>)>,
627 tx_dma: Peri<'d, impl TxDma<T>>,
628 rx_dma: Peri<'d, impl RxDma<T>>,
629 config: Config,
630 ) -> Self {
631 Self::new_inner(
632 peri,
633 new_pin!(sck, config.sck_af()),
634 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),
635 new_pin!(miso, AfType::input(config.miso_pull)),
636 new_pin!(cs, AfType::input(Pull::None)),
637 new_dma!(tx_dma),
638 new_dma!(rx_dma),
543 config, 639 config,
544 ) 640 )
545 } 641 }
546} 642}
547 643
548impl<'d> Spi<'d, Async> { 644impl<'d> Spi<'d, Async, Master> {
549 /// Create a new SPI driver. 645 /// Create a new SPI driver.
550 pub fn new<T: Instance, #[cfg(afio)] A>( 646 pub fn new<T: Instance, #[cfg(afio)] A>(
551 peri: Peri<'d, T>, 647 peri: Peri<'d, T>,
@@ -561,6 +657,7 @@ impl<'d> Spi<'d, Async> {
561 new_pin!(sck, config.sck_af()), 657 new_pin!(sck, config.sck_af()),
562 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), 658 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),
563 new_pin!(miso, AfType::input(config.miso_pull)), 659 new_pin!(miso, AfType::input(config.miso_pull)),
660 None,
564 new_dma!(tx_dma), 661 new_dma!(tx_dma),
565 new_dma!(rx_dma), 662 new_dma!(rx_dma),
566 config, 663 config,
@@ -581,6 +678,7 @@ impl<'d> Spi<'d, Async> {
581 new_pin!(sck, config.sck_af()), 678 new_pin!(sck, config.sck_af()),
582 None, 679 None,
583 new_pin!(miso, AfType::input(config.miso_pull)), 680 new_pin!(miso, AfType::input(config.miso_pull)),
681 None,
584 #[cfg(any(spi_v1, spi_v2, spi_v3))] 682 #[cfg(any(spi_v1, spi_v2, spi_v3))]
585 new_dma!(tx_dma), 683 new_dma!(tx_dma),
586 #[cfg(any(spi_v4, spi_v5, spi_v6))] 684 #[cfg(any(spi_v4, spi_v5, spi_v6))]
@@ -603,6 +701,7 @@ impl<'d> Spi<'d, Async> {
603 new_pin!(sck, config.sck_af()), 701 new_pin!(sck, config.sck_af()),
604 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), 702 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),
605 None, 703 None,
704 None,
606 new_dma!(tx_dma), 705 new_dma!(tx_dma),
607 None, 706 None,
608 config, 707 config,
@@ -623,6 +722,7 @@ impl<'d> Spi<'d, Async> {
623 None, 722 None,
624 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), 723 new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),
625 None, 724 None,
725 None,
626 new_dma!(tx_dma), 726 new_dma!(tx_dma),
627 None, 727 None,
628 config, 728 config,
@@ -646,7 +746,7 @@ impl<'d> Spi<'d, Async> {
646 config.bit_order = BitOrder::MsbFirst; 746 config.bit_order = BitOrder::MsbFirst;
647 config.frequency = freq; 747 config.frequency = freq;
648 748
649 Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) 749 Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config)
650 } 750 }
651 751
652 #[allow(dead_code)] 752 #[allow(dead_code)]
@@ -656,9 +756,11 @@ impl<'d> Spi<'d, Async> {
656 rx_dma: Option<ChannelAndRequest<'d>>, 756 rx_dma: Option<ChannelAndRequest<'d>>,
657 config: Config, 757 config: Config,
658 ) -> Self { 758 ) -> Self {
659 Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) 759 Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config)
660 } 760 }
761}
661 762
763impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
662 /// SPI write, using DMA. 764 /// SPI write, using DMA.
663 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { 765 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {
664 if data.is_empty() { 766 if data.is_empty() {
@@ -888,11 +990,12 @@ impl<'d> Spi<'d, Async> {
888 } 990 }
889} 991}
890 992
891impl<'d, M: PeriMode> Drop for Spi<'d, M> { 993impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> {
892 fn drop(&mut self) { 994 fn drop(&mut self) {
893 self.sck.as_ref().map(|x| x.set_as_disconnected()); 995 self.sck.as_ref().map(|x| x.set_as_disconnected());
894 self.mosi.as_ref().map(|x| x.set_as_disconnected()); 996 self.mosi.as_ref().map(|x| x.set_as_disconnected());
895 self.miso.as_ref().map(|x| x.set_as_disconnected()); 997 self.miso.as_ref().map(|x| x.set_as_disconnected());
998 self.nss.as_ref().map(|x| x.set_as_disconnected());
896 999
897 self.info.rcc.disable(); 1000 self.info.rcc.disable();
898 } 1001 }
@@ -1127,7 +1230,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {
1127// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 1230// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289
1128macro_rules! impl_blocking { 1231macro_rules! impl_blocking {
1129 ($w:ident) => { 1232 ($w:ident) => {
1130 impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { 1233 impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> {
1131 type Error = Error; 1234 type Error = Error;
1132 1235
1133 fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { 1236 fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> {
@@ -1135,7 +1238,7 @@ macro_rules! impl_blocking {
1135 } 1238 }
1136 } 1239 }
1137 1240
1138 impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { 1241 impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> {
1139 type Error = Error; 1242 type Error = Error;
1140 1243
1141 fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { 1244 fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> {
@@ -1149,11 +1252,11 @@ macro_rules! impl_blocking {
1149impl_blocking!(u8); 1252impl_blocking!(u8);
1150impl_blocking!(u16); 1253impl_blocking!(u16);
1151 1254
1152impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { 1255impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> {
1153 type Error = Error; 1256 type Error = Error;
1154} 1257}
1155 1258
1156impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { 1259impl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> {
1157 fn flush(&mut self) -> Result<(), Self::Error> { 1260 fn flush(&mut self) -> Result<(), Self::Error> {
1158 Ok(()) 1261 Ok(())
1159 } 1262 }
@@ -1186,7 +1289,7 @@ impl embedded_hal_1::spi::Error for Error {
1186 } 1289 }
1187} 1290}
1188 1291
1189impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { 1292impl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> {
1190 async fn flush(&mut self) -> Result<(), Self::Error> { 1293 async fn flush(&mut self) -> Result<(), Self::Error> {
1191 Ok(()) 1294 Ok(())
1192 } 1295 }
@@ -1328,7 +1431,7 @@ foreach_peripheral!(
1328 }; 1431 };
1329); 1432);
1330 1433
1331impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { 1434impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> {
1332 type Config = Config; 1435 type Config = Config;
1333 type ConfigError = (); 1436 type ConfigError = ();
1334 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { 1437 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md
index 4ea0e8871..3ba140b1f 100644
--- a/embassy-usb-logger/CHANGELOG.md
+++ b/embassy-usb-logger/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Fixed panic in `UsbLogger` when usb is disconnected
12
11## 0.5.1 - 2025-08-26 13## 0.5.1 - 2025-08-26
12 14
13## 0.5.0 - 2025-07-22 15## 0.5.0 - 2025-07-22
diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs
index de25abce1..7c3c5e709 100644
--- a/embassy-usb-logger/src/lib.rs
+++ b/embassy-usb-logger/src/lib.rs
@@ -8,7 +8,7 @@ use core::future::Future;
8use embassy_futures::join::join; 8use embassy_futures::join::join;
9use embassy_sync::pipe::Pipe; 9use embassy_sync::pipe::Pipe;
10use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; 10use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
11use embassy_usb::driver::Driver; 11use embassy_usb::driver::{Driver, EndpointError};
12use embassy_usb::{Builder, Config}; 12use embassy_usb::{Builder, Config};
13use log::{Metadata, Record}; 13use log::{Metadata, Record};
14 14
@@ -133,17 +133,25 @@ impl<const N: usize, T: ReceiverHandler + Send + Sync> UsbLogger<N, T> {
133 sender.wait_connection().await; 133 sender.wait_connection().await;
134 loop { 134 loop {
135 let len = self.buffer.read(&mut rx[..]).await; 135 let len = self.buffer.read(&mut rx[..]).await;
136 let _ = sender.write_packet(&rx[..len]).await; 136 if Err(EndpointError::Disabled) == sender.write_packet(&rx[..len]).await
137 if len as u8 == MAX_PACKET_SIZE { 137 || len as u8 == MAX_PACKET_SIZE && Err(EndpointError::Disabled) == sender.write_packet(&[]).await
138 let _ = sender.write_packet(&[]).await; 138 {
139 } 139 sender.wait_connection().await;
140 };
140 } 141 }
141 }; 142 };
142 let reciever_fut = async { 143 let reciever_fut = async {
143 let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; 144 let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
144 receiver.wait_connection().await; 145 receiver.wait_connection().await;
145 loop { 146 loop {
146 let n = receiver.read_packet(&mut reciever_buf).await.unwrap(); 147 let n = match receiver.read_packet(&mut reciever_buf).await {
148 Err(EndpointError::Disabled) => {
149 receiver.wait_connection().await;
150 continue;
151 }
152 Err(_) => continue,
153 Ok(n) => n,
154 };
147 match &self.recieve_handler { 155 match &self.recieve_handler {
148 Some(handler) => { 156 Some(handler) => {
149 let data = &reciever_buf[..n]; 157 let data = &reciever_buf[..n];
diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs
index cccf20949..0adcda614 100644
--- a/examples/stm32f4/src/bin/eth_w5500.rs
+++ b/examples/stm32f4/src/bin/eth_w5500.rs
@@ -12,6 +12,7 @@ use embassy_stm32::gpio::{Level, Output, Pull, Speed};
12use embassy_stm32::mode::Async; 12use embassy_stm32::mode::Async;
13use embassy_stm32::rng::Rng; 13use embassy_stm32::rng::Rng;
14use embassy_stm32::spi::Spi; 14use embassy_stm32::spi::Spi;
15use embassy_stm32::spi::mode::Master;
15use embassy_stm32::time::Hertz; 16use embassy_stm32::time::Hertz;
16use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi}; 17use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi};
17use embassy_time::{Delay, Timer}; 18use embassy_time::{Delay, Timer};
@@ -24,7 +25,7 @@ bind_interrupts!(struct Irqs {
24 HASH_RNG => rng::InterruptHandler<peripherals::RNG>; 25 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
25}); 26});
26 27
27type EthernetSPI = ExclusiveDevice<Spi<'static, Async>, Output<'static>, Delay>; 28type EthernetSPI = ExclusiveDevice<Spi<'static, Async, Master>, Output<'static>, Delay>;
28#[embassy_executor::task] 29#[embassy_executor::task]
29async fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static>, Output<'static>>) -> ! { 30async fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static>, Output<'static>>) -> ! {
30 runner.run().await 31 runner.run().await
diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs
index 61f31be24..f7ab20cdd 100644
--- a/examples/stm32h7/src/bin/spi.rs
+++ b/examples/stm32h7/src/bin/spi.rs
@@ -15,7 +15,7 @@ use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn main_task(mut spi: spi::Spi<'static, Blocking>) { 18async fn main_task(mut spi: spi::Spi<'static, Blocking, spi::mode::Master>) {
19 for n in 0u32.. { 19 for n in 0u32.. {
20 let mut write: String<128> = String::new(); 20 let mut write: String<128> = String::new();
21 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); 21 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs
index be6a26d82..cd9d6c789 100644
--- a/examples/stm32h7/src/bin/spi_bdma.rs
+++ b/examples/stm32h7/src/bin/spi_bdma.rs
@@ -20,7 +20,7 @@ use {defmt_rtt as _, panic_probe as _};
20static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit(); 20static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit();
21 21
22#[embassy_executor::task] 22#[embassy_executor::task]
23async fn main_task(mut spi: spi::Spi<'static, Async>) { 23async fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {
24 let (read_buffer, write_buffer) = unsafe { 24 let (read_buffer, write_buffer) = unsafe {
25 let ram = &mut *core::ptr::addr_of_mut!(RAM_D3); 25 let ram = &mut *core::ptr::addr_of_mut!(RAM_D3);
26 ram.initialize_all_copied(0); 26 ram.initialize_all_copied(0);
diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs
index 20cb67ba0..3d3c2f43e 100644
--- a/examples/stm32h7/src/bin/spi_dma.rs
+++ b/examples/stm32h7/src/bin/spi_dma.rs
@@ -15,7 +15,7 @@ use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn main_task(mut spi: spi::Spi<'static, Async>) { 18async fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {
19 for n in 0u32.. { 19 for n in 0u32.. {
20 let mut write: String<128> = String::new(); 20 let mut write: String<128> = String::new();
21 let mut read = [0; 128]; 21 let mut read = [0; 128];
diff --git a/examples/stm32h7rs/src/bin/spi.rs b/examples/stm32h7rs/src/bin/spi.rs
index 8c280fdae..3253304eb 100644
--- a/examples/stm32h7rs/src/bin/spi.rs
+++ b/examples/stm32h7rs/src/bin/spi.rs
@@ -15,7 +15,7 @@ use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn main_task(mut spi: spi::Spi<'static, Blocking>) { 18async fn main_task(mut spi: spi::Spi<'static, Blocking, spi::mode::Master>) {
19 for n in 0u32.. { 19 for n in 0u32.. {
20 let mut write: String<128> = String::new(); 20 let mut write: String<128> = String::new();
21 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); 21 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
diff --git a/examples/stm32h7rs/src/bin/spi_dma.rs b/examples/stm32h7rs/src/bin/spi_dma.rs
index 3fa69fd15..ca644c6a8 100644
--- a/examples/stm32h7rs/src/bin/spi_dma.rs
+++ b/examples/stm32h7rs/src/bin/spi_dma.rs
@@ -15,7 +15,7 @@ use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn main_task(mut spi: spi::Spi<'static, Async>) { 18async fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {
19 for n in 0u32.. { 19 for n in 0u32.. {
20 let mut write: String<128> = String::new(); 20 let mut write: String<128> = String::new();
21 let mut read = [0; 128]; 21 let mut read = [0; 128];
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
index 8e54938d1..0dbf515cf 100644
--- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -28,6 +28,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
28use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; 28use embassy_stm32::i2c::{self, Config as I2C_Config, I2c};
29use embassy_stm32::mode::Async; 29use embassy_stm32::mode::Async;
30use embassy_stm32::rng::{self, Rng}; 30use embassy_stm32::rng::{self, Rng};
31use embassy_stm32::spi::mode::Master;
31use embassy_stm32::spi::{Config as SPI_Config, Spi}; 32use embassy_stm32::spi::{Config as SPI_Config, Spi};
32use embassy_stm32::time::Hertz; 33use embassy_stm32::time::Hertz;
33use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; 34use embassy_stm32::{bind_interrupts, exti, pac, peripherals};
@@ -54,7 +55,7 @@ const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24)
54// Listen port for the webserver 55// Listen port for the webserver
55const HTTP_LISTEN_PORT: u16 = 80; 56const HTTP_LISTEN_PORT: u16 = 80;
56 57
57pub type SpeSpi = Spi<'static, Async>; 58pub type SpeSpi = Spi<'static, Async, Master>;
58pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>; 59pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>;
59pub type SpeInt = exti::ExtiInput<'static>; 60pub type SpeInt = exti::ExtiInput<'static>;
60pub type SpeRst = Output<'static>; 61pub type SpeRst = Output<'static>;
diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs
index e8310866a..cedff772c 100644
--- a/tests/stm32/src/bin/spi.rs
+++ b/tests/stm32/src/bin/spi.rs
@@ -8,6 +8,7 @@ use defmt::assert_eq;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::gpio::{Level, Output, Speed}; 9use embassy_stm32::gpio::{Level, Output, Speed};
10use embassy_stm32::mode::Blocking; 10use embassy_stm32::mode::Blocking;
11use embassy_stm32::spi::mode::Master;
11use embassy_stm32::spi::{self, Spi, Word}; 12use embassy_stm32::spi::{self, Spi, Word};
12use embassy_stm32::time::Hertz; 13use embassy_stm32::time::Hertz;
13 14
@@ -65,7 +66,7 @@ async fn main(_spawner: Spawner) {
65 cortex_m::asm::bkpt(); 66 cortex_m::asm::bkpt();
66} 67}
67 68
68fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) 69fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>)
69where 70where
70 W: core::ops::Not<Output = W>, 71 W: core::ops::Not<Output = W>,
71{ 72{
@@ -109,7 +110,7 @@ where
109 spi.blocking_write::<u8>(&[]).unwrap(); 110 spi.blocking_write::<u8>(&[]).unwrap();
110} 111}
111 112
112fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>, mosi_out: &mut Output<'_>) 113fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>, mosi_out: &mut Output<'_>)
113where 114where
114 W: core::ops::Not<Output = W>, 115 W: core::ops::Not<Output = W>,
115{ 116{
@@ -125,7 +126,7 @@ where
125 spi.blocking_read::<u8>(&mut []).unwrap(); 126 spi.blocking_read::<u8>(&mut []).unwrap();
126} 127}
127 128
128fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) 129fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>)
129where 130where
130 W: core::ops::Not<Output = W>, 131 W: core::ops::Not<Output = W>,
131{ 132{
diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs
index b4fdb8faa..c8cd92401 100644
--- a/tests/stm32/src/bin/spi_dma.rs
+++ b/tests/stm32/src/bin/spi_dma.rs
@@ -8,6 +8,7 @@ use defmt::assert_eq;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::gpio::{Level, Output, Speed}; 9use embassy_stm32::gpio::{Level, Output, Speed};
10use embassy_stm32::mode::Async; 10use embassy_stm32::mode::Async;
11use embassy_stm32::spi::mode::Master;
11use embassy_stm32::spi::{self, Spi, Word}; 12use embassy_stm32::spi::{self, Spi, Word};
12use embassy_stm32::time::Hertz; 13use embassy_stm32::time::Hertz;
13 14
@@ -78,7 +79,7 @@ async fn main(_spawner: Spawner) {
78 cortex_m::asm::bkpt(); 79 cortex_m::asm::bkpt();
79} 80}
80 81
81async fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) 82async fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>)
82where 83where
83 W: core::ops::Not<Output = W>, 84 W: core::ops::Not<Output = W>,
84{ 85{
@@ -142,7 +143,7 @@ where
142 spi.write(&buf).await.unwrap(); 143 spi.write(&buf).await.unwrap();
143} 144}
144 145
145async fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>, mosi_out: &mut Output<'_>) 146async fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>, mosi_out: &mut Output<'_>)
146where 147where
147 W: core::ops::Not<Output = W>, 148 W: core::ops::Not<Output = W>,
148{ 149{
@@ -168,7 +169,7 @@ where
168 spi.blocking_read::<u8>(&mut []).unwrap(); 169 spi.blocking_read::<u8>(&mut []).unwrap();
169} 170}
170 171
171async fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) 172async fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>)
172where 173where
173 W: core::ops::Not<Output = W>, 174 W: core::ops::Not<Output = W>,
174{ 175{