Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement partial embedded_can 0.4 compatibility (id conversions, transmit, receive) #56

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:
device_feature:
- fdcan_g0_g4_l5
- fdcan_h7
embedded_can_feature:
- "embedded-can-04"
- ""
steps:
- uses: actions/checkout@v2
- name: Cache cargo registry and index
Expand All @@ -54,12 +57,12 @@ jobs:
with:
use-cross: true
command: build
args: --verbose --release --target ${{ env.TARGET }} --features ${{ matrix.device_feature }}
args: --verbose --release --target ${{ env.TARGET }} --features ${{ matrix.device_feature }},${{ matrix.embedded_can_feature}}
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: test
args: --verbose --features ${{ matrix.device_feature }}
args: --verbose --features ${{ matrix.device_feature }},${{ matrix.embedded_can_feature}}

lint:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

* Update MSRV to 1.60
* Add `embedded-can-04` feature flag which enables conversions from and into `embedded_can::Id` types, and compatible receive and transmit functions.

## [v0.2.1] 2024-09-04

Expand Down
12 changes: 4 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,18 @@ features = ["fdcan_g0_g4_l5"]
targets = ["thumbv7em-none-eabihf"]

[features]
fdcan_g0_g4_l5 = [] # Peripheral map found on G0 G4 L5
fdcan_h7 = [] # Peripheral map found on H7
fdcan_g0_g4_l5 = [] # Peripheral map found on G0 G4 L5
fdcan_h7 = [] # Peripheral map found on H7
embedded-can-04 = ["embedded-can"] # Implement embedded-can 0.4 compatible traits

[dependencies]
bitflags = "1.3.2"
embedded-can = { version = "0.4", optional = true }
paste = "1.0"
vcell = "0.1.3"
nb = "1.0.0"
static_assertions = "1.1"
volatile-register = "0.2.1"

[dependencies.embedded-can-03]
version = "0.3"
optional = true
package = "embedded-can"


[profile.test]
opt-level = "s"
17 changes: 17 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@ impl From<TxFrameHeader> for IdReg {
}
}

#[cfg(feature = "embedded-can-04")]
impl<P> From<&P> for TxFrameHeader
where
P: embedded_can::Frame,
{
fn from(value: &P) -> Self {
TxFrameHeader {
len: value.dlc() as u8,
id: value.id().into(),
// Note: embedded_can::Frame is only CAN 2.0 thus far
frame_format: FrameFormat::Standard,
bit_rate_switching: false,
marker: None,
}
}
}

pub(crate) trait MergeTxFrameHeader {
fn merge(&self, header: TxFrameHeader);
}
Expand Down
73 changes: 73 additions & 0 deletions src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,37 @@ impl StandardId {
self.0
}
}

impl From<StandardId> for IdType {
fn from(_id: StandardId) -> Self {
IdType::StandardId
}
}

#[cfg(feature = "embedded-can-04")]
impl From<embedded_can::StandardId> for StandardId
{
#[inline]
fn from(value: embedded_can::StandardId) -> Self {
// unsafe: embedded_can range check is the same as here
unsafe {
Self::new_unchecked(value.as_raw())
}
}
}

#[cfg(feature = "embedded-can-04")]
impl Into<embedded_can::StandardId> for StandardId
{
#[inline]
fn into(self) -> embedded_can::StandardId {
// unsafe: embedded_can range check is the same as here
unsafe {
embedded_can::StandardId::new_unchecked(self.as_raw())
}
}
}

/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ExtendedId(u32);
Expand Down Expand Up @@ -103,6 +128,30 @@ impl From<ExtendedId> for IdType {
}
}

#[cfg(feature = "embedded-can-04")]
impl From<embedded_can::ExtendedId> for ExtendedId
{
#[inline]
fn from(value: embedded_can::ExtendedId) -> Self {
// Safety: embedded_can range check is the same as here
unsafe {
Self::new_unchecked(value.as_raw())
}
}
}

#[cfg(feature = "embedded-can-04")]
impl Into<embedded_can::ExtendedId> for ExtendedId
{
#[inline]
fn into(self) -> embedded_can::ExtendedId {
// Safety: embedded_can range check is the same as here
unsafe {
embedded_can::ExtendedId::new_unchecked(self.as_raw())
}
}
}

/// A CAN Identifier (standard or extended).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Id {
Expand All @@ -127,6 +176,30 @@ impl From<ExtendedId> for Id {
}
}

#[cfg(feature = "embedded-can-04")]
impl From<embedded_can::Id> for Id
{
#[inline]
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()),
}
}
}

#[cfg(feature = "embedded-can-04")]
impl Into<embedded_can::Id> for Id
{
#[inline]
fn into(self) -> embedded_can::Id {
match self {
Id::Standard(id) => embedded_can::Id::Standard(id.into()),
Id::Extended(id) => embedded_can::Id::Extended(id.into()),
}
}
}

