use stm32f1xx_hal::{afio, dma, gpio, pac, prelude::*, rcc, serial}; type TxDma = dma::TxDma, dma::dma1::C4>; type DMXUniverse = &'static mut [u8; DMX_LEN]; type DMXTransfer = dma::Transfer, TxDma>; struct TxDMAIdle { tx: TxDma, buffer: DMXUniverse, } struct TxDMABusy { transfer: DMXTransfer, } enum TxDMA { Idle(Option>), Busy(Option>), } impl TxDMA { fn new(tx: TxDma, buffer: DMXUniverse) -> Self { TxDMA::Idle(Some(TxDMAIdle { tx, buffer })) } fn start_sending(&mut self, tx_universe: &[u8]) { let TxDMA::Idle(idle) = self else { return; }; let Some(TxDMAIdle { tx, buffer }) = idle.take() else { panic!(); }; buffer.copy_from_slice(tx_universe); *self = TxDMA::Busy(Some(TxDMABusy { transfer: tx.write(buffer), })); } fn wait(&mut self) { let TxDMA::Busy(busy) = self else { return; }; let Some(TxDMABusy { transfer }) = busy.take() else { panic!(); }; let (buffer, tx) = transfer.wait(); *self = TxDMA::Idle(Some(TxDMAIdle { tx, buffer })); } fn is_idle(&mut self) -> bool { match self { TxDMA::Idle(_) => true, TxDMA::Busy(busy) => { let Some(TxDMABusy { transfer }) = busy.take() else { panic!(); }; let is_done = transfer.is_done(); busy.replace(TxDMABusy { transfer }); if is_done { self.wait(); } is_done } } } } #[allow(clippy::upper_case_acronyms)] pub struct DMX { tx_universe: DMXUniverse, sender: TxDMA, } impl DMX { #[allow(unsafe_code)] pub fn new( mem: &'static mut [u8], mut dma_channel: dma::dma1::C4, pa9: gpio::PA9, pa10: gpio::PA10, acrh: &mut gpio::Cr<'A', true>, mapr: &mut afio::MAPR, clocks: &rcc::Clocks, ) -> Self { // use provided memory region assert!(mem.len() >= DMX_LEN * 2); let (tx_universe, tx_buffer) = { let (tx_universe, rest) = mem.split_at_mut(DMX_LEN); let (tx_buffer, _) = rest.split_at_mut(DMX_LEN); let tx_universe: DMXUniverse = tx_universe.try_into().unwrap(); let tx_buffer: DMXUniverse = tx_buffer.try_into().unwrap(); (tx_universe, tx_buffer) }; // setup DMA1_CHANNEL4 interrupt on TransferComplete dma_channel.listen(dma::Event::TransferComplete); unsafe { pac::CorePeripherals::steal() .NVIC .set_priority(pac::Interrupt::DMA1_CHANNEL4, 1); } // Serial config let serial = serial::Serial::new( unsafe { pac::Peripherals::steal() }.USART1, ( pa9.into_alternate_open_drain(acrh), pa10, //.into_pull_up_input(acrh), ), mapr, 250_000.bps(), clocks, ); Self { tx_universe, sender: TxDMA::new(serial.tx.with_dma(dma_channel), tx_buffer), } } pub fn start_tx(&mut self) { self.sender.start_sending(self.tx_universe); } pub fn wait_tx(&mut self) { self.sender.wait(); } pub fn tx_is_idle(&mut self) -> bool { self.sender.is_idle() } }