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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结

發(fā)布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 前言

bxCAN是STM32系列最穩(wěn)定的IP核之一,無論有哪個新型號出來,這個IP核基本未變,可見這個IP核的設計是相當成熟的。本文所講述的內容屬于這個IP核的一部分,掌握了本文所講內容,就可以很方便地適用于所有STM32系列中包含bxCAN外設的型號。有關bxCAN的過濾器部分的內容在參考手冊中往往看得“不甚明白“,本文就過濾器的4種工作模式進行詳細講解并使用具體的代碼進行演示,這些代碼都進行過實測驗證通過的,希望能給讀者對于bxCAN過濾器有一個清晰的理解。

?

?

2. 準備工作

2.1.???為什么要過濾器?

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

第一種方法

???????? 每次看報紙時,你都看下每篇文章的標題,如果感興趣則繼續(xù)看下去,如果不感興趣,則忽略掉。

第二種方法

???????? 你告訴郵遞員,你只對財經新聞感興趣,請只將財經類報紙送過來,其他的就不要送過來了,就這樣,你看到的內容必定是你感興趣的財經類新聞。

上面那種方法好呢?很明顯,第二種方法是最好的,因為你不用自己每次判斷哪些新聞內容是你感興趣的,可以免受“垃圾”新聞干擾,從而可以節(jié)省時間忙其他事。bxCAN的過濾器就是采用上述第二種方法,你只需要設置好你感興趣的那些CAN報文ID,那么MCU就只能收到這些CAN報文,是從硬件上過濾掉,完全不需要軟件參與進來,從而節(jié)省了大大節(jié)省了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的資源是非常非常有限的,并不能任我們隨心所欲。因此,我們需要考慮另外一種替代方案,這種方案應該不受到數量限制。

?

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

圖 1 18位身份證號碼的各位定義

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

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

?

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

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

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

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

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

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

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

2.3.???驗證碼與屏蔽碼

仔細查看上面4個步驟,這不就是C代碼中的if語句嗎?如下:

