diff options
| -rw-r--r-- | embassy-stm32/src/i2s.rs | 339 | ||||
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 13 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2s_dma.rs | 13 |
3 files changed, 279 insertions, 86 deletions
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 094de2461..f11371f98 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -1,12 +1,13 @@ | |||
| 1 | //! Inter-IC Sound (I2S) | 1 | //! Inter-IC Sound (I2S) |
| 2 | 2 | ||
| 3 | use embassy_futures::join::join; | ||
| 3 | use embassy_hal_internal::into_ref; | 4 | use embassy_hal_internal::into_ref; |
| 5 | use stm32_metapac::spi::vals; | ||
| 4 | 6 | ||
| 5 | use crate::dma::ChannelAndRequest; | 7 | use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; |
| 6 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 7 | use crate::mode::Async; | 9 | use crate::mode::Async; |
| 8 | use crate::pac::spi::vals; | 10 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; |
| 9 | use crate::spi::{Config as SpiConfig, *}; | ||
| 10 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 11 | use crate::{Peripheral, PeripheralRef}; | 12 | use crate::{Peripheral, PeripheralRef}; |
| 12 | 13 | ||
| @@ -19,6 +20,19 @@ pub enum Mode { | |||
| 19 | Slave, | 20 | Slave, |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 23 | /// I2S function | ||
| 24 | #[derive(Copy, Clone)] | ||
| 25 | #[allow(dead_code)] | ||
| 26 | enum Function { | ||
| 27 | /// Transmit audio data | ||
| 28 | Transmit, | ||
| 29 | /// Receive audio data | ||
| 30 | Receive, | ||
| 31 | #[cfg(spi_v3)] | ||
| 32 | /// Transmit and Receive audio data | ||
| 33 | FullDuplex, | ||
| 34 | } | ||
| 35 | |||
| 22 | /// I2C standard | 36 | /// I2C standard |
| 23 | #[derive(Copy, Clone)] | 37 | #[derive(Copy, Clone)] |
| 24 | pub enum Standard { | 38 | pub enum Standard { |
| @@ -34,6 +48,30 @@ pub enum Standard { | |||
| 34 | PcmShortSync, | 48 | PcmShortSync, |
| 35 | } | 49 | } |
| 36 | 50 | ||
| 51 | /// SAI error | ||
| 52 | #[derive(Debug, PartialEq, Eq)] | ||
| 53 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 54 | pub enum Error { | ||
| 55 | /// `write` called on a SAI in receive mode. | ||
| 56 | NotATransmitter, | ||
| 57 | /// `read` called on a SAI in transmit mode. | ||
| 58 | NotAReceiver, | ||
| 59 | /// Overrun | ||
| 60 | Overrun, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl From<ringbuffer::Error> for Error { | ||
| 64 | fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { | ||
| 65 | #[cfg(feature = "defmt")] | ||
| 66 | { | ||
| 67 | if err == ringbuffer::Error::DmaUnsynced { | ||
| 68 | defmt::error!("Ringbuffer broken invariants detected!"); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | Self::Overrun | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 37 | impl Standard { | 75 | impl Standard { |
| 38 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | 76 | #[cfg(any(spi_v1, spi_v3, spi_f1))] |
| 39 | const fn i2sstd(&self) -> vals::I2sstd { | 77 | const fn i2sstd(&self) -> vals::I2sstd { |
| @@ -142,31 +180,62 @@ impl Default for Config { | |||
| 142 | } | 180 | } |
| 143 | } | 181 | } |
| 144 | 182 | ||
| 183 | /// I2S driver writer. Useful for moving write functionality across tasks. | ||
| 184 | pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>); | ||
| 185 | |||
| 186 | impl<'s, 'd, W: Word> Writer<'s, 'd, W> { | ||
| 187 | /// Write data to the I2S ringbuffer. | ||
| 188 | /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. | ||
| 189 | /// If thfre’s no space in the buffer, this waits until there is. | ||
| 190 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { | ||
| 191 | self.0.write_exact(data).await?; | ||
| 192 | Ok(()) | ||
| 193 | } | ||
| 194 | |||
| 195 | /// Reset the ring buffer to its initial state. | ||
| 196 | /// Can be used to recover from overrun. | ||
| 197 | /// The ringbuffer will always auto-reset on Overrun in any case. | ||
| 198 | pub fn reset(&mut self) { | ||
| 199 | self.0.clear(); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /// I2S driver reader. Useful for moving read functionality across tasks. | ||
| 204 | pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>); | ||
| 205 | |||
| 206 | impl<'s, 'd, W: Word> Reader<'s, 'd, W> { | ||
| 207 | /// Read data from the I2S ringbuffer. | ||
| 208 | /// SAI is always receiving data in the background. This function pops already-received data from the buffer. | ||
| 209 | /// If there’s less than data.len() data in the buffer, this waits until there is. | ||
| 210 | pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { | ||
| 211 | self.0.read_exact(data).await?; | ||
| 212 | Ok(()) | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Reset the ring buffer to its initial state. | ||
| 216 | /// Can be used to prevent overrun. | ||
| 217 | /// The ringbuffer will always auto-reset on Overrun in any case. | ||
| 218 | pub fn reset(&mut self) { | ||
| 219 | self.0.clear(); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 145 | /// I2S driver. | 223 | /// I2S driver. |
| 146 | pub struct I2S<'d> { | 224 | pub struct I2S<'d, W: Word> { |
| 147 | _peri: Spi<'d, Async>, | 225 | #[allow(dead_code)] |
| 226 | mode: Mode, | ||
| 227 | spi: Spi<'d, Async>, | ||
| 148 | txsd: Option<PeripheralRef<'d, AnyPin>>, | 228 | txsd: Option<PeripheralRef<'d, AnyPin>>, |
| 149 | rxsd: Option<PeripheralRef<'d, AnyPin>>, | 229 | rxsd: Option<PeripheralRef<'d, AnyPin>>, |
| 150 | ws: Option<PeripheralRef<'d, AnyPin>>, | 230 | ws: Option<PeripheralRef<'d, AnyPin>>, |
| 151 | ck: Option<PeripheralRef<'d, AnyPin>>, | 231 | ck: Option<PeripheralRef<'d, AnyPin>>, |
| 152 | mck: Option<PeripheralRef<'d, AnyPin>>, | 232 | mck: Option<PeripheralRef<'d, AnyPin>>, |
| 233 | tx_ring_buffer: Option<WritableRingBuffer<'d, W>>, | ||
| 234 | rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>, | ||
| 153 | } | 235 | } |
| 154 | 236 | ||
| 155 | /// I2S function | 237 | impl<'d, W: Word> I2S<'d, W> { |
| 156 | #[derive(Copy, Clone)] | 238 | /// Create a transmitter driver. |
| 157 | #[allow(dead_code)] | ||
| 158 | enum Function { | ||
| 159 | /// Transmit audio data | ||
| 160 | Transmit, | ||
| 161 | /// Receive audio data | ||
| 162 | Receive, | ||
| 163 | #[cfg(spi_v3)] | ||
| 164 | /// Transmit and Receive audio data | ||
| 165 | FullDuplex, | ||
| 166 | } | ||
| 167 | |||
| 168 | impl<'d> I2S<'d> { | ||
| 169 | /// Create a transmitter driver | ||
| 170 | pub fn new_txonly<T: Instance>( | 239 | pub fn new_txonly<T: Instance>( |
| 171 | peri: impl Peripheral<P = T> + 'd, | 240 | peri: impl Peripheral<P = T> + 'd, |
| 172 | sd: impl Peripheral<P = impl MosiPin<T>> + 'd, | 241 | sd: impl Peripheral<P = impl MosiPin<T>> + 'd, |
| @@ -174,18 +243,18 @@ impl<'d> I2S<'d> { | |||
| 174 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 243 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, |
| 175 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 244 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, |
| 176 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | 245 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, |
| 246 | txdma_buf: &'d mut [W], | ||
| 177 | freq: Hertz, | 247 | freq: Hertz, |
| 178 | config: Config, | 248 | config: Config, |
| 179 | ) -> Self { | 249 | ) -> Self { |
| 180 | into_ref!(sd); | ||
| 181 | Self::new_inner( | 250 | Self::new_inner( |
| 182 | peri, | 251 | peri, |
| 183 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 252 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 184 | None, | 253 | None, |
| 185 | ws, | 254 | ws, |
| 186 | ck, | 255 | ck, |
| 187 | mck, | 256 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 188 | new_dma!(txdma), | 257 | new_dma!(txdma).map(|d| (d, txdma_buf)), |
| 189 | None, | 258 | None, |
| 190 | freq, | 259 | freq, |
| 191 | config, | 260 | config, |
| @@ -193,42 +262,61 @@ impl<'d> I2S<'d> { | |||
| 193 | ) | 262 | ) |
| 194 | } | 263 | } |
| 195 | 264 | ||
| 196 | /// Create a receiver driver | 265 | /// Create a transmitter driver without a master clock pin. |
| 266 | pub fn new_txonly_nomck<T: Instance>( | ||
| 267 | peri: impl Peripheral<P = T> + 'd, | ||
| 268 | sd: impl Peripheral<P = impl MosiPin<T>> + 'd, | ||
| 269 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | ||
| 270 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | ||
| 271 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | ||
| 272 | txdma_buf: &'d mut [W], | ||
| 273 | freq: Hertz, | ||
| 274 | config: Config, | ||
| 275 | ) -> Self { | ||
| 276 | Self::new_inner( | ||
| 277 | peri, | ||
| 278 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 279 | None, | ||
| 280 | ws, | ||
| 281 | ck, | ||
| 282 | None, | ||
| 283 | new_dma!(txdma).map(|d| (d, txdma_buf)), | ||
| 284 | None, | ||
| 285 | freq, | ||
| 286 | config, | ||
| 287 | Function::Transmit, | ||
| 288 | ) | ||
| 289 | } | ||
| 290 | |||
| 291 | /// Create a receiver driver. | ||
| 197 | pub fn new_rxonly<T: Instance>( | 292 | pub fn new_rxonly<T: Instance>( |
| 198 | peri: impl Peripheral<P = T> + 'd, | 293 | peri: impl Peripheral<P = T> + 'd, |
| 199 | sd: impl Peripheral<P = impl MisoPin<T>> + 'd, | 294 | sd: impl Peripheral<P = impl MisoPin<T>> + 'd, |
| 200 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 295 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, |
| 201 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 296 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, |
| 202 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 297 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, |
| 203 | #[cfg(not(spi_v3))] txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | ||
| 204 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, | 298 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, |
| 299 | rxdma_buf: &'d mut [W], | ||
| 205 | freq: Hertz, | 300 | freq: Hertz, |
| 206 | config: Config, | 301 | config: Config, |
| 207 | ) -> Self { | 302 | ) -> Self { |
| 208 | into_ref!(sd); | ||
| 209 | Self::new_inner( | 303 | Self::new_inner( |
| 210 | peri, | 304 | peri, |
| 211 | None, | 305 | None, |
| 212 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 306 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 213 | ws, | 307 | ws, |
| 214 | ck, | 308 | ck, |
| 215 | mck, | 309 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 216 | #[cfg(not(spi_v3))] | ||
| 217 | new_dma!(txdma), | ||
| 218 | #[cfg(spi_v3)] | ||
| 219 | None, | 310 | None, |
| 220 | new_dma!(rxdma), | 311 | new_dma!(rxdma).map(|d| (d, rxdma_buf)), |
| 221 | freq, | 312 | freq, |
| 222 | config, | 313 | config, |
| 223 | #[cfg(not(spi_v3))] | ||
| 224 | Function::Transmit, | ||
| 225 | #[cfg(spi_v3)] | ||
| 226 | Function::Receive, | 314 | Function::Receive, |
| 227 | ) | 315 | ) |
| 228 | } | 316 | } |
| 229 | 317 | ||
| 230 | #[cfg(spi_v3)] | 318 | #[cfg(spi_v3)] |
| 231 | /// Create a full duplex transmitter driver | 319 | /// Create a full duplex driver. |
| 232 | pub fn new_full_duplex<T: Instance>( | 320 | pub fn new_full_duplex<T: Instance>( |
| 233 | peri: impl Peripheral<P = T> + 'd, | 321 | peri: impl Peripheral<P = T> + 'd, |
| 234 | txsd: impl Peripheral<P = impl MosiPin<T>> + 'd, | 322 | txsd: impl Peripheral<P = impl MosiPin<T>> + 'd, |
| @@ -237,44 +325,144 @@ impl<'d> I2S<'d> { | |||
| 237 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 325 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, |
| 238 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 326 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, |
| 239 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | 327 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, |
| 328 | txdma_buf: &'d mut [W], | ||
| 240 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, | 329 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, |
| 330 | rxdma_buf: &'d mut [W], | ||
| 241 | freq: Hertz, | 331 | freq: Hertz, |
| 242 | config: Config, | 332 | config: Config, |
| 243 | ) -> Self { | 333 | ) -> Self { |
| 244 | into_ref!(txsd, rxsd); | ||
| 245 | Self::new_inner( | 334 | Self::new_inner( |
| 246 | peri, | 335 | peri, |
| 247 | new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 336 | new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 248 | new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 337 | new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 249 | ws, | 338 | ws, |
| 250 | ck, | 339 | ck, |
| 251 | mck, | 340 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 252 | new_dma!(txdma), | 341 | new_dma!(txdma).map(|d| (d, txdma_buf)), |
| 253 | new_dma!(rxdma), | 342 | new_dma!(rxdma).map(|d| (d, rxdma_buf)), |
| 254 | freq, | 343 | freq, |
| 255 | config, | 344 | config, |
| 256 | Function::FullDuplex, | 345 | Function::FullDuplex, |
| 257 | ) | 346 | ) |
| 258 | } | 347 | } |
| 259 | 348 | ||
| 260 | /// Write audio data. | 349 | /// Start I2S driver. |
| 261 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 350 | pub fn start(&mut self) { |
| 262 | self._peri.read(data).await | 351 | self.spi.info.regs.cr1().modify(|w| { |
| 352 | w.set_spe(false); | ||
| 353 | }); | ||
| 354 | self.spi.set_word_size(W::CONFIG); | ||
| 355 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 356 | tx_ring_buffer.start(); | ||
| 357 | |||
| 358 | set_txdmaen(self.spi.info.regs, true); | ||
| 359 | } | ||
| 360 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 361 | rx_ring_buffer.start(); | ||
| 362 | // SPIv3 clears rxfifo on SPE=0 | ||
| 363 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | ||
| 364 | flush_rx_fifo(self.spi.info.regs); | ||
| 365 | |||
| 366 | set_rxdmaen(self.spi.info.regs, true); | ||
| 367 | } | ||
| 368 | self.spi.info.regs.cr1().modify(|w| { | ||
| 369 | w.set_spe(true); | ||
| 370 | }); | ||
| 371 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | ||
| 372 | self.spi.info.regs.cr1().modify(|w| { | ||
| 373 | w.set_cstart(true); | ||
| 374 | }); | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Reset the ring buffer to its initial state. | ||
| 378 | /// Can be used to recover from overrun. | ||
| 379 | pub fn clear(&mut self) { | ||
| 380 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 381 | rx_ring_buffer.clear(); | ||
| 382 | } | ||
| 383 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 384 | tx_ring_buffer.clear(); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | /// Stop I2S driver. | ||
| 389 | pub async fn stop(&mut self) { | ||
| 390 | let regs = self.spi.info.regs; | ||
| 391 | |||
| 392 | let tx_f = async { | ||
| 393 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 394 | tx_ring_buffer.stop().await; | ||
| 395 | |||
| 396 | set_txdmaen(regs, false); | ||
| 397 | } | ||
| 398 | }; | ||
| 399 | |||
| 400 | let rx_f = async { | ||
| 401 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 402 | rx_ring_buffer.stop().await; | ||
| 403 | |||
| 404 | set_rxdmaen(regs, false); | ||
| 405 | } | ||
| 406 | }; | ||
| 407 | |||
| 408 | join(rx_f, tx_f).await; | ||
| 409 | |||
| 410 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | ||
| 411 | { | ||
| 412 | if let Mode::Master = self.mode { | ||
| 413 | regs.cr1().modify(|w| { | ||
| 414 | w.set_csusp(true); | ||
| 415 | }); | ||
| 416 | |||
| 417 | while regs.cr1().read().cstart() {} | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | regs.cr1().modify(|w| { | ||
| 422 | w.set_spe(false); | ||
| 423 | }); | ||
| 424 | |||
| 425 | self.clear(); | ||
| 426 | } | ||
| 427 | |||
| 428 | /// Split the driver into a Reader/Writer pair. | ||
| 429 | /// Useful for splitting the reader/writer functionality across tasks or | ||
| 430 | /// for calling the read/write methods in parallel. | ||
| 431 | pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> { | ||
| 432 | match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) { | ||
| 433 | (None, _) => Err(Error::NotAReceiver), | ||
| 434 | (_, None) => Err(Error::NotATransmitter), | ||
| 435 | (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))), | ||
| 436 | } | ||
| 263 | } | 437 | } |
| 264 | 438 | ||
| 265 | /// Write audio data. | 439 | /// Read data from the I2S ringbuffer. |
| 266 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 440 | /// SAI is always receiving data in the background. This function pops already-received data from the buffer. |
| 267 | self._peri.write(data).await | 441 | /// If there’s less than data.len() data in the buffer, this waits until there is. |
| 442 | pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { | ||
| 443 | match &mut self.rx_ring_buffer { | ||
| 444 | Some(ring) => Reader(ring).read(data).await, | ||
| 445 | _ => Err(Error::NotAReceiver), | ||
| 446 | } | ||
| 268 | } | 447 | } |
| 269 | 448 | ||
| 270 | /// Transfer audio data. | 449 | /// Write data to the I2S ringbuffer. |
| 271 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 450 | /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. |
| 272 | self._peri.transfer(read, write).await | 451 | /// If thfre’s no space in the buffer, this waits until there is. |
| 452 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { | ||
| 453 | match &mut self.tx_ring_buffer { | ||
| 454 | Some(ring) => Writer(ring).write(data).await, | ||
| 455 | _ => Err(Error::NotATransmitter), | ||
| 456 | } | ||
| 273 | } | 457 | } |
| 274 | 458 | ||
| 275 | /// Transfer audio data in place. | 459 | /// Write data directly to the raw I2S ringbuffer. |
| 276 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 460 | /// This can be used to fill the buffer before starting the DMA transfer. |
| 277 | self._peri.transfer_in_place(data).await | 461 | pub async fn write_immediate(&mut self, data: &mut [W]) -> Result<(usize, usize), Error> { |
| 462 | match &mut self.tx_ring_buffer { | ||
| 463 | Some(ring) => Ok(ring.write_immediate(data)?), | ||
| 464 | _ => return Err(Error::NotATransmitter), | ||
| 465 | } | ||
| 278 | } | 466 | } |
| 279 | 467 | ||
| 280 | fn new_inner<T: Instance>( | 468 | fn new_inner<T: Instance>( |
| @@ -283,23 +471,23 @@ impl<'d> I2S<'d> { | |||
| 283 | rxsd: Option<PeripheralRef<'d, AnyPin>>, | 471 | rxsd: Option<PeripheralRef<'d, AnyPin>>, |
| 284 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 472 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, |
| 285 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 473 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, |
| 286 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 474 | mck: Option<PeripheralRef<'d, AnyPin>>, |
| 287 | txdma: Option<ChannelAndRequest<'d>>, | 475 | txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 288 | rxdma: Option<ChannelAndRequest<'d>>, | 476 | rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 289 | freq: Hertz, | 477 | freq: Hertz, |
| 290 | config: Config, | 478 | config: Config, |
| 291 | function: Function, | 479 | function: Function, |
| 292 | ) -> Self { | 480 | ) -> Self { |
| 293 | into_ref!(ws, ck, mck); | 481 | into_ref!(ws, ck); |
| 294 | 482 | ||
| 295 | ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 483 | ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 296 | ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 484 | ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 297 | mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 298 | 485 | ||
| 299 | let mut spi_cfg = SpiConfig::default(); | 486 | let spi = Spi::new_internal(peri, None, None, { |
| 300 | spi_cfg.frequency = freq; | 487 | let mut config = SpiConfig::default(); |
| 301 | 488 | config.frequency = freq; | |
| 302 | let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); | 489 | config |
| 490 | }); | ||
| 303 | 491 | ||
| 304 | let regs = T::info().regs; | 492 | let regs = T::info().regs; |
| 305 | 493 | ||
| @@ -390,22 +578,29 @@ impl<'d> I2S<'d> { | |||
| 390 | w.set_i2se(true); | 578 | w.set_i2se(true); |
| 391 | }); | 579 | }); |
| 392 | 580 | ||
| 393 | #[cfg(spi_v3)] | 581 | let mut opts = TransferOptions::default(); |
| 394 | regs.cr1().modify(|w| w.set_spe(true)); | 582 | opts.half_transfer_ir = true; |
| 395 | } | 583 | |
| 396 | 584 | Self { | |
| 397 | Self { | 585 | mode: config.mode, |
| 398 | _peri: spi, | 586 | spi, |
| 399 | txsd: txsd.map(|w| w.map_into()), | 587 | txsd: txsd.map(|w| w.map_into()), |
| 400 | rxsd: rxsd.map(|w| w.map_into()), | 588 | rxsd: rxsd.map(|w| w.map_into()), |
| 401 | ws: Some(ws.map_into()), | 589 | ws: Some(ws.map_into()), |
| 402 | ck: Some(ck.map_into()), | 590 | ck: Some(ck.map_into()), |
| 403 | mck: Some(mck.map_into()), | 591 | mck: mck.map(|w| w.map_into()), |
| 592 | tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { | ||
| 593 | WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) | ||
| 594 | }), | ||
| 595 | rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { | ||
| 596 | ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) | ||
| 597 | }), | ||
| 598 | } | ||
| 404 | } | 599 | } |
| 405 | } | 600 | } |
| 406 | } | 601 | } |
| 407 | 602 | ||
| 408 | impl<'d> Drop for I2S<'d> { | 603 | impl<'d, W: Word> Drop for I2S<'d, W> { |
| 409 | fn drop(&mut self) { | 604 | fn drop(&mut self) { |
| 410 | self.txsd.as_ref().map(|x| x.set_as_disconnected()); | 605 | self.txsd.as_ref().map(|x| x.set_as_disconnected()); |
| 411 | self.rxsd.as_ref().map(|x| x.set_as_disconnected()); | 606 | self.rxsd.as_ref().map(|x| x.set_as_disconnected()); |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d034c028e..a65b0cc64 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -311,8 +311,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 311 | } | 311 | } |
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself. | 314 | pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) { |
| 315 | fn set_word_size(&mut self, word_size: word_impl::Config) { | ||
| 316 | if self.current_word_size == word_size { | 315 | if self.current_word_size == word_size { |
| 317 | return; | 316 | return; |
| 318 | } | 317 | } |
| @@ -895,7 +894,7 @@ fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz { | |||
| 895 | kernel_clock / div | 894 | kernel_clock / div |
| 896 | } | 895 | } |
| 897 | 896 | ||
| 898 | trait RegsExt { | 897 | pub(crate) trait RegsExt { |
| 899 | fn tx_ptr<W>(&self) -> *mut W; | 898 | fn tx_ptr<W>(&self) -> *mut W; |
| 900 | fn rx_ptr<W>(&self) -> *mut W; | 899 | fn rx_ptr<W>(&self) -> *mut W; |
| 901 | } | 900 | } |
| @@ -983,7 +982,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { | |||
| 983 | } | 982 | } |
| 984 | } | 983 | } |
| 985 | 984 | ||
| 986 | fn flush_rx_fifo(regs: Regs) { | 985 | pub(crate) fn flush_rx_fifo(regs: Regs) { |
| 987 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 986 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 988 | while regs.sr().read().rxne() { | 987 | while regs.sr().read().rxne() { |
| 989 | #[cfg(not(spi_v2))] | 988 | #[cfg(not(spi_v2))] |
| @@ -997,7 +996,7 @@ fn flush_rx_fifo(regs: Regs) { | |||
| 997 | } | 996 | } |
| 998 | } | 997 | } |
| 999 | 998 | ||
| 1000 | fn set_txdmaen(regs: Regs, val: bool) { | 999 | pub(crate) fn set_txdmaen(regs: Regs, val: bool) { |
| 1001 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1000 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 1002 | regs.cr2().modify(|reg| { | 1001 | regs.cr2().modify(|reg| { |
| 1003 | reg.set_txdmaen(val); | 1002 | reg.set_txdmaen(val); |
| @@ -1008,7 +1007,7 @@ fn set_txdmaen(regs: Regs, val: bool) { | |||
| 1008 | }); | 1007 | }); |
| 1009 | } | 1008 | } |
| 1010 | 1009 | ||
| 1011 | fn set_rxdmaen(regs: Regs, val: bool) { | 1010 | pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { |
| 1012 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1011 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 1013 | regs.cr2().modify(|reg| { | 1012 | regs.cr2().modify(|reg| { |
| 1014 | reg.set_rxdmaen(val); | 1013 | reg.set_rxdmaen(val); |
| @@ -1169,7 +1168,7 @@ impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | |||
| 1169 | } | 1168 | } |
| 1170 | } | 1169 | } |
| 1171 | 1170 | ||
| 1172 | trait SealedWord { | 1171 | pub(crate) trait SealedWord { |
| 1173 | const CONFIG: word_impl::Config; | 1172 | const CONFIG: word_impl::Config; |
| 1174 | } | 1173 | } |
| 1175 | 1174 | ||
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs index 27b165f1b..68392847b 100644 --- a/examples/stm32f4/src/bin/i2s_dma.rs +++ b/examples/stm32f4/src/bin/i2s_dma.rs | |||
| @@ -1,13 +1,10 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::fmt::Write; | ||
| 5 | |||
| 6 | use defmt::*; | 4 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::i2s::{Config, I2S}; | 6 | use embassy_stm32::i2s::{Config, I2S}; |
| 9 | use embassy_stm32::time::Hertz; | 7 | use embassy_stm32::time::Hertz; |
| 10 | use heapless::String; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 9 | ||
| 13 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 17 | 14 | ||
| 15 | let mut dma_buffer = [0x00_u16; 128]; | ||
| 16 | |||
| 18 | let mut i2s = I2S::new_txonly( | 17 | let mut i2s = I2S::new_txonly( |
| 19 | p.SPI2, | 18 | p.SPI2, |
| 20 | p.PC3, // sd | 19 | p.PC3, // sd |
| @@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) { | |||
| 22 | p.PB10, // ck | 21 | p.PB10, // ck |
| 23 | p.PC6, // mck | 22 | p.PC6, // mck |
| 24 | p.DMA1_CH4, | 23 | p.DMA1_CH4, |
| 24 | &mut dma_buffer, | ||
| 25 | Hertz(1_000_000), | 25 | Hertz(1_000_000), |
| 26 | Config::default(), | 26 | Config::default(), |
| 27 | ); | 27 | ); |
| 28 | 28 | ||
| 29 | for n in 0u32.. { | 29 | for i in 0_u16.. { |
| 30 | let mut write: String<128> = String::new(); | 30 | i2s.write(&mut [i * 2; 64]).await.ok(); |
| 31 | core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); | 31 | i2s.write(&mut [i * 2 + 1; 64]).await.ok(); |
| 32 | i2s.write(&mut write.as_bytes()).await.ok(); | ||
| 33 | } | 32 | } |
| 34 | } | 33 | } |
