From 7d5372f57dad588acd21b96eeedcc1356adf66aa Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Thu, 28 Mar 2024 14:44:09 +0000 Subject: [PATCH] Decouple the `twai` driver from the `embedded-hal` traits (#1355) * Decouple the TWAI driver from the `embedded-hal` and `embedded-can` packages * No longer require `embedded-hal`/`embedded-can` for the TWAI example * Update `CHANGELOG.md` --- esp-hal/CHANGELOG.md | 4 +- esp-hal/src/twai/filter.rs | 95 ++++--- esp-hal/src/twai/mod.rs | 512 +++++++++++++++++++++++++++++++++---- examples/src/bin/twai.rs | 15 +- 4 files changed, 513 insertions(+), 113 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 07c26b0b0..3d3d87831 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -8,8 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Add `ADC::read_blocking` to xtensa chips (#1293) +- Add `ADC::read_blocking` to xtensa chips (#1293) - ESP32-C6 / ESP32-H2: Implement `ETM` for general purpose timers (#1274) - `interrupt::enable` now has a direct CPU enable counter part, `interrupt::enable_direct` (#1310) - `Delay::delay(time: fugit::MicrosDurationU64)` @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32 & ESP32-S2: Fix I²C frequency (#1306) - UART's TX/RX FIFOs are now cleared during initialization (#1344) - Fixed `LCD_CAM I8080` driver potentially sending garbage to display (#1301) - +- The TWAI driver can now be used without requiring the `embedded-hal` traits (#1355) ### Changed diff --git a/esp-hal/src/twai/filter.rs b/esp-hal/src/twai/filter.rs index 964775846..36aac7beb 100644 --- a/esp-hal/src/twai/filter.rs +++ b/esp-hal/src/twai/filter.rs @@ -1,12 +1,9 @@ -//! # Two-wire Automotive Interface (TWAI) Filters +//! Two-wire Automotive Interface (TWAI) Filters //! //! These are acceptance filters that limit which packets are received by the //! TWAI peripheral. -#[cfg(feature = "embedded-hal")] -use embedded_can::{ExtendedId, StandardId}; -#[cfg(not(feature = "embedded-hal"))] // FIXME -use embedded_hal_02::can::{ExtendedId, StandardId}; +use super::{ExtendedId, StandardId}; #[derive(Debug, PartialEq, Eq)] pub enum FilterType { @@ -14,6 +11,16 @@ pub enum FilterType { Dual, } +/// Interface for interacting with Acceptance Filters. +/// +/// The Acceptance Filter is a programmable message filtering unit that allows +/// the TWAI controller to accept or reject a received message based on the +/// message’s ID field. +/// +/// Only accepted messages will be stored in the Receive FIFO. +/// +/// The Acceptance Filter’s registers can be programmed to specify a single +/// filter, or two separate filters (dual filter mode). pub trait Filter { /// The type of the filter. const FILTER_TYPE: FilterType; @@ -27,8 +34,7 @@ pub trait Filter { pub type BitFilter = [u8; N]; -/// Internal macro used to convert a byte from a bytestring into a bit inside a -/// given code and mask. +// Convert a byte from a bytestring into a bit inside a given code and mask. macro_rules! set_bit_from_byte { ($code:expr, $mask:expr, $byte:expr, $shift:expr) => { match $byte { @@ -46,11 +52,11 @@ macro_rules! set_bit_from_byte { }; } -/// Convert a code and mask to the byte array needed at a register level. -/// -/// On the input mask, set bits (1) mean we care about the exact value of the -/// corresponding bit in the code, reset bits (0) mean the bit could be any -/// value. +// Convert a code and mask to the byte array needed at a register level. +// +// On the input mask, set bits (1) mean we care about the exact value of the +// corresponding bit in the code, reset bits (0) mean the bit could be any +// value. const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] { // Convert the filter code and mask into the full byte array needed for the // registers. @@ -66,10 +72,10 @@ const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] { ] } -/// A filter that matches against a single 11 bit id, the rtr bit, and the first +/// A filter that matches against a single 11 bit id, the RTR bit, and the first /// two bytes of the payload. /// -/// Warning: This is not a perfect filter. Extended ids that match the bit +/// Warning: This is not a perfect filter. Extended IDs that match the bit /// layout of this filter will also be accepted. pub struct SingleStandardFilter { /// The register representation of the filter. @@ -78,10 +84,10 @@ pub struct SingleStandardFilter { impl SingleStandardFilter { /// Create a new filter that matches against a single 11-bit standard id. - /// The filter can match against the packet's id, rtr bit, and first two + /// The filter can match against the packet's id, RTR bit, and first two /// bytes of the payload. /// - /// Example matching only even ids, allowing any rtr value and any payload + /// Example matching only even IDs, allowing any rtr value and any payload /// data: /// ``` /// const FILTER: SingleStandardFilter = @@ -105,7 +111,7 @@ impl SingleStandardFilter { idx += 1; } } - // Convert the rtr bit filter into the code and mask bits. + // Convert the RTR bit filter into the code and mask bits. { let shift = 20; set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift); @@ -173,7 +179,7 @@ impl SingleStandardFilter { acceptance_code |= (id_code.as_raw() as u32) << 21; acceptance_mask |= (id_mask.as_raw() as u32) << 21; - // Pack the rtr bit into the full layout. + // Pack the RTR bit into the full layout. acceptance_code |= (rtr_code as u32) << 20; acceptance_mask |= (rtr_mask as u32) << 20; @@ -193,18 +199,20 @@ impl Filter for SingleStandardFilter { self.raw } } -/// Warning: This is not a perfect filter. Standard ids that match the bit + +/// Warning: This is not a perfect filter. Standard IDs that match the bit /// layout of this filter will also be accepted. pub struct SingleExtendedFilter { raw: [u8; 8], } + impl SingleExtendedFilter { /// Create a filter that matches against a single 29-bit extended id. /// - /// The filter can match against the packet's id and the rtr bit. + /// The filter can match against the packet's id and the RTR bit. /// /// # Examples - /// A filter matching any odd extended ids, with any rtr value. + /// A filter matching any odd extended IDs, with any rtr value. /// ``` /// const FILTER: twai::filter::SingleExtendedFilter = /// twai::filter::SingleExtendedFilter::new(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1", b"x"); @@ -227,7 +235,7 @@ impl SingleExtendedFilter { idx += 1; } } - // Convert the rtr bit filter into the code and mask bits. + // Convert the RTR bit filter into the code and mask bits. { let shift = 2; set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift); @@ -258,7 +266,7 @@ impl SingleExtendedFilter { acceptance_code |= id_code.as_raw() << 3; acceptance_mask |= id_mask.as_raw() << 3; - // Pack the rtr bit into the full layout. + // Pack the RTR bit into the full layout. acceptance_code |= (rtr_code as u32) << 2; acceptance_mask |= (rtr_mask as u32) << 2; @@ -275,26 +283,27 @@ impl Filter for SingleExtendedFilter { } } -/// A filter that matches against two standard 11-bit standard ids. +/// A filter that matches against two standard 11-bit standard IDs. /// -/// The first filter part can match a packet's id, rtr bit, and the first byte -/// of the payload. The second filter part can match a packet's id and rtr bit. +/// The first filter part can match a packet's id, RTR bit, and the first byte +/// of the payload. The second filter part can match a packet's id and RTR bit. /// -/// Warning: This is not a perfect filter. Extended ids that match the bit +/// Warning: This is not a perfect filter. Extended IDs that match the bit /// layout of this filter will also be accepted. pub struct DualStandardFilter { raw: [u8; 8], } + impl DualStandardFilter { - /// Create a filter that matches against two standard 11-bit standard ids. + /// Create a filter that matches against two standard 11-bit standard IDs. /// - /// The first filter part can match a packet's id, rtr bit, and the first + /// The first filter part can match a packet's id, RTR bit, and the first /// byte of the payload. The second filter part can match a packet's id - /// and rtr bit. + /// and RTR bit. /// /// # Examples /// A filter that matches any standard id that ends with a 00 or a 11, with - /// any rtr, and with any payload on the first filter. + /// any RTR, and with any payload on the first filter. /// ``` /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new( /// b"xxxxxxxxx00", @@ -328,7 +337,7 @@ impl DualStandardFilter { idx += 1; } } - // Convert the first rtr bit filter into the code and mask bits. + // Convert the first RTR bit filter into the code and mask bits. { let shift = 20; set_bit_from_byte!(acceptance_code, acceptance_mask, first_rtr[0], shift); @@ -356,7 +365,7 @@ impl DualStandardFilter { idx += 1; } } - // Convert the second rtr bit filter into the code and mask bits. + // Convert the second RTR bit filter into the code and mask bits. { let shift = 4; set_bit_from_byte!(acceptance_code, acceptance_mask, second_rtr[0], shift); @@ -394,7 +403,7 @@ impl DualStandardFilter { acceptance_code |= (first_id_code.as_raw() as u32) << 21; acceptance_mask |= (first_id_mask.as_raw() as u32) << 21; - // Pack the rtr bit into the full layout. + // Pack the RTR bit into the full layout. acceptance_code |= (first_rtr_code as u32) << 20; acceptance_mask |= (first_rtr_mask as u32) << 20; @@ -408,7 +417,7 @@ impl DualStandardFilter { acceptance_code |= (second_id_code.as_raw() as u32) << 5; acceptance_mask |= (second_id_mask.as_raw() as u32) << 5; - // Pack the second rtr bit into the full layout. + // Pack the second RTR bit into the full layout. acceptance_code |= (second_rtr_code as u32) << 4; acceptance_mask |= (second_rtr_mask as u32) << 4; @@ -424,21 +433,22 @@ impl Filter for DualStandardFilter { self.raw } } + +/// Warning: This is not a perfect filter. Standard IDs that match the bit +/// layout of this filter will also be accepted. +/// /// NOTE: The dual extended id acceptance filters can only match "the first 16 /// bits of the 29-bit ID". -/// -/// -/// Warning: This is not a perfect filter. Standard ids that match the bit -/// layout of this filter will also be accepted. pub struct DualExtendedFilter { raw: [u8; 8], } + impl DualExtendedFilter { /// Create a filter that matches the first 16 bits of two 29-bit extended - /// ids. + /// IDs. /// /// # Examples - /// A filter that matches ids with 4 bits either set or reset in the higher + /// A filter that matches IDs with 4 bits either set or reset in the higher /// part of the id. For example this id matches: 0x000f000f, 0x000f000a, /// 0x0000000a, 0x0000000b. /// But it does not match: 0x000a000a @@ -478,7 +488,7 @@ impl DualExtendedFilter { raw: code_mask_to_register_array(acceptance_code, acceptance_mask), } } - /// Create a new filter matching the first 16 bits of two 29-bit ids. + /// Create a new filter matching the first 16 bits of two 29-bit IDs. /// /// The masks indicate which bits of the code the filter should match /// against. Set bits in the mask indicate that the corresponding bit in @@ -505,6 +515,7 @@ impl DualExtendedFilter { } } } + impl Filter for DualExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Dual; fn to_registers(&self) -> [u8; 8] { diff --git a/esp-hal/src/twai/mod.rs b/esp-hal/src/twai/mod.rs index 81813fec8..c8ba57eac 100644 --- a/esp-hal/src/twai/mod.rs +++ b/esp-hal/src/twai/mod.rs @@ -77,13 +77,6 @@ use core::marker::PhantomData; -#[cfg(feature = "embedded-hal")] -use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; -#[cfg(not(feature = "embedded-hal"))] // FIXME -use embedded_hal_02::can::{Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; -#[cfg(not(esp32c6))] -use fugit::HertzU32; - use self::filter::{Filter, FilterType}; use crate::{ clock::Clocks, @@ -95,6 +88,312 @@ use crate::{ pub mod filter; +/// CAN error kind +/// +/// This represents a common set of CAN operation errors. HAL implementations +/// are free to define more specific or additional error types. However, by +/// providing a mapping to these common CAN 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, +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), + Self::Bit => write!( + f, + "Bit value that is monitored differs from the bit value sent" + ), + Self::Stuff => write!(f, "Sixth consecutive equal bits detected"), + Self::Crc => write!(f, "Calculated CRC sequence does not equal the received one"), + Self::Form => write!( + f, + "A fixed-form bit field contains one or more illegal bits" + ), + Self::Acknowledge => write!(f, "Transmitted frame was not acknowledged"), + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From 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, + } + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::can::Error for ErrorKind { + fn kind(&self) -> embedded_hal_02::can::ErrorKind { + (*self).into() + } +} + +#[cfg(feature = "embedded-hal")] +impl From 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, + } + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_can::Error for ErrorKind { + fn kind(&self) -> embedded_can::ErrorKind { + (*self).into() + } +} + +/// Standard 11-bit CAN Identifier (`0..=0x7FF`). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct StandardId(u16); + +impl StandardId { + /// CAN ID `0`, the highest priority. + pub const ZERO: Self = StandardId(0); + + /// CAN 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 { + 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 this CAN Identifier as a raw 16-bit integer. + #[inline] + pub fn as_raw(&self) -> u16 { + self.0 + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From for embedded_hal_02::can::StandardId { + fn from(value: StandardId) -> Self { + embedded_hal_02::can::StandardId::new(value.as_raw()).unwrap() + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From for StandardId { + fn from(value: embedded_hal_02::can::StandardId) -> Self { + StandardId::new(value.as_raw()).unwrap() + } +} + +#[cfg(feature = "embedded-hal")] +impl From for embedded_can::StandardId { + fn from(value: StandardId) -> Self { + embedded_can::StandardId::new(value.as_raw()).unwrap() + } +} + +#[cfg(feature = "embedded-hal")] +impl From for StandardId { + fn from(value: embedded_can::StandardId) -> Self { + StandardId::new(value.as_raw()).unwrap() + } +} + +/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ExtendedId(u32); + +impl ExtendedId { + /// CAN ID `0`, the highest priority. + pub const ZERO: Self = ExtendedId(0); + + /// CAN 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 { + 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 this CAN 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) + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From for embedded_hal_02::can::ExtendedId { + fn from(value: ExtendedId) -> Self { + embedded_hal_02::can::ExtendedId::new(value.0).unwrap() + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From for ExtendedId { + fn from(value: embedded_hal_02::can::ExtendedId) -> Self { + ExtendedId::new(value.as_raw()).unwrap() + } +} + +#[cfg(feature = "embedded-hal")] +impl From for embedded_can::ExtendedId { + fn from(value: ExtendedId) -> Self { + embedded_can::ExtendedId::new(value.0).unwrap() + } +} + +#[cfg(feature = "embedded-hal")] +impl From for ExtendedId { + fn from(value: embedded_can::ExtendedId) -> Self { + ExtendedId::new(value.as_raw()).unwrap() + } +} + +/// A CAN Identifier (standard or extended). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Id { + /// Standard 11-bit Identifier (`0..=0x7FF`). + Standard(StandardId), + /// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`). + Extended(ExtendedId), +} + +impl From for Id { + #[inline] + fn from(id: StandardId) -> Self { + Id::Standard(id) + } +} + +impl From for Id { + #[inline] + fn from(id: ExtendedId) -> Self { + Id::Extended(id) + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From 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()), + } + } +} + +#[cfg(feature = "embedded-hal-02")] +impl From 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()), + } + } +} + +#[cfg(feature = "embedded-hal")] +impl From 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()), + } + } +} + +#[cfg(feature = "embedded-hal")] +impl From 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)] pub struct EspTwaiFrame { @@ -105,6 +404,38 @@ pub struct EspTwaiFrame { } impl EspTwaiFrame { + pub fn new(id: Id, data: &[u8]) -> Option { + // CAN2.0 frames cannot contain more than 8 bytes of data. + 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, + data: d, + dlc: data.len(), + is_remote: false, + }) + } + + pub fn new_remote(id: Id, dlc: usize) -> Option { + // CAN2.0 frames cannot have more than 8 bytes. + if dlc > 8 { + return None; + } + + Some(EspTwaiFrame { + id, + data: [0; 8], + dlc, + is_remote: true, + }) + } + /// Make a new frame from an id, pointer to the TWAI_DATA_x_REG registers, /// and the length of the data payload (dlc). /// @@ -129,36 +460,16 @@ impl EspTwaiFrame { } } -impl Frame for EspTwaiFrame { - fn new(id: impl Into, data: &[u8]) -> Option { - // CAN2.0 frames cannot contain more than 8 bytes of data. - 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, - }) +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::can::Frame for EspTwaiFrame { + fn new(id: impl Into, data: &[u8]) -> Option { + let id: embedded_hal_02::can::Id = id.into(); + Self::new(id.into(), data) } - fn new_remote(id: impl Into, dlc: usize) -> Option { - // CAN2.0 frames cannot have more than 8 bytes. - if dlc > 8 { - return None; - } - Some(EspTwaiFrame { - id: id.into(), - data: [0; 8], - dlc, - is_remote: true, - }) + fn new_remote(id: impl Into, dlc: usize) -> Option { + let id: embedded_hal_02::can::Id = id.into(); + Self::new_remote(id.into(), dlc) } fn is_extended(&self) -> bool { @@ -172,8 +483,8 @@ impl Frame for EspTwaiFrame { self.is_remote } - fn id(&self) -> Id { - self.id + fn id(&self) -> embedded_hal_02::can::Id { + self.id.into() } fn dlc(&self) -> usize { @@ -183,7 +494,48 @@ impl Frame for EspTwaiFrame { 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_frame() { + match self.is_remote { + true => &[], + false => &self.data[0..self.dlc], + } + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_can::Frame for EspTwaiFrame { + fn new(id: impl Into, data: &[u8]) -> Option { + let id: embedded_can::Id = id.into(); + Self::new(id.into(), data) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + let id: embedded_can::Id = id.into(); + Self::new_remote(id.into(), dlc) + } + + 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) -> 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], } @@ -314,7 +666,7 @@ where // 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(esp32c6))] - assert!(_clocks.apb_clock == HertzU32::MHz(80)); + assert!(_clocks.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 @@ -577,11 +929,22 @@ pub enum EspTwaiError { EmbeddedHAL(ErrorKind), } -impl Error for EspTwaiError { - fn kind(&self) -> ErrorKind { +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::can::Error for EspTwaiError { + fn kind(&self) -> embedded_hal_02::can::ErrorKind { match self { - Self::BusOff => ErrorKind::Other, - Self::EmbeddedHAL(kind) => *kind, + Self::BusOff => embedded_hal_02::can::ErrorKind::Other, + Self::EmbeddedHAL(kind) => (*kind).into(), + } + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_can::Error for EspTwaiError { + fn kind(&self) -> embedded_can::ErrorKind { + match self { + Self::BusOff => embedded_can::ErrorKind::Other, + Self::EmbeddedHAL(kind) => (*kind).into(), } } } @@ -618,7 +981,32 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl<'d, T> Can for Twai<'d, T> +#[cfg(feature = "embedded-hal-02")] +impl<'d, T> embedded_hal_02::can::Can for Twai<'d, T> +where + T: OperationInstance, +{ + type Frame = EspTwaiFrame; + type Error = EspTwaiError; + + /// Transmit a frame. + fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, 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.rx.receive() + } +} + +#[cfg(feature = "embedded-hal")] +impl<'d, T> embedded_can::nb::Can for Twai<'d, T> where T: OperationInstance, { @@ -673,9 +1061,9 @@ pub trait OperationInstance: Instance { /// Write a frame to the peripheral. fn write_frame(frame: &EspTwaiFrame) { // Assemble the frame information into the data_0 byte. - 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 frame_format: u8 = matches!(frame.id, Id::Extended(_)) 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 | dlc_bits; @@ -686,7 +1074,7 @@ pub trait OperationInstance: Instance { .write(|w| w.tx_byte_0().variant(data_0)); // Assemble the identifier information of the packet. - match frame.id() { + match frame.id { Id::Standard(id) => { let id = id.as_raw(); @@ -717,13 +1105,25 @@ pub trait OperationInstance: Instance { } // Store the data portion of the packet into the transmit buffer. - if frame.is_data_frame() { - match frame.id() { + if !frame.is_remote { + match frame.id { Id::Standard(_) => unsafe { - copy_to_data_register(register_block.data_3().as_ptr(), frame.data()) + copy_to_data_register( + register_block.data_3().as_ptr(), + match frame.is_remote { + true => &[], + false => &frame.data[0..frame.dlc], + }, + ) }, Id::Extended(_) => unsafe { - copy_to_data_register(register_block.data_5().as_ptr(), frame.data()) + copy_to_data_register( + register_block.data_5().as_ptr(), + match frame.is_remote { + true => &[], + false => &frame.data[0..frame.dlc], + }, + ) }, } } else { @@ -764,7 +1164,7 @@ pub trait OperationInstance: Instance { EspTwaiFrame::new_from_data_registers(id, register_block.data_3().as_ptr(), dlc) } } else { - EspTwaiFrame::new_remote(id, dlc).unwrap() + EspTwaiFrame::new_remote(id.into(), dlc).unwrap() } } else { // Frame uses extended 29 bit id. @@ -788,7 +1188,7 @@ pub trait OperationInstance: Instance { EspTwaiFrame::new_from_data_registers(id, register_block.data_5().as_ptr(), dlc) } } else { - EspTwaiFrame::new_remote(id, dlc).unwrap() + EspTwaiFrame::new_remote(id.into(), dlc).unwrap() } }; @@ -800,7 +1200,7 @@ pub trait OperationInstance: Instance { } } -#[cfg(any(esp32c3, esp32, esp32s2, esp32s3))] +#[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] impl Instance for crate::peripherals::TWAI0 { const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0; const NUMBER: usize = 0; @@ -834,7 +1234,7 @@ impl Instance for crate::peripherals::TWAI0 { } } -#[cfg(any(esp32c3, esp32, esp32s2, esp32s3))] +#[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] impl OperationInstance for crate::peripherals::TWAI0 {} #[cfg(esp32c6)] diff --git a/examples/src/bin/twai.rs b/examples/src/bin/twai.rs index f92d15193..9d98edc03 100644 --- a/examples/src/bin/twai.rs +++ b/examples/src/bin/twai.rs @@ -12,30 +12,19 @@ //! `IS_FIRST_SENDER` below must be set to false on one of the ESP's //% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3 -//% FEATURES: embedded-hal #![no_std] #![no_main] const IS_FIRST_SENDER: bool = true; -// Run this example with the embedded-hal feature enabled to use embedded-can instead of -// embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's -// upgrade to 1.0.0. cargo run --example twai --release -#[cfg(feature = "embedded-hal")] -use embedded_can::{nb::Can, Frame, StandardId}; -// Run this example without the embedded-hal flag to use the embedded-hal 0.2.7 CAN traits. -// cargo run --example twai --features embedded-hal-02 --release -#[cfg(feature = "embedded-hal-02")] -use embedded_hal_02::can::{Can, Frame, StandardId}; use esp_backtrace as _; use esp_hal::{ clock::ClockControl, gpio::IO, peripherals::Peripherals, prelude::*, - twai, - twai::filter::SingleStandardFilter, + twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId}, }; use esp_println::println; use nb::block; @@ -82,7 +71,7 @@ fn main() -> ! { if IS_FIRST_SENDER { // Send a frame to the other ESP - let frame = Frame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + let frame = EspTwaiFrame::new(StandardId::ZERO.into(), &[1, 2, 3]).unwrap(); block!(can.transmit(&frame)).unwrap(); println!("Sent a frame"); }