Last active
May 22, 2022 19:04
-
-
Save coljnr9/77fe03a2e48a58c3379e319f028dd4e7 to your computer and use it in GitHub Desktop.
RTIC (v1) + Serial DMA (STM32F7): DMA1_STREAM1 task only executes once
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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