Can2.0自由报文通讯~~基于小米微电机控制讲解

1.接线&信号电压及其逻辑规范

CAN_H&CAN_L是CAN的通讯线,基于差分信号组成,以确保数据的准确性。其中通讯的电缆线最好双绞。

CAN_H&CAN_L的低电平时逻辑1,高电平时逻辑0,该信号标准和串口有所区别。

电压差为2V时,显性电平,表示逻辑0

电压差为0V时,隐性电平,表示逻辑1

差分信号的好处:

数据在传输过程中,如果受到外界强影响,该影响同时作用在CAN_H&CAN_L两条线上,那么电压差不变。所以为了保证电缆中的信号同时受到影响,还需要让CAN_H&CAN_L两线双绞传输。

2.通讯数据帧

2.1通讯帧格式

起始位:1位bit

默认无通讯时,通讯逻辑位是1,当设备需要进行CAN通讯时,起始位被数据被置零

识别码:11位bit/29位bit

对应11位标准帧格式和29位扩展帧格式。识别码中包含设备优先级。

RTR位:1位bit

RTR=1时,该数据帧为数据类型帧

RTR=0时,该数据针为远程请求帧

控制码:6位bit

bit(0),IDE位用来区分标准帧格式和扩展帧格式,bit(0)=0 对应标准帧格式,bit(0)=1 对应扩展帧格式

bit(1),空闲位,目前固定逻辑0.

bit(2)~~bit(5),DLC位,即Date Link Control,二进制编码对应者0~~8,即后面跟着的据码长度,该长度最大8个字节。bit(2)~~bit(5)=8时,数据码长度就是8个字节长度,8*8=64bit。

CRC码:16位bit

这是为了确保数据准确性设置的

bit(0)~~bit(14)区域是CRC校验码,设备接收端根据数据计算得出CRC位,计算的CRC和接收的CRC不一致,说明数据存在问题,需要重新发送数据帧

bit(15) CRC界定符,固定逻辑1.目的是把CRC前的数据和CRC后面的数据分隔开。

ACK码:2位bit

bit(0) ACK确认槽,发送端发送的是逻辑1,接收端发送0作为应答。

bit(1) ACK界定位,固定逻辑1.隔开后方数据。

结束位:7位

bit(0)~~bit(6)数据全部为   1   ,表示数据帧传输结束。

2.2标准帧结构

2.3扩展帧结构

2.4数据帧汇总

该数据帧中核心的数据分成两大类,一类是硬件CAN接口自动处理部分数据,另一类是用户自定义数据

自动处理数据:

  1. 起始位
  2. CRC码
  3. ACK码
  4. 结束位

用户自定义数据:

  1. 识别码
  2. RTR位
  3. 控制码

其中CAN的报文中自动处理数据因为受CAN的接口控制,不需要调整。用户自定义数据则无需严格按照标准去做。

自定义的格式可以依据业务需求进行修改。

3.用户自定义数据

自定义数据类型以小米微电机为例

3.1小米微电机基础报文格式

数据域29位ID29位ID29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述通信类型数据区2目标地址数据区1

摘抄自手册

3.2通讯协议类型说明


3.2.1获取设备ID(通信类型0)

获取设备ID和64位MCU唯一标识符

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述0

bit15~8:标识主机

CAN_ID

目标电机

CAN_ID

0

应答帧:

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述0

目标电机

CAN_ID

0xFE

64位MCU标识

3.2.2运动模式电机控制指令(通信类型1)

用来向电机发送控制指令

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述1Byte2:力矩(0~65535)
对应(-12Nm~12Nm)

目标电机

CAN_ID

Byte0~1: 目标角度[0~65535]对应(-4π~4π)
Byte2~3: 目标角速度[0~65535]对应(-30rad/s~30rad/s)
Byte4~5:Kp [0~65535]对应(0.0~500.0)
Byte6~7:Kd [0~65535]对应(0.0~5.0)

应答帧:应答电机反馈帧(见通信类型2)

3.2.3电机反馈数据(通信类型2)

用来向主机反馈点击运行状态

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述2Bit8~Bit15:当前电机CAN ID
bit21~16:故障信息(0无 1有)
bit21: 未标定
bit20: HALL编码故障
bit19: 磁编码故障
bit18: 过温
bit17: 过流
bit16: 欠压故障
bit22~23:模式状态
0 : Reset模式[复位]
1 : Cali 模式[标定]
2 : Motor模式[运行]
主机CAN _ID
Byte0
Byte0~1: 当前角度
[0~65535]对应(-4π~4π)
Byte2~3: 当前角速度
[0~65535]对应(-30rad/s~30rad/s)
Byte4~5:当前力矩
[0~65535]对应(-12Nm~12Nm)
Byte6~7:当前温度:Temp(摄氏度)*10

