CAN總線結構示意圖:
說明: 1:CAN收發器(示意圖中的單元)根據兩總線CAN_H和CAN_L的電位差來判斷總線電平;
2:實際中CAN_H與CAN_L由雙絞線組成;
3:數據傳遞終端的電阻器,是為了避免數據傳輸反射回來,使數據遭到破壞;
4:電阻阻值為120Ω;
5:CAN通信實際上為單元之間的數據傳輸
CAN通信單元的組成:
每個通信單元軟件部分由數據幀、遙控幀、錯誤幀、過載幀、幀間隔組成;但不是上述5種幀都包含,具體看軟件怎樣編寫
1 數據幀的發送
1) 數據幀的組成(遙控幀與數據幀的組成類似,只是不包含數據幀的數據段)
2)STM32軟件編寫發送數據幀及解釋(遙控幀與數據幀的組成類似,只是不包含數據幀的數據段)
u8 CAN_Send_Msg(u8* msg,u8 len) { u8 mbox; u16 i=0; CanTxMsg TxMessage; //結構體的具體元素可查找STM32數據庫手冊 TxMessage.StdId=0x12; //報文的11位標准標識符,范圍0x000~0x7FF (設置數據幀的仲裁段的標准表示符) //TxMessage.ExtId=0x12; //報文的29位擴展標識符,范圍0x00000000~0x1FFFFFFF,由於IDE選擇為0,此元素可以不設置 (設置數據幀的仲裁段的擴展標識符) TxMessage.IDE=0; // IDE 0:選擇使用標准標識符 1:選擇使用擴展標識符 (設置數據幀的仲裁段的選擇) TxMessage.RTR=0; // RTR 0:選擇發送數據幀 1:選擇發送遙控幀 (設置數據幀的控制段) TxMessage.DLC=len; //DLC的大小為發送數據的長度,len最大為8,因為一個報文包含0~8個字節數據 (設置數據幀的控制段) for(i=0;i<len;i++) TxMessage.Data[i]=msg[i]; // 給數據幀的數據賦值 mbox= CAN_Transmit(CAN1, &TxMessage); i=0; while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF)) //檢測數據的發送狀態。如果失敗,等到i加到0xFFF退出循環,並返回"1";成功則返回"0". i++; if(i>=0XFFF) return 1; return 0; }
2 過濾器
在CAN協議里,報文的標識符不代表節點的地址,而是跟報文的內容相關的。因此,發送者以廣播的形式把報文發送給所有接受者。節點在接收報文時,根據標識符的值,決定軟件
是否需要該報文;如果需要,就拷貝到SRAM里;如果不需要,報文就被丟棄且無需軟件的干預。
比如示意圖中: 單元1要發送報文,它會將報文發送給單元2、單元3、單元4、單元5,而單元2、單元3、單元4、單元5會根據標識符的值,決定是否接收改報文。
為了知道哪些報文需要接收,哪些需要放棄,所以在此過程中,引入過濾器。通過過濾器來接收需要的報文。
1 幾個重要概念
1) 過濾器組
STM32總共提供14個過濾器組來處理CAN接收過濾問題,每個過濾器組包含兩個32位寄存器,即CAN_FiR0和CAN_FiR1組成(i=0~13),在設置為屏蔽位模式下,其中一個作為標
識符寄存器,另一個作為屏蔽碼寄存器。過濾器組中的每個過濾器,編號(叫做過濾器號)從0開始,到某個最大數值(這時最大值並非13,而是取決於14個過濾器組的模式和位寬的設
置,當全部配置為位寬為16,且為標識符列表模式時,最大編號為14*4-1=55)。
F0R1 | F0R2 | F1R1 | F1R2 | F2R1 | F2R2 | F3R1 | F3R2 | F4R1 | F4R2 | F5R1 | F5R2 | F6R1 | F6R2 | F7R1 | F7R2 | F8R1 | F8R2 | F9R1 | F9R2 | F10R1 | F10R2 | F11R1 | F11R2 | F12R1 | F12R2 | F13R1 | F13R2 |
2 過濾器過濾模式
過濾器過濾模式有屏蔽位模式和過濾器列表模式
1)屏蔽位模式
為了過濾出一組標識符,應該設置過濾器組工作在屏蔽位模式;
在屏蔽位模式下,標識符寄存器和屏蔽寄存器一起,指定報文標識符的任何一位,應該按照“必須匹配”或“不用關心”處理。
2)過濾器列表模式
為了過濾出一個標識符,應該設置過濾器組工作在標識符列表模式;
在標識符列表模式下,屏蔽寄存器也被當作標識符寄存器用。因此,不是采用一個標識符加一個屏蔽位的方式,而是使用2個標識符寄存器。接收報文標識符的每一位都必須跟過濾
器標識符相同。
3)過濾器的位寬
每個過濾器組的位寬都可以獨立配置,以滿足應用程序的不同需求。根據位寬的不同,每個過濾器組可提供:
• 1個32位過濾器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
• 2個16位過濾器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位
4)過濾器組的位寬模式和位寬設置
看手冊
5)過濾器匹配序號及優先規則
看手冊
2 CAN ID的分析
1) CAN ID分析
標准標識符:ID28~ID18
擴展標識符: ID28~D18 加上 ID17~D0
eg1:有標准標識符為:
0x6D1 (b 110 1101 0001) 占用ID的ID28~ID18,共11位
eg2:有擴展標識符為:
0x1EFEDFEA (b 1 1110 1111 1110 1101 1111 1110 1010) 其中紅色部分為基本標識符 粉色部分為擴展標識符
2)位寬為32位的屏蔽模式分析
如上圖所示:此種模式下,過濾器包含一個32位的標識符寄存器和一個32位的屏蔽寄存器,灰色部分顯示的是與CAN ID各位定位的映射關系。由圖可以看出映像關系恰好等於擴展
CAN ID左移3位再加上IDE、RTR及一個顯性電平得到。
所以如何將CAN ID所表示的各部分如何針對過濾器寄存器各部分對號入座,其主要是掌握其核心思想即可:1:在各種過濾器模式下,CAN ID與寄存器相應位置一定要匹配;2:在
屏蔽方式下,屏蔽寄存器某位為1表示接收到的CAN ID對應的位必須對驗證碼寄存器對應的位相同。
eg:下面以代碼例子,假設我們要接收多個ID:0x6D1 , 1EFEDFEA, 前面為標准標識符,后面為擴展標識符,要同時能接收這兩個標識符的情況來配置過濾器
u16 Std_ID =0x6D1;
u32 Ext_ID =0x1EFEDFEA;
u32 mask =0;
CAN_FilterInitTypeDef CAN_FilterInitStructure; //定義一個結構體變量
CAN_FilterInitStructure.CAN_FilterNumber=0; //設置過濾器組0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //設置過濾器組0為屏蔽模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //設置過濾器組0位寬為32位
/**************************************************************************************************************************************
標識符寄存器的設置,Ext_ID<<3對齊,再>>16取高16位
***************************************************************************************************************************************/
CAN_FilterInitStructure.CAN_FilterIdHigh=((Ext_ID<<3) >>16) & 0xffff; //設置標識符寄存器高字節。
CAN_FilterInitStructure.CAN_FilterIdLow=(u16)(Ext_ID<<3) | CAN_ID_EXT; //設置標識符寄存器低字節
/***********************************************************************************************************************************
這里也可以這樣設置,設置標識符寄存器高字節.這里為什么是左移5位呢?從上圖可以看出,CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],標准CAN ID本身是不包
含擴展ID數據,因此為了要將標准CAN ID放入此寄存器,標准CAN ID首先應左移5位后才能對齊。設置標識符寄存器低字節,這里也可以設置為CAN_ID_STD
CAN_FilterInitStructure.CAN_FilterIdHigh=Std_ID<<5;
CAN_FilterInitStructure.CAN_FilterIdLow=0 | CAN_ID_EXT;
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/*************************************************************************************************************************
屏蔽寄存器的設置這里的思路是先將標准CAN ID和擴展CAN ID對應的ID值先異或后取反,為什么?異或是為了找出兩個CAN ID有哪些位是相同的,是相同的位則說明需
要關心,需要關心的位對應的屏蔽碼位應該設置為1,因此需要取反一下。最后再整體左移3位。
****************************************************************************************************************************/
mask =(Std_ID<<18); //這里為什么左移18位?因為在標准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; //此過濾器組關聯到接收FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此過濾器組
CAN_FilterInit(&CAN_FilterInitStructure); //設置過濾器
3)位寬為32位的標識符列表模式
U16 std_id =0x6D1;
U32 ext_id =0x1EFEDFEA;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber=0; //設置過濾器組0,范圍為0~13
CAN_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; //此過濾器組關聯到接收FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此過濾器組
CAN_FilterInit(&CAN_FilterInitStructure); //設置過濾器
4)16位位寬屏蔽模式
5)16位位寬列表模式
6)屏蔽位模式的理解
假如過濾器組0工作在,位寬為32位標識符屏蔽模式:
設置 CAN_F0R1=0xFFFF 0000;
CAN_F0R2=0xFF00 FF00
其中存放到 CAN_F0R1 的值是期望收到的ID,即我們希望收到的映像(STID+EXTID+IDE+RTR),最好是:FFFF 0000
而 CAN_F0R2中的0xFF00 FF00就是我們需要關心的ID,表示收到的映像。其位[31:24]和[15:8]這16個位,必須和CAN_F0R1中對應的一模一樣,而另外的16個位則不
必關心,可以一樣也可以不一樣,都認為是正確的ID,即收到的映像必須是0xFFXX00XX,才算是正確的(X表示不關心)。也就是說屏蔽位CAN_F0R2中的數值:
1:必須匹配,到來的標識符位必須和過濾器對應的標識符寄存器位相一致
0:不關心,可以一樣,也可以不一樣,都認為是正確的ID
因此:
為了過濾出一組標識符,應該設置過濾器組工作在屏蔽模式
為了過濾出一個標識符,應該設置過濾器組工作在標識符列表模式