Skip to content

Commit

Permalink
Add support for DMA interrupts
Browse files Browse the repository at this point in the history
  • Loading branch information
hannobraun authored and mvertescher committed Jun 17, 2019
1 parent 04d30cd commit 913dc88
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 16 deletions.
33 changes: 26 additions & 7 deletions examples/serial_dma.rs
Expand Up @@ -13,11 +13,18 @@
extern crate panic_halt;


use cortex_m::{
asm,
interrupt,
};
use cortex_m_rt::entry;
use stm32f7xx_hal::{
prelude::*,
device,
dma::DMA,
device::self,
dma::{
self,
DMA,
},
serial::{
self,
Serial,
Expand Down Expand Up @@ -60,12 +67,24 @@ fn main() -> ! {

let mut hello = b"Hello, I'm a STM32F7xx!\r\n".as_ref();
loop {
let transfer = tx
.write_all(hello, &dma, stream)
.start(&dma);
let mut transfer = tx.write_all(hello, &dma, stream);

let res = interrupt::free(|_| {
transfer.enable_interrupts(&dma, dma::Interrupts {
transfer_complete: true,
transfer_error: true,
direct_mode_error: true,
.. dma::Interrupts::default()
});

let transfer = transfer.start(&dma);

asm::wfi();

transfer.wait(&dma)
.unwrap()
});

let res = transfer.wait(&dma)
.unwrap();
hello = res.source;
tx = res.target;
stream = res.stream;
Expand Down
95 changes: 86 additions & 9 deletions src/dma.rs
Expand Up @@ -11,11 +11,14 @@ use core::{
},
};

use cortex_m::interrupt::Nr;

use crate::{
device::{
dma2,
DMA1,
DMA2,
NVIC,
USART1,
USART2,
USART3,
Expand All @@ -24,6 +27,7 @@ use crate::{
USART6,
UART7,
UART8,
Interrupt,
},
rcc::Rcc,
serial,
Expand Down Expand Up @@ -184,6 +188,41 @@ impl<Target> Transfer<Target, Ready> where Target: Tx {
}
}

pub fn enable_interrupts(&mut self,
handle: &Handle<Target::Instance, Enabled>,
interrupts: Interrupts,
) {
handle.dma.st[Target::Stream::number()].cr.modify(|_, w| {
let w = if interrupts.transfer_complete {
w.tcie().enabled()
}
else { w };

let w = if interrupts.half_transfer {
w.htie().enabled()
}
else { w };

let w = if interrupts.transfer_error {
w.teie().enabled()
}
else { w };

let w = if interrupts.direct_mode_error {
w.dmeie().enabled()
}
else { w };

w
});

// Enable interrupt. Safe, because we're only doing an atomic write.
let nr = Target::INTERRUPT.nr();
unsafe {
(&*NVIC::ptr()).iser[nr as usize / 32].write(0x1 << (nr % 32))
}
}

pub fn start(self, handle: &Handle<Target::Instance, Enabled>)
-> Transfer<Target, Started>
{
Expand Down Expand Up @@ -221,6 +260,12 @@ impl<Target> Transfer<Target, Started> where Target: Tx {
pub fn wait(self, handle: &Handle<Target::Instance, Enabled>)
-> Result<TransferResources<Target>, (TransferResources<Target>, Error)>
{
// Disable interrupt. Safe, because we're only doing an atomic write.
let nr = Target::INTERRUPT.nr();
unsafe {
(&*NVIC::ptr()).icer[nr as usize / 32].write(0x1 << (nr % 32))
}

// Wait for transfer to finish
while self.is_active(handle) {
if let Err(error) = Error::check::<Target::Stream>(&handle.dma) {
Expand Down Expand Up @@ -264,15 +309,27 @@ pub trait Tx {
type Instance: Deref<Target = dma2::RegisterBlock>;
type Stream: Stream;
type Channel: Channel;

const INTERRUPT: Interrupt;
}

macro_rules! impl_tx {
($($ty:ty, $instance:ty, $stream:ident, $channel:ty;)*) => {
(
$(
$ty:ty,
$instance:ty,
$stream:ident,
$channel:ty,
$interrupt:ident;
)*
) => {
$(
impl Tx for $ty {
type Instance = $instance;
type Stream = $stream<$instance>;
type Channel = $channel;

const INTERRUPT: Interrupt = Interrupt::$interrupt;
}
)*
}
Expand All @@ -288,16 +345,16 @@ macro_rules! impl_tx {
// There's probably a smart way to achieve this, but I decided to declare
// victory and leave this problem to someone who actually needs this capability.
impl_tx!(
serial::Tx<USART1>, DMA2, Stream7, Channel4;
serial::Tx<USART2>, DMA1, Stream6, Channel4;
serial::Tx<USART3>, DMA1, Stream3, Channel4;
serial::Tx<USART1>, DMA2, Stream7, Channel4, DMA2_STREAM7;
serial::Tx<USART2>, DMA1, Stream6, Channel4, DMA1_STREAM6;
serial::Tx<USART3>, DMA1, Stream3, Channel4, DMA1_STREAM3;
// USART3 for DMA1, stream 4, channel 7 is unsupported
serial::Tx<UART4>, DMA1, Stream4, Channel4;
serial::Tx<UART5>, DMA1, Stream7, Channel4;
serial::Tx<USART6>, DMA2, Stream6, Channel5;
serial::Tx<UART4>, DMA1, Stream4, Channel4, DMA1_STREAM4;
serial::Tx<UART5>, DMA1, Stream7, Channel4, DMA1_STREAM7;
serial::Tx<USART6>, DMA2, Stream6, Channel5, DMA2_STREAM6;
// USART6 for DMA2, stream 7, channel 5 is unsupported
serial::Tx<UART7>, DMA1, Stream1, Channel5;
serial::Tx<UART8>, DMA1, Stream0, Channel5;
serial::Tx<UART7>, DMA1, Stream1, Channel5, DMA1_STREAM1;
serial::Tx<UART8>, DMA1, Stream0, Channel5, DMA1_STREAM0;
);


Expand Down Expand Up @@ -473,6 +530,26 @@ impl_instance!(
);


/// Used by [`Transfer::enable_interrupts`] to identify DMA interrupts
pub struct Interrupts {
pub transfer_complete: bool,
pub half_transfer: bool,
pub transfer_error: bool,
pub direct_mode_error: bool,
}

impl Default for Interrupts {
fn default() -> Self {
Self {
transfer_complete: false,
half_transfer: false,
transfer_error: false,
direct_mode_error: false,
}
}
}


/// A DMA error
#[derive(Debug)]
pub enum Error {
Expand Down

0 comments on commit 913dc88

Please sign in to comment.