編輯:關於Android編程
bxCAN是STM32系列最穩定的IP核之一,無論有哪個新型號出來,這個IP核基本未變,可見這個IP核的設計是相當成熟的。本文所講述的內容屬於這個IP核的一部分,掌握了本文所講內容,就可以很方便地適用於所有STM32系列中包含bxCAN外設的型號。有關bxCAN的過濾器部分的內容在參考手冊中往往看得“不甚明白“,本文就過濾器的4種工作模式進行詳細講解並使用具體的代碼進行演示,這些代碼都進行過實測驗證通過的,希望能給讀者對於bxCAN過濾器有一個清晰的理解。
在這裡,我們可以將CAN總線看成一個廣播消息通道,上面傳輸著各種類型的消息,好比報紙,有體育新聞,財經新聞,政治新聞,還有軍事新聞,每個人都有自己的喜好,不一定對所有新聞都感興趣,因此,在看報紙的時候,一般人都是只看自己感興趣的那類新聞,而過濾掉其他不感興趣的內容。那麼我們一般是怎麼過濾掉那些不感興趣的內容的呢?下面有兩種方法來實現這個目的:
第一種方法:
每次看報紙時,你都看下每篇文章的標題,如果感興趣則繼續看下去,如果不感興趣,則忽略掉。
第二種方法:
你告訴郵遞員,你只對財經新聞感興趣,請只將財經類報紙送過來,其他的就不要送過來了,就這樣,你看到的內容必定是你感興趣的財經類新聞。
上面那種方法好呢?很明顯,第二種方法是最好的,因為你不用自己每次判斷哪些新聞內容是你感興趣的,可以免受“垃圾”新聞干擾,從而可以節省時間忙其他事。bxCAN的過濾器就是采用上述第二種方法,你只需要設置好你感興趣的那些CAN報文ID,那麼MCU就只能收到這些CAN報文,是從硬件上過濾掉,完全不需要軟件參與進來,從而節省了大大節省了MCU的時間,可以更加專注於其他事務,這個就是bxCAN過濾器的意義所在。
假設我們是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位是表示出生年月,如下圖:
圖 1 18位身份證號碼的各位定義
如上圖,身份證中第7~10這4位數表示的是出生年份,那麼,我們可以這麼執行:
檢查想要進城的所有人的身份證號碼的第7~10位數字,如果這個數字依次為1156則可以進入,否則則不可以,至於身份證號碼的其他位則完全不關心。假如過幾天城主放寬進城條件為只要是1150年~1160前的人都可以進城,那麼,我們就可以只關注身份證號碼的第7~9這3位數是否為115就可以了,對不對?這樣一來,我們就可以非常完美地執行城主的要求了。
再變下,假設現在使用機器來當守衛,不再是人來執行這個“篩選”工作。機器是死的,沒有人那麼靈活,那麼機器又該如何執行呢?
對於機器來說,每一步都得細化到機器可以理解的程度,於是我們可以作如下細化:
第一步:獲取想進城的人的身份證號碼
第二步:只看獲取到身份證的第7~9位,其他位忽略
第三步:將忽略後的結果與1156進行比較
第四步:比較結果相同則通過,不同則不能通過
這種方式,我們稱之為掩碼模式。
if( x & y ==z) //x表示待檢查身份證號碼,y表示只關注第7~9位的屏蔽碼,Z則為1156,這裡叫做驗證碼 { //可以通過 } else { //不可以通過 }
對於機器來說,我們要為它准備好兩張紙片,一片寫上屏蔽碼,另一片紙片寫上驗證碼,屏蔽碼上相應位為1時,表示此位需要與驗證碼對應位進行比較,反之,則表示不需要。機器在執行任務的時候先將獲取的身份證號碼與屏蔽碼進行“與”操作,再將結果與驗證碼的進行比較,根據判斷是否相同來決定是否通過。整個判別流程如下所示:
圖 2 掩碼模式的計算過程
從上圖可以很容易地理解屏蔽碼與驗證碼的含義,這樣一來,能通過的結果數量就完全取決於屏蔽碼,設得寬,則可以通過的多(所有位為0,則不過任何過濾操作,則誰都可以通過),設得窄,則通過的少(所有位設為1,則只有一個能通過)。那麼知道這個有什麼用呢?因為bxCAN的過濾器的掩碼模式就是采用這種方式,在bxCAN中,分別采用了兩個寄存器(CAN_FiR1,CAN_FiR2)來存儲屏蔽碼與驗證碼,從而實現掩碼模式的工作流程的。這樣,我們就知道了bxCAN過濾器的掩碼模式的大概工作原理。
但是,我們得注意到,采用掩碼模式的方式並不能精確的對每一個ID進行過濾,打個比方,還是采用之前的守衛的例子,假如城主要求只有1150~1158年出生的人能通過,那麼,若我們還是才用掩碼模式,那麼掩碼就設為第7~9位為”1”,對應的,驗證碼的7~9位分別為”115”,這樣就可以了。但是,仔細一想,出生於1159的人還是可以通過,是不是?但總體來說,雖然沒有做到精確過濾,但我們還是能做到大體過濾的,而這個就是掩碼模式的缺點了。在實際應用時,取決於需求,有時我們會同時使用到列表模式和掩碼模式,這都是可能的。
綜合之前所述,下面我們來對比一下列表模式與掩碼模式這兩種模式的優缺點。
模式 優點 缺點 列表模式 能精確地過濾每個指定的CAN ID 有數量限制 掩碼模式 取決於屏蔽碼,有時無法完全精確到每一個CAN ID,部分不期望的CAN ID有時也會收到 數量取決於屏蔽碼,最多無上限
1986 年德國電氣商BOSCH公司開發出面向汽車的CAN 通信協議,剛開始的時候,CAN ID定義為11位,我們稱之為標准格式,ISO11898-1標准中CAN的基本格式如下圖所示:
圖 3 標准CAN報文格式定義
如上圖所示,標准CAN ID存放在上圖ID18~ID28中,共11位。隨著工業發展,後來發現11位的CAN ID已經不夠用,於是就增加了18位,擴展CAN ID到29位,如下圖所示:
圖 4 擴展CAN報文格式定義
從上圖對比擴展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,二進制展開為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,這個也將在後續的內容中多次用到,再次留意一下,擴展ID是位於基本ID的右方,在擴展CAN ID的構成中,擴展ID位於低18位,而基本ID位於高11位,於是要獲取一個擴展CANID的基本ID,就只需要將這個CANID右移18位(這種算法後續將多次用到,請務必記住!)。
終於進入到正題了!前面已經介紹了過濾器的列表模式與掩碼模式,以及掩碼模式下的屏蔽碼與驗證碼的含義,還介紹了標准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環境來說,本著不浪費的原則,這裡最好能有另外一個標志用告訴過濾器是否需要處理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種工作模式並給出對應的代碼示例.
本文硬件采用STM3240G-EVAL評估板和ZLG的USBCAN-2E-U及其配套的軟件工具CANTest來實現對MCU進行CAN報文的發送。工程使用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中工作模式以及所對應的代碼示例。
圖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而已,後續其他示例也是同樣理解,不再重復解釋。這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。就這樣配置好了。
圖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(); } }
可見,列表模式還是非常好理解的。
圖13 32位寬掩碼模式
如上圖所示,32位寬模式下,FilterIdHigh與FilterIdLow合在一起表示CAN_FxR1寄存器,用來存放驗證碼,而FilterMaskIdHigh與FilterMaskIdLow合在一起表示CAN_FxR2寄存器,用來存放屏蔽碼,關於驗證碼與屏蔽碼的概念在之前的2.3節已經明確說明了,不清楚的可以回過去看看2.3節的內容。在32位寬的掩碼模式下,既可以過濾標准CAN ID,也可以過濾擴展CAN ID,甚至兩者混合這來也是可以的,下面我們就這3中情況分別給出示例。
如下代碼示例:
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如上代碼所示,對於驗證碼,任意一個期望通過的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>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>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位寬的掩碼模式
如上圖所示,此圖是從STM32F405的參考文檔RM0090截取過來,版本為DocID018909 Rev12,但是經過我的嚴格測試,發現此圖是有問題的,在16位寬的掩碼模式下,CAN_FxR1的低16位是作為驗證碼,對應的16位屏蔽碼為CAN_FxR2的低16位,而不是CAN_FxR1的高16位,同樣的,CAN_FxR1的高16位是作為驗證碼,對應與CAN_FxR2的高16位為屏蔽碼。從測試結果來看,上圖的CAN_FxR1[16:31]需要與CAN_FxR2[0:15]對換位置即可,即CAN_FxR1的低16位的CAN_FxR2的低16位是一對組合,即CAN_FxR1的高16位的CAN_FxR2的高16位是另外一對這,即“低對低,高對高”才是正確的。在這種模式下,有兩對驗證碼與屏蔽碼組合,都只能對標准CAN ID進行過濾,於是,其示例代碼如下:
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; tmp="StdIdArray1[i]" mask="" sfilterconfig.filtermaskidlow="(mask<<5)|0x10;" sfilterconfig.filteridhigh="StdIdArray2[0]<<5;" num="sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);" i="0;" sfilterconfig.filtermaskidhigh="(mask<<5)|0x10;" sfilterconfig.filterfifoassignment="0;" can="" sfilterconfig.filteractivation="ENABLE;" sfilterconfig.banknumber="14;" pre="">
如上代碼所示,在這種模式下,其特殊之處就是可以配置兩套驗證碼,屏蔽碼組合,可以分別相對獨立地對標准CAN ID進行過濾。
上述代碼運行的STM3240G-EVAL評估板上,使用ZLG的USBCAN-2E-U盒子配合PC上的軟件CANTest進行驗證,整個系統連接後的效果如下圖所示:
圖 15 測試環境
測試時,逐個測試各個配置,並使用PC端軟件CANTest發送各個測試的CAN ID均能通過,而使用其他的CAN ID則不能通過,測試結果正常.
在實際的應用中,我們需要根據需求的實際情況來決定使用何種過濾配置,STM32F4的bxCAN提供了28個過濾器,在配置之前,我們需要先將那些需要通過的CANID進行整理,若數量少,則使用列表模式,精准,若只有標准CAN ID,則可以考慮使用16位寬模式,若需求中的CAN ID過多,則可以考慮使用多個過濾器,部分使用列表模式,部分使用掩碼模式,CAN ID值相近的可以歸納成一組,使用掩碼模式進行過濾。但使用掩碼模式的同時,我們也需要意識到,也有可能部分不期望的CAN ID也會通過過濾器,掩碼放得越寬,帶進其他CAN ID的幾率就越大,這點我們需要格外注意,視情況進行應用判斷和處理。另外,對於相近的CAN ID,我們可以提前計算好屏蔽碼,直接在代碼中填入,而不是在代碼中臨時計算,這樣可以提高軟件效率,大家視情況而定。
近段時間來Android上最火的框架非react native莫屬了,這裡我不去評價這個框架的好壞,畢竟只有用過的人才會有深刻的體會。但是我個人有一個習慣,在使用一個開源
以在搜索框搜索時,自動補全為例:其中還涉及到一個詞,Tokenizer:分詞器,分解器。上效果圖:MainActivity.java:package com.joan.t
1.案例效果圖 2.准備素材 progress1.png(78*78)
SlidingDrawer隱藏屏外的內容,並允許用戶通過handle以顯示隱藏內容。它可以垂直或水平滑動,它有倆個View組成,其一是可以拖動的handle,其二是隱藏內