CAN BUS的总线负载率是CAN总线架构协议设计时的一个重要的指标。一般建议负载率峰值不要高于80%,平均负载率不要超过50%。当然这只是一般建议,具体根据使用场景和系统设计而定。
负载率定义
关于CAN负载率的定义和计算,很多文章写得不求甚解,用帧数量来计算负载率是非常不正确的做法。
其实总线负载率的定义其实是非常简单明了的:
*总线负载率=总线每秒上传输占用时间/1s 100%
CAN2.0的总线负载率
对于CAN2.0而言,由于波特率是固定的,所以:
总线负载率=总线每秒上传输的实际bit数量/总线波特率100%*
原理非常简单,波特率的定义就是每秒CAN总线上可以传输多少CAN数据bit,总线负载率自然就是总线实际传输的bit数量比上总线可以承载的最大bit数了。
例如,100K的总线波特率,总线上最大承载的数据量就是100K个bit。如果总线上实际传输了50K个bit位,那么负载率就是50%。
CAN FD的总线负载率
CAN FD由于支持速率可变,总线占用时间的计算就稍微麻烦一些
负载率计算
对于上面的计算公式,对于一个CAN总线而言,波特率一般都是已知的。计算负载率的关键就是通过CAN报文统计出总线上每秒传输的bit数量。那么就需要回到CAN的帧格式来计算实际发生的bit数。
一帧数据包含以下数量的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 */
}
软件计算负载率的缺陷
对于软件统计负载率,即使采用精确计算填充位的算法,由于以下原因仍然不能真实的反应总线的负载情况。
- 对于CRC校验错误,或者格式错误的帧,软件层面一般不会接收到,也难以统计这部分错误帧产生的总线负载
硬件统计负载率
相比软件计算负载率,对需要精确计算总线负载的场合,更好的方案是用专业的硬件来统计发生的bit数量,并计算负载率。
测量CAN BUS总线负载率的工具和软件
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软件均支持总线负载率的显示。
评论
查看更多