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    /// Get 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    /// Create 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    ///     [0x00, 0x00],
183    /// );
184    /// ```
185    pub fn new_from_code_mask(
186        id_code: StandardId,
187        id_mask: StandardId,
188        rtr_code: bool,
189        rtr_mask: bool,
190        payload_code: [u8; 2],
191        payload_mask: [u8; 2],
192    ) -> Self {
193        // The bit values we desire to match against. This determines whether we want a
194        // set bit (1) or a reset bit (0).
195        let mut acceptance_code: u32 = 0;
196        // The acceptance mask, set bits (1) mean we care about the exact value of the
197        // corresponding bit in the code, reset bits (0) mean the bit could be any
198        // value.
199        let mut acceptance_mask: u32 = 0;
200
201        // Pack the id into the full layout.
202        acceptance_code |= (id_code.as_raw() as u32) << 21;
203        acceptance_mask |= (id_mask.as_raw() as u32) << 21;
204
205        // Pack the RTR bit into the full layout.
206        acceptance_code |= (rtr_code as u32) << 20;
207        acceptance_mask |= (rtr_mask as u32) << 20;
208
209        // Pack the payload bytes into the full layout.
210        acceptance_code |= ((payload_code[0] as u32) << 8) | (payload_code[1] as u32);
211        acceptance_mask |= ((payload_mask[0] as u32) << 8) | (payload_mask[1] as u32);
212
213        Self {
214            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
215        }
216    }
217}
218
219impl Filter for SingleStandardFilter {
220    const FILTER_TYPE: FilterType = FilterType::Single;
221    fn to_registers(&self) -> [u8; 8] {
222        self.raw
223    }
224}
225
226/// Warning: This is not a perfect filter. Standard IDs that match the bit
227/// layout of this filter will also be accepted.
228pub struct SingleExtendedFilter {
229    raw: [u8; 8],
230}
231
232impl SingleExtendedFilter {
233    /// Create a filter that matches against a single 29-bit extended id.
234    ///
235    /// The filter can match against the packet's id and the RTR bit.
236    ///
237    /// # Examples
238    /// A filter matching any odd extended IDs, with any rtr value.
239    /// ```rust, ignore
240    /// const FILTER: twai::filter::SingleExtendedFilter =
241    ///     twai::filter::SingleExtendedFilter::new(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1", b"x");
242    /// ```
243    pub const fn new(id: &BitFilter<29>, rtr: &BitFilter<1>) -> Self {
244        // The bit values we desire to match against. This determines whether we want a
245        // set bit (1) or a reset bit (0).
246        let mut acceptance_code: u32 = 0;
247        // The acceptance mask, set bits (1) mean we care about the exact value of the
248        // corresponding bit in the code, reset bits (0) mean the bit could be any
249        // value.
250        let mut acceptance_mask: u32 = 0;
251
252        // Convert the id filter into the code and mask bits.
253        {
254            let mut idx = 0;
255            while idx < 29 {
256                let shift = 31 - idx;
257                set_bit_from_byte!(acceptance_code, acceptance_mask, id[idx], shift);
258                idx += 1;
259            }
260        }
261        // Convert the RTR bit filter into the code and mask bits.
262        {
263            let shift = 2;
264            set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift);
265        }
266
267        Self {
268            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
269        }
270    }
271    /// The masks indicate which bits of the code the filter should match
272    /// against. Set bits in the mask indicate that the corresponding bit in
273    /// the code should match.
274    pub fn new_from_code_mask(
275        id_code: ExtendedId,
276        id_mask: ExtendedId,
277        rtr_code: bool,
278        rtr_mask: bool,
279    ) -> Self {
280        // The bit values we desire to match against. This determines whether we want a
281        // set bit (1) or a reset bit (0).
282        let mut acceptance_code: u32 = 0;
283        // The acceptance mask, set bits (1) mean we care about the exact value of the
284        // corresponding bit in the code, reset bits (0) mean the bit could be any
285        // value.
286        let mut acceptance_mask: u32 = 0;
287
288        // Pack the id into the full layout.
289        acceptance_code |= id_code.as_raw() << 3;
290        acceptance_mask |= id_mask.as_raw() << 3;
291
292        // Pack the RTR bit into the full layout.
293        acceptance_code |= (rtr_code as u32) << 2;
294        acceptance_mask |= (rtr_mask as u32) << 2;
295
296        Self {
297            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
298        }
299    }
300}
301
302impl Filter for SingleExtendedFilter {
303    const FILTER_TYPE: FilterType = FilterType::Single;
304    fn to_registers(&self) -> [u8; 8] {
305        self.raw
306    }
307}
308
309/// A filter that matches against two standard 11-bit standard IDs.
310///
311/// The first filter part can match a packet's id, RTR bit, and the first byte
312/// of the payload. The second filter part can match a packet's id and RTR bit.
313///
314/// Warning: This is not a perfect filter. Extended IDs that match the bit
315/// layout of this filter will also be accepted.
316pub struct DualStandardFilter {
317    raw: [u8; 8],
318}
319
320impl DualStandardFilter {
321    /// Create a filter that matches against two standard 11-bit standard IDs.
322    ///
323    /// The first filter part can match a packet's id, RTR bit, and the first
324    /// byte of the payload. The second filter part can match a packet's id
325    /// and RTR bit.
326    ///
327    /// # Examples
328    /// A filter that matches any standard id that ends with a 00 or a 11, with
329    /// any RTR, and with any payload on the first filter.
330    /// ```rust, ignore
331    /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new(
332    ///     b"xxxxxxxxx00",
333    ///     b"x",
334    ///     b"xxxxxxxx",
335    ///     b"xxxxxxxxx11",
336    ///     b"x",
337    /// );
338    /// ```
339    pub const fn new(
340        first_id: &BitFilter<11>,
341        first_rtr: &BitFilter<1>,
342        first_payload: &BitFilter<8>,
343        second_id: &BitFilter<11>,
344        second_rtr: &BitFilter<1>,
345    ) -> Self {
346        // The bit values we desire to match against. This determines whether we want a
347        // set bit (1) or a reset bit (0).
348        let mut acceptance_code: u32 = 0;
349        // The acceptance mask, set bits (1) mean we care about the exact value of the
350        // corresponding bit in the code, reset bits (0) mean the bit could be any
351        // value.
352        let mut acceptance_mask: u32 = 0;
353
354        // Convert the first id filter into the code and mask bits.
355        {
356            let mut idx = 0;
357            while idx < 11 {
358                let shift = 31 - idx;
359                set_bit_from_byte!(acceptance_code, acceptance_mask, first_id[idx], shift);
360                idx += 1;
361            }
362        }
363        // Convert the first RTR bit filter into the code and mask bits.
364        {
365            let shift = 20;
366            set_bit_from_byte!(acceptance_code, acceptance_mask, first_rtr[0], shift);
367        }
368        // Convert the first payload byte filter into the code and mask bits.
369        {
370            let mut idx = 0;
371            while idx < 4 {
372                let shift = 19 - idx;
373                set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift);
374                idx += 1;
375            }
376            while idx < 8 {
377                let shift = 3 + 4 - idx;
378                set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift);
379                idx += 1;
380            }
381        }
382        // Convert the second id filter into the code and mask bits.
383        {
384            let mut idx = 0;
385            while idx < 11 {
386                let shift = 15 - idx;
387                set_bit_from_byte!(acceptance_code, acceptance_mask, second_id[idx], shift);
388                idx += 1;
389            }
390        }
391        // Convert the second RTR bit filter into the code and mask bits.
392        {
393            let shift = 4;
394            set_bit_from_byte!(acceptance_code, acceptance_mask, second_rtr[0], shift);
395        }
396
397        Self {
398            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
399        }
400    }
401    /// The masks indicate which bits of the code the filter should match
402    /// against. Set bits in the mask indicate that the corresponding bit in
403    /// the code should match.
404    #[expect(clippy::too_many_arguments)]
405    pub fn new_from_code_mask(
406        first_id_code: StandardId,
407        first_id_mask: StandardId,
408        first_rtr_code: bool,
409        first_rtr_mask: bool,
410        first_payload_code: u8,
411        first_payload_mask: u8,
412        second_id_code: StandardId,
413        second_id_mask: StandardId,
414        second_rtr_code: bool,
415        second_rtr_mask: bool,
416    ) -> Self {
417        // The bit values we desire to match against. This determines whether we want a
418        // set bit (1) or a reset bit (0).
419        let mut acceptance_code: u32 = 0;
420        // The acceptance mask, set bits (1) mean we care about the exact value of the
421        // corresponding bit in the code, reset bits (0) mean the bit could be any
422        // value.
423        let mut acceptance_mask: u32 = 0;
424
425        // Pack the first id into the full layout.
426        acceptance_code |= (first_id_code.as_raw() as u32) << 21;
427        acceptance_mask |= (first_id_mask.as_raw() as u32) << 21;
428
429        // Pack the RTR bit into the full layout.
430        acceptance_code |= (first_rtr_code as u32) << 20;
431        acceptance_mask |= (first_rtr_mask as u32) << 20;
432
433        // Pack the first payload into the full layout.
434        acceptance_code |= ((first_payload_code & 0xF0) as u32) << 12;
435        acceptance_mask |= ((first_payload_mask & 0xF0) as u32) << 12;
436        acceptance_code |= (first_payload_code & 0x0F) as u32;
437        acceptance_mask |= (first_payload_mask & 0x0F) as u32;
438
439        // Pack the second id into the full layout.
440        acceptance_code |= (second_id_code.as_raw() as u32) << 5;
441        acceptance_mask |= (second_id_mask.as_raw() as u32) << 5;
442
443        // Pack the second RTR bit into the full layout.
444        acceptance_code |= (second_rtr_code as u32) << 4;
445        acceptance_mask |= (second_rtr_mask as u32) << 4;
446
447        Self {
448            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
449        }
450    }
451}
452
453impl Filter for DualStandardFilter {
454    const FILTER_TYPE: FilterType = FilterType::Dual;
455    fn to_registers(&self) -> [u8; 8] {
456        self.raw
457    }
458}
459
460/// Warning: This is not a perfect filter. Standard IDs that match the bit
461/// layout of this filter will also be accepted.
462///
463/// NOTE: The dual extended id acceptance filters can only match "the first 16
464/// bits of the 29-bit ID".
465pub struct DualExtendedFilter {
466    raw: [u8; 8],
467}
468
469impl DualExtendedFilter {
470    /// Create a filter that matches the first 16 bits of two 29-bit extended
471    /// IDs.
472    ///
473    /// # Examples
474    /// A filter that matches IDs with 4 bits either set or reset in the higher
475    /// part of the id. For example this id matches: 0x000f000f, 0x000f000a,
476    /// 0x0000000a, 0x0000000b.
477    /// But it does not match: 0x000a000a
478    /// ```rust, ignore
479    /// const FILTER: twai::filter::DualExtendedFilter =
480    ///     twai::filter::DualExtendedFilter::new([b"xxxxxxxxx0000xxx", b"xxxxxxxxx1111xxx"]);
481    /// ```
482    pub const fn new(ids: [&BitFilter<16>; 2]) -> Self {
483        // The bit values we desire to match against. This determines whether we want a
484        // set bit (1) or a reset bit (0).
485        let mut acceptance_code: u32 = 0;
486        // The acceptance mask, set bits (1) mean we care about the exact value of the
487        // corresponding bit in the code, reset bits (0) mean the bit could be any
488        // value.
489        let mut acceptance_mask: u32 = 0;
490
491        // Convert the id filters into the code and mask bits.
492        {
493            let mut filter_idx = 0;
494            while filter_idx < 2 {
495                let mut idx = 0;
496                while idx < 16 {
497                    let shift = 31 - (filter_idx * 16) - idx;
498                    set_bit_from_byte!(
499                        acceptance_code,
500                        acceptance_mask,
501                        ids[filter_idx][idx],
502                        shift
503                    );
504                    idx += 1;
505                }
506                filter_idx += 1;
507            }
508        }
509
510        Self {
511            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
512        }
513    }
514    /// Create a new filter matching the first 16 bits of two 29-bit IDs.
515    ///
516    /// The masks indicate which bits of the code the filter should match
517    /// against. Set bits in the mask indicate that the corresponding bit in
518    /// the code should match.
519    pub fn new_from_code_mask(ids_code: [u16; 2], ids_mask: [u16; 2]) -> Self {
520        // The bit values we desire to match against. This determines whether we want a
521        // set bit (1) or a reset bit (0).
522        let mut acceptance_code: u32 = 0;
523        // The acceptance mask, set bits (1) mean we care about the exact value of the
524        // corresponding bit in the code, reset bits (0) mean the bit could be any
525        // value.
526        let mut acceptance_mask: u32 = 0;
527
528        // Pack the first partial id into the full layout.
529        acceptance_code |= (ids_code[0] as u32) << 16;
530        acceptance_mask |= (ids_mask[0] as u32) << 16;
531
532        // Pack the second partial id into the full layout.
533        acceptance_code |= ids_code[1] as u32;
534        acceptance_mask |= ids_mask[1] as u32;
535
536        Self {
537            raw: code_mask_to_register_array(acceptance_code, acceptance_mask),
538        }
539    }
540}
541
542impl Filter for DualExtendedFilter {
543    const FILTER_TYPE: FilterType = FilterType::Dual;
544    fn to_registers(&self) -> [u8; 8] {
545        self.raw
546    }
547}