From 1125925e2fd4f3505c82b089ab46738af68e194b Mon Sep 17 00:00:00 2001 From: Stephen D <webmaster@scd31.com> Date: Sat, 8 Jul 2023 15:29:27 -0300 Subject: [PATCH] basic SPI working --- src/main.rs | 223 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 171 insertions(+), 52 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9354caf..6c670af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,10 +12,12 @@ mod packet; #[rtic::app(device = stm32f4xx_hal::pac, peripherals = true)] mod app { use hal::block; + use hal::dma; use hal::gpio::{self, Speed}; - use hal::pac::{SPI1, TIM5, USART1}; + use hal::pac::{self, SPI1, TIM5, USART1}; use hal::prelude::*; use hal::serial::{Config, Event, Serial}; + use hal::spi; use hal::spi::{Mode, Phase, Polarity, Spi}; use hal::timer::Delay; use rf4463::config::RADIO_CONFIG_500_2; @@ -28,6 +30,13 @@ mod app { // in # packets const BUFFER_LEN: usize = 50; + // in bytes + // explicitly make this smaller than a packet so that we don't buffer things in the tx DMA for a while + const PI_RX_BUFFER_LEN: usize = 256; + + // in bytes + const PI_TX_BUFFER_LEN: usize = 1; + const MODE: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, @@ -41,21 +50,33 @@ mod app { Delay<TIM5, 1000000>, >; + type RxTransfer = dma::Transfer< + dma::StreamX<pac::DMA1, 3>, + 0, + spi::Rx<pac::SPI2>, + dma::PeripheralToMemory, + &'static mut [u8; PI_RX_BUFFER_LEN], + >; + + type TxTransfer = dma::Transfer< + dma::StreamX<pac::DMA1, 4>, + 0, + spi::Tx<pac::SPI2>, + dma::MemoryToPeripheral, + &'static mut [u8; PI_TX_BUFFER_LEN], + >; + #[derive(Debug)] enum SlaveCmd { - BufferStatus, SendPacket, GetTemp, - Sync, } impl SlaveCmd { pub fn from_u8(i: u8) -> Option<Self> { match i { - 0x00 => Some(Self::BufferStatus), 0x01 => Some(Self::SendPacket), 0x02 => Some(Self::GetTemp), - 0x03 => Some(Self::Sync), _ => None, } } @@ -72,13 +93,18 @@ mod app { radio: Radio, radio_temp: f32, tx_buf: ConstGenericRingBuffer<Packet, BUFFER_LEN>, + + pi_tx: TxTransfer, + other_tx_buf: Option<&'static mut [u8; PI_TX_BUFFER_LEN]>, } #[local] struct Local { radio_irq: gpio::Pin<'B', 2, gpio::Input>, - usart: Serial<USART1>, state: SlaveState, + + pi_rx: RxTransfer, + other_rx_buf: Option<&'static mut [u8; PI_RX_BUFFER_LEN]>, } #[init] @@ -99,6 +125,7 @@ mod app { let mut sys_cfg = ctx.device.SYSCFG.constrain(); // setup 4463 spi + let mosi = gpioa.pa7.into_alternate().speed(Speed::VeryHigh); let miso = gpioa.pa6.into_alternate().speed(Speed::VeryHigh); let sclk = gpioa.pa5.into_alternate().speed(Speed::VeryHigh); @@ -116,36 +143,78 @@ mod app { radio_irq.enable_interrupt(&mut ctx.device.EXTI); radio_irq.trigger_on_edge(&mut ctx.device.EXTI, gpio::Edge::Falling); - // setup Pi UART - let tx = gpioa.pa9; - let rx = gpioa.pa10; - - let mut usart = Serial::new( - ctx.device.USART1, - (tx, rx), - Config::default().baudrate(921_600.bps()), - &clocks, - ) - .unwrap(); - - usart.listen(Event::Rxne); + // setup Pi SPI + let pi_mosi = gpiob.pb15.internal_resistor(gpio::Pull::Up); + let pi_miso = gpiob.pb14.internal_resistor(gpio::Pull::Down); + let pi_sclk = gpiob.pb13.internal_resistor(gpio::Pull::Down); + + // TODO nss? + + let mut pi_spi = ctx + .device + .SPI2 + .spi_slave((pi_sclk, pi_miso, pi_mosi, None), MODE); + pi_spi.set_internal_nss(false); + + let (tx, rx) = pi_spi.use_dma().txrx(); + + let streams = dma::StreamsTuple::new(ctx.device.DMA1); + + let tx_buffer = + cortex_m::singleton!(: [u8; PI_TX_BUFFER_LEN] = [0x02; PI_TX_BUFFER_LEN]).unwrap(); + let other_tx_buf = + Some(cortex_m::singleton!(: [u8; PI_TX_BUFFER_LEN] = [0u8; PI_TX_BUFFER_LEN]).unwrap()); + + let mut pi_tx = dma::Transfer::init_memory_to_peripheral( + streams.4, + tx, + tx_buffer, + None, + dma::config::DmaConfig::default().memory_increment(false), + ); + + pi_tx.start(|_tx| {}); + + let rx_buffer = + cortex_m::singleton!(: [u8; PI_RX_BUFFER_LEN] = [0u8; PI_RX_BUFFER_LEN]).unwrap(); + let other_rx_buf = + Some(cortex_m::singleton!(: [u8; PI_RX_BUFFER_LEN] = [0u8; PI_RX_BUFFER_LEN]).unwrap()); + + let mut pi_rx = dma::Transfer::init_peripheral_to_memory( + streams.3, + rx, + rx_buffer, + None, + dma::config::DmaConfig::default() + .memory_increment(true) + .fifo_enable(true) + .fifo_error_interrupt(true) + .transfer_complete_interrupt(true), + ); + + pi_rx.start(|_rx| {}); ( Shared { radio, radio_temp: 0.0, tx_buf: ConstGenericRingBuffer::new(), + + pi_tx, + other_tx_buf, }, Local { radio_irq, - usart, state: SlaveState::Idle, + + pi_rx, + other_rx_buf, }, init::Monotonics(), ) } - #[idle(shared=[radio, tx_buf, radio_temp])] + #[idle(shared=[radio, tx_buf, radio_temp, pi_tx, other_tx_buf])] fn idle(mut ctx: idle::Context) -> ! { let mut i = 0; let mut iterations_since_last_packet = 0; @@ -176,6 +245,15 @@ mod app { } } + // update our SPI bus to say if we have free space or not + let b = if ctx.shared.tx_buf.lock(|b| b.len() >= b.capacity() - 2) { + 0x01 // not enough space for packet + } else { + 0x02 // space for packet + }; + + set_spi_tx_byte(b, &mut ctx.shared.pi_tx, &mut ctx.shared.other_tx_buf); + iterations_since_last_packet = 0; } else { iterations_since_last_packet += 1; @@ -194,43 +272,74 @@ mod app { } } - #[task(binds = USART1, priority = 2, local = [state, usart], shared = [tx_buf, radio_temp])] - fn usart1(mut ctx: usart1::Context) { - let state = ctx.local.state; - let usart = ctx.local.usart; - let mut buf = ctx.shared.tx_buf; + #[task(binds = DMA1_STREAM3, priority = 2, local = [state, pi_rx, other_rx_buf], shared = [tx_buf, pi_tx, other_tx_buf])] + fn pi_spi_recv(mut ctx: pi_spi_recv::Context) { + let rx = ctx.local.pi_rx; + + let (old_buf, _) = rx + .next_transfer(ctx.local.other_rx_buf.take().unwrap()) + .unwrap(); + + for b in old_buf.iter() { + handle_incoming_byte( + *b, + ctx.local.state, + &mut ctx.shared.tx_buf, + &mut ctx.shared.pi_tx, + &mut ctx.shared.other_tx_buf, + ); + } + + *ctx.local.other_rx_buf = Some(old_buf); - let x = block!(usart.read()).unwrap(); + rx.clear_transfer_complete_interrupt(); + rx.clear_fifo_error_interrupt(); + } + + #[task(binds = EXTI2, shared = [radio], local = [radio_irq])] + fn radio_irq(mut ctx: radio_irq::Context) { + ctx.shared.radio.lock(|r| r.interrupt()).unwrap(); + let irq = ctx.local.radio_irq; + if irq.is_high() { + irq.clear_interrupt_pending_bit(); + } + } + + fn handle_incoming_byte< + T: rtic::Mutex<T = ConstGenericRingBuffer<Packet, BUFFER_LEN>>, + A: rtic::Mutex<T = TxTransfer>, + B: rtic::Mutex<T = Option<&'static mut [u8; PI_TX_BUFFER_LEN]>>, + >( + x: u8, + state: &mut SlaveState, + buf: &mut T, + tx: &mut A, + other_tx: &mut B, + ) { 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 - } - } - SlaveCmd::SendPacket => { - if buf.lock(|b| b.len() >= b.capacity() - 1) { - block!(usart.write(0x01)).unwrap(); // not enough space for a new packet + let b = if buf.lock(|b| b.len() >= b.capacity() - 2) { + 0x01 // not enough space for packet } else { - block!(usart.write(0x02)).unwrap(); // safe to send a packet - } + 0x02 // space for packet + }; + + set_spi_tx_byte(b, tx, other_tx); *state = SlaveState::RecvPacket(Packet::default()); } SlaveCmd::GetTemp => { - let temp = ctx.shared.radio_temp.lock(|x| *x); - usart.bwrite_all(&temp.to_le_bytes()).unwrap(); - } + todo!(); - SlaveCmd::Sync => { - usart.bwrite_all(&[0x03]).unwrap(); + /* + let temp = ctx.shared.radio_temp.lock(|x| *x); + usart.bwrite_all(&temp.to_le_bytes()).unwrap(); + */ } } } @@ -247,13 +356,23 @@ mod app { }; } - #[task(binds = EXTI2, shared = [radio], local = [radio_irq])] - fn radio_irq(mut ctx: radio_irq::Context) { - ctx.shared.radio.lock(|r| r.interrupt()).unwrap(); - - let irq = ctx.local.radio_irq; - if irq.is_high() { - irq.clear_interrupt_pending_bit(); - } + fn set_spi_tx_byte< + A: rtic::Mutex<T = TxTransfer>, + B: rtic::Mutex<T = Option<&'static mut [u8; PI_TX_BUFFER_LEN]>>, + >( + b: u8, + tx: &mut A, + other_tx: &mut B, + ) { + other_tx.lock(|t| t.as_mut().unwrap()[0] = b); + + other_tx.lock(|t| { + let (tx, _) = + tx.lock(|tx_transfer| tx_transfer.next_transfer(t.take().unwrap()).unwrap()); + + t.replace(tx); + }); } } + +// TODO need an interrupt to clear TX errors -- GitLab