From e2e024a66fb0489a4f881e7dcad0e04a6122f750 Mon Sep 17 00:00:00 2001
From: Stephen D <webmaster@scd31.com>
Date: Fri, 2 Jun 2023 21:01:39 -0300
Subject: [PATCH] DMA wip. not working

---
 src/main.rs | 222 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 167 insertions(+), 55 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index e6a2cc9..4087f6a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,21 +11,34 @@ mod packet;
 
 #[rtic::app(device = stm32f4xx_hal::pac, peripherals = true)]
 mod app {
-    use hal::block;
-    use hal::gpio;
-    use hal::pac::{SPI1, TIM5, USART1};
-    use hal::prelude::*;
-    use hal::serial::{Config, Event, Serial};
-    use hal::spi::{Mode, Phase, Polarity, Spi};
-    use hal::timer::Delay;
-    use rf4463::config::RADIO_CONFIG_500_2;
-    use rf4463::Rf4463;
+    use hal::{
+        block,
+        dma::{
+            config::DmaConfig,
+            traits::{Stream, StreamISR},
+            PeripheralToMemory, Stream2, StreamsTuple, Transfer,
+        },
+        gpio,
+        pac::{DMA2, SPI1, TIM5, USART1},
+        prelude::*,
+        serial::{self, Config, Rx, Serial, Tx},
+        spi::{Mode, Phase, Polarity, Spi},
+        timer::Delay,
+    };
+    use rf4463::{config::RADIO_CONFIG_500_2, Rf4463};
     use ringbuffer::{ConstGenericRingBuffer, RingBuffer, RingBufferRead, RingBufferWrite};
+    use rtic::Mutex;
 
     use crate::packet::Packet;
 
     // in # packets
     const BUFFER_LEN: usize = 50;
+    // # bytes
+    // if we make this too big, the interrupt may take too long to process them all
+    // and we could drop packets!
+    // ideally we would use the rf4463 interrupt (higher priority)
+    // to keep the transceiver fed
+    const DMA_BUFFER_LEN: usize = 128;
 
     const MODE: Mode = Mode {
         polarity: Polarity::IdleLow,
@@ -39,6 +52,14 @@ mod app {
         Delay<TIM5, 1000000>,
     >;
 
+    type RxTransfer = Transfer<
+        Stream2<DMA2>,
+        4,
+        Rx<USART1>,
+        PeripheralToMemory,
+        &'static mut [u8; DMA_BUFFER_LEN],
+    >;
+
     #[derive(Debug)]
     enum SlaveCmd {
         BufferStatus,
@@ -67,13 +88,24 @@ mod app {
     struct Shared {
         radio_temp: f32,
         tx_buf: ConstGenericRingBuffer<Packet, BUFFER_LEN>,
+
+        // Can use lock free for any resources that are only shared between tasks of the same priority
+        #[lock_free]
+        state: SlaveState,
+
+        #[lock_free]
+        tx: Tx<USART1>,
+
+        #[lock_free]
+        rx_transfer: RxTransfer,
+
+        #[lock_free]
+        rx_buffer: Option<&'static mut [u8; DMA_BUFFER_LEN]>,
     }
 
     #[local]
     struct Local {
         radio: Radio,
-        usart: Serial<USART1>,
-        state: SlaveState,
     }
 
     #[init]
@@ -110,26 +142,53 @@ mod app {
         let tx = gpioa.pa9;
         let rx = gpioa.pa10;
 
-        let mut usart = Serial::new(
+        let usart = Serial::new(
             ctx.device.USART1,
             (tx, rx),
-            Config::default().baudrate(921_600.bps()),
+            Config::default()
+                .baudrate(921_600.bps())
+                .dma(serial::config::DmaConfig::Rx),
             &clocks,
         )
         .unwrap();
 
-        usart.listen(Event::Rxne);
+        let (tx, mut rx) = usart.split();
+
+        rx.listen_idle();
+
+        let dma2 = StreamsTuple::new(ctx.device.DMA2);
+
+        let rx_buffer1 =
+            cortex_m::singleton!(: [u8; DMA_BUFFER_LEN] = [0; DMA_BUFFER_LEN]).unwrap();
+        let rx_buffer2 =
+            cortex_m::singleton!(: [u8; DMA_BUFFER_LEN] = [0; DMA_BUFFER_LEN]).unwrap();
+
+        // todo figure out double buffering
+        let mut rx_transfer = Transfer::init_peripheral_to_memory(
+            dma2.2,
+            rx,
+            rx_buffer1,
+            None,
+            DmaConfig::default()
+                .memory_increment(true)
+                .fifo_enable(true)
+                .fifo_error_interrupt(true)
+                .transfer_complete_interrupt(true),
+        );
+
+        rx_transfer.start(|_rx| {});
 
         (
             Shared {
                 radio_temp: 0.0,
                 tx_buf: ConstGenericRingBuffer::new(),
-            },
-            Local {
-                radio,
-                usart,
+                tx,
                 state: SlaveState::Idle,
+
+                rx_transfer,
+                rx_buffer: Some(rx_buffer2),
             },
+            Local { radio },
             init::Monotonics(),
         )
     }
@@ -144,7 +203,7 @@ mod app {
 
             i += 1;
 
-            // get temp every 500 packets, or whenever when idle TODO
+            // get temp every 500 packets, or whenever when idle
             if i >= 500 {
                 i = 0;
 
@@ -155,52 +214,105 @@ mod app {
         }
     }
 
-    #[task(binds = USART1, priority=1, local = [state, usart], shared = [tx_buf, radio_temp])]
+    #[task(binds = USART1, priority=1, shared = [state, tx_buf, tx,radio_temp, rx_transfer, rx_buffer])]
     fn usart1(mut ctx: usart1::Context) {
-        let state = ctx.local.state;
-        let usart = ctx.local.usart;
-        let mut buf = ctx.shared.tx_buf;
-
-        let x = block!(usart.read()).unwrap();
-
-        match state {
-            SlaveState::Idle => {
-                if let Some(cmd) = SlaveCmd::from_u8(x) {
-                    match cmd {
-                        SlaveCmd::BufferStatus => {
-                            if buf.lock(|b| b.is_full()) {
-                                block!(usart.write(0x01)).unwrap(); // not enough space for a new packet
-                            } else {
-                                block!(usart.write(0x02)).unwrap(); // safe to send a packet
-                            }
-                        }
+        let transfer = &mut ctx.shared.rx_transfer;
+
+        if transfer.is_idle() {
+            let byte_count =
+                DMA_BUFFER_LEN - usize::from(Stream2::<DMA2>::get_number_of_transfers());
+            let new_buf = ctx.shared.rx_buffer.take().unwrap();
+            let (buffer, _) = transfer.next_transfer(new_buf).unwrap();
+            let bytes = &buffer[..byte_count];
+
+            handle_bytes(
+                ctx.shared.state,
+                ctx.shared.tx_buf,
+                ctx.shared.tx,
+                ctx.shared.radio_temp,
+                bytes,
+            );
+
+            *ctx.shared.rx_buffer = Some(buffer);
+        }
+    }
+
+    // Must be same priority as USART1
+    #[task(binds = DMA2_STREAM2, priority=1, shared = [state, tx_buf, tx, radio_temp, rx_transfer, rx_buffer])]
+    fn dma2_stream2(mut ctx: dma2_stream2::Context) {
+        let transfer = &mut ctx.shared.rx_transfer;
+
+        if Stream2::<DMA2>::get_fifo_error_flag() {
+            transfer.clear_fifo_error_interrupt();
+        }
+        if Stream2::<DMA2>::get_transfer_complete_flag() {
+            transfer.clear_transfer_complete_interrupt();
+
+            let new_buf = ctx.shared.rx_buffer.take().unwrap();
+            let (buffer, _) = transfer.next_transfer(new_buf).unwrap();
+
+            handle_bytes(
+                ctx.shared.state,
+                ctx.shared.tx_buf,
+                ctx.shared.tx,
+                ctx.shared.radio_temp,
+                buffer,
+            );
+
+            *ctx.shared.rx_buffer = Some(buffer);
+        }
+    }
 
-                        SlaveCmd::SendPacket => {
-                            if buf.lock(|b| b.len() >= b.capacity() - 1) {
-                                block!(usart.write(0x01)).unwrap(); // not enough space for a new packet
-                            } else {
-                                block!(usart.write(0x02)).unwrap(); // safe to send a packet
+    fn handle_bytes<
+        B: Mutex<T = ConstGenericRingBuffer<Packet, BUFFER_LEN>>,
+        RT: Mutex<T = f32>,
+    >(
+        state: &mut SlaveState,
+        mut buf: B,
+        tx: &mut Tx<USART1>,
+        mut radio_temp: RT,
+        bytes: &[u8],
+    ) {
+        for &x in bytes {
+            match state {
+                SlaveState::Idle => {
+                    if let Some(cmd) = SlaveCmd::from_u8(x) {
+                        match cmd {
+                            SlaveCmd::BufferStatus => {
+                                if buf.lock(|b| b.is_full()) {
+                                    block!(tx.write(0x01)).unwrap(); // not enough space for a new packet
+                                } else {
+                                    block!(tx.write(0x02)).unwrap(); // safe to send a packet
+                                }
                             }
 
-                            *state = SlaveState::RecvPacket(Packet::default());
-                        }
+                            SlaveCmd::SendPacket => {
+                                if buf.lock(|b| b.len() >= b.capacity() - 1) {
+                                    block!(tx.write(0x01)).unwrap(); // not enough space for a new packet
+                                } else {
+                                    block!(tx.write(0x02)).unwrap(); // safe to send a packet
+                                }
 
-                        SlaveCmd::GetTemp => {
-                            let temp = ctx.shared.radio_temp.lock(|x| *x);
-                            usart.bwrite_all(&temp.to_le_bytes()).unwrap();
+                                *state = SlaveState::RecvPacket(Packet::default());
+                            }
+
+                            SlaveCmd::GetTemp => {
+                                let temp = radio_temp.lock(|x| *x);
+                                tx.bwrite_all(&temp.to_le_bytes()).unwrap();
+                            }
                         }
                     }
                 }
-            }
 
-            SlaveState::RecvPacket(pkt) => {
-                pkt.push(x);
-                if pkt.is_complete() {
-                    buf.lock(|b| b.enqueue(pkt.clone()));
+                SlaveState::RecvPacket(pkt) => {
+                    pkt.push(x);
+                    if pkt.is_complete() {
+                        buf.lock(|b| b.enqueue(pkt.clone()));
 
-                    *state = SlaveState::Idle;
+                        *state = SlaveState::Idle;
+                    }
                 }
-            }
-        };
+            };
+        }
     }
 }
-- 
GitLab