日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32 CAN过滤器

發布時間:2024/1/23 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32 CAN过滤器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 1 前言
  • 2 幾個重要的概念
    • 2.1 為什么要過濾器?
    • 2.2 兩種過濾模式(列表模式與掩碼模式)
    • 2.3 驗證碼與屏蔽碼
    • 2.4 列表模式與掩碼模式的對比
    • 2.5 標準CAN ID與擴展CAN ID
  • 3 設置過濾器模式
    • 3.1 32位寬的掩碼模式
    • 3.2 32位寬的列表模式
    • 3.3 16位寬的掩碼模式
    • 3.4 16位寬的列表模式
  • 4 過濾器匹配序號
  • 5 過濾器優先級規則
  • 6 總結

1 前言

STM32F10X的bxCAN是基本擴展CAN(Basic Extended CAN)的縮寫,它支持CAN協議2.0A和2.0B。
在CAN協議里,報文的標識符不代表節點的地址,而是和報文的內容相關的。因此,發送者以廣播的形式把報文發送給所有的接收者。節點在接收報文時,根據標識符(CAN ID)的值決定軟件是否需要該報文;如果需要,就拷貝到SRAM里;如果不需要,報文就被丟棄且無需軟件的干預。
為滿足這一需求,在互聯型產品中, bxCAN控制器為應用程序提供了28個位寬可變的、可配置的過濾器組(27~0);在其它產品中,bxCAN為應用程序提供了14個位寬可變的、可配置的過濾器組(13~0),以便只接收那些軟件需要的報文。硬件過濾的做法節省了CPU開銷,否則就必須由軟件過濾從而占用一定的CPU開銷。每個過濾器組x由2個32位寄存器,CAN_FxR0和CAN_FxR1組成。
[互聯型產品是指STM32F105xx和STM32F107xx微控制器。]

為了讓大家了解STM32的bxCAN的接收過濾機制,首先大家需要了解幾個概念。

2 幾個重要的概念

2.1 為什么要過濾器?

在這里,我們可以將CAN總線看成一個廣播消息通道,上面傳輸著各種類型的消息,好比報紙,有體育新聞,財經新聞,政治新聞,還有軍事新聞,每個人都有自己的喜好,不一定對所有新聞都感興趣,因此,在看報紙的時候,一般人都是只看自己感興趣的那類新聞,而過濾掉其他不感興趣的內容。那么我們一般是怎么過濾掉那些不感興趣的內容的呢?下面有兩種方法來實現這個目的:

第一種方法:
每次看報紙時,你都看下每篇文章的標題[文章標題就類似CAN ID即標識符],如果感興趣則繼續看下去,如果不感興趣,則忽略掉。

第二種方法:
你告訴郵遞員[郵遞員就是過濾器],你只對財經新聞感興趣,請只將財經類報紙送過來,其他的就不要送過來了,就這樣,你看到的內容必定是你感興趣的財經類新聞。

上面那種方法好呢?很明顯,第二種方法是最好的,因為你不用自己每次判斷哪些新聞內容是你感興趣的,可以免受“垃圾”新聞干擾,從而可以節省時間忙其他事。bxCAN的過濾器就是采用上述第二種方法,你只需要設置好你感興趣的那些CAN報文ID,那么MCU就只能收到這些CAN報文,是從硬件上過濾掉,完全不需要軟件參與進來,從而節省了大大節省了MCU的時間,可以更加專注于其他事務,這個就是bxCAN過濾器的意義所在。

2.2 兩種過濾模式(列表模式與掩碼模式)

假設我們是bxCAN這個IP的設計者,現在由我們來設計過濾器,那么我們該如何設計呢?

首先我們是不是很快就會想到只要準備好一張表,把我們需要關注的所有CAN報文ID寫上去,開始過濾的時候只要對比這張表,如果接收到的報文ID與表上的相符,則通過,如果表上沒有,則不通過,這個就是簡單的過濾方案。恭喜你!bxCAN過濾器的列表模式采用的就是這種方案。

