mirror of
https://github.com/embassy-rs/embassy.git
synced 2026-05-03 11:35:31 +00:00
net-esp-hosted: add Interface trait.
This commit is contained in:
62
embassy-net-esp-hosted/src/iface.rs
Normal file
62
embassy-net-esp-hosted/src/iface.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use embassy_time::Timer;
|
||||
use embedded_hal::digital::InputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::SpiDevice;
|
||||
|
||||
/// Physical interface trait for communicating with the ESP chip.
|
||||
pub trait Interface {
|
||||
/// Wait for the HANDSHAKE signal indicating the ESP is ready for a new transaction.
|
||||
async fn wait_for_handshake(&mut self);
|
||||
|
||||
/// Wait for the READY signal indicating the ESP has data to send.
|
||||
async fn wait_for_ready(&mut self);
|
||||
|
||||
/// Perform a SPI transfer, exchanging data with the ESP chip.
|
||||
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]);
|
||||
}
|
||||
|
||||
/// Standard SPI interface.
|
||||
///
|
||||
/// This interface is what's implemented in the upstream `esp-hosted-fg` firmware. It uses:
|
||||
/// - An `SpiDevice` for SPI communication (CS is handled by the device)
|
||||
/// - A handshake pin that signals when the ESP is ready for a new transaction
|
||||
/// - A ready pin that indicates when the ESP has data to send
|
||||
pub struct SpiInterface<SPI, IN> {
|
||||
spi: SPI,
|
||||
handshake: IN,
|
||||
ready: IN,
|
||||
}
|
||||
|
||||
impl<SPI, IN> SpiInterface<SPI, IN>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
IN: InputPin + Wait,
|
||||
{
|
||||
/// Create a new SpiInterface.
|
||||
pub fn new(spi: SPI, handshake: IN, ready: IN) -> Self {
|
||||
Self { spi, handshake, ready }
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI, IN> Interface for SpiInterface<SPI, IN>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
IN: InputPin + Wait,
|
||||
{
|
||||
async fn wait_for_handshake(&mut self) {
|
||||
self.handshake.wait_for_high().await.unwrap();
|
||||
}
|
||||
|
||||
async fn wait_for_ready(&mut self) {
|
||||
self.ready.wait_for_high().await.unwrap();
|
||||
}
|
||||
|
||||
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) {
|
||||
self.spi.transfer(rx, tx).await.unwrap();
|
||||
|
||||
// The esp-hosted firmware deasserts the HANDSHAKE pin a few us AFTER ending the SPI transfer
|
||||
// If we check it again too fast, we'll see it's high from the previous transfer, and if we send it
|
||||
// data it will get lost.
|
||||
Timer::after_micros(100).await;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
#![no_std]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
#![allow(async_fn_in_trait)]
|
||||
|
||||
use embassy_futures::select::{Either4, select4};
|
||||
use embassy_net_driver_channel as ch;
|
||||
use embassy_net_driver_channel::driver::LinkState;
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use embedded_hal::digital::{InputPin, OutputPin};
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::SpiDevice;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
|
||||
use crate::ioctl::{PendingIoctl, Shared};
|
||||
use crate::proto::{CtrlMsg, CtrlMsgPayload};
|
||||
@@ -19,9 +18,11 @@ mod proto;
|
||||
mod fmt;
|
||||
|
||||
mod control;
|
||||
mod iface;
|
||||
mod ioctl;
|
||||
|
||||
pub use control::*;
|
||||
pub use iface::*;
|
||||
|
||||
const MTU: usize = 1514;
|
||||
|
||||
@@ -118,20 +119,17 @@ impl State {
|
||||
/// Type alias for network driver.
|
||||
pub type NetDriver<'a> = ch::Device<'a, MTU>;
|
||||
|
||||
/// Create a new esp-hosted driver using the provided state, SPI peripheral and pins.
|
||||
/// Create a new esp-hosted driver using the provided state, interface, and reset pin.
|
||||
///
|
||||
/// Returns a device handle for interfacing with embassy-net, a control handle for
|
||||
/// interacting with the driver, and a runner for communicating with the WiFi device.
|
||||
pub async fn new<'a, SPI, IN, OUT>(
|
||||
pub async fn new<'a, I, OUT>(
|
||||
state: &'a mut State,
|
||||
spi: SPI,
|
||||
handshake: IN,
|
||||
ready: IN,
|
||||
iface: I,
|
||||
reset: OUT,
|
||||
) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>)
|
||||
) -> (NetDriver<'a>, Control<'a>, Runner<'a, I, OUT>)
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
IN: InputPin + Wait,
|
||||
I: Interface,
|
||||
OUT: OutputPin,
|
||||
{
|
||||
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
|
||||
@@ -142,10 +140,8 @@ where
|
||||
state_ch,
|
||||
shared: &state.shared,
|
||||
next_seq: 1,
|
||||
handshake,
|
||||
ready,
|
||||
reset,
|
||||
spi,
|
||||
iface,
|
||||
heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
|
||||
};
|
||||
|
||||
@@ -153,7 +149,7 @@ where
|
||||
}
|
||||
|
||||
/// Runner for communicating with the WiFi device.
|
||||
pub struct Runner<'a, SPI, IN, OUT> {
|
||||
pub struct Runner<'a, I, OUT> {
|
||||
ch: ch::Runner<'a, MTU>,
|
||||
state_ch: ch::StateRunner<'a>,
|
||||
shared: &'a Shared,
|
||||
@@ -161,16 +157,13 @@ pub struct Runner<'a, SPI, IN, OUT> {
|
||||
next_seq: u16,
|
||||
heartbeat_deadline: Instant,
|
||||
|
||||
spi: SPI,
|
||||
handshake: IN,
|
||||
ready: IN,
|
||||
iface: I,
|
||||
reset: OUT,
|
||||
}
|
||||
|
||||
impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT>
|
||||
impl<'a, I, OUT> Runner<'a, I, OUT>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
IN: InputPin + Wait,
|
||||
I: Interface,
|
||||
OUT: OutputPin,
|
||||
{
|
||||
/// Run the packet processing.
|
||||
@@ -185,11 +178,11 @@ where
|
||||
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
||||
|
||||
loop {
|
||||
self.handshake.wait_for_high().await.unwrap();
|
||||
self.iface.wait_for_handshake().await;
|
||||
|
||||
let ioctl = self.shared.ioctl_wait_pending();
|
||||
let tx = self.ch.tx_buf();
|
||||
let ev = async { self.ready.wait_for_high().await.unwrap() };
|
||||
let ev = async { self.iface.wait_for_ready().await };
|
||||
let hb = Timer::at(self.heartbeat_deadline);
|
||||
|
||||
match select4(ioctl, tx, ev, hb).await {
|
||||
@@ -243,15 +236,9 @@ where
|
||||
trace!("tx: {:02x}", &tx_buf[..40]);
|
||||
}
|
||||
|
||||
self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
|
||||
self.iface.transfer(&mut rx_buf, &tx_buf).await;
|
||||
|
||||
// The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer
|
||||
// If we check it again too fast, we'll see it's high from the previous transfer, and if we send it
|
||||
// data it will get lost.
|
||||
// Make sure we check it after 100us at minimum.
|
||||
let delay_until = Instant::now() + Duration::from_micros(100);
|
||||
self.handle_rx(&mut rx_buf);
|
||||
Timer::at(delay_until).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,12 @@ bind_interrupts!(struct Irqs {
|
||||
async fn wifi_task(
|
||||
runner: hosted::Runner<
|
||||
'static,
|
||||
ExclusiveDevice<Spim<'static>, Output<'static>, Delay>,
|
||||
Input<'static>,
|
||||
hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,
|
||||
Output<'static>,
|
||||
>,
|
||||
) -> ! {
|
||||
runner.run().await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! {
|
||||
runner.run().await
|
||||
@@ -60,15 +58,11 @@ async fn main(spawner: Spawner) {
|
||||
let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
|
||||
let spi = ExclusiveDevice::new(spi, cs, Delay);
|
||||
|
||||
let iface = hosted::SpiInterface::new(spi, handshake, ready);
|
||||
|
||||
static ESP_STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();
|
||||
let (device, mut control, runner) = embassy_net_esp_hosted::new(
|
||||
ESP_STATE.init(embassy_net_esp_hosted::State::new()),
|
||||
spi,
|
||||
handshake,
|
||||
ready,
|
||||
reset,
|
||||
)
|
||||
.await;
|
||||
let (device, mut control, runner) =
|
||||
embassy_net_esp_hosted::new(ESP_STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;
|
||||
|
||||
spawner.spawn(unwrap!(wifi_task(runner)));
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud";
|
||||
async fn wifi_task(
|
||||
runner: hosted::Runner<
|
||||
'static,
|
||||
ExclusiveDevice<Spim<'static>, Output<'static>, Delay>,
|
||||
Input<'static>,
|
||||
hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,
|
||||
Output<'static>,
|
||||
>,
|
||||
) -> ! {
|
||||
@@ -64,15 +63,11 @@ async fn main(spawner: Spawner) {
|
||||
let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
|
||||
let spi = ExclusiveDevice::new(spi, cs, Delay);
|
||||
|
||||
let iface = hosted::SpiInterface::new(spi, handshake, ready);
|
||||
|
||||
static STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();
|
||||
let (device, mut control, runner) = embassy_net_esp_hosted::new(
|
||||
STATE.init(embassy_net_esp_hosted::State::new()),
|
||||
spi,
|
||||
handshake,
|
||||
ready,
|
||||
reset,
|
||||
)
|
||||
.await;
|
||||
let (device, mut control, runner) =
|
||||
embassy_net_esp_hosted::new(STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;
|
||||
|
||||
spawner.spawn(unwrap!(wifi_task(runner)));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user