cancel
Showing results for 
Search instead for 
Did you mean: 

CAN messages filtering

Stepan Podhorsky
Associate II
Posted on August 15, 2017 at 14:16

Hello,

I have been developing control software based on STM32F207 and I have been using

CAN interface for communication. There are several messages with different IDs 

on the CAN bus which my software is interested in. I know that I have to

configure the CAN receiving filters but I don't know what is the better solution.

Shall I configure several filters for exact match of message identifier or shall

I configure only one filter with more general mask setting to receive more messages.

Thanks for any suggestions.

#stm32f2 #can
8 REPLIES 8
valentin
Senior
Posted on August 15, 2017 at 22:28

It depends. Are there any messages on the bus you're not interested in? Then you can create as few filters as possible to ignore those.

Or do you want to have specific messages routed to fifo0 and others to fifo1? Then you need to set up more filters and do that.

Filtering only gets really important when you have a very high bus load, very little cpu resources and are only interested in a small subset of the messages and want to do priority sorting by using the two fifos.

In short: Just start by accepting them all and see how your system handles it.

Posted on August 15, 2017 at 23:01

Hello Valentin,

thank you for your reaction. I am interested in only three message IDs so I have configured three filters for exact ID match. It seems to be functional. I have also thought about one filter configuration with a mask. The IDs I am interested in are 0x610, 0x660 and 0x6C1. I wasn't able to invent a mask only for these three IDs. Please can you tell me what are the c

onsequences to program processing speed in case I have three filters configured and in case I have only one filter configured

? Thanks.
Posted on August 15, 2017 at 23:10

The filters are hardware, so there is no impact on your processing speed whatsoever. It doesn't make a difference whether you use a mask or set up individual filters. Until you run out of filter banks it doesn't.

Here's a little code snippet I'm using from my personal can filter library. You can use these two functions to calculate the settings for the ID and MASK registers if you want to filter certain ID ranges.

/**
 * Calculates a mask to mask out all numbers outside id_min & id_max as good as possible.
 * This usually leads to masking out all numbers greater than the closest power of 2 of id_max.
 * Still, all combinations are checked.
 *
 * The criteria applied to keep a received frame is as follows: (received ID) AND (ID mask) = (ID arbitration), where AND is a bitwise operator.
 * A way to define a range by using these registers is to fill the MASK one with
 * (~((id_high) - (id_low)))
 * and the ID one with
 * ((id_low) & (id_high)).
 * Consequently, for id_low, it is generally better to choose a value with some LSBs cleared,
 * and for id_high a value that “logically contains�? id_low and with the same LSBs set. Example: the range 0x100-0x3FF
 * will work, but the range 0x100-0x2FF will not because 0x100 is not logically contained in 0x2FF (i.e. 0x100 & 0x2FF = 0).
 *
 * Note: (id_min & id_max) != 0
 *
 * Note 2: id_max-id_min needs to be increased to the next pow(2) - 1
 * This allows too many ids to pass and slightly increases cpu load but that's better than missing some wanted messages!
 *
 * @param id_minMinimum allowed ID (Std or Ext)
 * @param id_maxMax allowed ID
 * @returnRaw 32 bit mask. Not shifted, no IDE or RTR care bits set. In case of error: 0xFFFFFFFF -> id_min is not logically contained in id_max!
 */