但是,這種列表方案有點缺陷,即如果我們只關注一個報文ID,則需要往列表中寫入這個ID,如果需要關注兩個,則需要寫入兩個報文ID,如果需要關注100個,則需要寫入100個,如果需要1萬個,那么需要寫入1萬個,可問題是,有這個大的列表供我們使用嗎?大家都知道,MCU上的資源是有限的,不可能提供1萬個或更多,甚至100個都嫌多。非常明顯,這種列表的方式受到列表容量大小的限制,實際上,bxCAN的一個過濾器若工作在列表模式下,scale為32時,每個過濾器的列表只能寫入兩個報文ID,若scale為16時,每個過濾器的列表最多可寫入4個CAN ID,由此可見,MCU的資源是非常非常有限的,并不能任我們隨心所欲。因此,我們需要考慮另外一種替代方案,這種方案應該不受到數量限制。

下面假設我們是古時候一座城鎮的守衛,城主要求只有1156年出生的人才可以進城,我們又該如何執行呢?假設古時候的人也有類似今天的身份證,大家都知道,身份份證號碼中有4位是表示出生年月,如下圖:

如上圖,身份證中第7~10這4位數表示的是出生年份,那么,我們可以這么執行:

檢查想要進城的所有人的身份證號碼的第7~10位數字,如果這個數字依次為1156則可以進入,否則則不可以,至于身份證號碼的其他位則完全不關心。假如過幾天城主放寬進城條件為只要是1150年~1160前的人都可以進城,那么,我們就可以只關注身份證號碼的第7~9這3位數是否為115就可以了,對不對?這樣一來,我們就可以非常完美地執行城主的要求了。

再變下,假設現在使用機器來當守衛,不再是人來執行這個“篩選”工作。機器是死的,沒有人那么靈活,那么機器又該如何執行呢?

對于機器來說,每一步都得細化到機器可以理解的程度,于是我們可以作如下細化:

第一步:獲取想進城的人的身份證號碼

第二步:只看獲取到身份證的第7~9位,其他位忽略

第三步:將忽略后的結果與1156進行比較

第四步:比較結果相同則通過,不同則不能通過

這種方式,我們稱之為掩碼模式。

2.3 驗證碼與屏蔽碼

對于機器來說,我們要為它準備好兩張紙片,一片寫上屏蔽碼,另一片紙片寫上驗證碼,屏蔽碼上相應位為1時,表示此位需要與驗證碼對應位進行比較,反之,則表示不需要。機器在執行任務的時候先將獲取的身份證號碼與屏蔽碼進行“與”操作,再將結果與驗證碼的進行比較,根據判斷是否相同來決定是否通過。整個判別流程如下所示:

從上圖可以很容易地理解屏蔽碼與驗證碼的含義,這樣一來,能通過的結果數量就完全取決于屏蔽碼,設得寬,則可以通過的多(所有位為0,則不過任何過濾操作,則誰都可以通過),設得窄,則通過的少(所有位設為1,則只有一個能通過)。那么知道這個有什么用呢?因為bxCAN的過濾器的掩碼模式就是采用這種方式,在bxCAN中,分別采用了兩個寄存器(CAN_FiR1,CAN_FiR2)來存儲屏蔽碼與驗證碼,從而實現掩碼模式的工作流程的。這樣,我們就知道了bxCAN過濾器的掩碼模式的大概工作原理。

但是,我們得注意到,采用掩碼模式的方式并不能精確的對每一個ID進行過濾,打個比方,還是采用之前的守衛的例子,假如城主要求只有1150~1158年出生的人能通過,那么,若我們還是才用掩碼模式,那么掩碼就設為第79位為”1”,對應的,驗證碼的79位分別為”115”,這樣就可以了。但是,仔細一想,出生于1159的人還是可以通過,是不是?但總體來說,雖然沒有做到精確過濾,但我們還是能做到大體過濾的,而這個就是掩碼模式的缺點了。在實際應用時,取決于需求,有時我們會同時使用到列表模式和掩碼模式,這都是可能的。

2.4 列表模式與掩碼模式的對比

綜合之前所述,下面我們來對比一下列表模式與掩碼模式這兩種模式的優缺點。

模式優點缺點
列表模式能精確地過濾每個指定的CAN ID有數量限制
掩碼模式取決于屏蔽碼,有時無法完全精確到每一個CAN ID,部分不期望的CAN ID有時也會收到數量取決于屏蔽碼,最多無上限

2.5 標準CAN ID與擴展CAN ID

