一只海星的主页

CAN帧与SocketCan帧网络通信和转换

一、背景

can通信在汽车行业非常的普遍,搞车必备良器!
最近在做一个Can盒转发功能,就是将远程驾驶舱CAN帧发送车上socket can设备,进而转发到DCU达到控制车辆的目的。

二、协议简介

基础概念

Can总线

CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。

CAN协议主要有两种帧,标准帧和拓展帧

标准帧格式

CAN 标准帧信息为11个字节,包括两部分:信息和数据部分。前3个字节为信息部分。

字节1为帧信息。第7位(FF)表示帧格式,在标准帧中,FF=0;第6位(RTR)表示帧的类型,RTR=0表示为数据帧,RTR=1表示为远程帧;DLC表示在数据帧时实际的数据长度。
字节2、3为报文识别码,11位有效。
字节4~1为数据帧的实际数据,远程帧时无效。

CAN2.0B扩展帧

CAN 扩展帧信息为13个字节,包括两部分,信息和数据部分。前5个字节为信息部分

字节1为帧信息。第7位(FF)表示帧格式,在扩展帧中,FF=1;第6位(RTR)表示帧的类型,RTR=0表示为数据帧,RTR=1表示为远程帧;DLC 表示在数据帧时实际的数据长度。
字节2~5为报文识别码,其高29位有效。
字节6~13数据帧的实际数据,远程帧时无效。

三、Can盒功能实现

总体来说结构非常的简单,我就着框架图简单介绍下。

主要框架实现流程


首先驾驶舱和车辆之间需要有一个业务长连接,用来发起CAN盒转发任务。
可以是自己私有的TCP业务协议连接,或者基于MQTT实现的业务连接都行。

建立UDP连接

  • 驾驶舱启动UDP端口监听;
  • 通过业务长连接通知车辆发起连接命令;
  • 车辆主动连接到驾驶舱的UDP端口;

这里注意几点:
1. 驾驶舱如在公网,需要固定公网地址,这样就免去了两端的打洞连接过程,网络拓扑大大降低。
2. 转发连接可以是UDP也可以是tcp还可以是别的可靠连接等协议,满足不同场景需求;建议将连接封装成session做通道管理、心跳、RTT等,也方便切换。

转发驾驶舱下行命令

这里以拓展帧为例。
驾驶舱下发一个13字节的CAN标准拓展帧如:
0x88, 0x18, 0xFB, 0x31, 0xA9,0x00, 0x00, 0xFA, 0x00, 0x00, 0x00,0x09, 0xF3
通过网络发送到车端,需要用socket can接口发送到DCU底盘,这里需要做一个转换;
socket can接口抽象成
socket_can_send(int *sock_fd, uint32_t id, uint8_t len, uint8_t *data)

我们要将拓展帧用socket can接口转发至DCU!
首先我们来看看13个字节的拓展帧,由5个字节的头信息+8个字节的数据信息,逐个来看
字节01:0x88


bit7 FF=1 表示这是一个拓展帧
bit6 RTR=0 表示这是数据帧
bit0-bit3 DLC=8 表示这个帧的数据长度为8
字节02-04:0x18, 0xFB, 0x31, 0xA9

这里4个字节,注意我们用了bit0-bit28一共29位来表示CANID!也就是最大1FFF FFFF的值。
这里的CANID组合就是按字节序排列,CANID=0x18FB31A9这样的值!
直接填充到socket_can_send接口的id就行了吗?如果这样做,DCU会告诉你它什么也没收到!
socket canid定义

/**
 * struct can_frame - basic CAN frame structure
 * @can_id:  CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
 * @can_dlc: frame payload length in byte (0 .. 8) aka data length code
 *           N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
 *           mapping of the 'data length code' to the real payload length
 * @data:    CAN frame payload (up to 8 byte)
 */
struct can_frame {
 canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
 __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
 __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

can_id定义如下所示,是一个无符号的32位整形数
typedef __u32 canid_t;
can_id数据组织形式如下

“`
/*
 * Controller Area Network Identifier structure
 *
 * bit 0-28 : CAN identifier (11/29 bit)
 * bit 29 : error message frame flag (0 = data frame, 1 = error message)
 * bit 30 : remote transmission request flag (1 = rtr frame)
 * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
 */
“`
0-28位为标识符,如果是扩展帧,则高11位为标准ID
29位标识是数据帧还是错误消息
30位说明是否是远程帧
31位说明是标准帧还是扩展帧。

拓展帧canid与socket can_id转换
CANID=0x18FB31A9;FF=1表示拓展帧 RTR=0 表示数据帧
can_id bit 0-28 对应 CANID bit 0-28;
bit29 = 0 表示这是数据帧 对应 CANID RTR=0
bit30 = 0 表示这不是远程帧 对应 CANID RTR=0
bit31 = 1 表示这是一个拓展帧 对应 CANID FF=1
所以!
拓展帧CANID=0x18FB31A9 对应 socket can_id = 0x18FB31A9

具体代码实现,直接操作位的话参考can_frame中can_id的数据组织形式及处理
C语言我建议用位域,更加清晰!

转发DCU帧到驾驶舱

同上原理,反向canid转换一下,8位数据直接透传

参考

CAN帧格式(标准帧、拓展帧)
can_frame中can_id的数据组织形式及处理

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注