大疆M3508、M2006必备CAN总线知识与配置方法
使用大疆M3508、M2006的CAN总线知识与配置方法
前言:
两个月前的一篇文章:PID与三环控制发布以后,有不少朋友在微信上交流大疆M3508、M2006的使用问题,其中有一个方面值得单独拿出来聊一聊:CAN通信
对于很多朋友,包括我自己,使用大疆的M3508、M2006无刷直流电机和C610、C620电调是第一次接触到CAN通信。大疆的这几款产品原本设计的非常容易上手,应该几天就能熟练使用。但是由于C610、C620电调选用了CAN总线通信,给不少人带来了困扰。然而使用这几个电机并不需要把CAN通信了解的有多么深。
困扰往往来自:
- CAN总线之前用的比较少
- 现有CAN通信资料少而且复杂
这篇文章旨在快速扫盲,给出使用M3508、M2006电机的必备CAN通信知识,希望能降低使用门槛,给各位朋友节约时间。内容均使用M3508与M2006实测过,演示视频在文章末尾。
本文仅涉及必备知识,甚至尽量避开复杂的通信原理,力求浅显易懂。
0x00 需要额外的CAN收发器!!!
这件事要放到前面去说,因为已经有几位朋友的经历指出了其必要性。
如果我们有一块STM32开发板,也有电调电机和电源。那么我们的STM32引脚与电调的CAN_H和CAN_L之间是不能直接相连的。
需要购买如下图所示的CAN收发器,作为两者连接的桥梁。一般我用的是TJA1050芯片的模块。
CAN收发器是使用电机的必备硬件条件,一定要先确定自己有这个东西。至于原因,咱们接下来说明。
0x01 硬件层面分析
为什么需要CAN收发器
通过分析硬件层面我们能知道为什么需要额外的CAN收发器。请看下图:
从图中我们能看出,STM32拥有的外设叫做:CAN控制器,它负责CAN通信的筛选、优先级、仲裁等问题。相当于咱们的邮局,帮忙盖个邮戳,分分类。通过复用GPIO以后,它延伸出两个引脚:CAN_RX
和CAN_TX
,类比串口我们知道一个负责收一个负责发。
实际上,这两个引脚上传输的数据已经是CAN报文了,该有的格式它都有,STM32的CAN回环模式就相当于直接把CAN_RX
和CAN_TX
相连,就可以收到自己发的消息。
问题是,CAN在设计的时候为了消除共模干扰,特地选用了差分信号(也叫差模信号)传输。咱们的C610、C620电调,接收发送的就是差分信号。这才是CAN收发器的作用:
把来自STM32的信号转换成差分信号让电调听得懂,把来自电调的差分信号转换成STM32听得懂的信号。
请注意:
连接CAN_RX
和CAN_TX
引脚与CAN收发器的引脚时,并不需要像串口一样交叉。直接将RX连接到标有RX的引脚,TX类似。
120Ω的终端电阻呢?
不知道朋友们有没有注意到C610、C620电调上都有一个开关,C620的在侧面,旁边写着CAN RESISTOR
,这也是CAN总线的设计要求。
根据咱们学的《电路理论》这门课,当信号波长小于电路尺寸的时候,我们就不能把电路当成集中电路来分析。对于can总线是一样的,由于电调采用1MHz的通信频率,为了防止一些不必要的干扰,我们需要在CAN总线的两端分别用120Ω电阻跨接起来。比如之前图中的两端可以认为是咱们的CAN收发器是一端,下面的电调是另一端。
对于只有一个电调的情况下,可以打开电调上的电阻,然后直接和CAN收发器连接,这样虽然少了一端的终端电阻,但实际上可以运行。如果同时控制两个及以上的电调,那就可以打开其中两个的终端电阻,构成总线的两端。当然也可以手动组成如下图的总线结构并且关闭电调上的终端电阻,图中还加入了电容进行滤波。
0x02 软件配置简析
其实,看完硬件部分就已经可以正常使用电调电机了,因为大疆官方例程中已经把CAN通信配置好了,直接可以使用。
但是如果涉及非常多的设备同时控制,比如超过四个电机的情况下,或者还有其他的外设要通过CAN总线相连,简单了解软件配置也是很有好处。
而且,我发现很多朋友是在做RoboMaster比赛,全面的了解软硬件还是十分必要的,如果出现问题也有利于排查,不至于浪费大量时间。
接下来我们还是使用咱们的老朋友:大疆官方M2006例程、M3508例程来作为样本。主要探究以下问题:
- 通信频率如何设定为1MHz
- CAN过滤器的配置
- 报文的接收与发送
两个文件:bsp_can.c
和can.c
如何把通信频率设定为1MHz?
以下是文件can.c
中截取的代码(有删改):
CAN_HandleTypeDef hcan1;
/* CAN1 init function */
void MX_CAN1_Init(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 3;//分频系数设置为3
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SJW = CAN_SJW_1TQ;
// =================请看这里