1986 年德國電氣商BOSCH公司開發出面向汽車的CAN 通信協議,剛開始的時候,CAN ID定義為11位,我們稱之為標準格式,ISO11898-1標準中CAN的基本格式如下圖所示:

如上圖所示,標準CAN ID存放在上圖ID18~ID28中,共11位。隨著工業發展,后來發現11位的CAN ID已經不夠用,于是就增加了18位,擴展CAN ID到29位,如下圖所示:

從上圖對比擴展CAN報文與標準CAN報文,發現在仲裁域部分,擴展CAN報文的CAN ID包含了base Identifier與extension Identifier,即基本ID與擴展ID,而標準CAN報文的CAN ID部分只包含基本ID,擴展ID(ID0~ID17)被放在基本ID的右方,也就是說,屬于低位。知道這些有什么用呢?至少我們可以得到這兩條信息:

  • 標準ID一般小于或等于<=0x7FF(11位),只包含基本ID。
  • 對于擴展CAN的低18位為擴展ID,高11位為基本ID。

例如標準CAN ID 0x7E1,二進制展開為111 1110 0001 ,其全部是基本ID。

再例如擴展CAN ID 0x1835f107,二進制展開為[1 1000 0011 10][01 11110001 0000 0111],總共29位,左邊紅色中括號中的11位為基本ID,右邊綠色中括號內的18位為擴展ID,請記住這個信息!知道這個之后,我們可以很方便地將一個CANID拆分成基本ID和擴展ID,這個也將在后續的內容中多次用到,再次留意一下,擴展ID是位于基本ID的右方,在擴展CAN ID的構成中,擴展ID位于低18位,而基本ID位于高11位,于是要獲取一個擴展CANID的基本ID,就只需要將這個CANID右移18位。

3 設置過濾器模式

下圖來自STM32F10X參考手冊:

上圖中可以看出,過濾器一共有4種工作模式:32位寬的掩碼模式,32位寬的列表模式, 16位寬的列表模式,16位寬的掩碼模式
下面將分別介紹過濾器的4種工作模式:

3.1 32位寬的掩碼模式


如上圖所示,當寄存器FSC0 = 1; FBM0 = 0; 則0號過濾器處于32位寬的掩碼模式,這時F0R1需配置ID即前面說的驗證碼,F0R2需配置掩碼即前面說的屏蔽碼;其余幾個濾波器只是x從1變到13,對應不同的寄存器分別進行配置即可。最右邊的num表示這兩個32位寄存器都是配置同一個濾波器。
最下邊顯示的是與CAN ID各位對應的映射關系。上圖最下邊的映射關系恰好等于擴展CAN ID左移3位再補上IDE(擴展幀標識),RTR(遠程幀標志)。注意看上圖最底下的各位定義,可以看出,從右到左,首先,最低位是沒有用的,然后是RTR,表示是否為遠程幀,接著IDE,擴展幀標志,然后才是EXID[0:17]這18位擴展ID,最后才是STID[0:10]這11位標準ID,也就是前面所說的基本ID。在進行配置的時候,即將希望通過的CAN ID寫入的時候,要注意各個位對號入座,即基本ID放到對應的STD[0:10],擴展ID對應放到EXID[0:17],若是擴展幀,則需要將IDE設為“1”,標準幀則為“0”,數據幀設RTR為“0”,遠程幀設RTR為“1”。

