mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 12:50:53 +00:00
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:
parent
d5abc3fc85
commit
7d5372f57d
@ -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
|
||||
|
||||
|
@ -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<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] {
|
||||
|
@ -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;
|
||||
#[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)
|
||||
}
|
||||
|
||||
let mut d: [u8; 8] = [0; 8];
|
||||
let (left, _unused) = d.split_at_mut(data.len());
|
||||
left.clone_from_slice(data);
|
||||
|
||||
Some(EspTwaiFrame {
|
||||
id: id.into(),
|
||||
data: d,
|
||||
dlc: data.len(),
|
||||
is_remote: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self> {
|
||||
// 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)]
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user