mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
parent
7b590334e6
commit
348c87fc2f
@ -351,17 +351,46 @@ impl<'d, M: PeriMode> Spi<'d, M> {
|
|||||||
|
|
||||||
/// Blocking write.
|
/// Blocking write.
|
||||||
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
|
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
|
||||||
|
// needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
|
||||||
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
|
self.info.regs.cr1().modify(|w| w.set_spe(false));
|
||||||
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(self.info.regs);
|
flush_rx_fifo(self.info.regs);
|
||||||
self.set_word_size(W::CONFIG);
|
self.set_word_size(W::CONFIG);
|
||||||
for word in words.iter() {
|
for word in words.iter() {
|
||||||
let _ = transfer_word(self.info.regs, *word)?;
|
// this cannot use `transfer_word` because on SPIv2 and higher,
|
||||||
|
// the SPI RX state machine hangs if no physical pin is connected to the SCK AF.
|
||||||
|
// This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`.
|
||||||
|
// See https://github.com/embassy-rs/embassy/issues/2902
|
||||||
|
// This is not documented as an errata by ST, and I've been unable to find anything online...
|
||||||
|
#[cfg(not(any(spi_v1, spi_f1)))]
|
||||||
|
write_word(self.info.regs, *word)?;
|
||||||
|
|
||||||
|
// if we're doing tx only, after writing the last byte to FIFO we have to wait
|
||||||
|
// until it's actually sent. On SPIv1 you're supposed to use the BSY flag for this
|
||||||
|
// but apparently it's broken, it clears too soon. Workaround is to wait for RXNE:
|
||||||
|
// when it gets set you know the transfer is done, even if you don't care about rx.
|
||||||
|
// Luckily this doesn't affect SPIv2+.
|
||||||
|
// See http://efton.sk/STM32/gotcha/g68.html
|
||||||
|
// ST doesn't seem to document this in errata sheets (?)
|
||||||
|
#[cfg(any(spi_v1, spi_f1))]
|
||||||
|
transfer_word(self.info.regs, *word)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wait until last word is transmitted. (except on v1, see above)
|
||||||
|
#[cfg(not(any(spi_v1, spi_f1, spi_v2)))]
|
||||||
|
while !self.info.regs.sr().read().txc() {}
|
||||||
|
#[cfg(spi_v2)]
|
||||||
|
while self.info.regs.sr().read().bsy() {}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocking read.
|
/// Blocking read.
|
||||||
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||||
|
// needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
|
||||||
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
|
self.info.regs.cr1().modify(|w| w.set_spe(false));
|
||||||
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(self.info.regs);
|
flush_rx_fifo(self.info.regs);
|
||||||
self.set_word_size(W::CONFIG);
|
self.set_word_size(W::CONFIG);
|
||||||
@ -375,6 +404,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
|
|||||||
///
|
///
|
||||||
/// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
|
/// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
|
||||||
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||||
|
// needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
|
||||||
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
|
self.info.regs.cr1().modify(|w| w.set_spe(false));
|
||||||
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(self.info.regs);
|
flush_rx_fifo(self.info.regs);
|
||||||
self.set_word_size(W::CONFIG);
|
self.set_word_size(W::CONFIG);
|
||||||
@ -391,6 +423,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
|
|||||||
/// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
|
/// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
|
||||||
/// If `write` is shorter it is padded with zero bytes.
|
/// If `write` is shorter it is padded with zero bytes.
|
||||||
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
|
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
|
||||||
|
// needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
|
||||||
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
|
self.info.regs.cr1().modify(|w| w.set_spe(false));
|
||||||
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
self.info.regs.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(self.info.regs);
|
flush_rx_fifo(self.info.regs);
|
||||||
self.set_word_size(W::CONFIG);
|
self.set_word_size(W::CONFIG);
|
||||||
@ -465,7 +500,6 @@ impl<'d> Spi<'d, Blocking> {
|
|||||||
/// Create a new SPI driver, in TX-only mode, without SCK pin.
|
/// Create a new SPI driver, in TX-only mode, without SCK pin.
|
||||||
///
|
///
|
||||||
/// This can be useful for bit-banging non-SPI protocols.
|
/// This can be useful for bit-banging non-SPI protocols.
|
||||||
#[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons.
|
|
||||||
pub fn new_blocking_txonly_nosck<T: Instance>(
|
pub fn new_blocking_txonly_nosck<T: Instance>(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||||
@ -550,7 +584,6 @@ impl<'d> Spi<'d, Async> {
|
|||||||
/// Create a new SPI driver, in TX-only mode, without SCK pin.
|
/// Create a new SPI driver, in TX-only mode, without SCK pin.
|
||||||
///
|
///
|
||||||
/// This can be useful for bit-banging non-SPI protocols.
|
/// This can be useful for bit-banging non-SPI protocols.
|
||||||
#[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons.
|
|
||||||
pub fn new_txonly_nosck<T: Instance>(
|
pub fn new_txonly_nosck<T: Instance>(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||||
@ -900,8 +933,8 @@ impl RegsExt for Regs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
|
fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> {
|
||||||
if sr.ovr() {
|
if sr.ovr() && ovr {
|
||||||
return Err(Error::Overrun);
|
return Err(Error::Overrun);
|
||||||
}
|
}
|
||||||
#[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))]
|
#[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))]
|
||||||
@ -927,11 +960,11 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> {
|
fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
let sr = regs.sr().read();
|
let sr = regs.sr().read();
|
||||||
|
|
||||||
check_error_flags(sr)?;
|
check_error_flags(sr, ovr)?;
|
||||||
|
|
||||||
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
|
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
|
||||||
if sr.txe() {
|
if sr.txe() {
|
||||||
@ -948,7 +981,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
|
|||||||
loop {
|
loop {
|
||||||
let sr = regs.sr().read();
|
let sr = regs.sr().read();
|
||||||
|
|
||||||
check_error_flags(sr)?;
|
check_error_flags(sr, true)?;
|
||||||
|
|
||||||
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
|
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
|
||||||
if sr.rxne() {
|
if sr.rxne() {
|
||||||
@ -1032,7 +1065,7 @@ fn finish_dma(regs: Regs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
|
fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
|
||||||
spin_until_tx_ready(regs)?;
|
spin_until_tx_ready(regs, true)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ptr::write_volatile(regs.tx_ptr(), tx_word);
|
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> {
|
|||||||
Ok(rx_word)
|
Ok(rx_word)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)] // unused in SPIv1
|
||||||
|
fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {
|
||||||
|
// for write, we intentionally ignore the rx fifo, which will cause
|
||||||
|
// overrun errors that we have to ignore.
|
||||||
|
spin_until_tx_ready(regs, false)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::write_volatile(regs.tx_ptr(), tx_word);
|
||||||
|
|
||||||
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
|
regs.cr1().modify(|reg| reg.set_cstart(true));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with
|
// Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with
|
||||||
// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289
|
// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289
|
||||||
macro_rules! impl_blocking {
|
macro_rules! impl_blocking {
|
||||||
|
@ -94,19 +94,11 @@ async fn main(_spawner: Spawner) {
|
|||||||
drop(spi);
|
drop(spi);
|
||||||
|
|
||||||
// Test tx-only nosck.
|
// Test tx-only nosck.
|
||||||
#[cfg(feature = "spi-v1")]
|
|
||||||
{
|
|
||||||
let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config);
|
let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config);
|
||||||
spi.blocking_transfer(&mut buf, &data).unwrap();
|
|
||||||
spi.blocking_transfer_in_place(&mut buf).unwrap();
|
|
||||||
spi.blocking_write(&buf).unwrap();
|
spi.blocking_write(&buf).unwrap();
|
||||||
spi.blocking_read(&mut buf).unwrap();
|
|
||||||
spi.blocking_transfer::<u8>(&mut [], &[]).unwrap();
|
|
||||||
spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();
|
|
||||||
spi.blocking_read::<u8>(&mut []).unwrap();
|
|
||||||
spi.blocking_write::<u8>(&[]).unwrap();
|
spi.blocking_write::<u8>(&[]).unwrap();
|
||||||
|
spi.blocking_write(&buf).unwrap();
|
||||||
drop(spi);
|
drop(spi);
|
||||||
}
|
|
||||||
|
|
||||||
info!("Test OK");
|
info!("Test OK");
|
||||||
cortex_m::asm::bkpt();
|
cortex_m::asm::bkpt();
|
||||||
|
@ -132,8 +132,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
drop(spi);
|
drop(spi);
|
||||||
|
|
||||||
// Test tx-only nosck.
|
// Test tx-only nosck.
|
||||||
#[cfg(feature = "spi-v1")]
|
|
||||||
{
|
|
||||||
let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config);
|
let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config);
|
||||||
spi.blocking_write(&buf).unwrap();
|
spi.blocking_write(&buf).unwrap();
|
||||||
spi.write(&buf).await.unwrap();
|
spi.write(&buf).await.unwrap();
|
||||||
@ -144,7 +142,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
spi.write::<u8>(&[]).await.unwrap();
|
spi.write::<u8>(&[]).await.unwrap();
|
||||||
spi.blocking_write::<u8>(&[]).unwrap();
|
spi.blocking_write::<u8>(&[]).unwrap();
|
||||||
drop(spi);
|
drop(spi);
|
||||||
}
|
|
||||||
|
|
||||||
info!("Test OK");
|
info!("Test OK");
|
||||||
cortex_m::asm::bkpt();
|
cortex_m::asm::bkpt();
|
||||||
|
Loading…
Reference in New Issue
Block a user