static void CANFilterConfig_Scale32_IdMask(void) {CAN_FilterInitTypeDef CAN_FilterInitStructure;U16 std_id =0x7e9;U32 ext_id =0x1800f001;U32 mask =0;CAN_FilterInit(&CAN_FilterInitStructure); //初始化CAN_FilterInitStructrue結構體變量CAN_FilterInitStructure.CAN_FilterNumber=0; //設置過濾器組0,范圍為0~13CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //設置過濾器組0為屏蔽模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //設置過濾器組0位寬為32位//標識位寄存器的設置//ext_id<<3對齊,見上圖9,再>>16取高16位CAN_FilterInitStructure.CAN_FilterIdHigh=((ext_id<<3) >>16) &0xffff; //設置標識符寄存器高字節。CAN_FilterInitStructure.CAN_FilterIdLow=(U16)(ext_id<<3) | CAN_ID_EXT; //設置標識符寄存器低字節//如果要過濾標準幀,這里也可以這樣設置//CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5; //設置標識符寄存器高字節.這里為什么是左移5位呢?從上圖可以看出,CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],標準CAN ID本身是不包含擴展ID數據,因此為了要將標準CAN ID放入此寄存器,標準CAN ID首先 應左移5位后才能對齊.EXID[13~17]是5位//CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_STD; //設置標識符寄存器低字節//屏蔽寄存器的設置//這里的思路是先將標準CAN ID和擴展CAN ID對應的ID值先異或后取反,為什么?異或是為了找出兩個CAN ID有哪些位是相同的,是相同的位則說明需要關心,需要關心的位對應的屏蔽碼位應該設置為1,因此需要取反一下。最后再整體左移3位。mask =(std_id<<18);//這里為什么左移18位?因為從ISO11898中可以看出,標準CAN ID占ID18~ID28,為了與CAN_FilterIdHigh對齊,應左移2位,接著為了與擴展CAN對應,還應該再左移16位,因此,總共應左移2+16=18位。也可以用另一個方式來理解:直接看Mapping的內容,發現STDID相對EXID[0]偏移了18位,因此左移18位.mask ^=ext_id;//將對齊后的標準CAN與擴展CAN異或后取反mask =~mask;mask <<=3;//再整體左移3位mask |=0x02; //只接收數據幀,不接收遠程幀CAN_FilterInitStructure.CAN_FilterMaskIdHigh=(mask>>16)&0xffff; //設置屏蔽寄存器高字節CAN_FilterInitStructure.CAN_FilterMaskIdLow=mask&0xffff; //設置屏蔽寄存器低字節CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0; //此過濾器組關聯到接收FIFO0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此過濾器組CAN_FilterInit(&CAN_FilterInitStructure); //設置過濾器 }

上面的CAN_FilterIdHigh,CAN_FilterIdLow,CAN_FilterMaskIdHigh,CAN_FilterMaskIdLow四個參數是如何對應到CAN_FxR1和CAN_FxR2的配置,需要看庫里面的CAN_FilterInit函數
貼出CAN_FilterInit函數即可以理解上面的代碼注釋中多次提到的移位對齊。

/* Filter Scale */if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_16bit){/* 16-bit scale for the filter */CAN1->FS1R &= ~(uint32_t)filter_number_bit_pos;/* First 16-bit identifier and First 16-bit mask *//* Or First 16-bit identifier and Second 16-bit identifier */CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 = ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdLow) << 16) |(0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdLow);/* Second 16-bit identifier and Second 16-bit mask *//* Or Third 16-bit identifier and Fourth 16-bit identifier */CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 = ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) << 16) |(0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh);}if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_32bit){/* 32-bit scale for the filter */CAN1->FS1R |= filter_number_bit_pos;/* 32-bit identifier or First 32-bit identifier */CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 = ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh) << 16) |(0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdLow);/* 32-bit mask or Second 32-bit identifier */CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 = ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) << 16) |(0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdLow);}

以上的標準庫底層代碼總結就是:
如果是16位寬,則CAN_FilterMaskIdLow是配置FR1的高16位,CAN_FilterIdLow是配置FR1的低16位,CAN_FilterMaskIdHigh是配置FR2的高16位,CAN_FilterIdHigh是配置FR2的低16位。
如果是32位寬,則CAN_FilterIdHigh是配置FR1的高16位,CAN_FilterIdLow是配置FR1的低16位,CAN_FilterMaskIdHigh是配置FR2的高16位,CAN_FilterMaskIdLow是配置FR2的低16位。

注意:初始化結構體參數中CAN_FilterMaskIdLow和CAN_FilterMaskIdHigh有可能是作ID,有可能是作掩碼,具體要看情況(看處于哪種模式,對照前面的模式圖表)。

關于驗證碼與屏蔽碼的概念前面已經明確說明了,不清楚的可以回過去看看前面的內容。在32位寬的掩碼模式下,既可以過濾標準CAN ID,也可以過濾擴展CAN ID,甚至兩者混合著來也是可以的,下面我們就這3種情況分別給出示例。

【1、只針對標準CAN ID】
如下代碼示例:
注:下面使用HAL庫

