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}