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