1.8 RM 电机初步 - 实验篇

接线

Pasted image 20260709234819

手册在我的仓库中也有


CAN通信

请首先分清楚两个不同的概念: CAN ID 和电调 ID CAN ID 是大疆在手册中已经定好了的, 发送控制报文的时候, CAN ID 有两个固定的值0x200 和 0x1ff 电调 ID 是在电调是上我们手动去改的 ID

CAN通信波特率为1Mbps, 一条CAN总线上可以允许有多个电调, 其电调 ID 各不相同

单片机通过标准数据帧控制电调进而控制电机, 我们主要写的就是数据段的内容, 也就是下面表格的数据域

帧的 CAN ID 有两个 0x200 或0x1ff, 这两个是发送帧; 同样电机也会反馈他们的状态给你, 电机给你反馈的帧就是反馈帧, 它的 CAN ID 是 0x201~0x208 这个是根据电机电调 ID 来确定的, 比如电调 ID 是 1, 那么反馈帧就是 0x201, 电调 ID 是 2 反馈帧就是 0x202以此类推

0x200 分管电调 ID 为 1-4 的设备, 0x1ff 分管电调 ID 为 5-8 的设备, 具体看官网手册即可

帧的数据长度(DLC) 为 8 字节

控制电流范围 -16384016384 , 映射到电调输出的转矩电流 -20020A

Pasted image 20260710001604

核心问题是: 一个字节(8 位)最大只能装 0~255, 而控制电流值要到 16384, 一个字节根本装不下, 所以必须拆成两个字节接力装, 这就是高低位的意义

比如我要让 1 号电机输出 10A

1
2
3
current = (int16_t)(10.0f / 20.0f * 16384);  // = 8192
DATA[0] = current >> 8; // 高 8 位
DATA[1] = current & 0xFF; // 低 8 位

既然是 10A 就是 16384 的一半, 就是 8192, 我们从理论上走一遍装数据的流程

首先 current 被我们计算得到是 8192, 注意到他是 2^13, 那么他写成 16 位二进制就是 00100000 00000000 高亮的这一部分就是高位, 没高亮的就是低位 根据上边的表我们知道, 控制电流高 8 位放进 DATA[0], 所以我们直接向右移8 位, 把低八位挤掉

然后低八位我们直接与上一个 0xFF 即 1111 1111, 结果直接放入DATA[0]

大端 big-endian

放好之后得决定先发哪半截, 大疆协议规定高 8 位在前(DATA[0] 放高位,DATA[1] 放低位),这叫 大端序. 这不是数学要求, 只是大疆协定约定的摆放顺序

相应的, 电调也是按照第一个字节是高位的约定来读


CubeMX

根据原理图我们发现 A 板上具有 24v 供电的接口, 只要我们通过 GPIO 配置一下就好了Pasted image 20260710004139 继续查看原理图可知 24V 电源输出控制引脚PMOS: GPIO PH2~PH5, 原理图中高电平则供电

另外: CAN1: PD0 PD1 在 APB1 总线上 CAN2: PB12 PB13 在 APB1 总线上

这里我们就直接使用 A 板的输出 24v 供电, 就不接分电板了 并且在这个实验中, 我们只关心发送报文, 并不关心电机发送回来的数据, 所以我们会在后面写一些空的回调函数


首先我们配置基本的工程项目…记得将 HSE 配置为 12MHz, 不然起振会有问题Pasted image 20260710032935

然后我们将PH2~PH5配置为 GPIO_Output 并将其配置为推挽上拉, 以启用 24v 输出Pasted image 20260710004801 打开 CAN1 CAN2 我们这里虽然只使用到了 CAN1 但是代码中 CAN2 也包含了, 如果不打开会有一些编译不通过的问题Pasted image 20260710005948


代码

打开 Clion, 我们需要将另外的一些开源 bsp 文件(这里采用中科大视频中的代码)加入我们的编译中, 这个代码我已经改好了, 在我的仓库中Pasted image 20260710014807 我们需要将 main.c 改为 cpp 文件, 并注意图中的后缀名是否一致 然后打开根目录下的 CMakeLists.txt 文件, 做如图中的添加, 最后在顶部窗口的 tool, 选择 cmak, 点击清除缓存并重新加载项目. 然后编译, 看看是否如图一样出现一个内存分布, 这就代表成功了


首先包含我们要用到的库

1
2
3
4
/* Private includes ----------------------------------------------------------*/  
/* USER CODE BEGIN Includes */
#include "drv_bsp.h"
#include "drv_can.h"

由于在这个实验我们不关心电机发送回来的数据, 所以我们写个空回调函数以适用这个板级支持包

1
2
3
4
5
6
7
8
9
10
/* Private user code ---------------------------------------------------------*/  
/* USER CODE BEGIN 0 */
/**
* @brief 空回调函数
* * @param Rx_Buffer CAN接收的信息结构体
*/void NULL_Call_Back(struct Struct_CAN_Rx_Buffer *Rx_Buffer)
{

}
/* USER CODE END 0 */

main函数编写

我们要使能一个 24v 的电源输出 - LD代表 A 板 left down 位置的电源输出口, 并且使能 CAN1

1
2
3
4
/* USER CODE BEGIN 2 */  
BSP_Init(BSP_DC24_LD_ON);
CAN_Init(&hcan1, NULL_Call_Back);
/* USER CODE END 2 */

然后写电机转动的主逻辑, 像呼吸灯一样的转动. 将电调 ID 配置为 1, 这样我们就知道应该发送 CAN ID为 0x200

和上文讲过的一样, 我们将电流大小写到数据段上, 然后把它发出去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Infinite loop */  
/* USER CODE BEGIN WHILE */
int16_t cur = 0;
while (1)
{
while (cur < 16384)
{
cur += (0.1f * 16384); // 每次增加 10%
CAN1_0x200_Tx_Data[0] = cur >> 8;
CAN1_0x200_Tx_Data[1] = cur & 0xFF;
CAN_Send_Data(&hcan1, 0x200, CAN1_0x200_Tx_Data, 8);
HAL_Delay(50);
}
while (cur > -16384)
{
cur -= (0.1f * 16384);
CAN1_0x200_Tx_Data[0] = cur >> 8;
CAN1_0x200_Tx_Data[1] = cur & 0xFF;
CAN_Send_Data(&hcan1, 0x200, CAN1_0x200_Tx_Data, 8);
HAL_Delay(50);
}
/* USER CODE END WHILE */

烧录你的代码, 现在应该可以让电机转起来了!


一个小问题

如果你烧录代码之后断电, 再重新上电, 你大概率会发现电机不转了, 然后接上调试器发现他停在了这里Pasted image 20260710035319

这个原因是 HSE 起振裕度不够, 冷上电时 12MHz 晶振从静止开始起振,没在 HAL 的超时窗口内稳住12M, HAL_RCC_OscConfig 失败就进 Error_Handler 的死循环了

解决办法是你按下 Reset 键就好了. 当你按下复位键时, 晶振已经在正常工作了, 电机就转起来了!