5分钟学会CAN/CANFD总线负载率计算

接口/总线/驱动

1139人已加入

描述

CAN BUS的总线负载率是CAN总线架构协议设计时的一个重要的指标。一般建议负载率峰值不要高于80%,平均负载率不要超过50%。当然这只是一般建议,具体根据使用场景和系统设计而定。

负载率定义

关于CAN负载率的定义和计算,很多文章写得不求甚解,用帧数量来计算负载率是非常不正确的做法。

其实总线负载率的定义其实是非常简单明了的:

*总线负载率=总线每秒上传输占用时间/1s 100%

CAN2.0的总线负载率

对于CAN2.0而言,由于波特率是固定的,所以:

总线负载率=总线每秒上传输的实际bit数量/总线波特率100%*

Linux系统

原理非常简单,波特率的定义就是每秒CAN总线上可以传输多少CAN数据bit,总线负载率自然就是总线实际传输的bit数量比上总线可以承载的最大bit数了。

例如,100K的总线波特率,总线上最大承载的数据量就是100K个bit。如果总线上实际传输了50K个bit位,那么负载率就是50%。

CAN FD的总线负载率

CAN FD由于支持速率可变,总线占用时间的计算就稍微麻烦一些

Linux系统

负载率计算

对于上面的计算公式,对于一个CAN总线而言,波特率一般都是已知的。计算负载率的关键就是通过CAN报文统计出总线上每秒传输的bit数量。那么就需要回到CAN的帧格式来计算实际发生的bit数。

Linux系统

一帧数据包含以下数量的bit构成:

1位起始位。

11位标识符

1位RTR

6位控制域

0到64位数据字段

15位CRC

位填充是可能的,在上面的每一个序列的5个连续位相同的水平。最坏情况下大约是19位。

3位分隔符,ack等。

帧结束7位

帧后的3位间隔域。

如果软件需要精确计算负载率,无疑是比较麻烦的。因为对于软件层面,并感知不到除了标识符,控制域和数据域以外的其他bit,并且由于填充位的数量因数据不同而不同,软件做精确的bit位数量计算就比较耗费资源。

软件计算负载率

基于这样的情况,实际可以考虑3种方案

1. 按最少的填充位可能性来计算(忽略填充位)

忽略填充位显然会少统计很多bit数量,导致计算出的负载率比实际的偏低,但是每帧的bit数计算方式简单。

/* eff : 扩展帧标识 */

can_frame_length = (eff ? 67 : 47) + frame->len * 8;

2. 按最多的填充位的可能性来计算

按最多的填充位计算负载率,会导致计算出的负载率比实际的偏高,但是每帧的bit数计算方式也比较简单。

/* eff : 扩展帧标识 */

can_frame_length = (eff ? 80 : 55) + frame->len * 10;

3.依据每一帧数据,精确的计算出填充位的数量

这种方式是最精确的,但是由软件计算会比较复杂,开销较大,需要软件讲每一帧的bit按照二进制进行排列,然后按照CAN协议位填充的要求,遇到5个相同的bit就插入一个相反的填充位,也就是增加一个bit数。

以下是帧bit数计算代码

