mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-12-04 04:33:27 +00:00
nrf: Add NFCT driver.
Co-Authored-By: turbocool3r <turbocool3r@gmail.com> Co-Authored-By: ferris <ferris@devdroplets.com>
This commit is contained in:
parent
227e073fca
commit
0740b235ac
@ -161,6 +161,9 @@ embassy_hal_internal::peripherals! {
|
|||||||
EGU3,
|
EGU3,
|
||||||
EGU4,
|
EGU4,
|
||||||
EGU5,
|
EGU5,
|
||||||
|
|
||||||
|
// NFC
|
||||||
|
NFCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_uarte!(UARTE0, UARTE0, UARTE0);
|
impl_uarte!(UARTE0, UARTE0, UARTE0);
|
||||||
|
@ -181,6 +181,9 @@ embassy_hal_internal::peripherals! {
|
|||||||
EGU3,
|
EGU3,
|
||||||
EGU4,
|
EGU4,
|
||||||
EGU5,
|
EGU5,
|
||||||
|
|
||||||
|
// NFC
|
||||||
|
NFCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_usb!(USBD, USBD, USBD);
|
impl_usb!(USBD, USBD, USBD);
|
||||||
|
@ -184,6 +184,9 @@ embassy_hal_internal::peripherals! {
|
|||||||
EGU3,
|
EGU3,
|
||||||
EGU4,
|
EGU4,
|
||||||
EGU5,
|
EGU5,
|
||||||
|
|
||||||
|
// NFC
|
||||||
|
NFCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_usb!(USBD, USBD, USBD);
|
impl_usb!(USBD, USBD, USBD);
|
||||||
|
@ -176,6 +176,9 @@ embassy_hal_internal::peripherals! {
|
|||||||
// NVMC
|
// NVMC
|
||||||
NVMC,
|
NVMC,
|
||||||
|
|
||||||
|
// NFC
|
||||||
|
NFCT,
|
||||||
|
|
||||||
// UARTE, TWI & SPI
|
// UARTE, TWI & SPI
|
||||||
SERIAL0,
|
SERIAL0,
|
||||||
SERIAL1,
|
SERIAL1,
|
||||||
|
@ -86,6 +86,14 @@ pub mod gpiote;
|
|||||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||||
pub mod i2s;
|
pub mod i2s;
|
||||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "nrf52832",
|
||||||
|
feature = "nrf52833",
|
||||||
|
feature = "nrf52840",
|
||||||
|
feature = "_nrf5340-app"
|
||||||
|
))]
|
||||||
|
pub mod nfct;
|
||||||
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
pub mod nvmc;
|
pub mod nvmc;
|
||||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
409
embassy-nrf/src/nfct.rs
Normal file
409
embassy-nrf/src/nfct.rs
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
//! NFC tag emulator driver.
|
||||||
|
//!
|
||||||
|
//! This driver implements support for emulating an ISO14443-3 card. Anticollision and selection
|
||||||
|
//! are handled automatically in hardware, then the driver lets you receive and reply to
|
||||||
|
//! raw ISO14443-3 frames in software.
|
||||||
|
//!
|
||||||
|
//! Higher layers such as ISO14443-4 aka ISO-DEP and ISO7816 must be handled on top
|
||||||
|
//! in software.
|
||||||
|
|
||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
use core::future::poll_fn;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode};
|
||||||
|
|
||||||
|
use crate::interrupt::InterruptExt;
|
||||||
|
use crate::pac::nfct::vals;
|
||||||
|
use crate::peripherals::NFCT;
|
||||||
|
use crate::util::slice_in_ram;
|
||||||
|
use crate::{interrupt, pac, Peripheral};
|
||||||
|
|
||||||
|
/// NFCID1 (aka UID) of different sizes.
|
||||||
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
|
pub enum NfcId {
|
||||||
|
/// 4-byte UID.
|
||||||
|
SingleSize([u8; 4]),
|
||||||
|
/// 7-byte UID.
|
||||||
|
DoubleSize([u8; 7]),
|
||||||
|
/// 10-byte UID.
|
||||||
|
TripleSize([u8; 10]),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The protocol field to be sent in the `SEL_RES` response byte (b6-b7).
|
||||||
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
|
pub enum SelResProtocol {
|
||||||
|
/// Configured for Type 2 Tag platform.
|
||||||
|
#[default]
|
||||||
|
Type2 = 0,
|
||||||
|
/// Configured for Type 4A Tag platform, compliant with ISO/IEC_14443.
|
||||||
|
Type4A = 1,
|
||||||
|
/// Configured for the NFC-DEP Protocol.
|
||||||
|
NfcDep = 2,
|
||||||
|
/// Configured for the NFC-DEP Protocol and Type 4A Tag platform.
|
||||||
|
NfcDepAndType4A = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Config for the `NFCT` peripheral driver.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
/// NFCID1 to use during autocollision.
|
||||||
|
pub nfcid1: NfcId,
|
||||||
|
/// SDD pattern to be sent in `SENS_RES`.
|
||||||
|
pub sdd_pat: SddPat,
|
||||||
|
/// Platform config to be sent in `SEL_RES`.
|
||||||
|
pub plat_conf: u8,
|
||||||
|
/// Protocol to be sent in the `SEL_RES` response.
|
||||||
|
pub protocol: SelResProtocol,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt handler.
|
||||||
|
pub struct InterruptHandler {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl interrupt::typelevel::Handler<interrupt::typelevel::NFCT> for InterruptHandler {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
trace!("irq");
|
||||||
|
pac::NFCT.inten().write(|w| w.0 = 0);
|
||||||
|
WAKER.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
|
|
||||||
|
/// NFC error.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
/// Rx Error received while waiting for frame
|
||||||
|
RxError,
|
||||||
|
/// Rx buffer was overrun, increase your buffer size to resolve this
|
||||||
|
RxOverrun,
|
||||||
|
/// Lost field.
|
||||||
|
Deactivated,
|
||||||
|
/// Collision
|
||||||
|
Collision,
|
||||||
|
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||||
|
BufferNotInRAM,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NFC tag emulator driver.
|
||||||
|
pub struct NfcT<'d> {
|
||||||
|
_p: PeripheralRef<'d, NFCT>,
|
||||||
|
rx_buf: [u8; 256],
|
||||||
|
tx_buf: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> NfcT<'d> {
|
||||||
|
/// Create an Nfc Tag driver
|
||||||
|
pub fn new(
|
||||||
|
_p: impl Peripheral<P = NFCT> + 'd,
|
||||||
|
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::NFCT, InterruptHandler> + 'd,
|
||||||
|
config: &Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(_p);
|
||||||
|
|
||||||
|
let r = pac::NFCT;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let reset = (r.as_ptr() as *mut u32).add(0xFFC / 4);
|
||||||
|
reset.write_volatile(0);
|
||||||
|
reset.read_volatile();
|
||||||
|
reset.write_volatile(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nfcid_size = match &config.nfcid1 {
|
||||||
|
NfcId::SingleSize(bytes) => {
|
||||||
|
r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*bytes));
|
||||||
|
|
||||||
|
vals::Nfcidsize::NFCID1SINGLE
|
||||||
|
}
|
||||||
|
NfcId::DoubleSize(bytes) => {
|
||||||
|
let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap();
|
||||||
|
r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk));
|
||||||
|
|
||||||
|
let mut chunk = [0u8; 4];
|
||||||
|
chunk[1..].copy_from_slice(bytes);
|
||||||
|
r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk));
|
||||||
|
|
||||||
|
vals::Nfcidsize::NFCID1DOUBLE
|
||||||
|
}
|
||||||
|
NfcId::TripleSize(bytes) => {
|
||||||
|
let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap();
|
||||||
|
r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk));
|
||||||
|
|
||||||
|
let (bytes, chunk2) = bytes.split_last_chunk::<3>().unwrap();
|
||||||
|
let mut chunk = [0u8; 4];
|
||||||
|
chunk[1..].copy_from_slice(chunk2);
|
||||||
|
r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk));
|
||||||
|
|
||||||
|
let mut chunk = [0u8; 4];
|
||||||
|
chunk[1..].copy_from_slice(bytes);
|
||||||
|
r.nfcid1_3rd_last().write(|w| w.0 = u32::from_be_bytes(chunk));
|
||||||
|
|
||||||
|
vals::Nfcidsize::NFCID1TRIPLE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
r.sensres().write(|w| {
|
||||||
|
w.set_nfcidsize(nfcid_size);
|
||||||
|
w.set_bitframesdd(config.sdd_pat);
|
||||||
|
w.set_platfconfig(config.plat_conf & 0xF);
|
||||||
|
});
|
||||||
|
|
||||||
|
r.selres().write(|w| {
|
||||||
|
w.set_protocol(config.protocol as u8);
|
||||||
|
});
|
||||||
|
|
||||||
|
// errata
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
unsafe {
|
||||||
|
// Errata 57 nrf52832 only
|
||||||
|
//(0x40005610 as *mut u32).write_volatile(0x00000005);
|
||||||
|
//(0x40005688 as *mut u32).write_volatile(0x00000001);
|
||||||
|
//(0x40005618 as *mut u32).write_volatile(0x00000000);
|
||||||
|
//(0x40005614 as *mut u32).write_volatile(0x0000003F);
|
||||||
|
|
||||||
|
// Errata 98
|
||||||
|
(0x4000568C as *mut u32).write_volatile(0x00038148);
|
||||||
|
}
|
||||||
|
|
||||||
|
r.inten().write(|w| w.0 = 0);
|
||||||
|
|
||||||
|
interrupt::NFCT.unpend();
|
||||||
|
unsafe { interrupt::NFCT.enable() };
|
||||||
|
|
||||||
|
// clear all shorts
|
||||||
|
r.shorts().write(|_| {});
|
||||||
|
|
||||||
|
let res = Self {
|
||||||
|
_p,
|
||||||
|
tx_buf: [0u8; 256],
|
||||||
|
rx_buf: [0u8; 256],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(slice_in_ram(&res.tx_buf), "TX Buf not in ram");
|
||||||
|
assert!(slice_in_ram(&res.rx_buf), "RX Buf not in ram");
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for field on and select.
|
||||||
|
///
|
||||||
|
/// This waits for the field to become on, and then for a reader to select us. The ISO14443-3
|
||||||
|
/// sense, anticollision and select procedure is handled entirely in hardware.
|
||||||
|
///
|
||||||
|
/// When this returns, we have successfully been selected as a card. You must then
|
||||||
|
/// loop calling [`receive`](Self::receive) and responding with [`transmit`](Self::transmit).
|
||||||
|
pub async fn activate(&mut self) {
|
||||||
|
let r = pac::NFCT;
|
||||||
|
loop {
|
||||||
|
r.events_fieldlost().write_value(0);
|
||||||
|
r.events_fielddetected().write_value(0);
|
||||||
|
r.tasks_sense().write_value(1);
|
||||||
|
|
||||||
|
// enable autocoll
|
||||||
|
#[cfg(not(feature = "nrf52832"))]
|
||||||
|
r.autocolresconfig().write(|w| w.0 = 0b10);
|
||||||
|
|
||||||
|
r.framedelaymode().write(|w| {
|
||||||
|
w.set_framedelaymode(vals::Framedelaymode::WINDOW_GRID);
|
||||||
|
});
|
||||||
|
|
||||||
|
info!("waiting for field");
|
||||||
|
poll_fn(|cx| {
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
if r.events_fielddetected().read() != 0 {
|
||||||
|
r.events_fielddetected().write_value(0);
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
r.inten().write(|w| {
|
||||||
|
w.set_fielddetected(true);
|
||||||
|
});
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
embassy_time::Timer::after_millis(1).await; // workaround errata 190
|
||||||
|
|
||||||
|
r.events_selected().write_value(0);
|
||||||
|
r.tasks_activate().write_value(1);
|
||||||
|
|
||||||
|
trace!("Waiting to be selected");
|
||||||
|
poll_fn(|cx| {
|
||||||
|
let r = pac::NFCT;
|
||||||
|
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
if r.events_selected().read() != 0 || r.events_fieldlost().read() != 0 {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
r.inten().write(|w| {
|
||||||
|
w.set_selected(true);
|
||||||
|
w.set_fieldlost(true);
|
||||||
|
});
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
if r.events_fieldlost().read() != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add support for "window" frame delay, which is technically
|
||||||
|
// needed to be compliant with iso14443-4
|
||||||
|
r.framedelaymode().write(|w| {
|
||||||
|
w.set_framedelaymode(vals::Framedelaymode::FREE_RUN);
|
||||||
|
});
|
||||||
|
|
||||||
|
// disable autocoll
|
||||||
|
#[cfg(not(feature = "nrf52832"))]
|
||||||
|
r.autocolresconfig().write(|w| w.0 = 0b11u32);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transmit an ISO14443-3 frame to the reader.
|
||||||
|
///
|
||||||
|
/// You must call this only after receiving a frame with [`receive`](Self::receive),
|
||||||
|
/// and only once. Higher-layer protocols usually define timeouts, so calling this
|
||||||
|
/// too late can cause things to fail.
|
||||||
|
///
|
||||||
|
/// This will fail with [`Error::Deactivated`] if we have been deselected due to either
|
||||||
|
/// the field being switched off or due to the ISO14443 state machine. When this happens,
|
||||||
|
/// you must stop calling [`receive`](Self::receive) and [`transmit`](Self::transmit), reset
|
||||||
|
/// all protocol state, and go back to calling [`activate`](Self::activate).
|
||||||
|
pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), Error> {
|
||||||
|
let r = pac::NFCT;
|
||||||
|
|
||||||
|
//Setup DMA
|
||||||
|
self.tx_buf[..buf.len()].copy_from_slice(buf);
|
||||||
|
r.packetptr().write_value(self.tx_buf.as_ptr() as u32);
|
||||||
|
r.maxlen().write(|w| w.0 = buf.len() as _);
|
||||||
|
|
||||||
|
// Set packet length
|
||||||
|
r.txd().amount().write(|w| {
|
||||||
|
w.set_txdatabits(0);
|
||||||
|
w.set_txdatabytes(buf.len() as _);
|
||||||
|
});
|
||||||
|
|
||||||
|
r.txd().frameconfig().write(|w| {
|
||||||
|
w.set_crcmodetx(true);
|
||||||
|
w.set_discardmode(DiscardMode::DISCARD_END);
|
||||||
|
w.set_parity(true);
|
||||||
|
w.set_sof(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
r.events_error().write_value(0);
|
||||||
|
r.events_txframeend().write_value(0);
|
||||||
|
r.errorstatus().write(|w| w.0 = 0xffff_ffff);
|
||||||
|
|
||||||
|
// Start starttx task
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
r.tasks_starttx().write_value(1);
|
||||||
|
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
trace!("polling tx");
|
||||||
|
let r = pac::NFCT;
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
if r.events_fieldlost().read() != 0 {
|
||||||
|
return Poll::Ready(Err(Error::Deactivated));
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_txframeend().read() != 0 {
|
||||||
|
trace!("Txframend hit, should be finished trasmitting");
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_error().read() != 0 {
|
||||||
|
trace!("Got error?");
|
||||||
|
warn!("errors: {:08x}", r.errorstatus().read().0);
|
||||||
|
r.events_error().write_value(0);
|
||||||
|
return Poll::Ready(Err(Error::RxError));
|
||||||
|
}
|
||||||
|
|
||||||
|
r.inten().write(|w| {
|
||||||
|
w.set_txframeend(true);
|
||||||
|
w.set_error(true);
|
||||||
|
w.set_fieldlost(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive an ISO14443-3 frame from the reader.
|
||||||
|
///
|
||||||
|
/// After calling this, you must send back a response with [`transmit`](Self::transmit),
|
||||||
|
/// and only once. Higher-layer protocols usually define timeouts, so calling this
|
||||||
|
/// too late can cause things to fail.
|
||||||
|
pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
let r = pac::NFCT;
|
||||||
|
|
||||||
|
r.rxd().frameconfig().write(|w| {
|
||||||
|
w.set_crcmoderx(true);
|
||||||
|
w.set_parity(true);
|
||||||
|
w.set_sof(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Setup DMA
|
||||||
|
r.packetptr().write_value(self.rx_buf.as_mut_ptr() as u32);
|
||||||
|
r.maxlen().write(|w| w.0 = self.rx_buf.len() as _);
|
||||||
|
|
||||||
|
// Reset and enable the end event
|
||||||
|
r.events_rxframeend().write_value(0);
|
||||||
|
r.events_rxerror().write_value(0);
|
||||||
|
|
||||||
|
// Start enablerxdata only after configs are finished writing
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
r.tasks_enablerxdata().write_value(1);
|
||||||
|
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
trace!("polling rx");
|
||||||
|
let r = pac::NFCT;
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
if r.events_fieldlost().read() != 0 {
|
||||||
|
return Poll::Ready(Err(Error::Deactivated));
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_rxerror().read() != 0 {
|
||||||
|
trace!("RXerror got in recv frame, should be back in idle state");
|
||||||
|
r.events_rxerror().write_value(0);
|
||||||
|
warn!("errors: {:08x}", r.errorstatus().read().0);
|
||||||
|
return Poll::Ready(Err(Error::RxError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_rxframeend().read() != 0 {
|
||||||
|
trace!("RX Frameend got in recv frame, should have data");
|
||||||
|
r.events_rxframeend().write_value(0);
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
r.inten().write(|w| {
|
||||||
|
w.set_rxframeend(true);
|
||||||
|
w.set_rxerror(true);
|
||||||
|
w.set_fieldlost(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let n = r.rxd().amount().read().rxdatabytes() as usize - 2;
|
||||||
|
buf[..n].copy_from_slice(&self.rx_buf[..n]);
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
79
examples/nrf52840/src/bin/nfct.rs
Normal file
79
examples/nrf52840/src/bin/nfct.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_nrf::config::HfclkSource;
|
||||||
|
use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT};
|
||||||
|
use embassy_nrf::{bind_interrupts, nfct};
|
||||||
|
use {defmt_rtt as _, embassy_nrf as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
NFCT => nfct::InterruptHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let mut config = embassy_nrf::config::Config::default();
|
||||||
|
config.hfclk_source = HfclkSource::ExternalXtal;
|
||||||
|
let p = embassy_nrf::init(config);
|
||||||
|
|
||||||
|
dbg!("Setting up...");
|
||||||
|
let config = NfcConfig {
|
||||||
|
nfcid1: NfcId::DoubleSize([0x04, 0x68, 0x95, 0x71, 0xFA, 0x5C, 0x64]),
|
||||||
|
sdd_pat: nfct::SddPat::SDD00100,
|
||||||
|
plat_conf: 0b0000,
|
||||||
|
protocol: nfct::SelResProtocol::Type4A,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut nfc = NfcT::new(p.NFCT, Irqs, &config);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("activating");
|
||||||
|
nfc.activate().await;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("rxing");
|
||||||
|
let n = match nfc.receive(&mut buf).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
error!("rx error {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let req = &buf[..n];
|
||||||
|
info!("received frame {:02x}", req);
|
||||||
|
|
||||||
|
let mut deselect = false;
|
||||||
|
let resp = match req {
|
||||||
|
[0xe0, ..] => {
|
||||||
|
info!("Got RATS, tx'ing ATS");
|
||||||
|
&[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..]
|
||||||
|
}
|
||||||
|
[0xc2] => {
|
||||||
|
info!("Got deselect!");
|
||||||
|
deselect = true;
|
||||||
|
&[0xc2]
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
info!("Got unknown command!");
|
||||||
|
&[0xFF]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match nfc.transmit(resp).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!("tx error {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deselect {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user