能否在CAN总线上模仿仲裁?
Is it possible to imitate arbitration on CAN bus?
设置
我有两个节点连接到一个 CAN 总线。第一个节点是一个黑盒子,由一些实时硬件控制。第二个节点是 Linux 机器,带有 PEAK-USB CAN 控制器:
+--------+ +----------+
| HW CAN |--- CAN BUS ---| Linux PC |
+--------+ +----------+
为了调查一些与偶发帧丢失有关的问题,我想模拟 CAN 仲裁过程。为此,我将 CAN 比特率设置为 125Kb/s,并用延迟 1ms 的随机 CAN 帧对其进行泛洪,用 canbusload 控制总线负载 can-实用程序。我还监控 CAN 错误帧 运行 candump can0,0~0,#ffffffff
和整体 can 统计 ip -s -d link show can
:
26: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can state ERROR-ACTIVE restart-ms 0
bitrate 125000 sample-point 0.875
tq 500 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
pcan_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
clock 8000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0
RX: bytes packets errors dropped overrun mcast
120880 15110 0 0 0 0
TX: bytes packets errors dropped carrier collsns
234123 123412 0 0 0 0
问题
现在的问题是,当负载为 99% 时,给定的设置可以在零冲突(仲裁)或任何其他类型的错误帧的情况下工作数小时。当我减少延迟以增加总线负载时,write(2)
失败并显示 "ENOBUFS 105 No buffer space available" 或 "EAGAIN 11 Resource temporarily unavailable" -实际错误取决于我是修改 qlen 参数还是设置为默认值。
据我了解,我放的负载不是不够就是太多了。让两个节点进入仲裁的正确方法是什么?成功的结果将是接收到对应于 can/error.h
中的 CAN_ERR_LOSTARB
常量和 collsns 值而不是 0[= 的 CAN 错误帧49=].
源代码
硬件节点(带 CAN 板的 Arduino Due)
#include <due_can.h>
CAN_FRAME input, output;
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600);
Serial.println("start");
// Can0.begin(CAN_BPS_10K);
Can0.begin(CAN_BPS_125K);
// Can0.begin(CAN_BPS_250K);
output.id = 0x303;
output.length = 8;
output.data.low = 0x12abcdef;
output.data.high = 0x24abcdef;
}
// the loop function runs over and over again forever
void loop() {
Can0.sendFrame(output);
Can0.read(input);
delay(1);
}
Linux节点
#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 main(int argc, char *argv[])
{
int s;
int nbytes;
struct sockaddr_can addr;
struct can_frame frame;
struct ifreq ifr;
const char *ifname = "can0";
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Error while opening socket");
return -1;
}
strcpy(ifr.ifr_name, ifname);
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
printf("%s at index %d\n", ifname, ifr.ifr_ifindex);
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error in socket bind");
return -2;
}
frame.can_id = 0x304;
frame.can_dlc = 2;
frame.data[0] = 0x11;
frame.data[1] = 0x22;
int sleep_ms = atoi(argv[1]) * 1000;
for (;;) {
nbytes = write(s, &frame, sizeof(struct can_frame));
if (nbytes == -1) {
perror("write");
return 1;
}
usleep(sleep_ms);
}
return 0;
}
来自 documentation 小节 4.1.2 RAW 套接字选项 CAN_RAW_ERR_FILTER,它说错误默认情况下未激活,这就是 ip
中丢失的仲裁字段未被激活的原因增加。
为了打开所有错误,您需要添加这两行:
can_err_mask_t err_mask = CAN_ERR_MASK;
setsockopt(socket_can, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
但此功能并非适用于所有驱动程序和设备,因为它需要硬件具有环回模式。在 PEAK-USB 的情况下,如果设备的固件版本低于 4.x,则似乎没有环回 [source]。因此 SocketCAN 将无法检测到丢失的仲裁。
设置
我有两个节点连接到一个 CAN 总线。第一个节点是一个黑盒子,由一些实时硬件控制。第二个节点是 Linux 机器,带有 PEAK-USB CAN 控制器:
+--------+ +----------+
| HW CAN |--- CAN BUS ---| Linux PC |
+--------+ +----------+
为了调查一些与偶发帧丢失有关的问题,我想模拟 CAN 仲裁过程。为此,我将 CAN 比特率设置为 125Kb/s,并用延迟 1ms 的随机 CAN 帧对其进行泛洪,用 canbusload 控制总线负载 can-实用程序。我还监控 CAN 错误帧 运行 candump can0,0~0,#ffffffff
和整体 can 统计 ip -s -d link show can
:
26: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can state ERROR-ACTIVE restart-ms 0
bitrate 125000 sample-point 0.875
tq 500 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
pcan_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
clock 8000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0
RX: bytes packets errors dropped overrun mcast
120880 15110 0 0 0 0
TX: bytes packets errors dropped carrier collsns
234123 123412 0 0 0 0
问题
现在的问题是,当负载为 99% 时,给定的设置可以在零冲突(仲裁)或任何其他类型的错误帧的情况下工作数小时。当我减少延迟以增加总线负载时,write(2)
失败并显示 "ENOBUFS 105 No buffer space available" 或 "EAGAIN 11 Resource temporarily unavailable" -实际错误取决于我是修改 qlen 参数还是设置为默认值。
据我了解,我放的负载不是不够就是太多了。让两个节点进入仲裁的正确方法是什么?成功的结果将是接收到对应于 can/error.h
中的 CAN_ERR_LOSTARB
常量和 collsns 值而不是 0[= 的 CAN 错误帧49=].
源代码
硬件节点(带 CAN 板的 Arduino Due)
#include <due_can.h>
CAN_FRAME input, output;
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600);
Serial.println("start");
// Can0.begin(CAN_BPS_10K);
Can0.begin(CAN_BPS_125K);
// Can0.begin(CAN_BPS_250K);
output.id = 0x303;
output.length = 8;
output.data.low = 0x12abcdef;
output.data.high = 0x24abcdef;
}
// the loop function runs over and over again forever
void loop() {
Can0.sendFrame(output);
Can0.read(input);
delay(1);
}
Linux节点
#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 main(int argc, char *argv[])
{
int s;
int nbytes;
struct sockaddr_can addr;
struct can_frame frame;
struct ifreq ifr;
const char *ifname = "can0";
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Error while opening socket");
return -1;
}
strcpy(ifr.ifr_name, ifname);
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
printf("%s at index %d\n", ifname, ifr.ifr_ifindex);
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error in socket bind");
return -2;
}
frame.can_id = 0x304;
frame.can_dlc = 2;
frame.data[0] = 0x11;
frame.data[1] = 0x22;
int sleep_ms = atoi(argv[1]) * 1000;
for (;;) {
nbytes = write(s, &frame, sizeof(struct can_frame));
if (nbytes == -1) {
perror("write");
return 1;
}
usleep(sleep_ms);
}
return 0;
}
来自 documentation 小节 4.1.2 RAW 套接字选项 CAN_RAW_ERR_FILTER,它说错误默认情况下未激活,这就是 ip
中丢失的仲裁字段未被激活的原因增加。
为了打开所有错误,您需要添加这两行:
can_err_mask_t err_mask = CAN_ERR_MASK;
setsockopt(socket_can, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
但此功能并非适用于所有驱动程序和设备,因为它需要硬件具有环回模式。在 PEAK-USB 的情况下,如果设备的固件版本低于 4.x,则似乎没有环回 [source]。因此 SocketCAN 将无法检测到丢失的仲裁。