#![no_main] #![no_std] extern crate cortex_m; extern crate cortex_m_rt as rt; extern crate panic_semihosting; extern crate stm32f4xx_hal as hal; 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 ringbuffer::{ConstGenericRingBuffer, RingBuffer, RingBufferRead, RingBufferWrite}; use crate::packet::Packet; // in # packets const BUFFER_LEN: usize = 50; const MODE: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, }; type Radio = Rf4463< Spi<SPI1>, gpio::Pin<'B', 1, gpio::Output>, gpio::Pin<'B', 0, gpio::Output>, Delay<TIM5, 1000000>, >; #[derive(Debug)] enum SlaveCmd { BufferStatus, SendPacket, GetTemp, } impl SlaveCmd { pub fn from_u8(i: u8) -> Option<Self> { match i { 0x00 => Some(Self::BufferStatus), 0x01 => Some(Self::SendPacket), 0x02 => Some(Self::GetTemp), _ => None, } } } #[allow(clippy::large_enum_variant)] pub enum SlaveState { Idle, RecvPacket(Packet), } #[shared] struct Shared { radio_temp: f32, tx_buf: ConstGenericRingBuffer<Packet, BUFFER_LEN>, } #[local] struct Local { radio: Radio, usart: Serial<USART1>, state: SlaveState, } #[init] fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { let rcc = ctx.device.RCC.constrain(); let gpioa = ctx.device.GPIOA.split(); let gpiob = ctx.device.GPIOB.split(); // setup clocks let clocks = rcc .cfgr .use_hse(25.MHz()) .sysclk(100.MHz()) .pclk1(48.MHz()) .pclk2(48.MHz()) .freeze(); // setup 4463 spi let mosi = gpioa.pa7.into_alternate(); let miso = gpioa.pa6.into_alternate(); let sclk = gpioa.pa5.into_alternate(); let sdn = gpiob.pb1.into_push_pull_output(); let cs = gpiob.pb0.into_push_pull_output(); let spi = Spi::new(ctx.device.SPI1, (sclk, miso, mosi), MODE, 10.MHz(), &clocks); let delay = ctx.device.TIM5.delay_us(&clocks); let radio = Rf4463::new(spi, sdn, cs, delay, &mut RADIO_CONFIG_500_2.clone()).unwrap(); // 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); ( Shared { radio_temp: 0.0, tx_buf: ConstGenericRingBuffer::new(), }, Local { radio, usart, state: SlaveState::Idle, }, init::Monotonics(), ) } #[idle(local=[radio], shared=[tx_buf, radio_temp])] fn idle(mut ctx: idle::Context) -> ! { let mut i = 0; loop { if let Some(mut pkt) = ctx.shared.tx_buf.lock(|buf| buf.dequeue()) { ctx.local.radio.tx(&mut pkt.data).unwrap(); } i += 1; // get temp every 500 packets, or whenever when idle TODO if i >= 500 { i = 0; if let Ok(new_temp) = ctx.local.radio.get_temp() { ctx.shared.radio_temp.lock(|cur_temp| *cur_temp = new_temp); } } } } #[task(binds = USART1, priority=1, 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; 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 } } 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 } *state = SlaveState::RecvPacket(Packet::default()); } SlaveCmd::GetTemp => { let temp = ctx.shared.radio_temp.lock(|x| *x); usart.bwrite_all(&temp.to_le_bytes()).unwrap(); } } } } SlaveState::RecvPacket(pkt) => { pkt.push(x); if pkt.is_complete() { buf.lock(|b| b.enqueue(pkt.clone())); *state = SlaveState::Idle; } } }; } }