本章将分析 VESC 官方固件中关于 CAN 通信的代码,并介绍如何使用 CAN 总线进行 VESC 多电机控制。

前言

推荐没有 CAN 通信软件基础的读者先阅读《一文学会手写CAN通信,CUBEMX+STM32+HAL库》

这篇文章详细讲解了 CAN 收发配置、函数代码,有完整的代码示例,只需略微修改即可适配 VESC CAN 通信。

VESC 端 CAN 接收

  • 数据帧格式:扩展格式(29位ID)。
  • VESC ID:仲裁段(ID)取低8位。
  • VESC 指令值:仲裁段(ID)取高21位。

常用指令表

指令 指令值(十进制) 内容 数据长度(字节) 数据类型 物理单位
CAN_PACKET_SET_DUTY 0 设置占空比 4 int32_t duty cycle * 1e5 (50000 -> 50%)
CAN_PACKET_SET_CURRENT 1 设置电流 4 int32_t mA
CAN_PACKET_SET_CURRENT_BRAKE 2 设置制动电流 4 int32_t mA
CAN_PACKET_SET_RPM 3 设置电转速 4 int32_t erpm

其余指令可以参考 VESC 官方固件中的 comm_can.cdatatypes.h 文件。

指令发送示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void fdcan_send_vesc(FDCAN_HandleTypeDef *hfdcan, motor_vesc_t *motor_vesc)
{
fdcan_tx_header_vesc.Identifier = (motor_vesc->cmd << 8) + motor_vesc->id;
fdcan_tx_header_vesc.IdType = FDCAN_EXTENDED_ID;
fdcan_tx_header_vesc.TxFrameType = FDCAN_DATA_FRAME;
fdcan_tx_header_vesc.DataLength = FDCAN_DLC_BYTES_4;

fdcan_tx_data_vesc[0] = (motor_vesc->data >> 24) & 0xFF;
fdcan_tx_data_vesc[1] = (motor_vesc->data >> 16) & 0xFF;
fdcan_tx_data_vesc[2] = (motor_vesc->data >> 8) & 0xFF;
fdcan_tx_data_vesc[3] = motor_vesc->data & 0xFF;

HAL_FDCAN_AddMessageToTxFifoQ(hfdcan, &fdcan_tx_header_vesc, fdcan_tx_data_vesc);
}

将指令和 vesc id 打包成 CAN 报文 ID,指令内容作为报文数据段。

USER 端 CAN 接收

  • 数据帧格式:扩展格式(29位ID)。
  • VESC ID:仲裁段(ID)取低8位。
  • VESC 状态值:仲裁段(ID)取高21位。

常用状态表

状态 状态值(十进制) 内容 数据长度(字节) 数据类型
CAN_PACKET_STATUS 9 转速、电机电流、占空比 8 int32_t, int16_t, int16_t
CAN_PACKET_STATUS_2 14 安时、电池安时 8 int32_t, int32_t
CAN_PACKET_STATUS_3 15 瓦时、电池瓦时 8 int32_t, int32_t
CAN_PACKET_STATUS_4 16 MOSFET温度、电机温度、输入电流、电机位置 8 int16_t, int16_t, int16_t, int16_t

其余指令可以参考 VESC 官方固件中的 comm_can.cdatatypes.h 文件。

指令接收示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
switch (status){
case 9: // CAN_PACKET_STATUS
motor->rpm = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
motor->current = ((data[4] << 8) | data[5]);
motor->duty = ((data[6] << 8) | data[7]);
break;

case 14: // CAN_PACKET_STATUS_2
motor->amp_hours = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
motor->amp_hours_charged = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
break;

case 15: // CAN_PACKET_STATUS_3
motor->watt_hours = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
motor->watt_hours_charged = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
break;

case 16: // CAN_PACKET_STATUS_4
motor->temp_fet = ((data[0] << 8) | data[1]);
motor->temp_motor = ((data[2] << 8) | data[3]);
motor->current_in = ((data[4] << 8) | data[5]);
motor->pid_pos_now = ((data[6] << 8) | data[7]);
break;
// ...
}

其中 data 为接收到的 CAN 报文数据段,为长度为 8 的 uint8_t 数组。

结尾

VESC 系列教程到这里算是告一段落了。(撒花~)

粗略涵盖了 原理图、PCB、测试、上位机、软件 全流程,非常感谢你的阅读!

若有任何疑问,欢迎邮件交流~

回到目录