diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 02d47da8..0db5e7e2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -105,46 +105,37 @@ fn main() { // ======== // Generate DMA IRQs. - let mut dma_irqs: HashSet<&str> = HashSet::new(); - let mut bdma_irqs: HashSet<&str> = HashSet::new(); + let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { - match r.kind { - "dma" => { - for irq in p.interrupts { - dma_irqs.insert(irq.interrupt); - } + if r.kind == "dma" || r.kind == "bdma" { + for irq in p.interrupts { + dma_irqs + .entry(irq.interrupt) + .or_default() + .push((p.name, irq.signal)); } - "bdma" => { - for irq in p.interrupts { - bdma_irqs.insert(irq.interrupt); - } - } - _ => {} } } } - let tokens: Vec<_> = dma_irqs.iter().map(|s| format_ident!("{}", s)).collect(); - g.extend(quote! { - #( - #[crate::interrupt] - unsafe fn #tokens () { - crate::dma::dma::on_irq(); - } - )* - }); + for (irq, channels) in dma_irqs { + let irq = format_ident!("{}", irq); - let tokens: Vec<_> = bdma_irqs.iter().map(|s| format_ident!("{}", s)).collect(); - g.extend(quote! { - #( + let channels = channels + .iter() + .map(|(dma, ch)| format_ident!("{}_{}", dma, ch)); + + g.extend(quote! { #[crate::interrupt] - unsafe fn #tokens () { - crate::dma::bdma::on_irq(); + unsafe fn #irq () { + #( + ::on_irq(); + )* } - )* - }); + }); + } // ======== // Generate RccPeripheral impls diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 105bea50..ec557da3 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -38,26 +38,6 @@ impl State { static STATE: State = State::new(); -pub(crate) unsafe fn on_irq() { - foreach_peripheral! { - (bdma, BDMA1) => { - // BDMA1 in H7 doesn't use DMAMUX, which breaks - }; - (bdma, $dma:ident) => { - let isr = pac::$dma.isr().read(); - foreach_dma_channel! { - ($channel_peri:ident, $dma, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => { - let cr = pac::$dma.ch($channel_num).cr(); - if isr.tcif($channel_num) && cr.read().tcie() { - cr.write(|_| ()); // Disable channel interrupts with the default value. - STATE.ch_wakers[$index].wake(); - } - }; - } - }; - } -} - /// safety: must be called only once pub(crate) unsafe fn init() { foreach_interrupt! { @@ -150,6 +130,12 @@ foreach_dma_channel! { fn set_waker(&mut self, waker: &Waker) { unsafe { low_level_api::set_waker($index, waker) } } + + fn on_irq() { + unsafe { + low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); + } + } } impl crate::dma::Channel for crate::peripherals::$channel_peri {} @@ -243,4 +229,18 @@ mod low_level_api { w.set_teif(channel_number as _, true); }); } + + /// Safety: Must be called with a matching set of parameters for a valid dma channel + pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) { + let channel_num = channel_num as usize; + let index = index as usize; + + let isr = dma.isr().read(); + let cr = dma.ch(channel_num).cr(); + + if isr.tcif(channel_num) && cr.read().tcie() { + cr.write(|_| ()); // Disable channel interrupts with the default value. + STATE.ch_wakers[index].wake(); + } + } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 95a6eea2..9ef7e428 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -36,22 +36,6 @@ impl State { static STATE: State = State::new(); -pub(crate) unsafe fn on_irq() { - foreach_peripheral! { - (dma, $dma:ident) => { - foreach_dma_channel! { - ($channel_peri:ident, $dma, dma, $channel_num:expr, $index:expr, $dmamux:tt) => { - let cr = pac::$dma.st($channel_num).cr(); - if pac::$dma.isr($channel_num/4).read().tcif($channel_num%4) && cr.read().tcie() { - cr.write(|_| ()); // Disable channel interrupts with the default value. - STATE.ch_wakers[$index].wake(); - } - }; - } - }; - } -} - /// safety: must be called only once pub(crate) unsafe fn init() { foreach_interrupt! { @@ -137,6 +121,12 @@ foreach_dma_channel! { fn set_waker(&mut self, waker: &Waker) { unsafe {low_level_api::set_waker($index, waker )} } + + fn on_irq() { + unsafe { + low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); + } + } } impl crate::dma::Channel for crate::peripherals::$channel_peri { } @@ -240,4 +230,18 @@ mod low_level_api { w.set_teif(isrbit, true); }); } + + /// Safety: Must be called with a matching set of parameters for a valid dma channel + pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, index: u8) { + let channel_num = channel_num as usize; + let index = index as usize; + + let cr = dma.st(channel_num).cr(); + let isr = dma.isr(channel_num / 4).read(); + + if isr.tcif(channel_num % 4) && cr.read().tcie() { + cr.write(|_| ()); // Disable channel interrupts with the default value. + STATE.ch_wakers[index].wake(); + } + } } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 6bca969c..4768a448 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -88,6 +88,11 @@ pub(crate) mod sealed { /// Sets the waker that is called when this channel stops (either completed or manually stopped) fn set_waker(&mut self, waker: &Waker); + + /// This is called when this channel triggers an interrupt. + /// Note: Because some channels share an interrupt, this function might be + /// called for a channel that didn't trigger an interrupt. + fn on_irq(); } }