?

  • if( x & y ==z) //x表示待檢查身份證號碼,y表示只關注第7~9位的屏蔽碼,Z則為1156,這里叫做驗證碼

  • {

  • //可以通過

  • }

  • else

  • {

  • //不可以通過

  • }

  • ?

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

    圖 2 掩碼模式的計算過程

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

    ?

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

    ?

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

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

    ?

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

    ?

    2.5.???標準CAN ID與擴展CAN ID

    ?

    ?

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

    圖 3 標準CAN報文格式定義

    ?

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

    ?

    圖 4 擴展CAN報文格式定義

    從上圖對比擴展CAN報文與標準CAN報文,發(fā)現在仲裁域部分,擴展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,二進制展開為0b 0[111 1110 0001]?,只有中括號內的11位才有效,其全部是基本ID。

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

    3. bxCAN的過濾器的解決方案

    終于進入到正題了!前面已經介紹了過濾器的列表模式與掩碼模式,以及掩碼模式下的屏蔽碼與驗證碼的含義,還介紹了標準CAN ID與擴展CAN ID的組成部分。現在我們終于要站在bxCAN的角度來分析其過濾方案。

    首先過濾模式分列表模式和掩碼模式,因此,對于沒有過濾器,我們需要這么一個位來標記,用戶可以通過設置這個位來標記他到底是想要這個過濾器工作在列表模式下還是掩碼模式,于是,這個表示過濾模式的位就定義在CAN_FM1R寄存器中的FBMx位上,如下圖:

    ?

    圖5 CAN過濾器模式寄存器CAN_FM1R定義

    ?

    ?

    這里以STM32F407為例,bxCAN共有28個過濾器,于是上圖的每一個位對應地表示這28個過濾器的工作模式,供用戶設置。”0”表示掩碼模式,”1”表示列表模式。

    ?

    另外,我們知道了標準CAN ID位11位,而擴展CAN ID有29位,對于標準的CAN ID來說,我們有一個16位的寄存器來處理他足夠了,相應地,擴展CAN ID,我們就必須使用32位的寄存器來處理它,而在實際應用中,根據需求,我們可能自始至終都只需要處理11位的CAN ID。對于資源嚴重緊張的MCU環(huán)境來說,本著不浪費的原則,這里最好能有另外一個標志用告訴過濾器是否需要處理32位的CAN ID。于是,bxCAN處于這種考慮,也設置了這么一個寄存器CAN_FS1R來表示CAN ID的位寬,如下圖所示:

    ?

    圖6 CAN過濾器位寬寄存器CAN_FS1R定義

    ?

    ?

    如上圖,每一個位對應著bxCAN中28個過濾器的位寬,這個需要用戶來設置。

    于是根據模式與位寬的設置,我們共可以得出4中不同的組合:32位寬的列表模式,16位寬的列表模式,32位寬掩碼模式,16位寬的掩碼模式。如下圖所示:

    ?

    圖 7 CAN過濾器的4中工作模式

    ?

    ?

    在bxCAN中,每個過濾器都存在這么兩個寄存器CAN_FxR1和CAN_FxR2,這兩個寄存器都是32位的,他的定義并不是固定的,針對不同的工作模式組合他的定義是不一樣的,如列表模式-32位寬模式下,這兩個寄存器的各位定義都是一樣的,都用來存儲某個具體的期望通過的CAN ID,這樣就可以存入2個期望通過的CAN ID(標準CAN ID和擴展CAN ID均可);若在掩碼模式-32位寬模式下時,則CAN_FxR1用做32位寬的驗證碼,而CAN_FxR2則用作32位寬的屏蔽碼。在16位寬時,CAN_FxR1和CAN_FxR2都要各自拆分成兩個16位寬的寄存器來使用,在列表模式-16位寬模式下,CAN_FxR1和CAN_FxR2定義一樣,且各自拆成兩個,則總共可以寫入4個標準CAN ID,若在16位寬的掩碼模式下,則可以當做2對驗證碼+屏蔽碼組合來用,但它只能對標準CAN ID進行過濾。這個就是bxCAN過濾器的解決方案,它采用了這4種工作模式。

    ?

    本著從易到難得目的,下面我們將依次介紹如何使用bxCAN的這4種工作模式并給出對應的代碼示例.

    ?

    4. 應用實例

    4.1.???工程建立及主體代碼

    本文硬件采用STM3240G-EVAL評估板和ZLG的USBCAN-2E-U及其配套的軟件工具CANTest來實現對MCU進行CAN報文的發(fā)送。工程使用STM32CubeMx自動生成:

    ?

    引腳如下:

    PD0: CAN1_Rx

    PD1: CAN1_Tx

    PG6: LED1

    PG8: LED2

    PI9:? LED3

    PC7: LED4

    圖 8 引腳定義

    ?

    ?

    ?

    時鐘樹如下設置:

    ?

    ?

    圖 9時鐘樹設置

    在配置中的NVIC中,打開CAN1 RX0接收中斷,如下圖所示:

    ?

    ?

    圖 10 打開CAN1的RX0接收中斷

    ?

    ?

    其他的沒有什么特殊設置,生成工程后的main函數如下:

  • int main(void)

  • {

  • ?
  • /* USER CODE BEGIN 1 */

  • static CanTxMsgTypeDef TxMessage;

  • static CanRxMsgTypeDef RxMessage;

  • /* USER CODE END 1 */

  • ?
  • /* MCU Configuration----------------------------------------------------------*/

  • ?
  • /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  • HAL_Init();

  • ?
  • /* Configure the system clock */

  • SystemClock_Config();

  • ?
  • /* Initialize all configured peripherals */

  • MX_GPIO_Init();

  • MX_CAN1_Init();

  • ?
  • /* USER CODE BEGIN 2 */

  • hcan1.pTxMsg =&TxMessage;

  • hcan1.pRxMsg =&RxMessage;

  • CANFilterConfig_Scale32_IdList(); //列表模式-32位寬

  • //CANFilterConfig_Scale16_IdList(); //列表模式-16位寬

  • //CANFilterConfig_Scale32_IdMask_StandardIdOnly(); //掩碼模式-32位寬(只有標準CAN ID)

  • //CANFilterConfig_Scale32_IdMask_ExtendIdOnly(); //掩碼模式-32位寬(只用擴展CAN ID)

  • //CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(); //掩碼模式-32位寬(標準CANID與擴展CAN ID混合)

  • //CANFilterConfig_Scale16_IdMask(); //掩碼模式-16位寬

  • HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);

  • /* USER CODE END 2 */

  • ?
  • /* Infinite loop */

  • /* USER CODE BEGIN WHILE */

  • while (1)

  • {

  • /* USER CODE END WHILE */

  • ?
  • /* USER CODE BEGIN 3 */

  • }

  • /* USER CODE END 3 */

  • ?
  • }

  • 如上代碼所示,示例中將采用各種過濾器配置來演示,在測試時我們可以只保留一種配置,也可以全部打開,為了確保每種配置的準確性,這里建議只保留其中一種配置進行測試。

    另外,接收中斷回調函數如下所示:

  • void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)

  • {

  • if(hcan->pRxMsg->StdId ==0x321)

  • {

  • //handle the CAN message

  • HandleCANMessage(hcan->pRxMsg); //處理接收到的CAN報文

  • }

  • if(hcan->pRxMsg->ExtId ==0x1800f001)

  • {

  • HandleCANMessage(hcan->pRxMsg); //處理接收到的CAN報文

  • }

  • HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET); //若收到消息則閃爍下LED4

  • HAL_Delay(200);

  • HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_RESET);

  • HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);

  • }

  • ?

    接下來將分別介紹過濾器的4中工作模式以及所對應的代碼示例。

    4.2.???32位寬的列表模式

    11 32位寬下的CAN_FxR1與CAN_FxR2各位定義

    ?

    如上圖所示,在32位寬的列表模式下,CAN_FxR1與CAN_FxR2都用來存儲希望通過的CAN ID,由于是32位寬的,因此既可以存儲標準CAN ID,也可以存儲擴展CAN ID。注意看上圖最底下的各位定義,可以看出,從右到左,首先,最低位是沒有用的,然后是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_FilterConfTypeDef sFilterConfig;

  • uint32_t StdId =0x321; //這里寫入兩個CAN ID,一個位標準CAN ID

  • uint32_t ExtId =0x1800f001; //一個位擴展CAN ID

  • ?
  • sFilterConfig.FilterNumber = 0; //使用過濾器0

  • sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //設為列表模式

  • sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //配置為32位寬

  • sFilterConfig.FilterIdHigh = StdId<<5; //基本ID放入到STID中

  • sFilterConfig.FilterIdLow = 0|CAN_ID_STD; //設置IDE位為0

  • sFilterConfig.FilterMaskIdHigh = ((ExtId<<3)>>16)&0xffff;

  • sFilterConfig.FilterMaskIdLow = (ExtId<<3)&0xffff|CAN_ID_EXT; //設置IDE位為1

  • sFilterConfig.FilterFIFOAssignment = 0; //接收到的報文放入到FIFO0中

  • sFilterConfig.FilterActivation = ENABLE;

  • sFilterConfig.BankNumber = 14;

  • ?
  • if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)

  • {

  • Error_Handler();

  • }

  • }

  • ?

    這里需要說明一下,由于我們使用的是cube庫,在cube庫中,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而已,后續(xù)其他示例也是同樣理解,不再重復解釋。這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。就這樣配置好了。

    4.3.???16位寬的列表模式

    ?

    圖12 16位寬的列表模式

    ?

    如上圖所示,在16位寬的列表模式下,FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow這4個16位變量都是用來存儲一個標準CAN ID,這樣,就可以存放4個標準CAN ID了,需要注意地是,此種模式下,是不能處理擴展CANID,凡是需要過濾擴展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; //使用過濾器1

  • sFilterConfig.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.4.???32位寬掩碼模式

    圖13 32位寬掩碼模式

    ?

    如上圖所示,32位寬模式下,FilterIdHigh與FilterIdLow合在一起表示CAN_FxR1寄存器,用來存放驗證碼,而FilterMaskIdHigh與FilterMaskIdLow合在一起表示CAN_FxR2寄存器,用來存放屏蔽碼,關于驗證碼與屏蔽碼的概念在之前的2.3節(jié)已經明確說明了,不清楚的可以回過去看看2.3節(jié)的內容。在32位寬的掩碼模式下,既可以過濾標準CAN ID,也可以過濾擴展CAN ID,甚至兩者混合這來也是可以的,下面我們就這3中情況分別給出示例。

    4.4.1. 只針對標準CAN ID

    ?

    如下代碼示例:

  • 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 ID

  • uint16_t mask,num,tmp,i;

  • ?
  • sFilterConfig.FilterNumber = 2; //使用過濾器2

  • sFilterConfig.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相互同或后的最終結果,這個即是屏蔽碼。

    4.4.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; //使用過濾器3

  • sFilterConfig.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時有所差別,其他的都一樣。

    接下來是標準CAN ID與擴展CAN ID混合著來。

    4.4.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; //使用過濾器4

  • sFilterConfig.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();

  • }

  • }

  • 如上代碼所示,在混合的情況下,只需稍微修改下屏蔽碼的計算方式就可以了,其他的基本沒有什么變化。

    4.5.???16位寬掩碼模式

    如下圖所示:

    ?

    ?

    圖14 16位寬的掩碼模式

    ?

    如上圖所示,在16位寬的掩碼模式下,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 ID

  • 0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};

  • uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定義第二組標準CAN ID

  • 0x756,0x757,0x758,0x759,0x75A};

  • uint16_t mask,tmp,i,num;

  • ?
  • sFilterConfig.FilterNumber = 5; //使用過濾器5

  • sFilterConfig.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進行過濾。

    4.6.???測試驗證

    上述代碼運行的STM3240G-EVAL評估板上,使用ZLG的USBCAN-2E-U盒子配合PC上的軟件CANTest進行驗證,整個系統連接后的效果如下圖所示:

    ?

    圖 15 測試環(huán)境


    測試時,逐個測試各個配置,并使用PC端軟件CANTest發(fā)送各個測試的CAN ID均能通過,而使用其他的CAN ID則不能通過,測試結果正常.

    ?

    5. 總結

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


    示例代碼下載地址:http://download.csdn.net/detail/flydream0/9613116

    ?

    ?

    幾年前我有寫過一篇關于CAN ID過濾器的另外一篇文章,是基于標準庫的,感興趣的朋友可以參考下:http://blog.csdn.net/flydream0/article/details/8148791

    《新程序員》:云原生和全面數字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结的全部內容,希望文章能夠幫你解決所遇到的問題。

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