diff --git a/src/main.rs b/src/main.rs index e6a2cc92b4b25245947da9a2a3bc66e5ad210586..4087f6aa6ee847bf89f3fec954e23c2bc22053a4 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; + } } - } - }; + }; + } } }