1 | //! Definition for CAN Frames |
2 | use bit_field::BitField; |
3 | |
4 | use crate::can::enums::FrameCreateError; |
5 | |
6 | /// Calculate proper timestamp when available. |
7 | #[cfg (feature = "time" )] |
8 | pub type Timestamp = embassy_time::Instant; |
9 | |
10 | /// Raw register timestamp |
11 | #[cfg (not(feature = "time" ))] |
12 | pub type Timestamp = u16; |
13 | |
14 | /// CAN Header, without meta data |
15 | #[derive (Debug, Copy, Clone)] |
16 | pub struct Header { |
17 | id: embedded_can::Id, |
18 | len: u8, |
19 | flags: u8, |
20 | } |
21 | |
22 | #[cfg (feature = "defmt" )] |
23 | impl defmt::Format for Header { |
24 | fn format(&self, fmt: defmt::Formatter<'_>) { |
25 | match self.id() { |
26 | embedded_can::Id::Standard(id: &StandardId) => { |
27 | defmt::write!(fmt, "Can Standard ID={:x} len={}" , id.as_raw(), self.len,) |
28 | } |
29 | embedded_can::Id::Extended(id: &ExtendedId) => { |
30 | defmt::write!(fmt, "Can Extended ID={:x} len={}" , id.as_raw(), self.len,) |
31 | } |
32 | } |
33 | } |
34 | } |
35 | |
36 | impl Header { |
37 | const FLAG_RTR: usize = 0; // Remote |
38 | const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN |
39 | const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN |
40 | |
41 | /// Create new CAN Header |
42 | pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header { |
43 | let mut flags = 0u8; |
44 | flags.set_bit(Self::FLAG_RTR, rtr); |
45 | Header { id, len, flags } |
46 | } |
47 | |
48 | /// Create new CAN FD Header |
49 | pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header { |
50 | let mut flags = 0u8; |
51 | flags.set_bit(Self::FLAG_RTR, rtr); |
52 | flags.set_bit(Self::FLAG_FDCAN, true); |
53 | flags.set_bit(Self::FLAG_BRS, brs); |
54 | Header { id, len, flags } |
55 | } |
56 | |
57 | /// Return ID |
58 | pub fn id(&self) -> &embedded_can::Id { |
59 | &self.id |
60 | } |
61 | |
62 | /// Return length as u8 |
63 | pub fn len(&self) -> u8 { |
64 | self.len |
65 | } |
66 | |
67 | /// Is remote frame |
68 | pub fn rtr(&self) -> bool { |
69 | self.flags.get_bit(Self::FLAG_RTR) |
70 | } |
71 | |
72 | /// Request/is FDCAN frame |
73 | pub fn fdcan(&self) -> bool { |
74 | self.flags.get_bit(Self::FLAG_FDCAN) |
75 | } |
76 | |
77 | /// Request/is Flexible Data Rate |
78 | pub fn bit_rate_switching(&self) -> bool { |
79 | self.flags.get_bit(Self::FLAG_BRS) |
80 | } |
81 | |
82 | /// Get priority of frame |
83 | pub(crate) fn priority(&self) -> u32 { |
84 | match self.id() { |
85 | embedded_can::Id::Standard(id) => (id.as_raw() as u32) << 18, |
86 | embedded_can::Id::Extended(id) => id.as_raw(), |
87 | } |
88 | } |
89 | } |
90 | |
91 | /// Trait for FDCAN frame types, providing ability to construct from a Header |
92 | /// and to retrieve the Header from a frame |
93 | pub trait CanHeader: Sized { |
94 | /// Construct frame from header and payload |
95 | fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError>; |
96 | |
97 | /// Get this frame's header struct |
98 | fn header(&self) -> &Header; |
99 | } |
100 | |
101 | /// Payload of a classic CAN data frame. |
102 | /// |
103 | /// Contains 0 to 8 Bytes of data. |
104 | #[derive (Debug, Copy, Clone)] |
105 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
106 | pub struct ClassicData { |
107 | pub(crate) bytes: [u8; Self::MAX_DATA_LEN], |
108 | } |
109 | |
110 | impl ClassicData { |
111 | pub(crate) const MAX_DATA_LEN: usize = 8; |
112 | /// Creates a data payload from a raw byte slice. |
113 | /// |
114 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or |
115 | /// cannot be represented with an FDCAN DLC. |
116 | pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> { |
117 | if data.len() > 8 { |
118 | return Err(FrameCreateError::InvalidDataLength); |
119 | } |
120 | |
121 | let mut bytes = [0; 8]; |
122 | bytes[..data.len()].copy_from_slice(data); |
123 | |
124 | Ok(Self { bytes }) |
125 | } |
126 | |
127 | /// Raw read access to data. |
128 | pub fn raw(&self) -> &[u8] { |
129 | &self.bytes |
130 | } |
131 | |
132 | /// Checks if the length can be encoded in FDCAN DLC field. |
133 | pub const fn is_valid_len(len: usize) -> bool { |
134 | match len { |
135 | 0..=8 => true, |
136 | _ => false, |
137 | } |
138 | } |
139 | |
140 | /// Creates an empty data payload containing 0 bytes. |
141 | #[inline ] |
142 | pub const fn empty() -> Self { |
143 | Self { bytes: [0; 8] } |
144 | } |
145 | } |
146 | |
147 | /// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN |
148 | /// For CAN-FD support use FdFrame |
149 | #[derive (Debug, Copy, Clone)] |
150 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
151 | pub struct Frame { |
152 | can_header: Header, |
153 | data: ClassicData, |
154 | } |
155 | |
156 | impl Frame { |
157 | /// Create a new CAN classic Frame |
158 | pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
159 | let data = ClassicData::new(raw_data)?; |
160 | Ok(Frame { can_header, data: data }) |
161 | } |
162 | |
163 | /// Creates a new data frame. |
164 | pub fn new_data(id: impl Into<embedded_can::Id>, data: &[u8]) -> Result<Self, FrameCreateError> { |
165 | let eid: embedded_can::Id = id.into(); |
166 | let header = Header::new(eid, data.len() as u8, false); |
167 | Self::new(header, data) |
168 | } |
169 | |
170 | /// Create new extended frame |
171 | pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
172 | if let Some(id) = embedded_can::ExtendedId::new(raw_id) { |
173 | Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data) |
174 | } else { |
175 | Err(FrameCreateError::InvalidCanId) |
176 | } |
177 | } |
178 | |
179 | /// Create new standard frame |
180 | pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
181 | if let Some(id) = embedded_can::StandardId::new(raw_id) { |
182 | Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data) |
183 | } else { |
184 | Err(FrameCreateError::InvalidCanId) |
185 | } |
186 | } |
187 | |
188 | /// Create new remote frame |
189 | pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Result<Self, FrameCreateError> { |
190 | if len <= 8usize { |
191 | Self::new(Header::new(id.into(), len as u8, true), &[0; 8]) |
192 | } else { |
193 | Err(FrameCreateError::InvalidDataLength) |
194 | } |
195 | } |
196 | |
197 | /// Get reference to data |
198 | pub fn header(&self) -> &Header { |
199 | &self.can_header |
200 | } |
201 | |
202 | /// Return ID |
203 | pub fn id(&self) -> &embedded_can::Id { |
204 | &self.can_header.id |
205 | } |
206 | |
207 | /// Get reference to data |
208 | pub fn data(&self) -> &[u8] { |
209 | &self.data.raw() |
210 | } |
211 | |
212 | /// Get priority of frame |
213 | pub fn priority(&self) -> u32 { |
214 | self.header().priority() |
215 | } |
216 | } |
217 | |
218 | impl embedded_can::Frame for Frame { |
219 | fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> { |
220 | let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data); |
221 | match frameopt { |
222 | Ok(frame) => Some(frame), |
223 | Err(_) => None, |
224 | } |
225 | } |
226 | fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { |
227 | if len <= 8 { |
228 | let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]); |
229 | match frameopt { |
230 | Ok(frame) => Some(frame), |
231 | Err(_) => None, |
232 | } |
233 | } else { |
234 | None |
235 | } |
236 | } |
237 | fn is_extended(&self) -> bool { |
238 | match self.can_header.id { |
239 | embedded_can::Id::Extended(_) => true, |
240 | embedded_can::Id::Standard(_) => false, |
241 | } |
242 | } |
243 | fn is_remote_frame(&self) -> bool { |
244 | self.can_header.rtr() |
245 | } |
246 | fn id(&self) -> embedded_can::Id { |
247 | self.can_header.id |
248 | } |
249 | fn dlc(&self) -> usize { |
250 | self.can_header.len as usize |
251 | } |
252 | fn data(&self) -> &[u8] { |
253 | &self.data.raw() |
254 | } |
255 | } |
256 | |
257 | impl CanHeader for Frame { |
258 | fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> { |
259 | Self::new(header, data) |
260 | } |
261 | |
262 | fn header(&self) -> &Header { |
263 | self.header() |
264 | } |
265 | } |
266 | |
267 | /// Contains CAN frame and additional metadata. |
268 | /// |
269 | /// Timestamp is available if `time` feature is enabled. |
270 | /// For CAN-FD support use FdEnvelope |
271 | #[derive (Debug, Clone)] |
272 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
273 | pub struct Envelope { |
274 | /// Reception time. |
275 | pub ts: Timestamp, |
276 | /// The actual CAN frame. |
277 | pub frame: Frame, |
278 | } |
279 | |
280 | impl Envelope { |
281 | /// Convert into a tuple |
282 | pub fn parts(self) -> (Frame, Timestamp) { |
283 | (self.frame, self.ts) |
284 | } |
285 | } |
286 | |
287 | /// Payload of a (FD)CAN data frame. |
288 | /// |
289 | /// Contains 0 to 64 Bytes of data. |
290 | #[derive (Debug, Copy, Clone)] |
291 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
292 | pub struct FdData { |
293 | pub(crate) bytes: [u8; 64], |
294 | } |
295 | |
296 | impl FdData { |
297 | /// Creates a data payload from a raw byte slice. |
298 | /// |
299 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or |
300 | /// cannot be represented with an FDCAN DLC. |
301 | pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> { |
302 | if !FdData::is_valid_len(data.len()) { |
303 | return Err(FrameCreateError::InvalidDataLength); |
304 | } |
305 | |
306 | let mut bytes = [0; 64]; |
307 | bytes[..data.len()].copy_from_slice(data); |
308 | |
309 | Ok(Self { bytes }) |
310 | } |
311 | |
312 | /// Raw read access to data. |
313 | pub fn raw(&self) -> &[u8] { |
314 | &self.bytes |
315 | } |
316 | |
317 | /// Checks if the length can be encoded in FDCAN DLC field. |
318 | pub const fn is_valid_len(len: usize) -> bool { |
319 | match len { |
320 | 0..=8 => true, |
321 | 12 => true, |
322 | 16 => true, |
323 | 20 => true, |
324 | 24 => true, |
325 | 32 => true, |
326 | 48 => true, |
327 | 64 => true, |
328 | _ => false, |
329 | } |
330 | } |
331 | |
332 | /// Creates an empty data payload containing 0 bytes. |
333 | #[inline ] |
334 | pub const fn empty() -> Self { |
335 | Self { bytes: [0; 64] } |
336 | } |
337 | } |
338 | |
339 | /// Frame with up to 8 bytes of data payload as per Fd CAN |
340 | #[derive (Debug, Copy, Clone)] |
341 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
342 | pub struct FdFrame { |
343 | can_header: Header, |
344 | data: FdData, |
345 | } |
346 | |
347 | impl FdFrame { |
348 | /// Create a new CAN classic Frame |
349 | pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
350 | let data = FdData::new(raw_data)?; |
351 | Ok(FdFrame { can_header, data }) |
352 | } |
353 | |
354 | /// Create new extended frame |
355 | pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
356 | if let Some(id) = embedded_can::ExtendedId::new(raw_id) { |
357 | Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data) |
358 | } else { |
359 | Err(FrameCreateError::InvalidCanId) |
360 | } |
361 | } |
362 | |
363 | /// Create new standard frame |
364 | pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Result<Self, FrameCreateError> { |
365 | if let Some(id) = embedded_can::StandardId::new(raw_id) { |
366 | Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data) |
367 | } else { |
368 | Err(FrameCreateError::InvalidCanId) |
369 | } |
370 | } |
371 | |
372 | /// Create new remote frame |
373 | pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Result<Self, FrameCreateError> { |
374 | if len <= 8 { |
375 | Self::new(Header::new(id.into(), len as u8, true), &[0; 8]) |
376 | } else { |
377 | Err(FrameCreateError::InvalidDataLength) |
378 | } |
379 | } |
380 | |
381 | /// Get reference to data |
382 | pub fn header(&self) -> &Header { |
383 | &self.can_header |
384 | } |
385 | |
386 | /// Return ID |
387 | pub fn id(&self) -> &embedded_can::Id { |
388 | &self.can_header.id |
389 | } |
390 | |
391 | /// Get reference to data |
392 | pub fn data(&self) -> &[u8] { |
393 | &self.data.raw() |
394 | } |
395 | } |
396 | |
397 | impl embedded_can::Frame for FdFrame { |
398 | fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> { |
399 | match FdFrame::new(Header::new_fd(id.into(), raw_data.len() as u8, false, true), raw_data) { |
400 | Ok(frame) => Some(frame), |
401 | Err(_) => None, |
402 | } |
403 | } |
404 | fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { |
405 | if len <= 8 { |
406 | match FdFrame::new(Header::new_fd(id.into(), len as u8, true, true), &[0; 64]) { |
407 | Ok(frame) => Some(frame), |
408 | Err(_) => None, |
409 | } |
410 | } else { |
411 | None |
412 | } |
413 | } |
414 | fn is_extended(&self) -> bool { |
415 | match self.can_header.id { |
416 | embedded_can::Id::Extended(_) => true, |
417 | embedded_can::Id::Standard(_) => false, |
418 | } |
419 | } |
420 | fn is_remote_frame(&self) -> bool { |
421 | self.can_header.rtr() |
422 | } |
423 | fn id(&self) -> embedded_can::Id { |
424 | self.can_header.id |
425 | } |
426 | // Returns length in bytes even for CANFD packets which embedded-can does not really mention. |
427 | fn dlc(&self) -> usize { |
428 | self.can_header.len as usize |
429 | } |
430 | fn data(&self) -> &[u8] { |
431 | &self.data.raw() |
432 | } |
433 | } |
434 | |
435 | impl CanHeader for FdFrame { |
436 | fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> { |
437 | Self::new(header, data) |
438 | } |
439 | |
440 | fn header(&self) -> &Header { |
441 | self.header() |
442 | } |
443 | } |
444 | |
445 | /// Contains CAN FD frame and additional metadata. |
446 | /// |
447 | /// Timestamp is available if `time` feature is enabled. |
448 | #[derive (Debug, Clone)] |
449 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
450 | pub struct FdEnvelope { |
451 | /// Reception time. |
452 | pub ts: Timestamp, |
453 | |
454 | /// The actual CAN frame. |
455 | pub frame: FdFrame, |
456 | } |
457 | |
458 | impl FdEnvelope { |
459 | /// Convert into a tuple |
460 | pub fn parts(self) -> (FdFrame, Timestamp) { |
461 | (self.frame, self.ts) |
462 | } |
463 | } |
464 | |