Initial proof of concept
This commit is contained in:
29
node/Cargo.toml
Normal file
29
node/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "physical-node"
|
||||
description = "A node hosts peripherals."
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
comms = []
|
||||
single-packet-msgs = []
|
||||
usb = ["embassy-usb"]
|
||||
stm32 = ["embassy-stm32", "physical/stm32"]
|
||||
|
||||
[dependencies.physical]
|
||||
path = ".."
|
||||
[dependencies.embedded-hal]
|
||||
workspace = true
|
||||
[dependencies.embedded-hal-async]
|
||||
workspace = true
|
||||
[dependencies.defmt]
|
||||
workspace = true
|
||||
[dependencies.embassy-stm32]
|
||||
workspace = true
|
||||
optional = true
|
||||
[dependencies.embassy-usb]
|
||||
workspace = true
|
||||
optional = true
|
17
node/src/comms.rs
Normal file
17
node/src/comms.rs
Normal file
@ -0,0 +1,17 @@
|
||||
pub trait Sender {
|
||||
async fn send(&mut self, msg: &[u8]) -> Result<(), Reset>;
|
||||
}
|
||||
|
||||
pub trait Receiver {
|
||||
async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Reset>;
|
||||
}
|
||||
|
||||
//TODO: Replace with !
|
||||
pub struct Never;
|
||||
|
||||
/// Communication errors indicates either:
|
||||
/// Our connection was already disconnected, in which case we should reset and wait for new connection to made.
|
||||
/// or
|
||||
/// There was an unexpected, irrecoverable error in communication, in which case we don't want to enter a terminal error
|
||||
/// safe mode, because there is no indication the actual control is broken, so all we can really do is reset the connection.
|
||||
pub struct Reset;
|
12
node/src/lib.rs
Normal file
12
node/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "comms")]
|
||||
pub mod comms;
|
||||
pub mod spi;
|
||||
#[cfg(feature = "stm32")]
|
||||
pub mod stm32;
|
||||
|
||||
pub use physical::CriticalError;
|
||||
|
||||
pub const GPIO_ERROR_MSG: &'static str =
|
||||
"Driver does not support GPIO pins with expected failure states";
|
52
node/src/spi.rs
Normal file
52
node/src/spi.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::GPIO_ERROR_MSG;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi;
|
||||
use embedded_hal::spi::SpiBus;
|
||||
|
||||
/// End the SPI operation if the result was an error.
|
||||
/// This function will attempt to flush the SPI bus if the result being inspected was an error.
|
||||
/// If the flush fails, the flush error will be ignored and the original error will be returned.
|
||||
#[inline]
|
||||
pub fn end_spi_if_err<OkT, SpiT: SpiBus>(
|
||||
slave_select: &mut impl OutputPin,
|
||||
spi: &mut SpiT,
|
||||
result: Result<OkT, <SpiT as spi::ErrorType>::Error>,
|
||||
) -> Result<OkT, <SpiT as spi::ErrorType>::Error> {
|
||||
match result {
|
||||
Ok(_) => result,
|
||||
Err(_) => {
|
||||
// Ignore flush error and return the original error
|
||||
slave_select.set_high().expect(GPIO_ERROR_MSG);
|
||||
let _ = spi.flush();
|
||||
result
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// End the series of SPI operations and forward the error of the final operation if there was
|
||||
/// one. Error handling:
|
||||
/// * If there was an error in the SPI operation and an error flushing the bus, returns the original
|
||||
/// SPI error, not the bus error.
|
||||
/// * If there was not an error in the SPI operation but was an error flushing the bus, returns the
|
||||
/// bus flush error.
|
||||
/// * If there was an error in the SPI operation and no error flushing the bus, returns the original
|
||||
/// SPI error.
|
||||
#[inline]
|
||||
pub fn end_spi<OkT, SpiT: SpiBus>(
|
||||
slave_select: &mut impl OutputPin,
|
||||
spi: &mut SpiT,
|
||||
result: Result<OkT, <SpiT as spi::ErrorType>::Error>,
|
||||
) -> Result<OkT, <SpiT as spi::ErrorType>::Error> {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
slave_select.set_high().expect(GPIO_ERROR_MSG);
|
||||
spi.flush()?;
|
||||
result
|
||||
},
|
||||
Err(_) => {
|
||||
slave_select.set_high().expect(GPIO_ERROR_MSG);
|
||||
let _ = spi.flush();
|
||||
result
|
||||
},
|
||||
}
|
||||
}
|
2
node/src/stm32/mod.rs
Normal file
2
node/src/stm32/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "usb")]
|
||||
pub mod usb;
|
41
node/src/stm32/usb.rs
Normal file
41
node/src/stm32/usb.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// The library will have build errors when built on its own (due to not having embassy-stm32 feature for a specific microcontroller)
|
||||
// but it will work fine when used as a dependency for firmware that has the feature for a specific stm32 microcontroller.
|
||||
|
||||
use crate::comms;
|
||||
use embassy_stm32::peripherals::USB_OTG_FS;
|
||||
use embassy_stm32::usb_otg::{Driver, Endpoint, In, Out};
|
||||
use embassy_usb::driver::{EndpointIn, EndpointOut};
|
||||
use embassy_usb::UsbDevice;
|
||||
|
||||
pub type TypedUSB = UsbDevice<'static, Driver<'static, USB_OTG_FS>>;
|
||||
pub type TypedInterIn = Endpoint<'static, USB_OTG_FS, In>;
|
||||
pub type TypedInterOut = Endpoint<'static, USB_OTG_FS, Out>;
|
||||
|
||||
pub struct UsbIO {
|
||||
/// Send to master
|
||||
pub interrupt_in: TypedInterIn,
|
||||
/// Recieve from master
|
||||
pub interrupt_out: TypedInterOut,
|
||||
}
|
||||
|
||||
impl comms::Sender for TypedInterIn {
|
||||
async fn send(&mut self, msg: &[u8]) -> Result<(), comms::Reset> {
|
||||
self.write(msg).await.map_err(|_| comms::Reset)
|
||||
}
|
||||
}
|
||||
|
||||
impl comms::Receiver for TypedInterOut {
|
||||
#[cfg(feature = "single-packet-msgs")]
|
||||
async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), comms::Reset> {
|
||||
// This is OK when all our messages are smaller than a single packet.
|
||||
self.read(buffer)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| comms::Reset)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "single-packet-msgs"))]
|
||||
async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), comms::Reset> {
|
||||
todo!("Decide if we want a general purpose multi-packet message receive")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user