use atsamd_hal::{
clock::GenericClockController,
delay::Delay,
ehal::digital::OutputPin,
pac::{interrupt, Mclk},
prelude::*,
sercom::{uart, IoSet2, Sercom0},
};
use bbqueue::{self, BBBuffer, Consumer, Producer};
use cortex_m::interrupt::CriticalSection;
use cortex_m::peripheral::NVIC;
use nb;
pub use erpc::rpcs;
use seeed_erpc as erpc;
use super::pins::aliases::*;
use crate::WIFI_UART_BAUD;
pub struct WifiPins {
pub pwr: WifiPwrReset,
pub rxd: WifiRxdReset,
pub txd: WifiTxdReset,
pub mosi: WifiTxReset,
pub clk: WifiClkReset,
pub miso: WifiRxReset,
pub cs: WifiCsReset,
pub ready: WifiReadyReset,
pub dir: WifiDirReset,
}
pub struct Wifi {
_pwr: WifiPwr,
uart: WifiUart,
rx_buff_isr: Producer<'static, 512>,
rx_buff_input: Consumer<'static, 512>,
tx_buff_isr: Consumer<'static, 128>,
tx_buff_input: Producer<'static, 128>,
sequence: u32,
}
pub type WifiUartPads = uart::Pads<Sercom0, IoSet2, WifiRx, WifiTx>;
pub type WifiUart = uart::Uart<uart::Config<WifiUartPads>, uart::Duplex>;
impl Wifi {
pub fn init(
pins: WifiPins,
sercom0: Sercom0,
clocks: &mut GenericClockController,
mclk: &mut Mclk,
delay: &mut Delay,
rx_buff: &'static BBBuffer<512>,
tx_buff: &'static BBBuffer<128>,
) -> Wifi {
let gclk0 = clocks.gclk0();
let pads = uart::Pads::default().rx(pins.miso).tx(pins.mosi);
let uart = uart::Config::new(
mclk,
sercom0,
pads,
clocks.sercom0_core(&gclk0).unwrap().freq(),
)
.baud(
WIFI_UART_BAUD.Hz(),
uart::BaudMode::Fractional(uart::Oversampling::Bits16),
);
delay.delay_ms(10u8);
let mut pwr: WifiPwr = pins.pwr.into();
OutputPin::set_low(&mut pwr).ok();
delay.delay_ms(100u8);
OutputPin::set_high(&mut pwr).ok();
delay.delay_ms(200u8);
let (rx_buff_isr, rx_buff_input) = rx_buff.try_split().unwrap();
let (tx_buff_input, tx_buff_isr) = tx_buff.try_split().unwrap();
let sequence = 0;
let uart = uart.enable();
Wifi {
_pwr: pwr,
uart,
rx_buff_isr,
rx_buff_input,
tx_buff_isr,
tx_buff_input,
sequence,
}
}
pub fn enable(&mut self, _cs: &CriticalSection, nvic: &mut NVIC) {
unsafe {
nvic.set_priority(interrupt::SERCOM0_0, 1);
NVIC::unmask(interrupt::SERCOM0_0);
nvic.set_priority(interrupt::SERCOM0_2, 1);
NVIC::unmask(interrupt::SERCOM0_2);
}
self.uart.enable_interrupts(uart::Flags::RXC);
}
pub fn connect_to_ap<S: TryInto<heapless::String<64>>, P: TryInto<heapless::String<64>>>(
&mut self,
delay: &mut Delay,
ssid: S,
pw: P,
security: erpc::Security,
) -> Result<erpc::IPInfo, erpc::Err<()>> {
self.blocking_rpc(rpcs::AdapterInit {})?;
self.blocking_rpc(rpcs::DHCPClientStop {
interface: erpc::L3Interface::Station,
})?;
self.blocking_rpc(rpcs::WifiOff {})?;
delay.delay_ms(35u8);
self.blocking_rpc(rpcs::WifiOn {
mode: erpc::WifiMode::Station,
})?;
self.blocking_rpc(rpcs::WifiConnect {
ssid: ssid.try_into().map_err(|_| erpc::Err::Unknown)?,
password: pw.try_into().map_err(|_| erpc::Err::Unknown)?,
security: security,
semaphore: 0,
})?;
self.blocking_rpc(rpcs::DHCPClientStart {
interface: erpc::L3Interface::Station,
})?;
delay.delay_ms(25u8);
self.blocking_rpc(rpcs::GetIPInfo {
interface: erpc::L3Interface::Station,
})
.map_err(|_| erpc::Err::RPCErr(()))
}
pub fn _handle_rx(&mut self) {
match self.uart.read() {
Ok(b) => {
if let Ok(mut wgr) = self.rx_buff_isr.grant_exact(1) {
wgr[0] = b;
wgr.commit(1);
} else {
panic!("overrun");
}
}
Err(nb::Error::Other(e)) => {
panic!("unrecoverable read error");
}
Err(nb::Error::WouldBlock) => (),
};
}
pub fn _handle_data_empty(&mut self) {
if let Ok(rgr) = self.tx_buff_isr.read() {
let buf = rgr.buf();
self.uart.write(buf[0]).ok();
rgr.release(1);
} else {
self.uart.disable_interrupts(uart::Flags::DRE);
}
}
pub fn blocking_rpc<'a, RPC: erpc::RPC>(
&mut self,
mut rpc: RPC,
) -> Result<RPC::ReturnValue, erpc::Err<RPC::Error>> {
let mut tx_buff = heapless::Vec::new();
tx_buff
.extend_from_slice(&rpc.header(self.next_seq()).as_bytes())
.map_err(|_| erpc::Err::TXErr)?;
rpc.args(&mut tx_buff);
self.write_frame(&tx_buff).map_err(|_| erpc::Err::TXErr)?;
loop {
let result = self.recieve_rpc_response(&mut rpc);
if let Err(erpc::Err::NotOurs) = result {
continue;
};
break result;
}
}
fn recieve_rpc_response<'a, RPC: erpc::RPC>(
&mut self,
rpc: &mut RPC,
) -> Result<RPC::ReturnValue, erpc::Err<RPC::Error>> {
let fh = self.recieve_frame_header(rpc)?;
let mut buffer = [0u8; 2048];
let sz = fh.msg_length as usize;
self.recieve_bytes(&mut buffer[..sz]);
fh.check_crc(&buffer[..sz])?;
rpc.parse(&buffer[..sz])
}
fn recieve_frame_header<'a, RPC: erpc::RPC>(
&mut self,
_rpc: &mut RPC,
) -> Result<erpc::FrameHeader, erpc::Err<RPC::Error>> {
let mut buffer = [0u8; 4];
self.recieve_bytes(&mut buffer);
match erpc::FrameHeader::parse(&buffer[..]) {
Err(e) => Err(erpc::Err::Parsing(e)),
Ok(fh) => Ok(fh.1),
}
}
fn recieve_bytes(&mut self, mut buffer: &mut [u8]) {
while buffer.len() > 0 {
let r = match self.rx_buff_input.read() {
Ok(r) => r,
Err(_) => {
continue;
}
};
let b = r.buf();
let copy_amt = if b.len() < buffer.len() {
b.len()
} else {
buffer.len()
};
for (i, b) in b[..copy_amt].iter().enumerate() {
buffer[i] = *b;
}
buffer = &mut buffer[copy_amt..];
r.release(copy_amt);
}
}
fn write_frame(&mut self, msg: &heapless::Vec<u8, 64>) -> Result<(), ()> {
let header = erpc::FrameHeader::new_from_msg(msg);
self.tx(header.as_bytes().iter().chain(msg));
Ok(())
}
fn tx<'a, D: Iterator<Item = &'a u8>>(&mut self, data: D) {
for b in data {
if let Ok(mut wgr) = self.tx_buff_input.grant_exact(1) {
wgr[0] = *b;
wgr.commit(1);
}
self.uart.enable_interrupts(uart::Flags::DRE);
}
}
fn next_seq(&mut self) -> u32 {
self.sequence += 1;
self.sequence
}
}
pub mod wifi_prelude {
pub use crate::wifi::*;
pub use atsamd_hal::pac::Sercom0;
pub use atsamd_hal::pac::{interrupt, Mclk};
pub use bbqueue::{BBBuffer, Producer};
pub use cortex_m::interrupt::CriticalSection;
}
#[macro_export]
macro_rules! wifi_singleton {
($global_name:ident) => {
static mut $global_name: Option<Wifi> = None;
static WIFI_RX: BBBuffer<512> = BBBuffer::new();
static WIFI_TX: BBBuffer<128> = BBBuffer::new();
unsafe fn wifi_init(
_cs: &CriticalSection,
pins: WifiPins,
sercom0: Sercom0,
clocks: &mut GenericClockController,
mclk: &mut Mclk,
delay: &mut Delay,
) {
unsafe {
$global_name = Some(Wifi::init(
pins, sercom0, clocks, mclk, delay, &WIFI_RX, &WIFI_TX,
));
}
}
#[interrupt]
fn SERCOM0_0() {
unsafe {
$global_name.as_mut().map(|wifi| {
wifi._handle_data_empty();
});
}
}
#[interrupt]
fn SERCOM0_2() {
unsafe {
$global_name.as_mut().map(|wifi| {
wifi._handle_rx();
});
}
}
};
}