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::can::{CAN, CAN_IFLAG1, CAN_IMASK1};
use crate::pac;
const TX_MB: usize = 0;
const RX_MB: usize = 1;
mod code {
pub const RX_INACTIVE: u32 = 0b0000;
pub const RX_EMPTY: u32 = 0b0100;
pub const RX_OVERRUN: u32 = 0b0110;
pub const TX_INACTIVE: u32 = 0b1000;
pub const TX_DATA: u32 = 0b1100;
}
mod cs {
pub const CODE_SHIFT: u32 = 24;
pub const SRR: u32 = 1 << 22;
pub const IDE: u32 = 1 << 21;
pub const RTR: u32 = 1 << 20;
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 presdiv(clock_hz: u32, bitrate: u32) -> u8 {
assert!(bitrate > 0);
let div = clock_hz / (bitrate * 16);
if div < 1 {
0
} else {
let d = div - 1;
if d > 255 { 255 } else { d as u8 }
}
}
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: CAN) {
let flag = r.IFLAG1().read().0;
let mask = r.IMASK1().read().BUF31TO0M();
let pending = flag & mask;
if pending != 0 {
let mut next = CAN_IMASK1(0);
next.set_BUF31TO0M(mask & !pending);
r.IMASK1().write_value(next);
if pending & (1 << TX_MB) != 0 {
self.tx_waker.wake();
}
if pending & (1 << RX_MB) != 0 {
self.rx_waker.wake();
}
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> CAN;
fn state() -> &'static State;
const LPCG: Option<u32>;
}
#[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 {
const LPCG: ::core::option::Option<u32> = crate::clock::lpcg(stringify!($instance));
#[inline]
fn regs() -> crate::pac::peripherals::can::CAN {
unsafe { crate::pac::peripherals::can::CAN::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());
}
#[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 struct CanTx {
regs: CAN,
state: &'static State,
}
pub struct CanRx {
regs: CAN,
state: &'static State,
}
pub struct Can {
tx: CanTx,
rx: CanRx,
}
impl Can {
pub fn new<T: Instance>(peri: Peri<'static, T>, clock_hz: u32, config: Config) -> Self {
let _ = peri;
crate::clock::ungate(T::LPCG);
let r = T::regs();
let this = Self {
tx: CanTx {
regs: r,
state: T::state(),
},
rx: CanRx {
regs: r,
state: T::state(),
},
};
r.MCR().modify(|w| w.set_MDIS(false));
while r.MCR().read().LPMACK() {}
r.MCR().modify(|w| w.set_SOFTRST(true));
while r.MCR().read().SOFTRST() {}
r.MCR().modify(|w| {
w.set_FRZ(true);
w.set_HALT(true);
});
while !r.MCR().read().FRZACK() {}
r.CTRL1().modify(|w| {
w.set_PRESDIV(presdiv(clock_hz, config.bitrate));
w.set_PROPSEG(6);
w.set_PSEG1(3);
w.set_PSEG2(3);
w.set_RJW(3);
w.set_LPB(config.loopback);
});
r.MCR().modify(|w| {
w.set_MAXMB(RX_MB as u8);
w.set_SRXDIS(!config.loopback);
w.set_IRMQ(false);
});
r.RXMGMASK()
.write_value(pac::peripherals::can::CAN_RXMGMASK(0));
write_cs(r, TX_MB, code::TX_INACTIVE << cs::CODE_SHIFT);
write_cs(r, RX_MB, code::RX_INACTIVE << cs::CODE_SHIFT);
write_id(r, RX_MB, 0);
write_cs(r, RX_MB, code::RX_EMPTY << cs::CODE_SHIFT);
r.IFLAG1()
.write_value(CAN_IFLAG1((1 << TX_MB) | (1 << RX_MB)));
r.MCR().modify(|w| {
w.set_HALT(false);
w.set_FRZ(false);
});
while r.MCR().read().NOTRDY() {}
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_ready(&self) -> bool {
(self.regs.IFLAG1().read().0 & (1 << TX_MB)) != 0
|| (read_cs(self.regs, TX_MB) >> cs::CODE_SHIFT) & 0xF == code::TX_INACTIVE
}
fn load_tx(&mut self, frame: &Frame) {
let (ide, id_word) = encode_id(frame.id);
write_cs(self.regs, TX_MB, code::TX_INACTIVE << cs::CODE_SHIFT);
self.regs.IFLAG1().write_value(CAN_IFLAG1(1 << TX_MB));
write_id(self.regs, TX_MB, id_word);
write_data(self.regs, TX_MB, &frame.data);
let mut word = code::TX_DATA << cs::CODE_SHIFT;
word |= (u32::from(frame.dlc) & cs::DLC_MASK) << cs::DLC_SHIFT;
if ide {
word |= cs::IDE | cs::SRR;
}
if frame.remote {
word |= cs::RTR;
}
write_cs(self.regs, TX_MB, word);
}
pub async fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
check_bus(self.regs)?;
wait_flag(&self.state.tx_waker, self.regs, TX_MB, || {
self.tx_ready().then_some(Ok(()))
})
.await?;
self.load_tx(frame);
let guard = TxCancelGuard {
regs: self.regs,
armed: true,
};
wait_flag(&self.state.tx_waker, self.regs, TX_MB, || {
((self.regs.IFLAG1().read().0 & (1 << TX_MB)) != 0).then_some(Ok(()))
})
.await?;
guard.defuse();
self.regs.IFLAG1().write_value(CAN_IFLAG1(1 << TX_MB));
Ok(())
}
}
struct TxCancelGuard {
regs: CAN,
armed: bool,
}
impl TxCancelGuard {
fn defuse(mut self) {
self.armed = false;
}
}
impl Drop for TxCancelGuard {
fn drop(&mut self) {
if self.armed {
write_cs(self.regs, TX_MB, code::TX_INACTIVE << cs::CODE_SHIFT);
self.regs.IFLAG1().write_value(CAN_IFLAG1(1 << TX_MB));
}
}
}
impl CanRx {
fn drain_rx(&mut self) -> Result<Frame, Error> {
let cs = read_cs(self.regs, RX_MB);
let overrun = (cs >> cs::CODE_SHIFT) & 0xF == code::RX_OVERRUN;
let dlc = ((cs >> cs::DLC_SHIFT) & cs::DLC_MASK).min(8) as u8;
let ide = cs & cs::IDE != 0;
let remote = cs & cs::RTR != 0;
let id_word = read_id(self.regs, RX_MB);
let data = read_data(self.regs, RX_MB);
let _ = self.regs.TIMER().read();
self.regs.IFLAG1().write_value(CAN_IFLAG1(1 << RX_MB));
let id = decode_id(id_word, ide);
if overrun {
return Err(Error::Overrun);
}
Ok(Frame {
id,
remote,
dlc,
data,
})
}
fn rx_pending(&self) -> bool {
(self.regs.IFLAG1().read().0 & (1 << RX_MB)) != 0
}
pub async fn receive(&mut self) -> Result<Frame, Error> {
wait_flag(&self.state.rx_waker, self.regs, RX_MB, || {
self.rx_pending().then_some(Ok(()))
})
.await?;
self.drain_rx()
}
}
fn mb_ptr(regs: CAN, n: usize) -> *mut u32 {
regs.MB_8B_CS(n).as_ptr()
}
fn write_cs(regs: CAN, n: usize, value: u32) {
regs.MB_8B_CS(n).write_value(value);
}
fn read_cs(regs: CAN, n: usize) -> u32 {
regs.MB_8B_CS(n).read()
}
fn write_id(regs: CAN, n: usize, value: u32) {
let p = mb_ptr(regs, n).wrapping_add(1);
unsafe { p.write_volatile(value) };
}
fn read_id(regs: CAN, n: usize) -> u32 {
let p = mb_ptr(regs, n).wrapping_add(1);
unsafe { p.read_volatile() }
}
fn write_data(regs: CAN, n: usize, data: &[u8; 8]) {
let w0 = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
let w1 = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
let d0 = mb_ptr(regs, n).wrapping_add(2);
let d1 = mb_ptr(regs, n).wrapping_add(3);
unsafe { d0.write_volatile(w0) };
unsafe { d1.write_volatile(w1) };
}
fn read_data(regs: CAN, n: usize) -> [u8; 8] {
let d0 = mb_ptr(regs, n).wrapping_add(2);
let d1 = mb_ptr(regs, n).wrapping_add(3);
let w0 = unsafe { d0.read_volatile() };
let w1 = unsafe { d1.read_volatile() };
let mut out = [0u8; 8];
out[..4].copy_from_slice(&w0.to_be_bytes());
out[4..].copy_from_slice(&w1.to_be_bytes());
out
}
fn check_bus(regs: CAN) -> Result<(), Error> {
if regs.ESR1().read().FLTCONF() == 0b10 {
Err(Error::BusOff)
} else {
Ok(())
}
}
async fn wait_flag(
waker: &AtomicWaker,
regs: CAN,
mb: usize,
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(|_| {
regs.IMASK1().modify(|w| {
w.set_BUF31TO0M(w.BUF31TO0M() | (1 << mb));
});
});
if let Some(res) = ready() {
return Poll::Ready(res);
}
Poll::Pending
})
.await
}
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(id_word: u32, extended: bool) -> Id {
if extended {
embedded_can::ExtendedId::new(id_word & EXT_ID_MASK)
.map(Id::Extended)
.unwrap_or(Id::Standard(StandardId::ZERO))
} else {
StandardId::new(((id_word >> 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_ready() {
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);
}
self.rx.drain_rx().map_err(nb::Error::Other)
}
}
#[cfg(test)]
mod tests {
use super::{decode_id, encode_id, presdiv};
use embedded_can::{ExtendedId, Id, StandardId};
#[test]
fn presdiv_matches_bit_rate() {
assert_eq!(presdiv(40_000_000, 500_000), 4);
}
#[test]
fn standard_id_round_trips() {
let id = Id::Standard(StandardId::new(0x123).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(0x1AB_CDEF).unwrap());
let (ext, word) = encode_id(id);
assert!(ext);
assert_eq!(decode_id(word, ext), id);
}
}