impl From<Id> for IdType {
#[inline]
fn from(id: Id) -> Self {
Expand Down
117 changes: 111 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//!
//! | Feature | Description |
//! |---------|-------------|
//! | `embedded-can-03` | ~Implements the [`embedded-can`] 0.3 traits.~ |
//! | `embedded-can-04` | Enables conversions from and into [`embedded_can`] 0.4 CAN ID types, and compatible receive transmit functions. Note that only classic CAN is currently supported in `embedded_can`. |
//!
//! [`embedded-can`]: https://docs.rs/embedded-can

Expand All @@ -28,8 +28,6 @@ pub use crate::pac::fdcan::RegisterBlock;

/// Configuration of an FDCAN instance
pub mod config;
// #[cfg(feature = "embedded-can-03")] REVERT ME: embedded-can support
// mod embedded_can;
/// Filtering of CAN Messages
pub mod filter;
/// Header and info of transmitted and receiving frames
Expand All @@ -54,7 +52,7 @@ use filter::{
StandardFilterSlot, EXTENDED_FILTER_MAX, STANDARD_FILTER_MAX,
};
use frame::MergeTxFrameHeader;
use frame::{RxFrameInfo, TxFrameHeader};
use frame::{FrameFormat, RxFrameInfo, TxFrameHeader};
use id::{Id, IdReg};
use interrupt::{Interrupt, InterruptLine, Interrupts};

Expand Down Expand Up @@ -1060,6 +1058,33 @@ where
unsafe { Tx::<I, M>::conjure().transmit_preserve(frame, buffer, pending) }
}

/// Puts an embedded_can Frame in a free transmit mailbox for transmission on the bus.
#[cfg(feature = "embedded-can-04")]
pub fn transmit_frame(
&mut self,
frame: &impl embedded_can::Frame
) -> nb::Result<Option<()>, Infallible> {
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Tx::<I, M>::conjure().transmit_frame(frame) }
}

/// Puts an embedded_can Frame in a free transmit mailbox for transmission on the bus.
///
/// Implements the same `pending` behaviour as `transmit_preserve`.
#[cfg(feature = "embedded-can-04")]
pub fn transmit_preserve_frame<F, PTX, P>(
&mut self,
frame: &F,
pending: &mut PTX,
) -> nb::Result<Option<P>, Infallible>
where
F: embedded_can::Frame,
PTX: FnMut(Mailbox, TxFrameHeader, &[u32]) -> P,
{
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Tx::<I, M>::conjure().transmit_preserve_frame(frame, pending) }
}

/// Returns `true` if no frame is pending for transmission.
#[inline]
pub fn is_transmitter_idle(&self) -> bool {
Expand Down Expand Up @@ -1115,6 +1140,36 @@ where
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Rx::<I, M, Fifo1>::conjure().receive(buffer) }
}

/// Returns a received Classic CAN frame of a given type from FIFO_0 if available.
///
/// # Panics
///
/// Panics if an `CAN-FD` frame is received, as embedded_can::Frame has no FD support.
#[cfg(feature = "embedded-can-04")]
#[inline]
pub fn receive0_frame<F>(&mut self) -> nb::Result<ReceiveOverrun<F>, Infallible>
where
F: embedded_can::Frame,
{
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Rx::<I, M, Fifo0>::conjure().receive_frame() }
}

/// Returns a received Classic CAN frame of a given type from FIFO_1 if available.
///
/// # Panics
///
/// Panics if an `CAN-FD` frame is received, as embedded_can::Frame has no FD support.
#[cfg(feature = "embedded-can-04")]
#[inline]
pub fn receive1_frame<F>(&mut self) -> nb::Result<ReceiveOverrun<F>, Infallible>
where
F: embedded_can::Frame,
{
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Rx::<I, M, Fifo1>::conjure().receive_frame() }
}
}

/// FdCanControl Struct
Expand Down Expand Up @@ -1298,6 +1353,31 @@ where
Ok(pending_frame)
}

/// Puts an embedded_can Frame in a free transmit mailbox for transmission on the bus.
#[cfg(feature = "embedded-can-04")]
pub fn transmit_frame(
&mut self,
frame: &impl embedded_can::Frame
) -> nb::Result<Option<()>, Infallible> {
self.transmit(frame.into(), frame.data())
}

/// Puts an embedded_can Frame in a free transmit mailbox for transmission on the bus.
///
/// Implements the same `pending` behaviour as `transmit_preserve`.
#[cfg(feature = "embedded-can-04")]
pub fn transmit_preserve_frame<F, PTX, P>(
&mut self,
frame: &F,
pending: &mut PTX,
) -> nb::Result<Option<P>, Infallible>
where
F: embedded_can::Frame,
PTX: FnMut(Mailbox, TxFrameHeader, &[u32]) -> P,
{
self.transmit_preserve(frame.into(), frame.data(), pending)
}

/// Returns if the tx queue is able to accept new messages without having to cancel an existing one
#[inline]
pub fn tx_queue_is_full(&self) -> bool {
Expand Down Expand Up @@ -1517,8 +1597,6 @@ where

/// Returns a received frame if available.
///
/// Returns `Err` when a frame was lost due to buffer overrun.
///
/// # Panics
///
/// Panics if `buffer` is smaller than the header length.
Expand Down Expand Up @@ -1557,6 +1635,33 @@ where
}
}

/// Returns a received Classic CAN frame of a given type if available.
///
/// # Panics
///
/// Panics if an `CAN-FD` frame is received, as embedded_can::Frame has no FD support.
#[cfg(feature = "embedded-can-04")]
pub fn receive_frame<F>(&mut self) -> nb::Result<ReceiveOverrun<F>, Infallible>
where
F: embedded_can::Frame,
{
let mut buffer = [0_u8; 8];
let overrun = self.receive(&mut buffer)?;
let info = overrun.unwrap();
if info.frame_format != FrameFormat::Standard {
panic!("Received CAN-FD frame");
}
let frame = if info.rtr {
F::new_remote(info.id, info.len as usize)
} else {
F::new(info.id, &buffer[..info.len as usize])
}.unwrap();
match overrun {
ReceiveOverrun::NoOverrun(_) => Ok(ReceiveOverrun::NoOverrun(frame)),
ReceiveOverrun::Overrun(_) => Ok(ReceiveOverrun::Overrun(frame)),
}
}

#[inline]
fn registers(&self) -> &RegisterBlock {
unsafe { &*I::REGISTERS }
Expand Down