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 键就好了. 当你按下复位键时, 晶振已经在正常工作了, 电机就转起来了!