3.2.4电机使能运行 (通信类型3)

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述3bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

应答帧:应答电机反馈帧(见通信类型2)

3.2.5电机停止运行 (通信类型4)

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述4bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

正常运行时,data区需清0;
Byte[0]=1时:清故障;

应答帧:应答电机反馈帧(见通信类型2)

3.2.6设置电机机械零位(通信类型6)

会把当前电机位置设为机械零位(掉电丢失)

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述6bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

Byte[0]=1

应答帧:应答电机反馈帧(见通信类型2)

3.2.7设置电机CAN_ID(通信类型7)

更改当前电机CAN_ID , 立即生效。

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述7bit15~8:用来标识主CAN_ID
Bit16~23: 预设置CAN_ID

目标电机

CAN_ID

应答帧:应答电机反馈帧(见通信类型2)

3.2.8单个参数读取(通信类型17)

更改当前电机CAN_ID , 立即生效。

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述17bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

Byte0~1: index,参数列表详见4.1.11
Byte2~3: 00
Byte4~7: 00

应答帧:

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述17bit15~8:目标电机CAN_ID主机CAN_IDByte0~1: index,参数列表详见4.1.11
Byte2~3: 00
Byte4~7: 参数数据,1字节数据在Byte4

3.2.9单个参数写入(通信类型18) (掉电丢失)

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述18bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

Byte0~1: index,参数列表详见4.1.11
Byte2~3: 00
Byte4~7: 参数数据

应答帧:应答电机反馈帧(见通信类型2)

3.2.10故障反馈帧(通信类型21)

数据域29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述21bit15~8:用来标识主CAN_ID

目标电机

CAN_ID

Byte0~3: fault值(非0:有故障,0:正常)
bit16:A相电流采样过流
bit15~bit8:过载故障
bit7:编码器未标定
bit5:C相电流采样过流
bit4:B相电流采样过流
bit3:过压故障
bit2:欠压故障
bit1:驱动芯片故障
bit0:电机过温故障,默认80度
Byte4~7: warning值
bit0:电机过温预警,默认75度

应答帧:应答电机反馈帧(见通信类型2)

4.通讯代码实现

4.1数据定义

#define P_MIN -12.5f
#define P_MAX 12.5f
#define V_MIN -30.0f
#define V_MAX 30.0f
#define KP_MIN 0.0f
#define KP_MAX 500.0f
#define KD_MIN 0.0f
#define KD_MAX 5.0f
#define T_MIN -12.0f
#define T_MAX 12.0f

struct exCanIdInfo{
uint32_t id:8;
uint32_t data:16;
uint32_t mode:5;
uint32_t res:3;
};

can_receive_message_struct rxMsg;

can_trasnmit_message_struct txMsg={
.tx_sfid = 0,
.tx_efid = 0xff,
.tx_ft = CAN_FT_DATA,
.tx_ff = CAN_FF_EXTENDED,
.tx_dlen = 8,
};

#define txCanIdEx (((struct exCanIdInfo)&(txMsg.tx_efid)))
#define rxCanIdEx (((struct exCanIdInfo)&(rxMsg.rx_efid))) //将扩展帧id解析为自定义数据结构
int float_to_uint(float x, float x_min, float x_max, int bits){
float span = x_max - x_min;
float offset = x_min;
if(x > x_max) x=x_max;
else if(x < x_min) x= x_min;
return (int) ((x-offset)*((float)((1<<bits)-1))/span);
}
#define can_txd() can_message_transmit(CAN0, &txMsg)
#define can_rxd() can_message_receive(CAN0, CAN_FIFO1, &rxMsg)

4.2常见数据类型代码

4.2.1电机使能运行帧(通信类型3)

void motor_enable(uint8_t id, uint16_t master_id)
{
txCanIdEx.mode = 3;
txCanIdEx.id = id;
txCanIdEx.res = 0;
txCanIdEx.data = master_id;
txMsg.tx_dlen = 8;
txCanIdEx.data = 0;
can_txd();
}

4.2.2运控模式电机控制指令(通信类型1)

