CAN数据发送和接收的按位处理

最近整理了一下工作学习中的代码,对Can的收发数据有了不小的收获,其中让我记忆犹新的就是数据按位打包发送的实现方法,遂再次进行记录。


CAN的大概说明

CAN的全程为controller area network(可能吧,纯靠个人记忆手打),一种局域数据传输的总线式工具,通常数据传输距离为40M以内,数据传输波特率为1mbps(bit per second),他的计算方法与CAN的位同步机制有关,即把一个bit分成若干个TQ,配合分频进行,此处不多说了哈。


以下是本篇文章正文内容,好好看!

一、数据按位打包

通常的CAN总线,依靠其时序协议进行报文的传输,发送各种各样的帧frame。通常的发送需要的是11位标准帧ID、1位数据帧、和64位(8个字节)等的报文。我们可以直接发送8个字节的数据。对方接收到信息数据进行处理的时候也很方便。但是直接发送8个8bit数据太占用资源,一次只能最多发送8个数据,影响发送的速度和发送的数据类型。
所以数据按位打包是非常有必要的,我可以将各种位数的数据进行组合,只要最后的位数不超过64位即可,比如,我发送uint8_t test这个8位数据的前2位,这样我的剩余发送位数还剩62位。(但是要记住,CAN只能发送无符号的数据)

二、代码展示

1.按位发送

代码如下:注释信息也很清楚

int8_t msg_can_pack_bit(can_msg_descr_t* p_msg_descr, can_basic_frame_t* p_frame)  //将数据打包,各个数据的多少位分别进行填充,组成一个多位的整体,通过can进行8字节以内的发送。
{
	uint8_t i = 0;
	uint8_t bit_position = 0;  //bit的位置
	
	p_frame->id = p_msg_descr->id;  //ID解包出来  decode
	
	p_frame->data[0] = 0;    //64bit  data  清零
	p_frame->data[1] = 0;
	
	for(; i < p_msg_descr->num_fields; i++)  //区域数量
	{
		signed char bit_size = p_msg_descr->p_field[i].field_size;//第i区域的区域size,8-8-30bit,假设3个数据
		uint32_t item;  //项目
		if(bit_size < 0) 
        {
			bit_size = -bit_size;   //负数则反相
		}
		
		if(bit_size <= 8) 
        {
			item = *((uint8_t *)p_msg_descr->p_field[i].p_field_val);  //若区域大小小于8bit,小于八位的数,给item
		} 
        else if(bit_size <= 16) 
        {
			item = *((uint16_t *)p_msg_descr->p_field[i].p_field_val); //若区域大小小于16bit,小于16位的数,给item
		} 
        else if(bit_size <= 32) 
        {
			item = *((uint32_t *)p_msg_descr->p_field[i].p_field_val); //若区域大小小于32bit,小于32位的数,给item
		} 
        else 
        {
			return -1;
		}
		 	
		if(bit_position + bit_size > 64) 
        {
			return -1;
		}
		
		item &= (1 << bit_size) - 1;   //前bitsize-1位全是1,与了一下以保证前bitsize位固定数据
        
		if(bit_position < 32)   //还在data0的前32位数据里面
        {
			p_frame->data[0] |= (item << bit_position);  //左移bitpositon个位的位置,到达下一个放置bitdata的区域0-7 -8-15
			if(bit_position + bit_size > 32)   //46>32  给了16位,还剩下14位,但是从16位开始停止打包。
				p_frame->data[1] = (item >> (32 - bit_position));  //右移删除前面的32-16=16位,从这开始打包剩下的30-16=14位。作为data1的新14位
		} 
		else 
		{
			p_frame->data[1] |= (item << (bit_position - 32));
		}
		bit_position += bit_size;  //定位到上次打包结束的位置+1  ,8、16、46
	}
	
	p_frame->byte_size = bit_position >> 3;   //5字节,40位
	if((p_frame->byte_size < 8) && (bit_position & 0x07)) 
    {
		p_frame->byte_size++;  //5++=6
	}
	return 0;
}

2.按位接收

代码如下:接收的注释也很清楚

