diff options
| author | xoviat <[email protected]> | 2021-04-13 16:11:06 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-04-13 16:11:06 -0500 |
| commit | 8e040cc5d25280a2baf8189428c80ae8c7081d2b (patch) | |
| tree | 6de6c0062157e27a8f2001d565ce7227f4978e31 | |
| parent | 0bd35373c02356bfa28e17b7dc630b5dacb74206 (diff) | |
stm32: add draft spi trait (#130)
| -rw-r--r-- | embassy-stm32/src/f4/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/f4/spi.rs | 479 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 |
3 files changed, 481 insertions, 1 deletions
diff --git a/embassy-stm32/src/f4/mod.rs b/embassy-stm32/src/f4/mod.rs index b1fc0cf1d..9edde82ca 100644 --- a/embassy-stm32/src/f4/mod.rs +++ b/embassy-stm32/src/f4/mod.rs | |||
| @@ -1 +1,2 @@ | |||
| 1 | pub mod serial; | 1 | pub mod serial; |
| 2 | pub mod spi; | ||
diff --git a/embassy-stm32/src/f4/spi.rs b/embassy-stm32/src/f4/spi.rs new file mode 100644 index 000000000..bc73611fd --- /dev/null +++ b/embassy-stm32/src/f4/spi.rs | |||
| @@ -0,0 +1,479 @@ | |||
| 1 | //! Async SPI | ||
| 2 | |||
| 3 | use embassy::time; | ||
| 4 | |||
| 5 | use core::{future::Future, marker::PhantomData, mem, ops::Deref, pin::Pin, ptr}; | ||
| 6 | use embassy::{interrupt::Interrupt, traits::spi::FullDuplex, util::InterruptFuture}; | ||
| 7 | use nb; | ||
| 8 | |||
| 9 | pub use crate::hal::spi::{Mode, Phase, Polarity}; | ||
| 10 | use crate::hal::{ | ||
| 11 | bb, dma, | ||
| 12 | dma::config::DmaConfig, | ||
| 13 | dma::traits::{Channel, DMASet, PeriAddress, Stream}, | ||
| 14 | dma::{MemoryToPeripheral, PeripheralToMemory, Transfer}, | ||
| 15 | rcc::Clocks, | ||
| 16 | spi::Pins, | ||
| 17 | time::Hertz, | ||
| 18 | }; | ||
| 19 | use crate::interrupt; | ||
| 20 | use crate::pac; | ||
| 21 | use futures::future; | ||
| 22 | |||
| 23 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 24 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 25 | #[non_exhaustive] | ||
| 26 | pub enum Error { | ||
| 27 | TxBufferTooLong, | ||
| 28 | RxBufferTooLong, | ||
| 29 | Overrun, | ||
| 30 | ModeFault, | ||
| 31 | Crc, | ||
| 32 | } | ||
| 33 | |||
| 34 | fn read_sr<T: Instance>(spi: &T) -> nb::Result<u8, Error> { | ||
| 35 | let sr = spi.sr.read(); | ||
| 36 | Err(if sr.ovr().bit_is_set() { | ||
| 37 | nb::Error::Other(Error::Overrun) | ||
| 38 | } else if sr.modf().bit_is_set() { | ||
| 39 | nb::Error::Other(Error::ModeFault) | ||
| 40 | } else if sr.crcerr().bit_is_set() { | ||
| 41 | nb::Error::Other(Error::Crc) | ||
| 42 | } else if sr.rxne().bit_is_set() { | ||
| 43 | // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows | ||
| 44 | // reading a half-word) | ||
| 45 | return Ok(unsafe { ptr::read_volatile(&spi.dr as *const _ as *const u8) }); | ||
| 46 | } else { | ||
| 47 | nb::Error::WouldBlock | ||
| 48 | }) | ||
| 49 | } | ||
| 50 | |||
| 51 | fn write_sr<T: Instance>(spi: &T, byte: u8) -> nb::Result<(), Error> { | ||
| 52 | let sr = spi.sr.read(); | ||
| 53 | Err(if sr.ovr().bit_is_set() { | ||
| 54 | // Read from the DR to clear the OVR bit | ||
| 55 | let _ = spi.dr.read(); | ||
| 56 | nb::Error::Other(Error::Overrun) | ||
| 57 | } else if sr.modf().bit_is_set() { | ||
| 58 | // Write to CR1 to clear MODF | ||
| 59 | spi.cr1.modify(|_r, w| w); | ||
| 60 | nb::Error::Other(Error::ModeFault) | ||
| 61 | } else if sr.crcerr().bit_is_set() { | ||
| 62 | // Clear the CRCERR bit | ||
| 63 | spi.sr.modify(|_r, w| { | ||
| 64 | w.crcerr().clear_bit(); | ||
| 65 | w | ||
| 66 | }); | ||
| 67 | nb::Error::Other(Error::Crc) | ||
| 68 | } else if sr.txe().bit_is_set() { | ||
| 69 | // NOTE(write_volatile) see note above | ||
| 70 | unsafe { ptr::write_volatile(&spi.dr as *const _ as *mut u8, byte) } | ||
| 71 | return Ok(()); | ||
| 72 | } else { | ||
| 73 | nb::Error::WouldBlock | ||
| 74 | }) | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Interface to the Serial peripheral | ||
| 78 | pub struct Spi< | ||
| 79 | SPI: PeriAddress<MemSize = u8> + WithInterrupt, | ||
| 80 | TSTREAM: Stream + WithInterrupt, | ||
| 81 | RSTREAM: Stream + WithInterrupt, | ||
| 82 | CHANNEL: Channel, | ||
| 83 | > { | ||
| 84 | tx_stream: Option<TSTREAM>, | ||
| 85 | rx_stream: Option<RSTREAM>, | ||
| 86 | spi: Option<SPI>, | ||
| 87 | tx_int: TSTREAM::Interrupt, | ||
| 88 | rx_int: RSTREAM::Interrupt, | ||
| 89 | spi_int: SPI::Interrupt, | ||
| 90 | channel: PhantomData<CHANNEL>, | ||
| 91 | } | ||
| 92 | |||
| 93 | impl<SPI, TSTREAM, RSTREAM, CHANNEL> Spi<SPI, TSTREAM, RSTREAM, CHANNEL> | ||
| 94 | where | ||
| 95 | SPI: Instance | ||
| 96 | + PeriAddress<MemSize = u8> | ||
| 97 | + DMASet<TSTREAM, CHANNEL, MemoryToPeripheral> | ||
| 98 | + DMASet<RSTREAM, CHANNEL, PeripheralToMemory> | ||
| 99 | + WithInterrupt, | ||
| 100 | TSTREAM: Stream + WithInterrupt, | ||
| 101 | RSTREAM: Stream + WithInterrupt, | ||
| 102 | CHANNEL: Channel, | ||
| 103 | { | ||
| 104 | // Leaking futures is forbidden! | ||
| 105 | pub unsafe fn new<PINS>( | ||
| 106 | spi: SPI, | ||
| 107 | streams: (TSTREAM, RSTREAM), | ||
| 108 | pins: PINS, | ||
| 109 | tx_int: TSTREAM::Interrupt, | ||
| 110 | rx_int: RSTREAM::Interrupt, | ||
| 111 | spi_int: SPI::Interrupt, | ||
| 112 | mode: Mode, | ||
| 113 | freq: Hertz, | ||
| 114 | clocks: Clocks, | ||
| 115 | ) -> Self | ||
| 116 | where | ||
| 117 | PINS: Pins<SPI>, | ||
| 118 | { | ||
| 119 | let (tx_stream, rx_stream) = streams; | ||
| 120 | |||
| 121 | // let spi1: crate::pac::SPI1 = unsafe { mem::transmute(()) }; | ||
| 122 | // let mut hspi = crate::hal::spi::Spi::spi1( | ||
| 123 | // spi1, | ||
| 124 | // ( | ||
| 125 | // crate::hal::spi::NoSck, | ||
| 126 | // crate::hal::spi::NoMiso, | ||
| 127 | // crate::hal::spi::NoMosi, | ||
| 128 | // ), | ||
| 129 | // mode, | ||
| 130 | // freq, | ||
| 131 | // clocks, | ||
| 132 | // ); | ||
| 133 | |||
| 134 | unsafe { SPI::enable_clock() }; | ||
| 135 | |||
| 136 | let clock = SPI::clock_speed(clocks); | ||
| 137 | |||
| 138 | // disable SS output | ||
| 139 | // spi.cr2 | ||
| 140 | // .write(|w| w.ssoe().clear_bit().rxdmaen().set_bit().txdmaen().set_bit()); | ||
| 141 | spi.cr2.write(|w| w.ssoe().clear_bit()); | ||
| 142 | |||
| 143 | let br = match clock.0 / freq.0 { | ||
| 144 | 0 => unreachable!(), | ||
| 145 | 1..=2 => 0b000, | ||
| 146 | 3..=5 => 0b001, | ||
| 147 | 6..=11 => 0b010, | ||
| 148 | 12..=23 => 0b011, | ||
| 149 | 24..=47 => 0b100, | ||
| 150 | 48..=95 => 0b101, | ||
| 151 | 96..=191 => 0b110, | ||
| 152 | _ => 0b111, | ||
| 153 | }; | ||
| 154 | |||
| 155 | // mstr: master configuration | ||
| 156 | // lsbfirst: MSB first | ||
| 157 | // ssm: enable software slave management (NSS pin free for other uses) | ||
| 158 | // ssi: set nss high = master mode | ||
| 159 | // dff: 8 bit frames | ||
| 160 | // bidimode: 2-line unidirectional | ||
| 161 | // spe: enable the SPI bus | ||
| 162 | spi.cr1.write(|w| { | ||
| 163 | w.cpha() | ||
| 164 | .bit(mode.phase == Phase::CaptureOnSecondTransition) | ||
| 165 | .cpol() | ||
| 166 | .bit(mode.polarity == Polarity::IdleHigh) | ||
| 167 | .mstr() | ||
| 168 | .set_bit() | ||
| 169 | .br() | ||
| 170 | .bits(br) | ||
| 171 | .lsbfirst() | ||
| 172 | .clear_bit() | ||
| 173 | .ssm() | ||
| 174 | .set_bit() | ||
| 175 | .ssi() | ||
| 176 | .set_bit() | ||
| 177 | .rxonly() | ||
| 178 | .clear_bit() | ||
| 179 | .dff() | ||
| 180 | .clear_bit() | ||
| 181 | .bidimode() | ||
| 182 | .clear_bit() | ||
| 183 | .spe() | ||
| 184 | .set_bit() | ||
| 185 | }); | ||
| 186 | |||
| 187 | Self { | ||
| 188 | tx_stream: Some(tx_stream), | ||
| 189 | rx_stream: Some(rx_stream), | ||
| 190 | spi: Some(spi), | ||
| 191 | tx_int: tx_int, | ||
| 192 | rx_int: rx_int, | ||
| 193 | spi_int: spi_int, | ||
| 194 | channel: PhantomData, | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | impl<SPI, TSTREAM, RSTREAM, CHANNEL> FullDuplex<u8> for Spi<SPI, TSTREAM, RSTREAM, CHANNEL> | ||
| 200 | where | ||
| 201 | SPI: Instance | ||
| 202 | + PeriAddress<MemSize = u8> | ||
| 203 | + DMASet<TSTREAM, CHANNEL, MemoryToPeripheral> | ||
| 204 | + DMASet<RSTREAM, CHANNEL, PeripheralToMemory> | ||
| 205 | + WithInterrupt | ||
| 206 | + 'static, | ||
| 207 | TSTREAM: Stream + WithInterrupt + 'static, | ||
| 208 | RSTREAM: Stream + WithInterrupt + 'static, | ||
| 209 | CHANNEL: Channel + 'static, | ||
| 210 | { | ||
| 211 | type Error = Error; | ||
| 212 | |||
| 213 | type WriteFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||
| 214 | type ReadFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||
| 215 | type WriteReadFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||
| 216 | |||
| 217 | fn read<'a>(self: Pin<&'a mut Self>, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 218 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 219 | #[allow(mutable_transmutes)] | ||
| 220 | let static_buf: &'static mut [u8] = unsafe { mem::transmute(buf) }; | ||
| 221 | |||
| 222 | async move { | ||
| 223 | let rx_stream = this.rx_stream.take().unwrap(); | ||
| 224 | let spi = this.spi.take().unwrap(); | ||
| 225 | |||
| 226 | spi.cr2.modify(|_, w| w.errie().set_bit()); | ||
| 227 | |||
| 228 | let mut rx_transfer = Transfer::init( | ||
| 229 | rx_stream, | ||
| 230 | spi, | ||
| 231 | static_buf, | ||
| 232 | None, | ||
| 233 | DmaConfig::default() | ||
| 234 | .transfer_complete_interrupt(true) | ||
| 235 | .memory_increment(true) | ||
| 236 | .double_buffer(false), | ||
| 237 | ); | ||
| 238 | |||
| 239 | let fut = InterruptFuture::new(&mut this.rx_int); | ||
| 240 | let fut_err = InterruptFuture::new(&mut this.spi_int); | ||
| 241 | |||
| 242 | rx_transfer.start(|_spi| {}); | ||
| 243 | future::select(fut, fut_err).await; | ||
| 244 | |||
| 245 | let (rx_stream, spi, _buf, _) = rx_transfer.free(); | ||
| 246 | |||
| 247 | spi.cr2.modify(|_, w| w.errie().clear_bit()); | ||
| 248 | this.rx_stream.replace(rx_stream); | ||
| 249 | this.spi.replace(spi); | ||
| 250 | |||
| 251 | Ok(()) | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | fn write<'a>(self: Pin<&'a mut Self>, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 256 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 257 | #[allow(mutable_transmutes)] | ||
| 258 | let static_buf: &'static mut [u8] = unsafe { mem::transmute(buf) }; | ||
| 259 | |||
| 260 | async move { | ||
| 261 | let tx_stream = this.tx_stream.take().unwrap(); | ||
| 262 | let spi = this.spi.take().unwrap(); | ||
| 263 | |||
| 264 | // let mut tx_transfer = Transfer::init( | ||
| 265 | // tx_stream, | ||
| 266 | // spi, | ||
| 267 | // static_buf, | ||
| 268 | // None, | ||
| 269 | // DmaConfig::default() | ||
| 270 | // .transfer_complete_interrupt(true) | ||
| 271 | // .memory_increment(true) | ||
| 272 | // .double_buffer(false), | ||
| 273 | // ); | ||
| 274 | // | ||
| 275 | // let fut = InterruptFuture::new(&mut this.tx_int); | ||
| 276 | // | ||
| 277 | // tx_transfer.start(|_spi| {}); | ||
| 278 | // fut.await; | ||
| 279 | |||
| 280 | // let (tx_stream, spi, _buf, _) = tx_transfer.free(); | ||
| 281 | |||
| 282 | for i in 0..(static_buf.len() - 1) { | ||
| 283 | let byte = static_buf[i]; | ||
| 284 | nb::block!(write_sr(&spi, byte)); | ||
| 285 | } | ||
| 286 | |||
| 287 | this.tx_stream.replace(tx_stream); | ||
| 288 | this.spi.replace(spi); | ||
| 289 | |||
| 290 | Ok(()) | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | fn read_write<'a>( | ||
| 295 | self: Pin<&'a mut Self>, | ||
| 296 | read_buf: &'a mut [u8], | ||
| 297 | write_buf: &'a [u8], | ||
| 298 | ) -> Self::WriteReadFuture<'a> { | ||
| 299 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 300 | |||
| 301 | #[allow(mutable_transmutes)] | ||
| 302 | let write_static_buf: &'static mut [u8] = unsafe { mem::transmute(write_buf) }; | ||
| 303 | let read_static_buf: &'static mut [u8] = unsafe { mem::transmute(read_buf) }; | ||
| 304 | |||
| 305 | async move { | ||
| 306 | let tx_stream = this.tx_stream.take().unwrap(); | ||
| 307 | let rx_stream = this.rx_stream.take().unwrap(); | ||
| 308 | let spi_tx = this.spi.take().unwrap(); | ||
| 309 | let spi_rx: SPI = unsafe { mem::transmute_copy(&spi_tx) }; | ||
| 310 | |||
| 311 | spi_rx | ||
| 312 | .cr2 | ||
| 313 | .modify(|_, w| w.errie().set_bit().txeie().set_bit().rxneie().set_bit()); | ||
| 314 | |||
| 315 | // let mut tx_transfer = Transfer::init( | ||
| 316 | // tx_stream, | ||
| 317 | // spi_tx, | ||
| 318 | // write_static_buf, | ||
| 319 | // None, | ||
| 320 | // DmaConfig::default() | ||
| 321 | // .transfer_complete_interrupt(true) | ||
| 322 | // .memory_increment(true) | ||
| 323 | // .double_buffer(false), | ||
| 324 | // ); | ||
| 325 | // | ||
| 326 | // let mut rx_transfer = Transfer::init( | ||
| 327 | // rx_stream, | ||
| 328 | // spi_rx, | ||
| 329 | // read_static_buf, | ||
| 330 | // None, | ||
| 331 | // DmaConfig::default() | ||
| 332 | // .transfer_complete_interrupt(true) | ||
| 333 | // .memory_increment(true) | ||
| 334 | // .double_buffer(false), | ||
| 335 | // ); | ||
| 336 | // | ||
| 337 | // let tx_fut = InterruptFuture::new(&mut this.tx_int); | ||
| 338 | // let rx_fut = InterruptFuture::new(&mut this.rx_int); | ||
| 339 | // let rx_fut_err = InterruptFuture::new(&mut this.spi_int); | ||
| 340 | // | ||
| 341 | // rx_transfer.start(|_spi| {}); | ||
| 342 | // tx_transfer.start(|_spi| {}); | ||
| 343 | // | ||
| 344 | // time::Timer::after(time::Duration::from_millis(500)).await; | ||
| 345 | // | ||
| 346 | // // tx_fut.await; | ||
| 347 | // // future::select(rx_fut, rx_fut_err).await; | ||
| 348 | // | ||
| 349 | // let (rx_stream, spi_rx, _buf, _) = rx_transfer.free(); | ||
| 350 | // let (tx_stream, _, _buf, _) = tx_transfer.free(); | ||
| 351 | |||
| 352 | for i in 0..(read_static_buf.len() - 1) { | ||
| 353 | let byte = write_static_buf[i]; | ||
| 354 | loop { | ||
| 355 | let fut = InterruptFuture::new(&mut this.spi_int); | ||
| 356 | match write_sr(&spi_tx, byte) { | ||
| 357 | Ok(()) => break, | ||
| 358 | _ => {} | ||
| 359 | } | ||
| 360 | fut.await; | ||
| 361 | } | ||
| 362 | |||
| 363 | loop { | ||
| 364 | let fut = InterruptFuture::new(&mut this.spi_int); | ||
| 365 | match read_sr(&spi_tx) { | ||
| 366 | Ok(byte) => { | ||
| 367 | read_static_buf[i] = byte; | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | _ => {} | ||
| 371 | } | ||
| 372 | fut.await; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | spi_rx.cr2.modify(|_, w| { | ||
| 377 | w.errie() | ||
| 378 | .clear_bit() | ||
| 379 | .txeie() | ||
| 380 | .clear_bit() | ||
| 381 | .rxneie() | ||
| 382 | .clear_bit() | ||
| 383 | }); | ||
| 384 | this.rx_stream.replace(rx_stream); | ||
| 385 | this.tx_stream.replace(tx_stream); | ||
| 386 | this.spi.replace(spi_rx); | ||
| 387 | |||
| 388 | Ok(()) | ||
| 389 | } | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | mod private { | ||
| 394 | pub trait Sealed {} | ||
| 395 | } | ||
| 396 | |||
| 397 | pub trait WithInterrupt: private::Sealed { | ||
| 398 | type Interrupt: Interrupt; | ||
| 399 | } | ||
| 400 | |||
| 401 | pub trait Instance: Deref<Target = pac::spi1::RegisterBlock> + private::Sealed { | ||
| 402 | unsafe fn enable_clock(); | ||
| 403 | fn clock_speed(clocks: Clocks) -> Hertz; | ||
| 404 | } | ||
| 405 | |||
| 406 | macro_rules! dma { | ||
| 407 | ($($PER:ident => ($dma:ident, $stream:ident),)+) => { | ||
| 408 | $( | ||
| 409 | impl private::Sealed for dma::$stream<pac::$dma> {} | ||
| 410 | impl WithInterrupt for dma::$stream<pac::$dma> { | ||
| 411 | type Interrupt = interrupt::$PER; | ||
| 412 | } | ||
| 413 | )+ | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | macro_rules! spi { | ||
| 418 | ($($PER:ident => ($SPI:ident, $pclkX:ident, $apbXenr:ident, $en:expr),)+) => { | ||
| 419 | $( | ||
| 420 | impl private::Sealed for pac::$SPI {} | ||
| 421 | impl Instance for pac::$SPI { | ||
| 422 | unsafe fn enable_clock() { | ||
| 423 | const EN_BIT: u8 = $en; | ||
| 424 | // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. | ||
| 425 | let rcc = &(*pac::RCC::ptr()); | ||
| 426 | // Enable clock. | ||
| 427 | bb::set(&rcc.$apbXenr, EN_BIT); | ||
| 428 | // Stall the pipeline to work around erratum 2.1.13 (DM00037591) | ||
| 429 | cortex_m::asm::dsb(); | ||
| 430 | } | ||
| 431 | |||
| 432 | fn clock_speed(clocks: Clocks) -> Hertz { | ||
| 433 | clocks.$pclkX() | ||
| 434 | } | ||
| 435 | } | ||
| 436 | impl WithInterrupt for pac::$SPI { | ||
| 437 | type Interrupt = interrupt::$PER; | ||
| 438 | } | ||
| 439 | )+ | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | dma! { | ||
| 444 | DMA2_STREAM0 => (DMA2, Stream0), | ||
| 445 | DMA2_STREAM1 => (DMA2, Stream1), | ||
| 446 | DMA2_STREAM2 => (DMA2, Stream2), | ||
| 447 | DMA2_STREAM3 => (DMA2, Stream3), | ||
| 448 | DMA2_STREAM4 => (DMA2, Stream4), | ||
| 449 | DMA2_STREAM5 => (DMA2, Stream5), | ||
| 450 | DMA2_STREAM6 => (DMA2, Stream6), | ||
| 451 | DMA2_STREAM7 => (DMA2, Stream7), | ||
| 452 | DMA1_STREAM0 => (DMA1, Stream0), | ||
| 453 | DMA1_STREAM1 => (DMA1, Stream1), | ||
| 454 | DMA1_STREAM2 => (DMA1, Stream2), | ||
| 455 | DMA1_STREAM3 => (DMA1, Stream3), | ||
| 456 | DMA1_STREAM4 => (DMA1, Stream4), | ||
| 457 | DMA1_STREAM5 => (DMA1, Stream5), | ||
| 458 | DMA1_STREAM6 => (DMA1, Stream6), | ||
| 459 | } | ||
| 460 | |||
| 461 | #[cfg(any( | ||
| 462 | feature = "stm32f401", | ||
| 463 | feature = "stm32f410", | ||
| 464 | feature = "stm32f411", | ||
| 465 | feature = "stm32f446", | ||
| 466 | ))] | ||
| 467 | spi! { | ||
| 468 | SPI1 => (SPI1, pclk2, apb2enr, 12), | ||
| 469 | SPI2 => (SPI2, pclk1, apb2enr, 14), | ||
| 470 | // SPI6 => (SPI6, pclk2, apb2enr, 21), | ||
| 471 | SPI4 => (SPI3, pclk2, apb2enr, 13), | ||
| 472 | // SPI5 => (SPI3, pclk2, apb2enr, 20), | ||
| 473 | } | ||
| 474 | |||
| 475 | #[cfg(any(feature = "stm32f405", feature = "stm32f407"))] | ||
| 476 | spi! { | ||
| 477 | SPI1 => (SPI1, pclk2, apb2enr, 12), | ||
| 478 | SPI3 => (SPI3, pclk1, apb2enr, 15), | ||
| 479 | } | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 370b5f0db..56efa461c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -109,7 +109,7 @@ pub mod rtc; | |||
| 109 | feature = "stm32f469", | 109 | feature = "stm32f469", |
| 110 | feature = "stm32f479", | 110 | feature = "stm32f479", |
| 111 | ))] | 111 | ))] |
| 112 | pub use f4::serial; | 112 | pub use f4::{serial, spi}; |
| 113 | 113 | ||
| 114 | #[cfg(any( | 114 | #[cfg(any( |
| 115 | feature = "stm32f401", | 115 | feature = "stm32f401", |