static unsigned cfl_exact(struct can_frame *frame)
{
  uint8_t bitmap[16];
  unsigned start = 0, end;
  crc_t crc;
  uint16_t crc_be;
  uint8_t mask, lookfor;
  unsigned i, stuffed;
  const int8_t clz[32] = /* count of leading zeros in 5 bit numbers */
    { 5, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

  /* Prepare bitmap */
  memset(bitmap, 0, sizeof(bitmap));
  if (frame- >can_id & CAN_EFF_FLAG) {
    /* bit            7      0 7      0 7      0 7      0
     * bitmap[0-3]   |.sBBBBBB BBBBBSIE EEEEEEEE EEEEEEEE| s = SOF, B = Base ID (11 bits), S = SRR, I = IDE, E = Extended ID (18 bits)
     * bitmap[4-7]   |ER10DLC4 00000000 11111111 22222222| R = RTR, 0 = r0, 1 = r1, DLC4 = DLC, Data bytes
     * bitmap[8-11]  |33333333 44444444 55555555 66666666| Data bytes
     * bitmap[12-15] |77777777 ........ ........ ........| Data bytes
     */
    bitmap[0] = (frame- >can_id & CAN_EFF_MASK) > > 23;
    bitmap[1] = ((frame- >can_id > > 18) & 0x3f) < < 3 |
          3 < < 1                          | /* SRR, IDE */
          ((frame- >can_id > > 17) & 0x01);
    bitmap[2] = (frame- >can_id > > 9) & 0xff;
    bitmap[3] = (frame- >can_id > > 1) & 0xff;
    bitmap[4] = (frame- >can_id & 0x1) < < 7              |
          (!!(frame- >can_id & CAN_RTR_FLAG)) < < 6 |
          0 < < 4                         | /* r1, r0 */
          (frame- >can_dlc & 0xf);
    memcpy(&bitmap[5], &frame- >data, frame- >can_dlc);
    start = 1;
    end = 40 + 8*frame- >can_dlc;
  } else {
    /* bit           7      0 7      0 7      0 7      0
     * bitmap[0-3]  |.....sII IIIIIIII IRE0DLC4 00000000| s = SOF, I = ID (11 bits), R = RTR, E = IDE, DLC4 = DLC
     * bitmap[4-7]  |11111111 22222222 33333333 44444444| Data bytes
     * bitmap[8-11] |55555555 66666666 77777777 ........| Data bytes
     */
    bitmap[0] = (frame- >can_id & CAN_SFF_MASK) > > 9;
    bitmap[1] = (frame- >can_id > > 1) & 0xff;
    bitmap[2] = ((frame- >can_id < < 7) & 0xff) |
          (!!(frame- >can_id & CAN_RTR_FLAG)) < < 6 |
          0 < < 4 | /* IDE, r0 */
          (frame- >can_dlc & 0xf);
    memcpy(&bitmap[3], &frame- >data, frame- >can_dlc);
    start = 5;
    end = 24 + 8 * frame- >can_dlc;
  }

  /* Calc and append CRC */
  crc = calc_bitmap_crc(bitmap, start, end);
  crc_be = htons(crc < < 1);
  assert(end % 8 == 0);
  memcpy(bitmap + end / 8, &crc_be, 2);
  end += 15;

  /* Count stuffed bits */
  mask   = 0x1f;
  lookfor = 0;
  i   = start;
  stuffed = 0;
  while (i < end) {
    unsigned change;
    unsigned bits = (bitmap[i / 8] < < 8 | bitmap[i / 8 + 1]) > > (16 - 5 - i % 8);
    lookfor = lookfor ? 0 : mask; /* We alternate between looking for a series of zeros or ones */
    change = (bits & mask) ^ lookfor; /* 1 indicates a change */
    if (change) { /* No bit was stuffed here */
      i += clz[change];
      mask = 0x1f; /* Next look for 5 same bits */
    } else {
      i += (mask == 0x1f) ? 5 : 4;
      if (i <= end) {
        stuffed++;
        mask = 0x1e; /* Next look for 4 bits (5th bit is the stuffed one) */
      }
    }
  }
  return end - start + stuffed +
    3 +     /* CRC del, ACK, ACK del */
    7 +    /* EOF */
    3;    /* IFS */
}

软件计算负载率的缺陷

对于软件统计负载率,即使采用精确计算填充位的算法,由于以下原因仍然不能真实的反应总线的负载情况。

  1. 对于CRC校验错误,或者格式错误的帧,软件层面一般不会接收到,也难以统计这部分错误帧产生的总线负载

硬件统计负载率

相比软件计算负载率,对需要精确计算总线负载的场合,更好的方案是用专业的硬件来统计发生的bit数量,并计算负载率。

测量CAN BUS总线负载率的工具和软件

**Linux下使用canbusload **

canbusload 是linux CAN工具canutils的其中一个程序。它可以很方便的计算并刷新当前CAN总线上的负载率信息,并且提供了上述的3种软件算法进行统计(即忽略填充位,最大计算填充位,精确计算填充位)。

以下是canbusload 的使用方法

ubuser@ubuser-Lenovo-Product:/$ canbusload 

Usage: canbusload [options] < CAN interface >+
  (use CTRL-C to terminate canbusload)

Options: -t (show current time on the first line)
         -c (colorize lines)
         -b (show bargraph in 5% resolution)
         -r (redraw the terminal - similar to top)
         -i (ignore bitstuffing in bandwidth calculation)
         -e (exact calculation of stuffed bits)

Up to 16 CAN interfaces with mandatory bitrate can be specified on the 
commandline in the form: < ifname >@

测量负载率的硬件工具

SysMax的PCAN PRO和PCAN FD使用Pcanview软件,以及USBCAN-II的ZCANpro软件均支持总线负载率的显示。

Linux系统

Linux系统

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分