【问题标题】:Is it possible to imitate arbitration on CAN bus?是否可以在 CAN 总线上模拟仲裁?
【发布时间】:2019-07-18 07:36:45
【问题描述】:

设置

我有两个节点连接到一个 CAN 总线。第一个节点是一个黑盒,由一些实时硬件控制。第二个节点是一台带有 PEAK-USB CAN 控制器的 Linux 机器:

+--------+               +----------+
| HW CAN |--- CAN BUS ---| Linux PC |
+--------+               +----------+

为了调查与偶尔丢帧相关的一些问题,我想模拟 CAN 仲裁过程。为此,我将 CAN 比特率设置为 125Kb/s,并使用延迟为 1 毫秒的随机 CAN 帧对其进行泛洪,使用 can-utils 中的 canbusload 控制总线负载.我还监控运行 candump can0,0~0,#ffffffff 的 CAN 错误帧和使用 ip -s -d link show can 的整体 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暂时不可用" - 实际错误取决于我是修改 qlen 参数还是设置为默认值。

据我了解,我的负载要么不够,要么太多。让两个节点进入仲裁的正确方法是什么?成功的结果将是接收到的 CAN 错误帧,对应于来自 can/error.hCAN_ERR_LOSTARB 常数和 0 以外的 collsns 值。

源代码

硬件节点(Arduino Due with CAN board)

#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;
}

【问题讨论】:

  • 你能给我们生成随机 CAN 帧的代码和你的黑盒节点使用的 ID 吗?通常在 99% 的总线负载下你应该有错误,我怀疑只有你的 USB 设备在传输一些东西,而你的黑匣子永远无法发送。
  • @Benoît 我发布了两个节点的源代码。另外,ip show 清楚地表明有发送和接收的数据包。
  • 尝试以下操作:can_err_mask_t err_mask = CAN_ERR_MASK; setsockopt(socket_can, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &amp;err_mask, sizeof(err_mask));(绑定前)
  • 来自 [documentation] (kernel.org/doc/Documentation/networking/can.txt) 小节 4.1.2 RAW 套接字选项 CAN_RAW_ERR_FILTER,它说错误默认情况下未激活,这就是为什么 ip 中的 LOSTARB 字段没有增加.我给你的 2 行应该启用了这些错误,但驱动程序/固件可能不兼容。我在PEAK forum 上读到,如果您的 USB 设备的固件版本
  • “为此,我将 CAN 比特率设置为 125Kb/s,并用延迟为 1ms 的随机 CAN 帧填充它”这是一个非常弱的测试。 CAN 设计用于 100% 总线负载 - 仲裁“CSMA/CA”意味着即使在 100% 负载下也能避免碰撞。至于虚假仲裁错误,我能想到两件事:最明显的是臭名昭著的信号地丢失。更罕见的情况是两个节点同时发送相同的标识符(更准确地说是仲裁字段)但具有不同的有效负载。不过,我对 Linux 的绒毛一无所知 - 这可能是问题所在。

标签: can-bus socketcan


【解决方案1】:

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 将无法检测丢失的仲裁。

【讨论】:

    猜你喜欢
    • 2022-11-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-07
    • 2021-10-07
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多