mcp2517/
message.rs

1//!# CAN Message
2//! This library supports both CAN2.0 (up to 8 data bytes per CAN Frame)
3//! and CAN FD (up to 64 data bytes per CAN frame)
4//! formats with both standard and extended frame ID formats
5//!
6//! ## CAN 2.0 message construction example
7//! ```
8//!# use bytes::Bytes;
9//!# use mcp2517::message::{Can20,TxMessage};
10//!# use embedded_can::{Id,StandardId};
11//!#
12//! // Frame ID
13//! let message_id = Id::Standard(StandardId::new(0x123).unwrap());
14//! // Set message type to CAN2.0 with a maximum of 4 data bytes
15//! let message_type = Can20::<4>{};
16//! // Create payload buffer of 3 data bytes. DLC determined by length of payload buffer.
17//! let payload = [0x1, 0x2, 0x3];
18//! // Create Bytes object
19//! let bytes = Bytes::copy_from_slice(&payload);
20//! // Create message object
21//! let tx_message = TxMessage::new(message_type,bytes,message_id).unwrap();
22//!```
23//! ## CAN FD message construction example
24//! ```
25//!# use bytes::Bytes;
26//!# use mcp2517::message::{CanFd,TxMessage};
27//!# use embedded_can::{Id,StandardId};
28//!#
29//! // Frame ID
30//! let message_id = Id::Standard(StandardId::new(0x123).unwrap());
31//! // Set message type to CANfd with a max of 24 data bytes with bit rate switch enabled
32//! let message_type = CanFd::<24>{bitrate_switch: true};
33//! // Create payload buffer with 22 data bytes (here DLC will be 24 because 22 is not a supported DLC code)
34//! let payload = [0u8;22];
35//! // Create Bytes object
36//! let bytes = Bytes::copy_from_slice(&payload);
37//! // Create message object
38//! let tx_message = TxMessage::new(message_type,bytes,message_id).unwrap();
39//! ```
40
41use bytes::Bytes;
42use embedded_can::{ExtendedId, Id, StandardId};
43use log::debug;
44use modular_bitfield_msb::prelude::*;
45
46pub const STANDARD_IDENTIFIER_MASK: u16 = 0x7FF;
47
48pub const EXTENDED_IDENTIFIER_MASK: u32 = 0x3FFFF;
49
50pub const MAX_PAYLOAD_CAN_2_0: usize = 8;
51
52pub const MAX_PAYLOAD_CAN_FD: usize = 64;
53
54/// Data length code
55#[derive(BitfieldSpecifier, Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
56#[allow(clippy::upper_case_acronyms)]
57#[bits = 4]
58pub enum DLC {
59    Zero,
60    One,
61    Two,
62    Three,
63    Four,
64    Five,
65    Six,
66    Seven,
67    Eight,
68    Twelve,
69    Sixteen,
70    Twenty,
71    TwentyFour,
72    ThirtyTwo,
73    FortyEight,
74    SixtyFour,
75}
76
77/// Possible errors when creating a [TxMessage] object
78#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
79pub enum MessageError {
80    /// Payload length invalid
81    InvalidLength(usize),
82    /// Message type Length argument not divisble by 4
83    InvalidTypeSize(usize),
84}
85
86impl DLC {
87    fn from_length(value: usize) -> Result<Self, MessageError> {
88        match value {
89            0 => Ok(Self::Zero),
90            1 => Ok(Self::One),
91            2 => Ok(Self::Two),
92            3 => Ok(Self::Three),
93            4 => Ok(Self::Four),
94            5 => Ok(Self::Five),
95            6 => Ok(Self::Six),
96            7 => Ok(Self::Seven),
97            8 => Ok(Self::Eight),
98            12 => Ok(Self::Twelve),
99            16 => Ok(Self::Sixteen),
100            20 => Ok(Self::Twenty),
101            24 => Ok(Self::TwentyFour),
102            32 => Ok(Self::ThirtyTwo),
103            48 => Ok(Self::FortyEight),
104            64 => Ok(Self::SixtyFour),
105            val => Err(MessageError::InvalidLength(val)),
106        }
107    }
108}
109
110/// Transmit message object header
111#[bitfield(bits = 64)]
112#[derive(BitfieldSpecifier, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
113pub struct TxHeader {
114    // T0
115    #[skip]
116    __: B2,
117    /// standard ID in FD mode can be extended to 12 bits if sid11 is set
118    pub sid11: bool,
119    /// 18 lsb of extended ID
120    pub extended_identifier: B18,
121    /// standard ID bits or msb 11 bits of extended ID
122    pub standard_identifier: B11,
123    // T1
124    #[skip]
125    __: B16,
126    /// Sequence keeping track of transmitted messages in Transmit Event FIFO
127    pub sequence: B7,
128    /// In normal ESI mode, set if node is error passive, cleared if node is error active
129    pub error_status_indicator: bool,
130    /// Bit distinguishing between CAN and CAN FD formats
131    pub fd_frame: bool,
132    /// Enables bit rate switching in CAN FD frames
133    pub bit_rate_switch: bool,
134    /// Set if the frame is a RTR frame
135    pub remote_transmission_request: bool,
136    /// Set if extended ID is used
137    pub identifier_extension_flag: bool,
138    /// 4 bits identifying the payload length
139    pub data_length_code: DLC,
140}
141
142pub trait MessageType<const L: usize> {
143    /// Setup CAN message header depending on message type
144    fn setup_header(&self, header: &mut TxHeader, payload_length: usize) -> Result<(), MessageError>;
145}
146
147/// CAN 2.0 message type where `L` is the max number of payload bytes.
148/// For CAN2.0, `L` can either be 4 or 8.
149#[derive(Debug, Copy, Clone)]
150pub struct Can20<const L: usize> {}
151
152impl<const L: usize> MessageType<L> for Can20<L> {
153    fn setup_header(&self, _header: &mut TxHeader, payload_length: usize) -> Result<(), MessageError> {
154        if L > 8 || payload_length > 8 {
155            let max = payload_length.max(L);
156            debug!("Maximum of 64 bytes allowed. Current size: {max} bytes");
157            return Err(MessageError::InvalidLength(max));
158        }
159
160        if payload_length > L {
161            debug!("Payload length {payload_length} must be less than or equal {L}");
162            return Err(MessageError::InvalidLength(payload_length));
163        }
164
165        if L % 4 != 0 {
166            debug!("CAN2.0 generic argument must be 4 or 8");
167            return Err(MessageError::InvalidTypeSize(L));
168        }
169
170        Ok(())
171    }
172}
173/// CAN FD message type where `L` is the max number of payload bytes.
174/// `L` must be a multiple of 4 and `L` >= actual payload length in bytes
175#[derive(Debug, Copy, Clone)]
176pub struct CanFd<const L: usize> {
177    pub bitrate_switch: bool,
178}
179
180impl<const L: usize> MessageType<L> for CanFd<L> {
181    fn setup_header(&self, header: &mut TxHeader, payload_length: usize) -> Result<(), MessageError> {
182        if L > 64 || payload_length > 64 {
183            let max = payload_length.max(L);
184            debug!("Maximum of 64 bytes allowed. Current size: {max} bytes");
185            return Err(MessageError::InvalidLength(max));
186        }
187
188        if payload_length > L {
189            debug!("Payload length {payload_length} must be less than or equal {L}");
190            return Err(MessageError::InvalidLength(payload_length));
191        }
192
193        if L % 4 != 0 {
194            debug!("CANFD generic argument must be a multiple of 4");
195            return Err(MessageError::InvalidTypeSize(L));
196        }
197
198        header.set_bit_rate_switch(self.bitrate_switch);
199        header.set_fd_frame(true);
200        Ok(())
201    }
202}
203
204/// Transmit Message Object
205#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
206pub struct TxMessage<T: MessageType<L>, const L: usize> {
207    /// first 8 bytes of Transmit Message Object representing header
208    pub(crate) header: TxHeader,
209    /// Payload bytes of Message Object
210    pub(crate) buff: Bytes,
211    /// CAN message type (CAN 2.0 or CAN FD)
212    pub(crate) message_type: T,
213}
214
215impl<T: MessageType<L>, const L: usize> TxMessage<T, L> {
216    /// Create new CAN message
217    pub fn new(message_type: T, data: Bytes, identifier: Id) -> Result<Self, MessageError> {
218        let mut header = TxHeader::new();
219
220        let mut payload_length = data.len();
221
222        message_type.setup_header(&mut header, payload_length)?;
223
224        // length used to choose the next supported DLC
225        while let Err(MessageError::InvalidLength(_)) = DLC::from_length(payload_length) {
226            payload_length += 1;
227        }
228
229        header.set_data_length_code(DLC::from_length(payload_length)?);
230
231        match identifier {
232            Id::Standard(sid) => header.set_standard_identifier(sid.as_raw()),
233            Id::Extended(eid) => {
234                header.set_extended_identifier(eid.as_raw() & EXTENDED_IDENTIFIER_MASK);
235                header.set_standard_identifier((eid.as_raw() >> 18) as u16 & STANDARD_IDENTIFIER_MASK);
236                header.set_identifier_extension_flag(true);
237            }
238        }
239
240        Ok(TxMessage {
241            header,
242            buff: data,
243            message_type,
244        })
245    }
246
247    /// Returns payload as a `&[u8]`
248    pub fn get_payload(&self) -> &[u8] {
249        self.buff.as_ref()
250    }
251
252    /// Returns Header register of Transmit Message Object
253    pub fn get_header(&self) -> &TxHeader {
254        &self.header
255    }
256}
257
258/// Receive message object header
259#[bitfield(bits = 64)]
260#[derive(Default, PartialEq, Eq, Debug)]
261#[repr(u64)]
262pub struct RxHeader {
263    // R0
264    #[skip]
265    __: B2,
266    /// In FD mode the standard ID can be extended to 12 bit using r1
267    sid11: bool,
268    /// Extended Identifier
269    extended_identifier: B18,
270    /// Standard Identifier
271    standard_identifier: B11,
272    #[skip]
273    __: B16,
274    /// Filter Hit, number of filter that matched
275    filter_hit: B5,
276    #[skip]
277    __: B2,
278    /// Error Status Indicator
279    error_status_indicator: bool,
280    /// FD Frame; distinguishes between CAN and CAN FD formats
281    fd_frame: bool,
282    /// Bit Rate Switch; indicates if data bit rate was switched
283    bit_rate_switch: bool,
284    /// Remote Transmission Request; not used in CAN FD
285    remote_transmission_request: bool,
286    /// Identifier Extension Flag; distinguishes between base and extended format
287    identifier_extension_flag: bool,
288    /// Data Length Code
289    data_length_code: DLC,
290}
291
292impl RxHeader {
293    fn get_id(&self) -> Id {
294        if self.identifier_extension_flag() {
295            let id = ((self.standard_identifier() as u32) << 18) | (self.extended_identifier());
296            let extended_id = ExtendedId::new(id);
297            Id::Extended(extended_id.unwrap())
298        } else {
299            let id = StandardId::new(self.standard_identifier());
300            Id::Standard(id.unwrap())
301        }
302    }
303
304    #[cfg(test)]
305    pub fn new_test_cfg(identifier: Id) -> Self {
306        match identifier {
307            Id::Extended(eid) => Self::new()
308                .with_data_length_code(DLC::Eight)
309                .with_standard_identifier((eid.as_raw() >> 18) as u16 & STANDARD_IDENTIFIER_MASK)
310                .with_extended_identifier(eid.as_raw() & EXTENDED_IDENTIFIER_MASK)
311                .with_identifier_extension_flag(true),
312            Id::Standard(sid) => Self::new()
313                .with_data_length_code(DLC::Eight)
314                .with_standard_identifier(sid.as_raw()),
315        }
316    }
317}