diff options
| author | xoviat <[email protected]> | 2023-07-30 09:25:58 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-07-30 09:25:58 -0500 |
| commit | fd9b6487e12dff80bf9e23cba474af5d8773c8a7 (patch) | |
| tree | 1eb9b70ce491c9a9ad75729c189ed3c176a8951a /embassy-stm32 | |
| parent | 603c4cb4fa5f3dc2d95c5e47f13149beaa227bf5 (diff) | |
stm32/dma: impl. wringbuf for bdma
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/src/dma/bdma.rs | 154 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/dma.rs | 4 |
2 files changed, 155 insertions, 3 deletions
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7b5008f07..2905338de 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs | |||
| @@ -9,7 +9,7 @@ use atomic_polyfill::AtomicUsize; | |||
| 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; | 12 | use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 13 | use super::word::{Word, WordSize}; | 13 | use super::word::{Word, WordSize}; |
| 14 | use super::Dir; | 14 | use super::Dir; |
| 15 | use crate::_generated::BDMA_CHANNEL_COUNT; | 15 | use crate::_generated::BDMA_CHANNEL_COUNT; |
| @@ -559,3 +559,155 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { | |||
| 559 | fence(Ordering::SeqCst); | 559 | fence(Ordering::SeqCst); |
| 560 | } | 560 | } |
| 561 | } | 561 | } |
| 562 | |||
| 563 | pub struct WritableRingBuffer<'a, C: Channel, W: Word> { | ||
| 564 | cr: regs::Cr, | ||
| 565 | channel: PeripheralRef<'a, C>, | ||
| 566 | ringbuf: WritableDmaRingBuffer<'a, W>, | ||
| 567 | } | ||
| 568 | |||
| 569 | impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { | ||
| 570 | pub unsafe fn new_write( | ||
| 571 | channel: impl Peripheral<P = C> + 'a, | ||
| 572 | _request: Request, | ||
| 573 | peri_addr: *mut W, | ||
| 574 | buffer: &'a mut [W], | ||
| 575 | _options: TransferOptions, | ||
| 576 | ) -> Self { | ||
| 577 | into_ref!(channel); | ||
| 578 | |||
| 579 | let len = buffer.len(); | ||
| 580 | assert!(len > 0 && len <= 0xFFFF); | ||
| 581 | |||
| 582 | let dir = Dir::MemoryToPeripheral; | ||
| 583 | let data_size = W::size(); | ||
| 584 | |||
| 585 | let channel_number = channel.num(); | ||
| 586 | let dma = channel.regs(); | ||
| 587 | |||
| 588 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 589 | fence(Ordering::SeqCst); | ||
| 590 | |||
| 591 | #[cfg(bdma_v2)] | ||
| 592 | critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); | ||
| 593 | |||
| 594 | let mut w = regs::Cr(0); | ||
| 595 | w.set_psize(data_size.into()); | ||
| 596 | w.set_msize(data_size.into()); | ||
| 597 | w.set_minc(vals::Inc::ENABLED); | ||
| 598 | w.set_dir(dir.into()); | ||
| 599 | w.set_teie(true); | ||
| 600 | w.set_htie(true); | ||
| 601 | w.set_tcie(true); | ||
| 602 | w.set_circ(vals::Circ::ENABLED); | ||
| 603 | w.set_pl(vals::Pl::VERYHIGH); | ||
| 604 | w.set_en(true); | ||
| 605 | |||
| 606 | let buffer_ptr = buffer.as_mut_ptr(); | ||
| 607 | let mut this = Self { | ||
| 608 | channel, | ||
| 609 | cr: w, | ||
| 610 | ringbuf: WritableDmaRingBuffer::new(buffer), | ||
| 611 | }; | ||
| 612 | this.clear_irqs(); | ||
| 613 | |||
| 614 | #[cfg(dmamux)] | ||
| 615 | super::dmamux::configure_dmamux(&mut *this.channel, _request); | ||
| 616 | |||
| 617 | let ch = dma.ch(channel_number); | ||
| 618 | ch.par().write_value(peri_addr as u32); | ||
| 619 | ch.mar().write_value(buffer_ptr as u32); | ||
| 620 | ch.ndtr().write(|w| w.set_ndt(len as u16)); | ||
| 621 | |||
| 622 | this | ||
| 623 | } | ||
| 624 | |||
| 625 | pub fn start(&mut self) { | ||
| 626 | let ch = self.channel.regs().ch(self.channel.num()); | ||
| 627 | ch.cr().write_value(self.cr) | ||
| 628 | } | ||
| 629 | |||
| 630 | pub fn clear(&mut self) { | ||
| 631 | self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); | ||
| 632 | } | ||
| 633 | |||
| 634 | /// Write elements from the ring buffer | ||
| 635 | /// Return a tuple of the length written and the length remaining in the buffer | ||
| 636 | pub fn read(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { | ||
| 637 | self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) | ||
| 638 | } | ||
| 639 | |||
| 640 | /// Write an exact number of elements to the ringbuffer. | ||
| 641 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { | ||
| 642 | use core::future::poll_fn; | ||
| 643 | use core::sync::atomic::compiler_fence; | ||
| 644 | |||
| 645 | let mut written_data = 0; | ||
| 646 | let buffer_len = buffer.len(); | ||
| 647 | |||
| 648 | poll_fn(|cx| { | ||
| 649 | self.set_waker(cx.waker()); | ||
| 650 | |||
| 651 | compiler_fence(Ordering::SeqCst); | ||
| 652 | |||
| 653 | match self.read(&buffer[written_data..buffer_len]) { | ||
| 654 | Ok((len, remaining)) => { | ||
| 655 | written_data += len; | ||
| 656 | if written_data == buffer_len { | ||
| 657 | Poll::Ready(Ok(remaining)) | ||
| 658 | } else { | ||
| 659 | Poll::Pending | ||
| 660 | } | ||
| 661 | } | ||
| 662 | Err(e) => Poll::Ready(Err(e)), | ||
| 663 | } | ||
| 664 | }) | ||
| 665 | .await | ||
| 666 | } | ||
| 667 | |||
| 668 | /// The capacity of the ringbuffer. | ||
| 669 | pub fn cap(&self) -> usize { | ||
| 670 | self.ringbuf.cap() | ||
| 671 | } | ||
| 672 | |||
| 673 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 674 | STATE.ch_wakers[self.channel.index()].register(waker); | ||
| 675 | } | ||
| 676 | |||
| 677 | fn clear_irqs(&mut self) { | ||
| 678 | let dma = self.channel.regs(); | ||
| 679 | dma.ifcr().write(|w| { | ||
| 680 | w.set_htif(self.channel.num(), true); | ||
| 681 | w.set_tcif(self.channel.num(), true); | ||
| 682 | w.set_teif(self.channel.num(), true); | ||
| 683 | }); | ||
| 684 | } | ||
| 685 | |||
| 686 | pub fn request_stop(&mut self) { | ||
| 687 | let ch = self.channel.regs().ch(self.channel.num()); | ||
| 688 | |||
| 689 | // Disable the channel. Keep the IEs enabled so the irqs still fire. | ||
| 690 | // If the channel is enabled and transfer is not completed, we need to perform | ||
| 691 | // two separate write access to the CR register to disable the channel. | ||
| 692 | ch.cr().write(|w| { | ||
| 693 | w.set_teie(true); | ||
| 694 | w.set_htie(true); | ||
| 695 | w.set_tcie(true); | ||
| 696 | }); | ||
| 697 | } | ||
| 698 | |||
| 699 | pub fn is_running(&mut self) -> bool { | ||
| 700 | let ch = self.channel.regs().ch(self.channel.num()); | ||
| 701 | ch.cr().read().en() | ||
| 702 | } | ||
| 703 | } | ||
| 704 | |||
| 705 | impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { | ||
| 706 | fn drop(&mut self) { | ||
| 707 | self.request_stop(); | ||
| 708 | while self.is_running() {} | ||
| 709 | |||
| 710 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 711 | fence(Ordering::SeqCst); | ||
| 712 | } | ||
| 713 | } | ||
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 3c5c79fd8..9cd7aa8d5 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs | |||
| @@ -814,7 +814,7 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> { | |||
| 814 | } | 814 | } |
| 815 | 815 | ||
| 816 | impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { | 816 | impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { |
| 817 | pub unsafe fn new_read( | 817 | pub unsafe fn new_write( |
| 818 | channel: impl Peripheral<P = C> + 'a, | 818 | channel: impl Peripheral<P = C> + 'a, |
| 819 | _request: Request, | 819 | _request: Request, |
| 820 | peri_addr: *mut W, | 820 | peri_addr: *mut W, |
| @@ -899,7 +899,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { | |||
| 899 | self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) | 899 | self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) |
| 900 | } | 900 | } |
| 901 | 901 | ||
| 902 | /// Write an exact number of elements from the ringbuffer. | 902 | /// Write an exact number of elements to the ringbuffer. |
| 903 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { | 903 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { |
| 904 | use core::future::poll_fn; | 904 | use core::future::poll_fn; |
| 905 | use core::sync::atomic::compiler_fence; | 905 | use core::sync::atomic::compiler_fence; |
