英創(chuàng)EM335x工控主板,使用的Cortex-A8及WEC7操作系統(tǒng)。本文以EM335x工控主板光盤例程為例,簡單介紹一下C和C#代碼如何調(diào)用WEC7平臺下CAN驅(qū)動,實現(xiàn)CAN通信的方法。
C代碼
客戶可以在自己工程中添加例程中的EM335X_CAN.h及EM335X_CAN.cpp,使用里面封裝好的EM335X_CAN類。
#include 'EM335X_CAN.h'
EM335X_CAN can;
打開CAN
調(diào)用EM335X_CAN類的OpenCAN方法。比如用250Kbps打開CAN1(默認(rèn)只有CAN1)。
DWORD dwCanNo = 1;
DWORD dwBaudRate = 250000;
can.OpenCAN( dwCanNo, dwBaud );
關(guān)閉CAN
調(diào)用EM335X_CAN類的CloseCAN方法。
can.CloseCAN( );
發(fā)CAN數(shù)據(jù)包
調(diào)用EM335X_CAN類的WriteCAN方法。
can.WriteCAN(&canmsg );
接收CAN數(shù)據(jù)包
例程中使用了一個獨立的線程來接收。然后將收到的數(shù)據(jù)傳遞給PackagePro函數(shù)處理。參數(shù)一為數(shù)據(jù)buffer指針,參數(shù)二為數(shù)據(jù)的長度。
int EM335X_CAN::PackagePro(char* pBuf , int len)
用戶可以根據(jù)應(yīng)用具體需求,添加相應(yīng)的邏輯代碼。
CAN發(fā)送接收數(shù)據(jù)包結(jié)構(gòu)體定義
不論是CAN發(fā)送,還是CAN接收,都是以數(shù)據(jù)包為單位發(fā)送接收的。單個數(shù)據(jù)包大小為16字節(jié),結(jié)構(gòu)體定義如下:
typedef struct {
CAN_ID id;
BYTE dlc;
BYTE data[8];
}CAN_MESSAGE,*PCAN_MESSAGE;
id,一個CAN_ID的結(jié)構(gòu)體,該結(jié)構(gòu)體定義在下面說明。
dlc,1字節(jié),數(shù)據(jù)長度,取值0-8;
data,8字節(jié),傳輸?shù)臄?shù)據(jù)。
注:因為結(jié)構(gòu)體對齊的原因,該結(jié)構(gòu)體大小為16字節(jié)。
CAN_ID結(jié)構(gòu)體是一個整型,32位,用來記錄CAN通信所需的ID信息。
typedef struct{
unsigned int id:29;
unsigned int reserved:1;
unsigned int remote:1;
unsigned int extended:1;
}CAN_ID;
id,結(jié)構(gòu)體整型的低29位,表示id號。
reserved,第30位,用來標(biāo)記是接收的數(shù)據(jù)包,還是發(fā)送的數(shù)據(jù)包,默認(rèn)設(shè)置為0即可。
remote,第31位,用來設(shè)置是數(shù)據(jù)幀還是遠(yuǎn)程幀。0為數(shù)據(jù)幀,1為遠(yuǎn)程幀。
extended,第32位,用來設(shè)置是標(biāo)準(zhǔn)幀還是擴(kuò)展幀。0為標(biāo)準(zhǔn)幀,1為擴(kuò)展幀。
CAN過濾條件Filter設(shè)置
EM335x同樣支持?jǐn)?shù)據(jù)包過濾功能,設(shè)置Filter可以使得CAN只接收自己需要的數(shù)據(jù)包。
調(diào)用EM335X_CAN類的SetFilter方法,可以添加一個過濾條件,或者刪除一個已有的過濾條件。例如:
bResult = can.SetFilter( &Filter, FALSE );
第一個參數(shù)為過濾條件參數(shù),為一個CAN_FILTER的結(jié)構(gòu)體,在下面有說明。第二個參數(shù)如果為FALSE,則表示添加該過濾條件,如果為TRUE,則表示刪除已有的該過濾條件。
有多個過濾條件的情況下,只要數(shù)據(jù)包可以滿足任意一個過濾條件,那么該數(shù)據(jù)包就可以被接收。
CAN過濾條件Filter結(jié)構(gòu)體定義
CAN_FILTER結(jié)構(gòu)體定義如下:(CAN_ID結(jié)構(gòu)體的定義前面數(shù)據(jù)包結(jié)構(gòu)體里有說明)
typedef struct {
CAN_ID id;
CAN_ID mask;
}CAN_FILTER,*PCAN_FILTER;
這里的過濾邏輯如下:
假設(shè)收到的數(shù)據(jù)包里的id,我們記為id_message,與過濾條件中的filter參數(shù)里的id和mask滿足條件:(id_message&mask) == (id&mask),那么該數(shù)據(jù)包就可以接收,也就是說,mask表示需要進(jìn)行對比的位,如果數(shù)據(jù)包的id這幾位與filter設(shè)置里的id的這幾位相同,那么該數(shù)據(jù)包就可以接收。
比如:
一個filter的mask = 0x03,即2進(jìn)制的b0000 0011,即需要比較最后的兩位。
filter的id = 0x02,即2進(jìn)制的b0000 0010。
那么數(shù)據(jù)包id如果最后兩位為 10,該數(shù)據(jù)包就可以通過過濾條件被接收。
數(shù)據(jù)包id = 0xF7,即2進(jìn)制b1111 0111,無法接收。
數(shù)據(jù)包id = 0xE6,即2進(jìn)制b1110 0110,可以接收。
數(shù)據(jù)包id = 0x2E,即2進(jìn)制b0010 1110,可以接收。
CAN環(huán)回模式設(shè)置
環(huán)回模式為,可以選擇板子自己發(fā)送的數(shù)據(jù)包,是否自己也能同時接收到。
調(diào)用EM335X_CAN類的CAN_Loopback方法,如果希望自己發(fā)送的CAN包,自己也能接收到,那么設(shè)置第二個參數(shù)為TRUE。如果希望關(guān)閉環(huán)回功能,那么第二個參數(shù)設(shè)置為FALSE。
CAN其它命令
EM335X_CAN類的CanCommand方法可以控制CAN復(fù)位,啟動和停止。
BOOL CanCommand( CAN_COMMAND eCommand);
參數(shù)CAN_COMMAND是一個枚舉型,它的定義如下,STOP= 0,START =1,RESET=3:
typedef enum {
STOP,
START,
RESET
} CAN_COMMAND;
1、復(fù)位CAN
CAN復(fù)位會重置CAN驅(qū)動里的各個寄存器值,并執(zhí)行相關(guān)的初始化操作。
在打開CAN的時候,OpenCAN函數(shù)里已經(jīng)調(diào)用了該函數(shù)實現(xiàn)CAN復(fù)位。用戶可以根據(jù)自己應(yīng)用的實際情況,決定在什么時機(jī)執(zhí)行CAN復(fù)位。
2、啟動CAN
在設(shè)置好CAN波特率,環(huán)回,filter等參數(shù)后,CAN驅(qū)動線程并沒有馬上啟動,需要執(zhí)行CAN啟動,CAN線程才開始工作。
在打開CAN的時候,OpenCAN函數(shù)在設(shè)置完參數(shù)后調(diào)用CAN啟動。用戶可以根據(jù)自己應(yīng)用的實際情況,決定在什么時機(jī)執(zhí)行該函數(shù),例如:當(dāng)CAN接收線程的接收到錯誤事件時,可以在錯誤處理代碼里添加停止CAN,和重新啟動CAN的調(diào)用。
3、停止CAN
停止CAN會關(guān)閉CAN驅(qū)動線程,在關(guān)閉CAN的時候,CloseCAN函數(shù)調(diào)用CAN停止。
C#代碼
C#代碼參考了C代碼,相對C接口稍微做了調(diào)整。我們同樣封裝了一個CAN的類在EM335x_CAN_API.cs中,方便客戶添加到自己工程中。
打開CAN
打開CAN的流程為:打開CAN設(shè)備,獲得設(shè)備句柄,初始化CAN,然后設(shè)置CAN的參數(shù)(波特率,環(huán)回模式),創(chuàng)建CAN接收線程,最后啟動CAN,然后CAN驅(qū)動線程開始工作。
1、打開CAN設(shè)備
int CanNo = 1;
hCAN = CAN.OpenCAN(CanNo);
2、重置CAN
執(zhí)行Reset操作,初始化CAN。
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.RESET);
3、設(shè)置波特率
設(shè)置CAN的波特率,如250Kbps:
uBaud = 250000;
bRet = CAN.CAN_SetBaudRate(hCAN, uBaud);
4、設(shè)置CAN環(huán)回模式
如果希望自己發(fā)送的CAN包,自己也能接收到,那么可以設(shè)置第二個參數(shù)為1,例程中暫時關(guān)閉該功能,所以設(shè)置的0。
bRet = CAN.CAN_Loopback(hCAN, 0);
5、創(chuàng)建單獨的接收線程
因為接收時,函數(shù)需要等待CAN接收事件,為阻塞狀態(tài),不宜直接寫在主線程中,這里添加一個接收線程,專門處理CAN數(shù)據(jù)接收。
創(chuàng)建線程:
revThread = new Thread(new ThreadStart(BeginReceive));
threadStop = false;
revThread.Start();//啟動waitforMessage線程
6、啟動CAN
當(dāng)準(zhǔn)備就緒,就可以啟動CAN設(shè)備了。
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.START);
關(guān)閉CAN
主要是結(jié)束接收線程,停止CAN,及關(guān)閉CAN設(shè)備句柄等。
revThread.Abort();//結(jié)束線程
revThread.Join();
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.STOP);
bRet = CAN.CloseCAN(hCAN);
發(fā)CAN數(shù)據(jù)包
調(diào)用WriteFile發(fā)送CAN數(shù)據(jù)包。
bRet = CAN.WriteFile(hCAN, ref pktSend, CAN.sizePacket, ref uLen, 0);
接收CAN數(shù)據(jù)包
調(diào)用ReadFile發(fā)送CAN數(shù)據(jù)包。
bResult = CAN.ReadFile(hCAN, ref pktRev, CAN.sizePacket, ref uLen, 0);
CAN發(fā)送接收數(shù)據(jù)包結(jié)構(gòu)體定義
不論是CAN發(fā)送WriteFile,還是CAN接收ReadFile,都是以數(shù)據(jù)包為單位發(fā)送接收的。單個數(shù)據(jù)包大小為16字節(jié),結(jié)構(gòu)體定義如下:
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct CAN_MESSAGE
{
[FieldOffset(0)]
public uint id;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] data;//數(shù)據(jù)字節(jié)
}
id是一個整型4字節(jié),32位,用來記錄CAN通信所需的ID信息。
id的低29位,表示id號。
id的第30位,用來標(biāo)記是接收的數(shù)據(jù)包,還是發(fā)送的數(shù)據(jù)包。
id的第31位,用來設(shè)置是數(shù)據(jù)幀還是遠(yuǎn)程幀。該位為0則是數(shù)據(jù)幀,該位為1則是遠(yuǎn)程幀。
id的第32位,用來設(shè)置是標(biāo)準(zhǔn)幀還是擴(kuò)展幀。該位為0則是標(biāo)準(zhǔn)幀,該位為1則是擴(kuò)展幀。
data為12字節(jié)byte數(shù)組。
data[0]為CAN數(shù)據(jù)包內(nèi)數(shù)據(jù)的長度,取值0-8;
data[1]-data[8],8字節(jié),為CAN數(shù)據(jù)包內(nèi)傳輸?shù)臄?shù)據(jù)。
data[9]-data[11]未使用。
注:結(jié)構(gòu)體這樣設(shè)計的主要原因還是因為要和C代碼的驅(qū)動接口,做成這樣效率會高些。
例如,我們要發(fā)送一個,id為5的標(biāo)準(zhǔn)數(shù)據(jù)幀,數(shù)據(jù)長度為3,分別是0x01,0x02,0x03,代碼如下:
CAN_MESSAGE pktSend = new CAN_MESSAGE();
pktSend.id = 5;
//如果是數(shù)據(jù)幀就不變,如果是遠(yuǎn)程幀就
//pktSend.id = pktSend.id | 0x20000000;//remote
//如果是標(biāo)準(zhǔn)幀就不變,如果是擴(kuò)展幀就
//pktSend.id = pktSend.id | 0x40000000;//extended
pktSend.data[0] = 3;
pktSend.data[1] = 0x01;
pktSend.data[2] = 0x02;
pktSend.data[3] = 0x03;
例如,我們要發(fā)送一個,id為55的擴(kuò)展幀,數(shù)據(jù)長度為6,分別是0x04,0x04,0x04,0x05,0x05,0x05,代碼如下:
CAN_MESSAGE pktSend = new CAN_MESSAGE();
pktSend.id = 5;
//如果是數(shù)據(jù)幀就不變,如果是遠(yuǎn)程幀就
//pktSend.id = pktSend.id | 0x20000000;//remote
//如果是標(biāo)準(zhǔn)幀就不變,如果是擴(kuò)展幀就
pktSend.id = pktSend.id | 0x40000000;//extended
pktSend.data[0] = 6;
pktSend.data[1] = 0x04;
pktSend.data[2] = 0x04;
pktSend.data[3] = 0x04
pktSend.data[4] = 0x05;
pktSend.data[5] = 0x05;
pktSend.data[6] = 0x05;
CAN過濾條件Filter設(shè)置
EM335x同樣支持?jǐn)?shù)據(jù)包過濾功能,設(shè)置Filter可以使得CAN只接收自己需要的數(shù)據(jù)包。
調(diào)用EM335X_CAN類的SetFilter方法,可以添加一個過濾條件,或者刪除一個已有的過濾條件。例如:
CAN.CAN_SetFilter (hCAN, Filter, false );
第一個參數(shù)為CAN句柄,第二個參數(shù)為過濾條件參數(shù),為一個CAN_FILTER的結(jié)構(gòu)體,在下面有說明。第三個參數(shù)如果為FALSE,則表示添加該過濾條件,如果為TRUE,則表示刪除已有的該過濾條件。
有多個過濾條件的情況下,只要數(shù)據(jù)包可以滿足任意一個過濾條件,那么該數(shù)據(jù)包就可以被接收。
CAN過濾條件Filter結(jié)構(gòu)體定義
CAN_FILTER結(jié)構(gòu)體定義如下:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct CAN_FILTER
{
[FieldOffset(0)]
public uint id;
[FieldOffset(4)]
public uint mask;
}
這里的過濾邏輯如下:
假設(shè)收到的數(shù)據(jù)包里的id,我們記為id_message,與過濾條件中的filter參數(shù)里的id和mask滿足條件:(id_message&mask) == (id&mask),那么該數(shù)據(jù)包就可以接收,也就是說,mask表示需要進(jìn)行對比的位,如果數(shù)據(jù)包的id這幾位與filter設(shè)置里的id的這幾位相同,那么該數(shù)據(jù)包就可以接收。
比如:
一個filter的mask = 0x03,即2進(jìn)制的b0000 0011,即需要比較最后的兩位。
filter的id = 0x02,即2進(jìn)制的b0000 0010。
那么數(shù)據(jù)包id如果最后兩位為 10,該數(shù)據(jù)包就可以通過過濾條件被接收。
數(shù)據(jù)包id = 0xF7,即2進(jìn)制b1111 0111,無法接收。
數(shù)據(jù)包id = 0xE6,即2進(jìn)制b1110 0110,可以接收。
詳細(xì)信息,可以電話,郵件或論壇提問方式咨詢英創(chuàng)工程師。
-
嵌入式主板
+關(guān)注
關(guān)注
7文章
6085瀏覽量
35227
發(fā)布評論請先 登錄
相關(guān)推薦
評論