Zero-Cost DMA Abstractions in Embedded Rust with Embassy
Introduction
L’un des bugs les plus insidieux en C embarqué est le buffer aliasing DMA : modifier un buffer pendant qu’un transfert DMA est en cours. Rust élimine cette classe de bugs à la compilation grâce à son système d’ownership.
Le problème en C
uint8_t buffer[64];
void bad_code(void) {
HAL_UART_Transmit_DMA(&huart1, buffer, 64);
/* BUG : modifie le buffer pendant le transfer DMA ! */
memset(buffer, 0, 64);
}
La solution Rust — Ownership au service du DMA
#![no_std]
#![no_main]
use embassy_stm32::usart::{self, Uart};
use embassy_executor::Spawner;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut uart = Uart::new(
p.USART1, p.PA10, p.PA9,
Irqs, p.DMA1_CH1, p.DMA1_CH2,
usart::Config::default(),
).unwrap();
let buffer: [u8; 64] = [0x41; 64];
// Transfer prend ownership — impossible de modifier buffer
// pendant le transfer. Le compilateur le garantit.
uart.write(&buffer).await.unwrap();
}
Transferts DMA parallèles avec join!
use embassy_futures::join::join;
async fn parallel_transfers(
mut spi: Spi<'static, SPI1, DMA1_CH3, DMA1_CH4>,
mut uart: Uart<'static, USART1, DMA1_CH1, DMA1_CH2>,
) {
let spi_data = [0x01u8, 0x02, 0x03, 0x04];
let uart_data = *b"Hello from Embassy!\r\n";
// Les deux transfers s'exécutent en parallèle
join(
spi.write(&spi_data),
uart.write(&uart_data),
).await;
}
Cargo.toml pour STM32
[dependencies]
embassy-stm32 = { version = "0.1", features = [
"stm32f429zi", "time-driver-any", "exti"
]}
embassy-executor = { version = "0.5", features = [
"arch-cortex-m", "executor-thread"
]}
embassy-time = { version = "0.3" }
embassy-futures = { version = "0.1" }
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
Conclusion
Embassy démontre que les garanties de sécurité mémoire de Rust ne sont pas incompatibles avec la programmation embarquée bas-niveau. Le système d’ownership élimine statiquement toute une classe de bugs DMA sans aucun overhead runtime.