最近整理了一下工作学习中的代码,对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~

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。