#[cfg(feature = "eh1")]
use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId};
#[cfg(not(feature = "eh1"))]
use embedded_hal::can::{Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId};
use fugit::HertzU32;
use self::filter::{Filter, FilterType};
use crate::{
clock::Clocks,
gpio::{InputPin, InputSignal, OutputPin, OutputSignal},
peripheral::{Peripheral, PeripheralRef},
peripherals::twai0::RegisterBlock,
system::{self, PeripheralClockControl},
};
pub mod filter;
#[derive(Clone, Copy, Debug)]
pub struct EspTwaiFrame {
id: Id,
dlc: usize,
data: [u8; 8],
is_remote: bool,
}
impl EspTwaiFrame {
unsafe fn new_from_data_registers(
id: impl Into<Id>,
registers: *const u32,
dlc: usize,
) -> Self {
let mut data: [u8; 8] = [0; 8];
copy_from_data_register(&mut data[..dlc], registers);
Self {
id: id.into(),
data,
dlc,
is_remote: false,
}
}
}
impl Frame for EspTwaiFrame {
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
if data.len() > 8 {
return None;
}
let mut d: [u8; 8] = [0; 8];
let (left, _unused) = d.split_at_mut(data.len());
left.clone_from_slice(data);
Some(EspTwaiFrame {
id: id.into(),
data: d,
dlc: data.len(),
is_remote: false,
})
}
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self> {
if dlc > 8 {
return None;
}
Some(EspTwaiFrame {
id: id.into(),
data: [0; 8],
dlc,
is_remote: true,
})
}
fn is_extended(&self) -> bool {
match self.id {
Id::Standard(_) => false,
Id::Extended(_) => true,
}
}
fn is_remote_frame(&self) -> bool {
self.is_remote
}
fn id(&self) -> Id {
self.id
}
fn dlc(&self) -> usize {
self.dlc
}
fn data(&self) -> &[u8] {
match self.is_remote_frame() {
true => &[],
false => &self.data[0..self.dlc],
}
}
}
pub struct TimingConfig {
pub baud_rate_prescaler: u16,
pub sync_jump_width: u8,
pub tseg_1: u8,
pub tseg_2: u8,
pub triple_sample: bool,
}
pub enum BaudRate {
B125K,
B250K,
B500K,
B1000K,
Custom(TimingConfig),
}
impl BaudRate {
const fn timing(self) -> TimingConfig {
match self {
Self::B125K => TimingConfig {
baud_rate_prescaler: 32,
sync_jump_width: 3,
tseg_1: 15,
tseg_2: 4,
triple_sample: false,
},
Self::B250K => TimingConfig {
baud_rate_prescaler: 16,
sync_jump_width: 3,
tseg_1: 15,
tseg_2: 4,
triple_sample: false,
},
Self::B500K => TimingConfig {
baud_rate_prescaler: 8,
sync_jump_width: 3,
tseg_1: 15,
tseg_2: 4,
triple_sample: false,
},
Self::B1000K => TimingConfig {
baud_rate_prescaler: 4,
sync_jump_width: 3,
tseg_1: 15,
tseg_2: 4,
triple_sample: false,
},
Self::Custom(timing_config) => timing_config,
}
}
}
pub struct TwaiConfiguration<'d, T> {
peripheral: PeripheralRef<'d, T>,
}
impl<'d, T> TwaiConfiguration<'d, T>
where
T: Instance,
{
pub fn new<TX: OutputPin, RX: InputPin>(
peripheral: impl Peripheral<P = T> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
clocks: &Clocks,
baud_rate: BaudRate,
) -> Self {
PeripheralClockControl::enable(T::SYSTEM_PERIPHERAL);
crate::into_ref!(tx_pin, rx_pin);
tx_pin.connect_peripheral_to_output(T::OUTPUT_SIGNAL);
rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL);
crate::into_ref!(peripheral);
let mut cfg = TwaiConfiguration { peripheral };
cfg.set_baud_rate(baud_rate, clocks);
cfg
}
fn set_baud_rate(&mut self, baud_rate: BaudRate, clocks: &Clocks) {
assert!(clocks.apb_clock == HertzU32::MHz(80));
let timing = baud_rate.timing();
let prescale = (timing.baud_rate_prescaler / 2) - 1;
let sjw = timing.sync_jump_width - 1;
let tseg_1 = timing.tseg_1 - 1;
let tseg_2 = timing.tseg_2 - 1;
let triple_sample = timing.triple_sample;
#[cfg(esp32)]
let prescale = prescale as u8;
self.peripheral
.register_block()
.bus_timing_0()
.modify(|_, w| {
w.baud_presc()
.variant(prescale)
.sync_jump_width()
.variant(sjw)
});
self.peripheral
.register_block()
.bus_timing_1()
.modify(|_, w| {
w.time_seg1()
.variant(tseg_1)
.time_seg2()
.variant(tseg_2)
.time_samp()
.bit(triple_sample)
});
}
pub fn set_filter(&mut self, filter: impl Filter) {
let filter_mode_bit = filter.filter_type() == FilterType::Single;
self.peripheral
.register_block()
.mode()
.modify(|_, w| w.rx_filter_mode().bit(filter_mode_bit));
let registers = filter.to_registers();
unsafe {
copy_to_data_register(
self.peripheral.register_block().data_0().as_ptr(),
®isters,
);
}
}
pub fn set_error_warning_limit(&mut self, limit: u8) {
self.peripheral
.register_block()
.err_warning_limit()
.write(|w| w.err_warning_limit().variant(limit));
}
pub fn start(self) -> Twai<'d, T> {
self.peripheral
.register_block()
.mode()
.modify(|_, w| w.reset_mode().clear_bit());
Twai {
peripheral: self.peripheral,
}
}
}
pub struct Twai<'d, T> {
peripheral: PeripheralRef<'d, T>,
}
impl<'d, T> Twai<'d, T>
where
T: Instance,
{
pub fn stop(self) -> TwaiConfiguration<'d, T> {
self.peripheral
.register_block()
.mode()
.modify(|_, w| w.reset_mode().set_bit());
TwaiConfiguration {
peripheral: self.peripheral,
}
}
pub fn receive_error_count(&self) -> u8 {
self.peripheral
.register_block()
.rx_err_cnt()
.read()
.rx_err_cnt()
.bits()
}
pub fn transmit_error_count(&self) -> u8 {
self.peripheral
.register_block()
.tx_err_cnt()
.read()
.tx_err_cnt()
.bits()
}
pub fn is_bus_off(&self) -> bool {
self.peripheral
.register_block()
.status()
.read()
.bus_off_st()
.bit_is_set()
}
pub fn num_available_messages(&self) -> u8 {
self.peripheral
.register_block()
.rx_message_cnt()
.read()
.rx_message_counter()
.bits()
}
pub fn clear_receive_fifo(&self) {
while self.num_available_messages() > 0 {
self.release_receive_fifo();
}
}
fn release_receive_fifo(&self) {
self.peripheral
.register_block()
.cmd()
.write(|w| w.release_buf().set_bit());
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum EspTwaiError {
BusOff,
EmbeddedHAL(ErrorKind),
}
impl Error for EspTwaiError {
fn kind(&self) -> ErrorKind {
match self {
Self::BusOff => ErrorKind::Other,
Self::EmbeddedHAL(kind) => *kind,
}
}
}
#[inline(always)]
unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) {
for (i, dest) in dest.iter_mut().enumerate() {
*dest = src.add(i).read_volatile() as u8;
}
}
#[inline(always)]
unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) {
for (i, src) in src.iter().enumerate() {
dest.add(i).write_volatile(*src as u32);
}
}
impl<T> Can for Twai<'_, T>
where
T: Instance,
{
type Frame = EspTwaiFrame;
type Error = EspTwaiError;
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error> {
let status = self.peripheral.register_block().status().read();
if status.bus_off_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff));
}
if !status.tx_buf_st().bit_is_set() {
return nb::Result::Err(nb::Error::WouldBlock);
}
let frame_format: u8 = frame.is_extended() as u8;
let rtr_bit: u8 = frame.is_remote_frame() as u8;
let dlc_bits: u8 = frame.dlc() as u8 & 0b1111;
let data_0: u8 = frame_format << 7 | rtr_bit << 6 | dlc_bits;
self.peripheral
.register_block()
.data_0()
.write(|w| w.tx_byte_0().variant(data_0));
match frame.id() {
Id::Standard(id) => {
let id = id.as_raw();
self.peripheral
.register_block()
.data_1()
.write(|w| w.tx_byte_1().variant((id >> 3) as u8));
self.peripheral
.register_block()
.data_2()
.write(|w| w.tx_byte_2().variant((id << 5) as u8));
}
Id::Extended(id) => {
let id = id.as_raw();
self.peripheral
.register_block()
.data_1()
.write(|w| w.tx_byte_1().variant((id >> 21) as u8));
self.peripheral
.register_block()
.data_2()
.write(|w| w.tx_byte_2().variant((id >> 13) as u8));
self.peripheral
.register_block()
.data_3()
.write(|w| w.tx_byte_3().variant((id >> 5) as u8));
self.peripheral
.register_block()
.data_4()
.write(|w| w.tx_byte_4().variant((id << 3) as u8));
}
}
if frame.is_data_frame() {
match frame.id() {
Id::Standard(_) => unsafe {
copy_to_data_register(
self.peripheral.register_block().data_3().as_ptr(),
frame.data(),
)
},
Id::Extended(_) => unsafe {
copy_to_data_register(
self.peripheral.register_block().data_5().as_ptr(),
frame.data(),
)
},
}
} else {
}
self.peripheral
.register_block()
.cmd()
.write(|w| w.tx_req().set_bit());
nb::Result::Ok(None)
}
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error> {
let status = self.peripheral.register_block().status().read();
if status.bus_off_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff));
}
if !status.rx_buf_st().bit_is_set() {
return nb::Result::Err(nb::Error::WouldBlock);
}
if status.miss_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL(
ErrorKind::Overrun,
)));
}
let data_0 = self
.peripheral
.register_block()
.data_0()
.read()
.tx_byte_0()
.bits();
let is_standard_format = data_0 & 0b1 << 7 == 0;
let is_data_frame = data_0 & 0b1 << 6 == 0;
let dlc = (data_0 & 0b1111) as usize;
let frame = if is_standard_format {
let data_1 = self
.peripheral
.register_block()
.data_1()
.read()
.tx_byte_1()
.bits();
let data_2 = self
.peripheral
.register_block()
.data_2()
.read()
.tx_byte_2()
.bits();
let raw_id: u16 = ((data_1 as u16) << 3) | ((data_2 as u16) >> 5);
let id = StandardId::new(raw_id).unwrap();
if is_data_frame {
unsafe {
EspTwaiFrame::new_from_data_registers(
id,
self.peripheral.register_block().data_3().as_ptr(),
dlc,
)
}
} else {
EspTwaiFrame::new_remote(id, dlc).unwrap()
}
} else {
let data_1 = self
.peripheral
.register_block()
.data_1()
.read()
.tx_byte_1()
.bits();
let data_2 = self
.peripheral
.register_block()
.data_2()
.read()
.tx_byte_2()
.bits();
let data_3 = self
.peripheral
.register_block()
.data_3()
.read()
.tx_byte_3()
.bits();
let data_4 = self
.peripheral
.register_block()
.data_4()
.read()
.tx_byte_4()
.bits();
let raw_id: u32 = (data_1 as u32) << 21
| (data_2 as u32) << 13
| (data_3 as u32) << 5
| (data_4 as u32) >> 3;
let id = ExtendedId::new(raw_id).unwrap();
if is_data_frame {
unsafe {
EspTwaiFrame::new_from_data_registers(
id,
self.peripheral.register_block().data_5().as_ptr(),
dlc,
)
}
} else {
EspTwaiFrame::new_remote(id, dlc).unwrap()
}
};
self.release_receive_fifo();
nb::Result::Ok(frame)
}
}
pub trait Instance {
const SYSTEM_PERIPHERAL: system::Peripheral;
const INPUT_SIGNAL: InputSignal;
const OUTPUT_SIGNAL: OutputSignal;
fn register_block(&self) -> &RegisterBlock;
}
#[cfg(any(esp32c3, esp32s3))]
impl Instance for crate::peripherals::TWAI0 {
const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0;
const INPUT_SIGNAL: InputSignal = InputSignal::TWAI_RX;
const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI_TX;
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
}
#[cfg(esp32c6)]
impl Instance for crate::peripherals::TWAI0 {
const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0;
const INPUT_SIGNAL: InputSignal = InputSignal::TWAI0_RX;
const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI0_TX;
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
}
#[cfg(esp32c6)]
impl Instance for crate::peripherals::TWAI1 {
const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai1;
const INPUT_SIGNAL: InputSignal = InputSignal::TWAI1_RX;
const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI1_TX;
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
}