aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/spi/mod.rs66
-rw-r--r--tests/stm32/src/bin/spi.rs18
-rw-r--r--tests/stm32/src/bin/spi_dma.rs23
3 files changed, 72 insertions, 35 deletions
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 5d6277c33..720e80e0d 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -351,17 +351,46 @@ impl<'d, M: PeriMode> Spi<'d, M> {
351 351
352 /// Blocking write. 352 /// Blocking write.
353 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { 353 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
354 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
355 #[cfg(any(spi_v3, spi_v4, spi_v5))]
356 self.info.regs.cr1().modify(|w| w.set_spe(false));
354 self.info.regs.cr1().modify(|w| w.set_spe(true)); 357 self.info.regs.cr1().modify(|w| w.set_spe(true));
355 flush_rx_fifo(self.info.regs); 358 flush_rx_fifo(self.info.regs);
356 self.set_word_size(W::CONFIG); 359 self.set_word_size(W::CONFIG);
357 for word in words.iter() { 360 for word in words.iter() {
358 let _ = transfer_word(self.info.regs, *word)?; 361 // this cannot use `transfer_word` because on SPIv2 and higher,
362 // the SPI RX state machine hangs if no physical pin is connected to the SCK AF.
363 // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`.
364 // See https://github.com/embassy-rs/embassy/issues/2902
365 // This is not documented as an errata by ST, and I've been unable to find anything online...
366 #[cfg(not(any(spi_v1, spi_f1)))]
367 write_word(self.info.regs, *word)?;
368
369 // if we're doing tx only, after writing the last byte to FIFO we have to wait
370 // until it's actually sent. On SPIv1 you're supposed to use the BSY flag for this
371 // but apparently it's broken, it clears too soon. Workaround is to wait for RXNE:
372 // when it gets set you know the transfer is done, even if you don't care about rx.
373 // Luckily this doesn't affect SPIv2+.
374 // See http://efton.sk/STM32/gotcha/g68.html
375 // ST doesn't seem to document this in errata sheets (?)
376 #[cfg(any(spi_v1, spi_f1))]
377 transfer_word(self.info.regs, *word)?;
359 } 378 }
379
380 // wait until last word is transmitted. (except on v1, see above)
381 #[cfg(not(any(spi_v1, spi_f1, spi_v2)))]
382 while !self.info.regs.sr().read().txc() {}
383 #[cfg(spi_v2)]
384 while self.info.regs.sr().read().bsy() {}
385
360 Ok(()) 386 Ok(())
361 } 387 }
362 388
363 /// Blocking read. 389 /// Blocking read.
364 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 390 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
391 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
392 #[cfg(any(spi_v3, spi_v4, spi_v5))]
393 self.info.regs.cr1().modify(|w| w.set_spe(false));
365 self.info.regs.cr1().modify(|w| w.set_spe(true)); 394 self.info.regs.cr1().modify(|w| w.set_spe(true));
366 flush_rx_fifo(self.info.regs); 395 flush_rx_fifo(self.info.regs);
367 self.set_word_size(W::CONFIG); 396 self.set_word_size(W::CONFIG);
@@ -375,6 +404,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
375 /// 404 ///
376 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. 405 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
377 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 406 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
407 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
408 #[cfg(any(spi_v3, spi_v4, spi_v5))]
409 self.info.regs.cr1().modify(|w| w.set_spe(false));
378 self.info.regs.cr1().modify(|w| w.set_spe(true)); 410 self.info.regs.cr1().modify(|w| w.set_spe(true));
379 flush_rx_fifo(self.info.regs); 411 flush_rx_fifo(self.info.regs);
380 self.set_word_size(W::CONFIG); 412 self.set_word_size(W::CONFIG);
@@ -391,6 +423,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
391 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. 423 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
392 /// If `write` is shorter it is padded with zero bytes. 424 /// If `write` is shorter it is padded with zero bytes.
393 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 425 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
426 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
427 #[cfg(any(spi_v3, spi_v4, spi_v5))]
428 self.info.regs.cr1().modify(|w| w.set_spe(false));
394 self.info.regs.cr1().modify(|w| w.set_spe(true)); 429 self.info.regs.cr1().modify(|w| w.set_spe(true));
395 flush_rx_fifo(self.info.regs); 430 flush_rx_fifo(self.info.regs);
396 self.set_word_size(W::CONFIG); 431 self.set_word_size(W::CONFIG);
@@ -465,7 +500,6 @@ impl<'d> Spi<'d, Blocking> {
465 /// Create a new SPI driver, in TX-only mode, without SCK pin. 500 /// Create a new SPI driver, in TX-only mode, without SCK pin.
466 /// 501 ///
467 /// This can be useful for bit-banging non-SPI protocols. 502 /// This can be useful for bit-banging non-SPI protocols.
468 #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons.
469 pub fn new_blocking_txonly_nosck<T: Instance>( 503 pub fn new_blocking_txonly_nosck<T: Instance>(
470 peri: impl Peripheral<P = T> + 'd, 504 peri: impl Peripheral<P = T> + 'd,
471 mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, 505 mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
@@ -550,7 +584,6 @@ impl<'d> Spi<'d, Async> {
550 /// Create a new SPI driver, in TX-only mode, without SCK pin. 584 /// Create a new SPI driver, in TX-only mode, without SCK pin.
551 /// 585 ///
552 /// This can be useful for bit-banging non-SPI protocols. 586 /// This can be useful for bit-banging non-SPI protocols.
553 #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons.
554 pub fn new_txonly_nosck<T: Instance>( 587 pub fn new_txonly_nosck<T: Instance>(
555 peri: impl Peripheral<P = T> + 'd, 588 peri: impl Peripheral<P = T> + 'd,
556 mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, 589 mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
@@ -900,8 +933,8 @@ impl RegsExt for Regs {
900 } 933 }
901} 934}
902 935
903fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { 936fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> {
904 if sr.ovr() { 937 if sr.ovr() && ovr {
905 return Err(Error::Overrun); 938 return Err(Error::Overrun);
906 } 939 }
907 #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] 940 #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))]
@@ -927,11 +960,11 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
927 Ok(()) 960 Ok(())
928} 961}
929 962
930fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { 963fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> {
931 loop { 964 loop {
932 let sr = regs.sr().read(); 965 let sr = regs.sr().read();
933 966
934 check_error_flags(sr)?; 967 check_error_flags(sr, ovr)?;
935 968
936 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 969 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
937 if sr.txe() { 970 if sr.txe() {
@@ -948,7 +981,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
948 loop { 981 loop {
949 let sr = regs.sr().read(); 982 let sr = regs.sr().read();
950 983
951 check_error_flags(sr)?; 984 check_error_flags(sr, true)?;
952 985
953 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 986 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
954 if sr.rxne() { 987 if sr.rxne() {
@@ -1032,7 +1065,7 @@ fn finish_dma(regs: Regs) {
1032} 1065}
1033 1066
1034fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> { 1067fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
1035 spin_until_tx_ready(regs)?; 1068 spin_until_tx_ready(regs, true)?;
1036 1069
1037 unsafe { 1070 unsafe {
1038 ptr::write_volatile(regs.tx_ptr(), tx_word); 1071 ptr::write_volatile(regs.tx_ptr(), tx_word);
@@ -1047,6 +1080,21 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
1047 Ok(rx_word) 1080 Ok(rx_word)
1048} 1081}
1049 1082
1083#[allow(unused)] // unused in SPIv1
1084fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {
1085 // for write, we intentionally ignore the rx fifo, which will cause
1086 // overrun errors that we have to ignore.
1087 spin_until_tx_ready(regs, false)?;
1088
1089 unsafe {
1090 ptr::write_volatile(regs.tx_ptr(), tx_word);
1091
1092 #[cfg(any(spi_v3, spi_v4, spi_v5))]
1093 regs.cr1().modify(|reg| reg.set_cstart(true));
1094 }
1095 Ok(())
1096}
1097
1050// Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with 1098// Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with
1051// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 1099// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289
1052macro_rules! impl_blocking { 1100macro_rules! impl_blocking {
diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs
index 8be3b1a7c..0ffd0f653 100644
--- a/tests/stm32/src/bin/spi.rs
+++ b/tests/stm32/src/bin/spi.rs
@@ -94,19 +94,11 @@ async fn main(_spawner: Spawner) {
94 drop(spi); 94 drop(spi);
95 95
96 // Test tx-only nosck. 96 // Test tx-only nosck.
97 #[cfg(feature = "spi-v1")] 97 let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config);
98 { 98 spi.blocking_write(&buf).unwrap();
99 let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); 99 spi.blocking_write::<u8>(&[]).unwrap();
100 spi.blocking_transfer(&mut buf, &data).unwrap(); 100 spi.blocking_write(&buf).unwrap();
101 spi.blocking_transfer_in_place(&mut buf).unwrap(); 101 drop(spi);
102 spi.blocking_write(&buf).unwrap();
103 spi.blocking_read(&mut buf).unwrap();
104 spi.blocking_transfer::<u8>(&mut [], &[]).unwrap();
105 spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();
106 spi.blocking_read::<u8>(&mut []).unwrap();
107 spi.blocking_write::<u8>(&[]).unwrap();
108 drop(spi);
109 }
110 102
111 info!("Test OK"); 103 info!("Test OK");
112 cortex_m::asm::bkpt(); 104 cortex_m::asm::bkpt();
diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs
index a8001a111..fd26d3f71 100644
--- a/tests/stm32/src/bin/spi_dma.rs
+++ b/tests/stm32/src/bin/spi_dma.rs
@@ -132,19 +132,16 @@ async fn main(_spawner: Spawner) {
132 drop(spi); 132 drop(spi);
133 133
134 // Test tx-only nosck. 134 // Test tx-only nosck.
135 #[cfg(feature = "spi-v1")] 135 let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config);
136 { 136 spi.blocking_write(&buf).unwrap();
137 let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); 137 spi.write(&buf).await.unwrap();
138 spi.blocking_write(&buf).unwrap(); 138 spi.blocking_write(&buf).unwrap();
139 spi.write(&buf).await.unwrap(); 139 spi.blocking_write(&buf).unwrap();
140 spi.blocking_write(&buf).unwrap(); 140 spi.write(&buf).await.unwrap();
141 spi.blocking_write(&buf).unwrap(); 141 spi.write(&buf).await.unwrap();
142 spi.write(&buf).await.unwrap(); 142 spi.write::<u8>(&[]).await.unwrap();
143 spi.write(&buf).await.unwrap(); 143 spi.blocking_write::<u8>(&[]).unwrap();
144 spi.write::<u8>(&[]).await.unwrap(); 144 drop(spi);
145 spi.blocking_write::<u8>(&[]).unwrap();
146 drop(spi);
147 }
148 145
149 info!("Test OK"); 146 info!("Test OK");
150 cortex_m::asm::bkpt(); 147 cortex_m::asm::bkpt();