Introduction
This is a rather specific post that is motivated by the rather unintuitive use of Identifier filtering on an STM32Fxx CAN Bus and by the impossibility to find some easy examples. For a general overview, refer to the official manual from page 1092 onwards.
Identifier filtering
Every CAN message has an associated ID sent with its content. A receiver can decide on a hardware level which IDs to accept by using 27 so called filter banks which can implement either one 32-bit filter or two 16-bit filter. The usage of these also depends on your scale of CAN Msg IDs which can either be standard IDs (11-bit) or extended IDs (29-bit). In this post we assume the usage of the extended IDs.
Mask Mode
The filter makes use of two 29-bit numbers – composed in a FilterId
and a FilterMask
and there are two modes to be used: Identifier Mode and Mask Mode. The identifier mode is pretty simple: both numbers represent IDs that should be caught by the filter. For the Mask Mode that we are presenting here, the FilterMask
specifies which bits to compare with the FilterId
, some examples for extended IDs:
MASK = 0x1FFFFFFE
andID = 0x00000002
will catch IDs 2 and 3.MASK = 0x1FFFFFF8
andID = 0x00000000
will catch IDs 0 to 7.MASK = 0x1FFFFFFF
andID = 0x00000008
will catch ID 8.MASK = 0x00000001
andID = 0x00000001
will catch all odd IDs.
So for “normal” operations, you can only filter either single IDs or ranges in the form 2^N - (2^(N-1) - 1)
but there are also possibilities such as only accepting odd IDs and whatever you can combine with these representations. Each filter is also associated with a number, with generally being the lower the number the higher the priority, other priority rules are given in the documentation. One important point that is not mentioned though: In mask mode, a mask of 0x00
means that all IDs gets passed, be aware that this filter will “grab” all IDs from all other filters and the filter number doesn’t play a role anymore!
Implementation
The actual filters are actually used by configuring the CAN_FilterInitTypeDef
struct of the stm32f4xx_can.h
filter, which has various fields. For the specific case of this post, the filter is setup as seen in this snippet, with filter_id
and filter_mask
being an input from the user as specified before:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CAN_FilterInitTypeDef filter; filter.CAN_FilterIdHigh = ((filter_id << 5) | (filter_id >> (32 - 5))) & 0xFFFF; // STID[10:0] & EXTID[17:13] filter.CAN_FilterIdLow = (filter_id >> (11 - 3)) & 0xFFF8; // EXID[12:5] & 3 Reserved bits filter.CAN_FilterMaskIdHigh = ((filter_mask << 5) | (filter_mask >> (32 - 5))) & 0xFFFF; filter.CAN_FilterMaskIdLow = (filter_mask >> (11 - 3)) & 0xFFF8; filter.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; filter.CAN_FilterNumber = filter_num; filter.CAN_FilterMode = CAN_FilterMode_IdMask; filter.CAN_FilterScale = CAN_FilterScale_32bit; filter.CAN_FilterActivation = ENABLE; CAN_FilterInit(&filter); |
The reasoning behind this bit shifting is best understood by having a look at the referenced manual on p. 1092
. Whereas High
and Low
are the higher and lower indices of the filter registers respectively.
For ID list mode, each filter applied to 2 IDS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define REMOTE_FRAME 0 /* If = 1 the frame should be a remote frame. If = 0 the frame will be a data frame */ #define EXTID 1 /* If = 0 the frame should be a frame with standard ID. If = 1 the frame should be a frame with extended ID */ #define ID1 0x0CF00400 #define ID2 0x0CFD9200 #define FILTER_ID ((ID1 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) // ID1 = 0x0CF00400 (FxFR1[31:0]) #define FILTER_MASK ((ID2 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) // ID2 = 0x0CFD9200 (FxFR2[31:0]) sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = (FILTER_ID >> 16); // Filter ID2 LSB (FxFR2[0:15]) sFilterConfig.FilterIdLow = (FILTER_ID & 0xFFFF); // Filter ID1 LSB (FxFR1[0:15]) sFilterConfig.FilterMaskIdHigh = (FILTER_MASK >> 16); // Filter ID2 MSB (FxFR2[16:31]) sFilterConfig.FilterMaskIdLow = (FILTER_MASK & 0xFFFF); // Filter ID1 MSB (FxFR1[16:31]) |
Use more filters to filter more IDS
For each filter (new filter configuration) you need to increment .FilterBank:
sFilterConfig.FilterBank = 0; -> filter number 0
sFilterConfig.FilterBank = 1; -> filter number 1
sFilterConfig.FilterBank = 2; -> filter number 2 etc ..
Working codes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void CAN_filter_config(uint8_t filter_index, uint32_t ID, uint32_t MASK) { CAN_FilterTypeDef sFilter_config; // Create a new filter config structure for each call sFilter_config.FilterBank = filter_index; sFilter_config.FilterMode = CAN_FILTERMODE_IDLIST; sFilter_config.FilterScale = CAN_FILTERSCALE_32BIT; sFilter_config.FilterIdHigh = (ID >> 16); sFilter_config.FilterIdLow = (ID & 0xFFFF); sFilter_config.FilterMaskIdHigh = (MASK >> 16); sFilter_config.FilterMaskIdLow = (MASK & 0xFFFF); sFilter_config.FilterFIFOAssignment = CAN_FILTER_FIFO0; sFilter_config.FilterActivation = ENABLE; if (HAL_CAN_ConfigFilter(&hcan, &sFilter_config) != HAL_OK) { CAN_error(); } } |
1 2 3 4 5 6 7 8 9 10 11 |
#define FILTER_ID1 ((ID1 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_MASK1 ((ID2 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_ID2 ((ID3 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_MASK2 ((ID4 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_ID3 ((ID5 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_MASK3 ((ID6 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_ID4 ((ID7 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) #define FILTER_MASK4 ((ID8 << 3) | (REMOTE_FRAME<<1) | (EXTID <<2)) |