This repository has been archived on 2026-08-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
donglora/vendor/esp-hal/src/twai/mod.rs
T
swaitsandClaude Opus 4.6 3102fc747a feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:54 -06:00

1970 lines
63 KiB
Rust

//! # Two-wire Automotive Interface (TWAI)
//!
//! ## Overview
//!
//! The TWAI is a multi-master, multi-cast communication protocol with error
//! detection and signaling and inbuilt message priorities and arbitration. The
//! TWAI protocol is suited for automotive and industrial applications.
//!
//! See ESP-IDF's
#![doc = concat!("[TWAI documentation](https://docs.espressif.com/projects/esp-idf/en/latest/", crate::soc::chip!(), "/api-reference/peripherals/twai.html#twai-protocol-summary)")]
//! for a summary on the protocol.
//!
//! ## Configuration
//! The driver offers functions for initializing the TWAI peripheral, setting
//! up the timing parameters, configuring acceptance filters, handling
//! interrupts, and transmitting/receiving messages on the TWAI bus.
//!
//! This driver manages the ISO 11898-1 compatible TWAI
//! controllers. It supports Standard Frame Format (11-bit) and Extended Frame
//! Format (29-bit) frame identifiers.
//!
//! ## Examples
//!
//! ### Transmitting and Receiving Messages
//!
//! ```rust, no_run
#![doc = crate::before_snippet!()]
//! # use embedded_can::Id;
//! # use esp_hal::twai;
//! # use esp_hal::twai::filter;
//! # use esp_hal::twai::filter::SingleStandardFilter;
//! # use esp_hal::twai::TwaiConfiguration;
//! # use esp_hal::twai::BaudRate;
//! # use esp_hal::twai::TwaiMode;
//! # use embedded_can::Frame;
//! # use nb::block;
//! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI
//! // transceiver.
//! let can_rx_pin = peripherals.GPIO3;
//! let can_tx_pin = peripherals.GPIO2;
//!
//! // The speed of the TWAI bus.
//! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K;
//!
//! // Begin configuring the TWAI peripheral. The peripheral is in a reset like
//! // state that prevents transmission but allows configuration.
//! let mut can_config = twai::TwaiConfiguration::new(
//! peripherals.TWAI0,
//! can_rx_pin,
//! can_tx_pin,
//! TWAI_BAUDRATE,
//! TwaiMode::Normal
//! );
//!
//! // Partially filter the incoming messages to reduce overhead of receiving
//! // undesired messages
//! can_config.set_filter(const { SingleStandardFilter::new(b"xxxxxxxxxx0",
//! b"x", [b"xxxxxxxx", b"xxxxxxxx"]) });
//!
//! // Start the peripheral. This locks the configuration settings of the
//! // peripheral and puts it into operation mode, allowing packets to be sent
//! // and received.
//! let mut can = can_config.start();
//!
//! loop {
//! // Wait for a frame to be received.
//! let frame = block!(can.receive()).unwrap();
//!
//! // Transmit the frame back.
//! let _result = block!(can.transmit(&frame)).unwrap();
//! }
//! # }
//! ```
//!
//! ### Self-testing (self reception of transmitted messages)
//! ```rust, no_run
#![doc = crate::before_snippet!()]
//! # use embedded_can::Id;
//! # use esp_hal::twai;
//! # use esp_hal::twai::filter;
//! # use esp_hal::twai::filter::SingleStandardFilter;
//! # use esp_hal::twai::TwaiConfiguration;
//! # use esp_hal::twai::BaudRate;
//! # use esp_hal::twai::EspTwaiFrame;
//! # use esp_hal::twai::StandardId;
//! # use esp_hal::twai::TwaiMode;
//! # use embedded_can::Frame;
//! # use nb::block;
//! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI
//! // transceiver.
//! let can_rx_pin = peripherals.GPIO3;
//! let can_tx_pin = peripherals.GPIO2;
//!
//! // The speed of the TWAI bus.
//! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K;
//!
//! // Begin configuring the TWAI peripheral.
//! let mut can_config = twai::TwaiConfiguration::new(
//! peripherals.TWAI0,
//! can_rx_pin,
//! can_tx_pin,
//! TWAI_BAUDRATE,
//! TwaiMode::SelfTest
//! );
//!
//! // Partially filter the incoming messages to reduce overhead of receiving
//! // undesired messages
//! can_config.set_filter(const { SingleStandardFilter::new(b"xxxxxxxxxx0",
//! b"x", [b"xxxxxxxx", b"xxxxxxxx"]) });
//!
//! // Start the peripheral. This locks the configuration settings of the
//! // peripheral and puts it into operation mode, allowing packets to be sent
//! // and received.
//! let mut can = can_config.start();
//!
//! let frame = EspTwaiFrame::new_self_reception(StandardId::ZERO,
//! &[1, 2, 3]).unwrap(); // Wait for a frame to be received.
//! let frame = block!(can.receive()).unwrap();
//!
//! # loop {}
//! # }
//! ```
use core::marker::PhantomData;
use self::filter::{Filter, FilterType};
use crate::{
gpio::{
interconnect::{PeripheralInput, PeripheralOutput},
InputSignal,
OutputSignal,
Pull,
},
interrupt::InterruptHandler,
peripheral::{Peripheral, PeripheralRef},
peripherals::twai0::RegisterBlock,
system::PeripheralClockControl,
twai::filter::SingleStandardFilter,
Async,
Blocking,
InterruptConfigurable,
};
pub mod filter;
/// TWAI error kind
///
/// This represents a common set of TWAI operation errors. HAL implementations
/// are free to define more specific or additional error types. However, by
/// providing a mapping to these common TWAI errors, generic code can still
/// react to them.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// The peripheral receive buffer was overrun.
Overrun,
// MAC sublayer errors
/// A bit error is detected at that bit time when the bit value that is
/// monitored differs from the bit value sent.
Bit,
/// A stuff error is detected at the bit time of the sixth consecutive
/// equal bit level in a frame field that shall be coded by the method
/// of bit stuffing.
Stuff,
/// Calculated CRC sequence does not equal the received one.
Crc,
/// A form error shall be detected when a fixed-form bit field contains
/// one or more illegal bits.
Form,
/// An ACK error shall be detected by a transmitter whenever it does not
/// monitor a dominant bit during the ACK slot.
Acknowledge,
/// A different error occurred. The original error may contain more
/// information.
Other,
}
macro_rules! impl_display {
($($kind:ident => $msg:expr),* $(,)?) => {
impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
$(Self::$kind => write!(f, $msg)),*
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for ErrorKind {
fn format(&self, f: defmt::Formatter<'_>) {
match self {
$(Self::$kind => defmt::write!(f, $msg)),*
}
}
}
};
}
impl_display! {
Overrun => "The peripheral receive buffer was overrun",
Bit => "Bit value that is monitored differs from the bit value sent",
Stuff => "Sixth consecutive equal bits detected",
Crc => "Calculated CRC sequence does not equal the received one",
Form => "A fixed-form bit field contains one or more illegal bits",
Acknowledge => "Transmitted frame was not acknowledged",
Other => "A different error occurred. The original error may contain more information",
}
impl From<ErrorKind> for embedded_hal_02::can::ErrorKind {
fn from(value: ErrorKind) -> Self {
match value {
ErrorKind::Overrun => embedded_hal_02::can::ErrorKind::Overrun,
ErrorKind::Bit => embedded_hal_02::can::ErrorKind::Bit,
ErrorKind::Stuff => embedded_hal_02::can::ErrorKind::Stuff,
ErrorKind::Crc => embedded_hal_02::can::ErrorKind::Crc,
ErrorKind::Form => embedded_hal_02::can::ErrorKind::Form,
ErrorKind::Acknowledge => embedded_hal_02::can::ErrorKind::Acknowledge,
ErrorKind::Other => embedded_hal_02::can::ErrorKind::Other,
}
}
}
impl embedded_hal_02::can::Error for ErrorKind {
fn kind(&self) -> embedded_hal_02::can::ErrorKind {
(*self).into()
}
}
impl From<ErrorKind> for embedded_can::ErrorKind {
fn from(value: ErrorKind) -> Self {
match value {
ErrorKind::Overrun => embedded_can::ErrorKind::Overrun,
ErrorKind::Bit => embedded_can::ErrorKind::Bit,
ErrorKind::Stuff => embedded_can::ErrorKind::Stuff,
ErrorKind::Crc => embedded_can::ErrorKind::Crc,
ErrorKind::Form => embedded_can::ErrorKind::Form,
ErrorKind::Acknowledge => embedded_can::ErrorKind::Acknowledge,
ErrorKind::Other => embedded_can::ErrorKind::Other,
}
}
}
impl embedded_can::Error for ErrorKind {
fn kind(&self) -> embedded_can::ErrorKind {
(*self).into()
}
}
/// Specifies in which mode the TWAI controller will operate.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TwaiMode {
/// Normal operating mode
Normal,
/// Self-test mode (no acknowledgement required for a successful message
/// transmission)
SelfTest,
/// Listen only operating mode
ListenOnly,
}
/// Standard 11-bit TWAI Identifier (`0..=0x7FF`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StandardId(u16);
impl StandardId {
/// TWAI ID `0`, the highest priority.
pub const ZERO: Self = StandardId(0);
/// TWAI ID `0x7FF`, the lowest priority.
pub const MAX: Self = StandardId(0x7FF);
/// Tries to create a `StandardId` from a raw 16-bit integer.
///
/// This will return `None` if `raw` is out of range of an 11-bit integer
/// (`> 0x7FF`).
#[inline]
pub fn new(raw: u16) -> Option<Self> {
if raw <= 0x7FF {
Some(StandardId(raw))
} else {
None
}
}
/// Creates a new `StandardId` without checking if it is inside the valid
/// range.
///
/// # Safety
/// Using this method can create an invalid ID and is thus marked as unsafe.
#[inline]
pub const unsafe fn new_unchecked(raw: u16) -> Self {
StandardId(raw)
}
/// Returns TWAI Identifier as a raw 16-bit integer.
#[inline]
pub fn as_raw(&self) -> u16 {
self.0
}
}
impl From<StandardId> for embedded_hal_02::can::StandardId {
fn from(value: StandardId) -> Self {
embedded_hal_02::can::StandardId::new(value.as_raw()).unwrap()
}
}
impl From<embedded_hal_02::can::StandardId> for StandardId {
fn from(value: embedded_hal_02::can::StandardId) -> Self {
StandardId::new(value.as_raw()).unwrap()
}
}
impl From<StandardId> for embedded_can::StandardId {
fn from(value: StandardId) -> Self {
embedded_can::StandardId::new(value.as_raw()).unwrap()
}
}
impl From<embedded_can::StandardId> for StandardId {
fn from(value: embedded_can::StandardId) -> Self {
StandardId::new(value.as_raw()).unwrap()
}
}
/// Extended 29-bit TWAI Identifier (`0..=1FFF_FFFF`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ExtendedId(u32);
impl ExtendedId {
/// TWAI ID `0`, the highest priority.
pub const ZERO: Self = ExtendedId(0);
/// TWAI ID `0x1FFFFFFF`, the lowest priority.
pub const MAX: Self = ExtendedId(0x1FFF_FFFF);
/// Tries to create a `ExtendedId` from a raw 32-bit integer.
///
/// This will return `None` if `raw` is out of range of an 29-bit integer
/// (`> 0x1FFF_FFFF`).
#[inline]
pub fn new(raw: u32) -> Option<Self> {
if raw <= 0x1FFF_FFFF {
Some(ExtendedId(raw))
} else {
None
}
}
/// Creates a new `ExtendedId` without checking if it is inside the valid
/// range.
///
/// # Safety
/// Using this method can create an invalid ID and is thus marked as unsafe.
#[inline]
pub const unsafe fn new_unchecked(raw: u32) -> Self {
ExtendedId(raw)
}
/// Returns TWAI Identifier as a raw 32-bit integer.
#[inline]
pub fn as_raw(&self) -> u32 {
self.0
}
/// Returns the Base ID part of this extended identifier.
pub fn standard_id(&self) -> StandardId {
// ID-28 to ID-18
StandardId((self.0 >> 18) as u16)
}
}
impl From<ExtendedId> for embedded_hal_02::can::ExtendedId {
fn from(value: ExtendedId) -> Self {
embedded_hal_02::can::ExtendedId::new(value.0).unwrap()
}
}
impl From<embedded_hal_02::can::ExtendedId> for ExtendedId {
fn from(value: embedded_hal_02::can::ExtendedId) -> Self {
ExtendedId::new(value.as_raw()).unwrap()
}
}
impl From<ExtendedId> for embedded_can::ExtendedId {
fn from(value: ExtendedId) -> Self {
embedded_can::ExtendedId::new(value.0).unwrap()
}
}
impl From<embedded_can::ExtendedId> for ExtendedId {
fn from(value: embedded_can::ExtendedId) -> Self {
ExtendedId::new(value.as_raw()).unwrap()
}
}
/// A TWAI Identifier (standard or extended).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Id {
/// Standard 11-bit Identifier (`0..=0x7FF`).
Standard(StandardId),
/// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`).
Extended(ExtendedId),
}
impl From<StandardId> for Id {
#[inline]
fn from(id: StandardId) -> Self {
Id::Standard(id)
}
}
impl From<ExtendedId> for Id {
#[inline]
fn from(id: ExtendedId) -> Self {
Id::Extended(id)
}
}
impl From<Id> for embedded_hal_02::can::Id {
fn from(value: Id) -> Self {
match value {
Id::Standard(id) => embedded_hal_02::can::Id::Standard(id.into()),
Id::Extended(id) => embedded_hal_02::can::Id::Extended(id.into()),
}
}
}
impl From<embedded_hal_02::can::Id> for Id {
fn from(value: embedded_hal_02::can::Id) -> Self {
match value {
embedded_hal_02::can::Id::Standard(id) => Id::Standard(id.into()),
embedded_hal_02::can::Id::Extended(id) => Id::Extended(id.into()),
}
}
}
impl From<Id> for embedded_can::Id {
fn from(value: Id) -> Self {
match value {
Id::Standard(id) => embedded_can::Id::Standard(id.into()),
Id::Extended(id) => embedded_can::Id::Extended(id.into()),
}
}
}
impl From<embedded_can::Id> for Id {
fn from(value: embedded_can::Id) -> Self {
match value {
embedded_can::Id::Standard(id) => Id::Standard(id.into()),
embedded_can::Id::Extended(id) => Id::Extended(id.into()),
}
}
}
/// Structure backing the embedded_hal_02::can::Frame/embedded_can::Frame trait.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EspTwaiFrame {
id: Id,
dlc: usize,
data: [u8; 8],
is_remote: bool,
self_reception: bool,
}
impl EspTwaiFrame {
/// Creates a new `EspTwaiFrame` with the specified ID and data payload.
pub fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
// TWAI frames cannot contain more than 8 bytes of data.
if data.len() > 8 {
return None;
}
let mut d: [u8; 8] = [0; 8];
d[..data.len()].copy_from_slice(data);
Some(EspTwaiFrame {
id: id.into(),
data: d,
dlc: data.len(),
is_remote: false,
self_reception: false,
})
}
/// Creates a new `EspTwaiFrame` for a transmission request with the
/// specified ID and data length (DLC).
pub fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self> {
// TWAI frames cannot have more than 8 bytes.
if dlc > 8 {
return None;
}
Some(EspTwaiFrame {
id: id.into(),
data: [0; 8],
dlc,
is_remote: true,
self_reception: false,
})
}
/// Creates a new `EspTwaiFrame` ready for self-reception with the specified
/// ID and data payload.
pub fn new_self_reception(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
if data.len() > 8 {
return None;
}
let mut d: [u8; 8] = [0; 8];
d[..data.len()].copy_from_slice(data);
Some(EspTwaiFrame {
id: id.into(),
data: d,
dlc: data.len(),
is_remote: false,
self_reception: true,
})
}
/// Make a new frame from an id, pointer to the TWAI_DATA_x_REG registers,
/// and the length of the data payload (dlc).
///
/// # Safety
/// This is unsafe because it directly accesses peripheral registers.
unsafe fn new_from_data_registers(
id: impl Into<Id>,
registers: *const u32,
dlc: usize,
) -> Self {
let mut data: [u8; 8] = [0; 8];
// Copy the data from the memory mapped peripheral into actual memory.
copy_from_data_register(&mut data[..dlc], registers);
Self {
id: id.into(),
data,
dlc,
is_remote: false,
self_reception: false,
}
}
}
impl embedded_hal_02::can::Frame for EspTwaiFrame {
fn new(id: impl Into<embedded_hal_02::can::Id>, data: &[u8]) -> Option<Self> {
Self::new(id.into(), data)
}
fn new_remote(id: impl Into<embedded_hal_02::can::Id>, dlc: usize) -> Option<Self> {
Self::new_remote(id.into(), dlc)
}
fn is_extended(&self) -> bool {
matches!(self.id, Id::Extended(_))
}
fn is_remote_frame(&self) -> bool {
self.is_remote
}
fn id(&self) -> embedded_hal_02::can::Id {
self.id.into()
}
fn dlc(&self) -> usize {
self.dlc
}
fn data(&self) -> &[u8] {
// Remote frames do not contain data, yet have a value for the dlc so return
// an empty slice for remote frames.
match self.is_remote {
true => &[],
false => &self.data[0..self.dlc],
}
}
}
impl embedded_can::Frame for EspTwaiFrame {
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
Self::new(id.into(), data)
}
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {
Self::new_remote(id.into(), dlc)
}
fn is_extended(&self) -> bool {
matches!(self.id, Id::Extended(_))
}
fn is_remote_frame(&self) -> bool {
self.is_remote
}
fn id(&self) -> embedded_can::Id {
self.id.into()
}
fn dlc(&self) -> usize {
self.dlc
}
fn data(&self) -> &[u8] {
// Remote frames do not contain data, yet have a value for the dlc so return
// an empty slice for remote frames.
match self.is_remote {
true => &[],
false => &self.data[0..self.dlc],
}
}
}
/// The underlying timings for the TWAI peripheral.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimingConfig {
/// The baudrate prescaler is used to determine the period of each time
/// quantum by dividing the TWAI controller's source clock.
pub baud_rate_prescaler: u16,
/// The synchronization jump width is used to determine the maximum number
/// of time quanta a single bit time can be lengthened/shortened for
/// synchronization purposes.
pub sync_jump_width: u8,
/// Timing segment 1 consists of 1 to 16 time quanta before sample point.
pub tseg_1: u8,
/// Timing Segment 2 consists of 1 to 8 time quanta after sample point.
pub tseg_2: u8,
/// Enabling triple sampling causes 3 time quanta to be sampled per bit
/// instead of 1.
pub triple_sample: bool,
}
/// A selection of pre-determined baudrates for the TWAI driver.
/// Currently these timings are sourced from the ESP IDF C driver which assumes
/// an APB clock of 80MHz.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BaudRate {
/// A baud rate of 125 Kbps.
B125K,
/// A baud rate of 250 Kbps.
B250K,
/// A baud rate of 500 Kbps.
B500K,
/// A baud rate of 1 Mbps.
B1000K,
/// A custom baud rate defined by the user.
///
/// This variant allows users to specify their own timing configuration
/// using a `TimingConfig` struct.
Custom(TimingConfig),
}
impl BaudRate {
/// Convert the BaudRate into the timings that the peripheral needs.
// See: https://github.com/espressif/esp-idf/tree/master/components/hal/include/hal/twai_types.h
const fn timing(self) -> TimingConfig {
#[cfg(not(esp32h2))]
let timing = 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,
};
#[cfg(esp32h2)]
let timing = match self {
Self::B125K => TimingConfig {
baud_rate_prescaler: 16,
sync_jump_width: 3,
tseg_1: 11,
tseg_2: 4,
triple_sample: false,
},
Self::B250K => TimingConfig {
baud_rate_prescaler: 8,
sync_jump_width: 3,
tseg_1: 11,
tseg_2: 4,
triple_sample: false,
},
Self::B500K => TimingConfig {
baud_rate_prescaler: 4,
sync_jump_width: 3,
tseg_1: 11,
tseg_2: 4,
triple_sample: false,
},
Self::B1000K => TimingConfig {
baud_rate_prescaler: 2,
sync_jump_width: 3,
tseg_1: 11,
tseg_2: 4,
triple_sample: false,
},
Self::Custom(timing_config) => timing_config,
};
// clock source on ESP32-C6 is xtal (40MHz)
#[cfg(esp32c6)]
let timing = TimingConfig {
baud_rate_prescaler: timing.baud_rate_prescaler / 2,
..timing
};
timing
}
}
/// An inactive TWAI peripheral in the "Reset"/configuration state.
pub struct TwaiConfiguration<'d, DM: crate::Mode, T = AnyTwai> {
twai: PeripheralRef<'d, T>,
filter: Option<(FilterType, [u8; 8])>,
phantom: PhantomData<DM>,
mode: TwaiMode,
}
impl<'d, DM, T> TwaiConfiguration<'d, DM, T>
where
T: Instance,
DM: crate::Mode,
{
fn new_internal<TX: PeripheralOutput, RX: PeripheralInput>(
twai: impl Peripheral<P = T> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
baud_rate: BaudRate,
no_transceiver: bool,
mode: TwaiMode,
) -> Self {
crate::into_ref!(twai);
crate::into_mapped_ref!(tx_pin, rx_pin);
// Enable the peripheral clock for the TWAI peripheral.
PeripheralClockControl::enable(twai.peripheral());
PeripheralClockControl::reset(twai.peripheral());
let mut this = TwaiConfiguration {
twai,
filter: None, // We'll immediately call `set_filter`
phantom: PhantomData,
mode,
};
// Accept all messages by default.
this.set_filter(
const { SingleStandardFilter::new(b"xxxxxxxxxxx", b"x", [b"xxxxxxxx", b"xxxxxxxx"]) },
);
// Set RESET bit to 1
this.twai
.register_block()
.mode()
.write(|w| w.reset_mode().set_bit());
#[cfg(esp32)]
{
// Enable extended register layout
this.twai
.register_block()
.clock_divider()
.modify(|_, w| w.ext_mode().set_bit());
}
// Set up the GPIO pins.
let rx_pull = if no_transceiver {
tx_pin.set_to_open_drain_output(crate::private::Internal);
tx_pin.pull_direction(Pull::Up, crate::private::Internal);
Pull::Up
} else {
tx_pin.set_to_push_pull_output(crate::private::Internal);
Pull::None
};
this.twai.output_signal().connect_to(tx_pin);
// Setting up RX pin later allows us to use a single pin in tests.
// `set_to_push_pull_output` disables input, here we re-enable it if rx_pin
// uses the same GPIO.
rx_pin.init_input(rx_pull, crate::private::Internal);
this.twai.input_signal().connect_to(rx_pin);
// Freeze REC by changing to LOM mode
this.set_mode(TwaiMode::ListenOnly);
// Set TEC to 0
this.twai
.register_block()
.tx_err_cnt()
.write(|w| unsafe { w.tx_err_cnt().bits(0) });
// Set REC to 0
this.twai
.register_block()
.rx_err_cnt()
.write(|w| unsafe { w.rx_err_cnt().bits(0) });
// Set EWL to 96
this.twai
.register_block()
.err_warning_limit()
.write(|w| unsafe { w.err_warning_limit().bits(96) });
this.set_baud_rate(baud_rate);
this
}
fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) {
for core in crate::Cpu::other() {
crate::interrupt::disable(core, self.twai.interrupt());
}
unsafe { crate::interrupt::bind_interrupt(self.twai.interrupt(), handler.handler()) };
unwrap!(crate::interrupt::enable(
self.twai.interrupt(),
handler.priority()
));
}
/// Set the bitrate of the bus.
///
/// Note: The timings currently assume a APB_CLK of 80MHz.
fn set_baud_rate(&mut self, baud_rate: BaudRate) {
// TWAI is clocked from the APB_CLK according to Table 6-4 [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf)
// Included timings are all for 80MHz so assert that we are running at 80MHz.
#[cfg(not(any(esp32h2, esp32c6)))]
{
let apb_clock = crate::clock::Clocks::get().apb_clock;
assert!(apb_clock == fugit::HertzU32::MHz(80));
}
// Unpack the baud rate timings and convert them to the values needed for the
// register. Many of the registers have a minimum value of 1 which is
// represented by having zero bits set, therefore many values need to
// have 1 subtracted from them before being stored into the register.
let timing = baud_rate.timing();
#[cfg_attr(not(esp32), allow(unused_mut))]
let mut prescaler = timing.baud_rate_prescaler;
#[cfg(esp32)]
{
// From <https://github.com/espressif/esp-idf/blob/6e5a178b3120dced7fa5c29c655cc22ea182df3d/components/soc/esp32/register/soc/twai_struct.h#L79>
// and <https://github.com/espressif/esp-idf/blob/6e5a178b3120dced7fa5c29c655cc22ea182df3d/components/hal/esp32/include/hal/twai_ll.h#L528-L534>:
if timing.baud_rate_prescaler > 128 {
// Enable /2 baudrate divider by setting `brp_div`.
// `brp_div` is not an interrupt, it will prescale BRP by 2. Only available on
// ESP32 Revision 2 or later. Reserved otherwise.
self.twai
.register_block()
.int_ena()
.modify(|_, w| w.brp_div().set_bit());
prescaler = timing.baud_rate_prescaler / 2;
} else {
// Disable /2 baudrate divider by clearing brp_div.
self.twai
.register_block()
.int_ena()
.modify(|_, w| w.brp_div().clear_bit());
}
}
let prescale = (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;
// Set up the prescaler and sync jump width.
self.twai
.register_block()
.bus_timing_0()
.modify(|_, w| unsafe {
w.baud_presc().bits(prescale as _);
w.sync_jump_width().bits(sjw)
});
// Set up the time segment 1, time segment 2, and triple sample.
self.twai
.register_block()
.bus_timing_1()
.modify(|_, w| unsafe {
w.time_seg1().bits(tseg_1);
w.time_seg2().bits(tseg_2);
w.time_samp().bit(triple_sample)
});
// disable CLKOUT
self.twai
.register_block()
.clock_divider()
.modify(|_, w| w.clock_off().set_bit());
}
/// Set up the acceptance filter on the device.
///
/// NOTE: On a bus with mixed 11-bit and 29-bit packet id's, you may
/// experience an 11-bit filter match against a 29-bit frame and vice
/// versa. Your application should check the id again once a frame has
/// been received to make sure it is the expected value.
///
/// You may use a `const {}` block to ensure that the filter is parsed
/// during program compilation.
///
/// The filter is not applied to the peripheral until [`Self::start`] is
/// called.
///
/// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6)
pub fn set_filter(&mut self, filter: impl Filter) {
// Convert the filter into values for the registers and store them for later
// use.
self.filter = Some((filter.filter_type(), filter.to_registers()));
}
fn apply_filter(&self) {
let Some((filter_type, registers)) = self.filter.as_ref() else {
return;
};
let register_block = self.twai.register_block();
// Set or clear the rx filter mode bit depending on the filter type.
register_block
.mode()
.modify(|_, w| w.rx_filter_mode().bit(*filter_type == FilterType::Single));
// Copy the filter to the peripheral.
unsafe {
copy_to_data_register(register_block.data_0().as_ptr(), registers);
}
}
/// Set the error warning threshold.
///
/// In the case when any of an error counter value exceeds the threshold, or
/// all the error counter values are below the threshold, an error
/// warning interrupt will be triggered (given the enable signal is
/// valid).
pub fn set_error_warning_limit(&mut self, limit: u8) {
self.twai
.register_block()
.err_warning_limit()
.write(|w| unsafe { w.err_warning_limit().bits(limit) });
}
/// Set the operating mode based on provided option
fn set_mode(&self, mode: TwaiMode) {
self.twai.register_block().mode().modify(|_, w| {
// self-test mode turns off acknowledgement requirement
w.self_test_mode().bit(mode == TwaiMode::SelfTest);
w.listen_only_mode().bit(mode == TwaiMode::ListenOnly)
});
}
/// Put the peripheral into Operation Mode, allowing the transmission and
/// reception of packets using the new object.
pub fn start(self) -> Twai<'d, DM, T> {
self.apply_filter();
self.set_mode(self.mode);
// Clear the TEC and REC
self.twai
.register_block()
.tx_err_cnt()
.write(|w| unsafe { w.tx_err_cnt().bits(0) });
let rec =
if cfg!(any(esp32, esp32s2, esp32s3, esp32c3)) && self.mode == TwaiMode::ListenOnly {
// Errata workaround: Prevent transmission of dominant error frame while in
// listen only mode by setting REC to 128 before exiting reset mode.
// This forces the controller to be error passive (thus only transmits recessive
// bits). The TEC/REC remain frozen in listen only mode thus
// ensuring we remain error passive.
128
} else {
0
};
self.twai
.register_block()
.rx_err_cnt()
.write(|w| unsafe { w.rx_err_cnt().bits(rec) });
// Clear any interrupts by reading the status register
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] {
let _ = self.twai.register_block().int_raw().read();
} else {
let _ = self.twai.register_block().interrupt().read();
}
}
// Put the peripheral into operation mode by clearing the reset mode bit.
self.twai
.register_block()
.mode()
.modify(|_, w| w.reset_mode().clear_bit());
Twai {
rx: TwaiRx {
twai: unsafe { self.twai.clone_unchecked() },
phantom: PhantomData,
},
tx: TwaiTx {
twai: unsafe { self.twai.clone_unchecked() },
phantom: PhantomData,
},
twai: unsafe { self.twai.clone_unchecked() },
phantom: PhantomData,
}
}
}
impl<'d> TwaiConfiguration<'d, Blocking> {
/// Create a new instance of [TwaiConfiguration]
///
/// You will need to use a transceiver to connect to the TWAI bus
pub fn new<RX: PeripheralInput, TX: PeripheralOutput>(
peripheral: impl Peripheral<P = impl Instance> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
baud_rate: BaudRate,
mode: TwaiMode,
) -> Self {
Self::new_typed(peripheral.map_into(), rx_pin, tx_pin, baud_rate, mode)
}
/// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s
/// directly
///
/// You don't need a transceiver by following the description in the
/// `twai.rs` example
pub fn new_no_transceiver<RX: PeripheralInput, TX: PeripheralOutput>(
peripheral: impl Peripheral<P = impl Instance> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
baud_rate: BaudRate,
mode: TwaiMode,
) -> Self {
Self::new_no_transceiver_typed(peripheral.map_into(), rx_pin, tx_pin, baud_rate, mode)
}
}
impl<'d, T> TwaiConfiguration<'d, Blocking, T>
where
T: Instance,
{
/// Create a new instance of [TwaiConfiguration]
///
/// You will need to use a transceiver to connect to the TWAI bus
pub fn new_typed<RX: PeripheralInput, TX: PeripheralOutput>(
peripheral: impl Peripheral<P = T> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
baud_rate: BaudRate,
mode: TwaiMode,
) -> Self {
Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, false, mode)
}
/// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s
/// directly
///
/// You don't need a transceiver by following the description in the
/// `twai.rs` example
pub fn new_no_transceiver_typed<RX: PeripheralInput, TX: PeripheralOutput>(
peripheral: impl Peripheral<P = T> + 'd,
rx_pin: impl Peripheral<P = RX> + 'd,
tx_pin: impl Peripheral<P = TX> + 'd,
baud_rate: BaudRate,
mode: TwaiMode,
) -> Self {
Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, true, mode)
}
/// Convert the configuration into an async configuration.
pub fn into_async(mut self) -> TwaiConfiguration<'d, Async, T> {
self.set_interrupt_handler(self.twai.async_handler());
TwaiConfiguration {
twai: self.twai,
filter: self.filter,
phantom: PhantomData,
mode: self.mode,
}
}
}
impl<'d, T> TwaiConfiguration<'d, Async, T>
where
T: Instance,
{
/// Convert the configuration into a blocking configuration.
pub fn into_blocking(self) -> TwaiConfiguration<'d, Blocking, T> {
use crate::{interrupt, Cpu};
interrupt::disable(Cpu::current(), self.twai.interrupt());
// Re-create in blocking mode
TwaiConfiguration {
twai: self.twai,
filter: self.filter,
phantom: PhantomData,
mode: self.mode,
}
}
}
impl<T> crate::private::Sealed for TwaiConfiguration<'_, Blocking, T> where T: Instance {}
impl<T> InterruptConfigurable for TwaiConfiguration<'_, Blocking, T>
where
T: Instance,
{
fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
self.internal_set_interrupt_handler(handler);
}
}
/// An active TWAI peripheral in Normal Mode.
///
/// In this mode, the TWAI controller can transmit and receive messages
/// including error signals (such as error and overload frames).
pub struct Twai<'d, DM: crate::Mode, T = AnyTwai> {
twai: PeripheralRef<'d, T>,
tx: TwaiTx<'d, DM, T>,
rx: TwaiRx<'d, DM, T>,
phantom: PhantomData<DM>,
}
impl<'d, T, DM> Twai<'d, DM, T>
where
T: Instance,
DM: crate::Mode,
{
fn mode(&self) -> TwaiMode {
let mode = self.twai.register_block().mode().read();
if mode.self_test_mode().bit_is_set() {
TwaiMode::SelfTest
} else if mode.listen_only_mode().bit_is_set() {
TwaiMode::ListenOnly
} else {
TwaiMode::Normal
}
}
/// Stop the peripheral, putting it into reset mode and enabling
/// reconfiguration.
pub fn stop(self) -> TwaiConfiguration<'d, DM, T> {
// Put the peripheral into reset/configuration mode by setting the reset mode
// bit.
self.twai
.register_block()
.mode()
.modify(|_, w| w.reset_mode().set_bit());
let mode = self.mode();
TwaiConfiguration {
twai: self.twai,
filter: None, // filter already applied, no need to restore it
phantom: PhantomData,
mode,
}
}
/// Returns the value of the receive error counter.
pub fn receive_error_count(&self) -> u8 {
self.twai
.register_block()
.rx_err_cnt()
.read()
.rx_err_cnt()
.bits()
}
/// Returns the value of the transmit error counter.
pub fn transmit_error_count(&self) -> u8 {
self.twai
.register_block()
.tx_err_cnt()
.read()
.tx_err_cnt()
.bits()
}
/// Check if the controller is in a bus off state.
pub fn is_bus_off(&self) -> bool {
self.twai
.register_block()
.status()
.read()
.bus_off_st()
.bit_is_set()
}
/// Get the number of messages that the peripheral has available in the
/// receive FIFO.
///
/// Note that this may not be the number of valid messages in the receive
/// FIFO due to fifo overflow/overrun.
pub fn num_available_messages(&self) -> u8 {
self.twai
.register_block()
.rx_message_cnt()
.read()
.rx_message_counter()
.bits()
}
/// Clear the receive FIFO, discarding any valid, partial, or invalid
/// packets.
///
/// This is typically used to clear an overrun receive FIFO.
///
/// TODO: Not sure if this needs to be guarded against Bus Off or other
/// error states.
pub fn clear_receive_fifo(&self) {
while self.num_available_messages() > 0 {
release_receive_fifo(self.twai.register_block());
}
}
/// Sends the specified `EspTwaiFrame` over the TWAI bus.
pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> {
self.tx.transmit(frame)
}
/// Receives a TWAI frame from the TWAI bus.
pub fn receive(&mut self) -> nb::Result<EspTwaiFrame, EspTwaiError> {
self.rx.receive()
}
/// Consumes this `Twai` instance and splits it into transmitting and
/// receiving halves.
pub fn split(self) -> (TwaiRx<'d, DM, T>, TwaiTx<'d, DM, T>) {
(self.rx, self.tx)
}
}
/// Interface to the TWAI transmitter part.
pub struct TwaiTx<'d, DM: crate::Mode, T = AnyTwai> {
twai: PeripheralRef<'d, T>,
phantom: PhantomData<DM>,
}
impl<DM, T> TwaiTx<'_, DM, T>
where
T: Instance,
DM: crate::Mode,
{
/// Transmit a frame.
///
/// Because of how the TWAI registers are set up, we have to do some
/// assembly of bytes. Note that these registers serve a filter
/// configuration role when the device is in configuration mode so
/// patching the svd files to improve this may be non-trivial.
///
/// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.4.2)
///
/// NOTE: TODO: This may not work if using the self reception/self test
/// functionality. See notes 1 and 2 in the "Frame Identifier" section
/// of the reference manual.
pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> {
let register_block = self.twai.register_block();
let status = register_block.status().read();
// Check that the peripheral is not in a bus off state.
if status.bus_off_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff));
}
// Check that the peripheral is not already transmitting a packet.
if !status.tx_buf_st().bit_is_set() {
return nb::Result::Err(nb::Error::WouldBlock);
}
write_frame(register_block, frame);
Ok(())
}
}
/// Interface to the TWAI receiver part.
pub struct TwaiRx<'d, DM: crate::Mode, T = AnyTwai> {
twai: PeripheralRef<'d, T>,
phantom: PhantomData<DM>,
}
impl<DM, T> TwaiRx<'_, DM, T>
where
T: Instance,
DM: crate::Mode,
{
/// Receive a frame
pub fn receive(&mut self) -> nb::Result<EspTwaiFrame, EspTwaiError> {
let register_block = self.twai.register_block();
let status = register_block.status().read();
// Check that the peripheral is not in a bus off state.
if status.bus_off_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff));
}
// Check that we actually have packets to receive.
if !status.rx_buf_st().bit_is_set() {
return nb::Result::Err(nb::Error::WouldBlock);
}
// Check if the packet in the receive buffer is valid or overrun.
if status.miss_st().bit_is_set() {
return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL(
ErrorKind::Overrun,
)));
}
Ok(read_frame(register_block)?)
}
}
/// Represents errors that can occur in the TWAI driver.
/// This enum defines the possible errors that can be encountered when
/// interacting with the TWAI peripheral.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EspTwaiError {
/// TWAI peripheral has entered a bus-off state.
BusOff,
/// The received frame contains an invalid DLC.
NonCompliantDlc(u8),
/// Encapsulates errors defined by the embedded-hal crate.
EmbeddedHAL(ErrorKind),
}
impl embedded_hal_02::can::Error for EspTwaiError {
fn kind(&self) -> embedded_hal_02::can::ErrorKind {
if let Self::EmbeddedHAL(kind) = self {
(*kind).into()
} else {
embedded_hal_02::can::ErrorKind::Other
}
}
}
impl embedded_can::Error for EspTwaiError {
fn kind(&self) -> embedded_can::ErrorKind {
if let Self::EmbeddedHAL(kind) = self {
(*kind).into()
} else {
embedded_can::ErrorKind::Other
}
}
}
/// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into
/// the destination.
///
/// # Safety
/// This function is marked unsafe because it reads arbitrarily from
/// memory-mapped registers. Specifically, this function is used with the
/// TWAI_DATA_x_REG registers which has different results based on the mode of
/// the peripheral.
#[inline(always)]
unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) {
for (i, dest) in dest.iter_mut().enumerate() {
// Perform a volatile read to avoid compiler optimizations.
*dest = src.add(i).read_volatile() as u8;
}
}
/// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into
/// the destination.
///
/// # Safety
/// This function is marked unsafe because it writes arbitrarily to
/// memory-mapped registers. Specifically, this function is used with the
/// TWAI_DATA_x_REG registers which has different results based on the mode of
/// the peripheral.
#[inline(always)]
unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) {
for (i, src) in src.iter().enumerate() {
// Perform a volatile write to avoid compiler optimizations.
dest.add(i).write_volatile(*src as u32);
}
}
impl<DM, T> embedded_hal_02::can::Can for Twai<'_, DM, T>
where
T: Instance,
DM: crate::Mode,
{
type Frame = EspTwaiFrame;
type Error = EspTwaiError;
/// Transmit a frame.
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error> {
self.tx.transmit(frame)?;
// Success in readying packet for transmit. No packets can be replaced in the
// transmit buffer so return None in accordance with the
// embedded-can/embedded-hal trait.
nb::Result::Ok(None)
}
/// Return a received frame if there are any available.
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error> {
self.rx.receive()
}
}
impl<DM, T> embedded_can::nb::Can for Twai<'_, DM, T>
where
T: Instance,
DM: crate::Mode,
{
type Frame = EspTwaiFrame;
type Error = EspTwaiError;
/// Transmit a frame.
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error> {
self.tx.transmit(frame)?;
// Success in readying packet for transmit. No packets can be replaced in the
// transmit buffer so return None in accordance with the
// embedded-can/embedded-hal trait.
nb::Result::Ok(None)
}
/// Return a received frame if there are any available.
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error> {
self.rx.receive()
}
}
/// TWAI peripheral instance.
pub trait Instance: Peripheral<P = Self> + Into<AnyTwai> + 'static {
/// The identifier number for this TWAI instance.
fn number(&self) -> usize;
/// Returns the system peripheral marker for this instance.
fn peripheral(&self) -> crate::system::Peripheral;
/// Input signal.
fn input_signal(&self) -> InputSignal;
/// Output signal.
fn output_signal(&self) -> OutputSignal;
/// The interrupt associated with this TWAI instance.
fn interrupt(&self) -> crate::peripherals::Interrupt;
/// Provides an asynchronous interrupt handler for TWAI instance.
fn async_handler(&self) -> InterruptHandler;
/// Returns a reference to the register block for TWAI instance.
fn register_block(&self) -> &RegisterBlock;
/// Enables interrupts for the TWAI peripheral.
fn enable_interrupts(&self) {
let register_block = self.register_block();
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] {
register_block.int_ena().modify(|_, w| {
w.rx_int_ena().set_bit();
w.tx_int_ena().set_bit();
w.bus_err_int_ena().set_bit();
w.arb_lost_int_ena().set_bit();
w.err_passive_int_ena().set_bit()
});
} else {
register_block.interrupt_enable().modify(|_, w| {
w.ext_receive_int_ena().set_bit();
w.ext_transmit_int_ena().set_bit();
w.bus_err_int_ena().set_bit();
w.arbitration_lost_int_ena().set_bit();
w.err_passive_int_ena().set_bit()
});
}
}
}
/// Returns a reference to the asynchronous state for this TWAI instance.
fn async_state(&self) -> &asynch::TwaiAsyncState;
}
/// Read a frame from the peripheral.
fn read_frame(register_block: &RegisterBlock) -> Result<EspTwaiFrame, EspTwaiError> {
// Read the frame information and extract the frame id format and dlc.
let data_0 = 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 self_reception = data_0 & 0b1 << 4 != 0;
let dlc = data_0 & 0b1111;
if dlc > 8 {
// Release the packet we read from the FIFO, allowing the peripheral to prepare
// the next packet.
release_receive_fifo(register_block);
return Err(EspTwaiError::NonCompliantDlc(dlc));
}
let dlc = dlc as usize;
// Read the payload from the packet and construct a frame.
let (id, data_ptr) = if is_standard_format {
// Frame uses standard 11 bit id.
let data_1 = register_block.data_1().read().tx_byte_1().bits();
let data_2 = 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 = Id::from(StandardId::new(raw_id).unwrap());
(id, register_block.data_3().as_ptr())
} else {
// Frame uses extended 29 bit id.
let data_1 = register_block.data_1().read().tx_byte_1().bits();
let data_2 = register_block.data_2().read().tx_byte_2().bits();
let data_3 = register_block.data_3().read().tx_byte_3().bits();
let data_4 = 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 = Id::from(ExtendedId::new(raw_id).unwrap());
(id, register_block.data_5().as_ptr())
};
let mut frame = if is_data_frame {
unsafe { EspTwaiFrame::new_from_data_registers(id, data_ptr, dlc) }
} else {
EspTwaiFrame::new_remote(id, dlc).unwrap()
};
frame.self_reception = self_reception;
// Release the packet we read from the FIFO, allowing the peripheral to prepare
// the next packet.
release_receive_fifo(register_block);
Ok(frame)
}
/// Release the message in the buffer. This will decrement the received
/// message counter and prepare the next message in the FIFO for
/// reading.
fn release_receive_fifo(register_block: &RegisterBlock) {
register_block.cmd().write(|w| w.release_buf().set_bit());
}
/// Write a frame to the peripheral.
fn write_frame(register_block: &RegisterBlock, frame: &EspTwaiFrame) {
// Assemble the frame information into the data_0 byte.
let frame_format: u8 = matches!(frame.id, Id::Extended(_)) as u8;
let self_reception: u8 = frame.self_reception as u8;
let rtr_bit: u8 = frame.is_remote as u8;
let dlc_bits: u8 = frame.dlc as u8 & 0b1111;
let data_0: u8 = frame_format << 7 | rtr_bit << 6 | self_reception << 4 | dlc_bits;
register_block
.data_0()
.write(|w| unsafe { w.tx_byte_0().bits(data_0) });
// Assemble the identifier information of the packet and return where the data
// buffer starts.
let data_ptr = match frame.id {
Id::Standard(id) => {
let id = id.as_raw();
register_block
.data_1()
.write(|w| unsafe { w.tx_byte_1().bits((id >> 3) as u8) });
register_block
.data_2()
.write(|w| unsafe { w.tx_byte_2().bits((id << 5) as u8) });
register_block.data_3().as_ptr()
}
Id::Extended(id) => {
let id = id.as_raw();
register_block
.data_1()
.write(|w| unsafe { w.tx_byte_1().bits((id >> 21) as u8) });
register_block
.data_2()
.write(|w| unsafe { w.tx_byte_2().bits((id >> 13) as u8) });
register_block
.data_3()
.write(|w| unsafe { w.tx_byte_3().bits((id >> 5) as u8) });
register_block
.data_4()
.write(|w| unsafe { w.tx_byte_4().bits((id << 3) as u8) });
register_block.data_5().as_ptr()
}
};
// Store the data portion of the packet into the transmit buffer.
unsafe {
copy_to_data_register(
data_ptr,
match frame.is_remote {
true => &[], // RTR frame, so no data is included.
false => &frame.data[0..frame.dlc],
},
)
}
// Trigger the appropriate transmission request based on self_reception flag
if frame.self_reception {
register_block.cmd().write(|w| w.self_rx_req().set_bit());
} else {
// Set the transmit request command, this will lock the transmit buffer until
// the transmission is complete or aborted.
register_block.cmd().write(|w| w.tx_req().set_bit());
}
}
impl Instance for crate::peripherals::TWAI0 {
fn number(&self) -> usize {
0
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Twai0
}
fn input_signal(&self) -> InputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] {
InputSignal::TWAI_RX
} else {
InputSignal::TWAI0_RX
}
}
}
fn output_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] {
OutputSignal::TWAI_TX
} else {
OutputSignal::TWAI0_TX
}
}
}
fn interrupt(&self) -> crate::peripherals::Interrupt {
crate::peripherals::Interrupt::TWAI0
}
fn async_handler(&self) -> InterruptHandler {
asynch::twai0
}
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
unsafe { &*crate::peripherals::TWAI0::PTR }
}
fn async_state(&self) -> &asynch::TwaiAsyncState {
static STATE: asynch::TwaiAsyncState = asynch::TwaiAsyncState::new();
&STATE
}
}
#[cfg(twai1)]
impl Instance for crate::peripherals::TWAI1 {
fn number(&self) -> usize {
1
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Twai1
}
fn input_signal(&self) -> InputSignal {
InputSignal::TWAI1_RX
}
fn output_signal(&self) -> OutputSignal {
OutputSignal::TWAI1_TX
}
fn interrupt(&self) -> crate::peripherals::Interrupt {
crate::peripherals::Interrupt::TWAI1
}
fn async_handler(&self) -> InterruptHandler {
asynch::twai1
}
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
unsafe { &*crate::peripherals::TWAI1::PTR }
}
fn async_state(&self) -> &asynch::TwaiAsyncState {
static STATE: asynch::TwaiAsyncState = asynch::TwaiAsyncState::new();
&STATE
}
}
crate::any_peripheral! {
/// Any TWAI peripheral.
pub peripheral AnyTwai {
#[cfg(twai0)]
Twai0(crate::peripherals::TWAI0),
#[cfg(twai1)]
Twai1(crate::peripherals::TWAI1),
}
}
impl Instance for AnyTwai {
delegate::delegate! {
to match &self.0 {
#[cfg(twai0)]
AnyTwaiInner::Twai0(twai) => twai,
#[cfg(twai1)]
AnyTwaiInner::Twai1(twai) => twai,
} {
fn number(&self) -> usize;
fn peripheral(&self) -> crate::system::Peripheral;
fn input_signal(&self) -> InputSignal;
fn output_signal(&self) -> OutputSignal;
fn interrupt(&self) -> crate::peripherals::Interrupt;
fn async_handler(&self) -> InterruptHandler;
fn register_block(&self) -> &RegisterBlock;
fn async_state(&self) -> &asynch::TwaiAsyncState;
}
}
}
mod asynch {
use core::{future::poll_fn, task::Poll};
use embassy_sync::{
blocking_mutex::raw::CriticalSectionRawMutex,
channel::Channel,
waitqueue::AtomicWaker,
};
use procmacros::handler;
use super::*;
use crate::peripherals::TWAI0;
#[cfg(twai1)]
use crate::peripherals::TWAI1;
pub struct TwaiAsyncState {
pub tx_waker: AtomicWaker,
pub err_waker: AtomicWaker,
pub rx_queue: Channel<CriticalSectionRawMutex, Result<EspTwaiFrame, EspTwaiError>, 32>,
}
impl Default for TwaiAsyncState {
fn default() -> Self {
Self::new()
}
}
impl TwaiAsyncState {
pub const fn new() -> Self {
Self {
tx_waker: AtomicWaker::new(),
err_waker: AtomicWaker::new(),
rx_queue: Channel::new(),
}
}
}
impl<T> Twai<'_, crate::Async, T>
where
T: Instance,
{
/// Transmits an `EspTwaiFrame` asynchronously over the TWAI bus.
pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> {
self.tx.transmit_async(frame).await
}
/// Receives an `EspTwaiFrame` asynchronously over the TWAI bus.
pub async fn receive_async(&mut self) -> Result<EspTwaiFrame, EspTwaiError> {
self.rx.receive_async().await
}
}
impl<T> TwaiTx<'_, crate::Async, T>
where
T: Instance,
{
/// Transmits an `EspTwaiFrame` asynchronously over the TWAI bus.
pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> {
self.twai.enable_interrupts();
poll_fn(|cx| {
self.twai.async_state().tx_waker.register(cx.waker());
let register_block = self.twai.register_block();
let status = register_block.status().read();
// Check that the peripheral is not in a bus off state.
if status.bus_off_st().bit_is_set() {
return Poll::Ready(Err(EspTwaiError::BusOff));
}
// Check that the peripheral is not already transmitting a packet.
if !status.tx_buf_st().bit_is_set() {
return Poll::Pending;
}
write_frame(register_block, frame);
Poll::Ready(Ok(()))
})
.await
}
}
impl<T> TwaiRx<'_, crate::Async, T>
where
T: Instance,
{
/// Receives an `EspTwaiFrame` asynchronously over the TWAI bus.
pub async fn receive_async(&mut self) -> Result<EspTwaiFrame, EspTwaiError> {
self.twai.enable_interrupts();
poll_fn(|cx| {
self.twai.async_state().err_waker.register(cx.waker());
if let Poll::Ready(result) = self.twai.async_state().rx_queue.poll_receive(cx) {
return Poll::Ready(result);
}
let register_block = self.twai.register_block();
let status = register_block.status().read();
// Check that the peripheral is not in a bus off state.
if status.bus_off_st().bit_is_set() {
return Poll::Ready(Err(EspTwaiError::BusOff));
}
// Check if the packet in the receive buffer is valid or overrun.
if status.miss_st().bit_is_set() {
return Poll::Ready(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun)));
}
Poll::Pending
})
.await
}
}
fn handle_interrupt(register_block: &RegisterBlock, async_state: &TwaiAsyncState) {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] {
let intr_enable = register_block.int_ena().read();
let intr_status = register_block.int_raw().read();
let int_ena_reg = register_block.int_ena();
let tx_int_status = intr_status.tx_int_st();
let rx_int_status = intr_status.rx_int_st();
} else {
let intr_enable = register_block.interrupt_enable().read();
let intr_status = register_block.interrupt().read();
let int_ena_reg = register_block.interrupt_enable();
let tx_int_status = intr_status.transmit_int_st();
let rx_int_status = intr_status.receive_int_st();
}
}
if tx_int_status.bit_is_set() {
async_state.tx_waker.wake();
}
if rx_int_status.bit_is_set() {
let status = register_block.status().read();
let rx_queue = &async_state.rx_queue;
if status.bus_off_st().bit_is_set() {
let _ = rx_queue.try_send(Err(EspTwaiError::BusOff));
}
if status.miss_st().bit_is_set() {
let _ = rx_queue.try_send(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun)));
}
match read_frame(register_block) {
Ok(frame) => {
let _ = rx_queue.try_send(Ok(frame));
}
Err(e) => warn!("Error reading frame: {:?}", e),
}
}
if intr_status.bits() & 0b11111100 > 0 {
let err_capture = register_block.err_code_cap().read();
let status = register_block.status().read();
// Read error code direction (transmitting or receiving)
let ecc_direction = err_capture.ecc_direction().bit_is_set();
// If the error comes from Tx and Tx request is pending
if !ecc_direction && !status.tx_buf_st().bit_is_set() {
// Cancel a pending transmission request
register_block.cmd().write(|w| w.abort_tx().set_bit());
}
async_state.err_waker.wake();
}
// Clear interrupt request bits
unsafe {
int_ena_reg.modify(|_, w| w.bits(intr_enable.bits() & (!intr_status.bits() | 1)));
}
}
#[handler]
pub(super) fn twai0() {
let twai = unsafe { TWAI0::steal() };
let register_block = twai.register_block();
let async_state = twai.async_state();
handle_interrupt(register_block, async_state);
}
#[cfg(twai1)]
#[handler]
pub(super) fn twai1() {
let twai = unsafe { TWAI1::steal() };
let register_block = twai.register_block();
let async_state = twai.async_state();
handle_interrupt(register_block, async_state);
}
}