static void CANFilterConfig_Scale32_IdMask_StandardIdOnly(void) {CAN_FilterConfTypeDef sFilterConfig;uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定義一組標準CAN IDuint16_t mask,num,tmp,i;sFilterConfig.FilterNumber = 2; //使用過濾器2sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置為掩碼模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //設置為32位寬sFilterConfig.FilterIdHigh =(StdIdArray[0]<<5); //驗證碼可以設置為StdIdArray[]數組中任意一個,這里使用StdIdArray[0]作為驗證碼sFilterConfig.FilterIdLow =0;mask =0x7ff; //下面開始計算屏蔽碼num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);for(i =0; i<num; i++) //屏蔽碼位StdIdArray[]數組中所有成員的同或結果{tmp =StdIdArray[i] ^ (~StdIdArray[0]); //所有數組成員與第0個成員進行同或操作mask &=tmp;}sFilterConfig.FilterMaskIdHigh =(mask<<5);sFilterConfig.FilterMaskIdLow =0|0x02; //只接收數據幀sFilterConfig.FilterFIFOAssignment = 0; //設置通過的數據幀進入到FIFO0中sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

如上代碼所示,對于驗證碼,任意一個期望通過的CAN ID都是可以設為驗證碼的,但屏蔽碼,卻是所有期望通過的CAN ID相互同或后的最終結果。
注:對于移位不明白的參考前面模式圖中的的Mapping映射加以理解。

【2、只針對擴展CAN ID】
如下代碼示例:

static void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void) {CAN_FilterConfTypeDef sFilterConfig;//定義一組擴展CAN ID用來測試 uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};uint32_t mask,num,tmp,i;sFilterConfig.FilterNumber = 3; //使用過濾器3sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置為掩碼模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //設為32位寬sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//數組任意一個成員都可以作為驗證碼sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;mask =0x1fffffff;num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);for(i =0; i<num; i++) //屏蔽碼位數組各成員相互同或的結果{tmp =ExtIdArray[i] ^ (~ExtIdArray[0]); //都與第一個數據成員進行同或操作mask &=tmp;}mask <<=3; //對齊寄存器sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02; //只接收數據幀sFilterConfig.FilterFIFOAssignment = 0;sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

如上代碼所示,與之前的標準CAN ID相比,擴展CAN ID的驗證碼與屏蔽碼放入到相對應的寄存器時所移動的位數與標準CAN ID時有所差別,其他的都一樣。
注:對于移位不明白的參考前面模式圖中的的Mapping映射加以理解。

【3、標準CAN ID與擴展CAN ID混合過濾】

static void CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(void) {CAN_FilterConfTypeDef sFilterConfig;//定義一組標準CAN ID uint32_t StdIdArray[10] ={0x711,0x712,0x713,0x714,0x715,0x716,0x717,0x718,0x719,0x71a};//定義另外一組擴展CAN ID uint32_t ExtIdArray[10] ={0x1900fAB1,0x1900fAB2,0x1900fAB3,0x1900fAB4,0x1900fAB5,0x1900fAB6,0x1900fAB7,0x1900fAB8,0x1900fAB9,0x1900fABA};uint32_t mask,num,tmp,i,standard_mask,extend_mask,mix_mask;sFilterConfig.FilterNumber = 4; //使用過濾器4sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置為掩碼模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //設為32位寬sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff; //使用第一個擴展CAN ID作為驗證碼sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff);standard_mask =0x7ff; //下面是計算屏蔽碼num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);for(i =0; i<num; i++) //首先計算出所有標準CAN ID的屏蔽碼{tmp =StdIdArray[i] ^ (~StdIdArray[0]);standard_mask &=tmp;}extend_mask =0x1fffffff;num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);for(i =0; i<num; i++) //接著計算出所有擴展CAN ID的屏蔽碼{tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);extend_mask &=tmp;}mix_mask =(StdIdArray[0]<<18)^ (~ExtIdArray[0]); //再計算標準CAN ID與擴展CAN ID混合的屏蔽碼mask =(standard_mask<<18)& extend_mask &mix_mask; //最后計算最終的屏蔽碼mask <<=3; //對齊寄存器sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;sFilterConfig.FilterMaskIdLow = (mask&0xffff);sFilterConfig.FilterFIFOAssignment = 0;sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

如上代碼所示,在混合的情況下,只需稍微修改下屏蔽碼的計算方式就可以了,其他的基本沒有什么變化。
注:對于移位不明白的參考前面模式圖中的的Mapping映射加以理解。

3.2 32位寬的列表模式


如上圖所示,當寄存器FSC0 = 1; FBM0 = 1; 則0號過濾器處于32位寬的列表模式,這個過濾器一分為2可以保存兩個CAN ID; 這時F0R1和F0R2都是配置ID即前面說的驗證碼;其余幾個濾波器只是x從1變到13,一共14個,對應不同的寄存器分別進行配置即可。最右邊的num表示這兩個32位寄存器分別配置2個濾波器。
注意看上圖最底下的Mapping各位定義,可以看出,從右到左,首先,最低位是沒有用的,然后是RTR,表示是否為遠程幀,接著IDE,擴展幀標志,然后才是EXID[0:17]這18位擴展ID,最后才是STID[0:10]這11位標準ID,也就是前面所說的基本ID。在進行配置的時候,即將希望通過的CAN ID寫入的時候,要注意各個位對號入座,即基本ID放到對應的STD[0:10],擴展ID對應放到EXID[0:17],若是擴展幀,則需要將IDE設為“1”,標準幀則為“0”,數據幀設RTR為“0”,遠程幀設RTR為“1”。示例代碼如下:
使用標準庫:

static void CANFilterConfig_Scale32_IdList(void) {CAN_FilterInitTypeDef CAN_FilterInitStructure;U16 std_id =0x7e9;U32 ext_id =0x1800f001;CAN_FilterInit(&CAN_FilterInitStructure); //初始化CAN_FilterInitStructrue結構體變量CAN_FilterInitStructure.CAN_FilterNumber=0; //設置過濾器組0,范圍為0~13CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //設置過濾器組0為標識符列表模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //設置過濾器組0位寬為32位//設置屏蔽寄存器,這里當標識符寄存器用CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5) ; //為什么左移5位?與上面相同道理,這里不再重復解釋CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_STD; //設置標識符寄存器低字節,CAN_FilterIdLow的ID位可以隨意設置,在此模式下不會有效。//設置標識符寄存器CAN_FilterInitStructure.CAN_FilterMaskIdHigh=((ext_id<<3)>>16) & 0xffff; //設置屏蔽寄存器高字節CAN_FilterInitStructure.CAN_FilterMaskIdLow=((ext_id<<3)& 0xffff) | CAN_ID_EXT; //設置屏蔽寄存器低字節CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0; //此過濾器組關聯到接收FIFO0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此過濾器組CAN_FilterInit(&CAN_FilterInitStructure); //設置過濾器 }

使用HAL庫:

static void CANFilterConfig_Scale32_IdList(void) {CAN_FilterConfTypeDef sFilterConfig;uint32_t StdId =0x321; //這里寫入兩個CAN ID,一個位標準CAN IDuint32_t ExtId =0x1800f001; //一個位擴展CAN IDsFilterConfig.FilterNumber = 0; //使用過濾器0sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //設為列表模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //配置為32位寬sFilterConfig.FilterIdHigh = StdId<<5; //基本ID放入到STID中sFilterConfig.FilterIdLow = 0|CAN_ID_STD; //設置IDE位為0sFilterConfig.FilterMaskIdHigh = ((ExtId<<3)>>16)&0xffff;sFilterConfig.FilterMaskIdLow = (ExtId<<3)&0xffff|CAN_ID_EXT; //設置IDE位為1sFilterConfig.FilterFIFOAssignment = 0; //接收到的報文放入到FIFO0中sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

上面對兩種庫的配置可以看到都差不多。
這里需要說明一下,由于我們使用的是HAL庫,在HAL庫中,CAN_FxR1與CAN_FxR2寄存器分別被拆成兩段,CAN_FxR1寄存器的高16位對應著上面代碼中的FilterIdHigh,低16位對應著FilterIdLow,而CAN_FxR2寄存器的高16位對應著FilterMaskIdHigh,低16位對應著FilterMaskIdLow,這個CAN_FilterConfTypeDef的的4個成員FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow,不應該單純看其名字,被其名字誤導,而應該就單純地將這4個成員看成4個uint_16類型的變量x,y,m,n而已,后續其他示例也是同樣理解,不再重復解釋。這4個16位的變量其具體含義取決于當前過濾器工作與何種模式,比如當前32位寬的列表模式下,FilterIdHigh與FilterIdLow一起用來存放一個CAN ID,FilterMaskIdHigh與FilterMaskIdLow用來存放另一個CAN ID,不再表示其字面所示的mask含義,這點我們需要特別注意。

在上述代碼示例中,我們分別將標準CAN ID和擴展CAN ID放入到CAN_FxR1與CAN_FxR2寄存器中。對于標準CAN ID,對比 圖11,由于標準CAN ID只擁有標準ID,所以,只需要將標準ID放入到高16位的STID[0:10]中,高16位最右邊被EXID[13:17]占著,因此,需要將StdId左移5位才能剛好放入到CAN_FxR1的高16位中,于是有了:
sFilterConfig.FilterIdHigh = StdId<<5;

另一個擴展CAN ID ExtId類型,將其基本ID放入到STID中,擴展ID放入到EXID中,最后設置IDE位為1。就這樣配置好了。

3.3 16位寬的掩碼模式


如上圖所示,當寄存器FSC0 = 0; FBM0 = 0; 則0號過濾器處于16位寬的掩碼模式,這個過濾器一分為2可以保存兩個CAN ID和兩個掩碼;
CAN_FxR1的低16位是作為驗證碼,對應的16位屏蔽碼為CAN_FxR1的高16位,同樣的,CAN_FxR2的低16位是作為驗證碼,對應與CAN_FxR2的高16位為屏蔽碼。
其示例代碼如下:

static void CANFilterConfig_Scale16_IdMask(void) {CAN_FilterConfTypeDef sFilterConfig;uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5, //定義第一組標準CAN ID0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定義第二組標準CAN ID0x756,0x757,0x758,0x759,0x75A};uint16_t mask,tmp,i,num;sFilterConfig.FilterNumber = 5; //使用過濾器5sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置為掩碼模式sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //設為16位寬//配置第一個過濾對sFilterConfig.FilterIdLow =StdIdArray1[0]<<5; //設置第一個驗證碼mask =0x7ff;num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);for(i =0; i<num; i++) //計算第一個屏蔽碼{tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);mask &=tmp;}sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10; //只接收數據幀//配置第二個過濾對sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5; //設置第二個驗證碼mask =0x7ff;num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);for(i =0; i<num; i++) //計算第二個屏蔽碼{tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);mask &=tmp;}sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10; //只接收數據幀sFilterConfig.FilterFIFOAssignment = 0; //通過的CAN 消息放入到FIFO0中sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

如上代碼所示,在這種模式下,其特殊之處就是可以配置兩套驗證碼,屏蔽碼組合,可以分別相對獨立地對標準CAN ID進行過濾。

3.4 16位寬的列表模式


如上圖所示,當寄存器FSC0 = 0; FBM0 = 1; 則0號過濾器處于16位寬的列表模式,這個過濾器一分為4可以保存4個CAN ID;
在16位寬的列表模式下,FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow這4個16位變量都是用來存儲一個標準CAN ID,這樣,就可以存放4個標準CAN ID了,需要注意地是,此種模式下,不能處理擴展CAN ID,凡是需要過濾擴展CAN ID的,都是需要用到32位寬的模式。代碼示例:

static void CANFilterConfig_Scale16_IdList(void) {CAN_FilterConfTypeDef sFilterConfig;uint32_t StdId1 =0x123; //這里采用4個標準CAN ID作為例子uint32_t StdId2 =0x124;uint32_t StdId3 =0x125;uint32_t StdId4 =0x126;sFilterConfig.FilterNumber = 1; //使用過濾器1sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //設為列表模式sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //位寬設置為16位sFilterConfig.FilterIdHigh = StdId1<<5; //4個標準CAN ID分別放入到4個存儲中sFilterConfig.FilterIdLow = StdId2<<5;sFilterConfig.FilterMaskIdHigh = StdId3<<5;sFilterConfig.FilterMaskIdLow = StdId4<<5;sFilterConfig.FilterFIFOAssignment = 0; //接收到的報文放入到FIFO0中sFilterConfig.FilterActivation = ENABLE;sFilterConfig.BankNumber = 14;if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();} }

4 過濾器匹配序號

一旦收到的報文被存入FIFO,就可被應用程序訪問。通常情況下,報文中的數據被拷貝到SRAM中;為了把數據拷貝到合適的位置,應用程序需要根據報文的標識符來辨別不同的數據。
bxCAN提供了過濾器匹配序號,以簡化這一辨別過程。
根據過濾器優先級規則,過濾器匹配序號和報文一起,被存入郵箱中。因此每個收到的報文,都有與它相關聯的過濾器匹配序號。過濾器匹配序號可以通過下面兩種方式來使用:
● 把過濾器匹配序號跟一系列所期望的值進行比較
● 把過濾器匹配序號當作一個索引來訪問目標地址
對于標識符列表模式下的過濾器(非屏蔽方式的過濾器),軟件不需要直接跟標識符進行比較。
對于屏蔽位模式下的過濾器,軟件只須對需要的那些屏蔽位(必須匹配的位)進行比較即可。
在給過濾器編號時,并不考慮過濾器組是否為激活狀態。另外,每個FIFO各自對其關聯的過濾 器進行編號。請參考下圖的例子。

從上圖可以看出,兩個FIFO右邊的濾波器序號是順序累加的,與其關聯的濾波器是哪個Bank無關。但由于是順序累加的,所以與其關聯的濾波器的工作模式有關系,因為決定了這個濾波器要分配幾個序號。

小結:
當有一條報文通過過濾器匹配OK,過濾器匹配序號和報文一起,被存入郵箱中。應用程序可以讀取該條報文的匹配序號快速確認對應的是哪個濾波器,比如是個列表模式濾波器則直接可以確定其ID值,通過匹配序號用得好可以簡化對報文ID的比較過程。
具體如何使用過濾器匹配序號:
● 把過濾器匹配序號跟一系列所期望的值進行比較
● 把過濾器匹配序號當作一個索引來訪問目標地址

5 過濾器優先級規則

根據過濾器的不同配置,有可能一個報文標識符能通過多個過濾器的過濾;在這種情況下,存
放在接收郵箱中的過濾器匹配序號,根據下列優先級規則來確定:
● 位寬為32位的過濾器,優先級高于位寬為16位的過濾器
● 對于位寬相同的過濾器,標識符列表模式的優先級高于屏蔽位模式
● 位寬和模式都相同的過濾器,優先級由過濾器號決定,過濾器號小的優先級高

上面的例子說明了bxCAN的過濾器規則:在接收一個報文時,其標識符首先與配置在標識符列表模式下的過濾器相比較;如果匹配上,報文就被存放到相關聯的FIFO中,并且所匹配的過濾器的序號被存入過濾器匹配序號中。如同例子中所顯示,報文標識符跟#4標識符匹配,因此報文內容和FMI4被存入FIFO。
如果沒有匹配,報文標識符接著與配置在屏蔽位模式下的過濾器進行比較。
如果報文標識符沒有跟過濾器中的任何標識符相匹配,那么硬件就丟棄該報文,且不會對軟件有任何打擾。

小結:
在接收一個報文時,其標識符首先與配置在標識符列表模式下的過濾器相比較,順序是根據Filer Num從小到大且32位濾波器優先,如果都沒有匹配,報文標識符接著與配置在屏蔽位模式下的過濾器進行比較。

6 總結

在實際的應用中,我們需要根據需求的實際情況來決定使用何種過濾配置,STM32互聯型的bxCAN提供了28個過濾器,在配置之前,我們需要先將那些需要通過的CAN ID進行整理,若數量少,則使用列表模式,精準,不會放進任何無關的報文;若只有標準CAN ID,則可以考慮使用16位寬模式,若需求中的CAN ID過多,則可以考慮使用多個過濾器,部分使用列表模式,部分使用掩碼模式,CAN ID值相近的可以歸納成一組,使用掩碼模式進行過濾。但使用掩碼模式的同時,我們也需要意識到,也有可能部分不期望的CAN ID也會通過過濾器,掩碼放得越寬,帶進其他CAN ID的幾率就越大,這點我們需要格外注意,視情況進行應用判斷和處理。另外,對于相近的CAN ID,我們可以提前計算好屏蔽碼,直接在代碼中填入,而不是在代碼中臨時計算,這樣可以提高軟件效率。

參考鳴謝:
https://blog.csdn.net/flydream0/article/details/8148791

https://blog.csdn.net/flydream0/article/details/52317532

總結

以上是生活随笔為你收集整理的STM32 CAN过滤器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。