上位机为什么选择Linux系统,而不是Windows
-
内核集成CAN驱动
Linux内核从2.6.25开始加入了对CAN驱动的支持,这意味着在任何Linux发行版上连接CAN盒子,都直接适配支持。 笔者开发常用的PCAN-USB(Peak CAN),通过USB端口连接到上位机,在内核加载CAN模块,就可以像普通网络设备一样使用。淘宝上可以买到的 ZLG(周立功)和兼容ZLG的CAN卡,理论上也都可以适配。
-
遵循Socket的编程模型
Linux CAN完全照搬了以太网网络开发的模式,采用基于Socket的编程模型,所以Linux的CAN编程接口又称为SocketCAN 编程接口。熟悉Linux网络开发的工程师可以很容易地上手处理CAN报文的发送接收和解析。基于统一的Socket模型,可以直接和select,epoll等高级IO并发通讯模型适配,方便处理多通道CAN并发的场景。
-
易于使用脚本做二次开发
随着python 3.3增加了对SocketCAN的支持,开源社区涌现了不少优秀的CAN开发和测试的基础库,支持诸如CAN报文dump分析、DBC文件处理转换、诊断协议处理和应用,FOTA等等常见的汽车电子开发任务。只要熟悉基本python的开发知识,在这些基础库之上,进行配置定制或者进行二次开发,解决实际工作中的问题,难度大大降低了。
-
Windows下的CANoe平台很贵
以前汽车电子开发,一般都要在Vector的CANoe软硬件专业平台进行。这套平台目前只支持Windows操作系统,而且价格昂贵。如果要同时采集六个CAN口的设备,就至少需要两个VN1640的设备,价格至少要十几万起。而笔者使用PCAN-USB X6和SocketCAN搭建的开发测试系统,成本在一万元上下。除了在少数专业领域(之后会介绍),它能覆盖大多数常见的开发需求。另外,SocketCAN还能支持虚拟CAN设备(vcan),即使没有物理CAN卡,也可以使用模拟CAN设备,进行开发。这种vcan设备对于某些开发场景,有特别的价值。
总结下来,基于SocketCAN开发汽车电子应用,从软件开发角度来说,知识迁移成本低,特别是对于Linux系统工程师接近零成本。硬件部署成本相对于Vector CANoe显著降低。不管是软硬件都是很有潜力的选择。
CAN通讯简介
CAN全称是Controller Area Network(控制器区域网络),由博世公司开发,是车载领域广泛应用的通讯协议。CAN使用两条平行的差分信号线连接所有节点,双端接有120欧姆的特性阻抗。 每个节点都能够向总线发送或者接收消息,但无法同时进行的。发送消息采用广播形式,总线上其它节点都能监听到该消息,节点可以设置过滤器屏蔽不感兴趣的消息。当有多个消息同时发往总线时,高优先级的消息总是被优先发送,低优先级的消息会等待一段时间后再尝试发送。一个消息或帧主要包括标识符(ID),它表示信息的优先级,最多八个数据字节。CRC、ACK和其他帧部分也是消息的一部分。改进了的CAN FD将每个帧拓展至最多64字节。 消息采用不归零(NRZ)格式串联传送到主线并可被所有节点接收。
SocketCAN模型介绍
SocketCAN是集成到Linux kernel内的一整套开源的CAN驱动和网络协议栈,由大众汽车研究所开发。在它出现之前,Linux的CAN驱动和协议栈存在一个问题,就是多个进程无法并发的访问同一个CAN设备,一个进程也无法同时访问多个CAN设备。而SocketCAN将CAN抽象成一种网络设备,而不是字节设备。请看下图,它在Socket层和物理设备间增加了CAN驱动和新的CAN协议族(protocol family),和其他网络设备处于平行的地位。这样,应用层访问CAN设备,就像访问以太网设备一样,多个进程通过传统的socket API就可以并发的访问每个CAN设备。每个CAN设备在系统中也注册为独立的网络接口(network interface),每个进程都可以通过多个socket绑定不同的CAN网络接口来访问多个设备。
- 载入CAN驱动和注册CAN设备
$ modprobe can_dev
$ modprobe can
$ modprobe can_raw
$ sudo ip link set can0 type can bitrate 500000
$ sudo ip link set up can0
- 虚拟CAN网络
Linux通过SocketCAN可以支持虚拟CAN设备,可以用来模拟基于CAN协议的ECU组件,对逻辑进行一些初步的测试。 注册虚拟设备也很类似物理设备的流程
$ modprobe can
$ modprobe can_raw
$ modprobe vcan
$ sudo ip link add dev vcan0 type vcan
$ sudo ip link set up vcan0
$ ip link show vcan0
- 发送和接收CAN报文
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int s;
struct sockaddr_can addr;
struct ifreq ifr;
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
/* can0 is the CAN interface */
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr));
struct can_frame frame;
nbytes = read(s, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("can raw socket read");
return 1;
}
/* paranoid check ... */
if (nbytes < sizeof(struct can_frame)) {
fprintf(stderr, "read: incomplete CAN frame\n");
return 1;
}
/* do something with the received CAN frame */