diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-04-18 16:16:33 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-04-18 16:41:24 +0200 |
| commit | efc70debb3bbf7fb7e9b1a23a42e5db149de8ed6 (patch) | |
| tree | 26fd40195c0d9e5d546b78de697e42c3c36f4789 | |
| parent | 173c65b5430e57548cc747f0387dd001e30b1ac1 (diff) | |
stm32/dma: add double buffered mode for DMA, update DCMI.
| -rw-r--r-- | embassy-stm32/src/dcmi.rs | 37 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/dma.rs | 159 |
2 files changed, 182 insertions, 14 deletions
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 0b34553cf..c19be86c6 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs | |||
| @@ -434,9 +434,13 @@ where | |||
| 434 | result | 434 | result |
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | #[cfg(not(dma))] | ||
| 437 | async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { | 438 | async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { |
| 438 | todo!() | 439 | panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA."); |
| 439 | /* | 440 | } |
| 441 | |||
| 442 | #[cfg(dma)] | ||
| 443 | async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { | ||
| 440 | use crate::dma::TransferOptions; | 444 | use crate::dma::TransferOptions; |
| 441 | 445 | ||
| 442 | let data_len = buffer.len(); | 446 | let data_len = buffer.len(); |
| @@ -460,16 +464,24 @@ where | |||
| 460 | let r = self.inner.regs(); | 464 | let r = self.inner.regs(); |
| 461 | let src = r.dr().ptr() as *mut u32; | 465 | let src = r.dr().ptr() as *mut u32; |
| 462 | 466 | ||
| 463 | unsafe { | 467 | let mut transfer = unsafe { |
| 464 | channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default()); | 468 | crate::dma::DoubleBuffered::new_read( |
| 465 | } | 469 | &mut self.dma, |
| 470 | request, | ||
| 471 | src, | ||
| 472 | m0ar, | ||
| 473 | m1ar, | ||
| 474 | chunk_size, | ||
| 475 | TransferOptions::default(), | ||
| 476 | ) | ||
| 477 | }; | ||
| 466 | 478 | ||
| 467 | let mut last_chunk_set_for_transfer = false; | 479 | let mut last_chunk_set_for_transfer = false; |
| 468 | let mut buffer0_last_accessible = false; | 480 | let mut buffer0_last_accessible = false; |
| 469 | let dma_result = poll_fn(|cx| { | 481 | let dma_result = poll_fn(|cx| { |
| 470 | channel.set_waker(cx.waker()); | 482 | transfer.set_waker(cx.waker()); |
| 471 | 483 | ||
| 472 | let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() }; | 484 | let buffer0_currently_accessible = transfer.is_buffer0_accessible(); |
| 473 | 485 | ||
| 474 | // check if the accessible buffer changed since last poll | 486 | // check if the accessible buffer changed since last poll |
| 475 | if buffer0_last_accessible == buffer0_currently_accessible { | 487 | if buffer0_last_accessible == buffer0_currently_accessible { |
| @@ -480,21 +492,21 @@ where | |||
| 480 | if remaining_chunks != 0 { | 492 | if remaining_chunks != 0 { |
| 481 | if remaining_chunks % 2 == 0 && buffer0_currently_accessible { | 493 | if remaining_chunks % 2 == 0 && buffer0_currently_accessible { |
| 482 | m0ar = unsafe { m0ar.add(2 * chunk_size) }; | 494 | m0ar = unsafe { m0ar.add(2 * chunk_size) }; |
| 483 | unsafe { channel.set_buffer0(m0ar) } | 495 | unsafe { transfer.set_buffer0(m0ar) } |
| 484 | remaining_chunks -= 1; | 496 | remaining_chunks -= 1; |
| 485 | } else if !buffer0_currently_accessible { | 497 | } else if !buffer0_currently_accessible { |
| 486 | m1ar = unsafe { m1ar.add(2 * chunk_size) }; | 498 | m1ar = unsafe { m1ar.add(2 * chunk_size) }; |
| 487 | unsafe { channel.set_buffer1(m1ar) }; | 499 | unsafe { transfer.set_buffer1(m1ar) }; |
| 488 | remaining_chunks -= 1; | 500 | remaining_chunks -= 1; |
| 489 | } | 501 | } |
| 490 | } else { | 502 | } else { |
| 491 | if buffer0_currently_accessible { | 503 | if buffer0_currently_accessible { |
| 492 | unsafe { channel.set_buffer0(buffer.as_mut_ptr()) } | 504 | unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) } |
| 493 | } else { | 505 | } else { |
| 494 | unsafe { channel.set_buffer1(buffer.as_mut_ptr()) } | 506 | unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) } |
| 495 | } | 507 | } |
| 496 | if last_chunk_set_for_transfer { | 508 | if last_chunk_set_for_transfer { |
| 497 | channel.request_stop(); | 509 | transfer.request_stop(); |
| 498 | return Poll::Ready(()); | 510 | return Poll::Ready(()); |
| 499 | } | 511 | } |
| 500 | last_chunk_set_for_transfer = true; | 512 | last_chunk_set_for_transfer = true; |
| @@ -542,7 +554,6 @@ where | |||
| 542 | unsafe { Self::toggle(false) }; | 554 | unsafe { Self::toggle(false) }; |
| 543 | 555 | ||
| 544 | result | 556 | result |
| 545 | */ | ||
| 546 | } | 557 | } |
| 547 | } | 558 | } |
| 548 | 559 | ||
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9052aa110..62c092241 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | use core::future::Future; | 1 | use core::future::Future; |
| 2 | use core::marker::PhantomData; | ||
| 2 | use core::pin::Pin; | 3 | use core::pin::Pin; |
| 3 | use core::sync::atomic::{fence, Ordering}; | 4 | use core::sync::atomic::{fence, Ordering}; |
| 4 | use core::task::{Context, Poll}; | 5 | use core::task::{Context, Poll, Waker}; |
| 5 | 6 | ||
| 6 | use embassy_cortex_m::interrupt::Priority; | 7 | use embassy_cortex_m::interrupt::Priority; |
| 7 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 8 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| @@ -440,3 +441,159 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { | |||
| 440 | } | 441 | } |
| 441 | } | 442 | } |
| 442 | } | 443 | } |
| 444 | |||
| 445 | // ================================== | ||
| 446 | |||
| 447 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 448 | pub struct DoubleBuffered<'a, C: Channel, W: Word> { | ||
| 449 | channel: PeripheralRef<'a, C>, | ||
| 450 | _phantom: PhantomData<W>, | ||
| 451 | } | ||
| 452 | |||
| 453 | impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { | ||
| 454 | pub unsafe fn new_read( | ||
| 455 | channel: impl Peripheral<P = C> + 'a, | ||
| 456 | _request: Request, | ||
| 457 | peri_addr: *mut W, | ||
| 458 | buf0: *mut W, | ||
| 459 | buf1: *mut W, | ||
| 460 | len: usize, | ||
| 461 | options: TransferOptions, | ||
| 462 | ) -> Self { | ||
| 463 | into_ref!(channel); | ||
| 464 | assert!(len > 0 && len <= 0xFFFF); | ||
| 465 | |||
| 466 | let dir = Dir::PeripheralToMemory; | ||
| 467 | let data_size = W::bits(); | ||
| 468 | |||
| 469 | let channel_number = channel.num(); | ||
| 470 | let dma = channel.regs(); | ||
| 471 | |||
| 472 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 473 | fence(Ordering::SeqCst); | ||
| 474 | |||
| 475 | let mut this = Self { | ||
| 476 | channel, | ||
| 477 | _phantom: PhantomData, | ||
| 478 | }; | ||
| 479 | this.clear_irqs(); | ||
| 480 | |||
| 481 | #[cfg(dmamux)] | ||
| 482 | super::dmamux::configure_dmamux(&mut *this.channel, _request); | ||
| 483 | |||
| 484 | let ch = dma.st(channel_number); | ||
| 485 | ch.par().write_value(peri_addr as u32); | ||
| 486 | ch.m0ar().write_value(buf0 as u32); | ||
| 487 | ch.m1ar().write_value(buf1 as u32); | ||
| 488 | ch.ndtr().write_value(regs::Ndtr(len as _)); | ||
| 489 | ch.fcr().write(|w| { | ||
| 490 | if let Some(fth) = options.fifo_threshold { | ||
| 491 | // FIFO mode | ||
| 492 | w.set_dmdis(vals::Dmdis::DISABLED); | ||
| 493 | w.set_fth(fth.into()); | ||
| 494 | } else { | ||
| 495 | // Direct mode | ||
| 496 | w.set_dmdis(vals::Dmdis::ENABLED); | ||
| 497 | } | ||
| 498 | }); | ||
| 499 | ch.cr().write(|w| { | ||
| 500 | w.set_dir(dir.into()); | ||
| 501 | w.set_msize(data_size.into()); | ||
| 502 | w.set_psize(data_size.into()); | ||
| 503 | w.set_pl(vals::Pl::VERYHIGH); | ||
| 504 | w.set_minc(vals::Inc::INCREMENTED); | ||
| 505 | w.set_pinc(vals::Inc::FIXED); | ||
| 506 | w.set_teie(true); | ||
| 507 | w.set_tcie(true); | ||
| 508 | #[cfg(dma_v1)] | ||
| 509 | w.set_trbuff(true); | ||
| 510 | |||
| 511 | #[cfg(dma_v2)] | ||
| 512 | w.set_chsel(_request); | ||
| 513 | |||
| 514 | w.set_pburst(options.pburst.into()); | ||
| 515 | w.set_mburst(options.mburst.into()); | ||
| 516 | w.set_pfctrl(options.flow_ctrl.into()); | ||
| 517 | |||
| 518 | w.set_en(true); | ||
| 519 | }); | ||
| 520 | |||
| 521 | this | ||
| 522 | } | ||
| 523 | |||
| 524 | fn clear_irqs(&mut self) { | ||
| 525 | let channel_number = self.channel.num(); | ||
| 526 | let dma = self.channel.regs(); | ||
| 527 | let isrn = channel_number / 4; | ||
| 528 | let isrbit = channel_number % 4; | ||
| 529 | |||
| 530 | unsafe { | ||
| 531 | dma.ifcr(isrn).write(|w| { | ||
| 532 | w.set_tcif(isrbit, true); | ||
| 533 | w.set_teif(isrbit, true); | ||
| 534 | }) | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | pub unsafe fn set_buffer0(&mut self, buffer: *mut W) { | ||
| 539 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 540 | ch.m0ar().write_value(buffer as _); | ||
| 541 | } | ||
| 542 | |||
| 543 | pub unsafe fn set_buffer1(&mut self, buffer: *mut W) { | ||
| 544 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 545 | ch.m1ar().write_value(buffer as _); | ||
| 546 | } | ||
| 547 | |||
| 548 | pub fn is_buffer0_accessible(&mut self) -> bool { | ||
| 549 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 550 | unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1 | ||
| 551 | } | ||
| 552 | |||
| 553 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 554 | STATE.ch_wakers[self.channel.index()].register(waker); | ||
| 555 | } | ||
| 556 | |||
| 557 | pub fn request_stop(&mut self) { | ||
| 558 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 559 | |||
| 560 | // Disable the channel. Keep the IEs enabled so the irqs still fire. | ||
| 561 | unsafe { | ||
| 562 | ch.cr().write(|w| { | ||
| 563 | w.set_teie(true); | ||
| 564 | w.set_tcie(true); | ||
| 565 | }) | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | pub fn is_running(&mut self) -> bool { | ||
| 570 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 571 | unsafe { ch.cr().read() }.en() | ||
| 572 | } | ||
| 573 | |||
| 574 | /// Gets the total remaining transfers for the channel | ||
| 575 | /// Note: this will be zero for transfers that completed without cancellation. | ||
| 576 | pub fn get_remaining_transfers(&self) -> u16 { | ||
| 577 | let ch = self.channel.regs().st(self.channel.num()); | ||
| 578 | unsafe { ch.ndtr().read() }.ndt() | ||
| 579 | } | ||
| 580 | |||
| 581 | pub fn blocking_wait(mut self) { | ||
| 582 | while self.is_running() {} | ||
| 583 | |||
| 584 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 585 | fence(Ordering::SeqCst); | ||
| 586 | |||
| 587 | core::mem::forget(self); | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { | ||
| 592 | fn drop(&mut self) { | ||
| 593 | self.request_stop(); | ||
| 594 | while self.is_running() {} | ||
| 595 | |||
| 596 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 597 | fence(Ordering::SeqCst); | ||
| 598 | } | ||
| 599 | } | ||
