前言

本文简要总结本人近两周从0开始接触linux can 通信了解到的相关知识,使用的CAN设备为ARS408毫米波雷达,带有个人理解,辩证看待,虚心学习,欢迎指教。

linux can命令

雷达接在工控机的can0端口,接好后可以通过linux命令设置波特率接收到CAN报文

ifconfig # 产看can口,PC不带CAN口是没有的
# 1. 设置波特率
sudo ip link set can0 down # 不使能,再设置波特率,否则会出现设备忙
sudo ip link set can0 type can bitrate 500000 # 波特率500kb/s
ip -details link show can0 # 打印can0详细,查看修改
# 2. 查看接收的CAN帧
sudo ip link set can0 up # 使能
candump can0 # 接收can0发送的帧

CAN报文示例如下

can0  60A   [4]  01 88 4F 10
can0  60B   [8]  00 4E 83 FD 80 20 01 92
CAN 基础知识
消息ID类似MAC地址作用,用作总线仲裁
cam_frame.can_id 32bit 标识符(消息ID)29bit 29-31:1错误帧(0数据帧) 1远程帧 帧格式标志位(1扩展帧0标准帧) 标准帧11位扩展帧29位
CAN 编码相关
8x8矩阵 64bit
当前雷达CAN协议的字节序是大端编码,比特顺序是逆序
发送顺序矩阵从上到下,can_frame.data[0]是数据低位,data[0-7]即矩阵的0-7行

1. 字节序: 【小端 低位数据放低地址】"小弟弟"
    数据线位数决定着数据会按32/64bit方式分段传输,假设按字节(byte)编址,那么32bit即4字节数据有高低位,4个地址有高低位,如何存放
    字节序就是两种排列方案,大端即高位数据放低地址,小端即低位数据放低地址
    一块4G内存(32bit地址线),现有数据0x12345678的地址0x0000 0010,其最高址0xffff ffff,最低址0x0000 0000
    小端为 0x0010放0x78 0x0011放0x56 ...    大端编码即motorola格式,小端编码即intel格式
    
    反映在CAN的跨字节传输中为,当数据长度当前字节已经放不下了(数据是可能小于8bit的),需要跨字节存放,如0xf2,假设当前只能放6位
    intel格式即数据的高位放在高字节(下一字节)的高位,数据的低位放当前字节低位,起始位(第一位)在当前字节,数据会切成11 110010,val = 11<<6 + 110010
    motorola格式即数据的高位放在当前字节的高位,数据的低位放高字节低位,起始位在下一字节,数据会切成0x111100 10,val = 111110<<2 + 10
    其中所谓字节的高低位其实就是矩阵从上到下从左到右1-64的编号(即比特顺序正序),所以motorola格式数据排列反映在矩阵中就是往左上角挤

2. 比特顺序: 矩阵编号的顺序
    正序从左上到右下1-64,逆序从右上到左下1-64

在这里插入图片描述

CAN 编程
  • 建议在实际编程中将传感器厂商的协议 “翻译” 成结构体定义,这样对收到的数据memcpy进结构体即可,而不再需要对数据与或移位,这里给出参考 ARS408Protocol.h
  • 注: 此做法未在网上找到他人参考,未细究是否合理,对于小端序结构体按协议字段从上往下定义,代码自然简洁;但对大端序,这里逆着协议文档的字段定义,字节内的顺序收到后也要进行逆序再memcpy会显得比较麻烦
  • 存在即合理,大端序符合人类阅读习惯,小端序处理高效
参考

2020-07-12
  • 《linux can 编程详解》中示例代码的奇怪错误与过滤器的设置
// 设置过滤规则过滤can_id,一个int-32bit表示一条规则,可以用int数组设置多条
// 匹配规则为 (received_can_id & mask) == (can_id & mask)
// 如标准帧的mask宏#define CAN_SFF_MASK 0x0000 07FF 即 只保留can_id的后11位
/* struct can_filter rfilter[2]; */
/* rfilter[0].can_id = 0x60A; */
/* rfilter[0].can_mask = 0x7FF; // !!这里不能用CAN_SFF_MASK宏,它与内核冲突,内核中也定义了这个宏,值不是这里期待的值 */
/* rfilter[1].can_id = 0x60B; */
/* rfilter[1].can_mask = 0x7FF; */
/* setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); */
2020-07-22
  • 使用虚拟CAN模拟雷达发送数据
sudo modprobe vcan # 每次重启都需要设置,可以设置个alias
sudo ip link add dev vcan0 type vcan
sudo ip link set dev vcan0 up # 不需要设置波特率
ifconfig # 能查看到van0设备
# 发送报文 格式 消息ID#data[0].data[1]..... 需要连着发送
cansend vcan0 60A#02.88.4f.10 ; cansend vcan0 60B#00.4e.83.fd.80.20.01.92; cansend vcan0 60B#00.4e.83.fd.80.20.01.92
# 接收
candump vcan0
Logo

更多推荐