因CAN报文存在motorola和Intel不同字序,整型、浮点型,有无符号等不同设计,打包解包的代码非常容易出错,而且很难检查验证。
在项目开发中,已形成实用的通用函数,现分享如下(public和CanProc两模块)。
public.h
#ifndef __PUBLIC__H_
#define __PUBLIC__H_
#if 1
typedef signed char sint8;
typedef unsigned char uint8;
typedef signed short sint16;
typedef unsigned short uint16;
//typedef signed long sint32; //32位系统和64位系统,long字节不同,32位系统=4字书,
//64位系统=8字节
//typedef unsigned long uint32; //不同平台,int字节不同
typedef signed int sint32; //当前因simulink生成的代码,int是4字节,故保持一致
typedef unsigned int uint32;
typedef unsigned long long uint64;
typedef signed long long sint64;
typedef float float32;
typedef double float64;
#endif
//获得U32的n位的值
#define GetBitNValue(dword, n, offset) (((uint32)(dword) >> (offset)) &
((1UL << (n)) - 1))
#define GetNBitValueOfU64(ldword, n, byOffset) (((uint64)(ldword) >> (byOffset)) &
(((uint64)1 << (n)) - 1))
#define TestBit(b,offset) (1U & ((b)>>(offset))) // 检查状态字中的某一位是否置位
#define SetBit(b,offset) ((b) |= (1U<<(offset))) // 置位状态字中的某一位
#define ResetBit(b,offset) ((b) &= (~(1U<<(offset)))) // 复位状态字中的某一位
#define ArrCountOf(a) (sizeof(a) / sizeof(a[0]))
extern void SetBitOfU32Value(uint32 *pdwData, uint8 n, uint8 byOffset, uint32 dwValue);
extern void SetBitOfU8Value(uint8 *pbyData, uint8 n, uint8 byOffset, uint8 byValue);
extern void SetNBitValueOfU64(uint64 *pldwData, uint8 n, uint8 byOffset, uint64 ldwValue);
#endif
public.c
#include "public.h"
extern void SetBitOfU32Value(uint32 *pdwData, uint8 n, uint8 byOffset, uint32 dwValue)
{
uint32 tmp = *pdwData;
uint32 mask;
mask = (((uint32)1u << n) - 1) << byOffset;
tmp &= ~mask; //先清0
tmp |= (dwValue << byOffset) & mask; //值也需要保证没有更多的位
*pdwData = tmp;
}
extern void SetBitOfU8Value(uint8 *pbyData, uint8 n, uint8 byOffset, uint8 byValue)
{
uint8 tmp = *pbyData;
uint8 mask;
mask = (((uint8)1u << n) - 1) << byOffset;
tmp &= ~mask; //先清0
tmp |= (byValue << byOffset) & mask; //值也需要保证没有更多的位
*pbyData = tmp;
}
extern void SetNBitValueOfU64(uint64 *pldwData, uint8 n, uint8 byOffset, uint64 ldwValue)
{
uint64 tmp;
uint64 mask;
if (pldwData == NULL)
{
return;
}
mask = (((uint64)1 << n) - 1) << byOffset;
tmp = *pldwData;
tmp &= ~mask; //先清0
tmp |= (ldwValue << byOffset) & mask; //值也需要保证没有更多的位;
*pldwData = tmp;
}
CanProc,h
#ifndef _CAN_PROC_H_
#define _CAN_PROC_H_
//#include "public.h" //某些情况下需要
#define GetCanSigMsg(littleEndian, isSigned, ValueType) ((((littleEndian)&0xFFUL) << 16) |
(((isSigned)&0xFFUL) << 8) |
((ValueType)&0xFFUL))
extern void CanPack(float32 value, uint8 *data, uint8 startBit, uint8 BitLength,
uint32 ValueMsg, float32 factor, float32 offset);
extern float32 CanUnpack(const uint8 *data, uint8 startBit, uint8 BitLength,
uint32 ValueMsg, float32 factor, float32 offset);
#endif
CanProc.c
#include "CanProc.h"
#include "public.h"
extern void CanPack(float32 value, uint8 *data, uint8 startBit, uint8 BitLength,
uint32 ValueMsg, float32 factor, float32 offset)
{
float32 tmp;
sint32 BinaryValue = 0;
sint8 BytePos; // 当前操作的data[BytePos]
sint8 BitOffset; //在data[BytePos]中的起始bit
sint8 ValuePos; //当前处理的在BinaryValue的bit位置
sint8 DealLen; //处理的数据位数
sint8 RemainLen; //剩余未处理的数据位数
uint8 byValue;
if (data == NULL)
{
return;
}
tmp = (value - offset) / factor;
// 获得数据的二进制码
switch(ValueMsg & 0xFF)
{
case 0: //interge
{
if (!((ValueMsg>>8) & 0xFF)) //无符号型
{
if (tmp < 0)
{
tmp = 0;
}
}
BinaryValue = (sint32)tmp;
}
break;
case 1: //float
{
if (BitLength != 32)
{
BinaryValue = 0;
}
else
{
BinaryValue = *(sint32*)&tmp;
}
}
break;
case 2: //double //
default:
{
BinaryValue = 0;
}
break;
}
//buff填充 从signal的低字节处理,逐字节处理
BytePos = startBit >> 3; //除8
RemainLen = BitLength;
BitOffset = startBit - (BytePos<<3);
ValuePos = 0;
if ((ValueMsg>>16) & 0xFF) //Intel little endian
{
do
{
DealLen = (RemainLen < 8-BitOffset) ? RemainLen : (8-BitOffset);
byValue = (uint8)(BinaryValue >> ValuePos);
SetBitOfU8Value(data + BytePos, DealLen, BitOffset, byValue);
BytePos++;
ValuePos += DealLen;
RemainLen -= DealLen;
BitOffset = 0;
}while (RemainLen);
}
else //motorola / big endian mode
{
do
{
DealLen = (RemainLen < 8-BitOffset) ? RemainLen : (8-BitOffset);
byValue = (uint8)(BinaryValue >> ValuePos);
SetBitOfU8Value(data + BytePos, DealLen, BitOffset, byValue);
BytePos--;
ValuePos += DealLen;
RemainLen -= DealLen;
BitOffset = 0;
}while (RemainLen);
}
}
extern float32 CanUnpack(const uint8 *data, uint8 startBit, uint8 BitLength,
uint32 ValueMsg, float32 factor, float32 offset)
{
float32 rt = 0;
sint32 tmp;
sint32 BinaryValue = 0;
sint32 mask;
uint32 signedMask;
sint8 BytePos; // 当前操作的data[BytePos]
sint8 ValuePos; //当前处理的在BinaryValue的bit位置
sint8 BitOffset; //在data[BytePos]中的起始bit
sint8 DealLen; //处理的数据位数
sint8 RemainLen; //剩余未处理的数据位数
if (data == NULL)
{
return 0;
}
// 从signal的低字节处理,逐字节处理
BytePos = startBit >> 3; //除8
RemainLen = BitLength;
BitOffset = startBit - (BytePos<<3);
ValuePos = 0;
if ((ValueMsg>>16) & 0xFF) //Intel little endian
{
do
{
DealLen = (RemainLen < 8-BitOffset) ? RemainLen : (8-BitOffset);
tmp = GetBitNValue(data[BytePos], DealLen, BitOffset);
BinaryValue += (tmp << ValuePos);
BytePos++;
RemainLen -= DealLen;
BitOffset = 0;
ValuePos += DealLen;
}while (RemainLen);
}
else //motorola / big endian mode
{
do
{
DealLen = (RemainLen < 8-BitOffset) ? RemainLen : (8-BitOffset);
tmp = GetBitNValue(data[BytePos], DealLen, BitOffset);
BinaryValue += (tmp << ValuePos);
BytePos--;
RemainLen -= DealLen;
BitOffset = 0;
ValuePos += DealLen;
}while (RemainLen);
}
if ((ValueMsg>>8) & 0xFF)
{
mask = (1UL << (BitLength - 1));
if ((BinaryValue & mask) == mask)
{
signedMask = ~((1UL << BitLength) - 1);
BinaryValue |= (-1L & signedMask); //高位填充,扩展为32位/64位
}
}
switch(ValueMsg & 0xFF)
{
case 0: //interge
{
rt = (float32)BinaryValue * factor + offset;
}
break;
case 1: //float
{
if (BitLength != 32)
{
rt = 0;
}
else
{
rt = *(float32*)(&BinaryValue) * factor + offset;
}
}
break;
case 2: //double //嵌入式不支持
default:
{
rt = 0;
}
break;
}
return rt;
}
说明:CanProc.h为使用接口,已在实际项目中测试使用。
根据实测,使用英飞凌TC234,100Mhz主频,其发送一帧20信号的8字节扩展帧的处理时间约为41us,接收处理一帧11信号的8字节扩展帧处理时间约为10us;可以说控制器对收发的处理速度非常优秀的。
以下是使用样例
另外,我们在实际中,运用这两个接口,用simulink实现了很好的自动代码生成。



