esp_hal/twai/filter.rs
1//! Two-wire Automotive Interface (TWAI) Filters
2//!
3//! ## Overview
4//!
5//! The TWAI controller contains a hardware acceptance filter which can be used
6//! to filter messages of a particular ID. A node that filters out a message
7//! does not receive the message, but will still acknowledge it. Acceptance
8//! filters can make a node more efficient by filtering out messages sent over
9//! the bus that are irrelevant to the node.
10//!
11//! ## Configuration
12//!
13//! The acceptance filters are configured using two 32-bit values known as the
14//! acceptance code and the acceptance mask.
15
16use super::{ExtendedId, StandardId};
17
18#[derive(Debug, PartialEq, Eq)]
19/// Represents the type of filtering to be applied to incoming TWAI frames.
20pub enum FilterType {
21 /// Uses the acceptance code and mask to define a single filter, which
22 /// allows for the first two data bytes of a standard frame to be filtered,
23 /// or the entirety of an extended frame's 29-bit ID.
24 Single,
25 /// Uses the acceptance code and mask to define two separate filters
26 /// allowing for increased flexibility of ID's to accept, but does not allow
27 /// for all 29-bits of an extended ID to be filtered.
28 Dual,
29}
30
31/// Interface for interacting with Acceptance Filters.
32///
33/// The Acceptance Filter is a programmable message filtering unit that allows
34/// the TWAI controller to accept or reject a received message based on the
35/// message’s ID field.
36///
37/// Only accepted messages will be stored in the Receive FIFO.
38///
39/// The Acceptance Filter’s registers can be programmed to specify a single
40/// filter, or two separate filters (dual filter mode).
41pub trait Filter {
42 /// The type of the filter.
43 const FILTER_TYPE: FilterType;
44 /// Returns filter type.
45 fn filter_type(&self) -> FilterType {
46 Self::FILTER_TYPE
47 }
48
49 /// Returns the register level representation of the filter.
50 fn to_registers(&self) -> [u8; 8];
51}
52
53/// A type representing the bitmask used to filter incoming TWAI frames.
54pub type BitFilter<const N: usize> = [u8; N];
55
56// Convert a byte from a bytestring into a bit inside a given code and mask.
57macro_rules! set_bit_from_byte {
58 ($code:expr, $mask:expr, $byte:expr, $shift:expr) => {
59 match $byte {
60 b'0' => {
61 // Code bit is already zero, no need to set it.
62 $mask |= 1 << $shift;
63 }
64 b'1' => {
65 $code |= 1 << $shift;
66 $mask |= 1 << $shift;
67 }
68 b'x' => {}
69 _ => ::core::panic!("BitFilter bits must be either '1', '0' or 'x'."),
70 }
71 };
72}
73
74// Convert a code and mask to the byte array needed at a register level.
75//
76// On the input mask, set bits (1) mean we care about the exact value of the
77// corresponding bit in the code, reset bits (0) mean the bit could be any
78// value.
79const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] {
80 // Convert the filter code and mask into the full byte array needed for the
81 // registers.
82 let [code_3, code_2, code_1, code_0] = code.to_be_bytes();
83
84 // At a register level, set bits in the mask mean we don't care about the value
85 // of that bit. Therefore, we invert the mask.
86 // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6
87 let [mask_3, mask_2, mask_1, mask_0] = (!mask).to_be_bytes();
88
89 [
90 code_3, code_2, code_1, code_0, mask_3, mask_2, mask_1, mask_0,
91 ]
92}
93
94/// A filter that matches against a single 11 bit id, the RTR bit, and the first
95/// two bytes of the payload.
96///
97/// Warning: This is not a perfect filter. Extended IDs that match the bit
98/// layout of this filter will also be accepted.
99pub struct SingleStandardFilter {
100 /// The register representation of the filter.
101 raw: [u8; 8],
102}
103
104impl SingleStandardFilter {
105 #[procmacros::doc_replace]
106 /// Creates a new filter that matches against a single 11-bit standard id.
107 /// The filter can match against the packet's id, RTR bit, and first two
108 /// bytes of the payload.
109 ///
110 /// Example matching only even IDs, allowing any rtr value and any payload
111 /// data:
112 /// ```rust, no_run
113 /// # {before_snippet}
114 /// # use esp_hal::twai::filter::SingleStandardFilter;
115 /// const FILTER: SingleStandardFilter =
116 /// SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]);
117 /// # {after_snippet}
118 /// ```
119 pub const fn new(id: &BitFilter<11>, rtr: &BitFilter<1>, payload: [&BitFilter<8>; 2]) -> Self {
120 // The bit values we desire to match against. This determines whether we want a
121 // set bit (1) or a reset bit (0).
122 let mut acceptance_code: u32 = 0;
123 // The acceptance mask, set bits (1) mean we care about the exact value of the
124 // corresponding bit in the code, reset bits (0) mean the bit could be any
125 // value.
126 let mut acceptance_mask: u32 = 0;
127
128 // Convert the id filter into the code and mask bits.
129 {
130 let mut idx = 0;
131 while idx < 11 {
132 let shift = 31 - idx;
133 set_bit_from_byte!(acceptance_code, acceptance_mask, id[idx], shift);
134 idx += 1;
135 }
136 }
137 // Convert the RTR bit filter into the code and mask bits.
138 {
139 let shift = 20;
140 set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift);
141 }
142 // Convert the payload byte filter into the code and mask bits.
143 {
144 let mut payload_index = 0;
145 while payload_index < 2 {
146 let mut idx = 0;
147 while idx < 8 {
148 let shift = 15 - (8 * payload_index) - idx;
149 set_bit_from_byte!(
150 acceptance_code,
151 acceptance_mask,
152 payload[payload_index][idx],
153 shift
154 );
155 idx += 1;
156 }
157 payload_index += 1;
158 }
159 }
160
161 Self {
162 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
163 }
164 }
165
166 /// The masks indicate which bits of the code the filter should match
167 /// against. Set bits in the mask indicate that the corresponding bit in
168 /// the code should match.
169 ///
170 ///
171 /// # Examples
172 ///
173 /// A filter that matches every standard id that is even, is not an rtr
174 /// frame, with any bytes for the first two payload bytes.
175 /// ```rust, ignore
176 /// let filter = twai::filter::SingleStandardFilter::new_from_code_mask(
177 /// StandardId::new(0x000)?,
178 /// StandardId::new(0x001)?,
179 /// false,
180 /// true,
181 /// [0x00, 0x00],
182 /// );
183 /// ```
184 pub fn new_from_code_mask(
185 id_code: StandardId,
186 id_mask: StandardId,
187 rtr_code: bool,
188 rtr_mask: bool,
189 payload_code: [u8; 2],
190 payload_mask: [u8; 2],
191 ) -> Self {
192 // The bit values we desire to match against. This determines whether we want a
193 // set bit (1) or a reset bit (0).
194 let mut acceptance_code: u32 = 0;
195 // The acceptance mask, set bits (1) mean we care about the exact value of the
196 // corresponding bit in the code, reset bits (0) mean the bit could be any
197 // value.
198 let mut acceptance_mask: u32 = 0;
199
200 // Pack the id into the full layout.
201 acceptance_code |= (id_code.as_raw() as u32) << 21;
202 acceptance_mask |= (id_mask.as_raw() as u32) << 21;
203
204 // Pack the RTR bit into the full layout.
205 acceptance_code |= (rtr_code as u32) << 20;
206 acceptance_mask |= (rtr_mask as u32) << 20;
207
208 // Pack the payload bytes into the full layout.
209 acceptance_code |= ((payload_code[0] as u32) << 8) | (payload_code[1] as u32);
210 acceptance_mask |= ((payload_mask[0] as u32) << 8) | (payload_mask[1] as u32);
211
212 Self {
213 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
214 }
215 }
216}
217
218impl Filter for SingleStandardFilter {
219 const FILTER_TYPE: FilterType = FilterType::Single;
220 fn to_registers(&self) -> [u8; 8] {
221 self.raw
222 }
223}
224
225/// Warning: This is not a perfect filter. Standard IDs that match the bit
226/// layout of this filter will also be accepted.
227pub struct SingleExtendedFilter {
228 raw: [u8; 8],
229}
230
231impl SingleExtendedFilter {
232 /// Creates a new filter that matches against a single 29-bit extended id.
233 ///
234 /// The filter can match against the packet's id and the RTR bit.
235 ///
236 /// # Examples
237 /// A filter matching any odd extended IDs, with any rtr value.
238 /// ```rust, ignore
239 /// const FILTER: twai::filter::SingleExtendedFilter =
240 /// twai::filter::SingleExtendedFilter::new(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1", b"x");
241 /// ```
242 pub const fn new(id: &BitFilter<29>, rtr: &BitFilter<1>) -> Self {
243 // The bit values we desire to match against. This determines whether we want a
244 // set bit (1) or a reset bit (0).
245 let mut acceptance_code: u32 = 0;
246 // The acceptance mask, set bits (1) mean we care about the exact value of the
247 // corresponding bit in the code, reset bits (0) mean the bit could be any
248 // value.
249 let mut acceptance_mask: u32 = 0;
250
251 // Convert the id filter into the code and mask bits.
252 {
253 let mut idx = 0;
254 while idx < 29 {
255 let shift = 31 - idx;
256 set_bit_from_byte!(acceptance_code, acceptance_mask, id[idx], shift);
257 idx += 1;
258 }
259 }
260 // Convert the RTR bit filter into the code and mask bits.
261 {
262 let shift = 2;
263 set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift);
264 }
265
266 Self {
267 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
268 }
269 }
270 /// The masks indicate which bits of the code the filter should match
271 /// against. Set bits in the mask indicate that the corresponding bit in
272 /// the code should match.
273 pub fn new_from_code_mask(
274 id_code: ExtendedId,
275 id_mask: ExtendedId,
276 rtr_code: bool,
277 rtr_mask: bool,
278 ) -> Self {
279 // The bit values we desire to match against. This determines whether we want a
280 // set bit (1) or a reset bit (0).
281 let mut acceptance_code: u32 = 0;
282 // The acceptance mask, set bits (1) mean we care about the exact value of the
283 // corresponding bit in the code, reset bits (0) mean the bit could be any
284 // value.
285 let mut acceptance_mask: u32 = 0;
286
287 // Pack the id into the full layout.
288 acceptance_code |= id_code.as_raw() << 3;
289 acceptance_mask |= id_mask.as_raw() << 3;
290
291 // Pack the RTR bit into the full layout.
292 acceptance_code |= (rtr_code as u32) << 2;
293 acceptance_mask |= (rtr_mask as u32) << 2;
294
295 Self {
296 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
297 }
298 }
299}
300
301impl Filter for SingleExtendedFilter {
302 const FILTER_TYPE: FilterType = FilterType::Single;
303 fn to_registers(&self) -> [u8; 8] {
304 self.raw
305 }
306}
307
308/// A filter that matches against two standard 11-bit standard IDs.
309///
310/// The first filter part can match a packet's id, RTR bit, and the first byte
311/// of the payload. The second filter part can match a packet's id and RTR bit.
312///
313/// Warning: This is not a perfect filter. Extended IDs that match the bit
314/// layout of this filter will also be accepted.
315pub struct DualStandardFilter {
316 raw: [u8; 8],
317}
318
319impl DualStandardFilter {
320 /// Creates a new filter that matches against two standard 11-bit standard IDs.
321 ///
322 /// The first filter part can match a packet's id, RTR bit, and the first
323 /// byte of the payload. The second filter part can match a packet's id
324 /// and RTR bit.
325 ///
326 /// # Examples
327 /// A filter that matches any standard id that ends with a 00 or a 11, with
328 /// any RTR, and with any payload on the first filter.
329 /// ```rust, ignore
330 /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new(
331 /// b"xxxxxxxxx00",
332 /// b"x",
333 /// b"xxxxxxxx",
334 /// b"xxxxxxxxx11",
335 /// b"x",
336 /// );
337 /// ```
338 pub const fn new(
339 first_id: &BitFilter<11>,
340 first_rtr: &BitFilter<1>,
341 first_payload: &BitFilter<8>,
342 second_id: &BitFilter<11>,
343 second_rtr: &BitFilter<1>,
344 ) -> Self {
345 // The bit values we desire to match against. This determines whether we want a
346 // set bit (1) or a reset bit (0).
347 let mut acceptance_code: u32 = 0;
348 // The acceptance mask, set bits (1) mean we care about the exact value of the
349 // corresponding bit in the code, reset bits (0) mean the bit could be any
350 // value.
351 let mut acceptance_mask: u32 = 0;
352
353 // Convert the first id filter into the code and mask bits.
354 {
355 let mut idx = 0;
356 while idx < 11 {
357 let shift = 31 - idx;
358 set_bit_from_byte!(acceptance_code, acceptance_mask, first_id[idx], shift);
359 idx += 1;
360 }
361 }
362 // Convert the first RTR bit filter into the code and mask bits.
363 {
364 let shift = 20;
365 set_bit_from_byte!(acceptance_code, acceptance_mask, first_rtr[0], shift);
366 }
367 // Convert the first payload byte filter into the code and mask bits.
368 {
369 let mut idx = 0;
370 while idx < 4 {
371 let shift = 19 - idx;
372 set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift);
373 idx += 1;
374 }
375 while idx < 8 {
376 let shift = 3 + 4 - idx;
377 set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift);
378 idx += 1;
379 }
380 }
381 // Convert the second id filter into the code and mask bits.
382 {
383 let mut idx = 0;
384 while idx < 11 {
385 let shift = 15 - idx;
386 set_bit_from_byte!(acceptance_code, acceptance_mask, second_id[idx], shift);
387 idx += 1;
388 }
389 }
390 // Convert the second RTR bit filter into the code and mask bits.
391 {
392 let shift = 4;
393 set_bit_from_byte!(acceptance_code, acceptance_mask, second_rtr[0], shift);
394 }
395
396 Self {
397 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
398 }
399 }
400 /// The masks indicate which bits of the code the filter should match
401 /// against. Set bits in the mask indicate that the corresponding bit in
402 /// the code should match.
403 #[expect(clippy::too_many_arguments)]
404 pub fn new_from_code_mask(
405 first_id_code: StandardId,
406 first_id_mask: StandardId,
407 first_rtr_code: bool,
408 first_rtr_mask: bool,
409 first_payload_code: u8,
410 first_payload_mask: u8,
411 second_id_code: StandardId,
412 second_id_mask: StandardId,
413 second_rtr_code: bool,
414 second_rtr_mask: bool,
415 ) -> Self {
416 // The bit values we desire to match against. This determines whether we want a
417 // set bit (1) or a reset bit (0).
418 let mut acceptance_code: u32 = 0;
419 // The acceptance mask, set bits (1) mean we care about the exact value of the
420 // corresponding bit in the code, reset bits (0) mean the bit could be any
421 // value.
422 let mut acceptance_mask: u32 = 0;
423
424 // Pack the first id into the full layout.
425 acceptance_code |= (first_id_code.as_raw() as u32) << 21;
426 acceptance_mask |= (first_id_mask.as_raw() as u32) << 21;
427
428 // Pack the RTR bit into the full layout.
429 acceptance_code |= (first_rtr_code as u32) << 20;
430 acceptance_mask |= (first_rtr_mask as u32) << 20;
431
432 // Pack the first payload into the full layout.
433 acceptance_code |= ((first_payload_code & 0xF0) as u32) << 12;
434 acceptance_mask |= ((first_payload_mask & 0xF0) as u32) << 12;
435 acceptance_code |= (first_payload_code & 0x0F) as u32;
436 acceptance_mask |= (first_payload_mask & 0x0F) as u32;
437
438 // Pack the second id into the full layout.
439 acceptance_code |= (second_id_code.as_raw() as u32) << 5;
440 acceptance_mask |= (second_id_mask.as_raw() as u32) << 5;
441
442 // Pack the second RTR bit into the full layout.
443 acceptance_code |= (second_rtr_code as u32) << 4;
444 acceptance_mask |= (second_rtr_mask as u32) << 4;
445
446 Self {
447 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
448 }
449 }
450}
451
452impl Filter for DualStandardFilter {
453 const FILTER_TYPE: FilterType = FilterType::Dual;
454 fn to_registers(&self) -> [u8; 8] {
455 self.raw
456 }
457}
458
459/// Warning: This is not a perfect filter. Standard IDs that match the bit
460/// layout of this filter will also be accepted.
461///
462/// NOTE: The dual extended id acceptance filters can only match "the first 16
463/// bits of the 29-bit ID".
464pub struct DualExtendedFilter {
465 raw: [u8; 8],
466}
467
468impl DualExtendedFilter {
469 /// Creates a new filter that matches the first 16 bits of two 29-bit extended
470 /// IDs.
471 ///
472 /// # Examples
473 /// A filter that matches IDs with 4 bits either set or reset in the higher
474 /// part of the id. For example this id matches: 0x000f000f, 0x000f000a,
475 /// 0x0000000a, 0x0000000b.
476 /// But it does not match: 0x000a000a
477 /// ```rust, ignore
478 /// const FILTER: twai::filter::DualExtendedFilter =
479 /// twai::filter::DualExtendedFilter::new([b"xxxxxxxxx0000xxx", b"xxxxxxxxx1111xxx"]);
480 /// ```
481 pub const fn new(ids: [&BitFilter<16>; 2]) -> Self {
482 // The bit values we desire to match against. This determines whether we want a
483 // set bit (1) or a reset bit (0).
484 let mut acceptance_code: u32 = 0;
485 // The acceptance mask, set bits (1) mean we care about the exact value of the
486 // corresponding bit in the code, reset bits (0) mean the bit could be any
487 // value.
488 let mut acceptance_mask: u32 = 0;
489
490 // Convert the id filters into the code and mask bits.
491 {
492 let mut filter_idx = 0;
493 while filter_idx < 2 {
494 let mut idx = 0;
495 while idx < 16 {
496 let shift = 31 - (filter_idx * 16) - idx;
497 set_bit_from_byte!(
498 acceptance_code,
499 acceptance_mask,
500 ids[filter_idx][idx],
501 shift
502 );
503 idx += 1;
504 }
505 filter_idx += 1;
506 }
507 }
508
509 Self {
510 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
511 }
512 }
513 /// Creates a new filter matching the first 16 bits of two 29-bit IDs.
514 ///
515 /// The masks indicate which bits of the code the filter should match
516 /// against. Set bits in the mask indicate that the corresponding bit in
517 /// the code should match.
518 pub fn new_from_code_mask(ids_code: [u16; 2], ids_mask: [u16; 2]) -> Self {
519 // The bit values we desire to match against. This determines whether we want a
520 // set bit (1) or a reset bit (0).
521 let mut acceptance_code: u32 = 0;
522 // The acceptance mask, set bits (1) mean we care about the exact value of the
523 // corresponding bit in the code, reset bits (0) mean the bit could be any
524 // value.
525 let mut acceptance_mask: u32 = 0;
526
527 // Pack the first partial id into the full layout.
528 acceptance_code |= (ids_code[0] as u32) << 16;
529 acceptance_mask |= (ids_mask[0] as u32) << 16;
530
531 // Pack the second partial id into the full layout.
532 acceptance_code |= ids_code[1] as u32;
533 acceptance_mask |= ids_mask[1] as u32;
534
535 Self {
536 raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
537 }
538 }
539}
540
541impl Filter for DualExtendedFilter {
542 const FILTER_TYPE: FilterType = FilterType::Dual;
543 fn to_registers(&self) -> [u8; 8] {
544 self.raw
545 }
546}