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

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

文章目录

  • CAN的大概说明
  • 一、数据按位打包
  • 二、代码展示
    • 1.按位发送
    • 2.按位接收
  • 总结


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~

你可能感兴趣的:(网络,网络协议,stm32)