赞
踩
CAN 是控制器局域网络 (Controller Area Network) 的简称
它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,
并最终成为国际标准(ISO11519以及ISO11898),是国际上应用最广泛的现场总线之一。
在总线空闲时,所有的单元都可开始发送消息(多主控制)
“最先”访问总线的单元可获得发送权(不管后面消息的优先级是否比自身的高)
多个单元“同时”开始发送时,发送高优先级 ID 消息的单元可获得发送权(只在同时发送时起作用)
在 CAN 协议中,所有的消息都以固定的格式发送。当两个以上的单元同时开始发送消息时,
根据ID (标识符) 决定优先级,对各消息 ID 的每个位进行逐个仲裁比较。
仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻
停止发送而进行接收工作,此处这里ID非地址,而是一种表征优先级的标识符
与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的都不需要改变。
由CAN的物理层就可以看出,CAN是一种异步通信方式,所以通信前必须统一好
同一网络上每个单元的波特率,即使有一个单元的通信速度与其它的不一样,
此单元也会输出错误信号,妨碍整个网络的通信。不同网络间则可以有不同的通信速度。
可通过发送“遥控帧” 请求其他单元发送数据。
所有的单元都可以检测错误(错误检测功能)
检测出错误的单元会立即同时通知其他所有单元(错误通知功能)
正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送
的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)
CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是
持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,
当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。
CAN 总线可连接的单元总数理论上是没有限制的。但实际上可连接的单元数
受总线上的时间延迟及电气负载的限制。
ISO/OSI 基本参照模型和 CAN协议
由于系统将CAN设备作为网络设备进行管理,因此在CAN总线应用开发方面,Linux提供了SocketCAN接口,
使得CAN总线通信近似于和以太网的通信,应用程序开发接口更加通用,也更加灵活。
SocketCAN中大部分的数据结构和函数在头文件linux/can.h 中进行了定义。
CAN总线套接字的创建采用标准的网络套接字操作来完成。网络套接字在头
文件sys/socket.h中定义。套接字的初始化方法如下:
- int s;
- struct sockaddr_can addr;
- struct ifreq ifr;
- s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建SocketCAN套接字
- strcpy(ifr.ifr_name, "can0" );
- ioctl(s, SIOCGIFINDEX, &ifr); //指定can0设备
- addr.can_family = AF_CAN;
- addr.can_ifindex = ifr.ifr_ifindex;
- bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与can0绑定
在数据收发的内容方面,CAN总线与标准套接字通信稍有不同,
//每一次通信都采用can_ frame结构体将数据封装成帧。结构体定义如下: struct can_frame { canid_t can_id; //CAN标识符 __u8 can_dlc; //数据场的长度 __u8 data[8]; //数据 }; //can_id为帧的标识符,如果发出的是标准帧,就使用can_id的低11位; //如果为扩展帧,就使用0~28位。can_id的第29、30、31位是帧的标志位,用来定义帧的类型,定义如下: #define CAN_EFF_FLAG 0x80000000U //扩展帧的标识 #define CAN_RTR_FLAG 0x40000000U //远程帧的标识 #define CAN_ERR_FLAG 0x20000000U //错误帧的标识,用于错误检查 /*数据发送使用write函数来实现。如果发送的数据帧(标识符为0x123)包含单个字节(0xAB)的数据,可采用如下方法进行发送:*/ struct can_frame frame; frame.can_id = 0x123; //如果为扩展帧,那么frame.can_id = CAN_EFF_FLAG | 0x123; frame.can_dlc = 1; //数据长度为1 frame.data[0] = 0xAB; //数据内容为0xAB int nbytes = write(s, &frame, sizeof(frame)); //发送数据 if (nbytes != sizeof(frame)) //如果nbytes不等于帧长度,就说明发送失败 printf("Error\n!"); //如果要发送远程帧(标识符为0x123),可采用如下方法进行发送: struct can_frame frame; frame.can_id = CAN_RTR_FLAG | 0x123; write(s, &frame, sizeof(frame));
数据接收使用read函数来完成,实现如下:
- struct can_frame frame;
- int nbytes = read(s, &frame, sizeof(frame));
当然,套接字数据收发时常用的send、sendto、sendmsg以及对应的recv函数也都可以用于CAN总线数据的收发。
当帧接收后,可以通过判断can_id中的CAN_ERR_FLAG位来判断接收的帧是否为错误帧。
如果为错误帧,可以通过can_id的其他符号位来判断错误的具体原因。
错误帧的符号位在头文件linux/can/error.h中定义。
在数据接收时,系统可以根据预先设置的过滤规则,实现对报文的过滤。
过滤规则使用can_filter结构体来实现,定义如下:
- struct can_filter {
- canid_t can_id;
- canid_t can_mask;
- };
- //过滤的规则为:
- //接收到的数据帧的
- can_id & mask == can_id & mask
通过这条规则可以在系统中过滤掉所有不符合规则的报文,使得应用程
序不需要对无关的报文进行处理。在can_filter结构的can_id中,符号
位CAN_INV_FILTER在置位时可以实现can_id在执行过滤前的位反转。
用户可以为每个打开的套接字设置多条独立的过滤规则,使用方法如下:
- struct can_filter rfilter[2];
- rfilter[0].can_id = 0x123;
- rfilter[0].can_mask = CAN_SFF_MASK; //#define CAN_SFF_MASK 0x000007FFU
- rfilter[1].can_id = 0x200;
- rfilter[1].can_mask = 0x700;
- setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); //设置规则
在极端情况下,如果应用程序不需要接收报文,可以禁用过滤规则。
这样的话,原始套接字就会忽略所有接收到的报文。在这种仅仅发
送数据的应用中,可以在内核中省略接收队列,以此减少CPU资源的
消耗。禁用方法如下:
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //禁用过滤规则
通过错误掩码可以实现对错误帧的过滤,例如:
- can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
- setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, err_mask, sizeof(err_mask));
在默认情况下,本地回环功能是开启的,可以使用下面的方法关闭回环/开启功能:
- int loopback = 0; // 0表示关闭, 1表示开启(默认)
- setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
在本地回环功能开启的情况下,所有的发送帧都会被回环到与CAN总线接口对应的套接字上。
默认情况下,发送CAN报文的套接字不想接收自己发送的报文,因此发送套接字上的回环功
能是关闭的。可以在需要的时候改变这一默认行为:
- int ro = 1; // 0表示关闭(默认), 1表示开启
- setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &ro, sizeof(ro));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。