英创EM335x工控主板,使用的Cortex-A8及WEC7操作系统。本文以EM335x工控主板光盘例程为例,简单介绍一下C和C#代码如何调用WEC7平台下CAN驱动,实现CAN通信的方法。
C代码
客户可以在自己工程中添加例程中的EM335X_CAN.h及EM335X_CAN.cpp,使用里面封装好的EM335X_CAN类。
#include 'EM335X_CAN.h'
EM335X_CAN can;
打开CAN
调用EM335X_CAN类的OpenCAN方法。比如用250Kbps打开CAN1(默认只有CAN1)。
DWORD dwCanNo = 1;
DWORD dwBaudRate = 250000;
can.OpenCAN( dwCanNo, dwBaud );
关闭CAN
调用EM335X_CAN类的CloseCAN方法。
can.CloseCAN( );
发CAN数据包
调用EM335X_CAN类的WriteCAN方法。
can.WriteCAN(&canmsg );
接收CAN数据包
例程中使用了一个独立的线程来接收。然后将收到的数据传递给PackagePro函数处理。参数一为数据buffer指针,参数二为数据的长度。
int EM335X_CAN::PackagePro(char* pBuf , int len)
用户可以根据应用具体需求,添加相应的逻辑代码。
CAN发送接收数据包结构体定义
不论是CAN发送,还是CAN接收,都是以数据包为单位发送接收的。单个数据包大小为16字节,结构体定义如下:
typedef struct {
CAN_ID id;
BYTE dlc;
BYTE data[8];
}CAN_MESSAGE,*PCAN_MESSAGE;
id,一个CAN_ID的结构体,该结构体定义在下面说明。
dlc,1字节,数据长度,取值0-8;
data,8字节,传输的数据。
注:因为结构体对齐的原因,该结构体大小为16字节。
CAN_ID结构体是一个整型,32位,用来记录CAN通信所需的ID信息。
typedef struct{
unsigned int id:29;
unsigned int reserved:1;
unsigned int remote:1;
unsigned int extended:1;
}CAN_ID;
id,结构体整型的低29位,表示id号。
reserved,第30位,用来标记是接收的数据包,还是发送的数据包,默认设置为0即可。
remote,第31位,用来设置是数据帧还是远程帧。0为数据帧,1为远程帧。
extended,第32位,用来设置是标准帧还是扩展帧。0为标准帧,1为扩展帧。
CAN过滤条件Filter设置
EM335x同样支持数据包过滤功能,设置Filter可以使得CAN只接收自己需要的数据包。
调用EM335X_CAN类的SetFilter方法,可以添加一个过滤条件,或者删除一个已有的过滤条件。例如:
bResult = can.SetFilter( &Filter, FALSE );
第一个参数为过滤条件参数,为一个CAN_FILTER的结构体,在下面有说明。第二个参数如果为FALSE,则表示添加该过滤条件,如果为TRUE,则表示删除已有的该过滤条件。
有多个过滤条件的情况下,只要数据包可以满足任意一个过滤条件,那么该数据包就可以被接收。
CAN过滤条件Filter结构体定义
CAN_FILTER结构体定义如下:(CAN_ID结构体的定义前面数据包结构体里有说明)
typedef struct {
CAN_ID id;
CAN_ID mask;
}CAN_FILTER,*PCAN_FILTER;
这里的过滤逻辑如下:
假设收到的数据包里的id,我们记为id_message,与过滤条件中的filter参数里的id和mask满足条件:(id_message&mask) == (id&mask),那么该数据包就可以接收,也就是说,mask表示需要进行对比的位,如果数据包的id这几位与filter设置里的id的这几位相同,那么该数据包就可以接收。
比如:
一个filter的mask = 0x03,即2进制的b0000 0011,即需要比较最后的两位。
filter的id = 0x02,即2进制的b0000 0010。
那么数据包id如果最后两位为 10,该数据包就可以通过过滤条件被接收。
数据包id = 0xF7,即2进制b1111 0111,无法接收。
数据包id = 0xE6,即2进制b1110 0110,可以接收。
数据包id = 0x2E,即2进制b0010 1110,可以接收。
CAN环回模式设置
环回模式为,可以选择板子自己发送的数据包,是否自己也能同时接收到。
调用EM335X_CAN类的CAN_Loopback方法,如果希望自己发送的CAN包,自己也能接收到,那么设置第二个参数为TRUE。如果希望关闭环回功能,那么第二个参数设置为FALSE。
CAN其它命令
EM335X_CAN类的CanCommand方法可以控制CAN复位,启动和停止。
BOOL CanCommand( CAN_COMMAND eCommand);
参数CAN_COMMAND是一个枚举型,它的定义如下,STOP= 0,START =1,RESET=3:
typedef enum {
STOP,
START,
RESET
} CAN_COMMAND;
1、复位CAN
CAN复位会重置CAN驱动里的各个寄存器值,并执行相关的初始化操作。
在打开CAN的时候,OpenCAN函数里已经调用了该函数实现CAN复位。用户可以根据自己应用的实际情况,决定在什么时机执行CAN复位。
2、启动CAN
在设置好CAN波特率,环回,filter等参数后,CAN驱动线程并没有马上启动,需要执行CAN启动,CAN线程才开始工作。
在打开CAN的时候,OpenCAN函数在设置完参数后调用CAN启动。用户可以根据自己应用的实际情况,决定在什么时机执行该函数,例如:当CAN接收线程的接收到错误事件时,可以在错误处理代码里添加停止CAN,和重新启动CAN的调用。
3、停止CAN
停止CAN会关闭CAN驱动线程,在关闭CAN的时候,CloseCAN函数调用CAN停止。
C#代码
C#代码参考了C代码,相对C接口稍微做了调整。我们同样封装了一个CAN的类在EM335x_CAN_API.cs中,方便客户添加到自己工程中。
打开CAN
打开CAN的流程为:打开CAN设备,获得设备句柄,初始化CAN,然后设置CAN的参数(波特率,环回模式),创建CAN接收线程,最后启动CAN,然后CAN驱动线程开始工作。
1、打开CAN设备
int CanNo = 1;
hCAN = CAN.OpenCAN(CanNo);
2、重置CAN
执行Reset操作,初始化CAN。
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.RESET);
3、设置波特率
设置CAN的波特率,如250Kbps:
uBaud = 250000;
bRet = CAN.CAN_SetBaudRate(hCAN, uBaud);
4、设置CAN环回模式
如果希望自己发送的CAN包,自己也能接收到,那么可以设置第二个参数为1,例程中暂时关闭该功能,所以设置的0。
bRet = CAN.CAN_Loopback(hCAN, 0);
5、创建单独的接收线程
因为接收时,函数需要等待CAN接收事件,为阻塞状态,不宜直接写在主线程中,这里添加一个接收线程,专门处理CAN数据接收。
创建线程:
revThread = new Thread(new ThreadStart(BeginReceive));
threadStop = false;
revThread.Start(); // 启动waitforMessage线程
6、启动CAN
当准备就绪,就可以启动CAN设备了。
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.START);
关闭CAN
主要是结束接收线程,停止CAN,及关闭CAN设备句柄等。
revThread.Abort(); // 结束线程
revThread.Join();
bRet = CAN.CAN_Command(hCAN, (uint)CAN_COMMAND.STOP);
bRet = CAN.CloseCAN(hCAN);
发CAN数据包
调用WriteFile发送CAN数据包。
bRet = CAN.WriteFile(hCAN, ref pktSend, CAN.sizePacket, ref uLen, 0);
接收CAN数据包
调用ReadFile发送CAN数据包。
bResult = CAN.ReadFile(hCAN, ref pktRev, CAN.sizePacket, ref uLen, 0);
CAN发送接收数据包结构体定义
不论是CAN发送WriteFile,还是CAN接收ReadFile,都是以数据包为单位发送接收的。单个数据包大小为16字节,结构体定义如下:
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct CAN_MESSAGE
{
[FieldOffset(0)]
public uint id;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] data; // 数据字节
}
id是一个整型4字节,32位,用来记录CAN通信所需的ID信息。
id的低29位,表示id号。
id的第30位,用来标记是接收的数据包,还是发送的数据包。
id的第31位,用来设置是数据帧还是远程帧。该位为0则是数据帧,该位为1则是远程帧。
id的第32位,用来设置是标准帧还是扩展帧。该位为0则是标准帧,该位为1则是扩展帧。
data为12字节byte数组。
data[0]为CAN数据包内数据的长度,取值0-8;
data[1]-data[8],8字节,为CAN数据包内传输的数据。
data[9]-data[11]未使用。
注:结构体这样设计的主要原因还是因为要和C代码的驱动接口,做成这样效率会高些。
例如,我们要发送一个,id为5的标准数据帧,数据长度为3,分别是0x01,0x02,0x03,代码如下:
CAN_MESSAGE pktSend = new CAN_MESSAGE();
pktSend.id = 5;
// 如果是数据帧就不变,如果是远程帧就
// pktSend.id = pktSend.id | 0x20000000; // remote
// 如果是标准帧就不变,如果是扩展帧就
// pktSend.id = pktSend.id | 0x40000000; // extended
pktSend.data[0] = 3;
pktSend.data[1] = 0x01;
pktSend.data[2] = 0x02;
pktSend.data[3] = 0x03;
例如,我们要发送一个,id为55的扩展帧,数据长度为6,分别是0x04,0x04,0x04,0x05,0x05,0x05,代码如下:
CAN_MESSAGE pktSend = new CAN_MESSAGE();
pktSend.id = 5;
// 如果是数据帧就不变,如果是远程帧就
// pktSend.id = pktSend.id | 0x20000000; // remote
// 如果是标准帧就不变,如果是扩展帧就
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设置
EM335x同样支持数据包过滤功能,设置Filter可以使得CAN只接收自己需要的数据包。
调用EM335X_CAN类的SetFilter方法,可以添加一个过滤条件,或者删除一个已有的过滤条件。例如:
CAN.CAN_SetFilter (hCAN, Filter, false );
第一个参数为CAN句柄,第二个参数为过滤条件参数,为一个CAN_FILTER的结构体,在下面有说明。第三个参数如果为FALSE,则表示添加该过滤条件,如果为TRUE,则表示删除已有的该过滤条件。
有多个过滤条件的情况下,只要数据包可以满足任意一个过滤条件,那么该数据包就可以被接收。
CAN过滤条件Filter结构体定义
CAN_FILTER结构体定义如下:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct CAN_FILTER
{
[FieldOffset(0)]
public uint id;
[FieldOffset(4)]
public uint mask;
}
这里的过滤逻辑如下:
假设收到的数据包里的id,我们记为id_message,与过滤条件中的filter参数里的id和mask满足条件:(id_message&mask) == (id&mask),那么该数据包就可以接收,也就是说,mask表示需要进行对比的位,如果数据包的id这几位与filter设置里的id的这几位相同,那么该数据包就可以接收。
比如:
一个filter的mask = 0x03,即2进制的b0000 0011,即需要比较最后的两位。
filter的id = 0x02,即2进制的b0000 0010。
那么数据包id如果最后两位为 10,该数据包就可以通过过滤条件被接收。
数据包id = 0xF7,即2进制b1111 0111,无法接收。
数据包id = 0xE6,即2进制b1110 0110,可以接收。
详细信息,可以电话,邮件或论坛提问方式咨询英创工程师。
全部0条评论
快来发表一下你的评论吧 !