CAN
总线配置
在
Linux
系统中,
CAN
总线接口设备作为网络设备被系统进行统一管理。在控制台下,
CAN
总线的配置和
以太网的配置使用相同的命令。
在控制台上输入命令:
1.
ifconfig
–
a
可以得到以下结果:
1.
can0 Link encap:UNSPEC HWaddr
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
2.
NOARP MTU:16 Metric:1
3.
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
4.
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
5.
collisions:0 txqueuelen:10
6.
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
7.
Interrupt:18
8.
eth0 Link encap:Ethernet HWaddr 00:50:c2:22:3b:0e
9.
UP BROADCAST MULTICAST MTU:1500 Metric:1
10.
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
11.
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
12.
collisions:0 txqueuelen:1000
13.
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
14.
eth1 Link encap:Ethernet HWaddr 00:50:c2:22:3b:60
15.
UP BROADCAST MULTICAST MTU:1500 Metric:1
16.
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
17.
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
18.
collisions:0 txqueuelen:1000
19.
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
20.
Interrupt:41 Base address:0xe000
21.
lo Link encap:Local Loopback
22.
inet addr:127.0.0.1 Mask:255.0.0.0
23.
inet6 addr: ::1/128 Scope:Host
24.
UP LOOPBACK RUNNING MTU:16436 Metric:1
25.
RX packets:256 errors:0 dropped:0 overruns:0 frame:0
26.
TX packets:256 errors:0 dropped:0 overruns:0 carrier:0
27.
collisions:0 txqueuelen:0
28.
RX bytes:19952 (19.9 KB) TX bytes:19952 (19.9 KB)
在上面的结果中,
eth0
和
eth1
设备为以太网接口,
can0
设备为
CAN
总线接口。
接下来使用
ip
命令来配置
CAN
总线的位速率:
1.
ip link set can0 type can tq 125 prop-seg 6phase-seg1 7 phase-seg2 2 sjw
1
也可以使用
ip
命令直接设定位速率:
1.
ip link set can0 type can bitrate 125000
当设置完成后,可以通过下面的命令查询
can0
设备的参数设置:
1.
ip -details link show can0
当设置完成后,可以使用下面的命令使能
can0
设备:
1.
ifconfig can0 up
使用下面的命令取消
can0
设备使能:
1.
ifconfig can0 down
在设备工作中,可以使用下面的命令来查询工作状态:
1.
ip -details -statistics link show can0
CAN
总线应用开发接口
由于系统将
CAN
设备作为网络设备进行管理,因此在
CAN
总线应用开发方面,
Linux
提供了
SocketCAN
接
口,使得
CAN
总线通信近似于和以太网的通信,应用程序开发接口更加通用,也更加灵活。
此外,通过
https://gitorious.org/linux-can/can-utils
网站发布的基于
SocketCAN
的
can-utils
工具
套件,也可以实现简易的
CAN
总线通信。
下面具体介绍使用
SocketCAN
实现通信时使用的应用程序开发接口。
1.
初始化
SocketCAN
中大部分的数据结构和函数在头文件
linux/can.h
中进行了定义。
CAN
总线套接字的创建采用
标准的网络套接字操作来完成。网络套接字在头文件
sys/socket.h
中定义。套接字的初始化方法如下:
1.
int s;
2.
struct sockaddr_can addr;
3.
struct ifreq ifr;
4.
s
=
socket
(PF_CAN, SOCK_RAW, CAN_RAW); //
创建
SocketCAN
套接字
5.
strcpy(ifr.ifr_name, "can0" );
6.
ioctl(s, SIOCGIFINDEX, &ifr); //
指定
can0
设备
7.
addr.can_family
=
AF_CAN
;
8.
addr.can_ifindex
=
ifr
.ifr_ifindex;
9.
bind(s, (struct sockaddr *)&addr, sizeof(addr)); //
将套接字与
can0
绑定
2.
数据发送
在数据收发的内容方面,
CAN
总线与标准套接字通信稍有不同,每一次通信都采用
can_ frame
结构体将数
据封装成帧。结构体定义如下:
1.
struct can_frame {
2.
canid_t can_id; //CAN
标识符
3.
__u8 can_dlc; //
数据场的长度
4.
__u8 data[8]; //
数据
5.
};
can_id
为帧的标识符,
如果发出的是标准帧,
就使用
can_id
的低
11
位;
如果为扩展帧,
就使用
0
~
28
位。
can_id
的第
29
、
30
、
31
位是帧的标志位,用来定义帧的类型,定义如下:
1.
#define CAN_EFF_FLAG 0x80000000U //
扩展帧的标识
2.
#define CAN_RTR_FLAG 0x40000000U //
远程帧的标识
3.
#define CAN_ERR_FLAG 0x20000000U //
错误帧的标识,用于错误检查
数据发送使用
write
函数来实现。如果发送的数据帧
(
标识符为
0x123)
包含单个字节
(0xAB)
的数据,可采
用如下方法进行发送:
1.
struct can_frame frame;
2.
frame.can_id
=
0x123
; //
如果为扩展帧,那么
frame.can_id
=
CAN_EFF_FLAG
| 0x123;
3.
frame.can_dlc
=
1
; //
数据长度为
1
4.
frame.data[0] = 0xAB; //
数据内容为
0xAB
5.
int
nbytes
=
write
(s, &frame, sizeof(frame)); //
发送数据
6.
if (nbytes != sizeof(frame)) //
如果
nbytes
不等于帧长度,就说明发送失败
7.
printf("Error\n!");
如果要发送远程帧
(
标识符为
0x123)
,可采用如下方法进行发送:
1.
struct can_frame frame;
2.
frame.can_id
=
CAN_RTR_FLAG
| 0x123;
3.
write(s, &frame, sizeof(frame));
3.
数据接收
数据接收使用
read
函数来完成,实现如下:
1.
struct can_frame frame;
2.
int
nbytes
=
read
(s, &frame, sizeof(frame));
当然,
套接字数据收发时常用的
send
、
sendto
、
sendmsg
以及对应的
recv
函数也都可以用于
CAN
总线数据
的收发。
4.
错误处理
当帧接收后,可以通过判断
can_id
中的
CAN_ERR_FLAG
位来判断接收的帧是否为错误帧。如果为错误帧,
可以通过
can_id
的其他符号位来判断错误的具体原因。
错误帧的符号位在头文件
linux/can/error.h
中定义。
5.
过滤规则设置
在数据接收时,
系统可以根据预先设置的过滤规则,
实现对报文的过滤。
过滤规则使用
can_filter
结构体
来实现,定义如下:
1.
struct can_filter {
2.
canid_t can_id;
3.
canid_t can_mask;
4.
};
过滤的规则为:
1.
接收到的数据帧的
can_id &
mask
== can_id & mask
通过这条规则可以在系统中过滤掉所有不符合规则的报文,使得应用程序不需要对无关的报文进行处理。
在
can_filter
结构的
can_id
中,符号位
CAN_INV_FILTER
在置位时可以实现
can_id
在执行过滤前的位反
转。
用户可以为每个打开的套接字设置多条独立的过滤规则,使用方法如下:
1.
struct can_filter rfilter[2];
2.
rfilter[0]
.can_id
=
0x123
;
3.
rfilter[0]
.can_mask
=
CAN_SFF_MASK
; //#define CAN_SFF_MASK 0x000007FFU
4.
rfilter[1]
.can_id
=
0x200
;
5.
rfilter[1]
.can_mask
=
0x700
;
6.
setsockopt(s,
SOL_CAN_RAW,
CAN_RAW_FILTER,
&rfilter,
sizeof(rfilter));
//
设置规则
在极端情况下,如果应用程序不需要接收报文,可以禁用过滤规则。这样的话,原始套接字就会忽略所有
接收到的报文。在这种仅仅发送数据的应用中,可以在内核中省略接收队列,以此减少
CPU
资源的消耗。
禁用方法如下:
1.
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //
禁用过滤规则
通过错误掩码可以实现对错误帧的过滤,例如:
1.
can_err_mask_t
err_mask
= ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
2.
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, err_mask,
sizeof(err_mask));
在默认情况下,本地回环功能是开启的,可以使用下面的方法关闭回环
/
开启功能:
1.
int
loopback
=
0
; // 0
表示关闭
, 1
表示开启
(
默认
)
2.
setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback,
sizeof(loopback));
在本地回环功能开启的情况下,
所有的发送帧都会被回环到与
CAN
总线接口对应的套接字上。
默认情况下,
发送
CAN
报文的套接字不想接收自己发送的报文,因此发送套接字上的回环功能是关闭的。可以在需要的
时候改变这一默认行为:
1.
int
ro
=
1
; // 0
表示关闭
(
默认
), 1
表示开启
2.
setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &ro, sizeof(ro));