# 前言

为什么写了这个东西,导师画饼。能不能用,暂时不知道,反正我写了,用到之时自会有用,这里只记录思路。

# 算法步骤

  1. 从制定好的 excel 表中读取数据 Data .
  2. Data 进行数据预处理得到自己想要的数据结构 T .
  3. 根据 T 去解析一个 DID 的 DCM 函数定义.
  4. 根据 T 初始化一个 DID 对应的所有信号和存储大小.
  5. 根据 T 中的 DID 的功能解析信号填充.
  6. 生成注释和 C 函数及信号填充内容并写入 C 文件.
  7. 持续第 3~6 步直至 T 中 DID 全部被解析完并生成相应内容填充至 C 文件.

# DataT

Data 是 excel 表中你自己定义的数据,里面可以包含一个 DID 标识符、DID 功能、对应的所有信号名和信号长度和信号位置,你可以在 excel 表中自己定义想要被读取的数据。
T 则是你从 excel 表导入的数据预处理,毕竟一个 DID 可以包含多个信号,你需要构建成一个 DID 包含多个信号及对应信号存储信息的数据结构,方便你后续进行 DCM 函数的自动生成,你可以自己定义想要存的数据。

# DCM 函数定义

我不清楚其它对于 DCM 函数名是怎么定义的,起码在我这里 DCM 函数名不是标准的 C 语言的函数,而是 vector 对 C 语言封装后的函数声明,所以你需要对标准 C 函数的返回值,参数值进行转换适合 vector 函数的规范。

# 信号填充

这是最重要的步骤,可能会很长

# 信号长度和位置解析

假设一个 DBC 信号展示如图,同时你只有一个 DBC 信号的长度和位置,你怎么确定这个信号的 MSB、LSB 坐标以及是否跨字节?

DBC信号示意图

只有知道了信号在 DBC 中的 MSB 和 LSB 位置,才能进行信号填充中的移位填充操作,即所谓的 CAN 报文通信会话层的拆包与组包。
MSB 坐标可以有位置值除 8 和取余 8 求得,剩下的就是 LSB 坐标。
对于信号是否在一个字节内的,只需判断 MSB 是否大于信号长度即可 (#1),在不满足 #1 的条件下,判断信号长度是否大于等于 10 或者信号长度减去 MSB 所在的字节长度的值是否大于 8,否则跨两个字节,是则跨两个及以上字节。
以下是计算 MSB 和 LSB 坐标的伪代码

MSB byte index = poistion / 8
MSB bit index = poistion % 8 
// one byte
if MSB bit index + 1 >= Length
LSB byte index = MSB byte index
LSB bit index = MSB bit index - (Length - 1)
// two bytes
else if Length - (MSB bit index + 1) > 8 or Length >= 10
LSB byte index = MSB byte index + 1
LSB bit index = 8 - (Length - (MSB bit index + 1))
// three bytes and more
else
subLength(no msb byte index) = Length - (MSB bit index + 1)
last row = (subLength / 8) + 1
number in LSB byte index = subLength % 8
LSB byte index = MSB byte index + last row
LSB bit index = 8 - number in LSB byte index

# 移位填充操作

移位填充遵从的 Motorola 格式,即计算机科学里面所谓的大端模式,高位数据存低位,低位数据存高位。

注:这里说的移位填充操作指的是 DID 中的读数据功能,即把信号读进 DBC 信号矩阵中,还有一种是 DID 的写数据功能,即把 DBC 信号矩阵写进信号中,方式和读数据功能相反,但写法类似,不过数据类型转换不再是 uint8,而是根据每个信号类型不同进行进行强制类型转换 (uint8、uint16、uint32、Ascll、Hex)。

# 两种移位填充方式

将信号按 Motorola 格式填充至 DBC 中一共有两种方式,分别是

  1. 先清除信号不必要的 bit 位,再移位至合适的位置填充至 DBC ($1)
Data[x] |= (uint8)( (singal & mask) << shift)
  1. 先再移位至合适的位置,再清除信号不必要的 bit 位填充至 DBC ($2)
Data[x] |= (uint8)( (singal >> shift) & mask)

上述两种方式之后我会统一用 $1 和 $2 表达。

移位填充可分为三种情况,信号在一个字节内、两个字节内,三个字节及以上的。

# 信号在一个字节内

信号在一个字节内很简单,只需要执行 $1 即可。

Data[Start_Byte] |= (uint8)( (singal & (8 - Start_Bit)) << Start_Bit)

注:这里的 Start_xx,End_xx 指的是 MSB 和 LSB 的坐标位置。

# 信号在两个字节内

两个字节的则需要先执行 $1 把信号高位数据移至 DBC 低位,再执行 $2 把信号低位数据移至 DBC 高位。

len1 = (8 - Start_Bit)
Data[Start_Byte] |= (uint8)( (singal & len1 ) << Start_Bit)
Data[End_Byte] |= (uint8)( (singal >> len1 ) & (len - len1))

# 信号在三个字节及以上的

三个字节其实信号在两个字节的特化版,首先看 DBC 最低位,先执行 $1, 之后全部执行 $2,但是由于除了最高位可能不是满字节的,所以 DBC 最高位的掩码不是 8,其余全是 8,需要注意的是,从第二步开始的每一次移位都是很上一次信号对应位置移位的累加,假设有四次移位操作,除第一次和第四次不是满字节,其余都是满字节,则有这样的可能,3,11 (8+3),19 (8+8+3),23 (4+8+8+3)。

len1 = (8 - Start_Bit)
Data[Start_Byte] |= (uint8)( (singal & len1 ) << Start_Bit)
Data[Start_Byte - 1 ] |= (uint8)( (singal >> len1 ) & 0xFF)
len1 = len1 + 8
Data[Start_Byte - 2 ] |= (uint8)( (singal >> len1 ) & 0xFF)
....
len1 = len1 + 8
Data[Start_Byte - n ] |= (uint8)( (singal >> len1 ) & 0xFF)
Data[End_Byte] |= (uint8)( (singal >> len1 ) & (len - len1))

# 信号类型为 ASCII 或 HEX

ASCII 和 HEX 实际是一个 uint8 的数组,所以全程执行 $1 或 $2 都没问题,因为实际信号都完整填进 DBC 矩阵里了,这里以 $2 为例。

Data[Start_Byte] |= (uint8)( (singal[0] >> 0 ) << & 0xFF)
Data[Start_Byte - 1 ] |= (uint8)( (singal[1] >> 0 ) & 0xFF)
Data[Start_Byte - 2 ] |= (uint8)( (singal[2] >> 0 ) & 0xFF)
....
Data[Start_Byte - n ] |= (uint8)( (singal[n-1] >> 0 ) & 0xFF)
Data[End_Byte] |= (uint8)( (singal[n] >> 0 ) & 0xFF)
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Jelly27th 微信支付

微信支付

Jelly27th 支付宝

支付宝