车辆CAN信号,依据DBC文件解析流程

CAN信号解析流程

1.车辆CAN对应dbc文件

DBC文件是一种用于描述CAN(Controller Area Network)数据通信协议的文件格式,DBC文件中包含了CAN数据的信号定义、编码方式、单位、范围等信息,可以用于解析和生成CAN数据帧。

一个DBC文件通常包含多个数据帧和信号定义,每个数据帧包含了一个或多个信号,每个信号描述了CAN数据的一个或多个位。

dbc文件一个数据包包含内容如下:

 一个信号包含的有效内容:

Name

信号名称

Startbit

信号起始位

Length

信号长度

Byte Order

信号字节序(即大小端)

Value Type

信号数据类型

Factor

信号倍率

Offset

信号偏移量

Minimum

信号最小值

Maximum

信号最大值

2.解析步骤

信号解析公式:

实际值 = (十进制值 * Factor)+ Offset;

十进制值:接收的CAN数据包中,将信号对应的bit位长度取出,所对应的十进制数据值

解析步骤:

  1. 获取信号所需信息

  2. 判断数据是否需进行字节序转换

  3. 获取信号的十进制值

  4. 判断十进制值是否需要向有符号,浮点型转化

  5. 根据公式进行计算信号实际值

补充说明:二进制数据中,通常将最右边的位称为最低位(LSB),最左边的位称为最高位(MSB)。因此,在二进制数据中,bit0通常是指最右边的位,也就是最低位。

LSB的全称是"Least Significant Bit",:意为最低有效位;LSB位于二进制数的最右侧。数据位的最低位。
MSB的全称是"Most Significant Bit",:意为最高有效位。MSB位于二进制数的最左侧。数据位的最高位。

CAN每个报⽂可包含8Byte的字节数据域,在报⽂内数据的字节序和位序如下:

字节序:Byte0、Byte1、Byte2、Byte3、Byte4、Byte5、Byte6、Byte7

位序:bit7、bit6、bit5、bit4、bit3、bit2、bit1、bit0。

3.解析精度说明

针对dbc信号不同类型信号解析的精度说明。

目前dbc信号数据类型有:

Unsigned

需精度控制,数量占比85%以上

Signed

需精度控制,数量占比10%左右

Double

正常数据类型解析

Float

正常数据类型解析

Unsigned、Signed需精度控制类型说明:

针对这两类的信号数据精度,可采用整形,浮点型。

针对这两类的信号数据精度要求浮点型,则有Float和Double如下考虑:

Float和Double同样是两种浮点数类型。其中Float占用4个字节(32位),Double占用8个字节(64位)。

在精度方面:Double类型的精度要比Float类型高。Double类型可以表示的范围更广,小数点后的位数也更多,因此可以更精确地表示小数。

在使用性能方面:由于Double类型占用的内存空间更大,因此在处理大量浮点数运算时,Double类型的计算速度会比Float类型慢。

推荐高性能计算使用:Float类型

推荐高精度计算使用:Double类型

4.示例

Signed类型解析:

接收数据包的值为:f4 fc 00 00 00 00 00 00 (此数据包为16进制)

1. 获取信号所需信息

{
    "start_bit": 0,
    "bit_length": 16,
    "factor": 0.1,
    "offset": 0,
}

2. 判断数据是否需进行字节序转换

考虑底层(mcu)是否统一发送格式,统一大端或者小端,请于底层进行确认!!

当前内容需进行大小端转换。

f4 fc 00 00 00 00 00 00 == 》》00 00 00 00 00 00 fc f4

3. 获取信号的十进制值

二进制数据中,通常将最右边的位称为最低位(LSB),最左边的位称为最高位(MSB)。因此,在二进制数据中,bit0通常是指最右边的位,也就是最低位。

将获取的数据转化为二进制;

根据:start_bit 以及 bit_length,从信号的 bit 0 位 开始,取出总长度为 16 的数据串,并且转化为十进制数据值。

4. Signed需要向有符号转化

此处需将无符号转化为有符号数据。

当前使用取反加1法进行转化,(此外还有符号位扩展法,补码表示法)

将十进制值 转化为 二进制 并且 进行取反加1法,再转化为十进制

即-780

5. 根据公式进行计算信号实际值

根据计算公式:即可得出 signal = (-780 * 0.1)+ 0.0 = -78.0

所以接收到 signal 数据值为 -78

Unsigned类型解析:

接收数据包的值为:cc 0c 00 00 00 00 00 00 (此数据包为16进制)

1. 获取信号所需信息

{
    "start_bit": 1,
    "bit_length": 15,
    "factor": 0.01,
    "offset": 0,
}

2. 判断数据是否需进行字节序转换

考虑底层(mcu)是否统一发送格式,统一大端或者小端,请于底层进行确认!!

当前内容需进行大小端转换。

cc 0c 00 00 00 00 00 00 ==》》00 00 00 00 00 00 0c cc

3. 获取信号的十进制值

二进制数据中,通常将最右边的位称为最低位(LSB),最左边的位称为最高位(MSB)。因此,在二进制数据中,bit0通常是指最右边的位,也就是最低位。

将获取的数据转化为二进制;

根据:start_bit 以及 bit_length,从信号的 bit 1位开始,取出总长度为 15 的数据串,并且转化为十进制数据值。

4. Unsigned不需要向有符号,浮点型转化,跳过。

5. 根据公式进行计算信号实际值

根据计算公式:即可得出 signal = (1638 * 0.01)+ 0.0 = 16.38

所以接收到 signal 数据值为16.38

Double、Float类型解析

Double、Float类型解析同Signed类型解析流程。

需要注意流程 4向有符号,浮点型转化,这块步骤有所差异,请注意。

类型转化说明

64756(十进制) 向有符号转化 : 二进制取反加1  ==》-780

64756(十进制) 向有浮点转化 :???待补充

C++编码解析

流程3:

uint64_t jiexi_fun(
  uint16_t startbit, uint8_t length, const uint8_t *rawdata, std::string endian) {
  uint64_t data{0};
  uint8_t lsbbit = startbit % 8;
  uint8_t lsbbyte = startbit / 8;
  if (endian == "Motorola") {
    uint8_t msbbyte = lsbbyte - (lsbbit + length - 1) / 8;
    for (int i{msbbyte}; i < lsbbyte + 1; i++) {
      data+= ((uint64_t)rawdata[i]) << ((lsbbyte - i) * 8);
    }
  } else {
    uint8_t msbbyte = (startbit + length - 1) / 8;
    for (int i{msbbyte}; i >= lsbbyte; i--) {
      data += (((uint64_t)rawdata[i]) << (i - lsbbyte) * 8);
    }
  }
  data= data>> lsbbit;
 
  data <<= (64 - length);
  data >>= (64 - length);
  return data;
}

  • 7
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
解析CAN信号,你需要先读取DBC文件,然后使用Java中的位运算和字节数组来解析CAN数据。以下是一个简单的示例代码,用于解析CAN数据中的一个信号: ```java import com.github.pires.obd.reader.io.CsvLogWriter; import com.github.pires.obd.reader.net.ObdReading; import com.github.pires.obd.reader.net.ObdService; import com.github.pires.obd.reader.net.ObdServiceConnection; import com.github.pires.obd.reader.net.ObdServiceHandler; import com.github.pires.obd.reader.net.ObdServiceReceiver; import com.github.pires.obd.reader.net.ObdServiceStatus; import com.github.pires.obd.reader.net.ObdServiceStatusListener; import com.github.pires.obd.reader.util.Log; import com.google.inject.Inject; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class CanSignalParser { private DbcMessage message; private DbcSignal signal; public CanSignalParser(DbcMessage message, DbcSignal signal) { this.message = message; this.signal = signal; } public double getValue(byte[] data) { int startBit = signal.getStartBit(); int length = signal.getLength(); boolean littleEndian = signal.isLittleEndian(); int byteOffset = startBit / 8; int bitOffset = startBit % 8; if (littleEndian) { byteOffset = message.getLength() - byteOffset - length / 8; bitOffset = 8 - length % 8 - bitOffset; } int value = 0; for (int i = 0; i < length; i++) { int bit = (data[byteOffset] >> (bitOffset + i)) & 1; value |= bit << i; } double factor = signal.getFactor(); double offset = signal.getOffset(); double minValue = signal.getMinValue(); double maxValue = signal.getMaxValue(); double result = value * factor + offset; if (result < minValue) { result = minValue; } if (result > maxValue) { result = maxValue; } return result; } } ``` 这个例子接受一个`DbcMessage`对象和一个`DbcSignal`对象作为参数,并提供一个`getValue()`方法来计算信号的值。该方法接受一个字节数组,表示CAN数据,然后使用位运算和信号的起始位、长度、因子和偏移量来计算信号的实际值。 你可以使用以下代码来调用这个方法: ```java CanSignalParser parser = new CanSignalParser(message, signal); double value = parser.getValue(canData); ``` 其中`message`和`signal`是从DBC文件中读取的`DbcMessage`和`DbcSignal`对象,`canData`是一个字节数组,表示CAN数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值