uint32_t can_calc_range_mask(uint32_t id_min, uint32_t id_max) {
if (id_min >= id_max) {
return 0xffffffff;
}
/*
 * Round id_max-id_min up to next highest power of 2 - 1
 * -> https://community.st.com/external-link.jspa?url=http%3A%2F%2Fgraphics.stanford.edu%2F%7Eseander%2Fbithacks.html%23RoundUpPowerOf2Float
 */
uint32_t tmp = id_max - id_min;
tmp |= tmp >> 1;
tmp |= tmp >> 2;
tmp |= tmp >> 4;
tmp |= tmp >> 8;
tmp |= tmp >> 16;
id_max = id_min + tmp;
assert_param((id_min & id_max) != 0);
if ((id_min & id_max) == 0) {
// id_min not logically contained in id_max -> problem
/*
 * 0xFFFFFFFF should never be used in mask mode as this filters only ONE id for which list mode should be used.
 * Therefore this value now becomes an error flag.
 */
return 0xffffffff;
} else
return ~((id_max) - (id_min));
}
/**
 * Calculates the ID register for filtering a range of IDs.
 * See can_calc_range_mask for description of algorithm.
 *
 * Note: (id_min & id_max) != 0
 *
 * @param id_minMinimum allowed ID (Std or Ext)
 * @param id_maxMax allowed ID
 * @returnRaw 32 bit ID register value. Not shifted, no IDE or RTR care bits set.
 */
uint32_t can_calc_range_ID(uint32_t id_min, uint32_t id_max) {
assert_param((id_min & id_max) != 0); // catch here for now.
if ((id_min & id_max) == 0 || id_min >= id_max) {
// id_min not logically contained in id_max -> problem
/*
 * 0xFFFFFFFF should never be used in mask mode as this filters only ONE id for which list mode should be used.
 * Therefore this becomes an error flag.
 */
return 0xffffffff;
} else
return ((id_min) & (id_max));
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Enjoy!

Posted on August 15, 2017 at 23:33

The filtering to the best of my knowledge occurs in parallel, and even in the lamest imaginable implementation within the time of the entire message.

You could significantly decimate the number of messages accepted with a single filter that could pull these messages and a handful of others. The comparator can look for bits that are set or reset.

Mask of 0x70E, Comp of 0x600 would pass 32 messages including the 3 of interest

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Stepan Podhorsky
Associate II
Posted on August 16, 2017 at 19:29

Valentine, thank you very much for the code snippet you have added. It is very useful for me.

Posted on August 16, 2017 at 20:07

I computed those values in my head, but a 5 minute app could create and test a simple filter based on a list of IDs

/* CAN Filter Test - Determine if a single filter decimates sufficiently/effectively */
#include <windows.h>
#include <stdio.h>
int idtbl[] = {
 0x610, 0x660, 0x6C1, -1 };
int main(int argc, char **argv)
{
 int i;
 int x, y;
 i = 0;
 x = 0x7FF;
 y = 0x000;
 while(idtbl[i] != -1)
 {
 x &= idtbl[i];
 y |= idtbl[i];
 i++;
 }
 y = ~(~x & y) & 0x7FF;
 printf('COMP=%03X MASK=%03
X', x, y);
 printf('
PASSES
');
 for(i=0; i<0x800; i++)
 {
 if ((i & y) == x)
 printf(' %03
X', i);
 }
 return(1);
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

For three ID one could clearly use individual filters. The challenge comes when you exhaust the number of filters, and you still want limit those packets that get pulled/acknowledged by your node on a bus that is highly trafficked.

The code, I think, is helpful in understanding the mechanics of the CAN filtering, which seems to confuse a lot of people.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
valentin
Senior
Posted on August 18, 2017 at 05:08

Clive's reply got me thinking though. I'm currently working on improving my algorithm so that it doesn't matter any more which id_min and id_max I enter. It should always create a filter set so that at least

:(

all desired IDs pass.

That is only the case with Clive's method, not with mine. Let's see whether I can merge them

Posted on August 18, 2017 at 18:43

Since you have three standard IDs to filter, you can assemble a configuration for a single filter bank which contains these IDs. Just set the filterbank register to {0xcc00c200, 0xc200d820}, and configure this bank as active, match mode (FM1R bit = 1), 16 bit scale (FS1R bit = 0). If this is the only active filter bank, you can simply use the Filter Match Index to identify the message:

  • FMI = 0 -> ID = 0x610
  • FMI = 1 -> ID = 0x660
  • FMI = 2 -> ID = 0x6C1

(If not all of the filter bank slots are used, one of the valid slots have to fill in the empty ones so that no undesired messages can pass. Here the 0x610 filter is filling in the last empty slot.)