Skip to content

Instantly share code, notes, and snippets.

@coljnr9
Last active May 22, 2022 19:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coljnr9/77fe03a2e48a58c3379e319f028dd4e7 to your computer and use it in GitHub Desktop.
Save coljnr9/77fe03a2e48a58c3379e319f028dd4e7 to your computer and use it in GitHub Desktop.
RTIC (v1) + Serial DMA (STM32F7): DMA1_STREAM1 task only executes once
// These are the logs I see. There is a blink task I've omitted, that
// keeps going to inifinity, so I don't think the program is just hard
// crashing.
//
// INFO init
// INFO serial_rx_dma_complete_handler
// INFO Got b"TESTTESTTESTTEST" // Sent from my PC
// INFO Spawning start_serial_rx_dma
// INFO start_serial_rx_dma
// INFO Starting transfer
// Here, I send more data to the STM32, and I'd expect to see
// see the log from entering `serial_rx_dma_complete_handler`
#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
// Skip to relevant code...
let serial_dma = crate::DMA::new(p.DMA1);
let serial_rx_stream = serial_dma.streams.stream1;
let serial_dma_handle = serial_dma.handle.enable(&mut rcc.ahb1);
static mut BUFFER: [u8; 16] = [0; 16];
let buffer = unsafe { core::pin::Pin::new(&mut BUFFER) };
// Start a transfer in init.
let mut serial_rx_transfer_ready = usart3_rx.read_all(buffer, &serial_dma_handle, serial_rx_stream);
interrupt::free(|_| {
serial_rx_transfer_ready.enable_interrupts(
&serial_dma_handle,
stm32f7xx_hal::dma::Interrupts {
transfer_complete: true,
transfer_error: true,
direct_mode_error: true,
..stm32f7xx_hal::dma::Interrupts::default()
},
);
});
// Send this as a shared resource to the hardware task bound to DMA1_STREAM1
// which will recover the transfer resources by calling .wait() (though I think that
// returns immediately)
let serial_rx_transfer_started = serial_rx_transfer_ready.start(&serial_dma_handle);
// Init returns something like
(
Shared {
// ...
serial_rx_transfer_started: Some(serial_rx_transfer_started),
serial_rx_transfer_ready: None,
serial_dma_handle,
},
Local {
// ...
},
init::Monotonics(mono),
)
}
#[task(shared = [serial_rx_transfer_ready, serial_rx_transfer_started, serial_dma_handle])]
fn start_serial_rx_dma(cx: start_serial_rx_dma::Context) {
defmt::info!("start_serial_rx_dma");
let serial_rx_transfer_started = cx.shared.serial_rx_transfer_started;
let serial_rx_transfer_ready = cx.shared.serial_rx_transfer_ready;
let serial_dma_handle = cx.shared.serial_dma_handle;
// I think mutable access to shared resources has to happen in a lock
(serial_rx_transfer_ready, serial_rx_transfer_started, serial_dma_handle,)
.lock(|serial_rx_transfer_ready, serial_rx_transfer_started, serial_dma_handle| {
let mut serial_rx_transfer_ready = serial_rx_transfer_ready.take().unwrap();
let serial_dma_handle_ = serial_dma_handle;
interrupt::free(|_| {
serial_rx_transfer_ready.enable_interrupts(
serial_dma_handle_,
stm32f7xx_hal::dma::Interrupts {
transfer_complete: true,
transfer_error: true,
direct_mode_error: true,
..stm32f7xx_hal::dma::Interrupts::default()
},
);
defmt::info!("Starting transfer");
*serial_rx_transfer_started = Some(serial_rx_transfer_ready.start(serial_dma_handle_));
});
},
);
}
// I think this triggers on any of the enabled interrupts, but for now I'm just
// assuming that I only get here from `transfer_complete`.
// This only fires once, but I'd expect it to happen every time the DMA buffer fills.
// RTIC manual has a note about clearing interrupt flags manually in hardaware tasks,
// but it's not obvious to me how to do that.
#[task(binds = DMA1_STREAM1, shared = [serial_rx_transfer_started, serial_rx_transfer_ready, usart3_rx, serial_dma_handle])]
fn serial_rx_dma_complete_handler(cx: serial_rx_dma_complete_handler::Context) {
defmt::info!("serial_rx_dma_complete_handler");
let serial_rx_transfer_started = cx.shared.serial_rx_transfer_started;
let serial_rx_transfer_ready = cx.shared.serial_rx_transfer_ready;
let serial_dma_handle = cx.shared.serial_dma_handle;
(serial_rx_transfer_started, serial_rx_transfer_ready, serial_dma_handle,)
.lock(|serial_rx_transfer_started, serial_rx_transfer_ready, serial_dma_handle| {
// Get back the previously capture resources. Use them to
// build a Transfer<READY> and send it to start_serial_rx_dma
// as a shared resource.
let transfer_resources = serial_rx_transfer_started.take().unwrap()
.wait(serial_dma_handle).unwrap();
let buffer = *transfer_resources.buffer;
defmt::info!("Got {=[u8]:a}", buffer);
let usart3_rx = transfer_resources.target;
*serial_rx_transfer_ready = Some(usart3_rx.read_all(
transfer_resources.buffer,
serial_dma_handle,
transfer_resources.stream,
));
},
);
// Slow for now, and this doesn't feel quite right overall. But
// spawn this task, which should configure and start the next
// serial/dma transfer.
defmt::info!("Spawning start_serial_rx_dma");
start_serial_rx_dma::spawn_after(1000.millis()).ok();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment