CAN通信矩阵
整车CAN通信矩阵通常由OEM制定及发布,其定义了整车CAN网络中每个通信节点信息交互的格式,通信矩阵包含每个信号的具体定义,工程人员通过通信矩阵可以读取及解析整车CAN总线信号。
CAN信号定义
总线信号定义包括: 信号名称、信号长度、起始字节、起始位信号 ,例如查看 EngineDriverRequestTorque信号定义,可知该信号在通信报文中 “第2字节的第3位” 至 “第3字节的第0位”。
Signal Name 信号名称 | Start Byte 起始字节 | Start Bit 起始位 | Bit Length 信号长度 | Factor 比例因子 | Offset 偏移量 | Unit 单位 | Conversion formula 转换公式 |
EngineDriverRequestTorque | 2 | 3 | 12 | 0.25 | -150 | Nm | E=0.25N-150 |
CAN信号提取(传统方法)
根据通信矩阵定义,开发人员通过搭建简单的模型即可提取EngineDriverRequestTorque信号(如下图1所示),但是若信号的定义较另类或者信号数据较多时,则搭建提取信号模型也会变得会费时费力(如下图2所示)。
(图1)
(图2)
CAN信号提取(改进方法)
我们可将提取信号封装为统一的函数/class的形式,后续所有的CAN信号提取均可采用统一的形式,调用提取函数并填入该信号定义即可,如图3所示。
(图3)
高效的提取CAN信号方法说明
1、提取bool类型位信号时:填入位信号所在的起始字节idx_byte、起始位idx_bit即可。
bool getBitSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit)
{
bool log;
uint8 offs;
offs=idx_bit;
log = (*(buf + idx_byte) >> offs) & 1;
return log;
}
2、提取长度小于Byte信号时:填入信号所在的起始字节idx_byte、起始位idx_bit、信号长度len即可。
uint8 getByteSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len)
{
uint8 offs;
uint8 msg;
const uint8 bitmasku8[8] = {0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF};
offs = 8 - (7 - idx_bit) - len; // offs = sizeof(byte)-(bit_7-idx_bit)-len
msg = *(buf + idx_byte);
msg = (msg >> offs) & bitmasku8[len - 1];
return msg;
}
3、同理提取长度小于2Byte信号时:填入信号所在的起始字节idx_byte、起始位idx_bit、信号长度len即可。
uint16 getShortSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len)
{
uint8 offs;
uint16 msg;
const uint16 bitmasku16[16] ={0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
offs = 16 - (7 - idx_bit) - len; // offs = sizeof(short)-(bit_7-idx_bit)-len
msg = *(buf + idx_byte);
msg = (msg >> offs) & bitmasku16[len - 1];
return msg;
}
4、但当提取长度小于2Byte信号且信号位置跨度占据3个Byte时(信号位置如下图所示),则需稍作处理以防止数据的溢出。
而后填入需要位信号所在的起始字节idx_byte、起始位idx_bit以及信号长度len即可。
uint16 getShortSignalExt(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len)
{
uint8 offs;
uint32 msg;
const uint16 bitmasku16[16] ={0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
offs = 32 - (7 - idx_bit) - len; // offs = sizeof(int)-(bit_7-idx_bit)-len
msg = *(buf + idx_byte);
msg = (msg >> offs) & bitmasku16[len - 1];
return (uint16)msg;
}
高效的填充CAN信号方法说明
填充CAN信号 较 提取CAN信号 而言稍复杂一些,既要将数据填充到指定的位置又要确保原数据缓冲区其余位置的值保持不变。
1、填充bool类型位信号时:填入位信号所在的起始字节idx_byte、起始位idx_bit、位信号去值log即可。
void setBitSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,bool log)
{
uint8 offs;
uint8 msg;
uint8 mask;
offs = idx_bit;
mask = (~(1 << offs)); // 构造一个除需填充的数据位之外,其他位都是1的数据
msg = (*(buf + idx_byte)) & mask;
*(buf + idx_byte) = (msg) | (log << offs);
}
2、填充长度小于Byte信号时:填入信号所在的起始字节idx_byte、起始位idx_bit、信号长度len、信号取值val即可。
void setByteSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len,uint8 val)
{
uint8 offs;
uint8 msg;
uint8 mask;
const uint8 bitmasku8[8] = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
offs = 8 - (7 - idx_bit) - len;
mask = bitmasku8[len - 1];
mask = (~(mask << offs)); // 构造一个除需填充的数据位之外,其他位都是1的数据
msg = (*(buf + idx_byte)) & mask;
*(buf + idx_byte) = (msg) | ((val << offs)&(~mask));
}
3、同理填充长度小于2Byte信号时:填入信号所在的起始字节idx_byte、起始位idx_bit、信号长度len、信号取值val即可
void setShortSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len,uint16 val)
{
uint8 offs;
uint16 msg;
uint16 mask;
const uint16 bitmasku16[16] = {0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,
0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
offs = 8 - (7 - idx_bit) - len;
mask = bitmasku16[len - 1];
mask = (~(mask << offs)); // 构造一个除需填充的数据位之外,其他位都是1的数据
msg = (*(buf + idx_byte)) & mask;
*(buf + idx_byte) = (msg) | ((val << offs)&(~mask));
}
总结:
上述代码虽已在Visual C++上调试通过,但该代码更多的是提供思路借鉴,若需要在正式场合使用时,建议增加断言进行边界检查;并且很多的编译器并不支持指针偏移+强制转换,因此也建议可以通过数组移位+拼接的方式实现。如
uint16 getShortSignal(uint8* buf,uint8 idx_byte,uint8 idx_bit,uint8 len)
{
uint8 offs;
uint16 msg=0;
#ifndef RELEASE_VERSION // 增加断言
assert(idx_byte > MAX_INDEX_SIGNAL);
assert(idx_bit > MAX_INDEX_SIGNAL);
assert(len > MAX_INDEX_LENGTH);
#endif
offs = 16 - (7 - idx_bit) - len;
msg = ((uint16)buf[idx_byte]) << 8;
msg += buf[idx_byte + 1];
msg = (msg >> offs) & bitmasku16[len - 1];
}