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`
This commit is contained in:
Jesse Braham 2024-03-28 14:44:09 +00:00 committed by GitHub
parent d5abc3fc85
commit 7d5372f57d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 513 additions and 113 deletions

View File

@ -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

View File

@ -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
/// messages ID field.
///
/// Only accepted messages will be stored in the Receive FIFO.
///
/// The Acceptance Filters 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<const N: usize> = [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] {

View File

@ -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<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,
}
}
}
#[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<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,
}
}
}
#[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<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 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<StandardId> 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<embedded_hal_02::can::StandardId> for StandardId {
fn from(value: embedded_hal_02::can::StandardId) -> Self {
StandardId::new(value.as_raw()).unwrap()
}
}
#[cfg(feature = "embedded-hal")]
impl From<StandardId> for embedded_can::StandardId {
fn from(value: StandardId) -> Self {
embedded_can::StandardId::new(value.as_raw()).unwrap()
}
}
#[cfg(feature = "embedded-hal")]
impl From<embedded_can::StandardId> 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<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 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<ExtendedId> 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<embedded_hal_02::can::ExtendedId> for ExtendedId {
fn from(value: embedded_hal_02::can::ExtendedId) -> Self {
ExtendedId::new(value.as_raw()).unwrap()
}
}
#[cfg(feature = "embedded-hal")]
impl From<ExtendedId> for embedded_can::ExtendedId {
fn from(value: ExtendedId) -> Self {
embedded_can::ExtendedId::new(value.0).unwrap()
}
}
#[cfg(feature = "embedded-hal")]
impl From<embedded_can::ExtendedId> 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<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)
}
}
#[cfg(feature = "embedded-hal-02")]
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()),
}
}
}
#[cfg(feature = "embedded-hal-02")]
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()),
}
}
}
#[cfg(feature = "embedded-hal")]
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()),
}
}
}
#[cfg(feature = "embedded-hal")]
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)]
pub struct EspTwaiFrame {
@ -105,6 +404,38 @@ pub struct EspTwaiFrame {
}
impl EspTwaiFrame {
pub fn new(id: Id, data: &[u8]) -> Option<Self> {
// 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<Self> {
// 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<Id>, data: &[u8]) -> Option<Self> {
// 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<embedded_hal_02::can::Id>, data: &[u8]) -> Option<Self> {
let id: embedded_hal_02::can::Id = id.into();
Self::new(id.into(), data)
}
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self> {
// 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<embedded_hal_02::can::Id>, dlc: usize) -> Option<Self> {
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<embedded_can::Id>, data: &[u8]) -> Option<Self> {
let id: embedded_can::Id = id.into();
Self::new(id.into(), data)
}
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {
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<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()
}
}
#[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)]

View File

@ -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");
}