void motor_controlmode(uint8_t id, float torque, float MechPosition, float speed, float kp, float kd)
{
txCanIdEx.mode = 1;
txCanIdEx.id = id;
txCanIdEx.res = 0;
txCanIdEx.data = float_to_uint(torque,T_MIN,T_MAX,16);
txMsg.tx_dlen = 8;
txMsg.tx_data[0]=float_to_uint(MechPosition,P_MIN,P_MAX,16)>>8;
txMsg.tx_data[1]=float_to_uint(MechPosition,P_MIN,P_MAX,16);
txMsg.tx_data[2]=float_to_uint(speed,V_MIN,V_MAX,16)>>8;
txMsg.tx_data[3]=float_to_uint(speed,V_MIN,V_MAX,16);
txMsg.tx_data[4]=float_to_uint(kp,KP_MIN,KP_MAX,16)>>8;
txMsg.tx_data[5]=float_to_uint(kp,KP_MIN,KP_MAX,16);
txMsg.tx_data[6]=float_to_uint(kd,KD_MIN,KD_MAX,16)>>8;
txMsg.tx_data[7]=float_to_uint(kd,KD_MIN,KD_MAX,16);
can_txd();
}

 4.2.3电机停止运行帧(通信类型4)

void motor_reset(uint8_t id, uint16_t master_id)
{
txCanIdEx.mode = 4;
txCanIdEx.id = id;
txCanIdEx.res = 0;
txCanIdEx.data = master_id;
txMsg.tx_dlen = 8;
for(uint8_t i=0;i<8;i++)
{
txMsg.tx_data[i]=0;
}
can_txd();
}

 4.2.4电机模式参数写入命令(通信类型18,运行模式切换)

uint8_t runmode;
uint16_t index;
void motor_modechange(uint8_t id, uint16_t master_id)
{
txCanIdEx.mode = 0x12;
txCanIdEx.id = id;
txCanIdEx.res = 0;
txCanIdEx.data = master_id;
txMsg.tx_dlen = 8;
for(uint8_t i=0;i<8;i++)
{
txMsg.tx_data[i]=0;
}
memcpy(&txMsg.tx_data[0],&index,2);
memcpy(&txMsg.tx_data[4],&runmode, 1);
can_txd();
}

 4.2.5电机模式参数写入命令(通信类型18,控制参数写入)

uint16_t index;
float ref;
void motor_write(uint8_t id, uint16_t master_id)
{
txCanIdEx.mode = 0x12;
txCanIdEx.id = id;
txCanIdEx.res = 0;
txCanIdEx.data = master_id;
txMsg.tx_dlen = 8;
for(uint8_t i=0;i<8;i++)
{
txMsg.tx_data[i]=0;
}
memcpy(&txMsg.tx_data[0],&index,2);
memcpy(&txMsg.tx_data[4],&ref,4);
can_txd();
}

4.3通讯帧解析

上文的数据定义中,定义了一个扩展ID信息结构体,并将发送的数据结合组装成一个一个整体的数据帧。

细节摘录:

//扩展帧结构体
struct exCanIdInfo{
uint32_t id:8;
uint32_t data:16;
uint32_t mode:5;
uint32_t res:3;
};

//接收数据
can_receive_message_struct rxMsg;

//发送数据
can_trasnmit_message_struct txMsg={
.tx_sfid = 0,
.tx_efid = 0xff,
.tx_ft = CAN_FT_DATA,
.tx_ff = CAN_FF_EXTENDED,
.tx_dlen = 8,
};


//扩展帧结构和数据结合组成完整的数据
#define txCanIdEx (((struct exCanIdInfo)&(txMsg.tx_efid)))
#define rxCanIdEx (((struct exCanIdInfo)&(rxMsg.rx_efid))) 

struct exCanIdInfo{
uint32_t mode:5;
uint32_t id:8;
uint32_t res:3;
uint32_t data:16;
};

上诉写法是位域写法,定义了  uint32_t id:8;    的数据,但是只把32位数据存放在前8个bit中。

8+16+5+3=32符合扩展帧中仲裁字段的长度,也就是说这个结构体exCanIdInfo是为了构建仲裁字段的。

扩展帧中数据字段只控制数据长度,IDE功能别移除。

电机使能运行帧(通信类型3)

void motor_enable(uint8_t id, uint16_t master_id)
{
txCanIdEx.mode = 3;//选择控制模式
txCanIdEx.id = id;//8位ID,对应目标设备的ID
txCanIdEx.res = 0;

//发送端主机ID,同时包含一个扩展8位的数据
//16位数据
txCanIdEx.data = master_id;


txMsg.tx_dlen = 8;//发送数据的长度
txCanIdEx.data = 0;
can_txd();
}
数据域29位ID29位ID29位ID8Byte数据区
大小bit28~24bit23~8bit7~0Byte0~Byte7
描述通信类型数据区2目标地址数据区1

5.报文解析

全部通讯类型及其帧结构

下一篇链接

下下篇

  • 13
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Envis_liu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值