最近整理了一下工作学习中的代码,对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~
相关文章
发表评论