use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_can::{ErrorKind, Id, StandardId};
use pac::peripherals::fdcan::{FDCAN, FDCAN_IR, FDCAN_RXF0A, FDCAN_TXBAR, FDCAN_TXBCR};
use crate::pac;
const RX_FIFO0_ELEMS: usize = 8;
const ELEM_WORDS: usize = 4;
const ELEM_BYTES: usize = ELEM_WORDS * 4;
const RX_FIFO0_LOCAL: usize = 0;
const TX_BUF_LOCAL: usize = RX_FIFO0_ELEMS * ELEM_BYTES;
pub const REGION_BYTES: usize = TX_BUF_LOCAL + ELEM_BYTES;
const DATA_SIZE_8B: u8 = 0;
mod elem {
pub const RTR: u32 = 1 << 29;
pub const XTD: u32 = 1 << 30;
pub const DLC_SHIFT: u32 = 16;
pub const DLC_MASK: u32 = 0xF;
}
const STD_ID_SHIFT: u32 = 18;
const EXT_ID_MASK: u32 = 0x1FFF_FFFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
BusOff,
Overrun,
}
impl embedded_can::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::BusOff => ErrorKind::Other,
Error::Overrun => ErrorKind::Overrun,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Frame {
id: Id,
remote: bool,
dlc: u8,
data: [u8; 8],
}
impl embedded_can::Frame for Frame {
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
if data.len() > 8 {
return None;
}
let mut buf = [0u8; 8];
buf[..data.len()].copy_from_slice(data);
Some(Self {
id: id.into(),
remote: false,
dlc: data.len() as u8,
data: buf,
})
}
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self> {
if dlc > 8 {
return None;
}
Some(Self {
id: id.into(),
remote: true,
dlc: dlc as u8,
data: [0u8; 8],
})
}
fn is_extended(&self) -> bool {
matches!(self.id, Id::Extended(_))
}
fn is_remote_frame(&self) -> bool {
self.remote
}
fn id(&self) -> Id {
self.id
}
fn dlc(&self) -> usize {
self.dlc as usize
}
fn data(&self) -> &[u8] {
&self.data[..self.dlc as usize]
}
}
const fn nbrp(clock_hz: u32, bitrate: u32) -> u16 {
assert!(bitrate > 0);
let div = clock_hz / (bitrate * 16);
if div < 1 {
0
} else {
let d = div - 1;
if d > 511 { 511 } else { d as u16 }
}
}
pub struct State {
tx_waker: AtomicWaker,
rx_waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
tx_waker: AtomicWaker::new(),
rx_waker: AtomicWaker::new(),
}
}
fn on_interrupt(&self, r: FDCAN) {
let ir = r.IR().read();
let ie = r.IE().read();
let (mut tx, mut rx) = (false, false);
r.IE().modify(|w| {
if ie.TCE() && ir.TC() {
w.set_TCE(false);
tx = true;
}
if ie.RF0NE() && ir.RF0N() {
w.set_RF0NE(false);
rx = true;
}
if ie.BOE() && ir.BO() {
w.set_BOE(false);
tx = true;
rx = true;
}
});
if tx {
self.tx_waker.wake();
}
if rx {
self.rx_waker.wake();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub bitrate: u32,
pub loopback: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
bitrate: 500_000,
loopback: false,
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> FDCAN;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
}
macro_rules! impl_can {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::can::SealedInstance for crate::peripherals::$instance {
#[inline]
fn regs() -> crate::pac::peripherals::fdcan::FDCAN {
unsafe { crate::pac::peripherals::fdcan::FDCAN::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::can::State {
static STATE: crate::can::State = crate::can::State::new();
&STATE
}
}
impl crate::can::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
}
};
}
pub(crate) use impl_can;
pub fn on_interrupt<T: Instance>() {
T::state().on_interrupt(T::regs());
}
pub struct CanTx {
regs: FDCAN,
state: &'static State,
msg_ram: *mut u8,
ram_offset: usize,
}
pub struct CanRx {
regs: FDCAN,
state: &'static State,
msg_ram: *mut u8,
ram_offset: usize,
}
pub struct Can {
tx: CanTx,
rx: CanRx,
}
impl Can {
pub unsafe fn new<T: Instance>(
peri: Peri<'static, T>,
msg_ram_base: *mut (),
msg_ram_offset: usize,
clock_hz: u32,
config: Config,
) -> Self {
let _ = peri;
let r = T::regs();
let this = Self {
tx: CanTx {
regs: r,
state: T::state(),
msg_ram: msg_ram_base as *mut u8,
ram_offset: msg_ram_offset,
},
rx: CanRx {
regs: r,
state: T::state(),
msg_ram: msg_ram_base as *mut u8,
ram_offset: msg_ram_offset,
},
};
r.CCCR().modify(|w| w.set_INIT(true));
while !r.CCCR().read().INIT() {}
r.CCCR().modify(|w| {
w.set_CCE(true);
w.set_FDOE(false);
w.set_BRSE(false);
w.set_TEST(config.loopback);
w.set_MON(config.loopback);
});
if config.loopback {
r.TEST().modify(|w| w.set_LBCK(true));
}
r.NBTP().write(|w| {
w.set_NBRP(nbrp(clock_hz, config.bitrate));
w.set_NTSEG1(11);
w.set_NTSEG2(2);
w.set_NSJW(2);
});
r.SIDFC().modify(|w| w.set_LSS(0));
r.XIDFC().modify(|w| w.set_LSE(0));
r.GFC().write(|w| {
w.set_ANFS(0);
w.set_ANFE(0);
w.set_RRFS(false);
w.set_RRFE(false);
});
r.RXESC().modify(|w| w.set_F0DS(DATA_SIZE_8B));
r.TXESC().modify(|w| w.set_TBDS(DATA_SIZE_8B));
r.RXF0C().write(|w| {
w.set_F0SA(((msg_ram_offset + RX_FIFO0_LOCAL) >> 2) as u16);
w.set_F0S(RX_FIFO0_ELEMS as u8);
w.set_F0WM(0);
});
r.TXBC().write(|w| {
w.set_TBSA(((msg_ram_offset + TX_BUF_LOCAL) >> 2) as u16);
w.set_NDTB(1);
w.set_TFQS(0);
});
r.ILE().write(|w| w.set_EINT0(true));
r.CCCR().modify(|w| {
w.set_CCE(false);
w.set_INIT(false);
});
while r.CCCR().read().INIT() {}
this
}
pub fn split(self) -> (CanTx, CanRx) {
(self.tx, self.rx)
}
pub async fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
self.tx.transmit(frame).await
}
pub async fn receive(&mut self) -> Result<Frame, Error> {
self.rx.receive().await
}
}
impl CanTx {
fn tx_busy(&self) -> bool {
self.regs.TXBRP().read().0 & 1 != 0
}
fn load_tx(&mut self, frame: &Frame) {
let (xtd, id_word) = encode_id(frame.id);
let mut t0 = id_word;
if xtd {
t0 |= elem::XTD;
}
if frame.remote {
t0 |= elem::RTR;
}
let t1 = (u32::from(frame.dlc) & elem::DLC_MASK) << elem::DLC_SHIFT;
let w0 = u32::from_le_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
let w1 = u32::from_le_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
let p = ram_word(self.msg_ram, self.ram_offset, TX_BUF_LOCAL);
unsafe { p.write_volatile(t0) };
unsafe { p.wrapping_add(1).write_volatile(t1) };
unsafe { p.wrapping_add(2).write_volatile(w0) };
unsafe { p.wrapping_add(3).write_volatile(w1) };
self.regs.TXBAR().write_value(FDCAN_TXBAR(1));
}
pub async fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
check_bus(self.regs)?;
wait_flag(
&self.state.tx_waker,
self.regs,
|r| r.IE().modify(|w| w.set_TCE(true)),
|| (!self.tx_busy()).then_some(Ok(())),
)
.await?;
self.load_tx(frame);
let guard = TxCancelGuard {
regs: self.regs,
armed: true,
};
wait_flag(
&self.state.tx_waker,
self.regs,
|r| r.IE().modify(|w| w.set_TCE(true)),
|| (!self.tx_busy()).then_some(Ok(())),
)
.await?;
guard.defuse();
self.regs.IR().write_value(ir_tc());
Ok(())
}
}
struct TxCancelGuard {
regs: FDCAN,
armed: bool,
}
impl TxCancelGuard {
fn defuse(mut self) {
self.armed = false;
}
}
impl Drop for TxCancelGuard {
fn drop(&mut self) {
if self.armed {
self.regs.TXBCR().write_value(FDCAN_TXBCR(1));
}
}
}
impl CanRx {
fn rx_pending(&self) -> bool {
self.regs.RXF0S().read().F0FL() > 0
}
fn drain_rx(&mut self) -> Result<Frame, Error> {
let status = self.regs.RXF0S().read();
let overrun = status.RF0L();
let gi = status.F0GI() as usize;
let p = ram_word(
self.msg_ram,
self.ram_offset,
RX_FIFO0_LOCAL + gi * ELEM_BYTES,
);
let r0 = unsafe { p.read_volatile() };
let r1 = unsafe { p.wrapping_add(1).read_volatile() };
let w0 = unsafe { p.wrapping_add(2).read_volatile() };
let w1 = unsafe { p.wrapping_add(3).read_volatile() };
self.regs.RXF0A().write_value(FDCAN_RXF0A(gi as u32 & 0x3F));
if overrun {
return Err(Error::Overrun);
}
let extended = r0 & elem::XTD != 0;
let remote = r0 & elem::RTR != 0;
let dlc = ((r1 >> elem::DLC_SHIFT) & elem::DLC_MASK).min(8) as u8;
let mut data = [0u8; 8];
data[..4].copy_from_slice(&w0.to_le_bytes());
data[4..].copy_from_slice(&w1.to_le_bytes());
Ok(Frame {
id: decode_id(r0, extended),
remote,
dlc,
data,
})
}
pub async fn receive(&mut self) -> Result<Frame, Error> {
wait_flag(
&self.state.rx_waker,
self.regs,
|r| r.IE().modify(|w| w.set_RF0NE(true)),
|| self.rx_pending().then_some(Ok(())),
)
.await?;
let frame = self.drain_rx();
self.regs.IR().write_value(ir_rf0n());
frame
}
}
fn ram_word(msg_ram: *mut u8, ram_offset: usize, byte_off: usize) -> *mut u32 {
msg_ram.wrapping_add(ram_offset + byte_off) as *mut u32
}
fn check_bus(regs: FDCAN) -> Result<(), Error> {
if regs.PSR().read().BO() {
Err(Error::BusOff)
} else {
Ok(())
}
}
async fn wait_flag(
waker: &AtomicWaker,
regs: FDCAN,
arm: fn(&FDCAN),
ready: impl Fn() -> Option<Result<(), Error>>,
) -> Result<(), Error> {
poll_fn(|cx| {
if let Some(res) = ready() {
return Poll::Ready(res);
}
waker.register(cx.waker());
critical_section::with(|_| arm(®s));
if let Some(res) = ready() {
return Poll::Ready(res);
}
Poll::Pending
})
.await
}
unsafe impl Send for CanTx {}
unsafe impl Send for CanRx {}
unsafe impl Send for Can {}
fn ir_tc() -> FDCAN_IR {
let mut v = FDCAN_IR(0);
v.set_TC(true);
v
}
fn ir_rf0n() -> FDCAN_IR {
let mut v = FDCAN_IR(0);
v.set_RF0N(true);
v
}
fn encode_id(id: Id) -> (bool, u32) {
match id {
Id::Standard(s) => (false, u32::from(s.as_raw()) << STD_ID_SHIFT),
Id::Extended(e) => (true, e.as_raw() & EXT_ID_MASK),
}
}
fn decode_id(r0: u32, extended: bool) -> Id {
if extended {
embedded_can::ExtendedId::new(r0 & EXT_ID_MASK)
.map(Id::Extended)
.unwrap_or(Id::Standard(StandardId::ZERO))
} else {
StandardId::new(((r0 >> STD_ID_SHIFT) & 0x7FF) as u16)
.map(Id::Standard)
.unwrap_or(Id::Standard(StandardId::ZERO))
}
}
impl embedded_can::nb::Can for Can {
type Frame = Frame;
type Error = Error;
fn transmit(&mut self, frame: &Frame) -> nb::Result<Option<Frame>, Error> {
check_bus(self.tx.regs)?;
if self.tx.tx_busy() {
return Err(nb::Error::WouldBlock);
}
self.tx.load_tx(frame);
Ok(None)
}
fn receive(&mut self) -> nb::Result<Frame, Error> {
if !self.rx.rx_pending() {
return Err(nb::Error::WouldBlock);
}
let frame = self.rx.drain_rx().map_err(nb::Error::Other)?;
self.rx.regs.IR().write_value(ir_rf0n());
Ok(frame)
}
}
#[cfg(test)]
mod tests {
use super::{decode_id, encode_id, nbrp};
use embedded_can::{ExtendedId, Id, StandardId};
#[test]
fn nbrp_matches_bit_rate() {
assert_eq!(nbrp(80_000_000, 500_000), 9);
}
#[test]
fn standard_id_round_trips() {
let id = Id::Standard(StandardId::new(0x2AB).unwrap());
let (ext, word) = encode_id(id);
assert!(!ext);
assert_eq!(decode_id(word, ext), id);
}
#[test]
fn extended_id_round_trips() {
let id = Id::Extended(ExtendedId::new(0x0C0F_FEE1).unwrap());
let (ext, word) = encode_id(id);
assert!(ext);
assert_eq!(decode_id(word, ext), id);
}
}