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];
}