ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CAN打包解包公用函数

2021-12-15 20:33:02  阅读:269  来源: 互联网

标签:BitOffset typedef BytePos 公用 解包 RemainLen data 打包 BinaryValue


因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))  
/*
************************************************************************
* Description : 获得64位数值中的N位的值
* Parameters  : ldword,64位数
				n,想获取的位数
				byOffset,偏移值
* Returns	  : 想要获得的结果
* Notes 	  : n和byOffset均不能超过63
************************************************************************
*/
#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"

/*
************************************************************************
* Description : 设置32位值中的N位的值
* Parameters  : pdwData,需要改的值指针
                n,修改的位数
                byOffset,偏移
                dwValue,设置值
* Returns     : none
* Notes       : 
************************************************************************
*/
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;
}

/*
************************************************************************
* Description : 设置8位值中的N位的值
* Parameters  : pbyData,需要改的值指针
                n,修改的位数
                byOffset,偏移
                byValue,设置值
* Returns     : none
* Notes       : 
************************************************************************
*/
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;
}

/*
************************************************************************
* Description : 设置64位值中的N位的值
* Parameters  : pldwData,需要设置的值的指针
                n,修改的位数
                byOffset,偏移值
                ldwValue,设置值
* Returns     : none
* Notes       : n和byOffset均不能超过63
************************************************************************
*/
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"  //某些情况下需要

/*
************************************************************************
* Description : CAN打包解包的字节序和数据类型整合
* Parameters  : (都是DBC相关的信息)
                littleEndian,大小端 0-大端motorola 1-小端intel
                isSigned,有无符号 unsigned - 0 signed- 1 float - 1 double - 1
                ValueType,数据类型 Integer(unsigned/signed) - 0, float - 1, double - 2
* Returns     : uint32, 集合
* Notes       : 
************************************************************************
*/
#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"

/*
************************************************************************
* Description : CAN报文打包 发送的前置
* Parameters  : value, 值
                data, CAN发送报文数据buf首地址
                ValueMsg,字节序和数据类型等信息的一个集合 见GetCanSigMsg()
                isSigned,有无符号 unsigned - 0 signed- 1 Single - 1 double - 1
                ValueType,数据类型 Integer(unsigned/signed) - 0, Single - 1, double - 2
                factor,分辨率
                offset,偏移量
                
* Returns     : 
* Notes       : 1)只支持8字节数据 有符号/无符号(整型)、浮点型;大小端
                另外 嵌入式中不支持double,将其处理成float,若移植为
                其他计算机用途,注意修改
                2)因运行效率问题,取消使用sint64(BinaryValue、mask、signedMask)    
                改为sint32,若有需要使用double,则需要改回
                //另外 负浮点型直接强制转化为unsigned,是未定义的行为
************************************************************************
*/
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:
        {
            /* //double的预留代码   
            if (BitLength != 64)
            {
                BinaryValue = 0;
            }
            else
            {
                float64 dtmp = tmp;
                BinaryValue = *(sint64*)&dtmp;
            }
            */
            
            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);
    }
}

/*
************************************************************************
* Description : CAN报文解包
* Parameters  : data, CAN报文数据buf首地址
                startBit,起始位
                BitLength,位长度
                ValueMsg,字节序和数据类型等信息的一个集合 见GetCanSigMsg()
                factor,分辨率
                offset,偏移量
                
* Returns     : 解析结果
* Notes       : 1)只支持8字节数据 有符号/无符号(整型)、浮点型;大小端
                另外 嵌入式中不支持double,将其处理成float,若移植为
                其他计算机用途,注意修改
                此函数直接根据dbc做处理 
                2)因运行效率问题,取消使用sint64(BinaryValue、mask、signedMask)    
                改为sint32,若有需要使用double,则需要改回
************************************************************************
*/
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:
        {
            /* //double的预留代码  
            if (BitLength != 64)
            {
                rt = 0;
            }
            else
            {
                rt = (float32)(*(float64*)(&BinaryValue) * factor + offset);
            }
            */

            rt = 0;
        }
        break;
    }
    
    return rt;
}

说明:CanProc.h为使用接口,已在实际项目中测试使用。

根据实测,使用英飞凌TC234,100Mhz主频,其发送一帧20信号的8字节扩展帧的处理时间约为41us,接收处理一帧11信号的8字节扩展帧处理时间约为10us;可以说控制器对收发的处理速度非常优秀的。

以下是使用样例

另外,我们在实际中,运用这两个接口,用simulink实现了很好的自动代码生成。 

标签:BitOffset,typedef,BytePos,公用,解包,RemainLen,data,打包,BinaryValue
来源: https://blog.csdn.net/amwitf/article/details/121960073

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有