atsamd_hal/dmac/
async_api.rs

1//! APIs for async DMAC operations.
2
3use atsamd_hal_macros::hal_cfg;
4
5use crate::{
6    async_hal::interrupts::{Handler, DMAC},
7    dmac::{waker::WAKERS, TriggerSource},
8    util::BitIter,
9};
10
11/// Interrupt handler for the DMAC peripheral.
12pub struct InterruptHandler {
13    _private: (),
14}
15
16impl crate::typelevel::Sealed for InterruptHandler {}
17
18#[hal_cfg(any("dmac-d11", "dmac-d21"))]
19impl Handler<DMAC> for InterruptHandler {
20    unsafe fn on_interrupt() {
21        // SAFETY: Here we can't go through the `with_chid` method to safely access
22        // the different channel interrupt flags. Instead, we read the ID in a short
23        // critical section, and make sure to RESET the CHID field to whatever
24        // it was before this function ran.
25        let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
26
27        critical_section::with(|_| {
28            let old_id = dmac.chid().read().id().bits();
29            let pending_interrupts = BitIter(dmac.intstatus().read().bits());
30
31            // Iterate over channels and check their interrupt status
32            for pend_channel in pending_interrupts {
33                unsafe { dmac.chid().modify(|_, w| w.id().bits(pend_channel as u8)) };
34
35                let wake = if dmac.chintflag().read().tcmpl().bit_is_set() {
36                    // Transfer complete. Don't clear the flag, but
37                    // disable the interrupt. Flag will be cleared when polled
38                    dmac.chintenclr().modify(|_, w| w.tcmpl().set_bit());
39                    true
40                } else if dmac.chintflag().read().terr().bit_is_set() {
41                    // Transfer error
42                    dmac.chintenclr().modify(|_, w| w.terr().set_bit());
43                    true
44                } else {
45                    false
46                };
47
48                if wake {
49                    dmac.chctrla().modify(|_, w| w.enable().clear_bit());
50                    dmac.chctrlb()
51                        .modify(|_, w| w.trigsrc().variant(TriggerSource::Disable));
52                    WAKERS[pend_channel as usize].wake();
53                }
54            }
55
56            // Reset the CHID.ID register
57            unsafe {
58                dmac.chid().write(|w| w.id().bits(old_id));
59            }
60        });
61    }
62}
63
64#[hal_cfg("dmac-d5x")]
65impl Handler<DMAC> for InterruptHandler {
66    unsafe fn on_interrupt() {
67        let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
68
69        let pending_channels = BitIter(dmac.intstatus().read().bits());
70        for channel in pending_channels.map(|c| c as usize) {
71            let wake = if dmac
72                .channel(channel)
73                .chintflag()
74                .read()
75                .tcmpl()
76                .bit_is_set()
77            {
78                // Transfer complete. Don't clear the flag, but
79                // disable the interrupt. Flag will be cleared when polled
80                dmac.channel(channel)
81                    .chintenclr()
82                    .modify(|_, w| w.tcmpl().set_bit());
83                true
84            } else if dmac.channel(channel).chintflag().read().terr().bit_is_set() {
85                // Transfer error
86                dmac.channel(channel)
87                    .chintenclr()
88                    .modify(|_, w| w.terr().set_bit());
89                true
90            } else {
91                false
92            };
93
94            if wake {
95                dmac.channel(channel).chctrla().modify(|_, w| {
96                    w.enable().clear_bit();
97                    w.trigsrc().variant(TriggerSource::Disable)
98                });
99                WAKERS[channel].wake();
100            }
101        }
102    }
103}