int8_t msg_can_unpack_bit(can_msg_descr_t* p_msg_descr, can_basic_frame_t* p_frame)
{
	uint8_t i = 0;
	uint8_t bit_position = 0;
	uint32_t item;
    uint32_t sign_mask;
	
	p_msg_descr->id = p_frame->id;

	for(; i < p_msg_descr->num_fields; i++)
	{
		int8_t bit_size = p_msg_descr->p_field[i].field_size;  //8bit
		uint32_t val = 0;
		uint32_t mask = 0;
		uint8_t pos_flag = 1;
		
		if(bit_size < 0) 
        {
			bit_size = -bit_size;
			pos_flag = 0;
		}
		
		if(bit_position + bit_size > 64)
        {
			return -1;
		}
		
		mask = (1 << bit_size) - 1;  //前7位都是1
		
		if(bit_position < 32)
		{
			val = (p_frame->data[0] >> bit_position) & mask;  //前8位取出
			if(bit_position + bit_size > 32) //起到衔接的作用
			{
				unsigned mask2 = (1 << (bit_position + bit_size - 32)) - 1;
				val |= (p_frame->data[1] & mask2) << (32 - bit_position);
			}
		}
		else if(bit_position >= 32)
		{
			val = (p_frame->data[1] >> (bit_position - 32)) & mask;
		}
		
		sign_mask = 1U << (bit_size - 1);//1左移7位
		val = val & ((1U << bit_size) - 1);//前7位取出

		item = (val ^ sign_mask) - sign_mask; //进一步处理
		
		if(pos_flag) 
        {
			item &= mask;
		}		
		
		if(bit_size <= 8) 
        {
			*((uint8_t *)p_msg_descr->p_field[i].p_field_val) = item;
		} 
        else if(bit_size <= 16) 
        {
			*((uint16_t *)p_msg_descr->p_field[i].p_field_val) = item;
		} 
        else if(bit_size <= 32) 
        {
			*((uint32_t *)p_msg_descr->p_field[i].p_field_val) = item;
		} 
        else 
        {
			return -1;
		}
		
		bit_position += bit_size;
	}	
	return 0;
}

要记住,接收和发送不会自己判断这个数据是多少位的,你需要提前把需要接收的位数计算好,在解析的时候把数据需要接收的位数提前定义好。
如下是定义的结构体类型:

typedef struct 
{
	void * p_field_val;  //单个区域数值val
	int8_t field_size;	//单个区域的size
}can_field_t;

typedef struct 
{
	uint16_t id;    	//解包id
	uint8_t num_fields;	//几个区域
	can_field_t* p_field;  //区域的内容
}can_msg_descr_t;

总结

以上就是今天要讲的内容,本文仅仅简单介绍了按位打包数据的使用,除了CAN总线报文的发送和接收,其他地方也可以使用,一定要活学活用,希望对你有帮助。bye~

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在LabVIEW中,CAN通信发送数据正常但无法接收数据可能有以下几个可能的原因: 1. 硬件连接问题:首先,确保CAN硬件与计算机正确连接。检查硬件设备、电缆和连接接口是否正常。确保CAN适配器的供电正常,也可以尝试更换另一个适配器进行测试。 2. 软件配置问题:LabVIEW的CAN通信模块通常需要进行配置才能正确接收CAN数据。确保已正确设置接口类型、波特率和其他相关参数。同时,确保已正确配置接收缓冲区,以便能够接收到期望的数据。 3. 错误的ID或数据格式:在CAN通信中,每个消息都有唯一的标识符(ID),用于区分不同的消息。在发送接收数据时,确保ID的设置正确。另外,确保发送接收数据格式(例如数据长度、格式等)一致。 4. 信号干扰和噪声:在CAN通信中,信号干扰和噪声可能导致数据传输错误或丢失。确保CAN通信线路与其他高电磁干扰设备(如电机、高频设备等)隔离。同时,使用屏蔽电缆和滤波器可以减少信号干扰。 5. 软件逻辑错误:最后,检查LabVIEW程序的逻辑是否正确。确保在数据接收部分没有逻辑错误或数据处理问题。可以利用LabVIEW的调试工具,例如数据监视器和调试器,来检查数据的流动和处理过程。 综上所述,通过检查硬件连接,正确配置软件,确保正确的ID和数据格式,减少信号干扰,以及排除软件逻辑错误,可以解决LabVIEW中CAN通信发送数据正常无法接收数据的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cap reagan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值